/** * Serve-time-injected annotation layer. `annotateSnippet(slug, annotationsJson)` * returns an escaped JS+CSS string that `server.ts` splices before ``. * * Same convention as `sseSnippet`/`mermaidSnippet` in templates.ts: an exported * function that builds a `` inside any comment/quote cannot close the tag. // The result is still valid JS (\u003c in a string/JSON literal), so hydration // parses correctly. const safeJson = annotationsJson.replace(/ [data-artifact-annotate] { all: initial; user-select: none; /* Derived defaults; boot JS sets --aa-bg/--aa-fg/--aa-accent on and --aa-host-* for host tokens that exist (an element rule beats an inherited custom property, so the host value must come through var(), not override). */ --aa-muted: var(--aa-host-muted, color-mix(in srgb, var(--aa-fg) 55%, var(--aa-bg))); --aa-border: var(--aa-host-border, color-mix(in srgb, var(--aa-fg) 18%, var(--aa-bg))); --aa-code-bg: var(--aa-host-code-bg, color-mix(in srgb, var(--aa-fg) 6%, var(--aa-bg))); } [data-artifact-annotate] *, [data-artifact-annotate] *::before, [data-artifact-annotate] *::after { box-sizing: border-box; } [data-artifact-annotate] [hidden] { display: none !important; } [data-artifact-annotate] textarea, [data-artifact-annotate] .comment, [data-artifact-annotate] .reply, [data-artifact-annotate] .feedback { user-select: text; } [data-artifact-annotate] button, [data-artifact-annotate] textarea { font: inherit; } [data-artifact-annotate] :is(button, a, summary):focus-visible { outline: 2px solid var(--aa-accent); outline-offset: 3px; } [data-artifact-annotate] button:disabled { opacity: .45; cursor: default; } [data-artifact-annotate] .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } [data-aa-pin-tab] { outline: 2px dashed var(--aa-accent); outline-offset: 4px; cursor: crosshair; } [data-aa-pin-tab]:hover, [data-aa-pin-tab]:focus { outline-style: solid; } ::highlight(artifact-comment) { background-color: color-mix(in srgb, var(--aa-accent) 30%, transparent); } @keyframes aa-pop { from { opacity: 0; transform: scale(.96) translateY(-4px); } to { opacity: 1; transform: none; } } @keyframes aa-toast { from { opacity: 0; transform: translateX(-50%) translateY(8px); } to { opacity: 1; transform: translateX(-50%); } } #artifact-ui { position: fixed; bottom: 20px; right: 20px; z-index: 2147483000; display: flex; gap: 8px; align-items: center; } #artifact-ui.review-open { right: 416px; } #artifact-ui.review-open #artifact-annotate-btn { display: none; } body.aa-review-layout { padding-right: 400px; } #artifact-annotate-btn { font: 600 13px/1 system-ui, sans-serif; background: var(--aa-accent); color: var(--aa-bg); border: none; border-radius: 999px; padding: 10px 16px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,.25); transition: transform .15s ease, box-shadow .15s ease; } #artifact-share-btn { font: 600 13px/1 system-ui, sans-serif; background: var(--aa-bg); color: var(--aa-fg); border: 1px solid var(--aa-border); border-radius: 999px; padding: 10px 16px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,.25); transition: transform .15s ease, box-shadow .15s ease; } #artifact-share-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 14px rgba(0,0,0,.3); } #artifact-share-menu { position: fixed; bottom: 64px; right: 20px; z-index: 2147483001; min-width: 220px; background: var(--aa-bg); color: var(--aa-fg); border: 1px solid var(--aa-border); border-radius: 10px; padding: 6px; box-shadow: 0 8px 24px rgba(0,0,0,.28); font: 13px system-ui, sans-serif; display: none; animation: aa-pop .16s ease; } #artifact-share-menu button { all: unset; display: block; width: 100%; box-sizing: border-box; padding: 8px 12px; border-radius: 6px; cursor: pointer; font: 13px system-ui, sans-serif; color: var(--aa-fg); } #artifact-share-menu button:hover { background: color-mix(in srgb, var(--aa-fg) 8%, transparent); } @media print { #artifact-ui, #artifact-annotate-panel, #artifact-annotate-popover, #artifact-annotate-toast, #artifact-share-menu { display: none !important; } body { print-color-adjust: exact; -webkit-print-color-adjust: exact; } body.aa-review-layout { padding-right: 0 !important; } } .aa-print-comments { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border, #ddd); } .aa-print-comments h2 { font-size: 1.05rem; margin: 0 0 .6em; } .aa-print-comments .item { margin-bottom: .9em; } .aa-print-comments blockquote { margin: 0 0 .2em; padding: 2px 10px; font-style: italic; border-left: 3px solid var(--accent, #d67858); color: var(--muted, #888); } .aa-print-comments p { margin: 0; } #artifact-annotate-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 14px rgba(0,0,0,.3); } #artifact-annotate-btn.active { outline: 2px solid var(--aa-fg); box-shadow: 0 0 0 5px color-mix(in srgb, var(--aa-accent) 25%, transparent); } #artifact-annotate-btn .badge { display: inline-block; margin-left: 6px; min-width: 16px; padding: 0 4px; border-radius: 999px; background: var(--aa-bg); color: var(--aa-accent); font-size: 11px; text-align: center; } #artifact-annotate-panel { position: fixed; top: 16px; right: 16px; bottom: 16px; width: 380px; z-index: 2147483000; background: var(--aa-bg); color: var(--aa-fg); border: 1px solid var(--aa-border); border-radius: 16px; overflow: hidden; box-shadow: 0 12px 48px rgba(0,0,0,.18); font: 14px/1.55 system-ui, sans-serif; display: flex; flex-direction: column; visibility: hidden; transform: translateX(12px); opacity: 0; transition: transform .16s ease, opacity .16s ease, visibility .16s; } #artifact-annotate-panel.open { visibility: visible; transform: none; opacity: 1; } /* ?panel=open share renders show the review, not the tooling */ #artifact-annotate-panel.sharemode .actions, #artifact-annotate-panel.sharemode .close, #artifact-annotate-panel.sharemode .compare { display: none; } #artifact-annotate-panel header { display: flex; align-items: center; gap: 16px; flex: none; padding: 16px 20px; border-bottom: 1px solid var(--aa-border); } #artifact-annotate-panel header h2 { font: 650 18px/1.4 system-ui, sans-serif; margin: 0 auto 0 0; padding: 0; border: 0; color: inherit; } #artifact-annotate-panel .close { border: 0; background: none; color: var(--aa-muted); font-size: 24px; padding: 0 4px; cursor: pointer; } #artifact-annotate-panel .compare { font-size: 12px; color: var(--aa-muted); text-decoration: none; } #artifact-annotate-panel .compare:hover { color: var(--aa-fg); text-decoration: underline; } #artifact-annotate-panel .panel-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; } #artifact-annotate-panel .list { padding: 0 20px 20px; } #artifact-annotate-panel .hint { color: var(--aa-muted); font-size: 13px; line-height: 1.5; margin: 0 0 16px; } #artifact-annotate-panel .list-heading { margin: 4px 0 12px; font: 600 12px/1.5 system-ui, sans-serif; color: var(--aa-muted); } #artifact-annotate-panel .list-heading span { margin-left: 6px; } #artifact-annotate-panel .item { border: 1px solid var(--aa-border); border-radius: 10px; padding: 14px; margin-bottom: 10px; overflow-wrap: anywhere; } #artifact-annotate-panel .item-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px; font-size: 11px; } #artifact-annotate-panel .intent-label { font-weight: 600; color: var(--aa-fg); } #artifact-annotate-panel .item .quote { color: var(--aa-muted); font-size: 12px; margin-bottom: 10px; text-align: left; line-height: 1.5; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } #artifact-annotate-panel .item .stale { color: var(--aa-accent); } #artifact-annotate-panel .item .actions { display: flex; gap: 14px; margin-top: 12px; } #artifact-annotate-panel .reply { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--aa-border); white-space: pre-wrap; } #artifact-annotate-panel .sent { border-top: 1px solid var(--aa-border); margin-top: 20px; padding-top: 16px; } #artifact-annotate-panel .sent summary { color: var(--aa-muted); font-size: 13px; cursor: pointer; margin-bottom: 12px; } #artifact-annotate-panel .empty { padding: 16px 8px 24px; text-align: center; color: var(--aa-muted); font-size: 13px; } #artifact-annotate-panel .empty strong { font-weight: 500; color: var(--aa-fg); } #artifact-annotate-panel .empty p { margin: 8px 0 0; } #artifact-annotate-panel .item textarea { width: 100%; box-sizing: border-box; min-height: 50px; resize: vertical; font: 13px/1.5 system-ui, sans-serif; margin-top: 4px; padding: 6px 8px; background: var(--aa-code-bg); color: var(--aa-fg); border: 1px solid var(--aa-border); border-radius: 8px; outline: none; } #artifact-annotate-panel .item textarea:focus { border-color: var(--aa-accent); } #artifact-annotate-panel button.link { background: none; border: none; color: var(--aa-muted); cursor: pointer; font-size: 12px; padding: 2px 0; } #artifact-annotate-panel button.link:hover { color: var(--aa-fg); text-decoration: underline; } #artifact-annotate-panel footer { flex: none; padding: 16px 20px 20px; border-top: 1px solid var(--aa-border); } #artifact-annotate-panel .send-status { font-size: 12px; color: var(--aa-muted); margin: 0 0 12px; } #artifact-annotate-panel .pin.active { color: var(--aa-accent); } #artifact-annotate-panel .reload-artifact { width: 100%; margin-bottom: 12px; padding: 8px; border: 1px solid var(--aa-border); background: var(--aa-code-bg); color: var(--aa-fg); border-radius: 8px; cursor: pointer; } @media (max-width: 900px) { body.aa-review-layout { padding-right: 0; } #artifact-annotate-panel { top: auto; right: 8px; bottom: 8px; width: min(380px, calc(100vw - 16px)); max-height: 72dvh; } #artifact-ui.review-open { right: auto; left: 8px; } } @media (max-width: 500px) { #artifact-ui.review-open { display: none; } } @media (prefers-reduced-motion: reduce) { #artifact-annotate-panel { transition: none; } [data-artifact-annotate] * { animation: none !important; } } #artifact-annotate-panel .send { width: 100%; padding: 12px; border: none; border-radius: 9px; cursor: pointer; background: var(--aa-accent); color: var(--aa-bg); font: 600 14px system-ui, sans-serif; } #artifact-annotate-panel .send:disabled { opacity: .5; cursor: default; } #artifact-annotate-popover { padding: 20px; color: var(--aa-fg); } #artifact-annotate-popover .context-row { display: flex; align-items: baseline; gap: 8px; margin: 14px 0 8px; } #artifact-annotate-popover .pop-quote { flex: 1; font-size: 12px; line-height: 1.5; color: var(--aa-muted); max-height: 54px; overflow: hidden; overflow-wrap: anywhere; } #artifact-annotate-popover .intent { display: flex; padding: 3px; margin: 0; border: 1px solid var(--aa-border); border-radius: 9px; background: var(--aa-code-bg); min-width: 0; } #artifact-annotate-popover .intent label { flex: 1; margin: 0; cursor: pointer; text-align: center; position: relative; } #artifact-annotate-popover .intent input { position: absolute; width: 1px; height: 1px; opacity: 0; } #artifact-annotate-popover .intent span { display: block; padding: 7px 4px; border-radius: 6px; font-size: 12px; color: var(--aa-muted); } #artifact-annotate-popover .intent input:checked + span { background: var(--aa-bg); color: var(--aa-fg); box-shadow: 0 1px 4px rgba(0,0,0,.12); font-weight: 600; } #artifact-annotate-popover .intent input:focus-visible + span { outline: 2px solid var(--aa-accent); outline-offset: 1px; } #artifact-annotate-popover textarea { display: block; width: 100%; min-height: 112px; resize: vertical; font: 14px/1.6 system-ui, sans-serif; padding: 12px; outline: none; background: var(--aa-bg); color: var(--aa-fg); border: 1px solid var(--aa-border); border-radius: 8px; } #artifact-annotate-popover textarea:focus { border-color: var(--aa-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--aa-accent) 22%, transparent); } #artifact-annotate-popover .preview { min-height: 112px; max-height: 240px; overflow: auto; padding: 12px; border: 1px solid var(--aa-border); border-radius: 8px; } #artifact-annotate-popover .row { display: flex; align-items: center; gap: 14px; padding: 10px 0; } #artifact-annotate-popover .secondary { margin-left: auto; font: 600 12px/1.5 system-ui, sans-serif; padding: 7px 12px; border: 1px solid var(--aa-border); border-radius: 7px; background: var(--aa-code-bg); color: var(--aa-fg); cursor: pointer; } #artifact-annotate-popover .composer-help { display: flex; align-items: baseline; justify-content: space-between; color: var(--aa-muted); font-size: 11px; } #artifact-annotate-popover .pin-hint { margin: 12px 0 0; color: var(--aa-accent); } .md-preview p { margin: 0 0 6px; } .md-preview p:last-child { margin-bottom: 0; } .md-preview ul, .md-preview ol { margin: 0 0 6px; padding-left: 18px; } .md-preview h1, .md-preview h2, .md-preview h3, .md-preview h4 { font-size: 13px; margin: 8px 0 4px; } .md-preview code { background: var(--aa-code-bg); border: 1px solid var(--aa-border); border-radius: 4px; padding: 0 4px; font: 12px ui-monospace, monospace; } .md-preview pre { background: var(--aa-code-bg); border: 1px solid var(--aa-border); border-radius: 8px; padding: 8px; overflow: auto; margin: 0 0 6px; } .md-preview pre code { background: none; border: none; padding: 0; } .md-preview blockquote { border-left: 3px solid var(--aa-border); margin: 0 0 6px; padding: 2px 8px; color: var(--aa-muted); } .md-preview a { color: var(--aa-accent); } .md-preview img { max-width: 100%; } #artifact-annotate-toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 2147483002; background: var(--aa-fg); color: var(--aa-bg); padding: 10px 16px; border-radius: 6px; font: 13px system-ui, sans-serif; display: none; animation: aa-toast .25s ease; } #artifact-annotate-panel .feedback { white-space: pre-wrap; font: 11px/1.4 ui-monospace, monospace; background: var(--aa-code-bg); border: 1px solid var(--aa-border); border-radius: 6px; padding: 8px; max-height: 200px; overflow: auto; margin-top: 8px; } `; }