paperclipai/paperclip · error

Could not load wireframe viewer styles

Error message

Could not load wireframe viewer styles

What it means

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.

Solutions

  1. Verify the template still contains a `<style>...</style>` block; restore it if a refactor removed it unintentionally.
  2. Widen the regex to `/<style[^>]*>[\s\S]*?<\/style>/` for attributed tags.
  3. Point the generator at the new CSS source if styles were deliberately extracted.
  4. Run the generator again to confirm the viewer builds.

Example fix

// before
const style = template.match(/<style>[\s\S]*?<\/style>/)?.[0];
// after
const style = template.match(/<style[^>]*>[\s\S]*?<\/style>/)?.[0];
Defensive patterns

Strategy: validation

Validate before calling

if (!/<style[^>]*>[\s\S]*?<\/style>/.test(readFileSync(templatePath, "utf8"))) {
  console.warn("template has no inline <style>; v4 viewer generation will fail");
}

Type guard

const templateIsStyled = (html) => /<style[^>]*>[\s\S]*?<\/style>/.test(html);

Try / catch

try {
  buildViewer();
} catch (err) {
  if (err.message === "Could not load wireframe viewer styles") {
    console.error("Restore the inline <style> block in site-template.html or adapt the extractor.");
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/64f9ef296f27e9d4. Report an issue: GitHub.

Appendix: source

Thrown at doc/plans/chat-adapters/generate-wireframes-v4.mjs:324

writeFileSync(join(out, "flow.svg"), `${flowSvg()}\n`);

const groups = [
  ["Start", sharedScreens.filter((screen) => screen.group === "Start")],
  ...providers.map((provider) => [provider.name, providerScreens.filter((screen) => screen.provider === provider)]),
  ["Paperclip", sharedScreens.filter((screen) => screen.group === "Paperclip")]
];
const orderedScreens = groups.flatMap(([, screens]) => screens);

function uiDoc() {
  const inventory = orderedScreens.map((screen) => `| ${screen.id} | ${screen.group} | ${screen.title} | ${screen.desktopSize} | ${screen.mobileSize} |`).join("\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");
  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`;
}

function viewerHtml() {
  const template = readFileSync(join(root, "../../../packages/skills-catalog/catalog/bundled/product/wireframe/assets/site-template.html"), "utf8");
  const style = template.match(/<style>[\s\S]*?<\/style>/)?.[0];
  if (!style) throw new Error("Could not load wireframe viewer styles");
  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");
  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) => {
    const notes = screen.annotations.map((note, index) => `<li><b>${index + 1}</b> — ${esc(note).replaceAll("**", "")}</li>`).join("\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>`;
  }).join("\n")}`).join("\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>`;
}

writeFileSync(join(root, "2026-09-04-chat-adapters-ui-surfaces-v4.md"), `${uiDoc()}\n`);
writeFileSync(join(root, "index.html"), `${viewerHtml()}\n`);
console.log(`Generated ${orderedScreens.length * 2 + 1} v4 SVGs, UI specification, and index.html`);

View on GitHub (pinned to 3f1d897a7c)