html_compose.gallery.resources
1class IframeScripts: 2 parent = """ 3// Iframe auto-sizing: uses adoptedStyleSheets to survive HTML morphs 4window._iframeSizer ??= (() => { 5 const SELECTOR = "iframe.o-showcase-content"; 6 const sheet = new CSSStyleSheet(); 7 document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; 8 9 const sync = () => { 10 const rules = []; 11 for (const frame of document.querySelectorAll(SELECTOR)) { 12 const height = frame.contentDocument?.body?.scrollHeight; 13 if (height && frame.id) rules.push(`#${CSS.escape(frame.id)} { height: ${height}px; }`); 14 } 15 // Only update when we have heights - preserves rules when iframe not yet ready 16 if (rules.length) sheet.replaceSync(rules.join("\\n")); 17 }; 18 19 const resizeObs = new ResizeObserver(sync); 20 21 const observe = () => { 22 for (const frame of document.querySelectorAll(SELECTOR)) { 23 if (frame.contentDocument?.body) resizeObs.observe(frame.contentDocument.body); 24 frame.onload = () => { 25 if (frame.contentDocument?.body) resizeObs.observe(frame.contentDocument.body); 26 sync(); 27 }; 28 } 29 sync(); 30 }; 31 32 // MutationObserver runs indefinitely (never disconnected) 33 new MutationObserver(observe).observe(document.body, { childList: true, subtree: true, attributes: true }); 34 return { observe }; 35})(); 36window._iframeSizer.observe();""" 37 38 39NAV_STYLES = """ 40:host { 41 display: block; 42 font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 43 font-size: 14px; 44 padding: 12px 0; 45} 46ul { 47 list-style: none; 48 margin: 0; 49 padding: 0; 50} 51details { 52 margin: 0; 53} 54summary { 55 cursor: pointer; 56 padding: 6px 16px; 57 color: #333; 58 font-weight: 500; 59 user-select: none; 60} 61summary:hover { 62 background: #f5f5f5; 63} 64details > ul { 65 padding-left: 12px; 66} 67details details summary { 68 font-weight: 400; 69 color: #666; 70 padding: 4px 16px; 71 font-size: 13px; 72} 73a { 74 display: block; 75 padding: 4px 16px; 76 color: #0066cc; 77 text-decoration: none; 78 font-size: 13px; 79} 80a:hover { 81 background: #e8f4fc; 82} 83""" 84 85GALLERY_STYLES = """ 86/* Gallery base styles */ 87* { 88 box-sizing: border-box; 89} 90 91body { 92 margin: 0; 93 padding: 0; 94 font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 95 background: #f5f5f5; 96 color: #333; 97} 98 99/* Main layout */ 100.o-gallery-container { 101 display: flex; 102 flex-direction: row; 103 align-items: stretch; 104 height: 100vh; 105} 106 107/* Left navigation panel */ 108.o-left-panel { 109 flex: 0 0 240px; 110 overflow-y: auto; 111 overflow-x: hidden; 112 background: #fff; 113 border-right: 1px solid #e0e0e0; 114} 115 116/* Hide the empty right panel */ 117.o-right-panel { 118 display: none; 119} 120 121/* Center content panel */ 122.o-center-panel { 123 flex: 1; 124 display: flex; 125 flex-direction: column; 126 gap: 24px; 127 padding: 24px; 128 overflow-y: auto; 129 background: #f5f5f5; 130} 131 132/* Showcase card container */ 133.o-showcase-card { 134 flex-shrink: 0; /* Don't shrink - overflow and scroll instead */ 135 background: #fff; 136 border: 1px solid #e0e0e0; 137 border-radius: 6px; 138 overflow: hidden; 139 box-shadow: 0 1px 3px rgba(0,0,0,0.04); 140} 141 142/* Showcase header */ 143.o-showcase-header { 144 display: flex; 145 justify-content: space-between; 146 align-items: center; 147 padding: 10px 16px; 148 font-size: 14px; 149 font-weight: 500; 150 color: #555; 151 background: #fafafa; 152 border-bottom: 1px solid #e0e0e0; 153} 154 155.o-showcase-title { 156 flex: 1; 157} 158 159.o-showcase-link { 160 color: #888; 161 text-decoration: none; 162 padding: 4px 8px; 163 border-radius: 4px; 164 font-size: 13px; 165} 166 167.o-showcase-link:hover { 168 background: #e8e8e8; 169 color: #333; 170} 171 172/* Showcase content container */ 173.o-showcase-content { 174 background: #fff; 175} 176 177/* Iframe showcases */ 178iframe.o-showcase-content { 179 display: block; 180 border: none; 181 width: 100%; 182 flex-shrink: 0; 183 overflow: hidden; 184} 185"""
class
IframeScripts:
2class IframeScripts: 3 parent = """ 4// Iframe auto-sizing: uses adoptedStyleSheets to survive HTML morphs 5window._iframeSizer ??= (() => { 6 const SELECTOR = "iframe.o-showcase-content"; 7 const sheet = new CSSStyleSheet(); 8 document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; 9 10 const sync = () => { 11 const rules = []; 12 for (const frame of document.querySelectorAll(SELECTOR)) { 13 const height = frame.contentDocument?.body?.scrollHeight; 14 if (height && frame.id) rules.push(`#${CSS.escape(frame.id)} { height: ${height}px; }`); 15 } 16 // Only update when we have heights - preserves rules when iframe not yet ready 17 if (rules.length) sheet.replaceSync(rules.join("\\n")); 18 }; 19 20 const resizeObs = new ResizeObserver(sync); 21 22 const observe = () => { 23 for (const frame of document.querySelectorAll(SELECTOR)) { 24 if (frame.contentDocument?.body) resizeObs.observe(frame.contentDocument.body); 25 frame.onload = () => { 26 if (frame.contentDocument?.body) resizeObs.observe(frame.contentDocument.body); 27 sync(); 28 }; 29 } 30 sync(); 31 }; 32 33 // MutationObserver runs indefinitely (never disconnected) 34 new MutationObserver(observe).observe(document.body, { childList: true, subtree: true, attributes: true }); 35 return { observe }; 36})(); 37window._iframeSizer.observe();"""
parent =
'\n// Iframe auto-sizing: uses adoptedStyleSheets to survive HTML morphs\nwindow._iframeSizer ??= (() => {\n const SELECTOR = "iframe.o-showcase-content";\n const sheet = new CSSStyleSheet();\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];\n\n const sync = () => {\n const rules = [];\n for (const frame of document.querySelectorAll(SELECTOR)) {\n const height = frame.contentDocument?.body?.scrollHeight;\n if (height && frame.id) rules.push(`#${CSS.escape(frame.id)} { height: ${height}px; }`);\n }\n // Only update when we have heights - preserves rules when iframe not yet ready\n if (rules.length) sheet.replaceSync(rules.join("\\n"));\n };\n\n const resizeObs = new ResizeObserver(sync);\n\n const observe = () => {\n for (const frame of document.querySelectorAll(SELECTOR)) {\n if (frame.contentDocument?.body) resizeObs.observe(frame.contentDocument.body);\n frame.onload = () => {\n if (frame.contentDocument?.body) resizeObs.observe(frame.contentDocument.body);\n sync();\n };\n }\n sync();\n };\n\n // MutationObserver runs indefinitely (never disconnected)\n new MutationObserver(observe).observe(document.body, { childList: true, subtree: true, attributes: true });\n return { observe };\n})();\nwindow._iframeSizer.observe();'
NAV_STYLES =
'\n:host {\n display: block;\n font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n font-size: 14px;\n padding: 12px 0;\n}\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\ndetails {\n margin: 0;\n}\nsummary {\n cursor: pointer;\n padding: 6px 16px;\n color: #333;\n font-weight: 500;\n user-select: none;\n}\nsummary:hover {\n background: #f5f5f5;\n}\ndetails > ul {\n padding-left: 12px;\n}\ndetails details summary {\n font-weight: 400;\n color: #666;\n padding: 4px 16px;\n font-size: 13px;\n}\na {\n display: block;\n padding: 4px 16px;\n color: #0066cc;\n text-decoration: none;\n font-size: 13px;\n}\na:hover {\n background: #e8f4fc;\n}\n'
GALLERY_STYLES =
'\n/* Gallery base styles */\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n padding: 0;\n font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n background: #f5f5f5;\n color: #333;\n}\n\n/* Main layout */\n.o-gallery-container {\n display: flex;\n flex-direction: row;\n align-items: stretch;\n height: 100vh;\n}\n\n/* Left navigation panel */\n.o-left-panel {\n flex: 0 0 240px;\n overflow-y: auto;\n overflow-x: hidden;\n background: #fff;\n border-right: 1px solid #e0e0e0;\n}\n\n/* Hide the empty right panel */\n.o-right-panel {\n display: none;\n}\n\n/* Center content panel */\n.o-center-panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 24px;\n padding: 24px;\n overflow-y: auto;\n background: #f5f5f5;\n}\n\n/* Showcase card container */\n.o-showcase-card {\n flex-shrink: 0; /* Don\'t shrink - overflow and scroll instead */\n background: #fff;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n overflow: hidden;\n box-shadow: 0 1px 3px rgba(0,0,0,0.04);\n}\n\n/* Showcase header */\n.o-showcase-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n color: #555;\n background: #fafafa;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.o-showcase-title {\n flex: 1;\n}\n\n.o-showcase-link {\n color: #888;\n text-decoration: none;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 13px;\n}\n\n.o-showcase-link:hover {\n background: #e8e8e8;\n color: #333;\n}\n\n/* Showcase content container */\n.o-showcase-content {\n background: #fff;\n}\n\n/* Iframe showcases */\niframe.o-showcase-content {\n display: block;\n border: none;\n width: 100%;\n flex-shrink: 0;\n overflow: hidden;\n}\n'