{"record":{"id":"64f9ef296f27e9d4","repo":"paperclipai/paperclip","slug":"could-not-load-wireframe-viewer-styles-generate-wireframes","errorCode":null,"errorMessage":"Could not load wireframe viewer styles","messagePattern":"Could not load wireframe viewer styles","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"doc/plans/chat-adapters/generate-wireframes-v4.mjs","lineNumber":324,"sourceCode":"writeFileSync(join(out, \"flow.svg\"), `${flowSvg()}\\n`);\n\nconst groups = [\n  [\"Start\", sharedScreens.filter((screen) => screen.group === \"Start\")],\n  ...providers.map((provider) => [provider.name, providerScreens.filter((screen) => screen.provider === provider)]),\n  [\"Paperclip\", sharedScreens.filter((screen) => screen.group === \"Paperclip\")]\n];\nconst orderedScreens = groups.flatMap(([, screens]) => screens);\n\nfunction uiDoc() {\n  const inventory = orderedScreens.map((screen) => `| ${screen.id} | ${screen.group} | ${screen.title} | ${screen.desktopSize} | ${screen.mobileSize} |`).join(\"\\n\");\n  const details = orderedScreens.map((screen) => `### ${screen.id} · ${screen.title}\\n\\nPurpose: ${screen.subtitle}\\n\\n${screen.annotations.map((item, index) => `${index + 1}. ${item}`).join(\"\\n\")}\\n\\nRationale: ${screen.rationale}`).join(\"\\n\\n\");\n  return `# Paperclip Chat Adapters UI Surfaces — v4\\n\\nDate: 2026-09-04  \\nPaperclip base: \\`${baseSha}\\`  \\nReview viewer: [\\`index.html\\`](./index.html)  \\nWireframes: [\\`wireframes-v4/\\`](./wireframes-v4/)\\n\\n## Product rules represented\\n\\n- \\`/apps\\` remains the Connectors catalog. A purpose choice appears only for platforms such as GitHub that can be both a chat medium and an agent tool.\\n- Chat setup asks for the agent first, then performs a provider-owned installation handoff with reasonable defaults.\\n- Each provider endpoint has the complete existing-style detail shell: Overview, Settings, Access, Conversations, and Activity.\\n- Setup and settings use ordinary top-to-bottom sections. Desktop canvases grow to fit their content; the pages are not compressed into an 800px dashboard or bento grid.\\n- Provider capabilities are implementation guarantees, not endpoint preferences. Paperclip automatically uses the maximum safe set available to that adapter, provider installation, conversation type, and current Paperclip permission check.\\n- Settings therefore contain only genuine choices: scope, task boundaries, delivery/deployment, credentials, and explicit provider permission grants. Reactions, streaming, rich messages/cards, actions, modals, commands, files, edits, and private fallbacks are never shown as on/off settings.\\n- Overview reports the capability set as **Available automatically**. Conversation walkthroughs demonstrate it in the provider's native medium.\\n- Red dashed marks and numbers are review annotations, not proposed UI.\\n\\n## Complete tab coverage\\n\\nEvery provider group contains Setup, Overview, Settings, Access, Conversations, Activity, and a behavior walkthrough. The five tab names shown in the endpoint sidebar each have a provider-specific desktop and mobile wireframe. Shared generic Overview/Access/Conversations/Activity mockups are removed from the current viewer so they cannot be mistaken for the provider-specific designs.\\n\\n## Inventory\\n\\n| ID | Group | Surface | Desktop | Mobile |\\n|---|---|---|---|---|\\n${inventory}\\n\\n## Annotation notes\\n\\n${details}\\n\\n## Verification intent\\n\\n- Desktop SVGs use 1280px width and whatever height their ordinary vertical content requires.\\n- Mobile SVGs use 375px width, minimum 48px controls, and enough height to avoid clipping.\\n- The house palette remains white, black, \\`#e6e6e6\\`, \\`#666\\`, and annotation-only \\`#d33\\`, with 1.5px black strokes and 12/14/20/28px type.\\n- Secrets appear only as masked references. No provider feature toggle can disable a safe supported capability.\\n`;\n}\n\nfunction viewerHtml() {\n  const template = readFileSync(join(root, \"../../../packages/skills-catalog/catalog/bundled/product/wireframe/assets/site-template.html\"), \"utf8\");\n  const style = template.match(/<style>[\\s\\S]*?<\\/style>/)?.[0];\n  if (!style) throw new Error(\"Could not load wireframe viewer styles\");\n  const toc = groups.map(([label, screens]) => `<h2>${esc(label)}</h2>${screens.map((screen) => `<a href=\"#s${screen.id}\"><span class=\"num\">${Number(screen.id)}</span>${esc(screen.title)}</a>`).join(\"\\n\")}`).join(\"\\n\");\n  const sections = groups.map(([label, screens]) => `<div class=\"provider-break\"><div class=\"lede\">${esc(label)}</div><h2>${label === \"Start\" ? \"Shared connection start\" : label === \"Paperclip\" ? \"Shared Paperclip surfaces\" : `${esc(label)} connector`}</h2></div>${screens.map((screen) => {\n    const notes = screen.annotations.map((note, index) => `<li><b>${index + 1}</b> — ${esc(note).replaceAll(\"**\", \"\")}</li>`).join(\"\\n\");\n    return `<section id=\"s${screen.id}\"><div class=\"lede\">${esc(screen.group)} · ${esc(screen.tab || \"Shared\")}</div><h2><span class=\"step-num\">${Number(screen.id)}.</span>${esc(screen.title)}</h2><p class=\"desc\">${esc(screen.subtitle)}</p><div class=\"grid\"><div class=\"wire\" data-zoom data-caption=\"${screen.id} · ${esc(screen.title)} (desktop)\"><div class=\"label\"><span>${screen.id}-${screen.slug}.svg</span><span>${screen.desktopSize} · desktop</span></div><img src=\"wireframes-v4/${screen.id}-${screen.slug}.svg\" alt=\"${esc(screen.title)} desktop wireframe\" /></div><div class=\"wire mobile-wire mobile-col\" data-zoom data-caption=\"${screen.id} · ${esc(screen.title)} (mobile)\"><div class=\"label\"><span>mobile</span><span>${screen.mobileSize}</span></div><img src=\"wireframes-v4/${screen.id}-${screen.slug}-mobile.svg\" alt=\"${esc(screen.title)} mobile wireframe\" /></div><div class=\"notes-col\"><div class=\"notes\"><h3>Annotations</h3><ul>${notes}</ul><div class=\"why\"><b>Rationale:</b> ${esc(screen.rationale)}</div></div></div></div></section>`;\n  }).join(\"\\n\")}`).join(\"\\n\");\n  return `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/><title>Paperclip chat adapters — complete provider review</title>${style}<style>.doc-links{display:flex;flex-wrap:wrap;gap:8px 16px;margin-top:16px}.doc-links a{min-height:48px;display:inline-flex;align-items:center;font-size:13px;font-weight:600}.notice{max-width:var(--maxw);margin:-32px 0 48px;padding:14px 18px;background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:4px}.notice p{margin:0}.decision{margin-top:24px;padding:18px;background:var(--panel);border:1px solid var(--line);border-radius:8px}.decision b{display:block;margin-bottom:6px}.provider-break{max-width:var(--maxw);margin:80px 0 8px;padding-top:24px;border-top:2px solid var(--ink)}.provider-break h2{font-size:28px;margin:6px 0 0}.toc-body h2{margin-top:18px}code{font-size:.92em}</style></head><body><div class=\"shell\"><details class=\"toc\"><summary class=\"toc-summary\"><span><span class=\"crumb\">Chat adapters · v4</span><br><span class=\"title\">Jump to a screen</span></span><span class=\"chevron\" aria-hidden=\"true\"></span></summary><nav class=\"toc-body\" aria-label=\"Section navigation\"><h1>Chat adapters</h1><div style=\"font-size:13px;color:var(--muted);margin-bottom:16px\">Complete provider review</div><h2>Documents</h2><a href=\"2026-09-03-chat-adapters-architecture.md\"><span class=\"num\">A</span>Architecture</a><a href=\"2026-09-04-chat-adapters-ui-surfaces-v4.md\"><span class=\"num\">U</span>UI specification v4</a><a href=\"2026-09-04-chat-adapters-platform-surfaces.md\"><span class=\"num\">P</span>Platform research</a><h2>Flow</h2><a href=\"#flow\"><span class=\"num\">⤳</span>Product flow</a>${toc}<h2>Review</h2><a href=\"#coverage\"><span class=\"num\">✓</span>Decisions and coverage</a></nav></details><main><header class=\"hero\"><div class=\"crumb\">Paperclip · Connectors · Complete v4</div><h1>Every provider. Every endpoint tab.</h1><p>Slack, GitHub, Microsoft Teams, and Telegram each have a conventional vertical setup plus provider-specific Overview, Settings, Access, Conversations, and Activity pages. Capabilities are automatic implementation behavior, not switches.</p><div class=\"decision\"><b>Maximum safe capability is the invariant</b><span>Paperclip always uses every safe feature supported by the adapter, provider installation, current conversation type, and current Paperclip permission check. Settings contain only genuine operator choices.</span></div><div class=\"doc-links\"><a href=\"2026-09-03-chat-adapters-architecture.md\">Architecture plan</a><a href=\"2026-09-04-chat-adapters-ui-surfaces-v4.md\">UI specification v4</a><a href=\"2026-09-04-chat-adapters-platform-surfaces.md\">Platform-specific research</a></div><div class=\"pills\"><span class=\"pill\">33 product surfaces</span><span class=\"pill\">5 tabs × 4 providers</span><span class=\"pill\">Tall vertical pages</span><span class=\"pill\">Desktop + mobile</span></div></header><div class=\"notice\" role=\"note\"><p><b>Review convention:</b> red dashed marks are annotations, not proposed UI. Feature lists on Overview are informational; they are not settings.</p></div><section id=\"flow\" class=\"flow-section\"><div class=\"lede\">Navigation and product flow</div><h2>Shared start, provider setup, complete detail shell</h2><p class=\"desc\">Every activated endpoint lands in the same five-tab shell. The content is provider-specific because installation, identity, native thread boundaries, and diagnostics differ.</p><div class=\"wire\" data-zoom data-caption=\"Complete chat connector product flow\"><div class=\"label\"><span>flow.svg</span><span>1280×980</span></div><img src=\"wireframes-v4/flow.svg\" alt=\"Complete chat connector product flow\"/></div></section>${sections}<section id=\"coverage\"><div class=\"lede\">Review</div><h2>What changed in v4</h2><div class=\"notes\"><ul><li><b>No bento setup pages:</b> screens 13, 16, 19, and 22 are now long, conventional, top-to-bottom installation flows.</li><li><b>No feature toggles:</b> reactions, streaming, rich content, actions, modals, commands, files, edits, and private fallbacks are automatic whenever safely available.</li><li><b>Complete endpoint shells:</b> every provider has an Overview, Settings, Access, Conversations, and Activity wireframe for desktop and mobile.</li><li><b>Provider specificity:</b> installation, identity keys, task boundaries, permission grants, activity ledgers, and fallbacks reflect the actual platform.</li><li><b>Stable original anchors:</b> IDs 13–24 still refer to the same provider setup/settings/walkthrough topics; new tabs use IDs 25–40.</li><li><b>Paperclip base:</b> <code>${baseSha}</code>, matching <code>origin/master</code> when generated.</li></ul></div></section><div class=\"footer\">Generated with Paperclip’s wireframe contract. Page height follows content; no provider setup or settings page is forced into an 800px dashboard.</div></main></div><div class=\"lightbox\" id=\"lb\" aria-hidden=\"true\"><span class=\"close\" id=\"lbClose\" role=\"button\" aria-label=\"Close preview\">×</span><img id=\"lbImg\" alt=\"\"/><div class=\"caption\" id=\"lbCap\"></div></div><script>const lb=document.getElementById('lb'),lbImg=document.getElementById('lbImg'),lbCap=document.getElementById('lbCap');document.querySelectorAll('[data-zoom]').forEach(el=>el.addEventListener('click',()=>{const target=el.querySelector('img');if(!target)return;lbImg.src=target.src;lbImg.alt=target.alt;lbCap.textContent=el.dataset.caption||target.alt||'';lb.classList.add('open');lb.setAttribute('aria-hidden','false')}));function closeLightbox(){lb.classList.remove('open');lb.setAttribute('aria-hidden','true')}lb.addEventListener('click',closeLightbox);document.getElementById('lbClose').addEventListener('click',closeLightbox);document.addEventListener('keydown',e=>{if(e.key==='Escape')closeLightbox()});const tocElement=document.querySelector('details.toc'),media=window.matchMedia('(max-width:900px)'),setToc=()=>{tocElement.open=!media.matches};setToc();media.addEventListener('change',setToc);tocElement.querySelectorAll('.toc-body a').forEach(link=>link.addEventListener('click',()=>{if(media.matches)tocElement.open=false}));</script></body></html>`;\n}\n\nwriteFileSync(join(root, \"2026-09-04-chat-adapters-ui-surfaces-v4.md\"), `${uiDoc()}\\n`);\nwriteFileSync(join(root, \"index.html\"), `${viewerHtml()}\\n`);\nconsole.log(`Generated ${orderedScreens.length * 2 + 1} v4 SVGs, UI specification, and index.html`);\n","sourceCodeStart":306,"sourceCodeEnd":336,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/doc/plans/chat-adapters/generate-wireframes-v4.mjs#L306-L336","documentation":"Same contract as v3: the v4 generator's `viewerHtml()` regex-extracts the inline `<style>` block from the shared site template and throws this fixed message when the block is missing. It guards against shipping a review viewer with no design tokens.","triggerScenarios":"Running `node doc/plans/chat-adapters/generate-wireframes-v4.mjs` after `packages/skills-catalog/catalog/bundled/product/wireframe/assets/site-template.html` was changed so the regex `/<style>[\\s\\S]*?<\\/style>/` no longer matches — external CSS extraction, attributed style tag, or truncated/merged-away style section.","commonSituations":"Design-system refactor moved template styles into a separate stylesheet; running older generator versions against a newer repo state; a partial checkout/merge left the asset stale.","solutions":["Verify the template still contains a `<style>...</style>` block; restore it if a refactor removed it unintentionally.","Widen the regex to `/<style[^>]*>[\\s\\S]*?<\\/style>/` for attributed tags.","Point the generator at the new CSS source if styles were deliberately extracted.","Run the generator again to confirm the viewer builds."],"exampleFix":"// before\nconst style = template.match(/<style>[\\s\\S]*?<\\/style>/)?.[0];\n// after\nconst style = template.match(/<style[^>]*>[\\s\\S]*?<\\/style>/)?.[0];","handlingStrategy":"validation","validationCode":"if (!/<style[^>]*>[\\s\\S]*?<\\/style>/.test(readFileSync(templatePath, \"utf8\"))) {\n  console.warn(\"template has no inline <style>; v4 viewer generation will fail\");\n}","typeGuard":"const templateIsStyled = (html) => /<style[^>]*>[\\s\\S]*?<\\/style>/.test(html);","tryCatchPattern":"try {\n  buildViewer();\n} catch (err) {\n  if (err.message === \"Could not load wireframe viewer styles\") {\n    console.error(\"Restore the inline <style> block in site-template.html or adapt the extractor.\");\n  } else throw err;\n}","preventionTips":["Keep the shared template's inline styles intact; extract styles only with a coordinated update to all generator versions.","Add a smoke test that runs one generator after template changes.","Use attribute-tolerant style regexes in generator code."],"tags":["build-script","regex","template"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}