@2lab.ai/ui 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -2
- package/dist/agent-inbox.js +1061 -0
- package/dist/agent-inbox.js.map +1 -0
- package/dist/agent-queue.js +1610 -0
- package/dist/agent-queue.js.map +1 -0
- package/dist/agent-run.js +859 -0
- package/dist/agent-run.js.map +1 -0
- package/dist/ai-answer.js +687 -0
- package/dist/ai-answer.js.map +1 -0
- package/dist/components/agent-inbox/agent-inbox.d.ts +158 -0
- package/dist/components/agent-inbox/index.d.ts +2 -0
- package/dist/components/agent-inbox/styles.d.ts +6 -0
- package/dist/components/agent-queue/agent-queue.d.ts +41 -0
- package/dist/components/agent-queue/index.d.ts +6 -0
- package/dist/components/agent-queue/logic.d.ts +261 -0
- package/dist/components/agent-queue/strings.d.ts +473 -0
- package/dist/components/agent-queue/styles.d.ts +8 -0
- package/dist/components/agent-run/agent-run.d.ts +273 -0
- package/dist/components/agent-run/index.d.ts +2 -0
- package/dist/components/agent-run/styles.d.ts +6 -0
- package/dist/components/ai-answer/ai-answer.d.ts +92 -0
- package/dist/components/ai-answer/index.d.ts +2 -0
- package/dist/components/ai-answer/styles.d.ts +2 -0
- package/dist/components/context-window/context-window.d.ts +156 -0
- package/dist/components/context-window/index.d.ts +2 -0
- package/dist/components/context-window/styles.d.ts +2 -0
- package/dist/components/memory-panel/index.d.ts +2 -0
- package/dist/components/memory-panel/memory-panel.d.ts +101 -0
- package/dist/components/memory-panel/styles.d.ts +2 -0
- package/dist/components/model-picker/index.d.ts +2 -0
- package/dist/components/model-picker/model-picker.d.ts +136 -0
- package/dist/components/model-picker/morph.d.ts +9 -0
- package/dist/components/model-picker/styles.d.ts +6 -0
- package/dist/components/tool-call-card/index.d.ts +7 -0
- package/dist/components/tool-call-card/logic.d.ts +87 -0
- package/dist/components/tool-call-card/strings.d.ts +94 -0
- package/dist/components/tool-call-card/styles.d.ts +5 -0
- package/dist/components/tool-call-card/tool-call-card.d.ts +49 -0
- package/dist/components/tool-call-card/tool-call-group.d.ts +32 -0
- package/dist/context-window.js +384 -0
- package/dist/context-window.js.map +1 -0
- package/dist/core/element.d.ts +41 -0
- package/dist/core/format.d.ts +14 -0
- package/dist/core/html.d.ts +10 -0
- package/dist/core/tokens.d.ts +14 -0
- package/dist/format-BBGWXPzk.js +51 -0
- package/dist/format-BBGWXPzk.js.map +1 -0
- package/dist/html-elD6Bb_u.js +175 -0
- package/dist/html-elD6Bb_u.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +129 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-panel.js +1255 -0
- package/dist/memory-panel.js.map +1 -0
- package/dist/model-picker.js +858 -0
- package/dist/model-picker.js.map +1 -0
- package/dist/tokens.css +4 -0
- package/dist/tool-call-card.js +1046 -0
- package/dist/tool-call-card.js.map +1 -0
- package/package.json +79 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-answer.js","sources":["../src/components/ai-answer/styles.ts","../src/components/ai-answer/ai-answer.ts"],"sourcesContent":["/** Ported from the soma-work dashboard (.prd/affectui/re-ai-answer.md D2 · D6–D10 · D15–D18, `.ans-*` + `.md-doc`). */\nexport const styles = `\n.ui-root { container: ans / inline-size; }\n.ans-card { position: relative; padding: 12px 14px 10px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); color: var(--text); box-shadow: var(--ib-shadow); overflow: hidden; }\n.ans-card.is-in > * { animation: ans-in var(--dur-fade) var(--ease-out) both; }\n.ans-title { margin: 0 0 10px; font-size: 15px; font-weight: 600; line-height: 1.35; letter-spacing: -0.01em; color: var(--text); overflow-wrap: anywhere; }\n\n/* Link cards (D6): a sideways row, scrollbar hidden, both ends fade. */\n.ans-srcs { display: flex; gap: 8px; margin: -4px -14px 8px; padding: 4px 14px; list-style: none; overflow-x: auto; scrollbar-width: none;\n -webkit-mask: linear-gradient(90deg, transparent, black 12px, black calc(100% - 12px), transparent);\n mask: linear-gradient(90deg, transparent, black 12px, black calc(100% - 12px), transparent); }\n.ans-srcs::-webkit-scrollbar { display: none; }\n.ans-srcs > li { flex: none; display: flex; }\n.ans-src { display: flex; flex-direction: column; gap: 4px; width: 148px; min-height: 64px; padding: 8px 10px; border-radius: 10px; background: var(--surface-raised); color: inherit; text-decoration: none; transition: background-color var(--dur-press) ease, box-shadow var(--dur-press) ease; }\n.ans-card.is-in .ans-src { animation: ans-pop var(--dur-move) var(--ease-out) both; animation-delay: calc(var(--ans-i, 0) * 60ms); }\n.ans-src:hover { background: var(--ib-pill); }\n.ans-src.is-lit { box-shadow: inset 0 0 0 1.5px var(--text-tertiary); }\n.ans-src-site { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 11px; color: var(--text-secondary); }\n.ans-src-name { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }\n.ans-src-num { flex: none; font-variant-numeric: tabular-nums; color: var(--text-tertiary); }\n.ans-src-title { display: -webkit-box; overflow: hidden; -webkit-line-clamp: 2; -webkit-box-orient: vertical; font-size: 12px; line-height: 1.4; color: var(--text); word-break: break-word; }\n\n/* Label (D9) and body (D2). */\n.ans-label { margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--text-secondary); }\n.ans-body { font-size: 13.5px; line-height: 1.6; }\n.ans-body > :last-child { margin-bottom: 0; }\n\n/* The document (dashboard .md-doc). */\n.md-doc { color: var(--text); word-break: break-word; }\n.md-doc h1, .md-doc h2, .md-doc h3, .md-doc h4, .md-doc h5, .md-doc h6 { margin: 1.3em 0 0.5em; line-height: 1.3; letter-spacing: -0.01em; }\n.md-doc h1, .md-doc h2 { font-size: 1.15em; }\n.md-doc h3, .md-doc h4, .md-doc h5, .md-doc h6 { font-size: 1em; }\n.md-doc > :first-child { margin-top: 0; }\n.md-doc p, .md-doc ul, .md-doc ol, .md-doc blockquote, .md-doc pre, .md-doc .md-table { margin: 0 0 0.9em; }\n.md-doc ul, .md-doc ol { padding-left: 1.4em; }\n.md-doc li + li { margin-top: 0.2em; }\n.md-doc li > ul, .md-doc li > ol { margin: 0.2em 0 0; }\n.md-doc code { padding: 0.1em 0.35em; border-radius: 4px; background: var(--ib-pill); font: 0.88em var(--mono); }\n.md-doc pre { padding: 12px 14px; overflow: auto; border: 1px solid var(--border); border-radius: 8px; background: var(--ib-col); }\n.md-doc pre code { padding: 0; background: none; font-size: 12.5px; line-height: 1.55; }\n.md-doc blockquote { padding: 2px 0 2px 12px; border-left: 3px solid var(--border-focus); color: var(--text-secondary); }\n.md-doc blockquote > :last-child { margin-bottom: 0; }\n.md-doc hr { margin: 1.4em 0; border: 0; border-top: 1px solid var(--border); }\n.md-doc a { color: var(--accent); }\n.md-doc .md-table { overflow-x: auto; }\n.md-doc table { border-collapse: collapse; font-size: 0.93em; }\n.md-doc th, .md-doc td { padding: 6px 10px; border: 1px solid var(--border); text-align: left; vertical-align: top; }\n.md-doc th { background: var(--ib-col); font-weight: 600; }\n\n/* Link chips (D7) — lit = inverted (D8). */\n.ans-body .ans-cite { display: inline-block; max-width: 16ch; height: 18px; margin: 0 1px 0 3px; padding: 0 6px; overflow: hidden; border-radius: 6px; background: var(--surface-raised); font-size: 11px; font-weight: 500; line-height: 18px; color: var(--text-secondary); text-decoration: none; white-space: nowrap; text-overflow: ellipsis; vertical-align: middle; transition: background-color var(--dur-press) ease, color var(--dur-press) ease; }\n.ans-body .ans-cite:hover { background: var(--ib-pill); color: var(--text); }\n.ans-body .ans-cite.is-lit { background: var(--text); color: var(--surface); }\n.ans-nb { white-space: nowrap; }\n.ans-cite:focus-visible, .ans-src:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }\n\n/* Footer (D10): Copy · status · response time. */\n.ans-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); }\n.ans-btn { display: inline-flex; flex: none; align-items: center; justify-content: center; gap: 5px; min-height: 32px; padding: 5px 12px; border: 1px solid var(--ib-card-border); border-radius: 8px; background: var(--ib-card); font-size: 13px; font-weight: 500; color: var(--ib-text); white-space: nowrap; transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease; }\n.ans-btn svg { width: 13px; height: 13px; }\n.ans-copy[data-copied] { color: var(--green); }\n.ans-status { flex: 1 1 0; min-width: 0; font-size: 12px; color: var(--text-tertiary); }\n.ans-end { display: inline-flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px 10px; margin-left: auto; min-width: 0; }\n.ans-took { font-size: 11.5px; color: var(--text-tertiary); font-variant-numeric: tabular-nums; white-space: nowrap; }\n@media (hover: hover) and (pointer: fine) { .ans-btn:hover { border-color: var(--ib-muted); } }\n\n/* Writing (D12): a sweep over the words + a pulsing run-colour dot. */\n.ans-foot.is-writing { min-height: 41px; }\n.ans-writing { display: flex; align-items: center; gap: 8px; font-size: 12px; }\n.ans-shimmer { color: transparent; background: linear-gradient(90deg, var(--text-tertiary) 38%, var(--text) 50%, var(--text-tertiary) 62%) 100% 0 / 300% 100% no-repeat; -webkit-background-clip: text; background-clip: text; animation: ans-sweep 1.6s linear infinite; }\n.ans-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--st-run); animation: ans-pulse 1s ease-in-out infinite; }\n\n/* Loading · empty · failed (D13). */\n.ans-state { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin: 0; font-size: 12px; color: var(--text-tertiary); }\n.ans-error { font-size: 12px; color: var(--st-fail-text); overflow-wrap: anywhere; }\n\n@keyframes ans-in { from { opacity: 0; filter: blur(2px); transform: translateY(3px); } to { opacity: 1; filter: none; transform: none; } }\n@keyframes ans-pop { from { opacity: 0; transform: translateY(6px) scale(0.96); } to { opacity: 1; transform: none; } }\n@keyframes ans-sweep { from { background-position: 100% 0; } to { background-position: 0 0; } }\n@keyframes ans-pulse { 50% { transform: scale(0.6); opacity: 0.45; } }\n\n@container ans (min-width: 520px) {\n .ans-card { padding: 16px 18px 12px; }\n .ans-title { font-size: 16.5px; }\n .ans-srcs { margin: -4px -18px 10px; padding: 4px 18px; }\n .ans-src { width: 172px; }\n .ans-body { font-size: 14px; }\n}\n\n/* Touch (D16): the chip's hit area grows, chips are never cut, buttons are 44px. */\n@media (pointer: coarse) {\n .ans-body .ans-cite { position: relative; overflow: visible; max-width: none; }\n .ans-cite::after { content: ''; position: absolute; inset: -10px -4px; }\n .ans-btn { min-height: 44px; }\n}\n/* The global rule only shortens durations, so infinite loops would flicker: stop them. */\n@media (prefers-reduced-motion: reduce) {\n .ans-shimmer, .ans-dot, .ans-src, .ans-card.is-in > * { animation: none !important; }\n .ans-shimmer { background: none; color: var(--text-tertiary); }\n .ans-src, .ans-body .ans-cite { transition: none; }\n}\n`;\n","import { define, prefersReducedMotion, UiElement } from '../../core/element';\nimport { esc, escAttr, safeHref } from '../../core/html';\nimport { styles } from './styles';\n\n/**\n * One assistant reply. Links are read out of the markdown itself — they are links the answer contains, not\n * proven sources, so the UI calls them “links” (never “sources” / “citations”).\n */\nexport interface AiAnswerData {\n /** The answer as the model wrote it (markdown). Rendered safely: raw HTML never survives. */\n markdown: string;\n /** Time from the instruction to the answer, in ms. Shown in the footer; nothing when absent or ≤ 0. */\n responseMs?: number;\n /** More text is coming: shows the writing row, hides Copy, marks the card `aria-busy`. */\n streaming?: boolean;\n /** The host is fetching a recorded answer (no text yet). */\n loading?: boolean;\n /** Optional heading above the card (e.g. the question or the turn’s summary title). */\n title?: string;\n /** Failure detail (e.g. `HTTP 404`). Without text it replaces the answer; with text it shows in the footer. */\n error?: string;\n /** Show “Try again” next to an error (emits `ui-retry`). Default false. */\n canRetry?: boolean;\n /** Titles the host already knows for some URLs (e.g. the PR this session opened). Beat labels and URL-derived titles. */\n linkTitles?: Record<string, string>;\n}\n\n/** A link found in the answer, numbered by first appearance (one per URL; a trailing slash is the same URL). */\nexport interface AnswerLink {\n /** 1-based number shown on the card. */\n n: number;\n href: string;\n /** Site: `GitHub` or the host without `www.`. */\n name: string;\n title: string;\n /** Short chip label: `#123`, a 7-char sha, `GitHub` or the host. */\n chip: string;\n}\n\nexport type AnswerLang = 'en' | 'ko';\n\nexport interface AnswerRender {\n html: string;\n links: AnswerLink[];\n}\n\nconst COPIED_MS = 1500;\n/** Upper bound of a smooth row scroll; a reveal inside it measures from the glide's target. */\nconst GLIDE_MS = 600;\n\nconst plural = (n: number, one: string, many: string) => `${n} ${n === 1 ? one : many}`;\n\nconst STRINGS = {\n en: {\n label: (n: number) => (n ? `Answer · ${plural(n, 'link', 'links')}` : 'Answer'),\n list: (n: number) => `${plural(n, 'link', 'links')} in this answer`,\n cite: (n: number, title: string, name: string) => `Link ${n}: ${title} (${name})`,\n issue: 'Issue',\n commit: 'Commit',\n linksHead: 'Links:',\n copy: 'Copy',\n copied: 'Copied',\n copiedSay: (n: number) => (n ? `Copied the answer and ${plural(n, 'link', 'links')}` : 'Copied the answer'),\n selected: 'Selected — press ⌘C / Ctrl+C to copy',\n writing: 'Writing the answer',\n loading: 'Loading…',\n empty: 'No answer text was recorded',\n failed: 'Couldn’t get the answer',\n retry: 'Try again',\n under: 'Responded in under 1s',\n secs: (s: number) => `Responded in ${s}s`,\n mins: (m: number, s: number) => `Responded in ${m}m ${s}s`,\n hours: (h: number, m: number) => `Responded in ${h}h ${m}m`,\n },\n ko: {\n label: (n: number) => (n ? `답변 · 링크 ${n}` : '답변'),\n list: (n: number) => `답변에 포함된 링크 ${n}개`,\n cite: (n: number, title: string, name: string) => `링크 ${n}: ${title} (${name})`,\n issue: '이슈',\n commit: '커밋',\n linksHead: '링크:',\n copy: '복사',\n copied: '복사됨',\n copiedSay: (n: number) => (n ? `답변과 링크 ${n}개를 복사했습니다` : '답변을 복사했습니다'),\n selected: '선택했습니다 — ⌘C / Ctrl+C로 복사',\n writing: '응답 작성 중',\n loading: '불러오는 중…',\n empty: '기록된 답변 내용이 없습니다',\n failed: '답변을 불러오지 못했습니다',\n retry: '다시 시도',\n under: '응답 1초 미만',\n secs: (s: number) => `응답 ${s}초`,\n mins: (m: number, s: number) => `응답 ${m}분 ${s}초`,\n hours: (h: number, m: number) => `응답 ${h}시간 ${m}분`,\n },\n};\ntype Strings = (typeof STRINGS)['en'];\nconst strings = (lang?: string): Strings => (lang === 'ko' ? STRINGS.ko : STRINGS.en);\n\nconst HTTP = /^https?:\\/\\//i;\n/** Placeholders (private-use code points, stripped from the input) for code spans and links while marks run. */\nconst CODE = '\\uE000';\nconst LINK = '\\uE001';\n\n/** `&`-entities back to text (the inverse of esc / escAttr). */\nfunction unesc(s: string): string {\n return s\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&');\n}\n\n/** Plain text of a rendered label. */\nconst textOf = (html: string) => unesc(html.replace(/<[^>]*>/g, ''));\n\n/** One link per URL: a trailing slash does not make another page. */\nexport function answerLinkKey(href: string): string {\n return String(href ?? '').replace(/\\/+$/, '');\n}\n\nfunction decode(s: string): string {\n try {\n return decodeURIComponent(s);\n } catch {\n return s;\n }\n}\n\n/** Parens inside a URL travel percent-encoded (a markdown link target stops at the first `)`), read back later. */\nconst encodeParens = (u: string) => u.replace(/\\(/g, '%28').replace(/\\)/g, '%29');\nconst decodeParens = (u: string) => u.replace(/%28/gi, '(').replace(/%29/gi, ')');\n\n/**\n * One stretch of prose (no code span, no markdown link): `<url>`, `<url|label>` and bare http(s) URLs become\n * `[label](url)`. A bare URL leaves trailing `.,;:!?*` and an unmatched `)` outside the link.\n */\nfunction linkifyText(s: string): string {\n return s.replace(\n /<(https?:\\/\\/[^\\s<>|]+)(?:\\|([^<>]*))?>|https?:\\/\\/[^\\s<>[\\]\"'`]+/gi,\n (m: string, url?: string, label?: string) => {\n if (url) {\n const l = String(label ?? '')\n .replace(/[[\\]]/g, '')\n .trim();\n return `[${l || url}](${encodeParens(url)})`;\n }\n let u = m;\n let tail = '';\n for (let guard = 0; guard < 8; guard++) {\n const p = u.match(/[.,;:!?*]+$/);\n if (p) {\n tail = p[0] + tail;\n u = u.slice(0, u.length - p[0].length);\n continue;\n }\n if (u.endsWith(')') && u.split('(').length < u.split(')').length) {\n tail = `)${tail}`;\n u = u.slice(0, -1);\n continue;\n }\n break;\n }\n return `[${u}](${encodeParens(u)})${tail}`;\n },\n );\n}\n\n/**\n * The dashboard’s `ansLinkify`: every link in the answer as a markdown link. Markdown links are protected first\n * (a label may be a code span, a target may hold balanced parens — encoded), code spans and fenced blocks (also\n * inside a `>` quote; a quoted fence ends with the quote) stay as written, only http(s) is linked.\n */\nexport function answerLinkify(raw: string): string {\n let fence = '';\n let fenceQuoted = false;\n return String(raw ?? '')\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n .map((line) => {\n const quoted = /^\\s{0,3}>/.test(line);\n const body = line.replace(/^(\\s{0,3}>\\s?)+/, '');\n if (fence && fenceQuoted && !quoted) fence = '';\n if (fence) {\n if (body.trim().startsWith(fence)) fence = '';\n return line;\n }\n const f = body.match(/^\\s{0,3}(`{3,}|~{3,})/);\n if (f) {\n fence = f[1];\n fenceQuoted = quoted;\n return line;\n }\n return line\n .split(/(!?\\[[^\\]]+\\]\\((?:[^()\\s]|\\([^()\\s]*\\))+\\)|`[^`]+`)/)\n .map((part, i) => {\n if (!(i % 2)) return linkifyText(part);\n if (part.startsWith('`')) return part;\n const at = part.indexOf('](');\n return `${part.slice(0, at + 2)}${encodeParens(part.slice(at + 2, -1))})`;\n })\n .join('');\n })\n .join('\\n');\n}\n\n/** Builds the markup for one http(s) link: `href` is plain text, `labelHtml` is already safe HTML. */\nexport type LinkRenderer = (href: string, labelHtml: string) => string;\n\nconst plainLink: LinkRenderer = (href, label) =>\n `<a href=\"${safeHref(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${label}</a>`;\n\n/** Bold, italic and strike on already-escaped text. */\nfunction marks(t: string): string {\n return t\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/(^|[^*\\w])\\*([^*\\s][^*]*?)\\*(?!\\w)/g, '$1<em>$2</em>')\n .replace(/~~([^~]+)~~/g, '<del>$1</del>');\n}\n\n/** Inline marks on one line: escape first, then code spans, links (http(s) only), bold, italic, strike. */\nfunction mdInline(s: string, link: LinkRenderer): string {\n const codes: string[] = [];\n const rawCodes: string[] = [];\n const links: string[] = [];\n // Code HTML goes back only into text/label positions; a URL gets the raw backtick text back instead, so a code\n // span inside a link target can never place markup (or a quote) into the href attribute.\n const restoreCodes = (x: string) => x.replace(/\\uE000(\\d+)\\uE000/g, (_m, i) => codes[Number(i)] ?? '');\n const restoreRaw = (x: string) => x.replace(/\\uE000(\\d+)\\uE000/g, (_m, i) => `\\`${rawCodes[Number(i)] ?? ''}\\``);\n let t = String(s ?? '')\n .replace(/[\\uE000\\uE001]/g, '')\n .replace(/`([^`]+)`/g, (_m, c: string) => {\n codes.push(`<code>${escAttr(c)}</code>`);\n rawCodes.push(c);\n return `${CODE}${codes.length - 1}${CODE}`;\n });\n t = esc(t);\n // Links become placeholders so emphasis never reaches into an href.\n t = t.replace(/!?\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g, (_m, label: string, href: string) => {\n const url = restoreRaw(unesc(href));\n if (!HTTP.test(url)) return label;\n links.push(link(url, restoreCodes(marks(label))));\n return `${LINK}${links.length - 1}${LINK}`;\n });\n t = marks(t);\n t = t.replace(/\\uE001(\\d+)\\uE001/g, (_m, i) => links[Number(i)] ?? '');\n return restoreCodes(t);\n}\n\n/**\n * Markdown as a document — the dashboard’s `renderMarkdownDoc`: headings, paragraphs, nested lists, tables,\n * fenced code, quotes, rules. Everything is escaped before it is marked up; only http(s) links are links.\n */\nexport function answerMarkdown(src: string, link: LinkRenderer = plainLink): string {\n const lines = String(src ?? '')\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n');\n const out: string[] = [];\n let para: string[] = [];\n let i = 0;\n const inline = (s: string) => mdInline(s, link);\n const flush = () => {\n if (para.length) out.push(`<p>${para.map(inline).join('<br>')}</p>`);\n para = [];\n };\n const cells = (l: string) =>\n l\n .trim()\n .replace(/^\\|/, '')\n .replace(/\\|$/, '')\n .split('|')\n .map((c) => c.trim());\n const listRe = /^(\\s*)([-*+]|\\d{1,3}[.)])\\s+(.*)$/;\n while (i < lines.length) {\n const line = lines[i];\n const fence = line.match(/^\\s{0,3}(`{3,}|~{3,})\\s*([\\w+#.-]*)/);\n if (fence) {\n flush();\n const code: string[] = [];\n i++;\n while (i < lines.length && !lines[i].trim().startsWith(fence[1])) {\n code.push(lines[i]);\n i++;\n }\n i++;\n out.push(\n `<pre class=\"md-code\"><code${fence[2] ? ` data-lang=\"${escAttr(fence[2])}\"` : ''}>${esc(code.join('\\n'))}</code></pre>`,\n );\n continue;\n }\n if (!line.trim()) {\n flush();\n i++;\n continue;\n }\n const h = line.match(/^\\s{0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/);\n if (h) {\n flush();\n out.push(`<h${h[1].length}>${inline(h[2])}</h${h[1].length}>`);\n i++;\n continue;\n }\n if (/^\\s{0,3}([-*_])(\\s*\\1){2,}\\s*$/.test(line)) {\n flush();\n out.push('<hr>');\n i++;\n continue;\n }\n if (/^\\s{0,3}>/.test(line)) {\n flush();\n const quote: string[] = [];\n while (i < lines.length && /^\\s{0,3}>/.test(lines[i])) {\n quote.push(lines[i].replace(/^\\s{0,3}>\\s?/, ''));\n i++;\n }\n out.push(`<blockquote>${answerMarkdown(quote.join('\\n'), link)}</blockquote>`);\n continue;\n }\n if (\n line.includes('|') &&\n i + 1 < lines.length &&\n /^\\s*\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?\\s*$/.test(lines[i + 1])\n ) {\n flush();\n const head = cells(line);\n const rows: string[][] = [];\n i += 2;\n while (i < lines.length && lines[i].includes('|') && lines[i].trim()) {\n rows.push(cells(lines[i]));\n i++;\n }\n out.push(\n `<div class=\"md-table\"><table><thead><tr>${head.map((c) => `<th>${inline(c)}</th>`).join('')}</tr></thead><tbody>${rows\n .map((r) => `<tr>${head.map((_c, k) => `<td>${inline(r[k] ?? '')}</td>`).join('')}</tr>`)\n .join('')}</tbody></table></div>`,\n );\n continue;\n }\n if (listRe.test(line)) {\n flush();\n const items: Array<{ indent: number; ordered: boolean; text: string }> = [];\n while (i < lines.length) {\n const m = lines[i].match(listRe);\n if (m) {\n items.push({ indent: m[1].replace(/\\t/g, ' ').length, ordered: /\\d/.test(m[2]), text: m[3] });\n i++;\n continue;\n }\n if (items.length && /^\\s{2,}\\S/.test(lines[i])) {\n items[items.length - 1].text += ` ${lines[i].trim()}`;\n i++;\n continue;\n }\n break;\n }\n let html = '';\n const stack: Array<{ indent: number; tag: string }> = [];\n for (const it of items) {\n while (stack.length > 1 && it.indent < stack[stack.length - 1].indent) html += `</li></${stack.pop()!.tag}>`;\n const top = stack[stack.length - 1];\n if (!top || it.indent > top.indent) {\n const tag = it.ordered ? 'ol' : 'ul';\n stack.push({ indent: it.indent, tag });\n html += `<${tag}><li>`;\n } else {\n html += '</li><li>';\n }\n html += inline(it.text);\n }\n while (stack.length) html += `</li></${stack.pop()!.tag}>`;\n out.push(html);\n continue;\n }\n para.push(line.trim());\n i++;\n }\n flush();\n return out.join('\\n');\n}\n\n/** What a link card is called — name (site), title and chip label (the dashboard’s `ansSourceOf`). */\nexport function answerLinkInfo(\n href: string,\n label = '',\n lang: AnswerLang = 'en',\n titles?: Record<string, string>,\n): Omit<AnswerLink, 'n'> {\n const t = strings(lang);\n const url = String(href ?? '');\n const m = url.match(/^https?:\\/\\/([^/?#]+)([^?#]*)(\\?[^#]*)?/i);\n const host = m ? m[1].toLowerCase().replace(/^www\\./, '') : url;\n const path = m ? m[2] : '';\n let name = host;\n let chip = host;\n let title = '';\n if (host === 'github.com') {\n name = 'GitHub';\n chip = 'GitHub';\n const seg = path.split('/').filter(Boolean);\n const repo = seg.slice(0, 2).join('/');\n const num = seg.length >= 4 && /^\\d+$/.test(seg[3]) ? seg[3] : '';\n if (num && seg[2] === 'pull') {\n title = `PR #${num} · ${repo}`;\n chip = `#${num}`;\n } else if (num && seg[2] === 'issues') {\n title = `${t.issue} #${num} · ${repo}`;\n chip = `#${num}`;\n } else if (seg.length >= 4 && seg[2] === 'commit' && /^[0-9a-f]{7,40}$/i.test(seg[3])) {\n chip = seg[3].slice(0, 7);\n title = `${t.commit} ${chip} · ${repo}`;\n } else title = repo ? repo + (seg.length > 2 ? ` · ${decode(seg.slice(2).join('/'))}` : '') : 'github.com';\n } else {\n title = decode((path + (m?.[3] ?? '')).replace(/\\/+$/, '')) || host;\n }\n if (label) title = label;\n if (titles) {\n const key = answerLinkKey(url);\n for (const [u, known] of Object.entries(titles)) if (known && answerLinkKey(u) === key) title = String(known);\n }\n return { href: url, name, title, chip };\n}\n\n/** A link chip, named after its card. */\nfunction citeHtml(link: AnswerLink, k: number, t: Strings): string {\n return `<a class=\"ans-cite\" data-n=\"${link.n}\" data-key=\"cite-${k}\" href=\"${safeHref(link.href)}\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"${escAttr(\n t.cite(link.n, link.title, link.name),\n )}\">${esc(link.chip)}</a>`;\n}\n\n/**\n * The answer as a document plus its links (the dashboard’s `renderAnswer`). Links are numbered by first appearance;\n * every link becomes a chip — a bare URL only the chip, a labelled link its words then the chip. A `[1]` the answer\n * writes without a URL stays text (no invented citations).\n */\nexport function renderAnswer(\n markdown: string,\n opts: { lang?: AnswerLang; titles?: Record<string, string> } = {},\n): AnswerRender {\n const t = strings(opts.lang);\n const md = answerLinkify(markdown);\n const byKey = new Map<string, { href: string; label: string; n: number }>();\n const found: Array<{ href: string; label: string; n: number }> = [];\n answerMarkdown(md, (url, labelHtml) => {\n const href = decodeParens(url);\n const text = textOf(labelHtml);\n const key = answerLinkKey(href);\n const label = text && text !== href ? text : '';\n const seen = byKey.get(key);\n if (!seen) {\n const f = { href, label, n: found.length + 1 };\n byKey.set(key, f);\n found.push(f);\n } else if (!seen.label && label) seen.label = label;\n return '';\n });\n const links = found.map((f) => ({ ...answerLinkInfo(f.href, f.label, opts.lang, opts.titles), n: f.n }));\n let k = 0;\n const html = answerMarkdown(md, (url, labelHtml) => {\n const href = decodeParens(url);\n const link = links[(byKey.get(answerLinkKey(href))?.n ?? 1) - 1];\n return (textOf(labelHtml) === href ? '' : labelHtml) + citeHtml(link, k++, t);\n });\n // Punctuation or a particle written right after a link stays on the chip's line: “(commit 3f9c2e1).”, “#999가”.\n return {\n html: html.replace(\n /(<a class=\"ans-cite\"[^>]*>[^<]*<\\/a>)([^\\s<]{1,12})(?![^\\s<])/g,\n '<span class=\"ans-nb\">$1$2</span>',\n ),\n links,\n };\n}\n\n/** What Copy puts on the clipboard: the answer as written (markdown kept) plus its numbered links. */\nexport function answerCopyText(markdown: string, links: AnswerLink[], lang: AnswerLang = 'en'): string {\n const text = String(markdown ?? '').trim();\n if (!links.length) return text;\n return `${text}\\n\\n${strings(lang).linksHead}\\n${links.map((l) => `[${l.n}] ${l.title} – ${l.href}`).join('\\n')}`;\n}\n\n/** “Responded in 4m 12s” / “응답 4분 12초”; empty for ≤ 0 or not a number. */\nexport function answerTookLabel(ms: number, lang: AnswerLang = 'en'): string {\n const t = strings(lang);\n const v = Number(ms);\n if (!Number.isFinite(v) || v <= 0) return '';\n if (v < 1000) return t.under;\n const sec = Math.floor(v / 1000);\n if (sec < 60) return t.secs(sec);\n const min = Math.floor(sec / 60);\n if (min < 60) return t.mins(min, sec % 60);\n return t.hours(Math.floor(min / 60), min % 60);\n}\n\nconst CHECK_SVG =\n '<svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3.5 8.5l3 3 6-7\"/></svg>';\n\n/**\n * `<ui-ai-answer>` — an assistant reply as a card: the markdown rendered safely, the links it contains as a\n * numbered row of link cards plus chips in the text (hover / focus lights the pair), Copy (`ui-copy`), the response\n * time, a writing state while it streams, and loading / empty / failed states.\n */\nexport class UiAiAnswer extends UiElement<AiAnswerData> {\n static override styles = [styles];\n static get observedAttributes() {\n return ['lang'];\n }\n\n #links: AnswerLink[] = [];\n #lit = 0;\n #copied = false;\n #status = '';\n #timer = 0;\n #shownMd = '';\n #glide: { left: number; until: number } | null = null;\n\n constructor() {\n super();\n this.root.addEventListener('mouseover', (e) => this.#hover(e, true));\n this.root.addEventListener('mouseout', (e) => this.#hover(e, false));\n this.root.addEventListener('focusin', (e) => this.#hover(e, true));\n this.root.addEventListener('focusout', (e) => this.#hover(e, false));\n this.root.addEventListener('click', (e) => {\n const t = e.target as Element | null;\n const a = t?.closest?.('a[data-n]');\n if (a) {\n this.#follow(e, a);\n return;\n }\n if (t?.closest?.('.ans-copy')) this.#copy();\n else if (t?.closest?.('.ans-retry')) this.emit('ui-retry', {});\n });\n }\n\n attributeChangedCallback(): void {\n this.requestRender();\n }\n\n override disconnectedCallback(): void {\n clearTimeout(this.#timer);\n this.#copied = false;\n this.#status = '';\n }\n\n /** The links of the last render, in card order. */\n get links(): AnswerLink[] {\n return this.#links.slice();\n }\n\n get #lang(): AnswerLang {\n return this.getAttribute('lang') === 'ko' ? 'ko' : 'en';\n }\n\n protected override update(): void {\n // A streaming re-render must not throw the link row back to its start.\n const left = this.$('.ans-srcs')?.scrollLeft ?? 0;\n super.update();\n const row = this.$('.ans-srcs');\n if (row && left) row.scrollLeft = left;\n }\n\n protected render(): string {\n const d = this.data;\n if (!d) {\n this.#links = [];\n return '';\n }\n const lang = this.#lang;\n const t = strings(lang);\n const md = String(d.markdown ?? '');\n const has = md.trim() !== '';\n const streaming = !!d.streaming;\n const error = d.error ? String(d.error) : '';\n const title = d.title ? `<h3 class=\"ans-title\">${esc(d.title)}</h3>` : '';\n const retry =\n error && d.canRetry\n ? `<button type=\"button\" class=\"ans-btn ans-retry\" data-key=\"retry\">${esc(t.retry)}</button>`\n : '';\n const failed = `<span class=\"ans-error\">${esc(t.failed)}${error ? ` (${esc(error)})` : ''}</span>`;\n\n // Entry motion runs once per answer — not for a streamed continuation, not for a re-render of the same text.\n const prev = this.#shownMd;\n this.#shownMd = md;\n const enter = has && !streaming && !(prev && md.startsWith(prev));\n\n if (!has && !streaming) {\n this.#links = [];\n const state = error\n ? `<div class=\"ans-state\">${failed}${retry}</div>`\n : d.loading\n ? `<p class=\"ans-state ans-loading\">${esc(t.loading)}</p>`\n : `<p class=\"ans-state ans-empty\">${esc(t.empty)}</p>`;\n return `<section class=\"ans-card\" aria-label=\"${escAttr(t.label(0))}\"${d.loading && !error ? ' aria-busy=\"true\"' : ''}>${title}${state}</section>`;\n }\n\n const a = has ? renderAnswer(md, { lang, titles: d.linkTitles }) : { html: '', links: [] };\n this.#links = a.links;\n const n = a.links.length;\n const srcs = n\n ? `<ol class=\"ans-srcs\" aria-label=\"${escAttr(t.list(n))}\">${a.links\n .map(\n (l, i) =>\n `<li style=\"--ans-i:${Math.min(i, 6)}\"><a class=\"ans-src\" data-n=\"${l.n}\" data-key=\"src-${l.n}\" href=\"${safeHref(\n l.href,\n )}\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"${escAttr(l.href)}\"><span class=\"ans-src-site\"><span class=\"ans-src-name\">${esc(\n l.name,\n )}</span><span class=\"ans-src-num\">${l.n}</span></span><span class=\"ans-src-title\">${esc(l.title)}</span></a></li>`,\n )\n .join('')}</ol>`\n : '';\n const body = has ? `<div class=\"ans-body md-doc\">${a.html}</div>` : '';\n let foot: string;\n if (streaming) {\n foot = `<div class=\"ans-foot is-writing\"><span class=\"ans-writing\"><span class=\"ans-shimmer\">${esc(\n t.writing,\n )}</span><i class=\"ans-dot\" aria-hidden=\"true\"></i></span></div>`;\n } else {\n const took = answerTookLabel(Number(d.responseMs), lang);\n const right = error ? `${failed}${retry}` : took ? `<span class=\"ans-took\">${esc(took)}</span>` : '';\n foot = `<div class=\"ans-foot\"><button type=\"button\" class=\"ans-btn ans-copy\" data-key=\"copy\"${\n this.#copied ? ' data-copied' : ''\n }>${this.#copyLabel(t)}</button><span class=\"ans-status\" role=\"status\" aria-live=\"polite\">${esc(\n this.#status,\n )}</span>${right ? `<span class=\"ans-end\">${right}</span>` : ''}</div>`;\n }\n return `<section class=\"ans-card${enter ? ' is-in' : ''}\" aria-labelledby=\"ans-label\"${streaming ? ' aria-busy=\"true\"' : ''}>${title}${srcs}<div class=\"ans-label\" id=\"ans-label\">${esc(\n t.label(n),\n )}</div>${body}${foot}</section>`;\n }\n\n #copyLabel(t: Strings): string {\n return this.#copied ? `${CHECK_SVG}<span>${esc(t.copied)}</span>` : `<span>${esc(t.copy)}</span>`;\n }\n\n protected override updated(): void {\n if (this.#lit && !this.#links.some((l) => l.n === this.#lit)) this.#lit = 0;\n this.#paintLit();\n }\n\n /** Hover / focus on a chip or card lights its number (D8); leaving clears. */\n #hover(e: Event, on: boolean): void {\n const el = (e.target as Element | null)?.closest?.('[data-n]');\n if (!el) return;\n const to = (e as MouseEvent | FocusEvent).relatedTarget as Element | null;\n if (!on && to?.closest?.('[data-n]') === el) return;\n const n = on ? Number(el.getAttribute('data-n')) : 0;\n if (n === this.#lit) return;\n this.#lit = n;\n this.#paintLit();\n if (n) this.#reveal(n);\n }\n\n #paintLit(): void {\n const key = String(this.#lit);\n for (const el of this.$$('[data-n]'))\n el.classList.toggle('is-lit', !!this.#lit && el.getAttribute('data-n') === key);\n }\n\n /** A lit card outside the row slides the row sideways — never the page (no scrollIntoView). */\n #reveal(n: number): void {\n const row = this.$('.ans-srcs');\n const src = row?.querySelector(`.ans-src[data-n=\"${n}\"]`);\n if (!row || !src || typeof row.scrollTo !== 'function') return;\n // Measure against where a glide still in flight will land, so fast Tab presses are not skipped.\n const now = Date.now();\n const at = this.#glide && now < this.#glide.until ? this.#glide.left : row.scrollLeft;\n const shift = row.scrollLeft - at;\n const rr = row.getBoundingClientRect();\n const sr = src.getBoundingClientRect();\n const l = sr.left + shift;\n const r = sr.right + shift;\n const target = r > rr.right ? at + (r - rr.right) + 16 : l < rr.left ? at - (rr.left - l) - 16 : null;\n if (target === null) return;\n const left = Math.max(0, Math.min(target, row.scrollWidth - row.clientWidth));\n const smooth = !prefersReducedMotion();\n this.#glide = smooth ? { left, until: now + GLIDE_MS } : null;\n row.scrollTo({ left, behavior: smooth ? 'smooth' : 'auto' });\n }\n\n #follow(e: Event, a: Element): void {\n const n = Number(a.getAttribute('data-n'));\n const link = this.#links[n - 1];\n if (!link) return;\n const from = a.classList.contains('ans-cite') ? 'chip' : 'card';\n if (!this.emit('ui-link', { url: link.href, index: n - 1, from })) e.preventDefault();\n }\n\n #copy(): void {\n const d = this.data;\n const md = String(d?.markdown ?? '');\n if (!d || d.streaming || !md.trim()) return;\n const text = answerCopyText(md, this.#links, this.#lang);\n if (!this.emit('ui-copy', { text })) return;\n const done = () => this.#flash(true, strings(this.#lang).copiedSay(this.#links.length));\n const fallback = () => (this.#viaTextarea(text) ? done() : this.#selectBody());\n try {\n const clip = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clip && typeof clip.writeText === 'function') clip.writeText(text).then(done, fallback);\n else fallback();\n } catch {\n fallback();\n }\n }\n\n /** Without the Clipboard API (plain http): copy the raw text from a hidden textarea. */\n #viaTextarea(text: string): boolean {\n if (typeof document.execCommand !== 'function') return false;\n try {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.setAttribute('readonly', '');\n ta.setAttribute('aria-hidden', 'true');\n ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0;pointer-events:none';\n this.root.append(ta);\n ta.select();\n const ok = document.execCommand('copy');\n ta.remove();\n this.$<HTMLButtonElement>('.ans-copy')?.focus({ preventScroll: true });\n return !!ok;\n } catch {\n return false;\n }\n }\n\n /** Last resort: select the rendered answer so ⌘C / Ctrl+C copies it. */\n #selectBody(): void {\n const body = this.$('.ans-body');\n try {\n const sel = typeof getSelection === 'function' ? getSelection() : null;\n if (body && sel) {\n const range = document.createRange();\n range.selectNodeContents(body);\n sel.removeAllRanges();\n sel.addRange(range);\n }\n } catch {\n /* selection is best effort */\n }\n this.#flash(false, strings(this.#lang).selected);\n }\n\n /** Copy feedback lives in place (no re-render) and clears after 1.5 s. */\n #flash(copied: boolean, message: string): void {\n this.#copied = copied;\n this.#status = message;\n this.#paintCopy();\n clearTimeout(this.#timer);\n this.#timer = window.setTimeout(() => {\n this.#copied = false;\n this.#status = '';\n this.#paintCopy();\n }, COPIED_MS);\n }\n\n #paintCopy(): void {\n const b = this.$('.ans-copy');\n if (b) {\n b.innerHTML = this.#copyLabel(strings(this.#lang));\n b.toggleAttribute('data-copied', this.#copied);\n }\n const s = this.$('.ans-status');\n if (s) s.textContent = this.#status;\n }\n}\n\ndefine('ui-ai-answer', UiAiAnswer);\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ui-ai-answer': UiAiAnswer;\n }\n}\n"],"names":[],"mappings":";AACO,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AC6CtB,MAAM,YAAY;AAElB,MAAM,WAAW;AAEjB,MAAM,SAAS,CAAC,GAAW,KAAa,SAAiB,GAAG,CAAC,IAAI,MAAM,IAAI,MAAM,IAAI;AAErF,MAAM,UAAU;AAAA,EACd,IAAI;AAAA,IACF,OAAO,CAAC,MAAe,IAAI,YAAY,OAAO,GAAG,QAAQ,OAAO,CAAC,KAAK;AAAA,IACtE,MAAM,CAAC,MAAc,GAAG,OAAO,GAAG,QAAQ,OAAO,CAAC;AAAA,IAClD,MAAM,CAAC,GAAW,OAAe,SAAiB,QAAQ,CAAC,KAAK,KAAK,KAAK,IAAI;AAAA,IAC9E,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW,CAAC,MAAe,IAAI,yBAAyB,OAAO,GAAG,QAAQ,OAAO,CAAC,KAAK;AAAA,IACvF,UAAU;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM,CAAC,MAAc,gBAAgB,CAAC;AAAA,IACtC,MAAM,CAAC,GAAW,MAAc,gBAAgB,CAAC,KAAK,CAAC;AAAA,IACvD,OAAO,CAAC,GAAW,MAAc,gBAAgB,CAAC,KAAK,CAAC;AAAA,EAAA;AAAA,EAE1D,IAAI;AAAA,IACF,OAAO,CAAC,MAAe,IAAI,WAAW,CAAC,KAAK;AAAA,IAC5C,MAAM,CAAC,MAAc,cAAc,CAAC;AAAA,IACpC,MAAM,CAAC,GAAW,OAAe,SAAiB,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI;AAAA,IAC5E,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW,CAAC,MAAe,IAAI,UAAU,CAAC,cAAc;AAAA,IACxD,UAAU;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM,CAAC,MAAc,MAAM,CAAC;AAAA,IAC5B,MAAM,CAAC,GAAW,MAAc,MAAM,CAAC,KAAK,CAAC;AAAA,IAC7C,OAAO,CAAC,GAAW,MAAc,MAAM,CAAC,MAAM,CAAC;AAAA,EAAA;AAEnD;AAEA,MAAM,UAAU,CAAC,SAA4B,SAAS,OAAO,QAAQ,KAAK,QAAQ;AAElF,MAAM,OAAO;AAEb,MAAM,OAAO;AACb,MAAM,OAAO;AAGb,SAAS,MAAM,GAAmB;AAChC,SAAO,EACJ,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,GAAG,EACpB,QAAQ,UAAU,GAAG;AAC1B;AAGA,MAAM,SAAS,CAAC,SAAiB,MAAM,KAAK,QAAQ,YAAY,EAAE,CAAC;AAG5D,SAAS,cAAc,MAAsB;AAClD,SAAO,OAAO,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;AAC9C;AAEA,SAAS,OAAO,GAAmB;AACjC,MAAI;AACF,WAAO,mBAAmB,CAAC;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,MAAM,eAAe,CAAC,MAAc,EAAE,QAAQ,OAAO,KAAK,EAAE,QAAQ,OAAO,KAAK;AAChF,MAAM,eAAe,CAAC,MAAc,EAAE,QAAQ,SAAS,GAAG,EAAE,QAAQ,SAAS,GAAG;AAMhF,SAAS,YAAY,GAAmB;AACtC,SAAO,EAAE;AAAA,IACP;AAAA,IACA,CAAC,GAAW,KAAc,UAAmB;AAC3C,UAAI,KAAK;AACP,cAAM,IAAI,OAAO,SAAS,EAAE,EACzB,QAAQ,UAAU,EAAE,EACpB,KAAA;AACH,eAAO,IAAI,KAAK,GAAG,KAAK,aAAa,GAAG,CAAC;AAAA,MAC3C;AACA,UAAI,IAAI;AACR,UAAI,OAAO;AACX,eAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS;AACtC,cAAM,IAAI,EAAE,MAAM,aAAa;AAC/B,YAAI,GAAG;AACL,iBAAO,EAAE,CAAC,IAAI;AACd,cAAI,EAAE,MAAM,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM;AACrC;AAAA,QACF;AACA,YAAI,EAAE,SAAS,GAAG,KAAK,EAAE,MAAM,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,EAAE,QAAQ;AAChE,iBAAO,IAAI,IAAI;AACf,cAAI,EAAE,MAAM,GAAG,EAAE;AACjB;AAAA,QACF;AACA;AAAA,MACF;AACA,aAAO,IAAI,CAAC,KAAK,aAAa,CAAC,CAAC,IAAI,IAAI;AAAA,IAC1C;AAAA,EAAA;AAEJ;AAOO,SAAS,cAAc,KAAqB;AACjD,MAAI,QAAQ;AACZ,MAAI,cAAc;AAClB,SAAO,OAAO,OAAO,EAAE,EACpB,QAAQ,UAAU,IAAI,EACtB,MAAM,IAAI,EACV,IAAI,CAAC,SAAS;AACb,UAAM,SAAS,YAAY,KAAK,IAAI;AACpC,UAAM,OAAO,KAAK,QAAQ,mBAAmB,EAAE;AAC/C,QAAI,SAAS,eAAe,CAAC,OAAQ,SAAQ;AAC7C,QAAI,OAAO;AACT,UAAI,KAAK,KAAA,EAAO,WAAW,KAAK,EAAG,SAAQ;AAC3C,aAAO;AAAA,IACT;AACA,UAAM,IAAI,KAAK,MAAM,uBAAuB;AAC5C,QAAI,GAAG;AACL,cAAQ,EAAE,CAAC;AACX,oBAAc;AACd,aAAO;AAAA,IACT;AACA,WAAO,KACJ,MAAM,qDAAqD,EAC3D,IAAI,CAAC,MAAM,MAAM;AAChB,UAAI,EAAE,IAAI,GAAI,QAAO,YAAY,IAAI;AACrC,UAAI,KAAK,WAAW,GAAG,EAAG,QAAO;AACjC,YAAM,KAAK,KAAK,QAAQ,IAAI;AAC5B,aAAO,GAAG,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC,GAAG,aAAa,KAAK,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC;AAAA,IACxE,CAAC,EACA,KAAK,EAAE;AAAA,EACZ,CAAC,EACA,KAAK,IAAI;AACd;AAKA,MAAM,YAA0B,CAAC,MAAM,UACrC,YAAY,SAAS,IAAI,CAAC,+CAA+C,KAAK;AAGhF,SAAS,MAAM,GAAmB;AAChC,SAAO,EACJ,QAAQ,oBAAoB,qBAAqB,EACjD,QAAQ,uCAAuC,eAAe,EAC9D,QAAQ,gBAAgB,eAAe;AAC5C;AAGA,SAAS,SAAS,GAAW,MAA4B;AACvD,QAAM,QAAkB,CAAA;AACxB,QAAM,WAAqB,CAAA;AAC3B,QAAM,QAAkB,CAAA;AAGxB,QAAM,eAAe,CAAC,MAAc,EAAE,QAAQ,sBAAsB,CAAC,IAAI,MAAM,MAAM,OAAO,CAAC,CAAC,KAAK,EAAE;AACrG,QAAM,aAAa,CAAC,MAAc,EAAE,QAAQ,sBAAsB,CAAC,IAAI,MAAM,KAAK,SAAS,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI;AAC/G,MAAI,IAAI,OAAO,KAAK,EAAE,EACnB,QAAQ,mBAAmB,EAAE,EAC7B,QAAQ,cAAc,CAAC,IAAI,MAAc;AACxC,UAAM,KAAK,SAAS,QAAQ,CAAC,CAAC,SAAS;AACvC,aAAS,KAAK,CAAC;AACf,WAAO,GAAG,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI;AAAA,EAC1C,CAAC;AACH,MAAI,IAAI,CAAC;AAET,MAAI,EAAE,QAAQ,gCAAgC,CAAC,IAAI,OAAe,SAAiB;AACjF,UAAM,MAAM,WAAW,MAAM,IAAI,CAAC;AAClC,QAAI,CAAC,KAAK,KAAK,GAAG,EAAG,QAAO;AAC5B,UAAM,KAAK,KAAK,KAAK,aAAa,MAAM,KAAK,CAAC,CAAC,CAAC;AAChD,WAAO,GAAG,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI;AAAA,EAC1C,CAAC;AACD,MAAI,MAAM,CAAC;AACX,MAAI,EAAE,QAAQ,sBAAsB,CAAC,IAAI,MAAM,MAAM,OAAO,CAAC,CAAC,KAAK,EAAE;AACrE,SAAO,aAAa,CAAC;AACvB;AAMO,SAAS,eAAe,KAAa,OAAqB,WAAmB;AAClF,QAAM,QAAQ,OAAO,OAAO,EAAE,EAC3B,QAAQ,UAAU,IAAI,EACtB,MAAM,IAAI;AACb,QAAM,MAAgB,CAAA;AACtB,MAAI,OAAiB,CAAA;AACrB,MAAI,IAAI;AACR,QAAM,SAAS,CAAC,MAAc,SAAS,GAAG,IAAI;AAC9C,QAAM,QAAQ,MAAM;AAClB,QAAI,KAAK,OAAQ,KAAI,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM;AACnE,WAAO,CAAA;AAAA,EACT;AACA,QAAM,QAAQ,CAAC,MACb,EACG,OACA,QAAQ,OAAO,EAAE,EACjB,QAAQ,OAAO,EAAE,EACjB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,MAAM;AACxB,QAAM,SAAS;AACf,SAAO,IAAI,MAAM,QAAQ;AACvB,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,QAAQ,KAAK,MAAM,qCAAqC;AAC9D,QAAI,OAAO;AACT,YAAA;AACA,YAAM,OAAiB,CAAA;AACvB;AACA,aAAO,IAAI,MAAM,UAAU,CAAC,MAAM,CAAC,EAAE,KAAA,EAAO,WAAW,MAAM,CAAC,CAAC,GAAG;AAChE,aAAK,KAAK,MAAM,CAAC,CAAC;AAClB;AAAA,MACF;AACA;AACA,UAAI;AAAA,QACF,6BAA6B,MAAM,CAAC,IAAI,eAAe,QAAQ,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC;AAAA,MAAA;AAE1G;AAAA,IACF;AACA,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAA;AACA;AACA;AAAA,IACF;AACA,UAAM,IAAI,KAAK,MAAM,mCAAmC;AACxD,QAAI,GAAG;AACL,YAAA;AACA,UAAI,KAAK,KAAK,EAAE,CAAC,EAAE,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG;AAC7D;AACA;AAAA,IACF;AACA,QAAI,iCAAiC,KAAK,IAAI,GAAG;AAC/C,YAAA;AACA,UAAI,KAAK,MAAM;AACf;AACA;AAAA,IACF;AACA,QAAI,YAAY,KAAK,IAAI,GAAG;AAC1B,YAAA;AACA,YAAM,QAAkB,CAAA;AACxB,aAAO,IAAI,MAAM,UAAU,YAAY,KAAK,MAAM,CAAC,CAAC,GAAG;AACrD,cAAM,KAAK,MAAM,CAAC,EAAE,QAAQ,gBAAgB,EAAE,CAAC;AAC/C;AAAA,MACF;AACA,UAAI,KAAK,eAAe,eAAe,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC,eAAe;AAC7E;AAAA,IACF;AACA,QACE,KAAK,SAAS,GAAG,KACjB,IAAI,IAAI,MAAM,UACd,oDAAoD,KAAK,MAAM,IAAI,CAAC,CAAC,GACrE;AACA,YAAA;AACA,YAAM,OAAO,MAAM,IAAI;AACvB,YAAM,OAAmB,CAAA;AACzB,WAAK;AACL,aAAO,IAAI,MAAM,UAAU,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,MAAM,CAAC,EAAE,QAAQ;AACpE,aAAK,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC;AACzB;AAAA,MACF;AACA,UAAI;AAAA,QACF,2CAA2C,KAAK,IAAI,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,uBAAuB,KAChH,IAAI,CAAC,MAAM,OAAO,KAAK,IAAI,CAAC,IAAI,MAAM,OAAO,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,OAAO,EACvF,KAAK,EAAE,CAAC;AAAA,MAAA;AAEb;AAAA,IACF;AACA,QAAI,OAAO,KAAK,IAAI,GAAG;AACrB,YAAA;AACA,YAAM,QAAmE,CAAA;AACzE,aAAO,IAAI,MAAM,QAAQ;AACvB,cAAM,IAAI,MAAM,CAAC,EAAE,MAAM,MAAM;AAC/B,YAAI,GAAG;AACL,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,OAAO,MAAM,EAAE,QAAQ,SAAS,KAAK,KAAK,EAAE,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG;AAC/F;AACA;AAAA,QACF;AACA,YAAI,MAAM,UAAU,YAAY,KAAK,MAAM,CAAC,CAAC,GAAG;AAC9C,gBAAM,MAAM,SAAS,CAAC,EAAE,QAAQ,IAAI,MAAM,CAAC,EAAE,KAAA,CAAM;AACnD;AACA;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,OAAO;AACX,YAAM,QAAgD,CAAA;AACtD,iBAAW,MAAM,OAAO;AACtB,eAAO,MAAM,SAAS,KAAK,GAAG,SAAS,MAAM,MAAM,SAAS,CAAC,EAAE,OAAQ,SAAQ,UAAU,MAAM,IAAA,EAAO,GAAG;AACzG,cAAM,MAAM,MAAM,MAAM,SAAS,CAAC;AAClC,YAAI,CAAC,OAAO,GAAG,SAAS,IAAI,QAAQ;AAClC,gBAAM,MAAM,GAAG,UAAU,OAAO;AAChC,gBAAM,KAAK,EAAE,QAAQ,GAAG,QAAQ,KAAK;AACrC,kBAAQ,IAAI,GAAG;AAAA,QACjB,OAAO;AACL,kBAAQ;AAAA,QACV;AACA,gBAAQ,OAAO,GAAG,IAAI;AAAA,MACxB;AACA,aAAO,MAAM,OAAQ,SAAQ,UAAU,MAAM,IAAA,EAAO,GAAG;AACvD,UAAI,KAAK,IAAI;AACb;AAAA,IACF;AACA,SAAK,KAAK,KAAK,MAAM;AACrB;AAAA,EACF;AACA,QAAA;AACA,SAAO,IAAI,KAAK,IAAI;AACtB;AAGO,SAAS,eACd,MACA,QAAQ,IACR,OAAmB,MACnB,QACuB;AACvB,QAAM,IAAI,QAAQ,IAAI;AACtB,QAAM,MAAM,OAAO,QAAQ,EAAE;AAC7B,QAAM,IAAI,IAAI,MAAM,0CAA0C;AAC9D,QAAM,OAAO,IAAI,EAAE,CAAC,EAAE,cAAc,QAAQ,UAAU,EAAE,IAAI;AAC5D,QAAM,OAAO,IAAI,EAAE,CAAC,IAAI;AACxB,MAAI,OAAO;AACX,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,SAAS,cAAc;AACzB,WAAO;AACP,WAAO;AACP,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAC1C,UAAM,OAAO,IAAI,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACrC,UAAM,MAAM,IAAI,UAAU,KAAK,QAAQ,KAAK,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI;AAC/D,QAAI,OAAO,IAAI,CAAC,MAAM,QAAQ;AAC5B,cAAQ,OAAO,GAAG,MAAM,IAAI;AAC5B,aAAO,IAAI,GAAG;AAAA,IAChB,WAAW,OAAO,IAAI,CAAC,MAAM,UAAU;AACrC,cAAQ,GAAG,EAAE,KAAK,KAAK,GAAG,MAAM,IAAI;AACpC,aAAO,IAAI,GAAG;AAAA,IAChB,WAAW,IAAI,UAAU,KAAK,IAAI,CAAC,MAAM,YAAY,oBAAoB,KAAK,IAAI,CAAC,CAAC,GAAG;AACrF,aAAO,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC;AACxB,cAAQ,GAAG,EAAE,MAAM,IAAI,IAAI,MAAM,IAAI;AAAA,IACvC,MAAO,SAAQ,OAAO,QAAQ,IAAI,SAAS,IAAI,MAAM,OAAO,IAAI,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,KAAK,MAAM;AAAA,EAChG,OAAO;AACL,YAAQ,QAAQ,QAAQ,IAAI,CAAC,KAAK,KAAK,QAAQ,QAAQ,EAAE,CAAC,KAAK;AAAA,EACjE;AACA,MAAI,MAAO,SAAQ;AACnB,MAAI,QAAQ;AACV,UAAM,MAAM,cAAc,GAAG;AAC7B,eAAW,CAAC,GAAG,KAAK,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,SAAS,cAAc,CAAC,MAAM,IAAK,SAAQ,OAAO,KAAK;AAAA,EAC9G;AACA,SAAO,EAAE,MAAM,KAAK,MAAM,OAAO,KAAA;AACnC;AAGA,SAAS,SAAS,MAAkB,GAAW,GAAoB;AACjE,SAAO,+BAA+B,KAAK,CAAC,oBAAoB,CAAC,WAAW,SAAS,KAAK,IAAI,CAAC,2DAA2D;AAAA,IACxJ,EAAE,KAAK,KAAK,GAAG,KAAK,OAAO,KAAK,IAAI;AAAA,EAAA,CACrC,KAAK,IAAI,KAAK,IAAI,CAAC;AACtB;AAOO,SAAS,aACd,UACA,OAA+D,IACjD;AACd,QAAM,IAAI,QAAQ,KAAK,IAAI;AAC3B,QAAM,KAAK,cAAc,QAAQ;AACjC,QAAM,4BAAY,IAAA;AAClB,QAAM,QAA2D,CAAA;AACjE,iBAAe,IAAI,CAAC,KAAK,cAAc;AACrC,UAAM,OAAO,aAAa,GAAG;AAC7B,UAAM,OAAO,OAAO,SAAS;AAC7B,UAAM,MAAM,cAAc,IAAI;AAC9B,UAAM,QAAQ,QAAQ,SAAS,OAAO,OAAO;AAC7C,UAAM,OAAO,MAAM,IAAI,GAAG;AAC1B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,EAAE,MAAM,OAAO,GAAG,MAAM,SAAS,EAAA;AAC3C,YAAM,IAAI,KAAK,CAAC;AAChB,YAAM,KAAK,CAAC;AAAA,IACd,WAAW,CAAC,KAAK,SAAS,YAAY,QAAQ;AAC9C,WAAO;AAAA,EACT,CAAC;AACD,QAAM,QAAQ,MAAM,IAAI,CAAC,OAAO,EAAE,GAAG,eAAe,EAAE,MAAM,EAAE,OAAO,KAAK,MAAM,KAAK,MAAM,GAAG,GAAG,EAAE,IAAI;AACvG,MAAI,IAAI;AACR,QAAM,OAAO,eAAe,IAAI,CAAC,KAAK,cAAc;AAClD,UAAM,OAAO,aAAa,GAAG;AAC7B,UAAM,OAAO,OAAO,MAAM,IAAI,cAAc,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC;AAC/D,YAAQ,OAAO,SAAS,MAAM,OAAO,KAAK,aAAa,SAAS,MAAM,KAAK,CAAC;AAAA,EAC9E,CAAC;AAED,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAGO,SAAS,eAAe,UAAkB,OAAqB,OAAmB,MAAc;AACrG,QAAM,OAAO,OAAO,YAAY,EAAE,EAAE,KAAA;AACpC,MAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,SAAO,GAAG,IAAI;AAAA;AAAA,EAAO,QAAQ,IAAI,EAAE,SAAS;AAAA,EAAK,MAAM,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC;AACjH;AAGO,SAAS,gBAAgB,IAAY,OAAmB,MAAc;AAC3E,QAAM,IAAI,QAAQ,IAAI;AACtB,QAAM,IAAI,OAAO,EAAE;AACnB,MAAI,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,EAAG,QAAO;AAC1C,MAAI,IAAI,IAAM,QAAO,EAAE;AACvB,QAAM,MAAM,KAAK,MAAM,IAAI,GAAI;AAC/B,MAAI,MAAM,GAAI,QAAO,EAAE,KAAK,GAAG;AAC/B,QAAM,MAAM,KAAK,MAAM,MAAM,EAAE;AAC/B,MAAI,MAAM,GAAI,QAAO,EAAE,KAAK,KAAK,MAAM,EAAE;AACzC,SAAO,EAAE,MAAM,KAAK,MAAM,MAAM,EAAE,GAAG,MAAM,EAAE;AAC/C;AAEA,MAAM,YACJ;AAOK,MAAM,mBAAmB,UAAwB;AAAA,EACtD,OAAgB,SAAS,CAAC,MAAM;AAAA,EAChC,WAAW,qBAAqB;AAC9B,WAAO,CAAC,MAAM;AAAA,EAChB;AAAA,EAEA,SAAuB,CAAA;AAAA,EACvB,OAAO;AAAA,EACP,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAiD;AAAA,EAEjD,cAAc;AACZ,UAAA;AACA,SAAK,KAAK,iBAAiB,aAAa,CAAC,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;AACnE,SAAK,KAAK,iBAAiB,YAAY,CAAC,MAAM,KAAK,OAAO,GAAG,KAAK,CAAC;AACnE,SAAK,KAAK,iBAAiB,WAAW,CAAC,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;AACjE,SAAK,KAAK,iBAAiB,YAAY,CAAC,MAAM,KAAK,OAAO,GAAG,KAAK,CAAC;AACnE,SAAK,KAAK,iBAAiB,SAAS,CAAC,MAAM;AACzC,YAAM,IAAI,EAAE;AACZ,YAAM,IAAI,GAAG,UAAU,WAAW;AAClC,UAAI,GAAG;AACL,aAAK,QAAQ,GAAG,CAAC;AACjB;AAAA,MACF;AACA,UAAI,GAAG,UAAU,WAAW,QAAQ,MAAA;AAAA,eAC3B,GAAG,UAAU,YAAY,QAAQ,KAAK,YAAY,EAAE;AAAA,IAC/D,CAAC;AAAA,EACH;AAAA,EAEA,2BAAiC;AAC/B,SAAK,cAAA;AAAA,EACP;AAAA,EAES,uBAA6B;AACpC,iBAAa,KAAK,MAAM;AACxB,SAAK,UAAU;AACf,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,QAAsB;AACxB,WAAO,KAAK,OAAO,MAAA;AAAA,EACrB;AAAA,EAEA,IAAI,QAAoB;AACtB,WAAO,KAAK,aAAa,MAAM,MAAM,OAAO,OAAO;AAAA,EACrD;AAAA,EAEmB,SAAe;AAEhC,UAAM,OAAO,KAAK,EAAE,WAAW,GAAG,cAAc;AAChD,UAAM,OAAA;AACN,UAAM,MAAM,KAAK,EAAE,WAAW;AAC9B,QAAI,OAAO,KAAM,KAAI,aAAa;AAAA,EACpC;AAAA,EAEU,SAAiB;AACzB,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,GAAG;AACN,WAAK,SAAS,CAAA;AACd,aAAO;AAAA,IACT;AACA,UAAM,OAAO,KAAK;AAClB,UAAM,IAAI,QAAQ,IAAI;AACtB,UAAM,KAAK,OAAO,EAAE,YAAY,EAAE;AAClC,UAAM,MAAM,GAAG,KAAA,MAAW;AAC1B,UAAM,YAAY,CAAC,CAAC,EAAE;AACtB,UAAM,QAAQ,EAAE,QAAQ,OAAO,EAAE,KAAK,IAAI;AAC1C,UAAM,QAAQ,EAAE,QAAQ,yBAAyB,IAAI,EAAE,KAAK,CAAC,UAAU;AACvE,UAAM,QACJ,SAAS,EAAE,WACP,oEAAoE,IAAI,EAAE,KAAK,CAAC,cAChF;AACN,UAAM,SAAS,2BAA2B,IAAI,EAAE,MAAM,CAAC,GAAG,QAAQ,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE;AAGzF,UAAM,OAAO,KAAK;AAClB,SAAK,WAAW;AAChB,UAAM,QAAQ,OAAO,CAAC,aAAa,EAAE,QAAQ,GAAG,WAAW,IAAI;AAE/D,QAAI,CAAC,OAAO,CAAC,WAAW;AACtB,WAAK,SAAS,CAAA;AACd,YAAM,QAAQ,QACV,0BAA0B,MAAM,GAAG,KAAK,WACxC,EAAE,UACA,oCAAoC,IAAI,EAAE,OAAO,CAAC,SAClD,kCAAkC,IAAI,EAAE,KAAK,CAAC;AACpD,aAAO,yCAAyC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,QAAQ,sBAAsB,EAAE,IAAI,KAAK,GAAG,KAAK;AAAA,IACxI;AAEA,UAAM,IAAI,MAAM,aAAa,IAAI,EAAE,MAAM,QAAQ,EAAE,WAAA,CAAY,IAAI,EAAE,MAAM,IAAI,OAAO,CAAA,EAAC;AACvF,SAAK,SAAS,EAAE;AAChB,UAAM,IAAI,EAAE,MAAM;AAClB,UAAM,OAAO,IACT,oCAAoC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,MAC1D;AAAA,MACC,CAAC,GAAG,MACF,sBAAsB,KAAK,IAAI,GAAG,CAAC,CAAC,gCAAgC,EAAE,CAAC,mBAAmB,EAAE,CAAC,WAAW;AAAA,QACtG,EAAE;AAAA,MAAA,CACH,sDAAsD,QAAQ,EAAE,IAAI,CAAC,2DAA2D;AAAA,QAC/H,EAAE;AAAA,MAAA,CACH,oCAAoC,EAAE,CAAC,6CAA6C,IAAI,EAAE,KAAK,CAAC;AAAA,IAAA,EAEpG,KAAK,EAAE,CAAC,UACX;AACJ,UAAM,OAAO,MAAM,gCAAgC,EAAE,IAAI,WAAW;AACpE,QAAI;AACJ,QAAI,WAAW;AACb,aAAO,wFAAwF;AAAA,QAC7F,EAAE;AAAA,MAAA,CACH;AAAA,IACH,OAAO;AACL,YAAM,OAAO,gBAAgB,OAAO,EAAE,UAAU,GAAG,IAAI;AACvD,YAAM,QAAQ,QAAQ,GAAG,MAAM,GAAG,KAAK,KAAK,OAAO,0BAA0B,IAAI,IAAI,CAAC,YAAY;AAClG,aAAO,uFACL,KAAK,UAAU,iBAAiB,EAClC,IAAI,KAAK,WAAW,CAAC,CAAC,sEAAsE;AAAA,QAC1F,KAAK;AAAA,MAAA,CACN,UAAU,QAAQ,yBAAyB,KAAK,YAAY,EAAE;AAAA,IACjE;AACA,WAAO,2BAA2B,QAAQ,WAAW,EAAE,gCAAgC,YAAY,sBAAsB,EAAE,IAAI,KAAK,GAAG,IAAI,yCAAyC;AAAA,MAClL,EAAE,MAAM,CAAC;AAAA,IAAA,CACV,SAAS,IAAI,GAAG,IAAI;AAAA,EACvB;AAAA,EAEA,WAAW,GAAoB;AAC7B,WAAO,KAAK,UAAU,GAAG,SAAS,SAAS,IAAI,EAAE,MAAM,CAAC,YAAY,SAAS,IAAI,EAAE,IAAI,CAAC;AAAA,EAC1F;AAAA,EAEmB,UAAgB;AACjC,QAAI,KAAK,QAAQ,CAAC,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,QAAQ,OAAO;AAC1E,SAAK,UAAA;AAAA,EACP;AAAA;AAAA,EAGA,OAAO,GAAU,IAAmB;AAClC,UAAM,KAAM,EAAE,QAA2B,UAAU,UAAU;AAC7D,QAAI,CAAC,GAAI;AACT,UAAM,KAAM,EAA8B;AAC1C,QAAI,CAAC,MAAM,IAAI,UAAU,UAAU,MAAM,GAAI;AAC7C,UAAM,IAAI,KAAK,OAAO,GAAG,aAAa,QAAQ,CAAC,IAAI;AACnD,QAAI,MAAM,KAAK,KAAM;AACrB,SAAK,OAAO;AACZ,SAAK,UAAA;AACL,QAAI,EAAG,MAAK,QAAQ,CAAC;AAAA,EACvB;AAAA,EAEA,YAAkB;AAChB,UAAM,MAAM,OAAO,KAAK,IAAI;AAC5B,eAAW,MAAM,KAAK,GAAG,UAAU;AACjC,SAAG,UAAU,OAAO,UAAU,CAAC,CAAC,KAAK,QAAQ,GAAG,aAAa,QAAQ,MAAM,GAAG;AAAA,EAClF;AAAA;AAAA,EAGA,QAAQ,GAAiB;AACvB,UAAM,MAAM,KAAK,EAAE,WAAW;AAC9B,UAAM,MAAM,KAAK,cAAc,oBAAoB,CAAC,IAAI;AACxD,QAAI,CAAC,OAAO,CAAC,OAAO,OAAO,IAAI,aAAa,WAAY;AAExD,UAAM,MAAM,KAAK,IAAA;AACjB,UAAM,KAAK,KAAK,UAAU,MAAM,KAAK,OAAO,QAAQ,KAAK,OAAO,OAAO,IAAI;AAC3E,UAAM,QAAQ,IAAI,aAAa;AAC/B,UAAM,KAAK,IAAI,sBAAA;AACf,UAAM,KAAK,IAAI,sBAAA;AACf,UAAM,IAAI,GAAG,OAAO;AACpB,UAAM,IAAI,GAAG,QAAQ;AACrB,UAAM,SAAS,IAAI,GAAG,QAAQ,MAAM,IAAI,GAAG,SAAS,KAAK,IAAI,GAAG,OAAO,MAAM,GAAG,OAAO,KAAK,KAAK;AACjG,QAAI,WAAW,KAAM;AACrB,UAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,IAAI,cAAc,IAAI,WAAW,CAAC;AAC5E,UAAM,SAAS,CAAC,qBAAA;AAChB,SAAK,SAAS,SAAS,EAAE,MAAM,OAAO,MAAM,aAAa;AACzD,QAAI,SAAS,EAAE,MAAM,UAAU,SAAS,WAAW,QAAQ;AAAA,EAC7D;AAAA,EAEA,QAAQ,GAAU,GAAkB;AAClC,UAAM,IAAI,OAAO,EAAE,aAAa,QAAQ,CAAC;AACzC,UAAM,OAAO,KAAK,OAAO,IAAI,CAAC;AAC9B,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,EAAE,UAAU,SAAS,UAAU,IAAI,SAAS;AACzD,QAAI,CAAC,KAAK,KAAK,WAAW,EAAE,KAAK,KAAK,MAAM,OAAO,IAAI,GAAG,KAAA,CAAM,KAAK,eAAA;AAAA,EACvE;AAAA,EAEA,QAAc;AACZ,UAAM,IAAI,KAAK;AACf,UAAM,KAAK,OAAO,GAAG,YAAY,EAAE;AACnC,QAAI,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,OAAQ;AACrC,UAAM,OAAO,eAAe,IAAI,KAAK,QAAQ,KAAK,KAAK;AACvD,QAAI,CAAC,KAAK,KAAK,WAAW,EAAE,KAAA,CAAM,EAAG;AACrC,UAAM,OAAO,MAAM,KAAK,OAAO,MAAM,QAAQ,KAAK,KAAK,EAAE,UAAU,KAAK,OAAO,MAAM,CAAC;AACtF,UAAM,WAAW,MAAO,KAAK,aAAa,IAAI,IAAI,KAAA,IAAS,KAAK,YAAA;AAChE,QAAI;AACF,YAAM,OAAO,OAAO,cAAc,cAAc,UAAU,YAAY;AACtE,UAAI,QAAQ,OAAO,KAAK,cAAc,WAAY,MAAK,UAAU,IAAI,EAAE,KAAK,MAAM,QAAQ;AAAA,UACrF,UAAA;AAAA,IACP,QAAQ;AACN,eAAA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,aAAa,MAAuB;AAClC,QAAI,OAAO,SAAS,gBAAgB,WAAY,QAAO;AACvD,QAAI;AACF,YAAM,KAAK,SAAS,cAAc,UAAU;AAC5C,SAAG,QAAQ;AACX,SAAG,aAAa,YAAY,EAAE;AAC9B,SAAG,aAAa,eAAe,MAAM;AACrC,SAAG,MAAM,UAAU;AACnB,WAAK,KAAK,OAAO,EAAE;AACnB,SAAG,OAAA;AACH,YAAM,KAAK,SAAS,YAAY,MAAM;AACtC,SAAG,OAAA;AACH,WAAK,EAAqB,WAAW,GAAG,MAAM,EAAE,eAAe,MAAM;AACrE,aAAO,CAAC,CAAC;AAAA,IACX,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,cAAoB;AAClB,UAAM,OAAO,KAAK,EAAE,WAAW;AAC/B,QAAI;AACF,YAAM,MAAM,OAAO,iBAAiB,aAAa,iBAAiB;AAClE,UAAI,QAAQ,KAAK;AACf,cAAM,QAAQ,SAAS,YAAA;AACvB,cAAM,mBAAmB,IAAI;AAC7B,YAAI,gBAAA;AACJ,YAAI,SAAS,KAAK;AAAA,MACpB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,SAAK,OAAO,OAAO,QAAQ,KAAK,KAAK,EAAE,QAAQ;AAAA,EACjD;AAAA;AAAA,EAGA,OAAO,QAAiB,SAAuB;AAC7C,SAAK,UAAU;AACf,SAAK,UAAU;AACf,SAAK,WAAA;AACL,iBAAa,KAAK,MAAM;AACxB,SAAK,SAAS,OAAO,WAAW,MAAM;AACpC,WAAK,UAAU;AACf,WAAK,UAAU;AACf,WAAK,WAAA;AAAA,IACP,GAAG,SAAS;AAAA,EACd;AAAA,EAEA,aAAmB;AACjB,UAAM,IAAI,KAAK,EAAE,WAAW;AAC5B,QAAI,GAAG;AACL,QAAE,YAAY,KAAK,WAAW,QAAQ,KAAK,KAAK,CAAC;AACjD,QAAE,gBAAgB,eAAe,KAAK,OAAO;AAAA,IAC/C;AACA,UAAM,IAAI,KAAK,EAAE,aAAa;AAC9B,QAAI,EAAG,GAAE,cAAc,KAAK;AAAA,EAC9B;AACF;AAEA,OAAO,gBAAgB,UAAU;"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { UiElement } from '../../core/element.js';
|
|
2
|
+
/** Where a run stands. `waiting` = it needs the person; `idle` = parked (sleeping, archived, never started). */
|
|
3
|
+
export type InboxStatus = 'waiting' | 'running' | 'done' | 'failed' | 'idle';
|
|
4
|
+
/** A button in an open card. A string `disabled` keeps it visible and says why it cannot be pressed. */
|
|
5
|
+
export interface InboxAction {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
disabled?: boolean | string;
|
|
9
|
+
}
|
|
10
|
+
/** One line of an open card's step list. Plain strings are steps; the last one is drawn as the current step. */
|
|
11
|
+
export interface InboxDetail {
|
|
12
|
+
label: string;
|
|
13
|
+
/** Right-aligned meta (time, tool calls…). */
|
|
14
|
+
meta?: string;
|
|
15
|
+
state?: 'done' | 'current' | 'pending';
|
|
16
|
+
}
|
|
17
|
+
export interface InboxCard {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
/** One or two sentences: why the card is here / where it stands (clamped to two lines). */
|
|
21
|
+
summary?: string;
|
|
22
|
+
status: InboxStatus;
|
|
23
|
+
/** Replaces the status word (e.g. `Sleeping`, `Archived`, `Retrying`). */
|
|
24
|
+
statusText?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Epoch ms the current status began. `running`: the live clock runs from here (plus `elapsedMs`);
|
|
27
|
+
* every other status shows it as recency (`· 6m ago`). Without it no clock and no age are shown.
|
|
28
|
+
*/
|
|
29
|
+
since?: number;
|
|
30
|
+
/** Active time in ms: `done` shows `Done in …`; `running` adds it to the live clock (time before `since`). */
|
|
31
|
+
elapsedMs?: number;
|
|
32
|
+
/** The task in progress: `#id label · 3m 12s` (live from `startedAt`, else the static `elapsedMs`). */
|
|
33
|
+
task?: {
|
|
34
|
+
id?: string;
|
|
35
|
+
label: string;
|
|
36
|
+
startedAt?: number;
|
|
37
|
+
elapsedMs?: number;
|
|
38
|
+
};
|
|
39
|
+
/** Sub-agent counts. Dots are drawn from these counts only; the rest of `total` is drawn hollow (unknown). */
|
|
40
|
+
agents?: {
|
|
41
|
+
total: number;
|
|
42
|
+
running?: number;
|
|
43
|
+
done?: number;
|
|
44
|
+
failed?: number;
|
|
45
|
+
};
|
|
46
|
+
/** Total tokens (all kinds). Shown only when given. */
|
|
47
|
+
tokens?: number;
|
|
48
|
+
/** API-equivalent cost in USD. Shown only when given. */
|
|
49
|
+
costUsd?: number;
|
|
50
|
+
/** Names of the files the run made (newest first). Up to four chips + `+N`. */
|
|
51
|
+
artifacts?: string[];
|
|
52
|
+
/** Tasks / steps done of total, shown in the card foot. */
|
|
53
|
+
steps?: {
|
|
54
|
+
done: number;
|
|
55
|
+
total: number;
|
|
56
|
+
};
|
|
57
|
+
/** Context window used, 0–100. Above 60 warns, above 80 is danger. */
|
|
58
|
+
contextPct?: number;
|
|
59
|
+
/** Model (agent) name, shown at the left of the open card's button row. */
|
|
60
|
+
model?: string;
|
|
61
|
+
/** The one decision this viewer can make here — the card's single ink button. */
|
|
62
|
+
primary?: InboxAction;
|
|
63
|
+
/** Secondary buttons (outline), left of the primary. */
|
|
64
|
+
actions?: InboxAction[];
|
|
65
|
+
/** The open card's step list, oldest first. Over five, the earliest fold into `+N earlier`. */
|
|
66
|
+
details?: Array<string | InboxDetail>;
|
|
67
|
+
}
|
|
68
|
+
export interface InboxColumn {
|
|
69
|
+
id: string;
|
|
70
|
+
label: string;
|
|
71
|
+
/** Shown while the column draws no cards. Default `Nothing here`. */
|
|
72
|
+
emptyText?: string;
|
|
73
|
+
/** Cards in display order. A card id listed twice renders once (first wins). */
|
|
74
|
+
cards: InboxCard[];
|
|
75
|
+
}
|
|
76
|
+
export interface AgentInboxData {
|
|
77
|
+
columns: InboxColumn[];
|
|
78
|
+
/** Card ids that start open the first time they appear. Afterwards the person's toggles rule. */
|
|
79
|
+
expanded?: string[];
|
|
80
|
+
/** Label of the open card's Open button (`ui-open`). `false` hides it. Default `Open` / `열기`. */
|
|
81
|
+
openLabel?: string | false;
|
|
82
|
+
}
|
|
83
|
+
export type InboxTone = 'wait' | 'run' | 'done' | 'fail' | 'rest';
|
|
84
|
+
/** A card held in the column it is drawn in while its new status shows. */
|
|
85
|
+
export interface LaneHold {
|
|
86
|
+
from: string;
|
|
87
|
+
to: string;
|
|
88
|
+
until: number;
|
|
89
|
+
}
|
|
90
|
+
/** How long a card whose column changed shows its new status in place before it moves. */
|
|
91
|
+
export declare const BOARD_HOLD_MS = 900;
|
|
92
|
+
/** A burst of announcements within this window is said once. */
|
|
93
|
+
export declare const ANNOUNCE_MS = 1000;
|
|
94
|
+
export type ArtifactKind = 'image' | 'html' | 'markdown' | 'pdf' | 'audio' | 'video' | 'text' | 'file';
|
|
95
|
+
/** The status a card is drawn with (anything unknown reads as idle). */
|
|
96
|
+
export declare function statusOf(card: Pick<InboxCard, 'status'>): InboxStatus;
|
|
97
|
+
/** Status → tag tone: waiting amber, running blue, done green, failed red, idle grey. */
|
|
98
|
+
export declare function statusTone(status: InboxStatus): InboxTone;
|
|
99
|
+
/** Glyph kind from a file name's extension. */
|
|
100
|
+
export declare function artifactKind(name: string): ArtifactKind;
|
|
101
|
+
/**
|
|
102
|
+
* Sub-agent dots from counts: running, failed, done, then unknown (hollow) — at most `max`, the rest as `more`.
|
|
103
|
+
* Only what the counts say is drawn; nothing is invented.
|
|
104
|
+
*/
|
|
105
|
+
export declare function agentDots(a: InboxCard['agents'], max?: number): {
|
|
106
|
+
dots: Array<'running' | 'failed' | 'done' | 'unknown'>;
|
|
107
|
+
more: number;
|
|
108
|
+
};
|
|
109
|
+
/** How many cards need the person (waiting + failed) and how many run — what the live region says. */
|
|
110
|
+
export declare function boardCounts(columns: InboxColumn[]): {
|
|
111
|
+
needs: number;
|
|
112
|
+
running: number;
|
|
113
|
+
};
|
|
114
|
+
/** The reference board's three columns from a flat list: Needs you (waiting, failed) · Running · Done (done, idle). */
|
|
115
|
+
export declare function columnsByStatus(cards: InboxCard[], lang?: 'en' | 'ko'): InboxColumn[];
|
|
116
|
+
/**
|
|
117
|
+
* The column each card is drawn in this render. `want` = the column its data puts it in; `drawn` = the column
|
|
118
|
+
* it is drawn in now (cards on screen only); `holds` = the running holds. A card whose column changed stays where
|
|
119
|
+
* it is drawn until its deadline (`now + BOARD_HOLD_MS` when the change is first seen). Data that returns to the
|
|
120
|
+
* drawn column drops the hold; a new target keeps the first deadline. `on = false` (reduced motion, first paint,
|
|
121
|
+
* hidden tab) moves every card at once. Returns the columns, the holds still running and the earliest deadline.
|
|
122
|
+
*/
|
|
123
|
+
export declare function holdLanes(want: Record<string, string>, drawn: Record<string, string>, holds: Record<string, LaneHold>, now: number, on: boolean): {
|
|
124
|
+
lanes: Record<string, string>;
|
|
125
|
+
holds: Record<string, LaneHold>;
|
|
126
|
+
wakeAt: number;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Put held cards back where they are drawn: each leaves its new column and returns to `lanes[id]` at its place
|
|
130
|
+
* in that column's previous order (`drawnOrder`). Held cards are taken out first and put back in ascending
|
|
131
|
+
* place, so cards leaving one column together keep their order there. A hold whose column is gone is dropped.
|
|
132
|
+
*/
|
|
133
|
+
export declare function placeHolds(slots: Record<string, string[]>, lanes: Record<string, string>, drawnOrder: Record<string, string[]>): {
|
|
134
|
+
slots: Record<string, string[]>;
|
|
135
|
+
dropped: string[];
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* `<ui-agent-inbox>` — a calm kanban of agent runs: labelled columns with counts, cards with a tinted status tag
|
|
139
|
+
* (Waiting for you · Running · Done · Failed), the task in progress, sub-agents, tokens · cost, files, and a card
|
|
140
|
+
* that opens in place to its steps and one ink decision. A card whose column changes shows its new status where it
|
|
141
|
+
* is for 900 ms, then glides over (FLIP). Cards keep their DOM node for their whole life.
|
|
142
|
+
*/
|
|
143
|
+
export declare class UiAgentInbox extends UiElement<AgentInboxData> {
|
|
144
|
+
#private;
|
|
145
|
+
static styles: string[];
|
|
146
|
+
static get observedAttributes(): string[];
|
|
147
|
+
attributeChangedCallback(): void;
|
|
148
|
+
constructor();
|
|
149
|
+
disconnectedCallback(): void;
|
|
150
|
+
/** The board is reconciled by `update()` (keyed, persistent nodes); there is no single markup string. */
|
|
151
|
+
protected render(): string;
|
|
152
|
+
protected update(): void;
|
|
153
|
+
}
|
|
154
|
+
declare global {
|
|
155
|
+
interface HTMLElementTagNameMap {
|
|
156
|
+
'ui-agent-inbox': UiAgentInbox;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { AgentInboxData, ArtifactKind, InboxAction, InboxCard, InboxColumn, InboxDetail, InboxStatus, InboxTone, LaneHold, } from './agent-inbox.js';
|
|
2
|
+
export { ANNOUNCE_MS, agentDots, artifactKind, BOARD_HOLD_MS, boardCounts, columnsByStatus, holdLanes, placeHolds, statusOf, statusTone, UiAgentInbox, } from './agent-inbox.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ported from the soma-work dashboard board (`.kanban` / `.card` / BOARD RESPONSIVE, .prd/10 §3–§5 and
|
|
3
|
+
* .prd/affectui/re-agent-inbox.md D1–D10). The status-tag tints are derived from the status tokens with
|
|
4
|
+
* color-mix, so one rule holds in light and dark (text on its tint ≥ 4.5:1 in both).
|
|
5
|
+
*/
|
|
6
|
+
export declare const styles = "\n:host {\n --ib-run-bg: color-mix(in srgb, var(--st-run) 16%, var(--ib-card));\n --ib-run-fg: color-mix(in srgb, var(--st-run) 70%, var(--ib-text));\n --ib-wait-bg: color-mix(in srgb, var(--st-wait) 16%, var(--ib-card));\n --ib-wait-fg: color-mix(in srgb, var(--st-wait) 70%, var(--ib-text));\n --ib-done-bg: color-mix(in srgb, var(--st-done) 16%, var(--ib-card));\n --ib-done-fg: color-mix(in srgb, var(--st-done) 70%, var(--ib-text));\n --ib-fail-bg: color-mix(in srgb, var(--st-fail) 16%, var(--ib-card));\n --ib-fail-fg: color-mix(in srgb, var(--st-fail-text) 70%, var(--ib-text));\n --ib-rest-bg: var(--ib-pill);\n --ib-rest-fg: var(--ib-text-2);\n}\n.ib { container: ib / inline-size; position: relative; }\n\n/* Board = equal columns. No overflow clip on columns: a card flying between columns stays visible. */\n.ib-board { display: grid; grid-template-columns: repeat(var(--ib-cols, 3), minmax(0, 1fr)); gap: 12px; align-items: start; }\n.ib-col { position: relative; background: var(--ib-col); border-radius: 14px; padding: 2px 4px 4px; min-width: 0; display: flex; flex-direction: column; }\n.ib-col-head { display: flex; align-items: center; gap: 8px; padding: 10px 8px 8px 10px; min-height: 40px; }\n.ib-col-spark { color: var(--ib-spark); display: inline-flex; flex-shrink: 0; }\n.ib-col-spark svg, .ib-spark svg { width: 14px; height: 14px; }\n.ib-col-name { flex: 1; min-width: 0; margin: 0; text-align: center; font-size: 12px; font-weight: 500; line-height: 1.4; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ib-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n:host([lang=\"ko\"]) .ib-col-name { letter-spacing: 0.04em; }\n.ib-count { flex-shrink: 0; background: var(--ib-pill); color: var(--ib-text-2); border-radius: 6px; padding: 1px 7px; font-size: 12px; font-weight: 500; min-width: 22px; text-align: center; font-variant-numeric: tabular-nums; }\n.ib-cards { position: relative; display: flex; flex-direction: column; gap: 8px; list-style: none; margin: 0; padding: 0; }\n.ib-empty { margin: 0; text-align: center; color: var(--ib-muted); font-size: 13px; padding: 14px 8px 18px; }\n.ib-empty[hidden] { display: none; }\n\n/* List = the same columns stacked full width (same DOM). Forced with view=\"list\"; automatic below 640px. */\n:host([view=\"list\"]) .ib-board { grid-template-columns: minmax(0, 1fr); }\n:host([view=\"list\"]) .ib-col-name { flex: 0 1 auto; text-align: left; }\n:host([view=\"list\"]) .ib-summary { -webkit-line-clamp: 1; }\n:host([view=\"list\"]) .ib-empty { padding: 10px 8px 16px; }\n/* Narrow board: the columns scroll sideways inside the board (the page never scrolls sideways). */\n@container ib (max-width: 839px) {\n :host(:not([view=\"list\"])) .ib-board {\n grid-template-columns: repeat(var(--ib-cols, 3), minmax(min(300px, 84cqi), 1fr));\n overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; padding-bottom: 8px;\n }\n :host(:not([view=\"list\"])) .ib-col { scroll-snap-align: start; }\n}\n@container ib (max-width: 639px) {\n :host(:not([view=\"board\"])) .ib-board { grid-template-columns: minmax(0, 1fr); overflow-x: visible; scroll-snap-type: none; padding-bottom: 0; }\n :host(:not([view=\"board\"])) .ib-col-name { flex: 0 1 auto; text-align: left; }\n :host(:not([view=\"board\"])) .ib-summary { -webkit-line-clamp: 1; }\n :host(:not([view=\"board\"])) .ib-empty { padding: 10px 8px 16px; }\n}\n\n/* \u2500\u2500 CARD \u2014 collapsed face + in-place expansion \u2500\u2500 */\n.ib-card {\n position: relative; background: var(--ib-card); border: 1px solid var(--ib-card-border); border-radius: 12px;\n box-shadow: var(--ib-shadow); color: var(--ib-text); transition: border-color 150ms ease, box-shadow 150ms ease;\n --st-color: var(--ib-muted);\n}\n.ib-card[data-status=\"waiting\"] { --st-color: var(--st-wait); }\n.ib-card[data-status=\"running\"] { --st-color: var(--st-run); }\n.ib-card[data-status=\"done\"] { --st-color: var(--st-done); }\n.ib-card[data-status=\"failed\"] { --st-color: var(--st-fail); }\n/* Parked cards are dimmed with colour, not opacity (opacity would fight the enter / exit fades). */\n.ib-card[data-status=\"idle\"] { background: var(--ib-card-dim); box-shadow: none; }\n.ib-card[data-status=\"idle\"] .ib-title { color: var(--ib-text-2); font-weight: 500; }\n.ib-card[data-status=\"idle\"] .ib-summary { color: var(--ib-muted); }\n.ib-card.is-flying { z-index: 5; box-shadow: var(--ib-shadow-raised); }\n.ib-card.is-exiting { position: absolute; z-index: 1; pointer-events: none; }\n.ib-face { display: block; padding: 12px 14px; border-radius: 11px; container: ibface / inline-size; }\n.ib-face[role=\"button\"] { cursor: pointer; }\n.ib-face:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }\n.ib-head { display: flex; align-items: center; gap: 8px; min-width: 0; }\n.ib-spark { flex-shrink: 0; display: inline-flex; color: var(--st-color); }\n.ib-title { flex: 1 1 auto; font-size: 15px; font-weight: 600; line-height: 1.3; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ib-summary {\n margin-top: 6px; font-size: 13.5px; line-height: 1.45; color: var(--ib-text-2);\n display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: anywhere;\n}\n/* The task in progress. */\n.ib-task { margin-top: 8px; display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 13px; color: var(--ib-text); background: var(--ib-pill); border-radius: 8px; padding: 5px 9px; }\n.ib-task-id { flex-shrink: 0; color: var(--ib-muted); font-variant-numeric: tabular-nums; font-size: 12px; }\n.ib-task-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ib-task-time { flex-shrink: 0; color: var(--ib-text-2); font-variant-numeric: tabular-nums; }\n/* Sub-agents: status dots + counts. */\n.ib-agents { margin-top: 8px; display: flex; align-items: center; gap: 7px; min-width: 0; font-size: 12.5px; color: var(--ib-text-2); }\n.ib-agents > svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--ib-muted); }\n.ib-dots { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; }\n.ib-adot { width: 7px; height: 7px; border-radius: 50%; background: var(--ib-muted); display: inline-block; }\n.ib-adot.is-running { background: var(--st-run); animation: ib-pulse 1.6s ease-in-out infinite; }\n.ib-adot.is-done { background: var(--st-done); }\n.ib-adot.is-failed { background: var(--st-fail); }\n.ib-adot.is-unknown { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ib-muted); }\n.ib-dots-more { font-size: 11px; color: var(--ib-muted); margin-left: 2px; font-variant-numeric: tabular-nums; }\n.ib-agents-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }\n.ib-agents-text b { color: var(--ib-text); font-weight: 600; }\n.ib-agents-text .ib-fail { color: var(--st-fail-text); font-weight: 600; }\n/* Tokens \u00B7 cost. */\n.ib-usage { margin-top: 8px; display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; font-size: 13px; color: var(--ib-text-2); font-variant-numeric: tabular-nums; }\n.ib-cnt { color: var(--ib-text); font-weight: 600; }\n.ib-unit { margin-left: 3px; color: var(--ib-muted); }\n.ib-sep { color: var(--ib-muted); }\n/* Files the run made: count, newest chips, +N. */\n.ib-arts { display: flex; align-items: center; gap: 5px; margin-top: 8px; min-width: 0; overflow: hidden; }\n.ib-arts-n { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; font-size: 11px; font-weight: 600; color: var(--ib-text-2); font-variant-numeric: tabular-nums; }\n.ib-glyph { width: 14px; height: 14px; flex-shrink: 0; }\n.ib-chip {\n display: inline-flex; align-items: center; gap: 5px; flex: 1 1 0; min-width: 0; max-width: 160px; height: 24px; padding: 0 8px;\n border: 1px solid var(--ib-card-border); border-radius: 999px; background: var(--ib-pill); color: var(--ib-text); font-size: 11.5px; line-height: 1;\n --tint: var(--text-secondary);\n}\n.ib-chip .ib-glyph { color: var(--tint); }\n.ib-chip.is-image { --tint: var(--green); }\n.ib-chip.is-html { --tint: var(--orange); }\n.ib-chip.is-audio { --tint: var(--purple); }\n.ib-chip.is-video, .ib-chip.is-pdf { --tint: var(--red); }\n.ib-chip.is-markdown { --tint: var(--accent); }\n.ib-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ib-arts-more { flex-shrink: 0; font-size: 11px; color: var(--ib-muted); }\n/* Foot: the status tag, then the metrics on the right (a narrow foot wraps them below). */\n.ib-foot { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: 13px; color: var(--ib-text-2); min-width: 0; }\n.ib-tag {\n display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; flex: 0 1 auto; height: 24px; padding: 0 10px 0 8px;\n border-radius: 999px; font-size: 12.5px; font-weight: 500; white-space: nowrap; overflow: hidden; font-variant-numeric: tabular-nums;\n}\n.ib-tag[data-tone=\"run\"] { background: var(--ib-run-bg); color: var(--ib-run-fg); }\n.ib-tag[data-tone=\"wait\"] { background: var(--ib-wait-bg); color: var(--ib-wait-fg); }\n.ib-tag[data-tone=\"done\"] { background: var(--ib-done-bg); color: var(--ib-done-fg); }\n.ib-tag[data-tone=\"fail\"] { background: var(--ib-fail-bg); color: var(--ib-fail-fg); }\n.ib-tag[data-tone=\"rest\"] { background: var(--ib-rest-bg); color: var(--ib-rest-fg); }\n.ib-tag-text { overflow: hidden; text-overflow: ellipsis; }\n.ib-tag-icon { flex-shrink: 0; display: inline-flex; align-items: center; color: currentColor; }\n.ib-tag-icon svg { width: 12px; height: 12px; }\n.ib-ago { color: inherit; }\n.ib-ago::before { content: \"\u00B7\"; margin: 0 6px; }\n/* The state word (\"Running \u00B7 \", \"Done in \") goes only when the face has 300px of content or less. */\n@container ibface (max-width: 300px) { .ib-tag .ib-pre { display: none; } }\n.ib-dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: inline-block; }\n.ib-dot.is-hollow { background: transparent; box-shadow: inset 0 0 0 1.5px currentColor; }\n/* The running ring: its track follows the tag colour, so it reads on the tint. */\n.ib-spin {\n width: 12px; height: 12px; border-radius: 50%; display: inline-block;\n border: 2px solid color-mix(in srgb, currentColor 25%, transparent); border-top-color: currentColor; border-right-color: currentColor;\n animation: ib-spin 0.9s linear infinite;\n}\n.ib-metrics { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; row-gap: 4px; max-width: 100%; color: var(--ib-muted); flex-shrink: 0; margin-left: auto; font-variant-numeric: tabular-nums; }\n.ib-metric { display: inline-flex; align-items: center; gap: 5px; padding: 0 9px; }\n.ib-metric + .ib-metric { border-left: 1px solid var(--ib-card-border); }\n.ib-metric:last-child { padding-right: 0; }\n.ib-metric svg { width: 13px; height: 13px; }\n.ib-metric.is-warn svg { color: var(--st-wait); }\n.ib-metric.is-danger { color: var(--st-fail-text); font-weight: 600; }\n\n/* Expansion \u2014 grid row 0fr \u2192 1fr; cards below ride the animated row. */\n.ib-expand { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-expand) var(--ease-out); }\n.ib-card.is-open .ib-expand { grid-template-rows: 1fr; }\n.ib-expand-inner { min-height: 0; overflow: hidden; opacity: 0; transition: opacity var(--dur-expand) var(--ease-out); }\n.ib-card.is-open .ib-expand-inner { opacity: 1; }\n.ib-card.no-motion .ib-expand, .ib-card.no-motion .ib-expand-inner { transition: none; }\n.ib-body { margin: 0 14px; padding: 12px 0 14px; border-top: 1px solid var(--ib-card-border); font-size: 13px; color: var(--ib-text-2); }\n/* Steps: dot + connector, the current step's dot in ink. */\n.ib-more { font-size: 12px; color: var(--ib-muted); padding: 0 0 6px 16px; }\n.ib-steps { list-style: none; margin: 0; padding: 0; }\n.ib-step { position: relative; display: flex; gap: 10px; padding: 0 0 8px 16px; line-height: 1.4; color: var(--ib-text-2); }\n.ib-step::before { content: ''; position: absolute; left: 2px; top: 6px; width: 6px; height: 6px; border-radius: 50%; background: var(--ib-muted); opacity: 0.55; }\n.ib-step::after { content: ''; position: absolute; left: 4.5px; top: 15px; bottom: 1px; width: 1px; background: var(--ib-card-border); }\n.ib-step:last-child { padding-bottom: 0; }\n.ib-step:last-child::after { display: none; }\n.ib-step.is-current { color: var(--ib-text); }\n.ib-step.is-current::before { background: var(--ib-text); opacity: 1; }\n.ib-step.is-pending { color: var(--ib-muted); }\n.ib-step-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.ib-step-meta { flex-shrink: 0; color: var(--ib-muted); font-size: 12.5px; font-variant-numeric: tabular-nums; }\n/* Button row: model on the left, secondary outline buttons, one ink primary last. */\n.ib-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--ib-card-border); }\n.ib-bar:first-child { margin-top: 0; padding-top: 0; border-top: 0; }\n.ib-model { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ib-muted); font-size: 13px; }\n.ib-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; margin-left: auto; }\n.ib-btn {\n display: inline-flex; align-items: center; justify-content: center; background: var(--ib-card); border: 1px solid var(--ib-card-border);\n border-radius: 8px; color: var(--ib-text); font: inherit; font-size: 13px; font-weight: 500; padding: 5px 12px; min-height: 32px;\n white-space: nowrap; cursor: pointer;\n transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease, opacity 150ms ease, transform var(--dur-press) var(--ease-out);\n}\n.ib-btn.is-primary { background: var(--ib-text); border-color: var(--ib-text); color: var(--ib-card); }\n.ib-btn[aria-disabled=\"true\"] { opacity: 0.5; cursor: not-allowed; }\n.ib-btn:active:not([aria-disabled=\"true\"]):not(:focus-visible) { transform: scale(0.97); }\n\n@keyframes ib-spin { to { transform: rotate(360deg); } }\n@keyframes ib-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }\n\n/* Hover \u2014 pointer devices only; colour / border, never movement. */\n@media (hover: hover) and (pointer: fine) {\n .ib-card:hover { border-color: var(--ib-muted); }\n .ib-card[data-status=\"idle\"]:hover { border-color: var(--ib-card-border); }\n .ib-btn:hover:not([aria-disabled=\"true\"]) { border-color: var(--ib-muted); }\n .ib-btn.is-primary:hover:not([aria-disabled=\"true\"]) { border-color: var(--ib-text); opacity: 0.88; }\n}\n/* 44px targets on touch and narrow screens. */\n@media (pointer: coarse), (max-width: 639px) {\n .ib-btn { min-height: 44px; padding: 8px 14px; }\n}\n/* Reduced motion: the global rule only shortens durations, so infinite loops stop here; the ring stays whole. */\n@media (prefers-reduced-motion: reduce) {\n .ib-spin { animation: none; border-color: currentColor; }\n .ib-adot.is-running { animation: none; }\n}\n";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { UiElement } from '../../core/element.js';
|
|
2
|
+
import { type AgentQueueData, type AqLane, type AqProvider } from './logic.js';
|
|
3
|
+
import { type Strings } from './strings.js';
|
|
4
|
+
/** `ui-job-open` detail: a queue chip, a job's past-runs button or its run strip was activated. */
|
|
5
|
+
export interface AqJobOpenDetail {
|
|
6
|
+
jobId: string;
|
|
7
|
+
}
|
|
8
|
+
/** `ui-account-open` detail: an account mark was opened. Cancel the event to suppress the built-in card. */
|
|
9
|
+
export interface AqAccountOpenDetail {
|
|
10
|
+
providerId: string;
|
|
11
|
+
accountId: string;
|
|
12
|
+
}
|
|
13
|
+
/** One polite sentence per data update for capacity (all limited > available again > under 20%), or ''. */
|
|
14
|
+
export declare function capacityNews(prev: AqProvider[] | undefined, next: AqProvider[] | undefined, nowMs: number, t?: Strings): string;
|
|
15
|
+
/** One polite sentence per data update for the lane (a newer failed run > a job that starts waiting), or ''. */
|
|
16
|
+
export declare function laneNews(prev: AqLane | undefined, next: AqLane | undefined, t?: Strings): string;
|
|
17
|
+
/**
|
|
18
|
+
* `<ui-agent-queue>` — capacity and the scheduled-job lane, the soma-work dashboard's adaptation of affectUI Agent
|
|
19
|
+
* Queue. Provider capacity reads accounts as workers (who is usable, who is limited and when it returns); the lane
|
|
20
|
+
* reads scheduled jobs as a queue (what runs next, how many run now, 24 hours of run records). Countdowns tick once
|
|
21
|
+
* a second in place; state changes are spoken once in a polite live region.
|
|
22
|
+
*/
|
|
23
|
+
export declare class UiAgentQueue extends UiElement<AgentQueueData> {
|
|
24
|
+
#private;
|
|
25
|
+
static styles: string[];
|
|
26
|
+
static get observedAttributes(): string[];
|
|
27
|
+
constructor();
|
|
28
|
+
get data(): AgentQueueData | undefined;
|
|
29
|
+
set data(value: AgentQueueData | undefined);
|
|
30
|
+
attributeChangedCallback(): void;
|
|
31
|
+
connectedCallback(): void;
|
|
32
|
+
disconnectedCallback(): void;
|
|
33
|
+
protected render(): string;
|
|
34
|
+
protected update(): void;
|
|
35
|
+
protected updated(): void;
|
|
36
|
+
}
|
|
37
|
+
declare global {
|
|
38
|
+
interface HTMLElementTagNameMap {
|
|
39
|
+
'ui-agent-queue': UiAgentQueue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { AqAccountOpenDetail, AqJobOpenDetail } from './agent-queue.js';
|
|
2
|
+
export { capacityNews, laneNews, UiAgentQueue } from './agent-queue.js';
|
|
3
|
+
export type { AgentQueueData, AqAccount, AqAccountState, AqJob, AqLane, AqPoolState, AqProvider, AqReason, AqRefill, AqRun, AqRunStatus, DayRuns, RunBin, TimelineMark, TimeUnit, } from './logic.js';
|
|
4
|
+
export { accountState, advanceProvider, axisPos, dayRuns, emptyReturnAt, firstReturn, fmtCount, fmtPct, LOW_PCT, nextRefill, niceMax, normalize, orderJobs, plain, poolState, runBins, runKind, scheduleLabel, timelineMarks, toMs, upNext, } from './logic.js';
|
|
5
|
+
export type { TimeKind } from './strings.js';
|
|
6
|
+
export { timeText } from './strings.js';
|