.\");\n } else if (m.type === \"claudeLoginStatus\") {\n if (m.status === \"done\") {\n S.cliReport = Object.assign({}, S.cliReport || {}, { claude: \"ok\" });\n renderNotice(\"Claude sign-in complete.\");\n if (S.cli === \"claude\") vscode.postMessage({ type: \"platform\", cli: \"claude\" });\n } else {\n renderNotice(\"Claude sign-in failed: \" + (m.error || \"Login was not completed.\"));\n }\n } else if (m.type === \"codexLoginChallenge\") {\n renderNotice(\"Codex sign-in opened. If the browser did not open, visit:\\n\" + m.url + \"\\nEnter this one-time code on the page: \" + m.code);\n } else if (m.type === \"codexLoginStatus\") {\n if (m.status === \"done\") {\n S.cliReport = Object.assign({}, S.cliReport || {}, { codex: \"ok\" });\n renderNotice(\"Codex sign-in complete.\");\n if (S.cli === \"codex\") vscode.postMessage({ type: \"platform\", cli: \"codex\" });\n } else {\n renderNotice(\"Codex sign-in failed: \" + (m.error || \"Login was not completed.\"));\n }\n } else if (m.type === \"toast\") renderNotice(m.text || \"\");\n else if (m.type === \"openUrl\" && m.url) window.open(m.url, \"_blank\", \"noopener,noreferrer\");\n else if (m.type === \"storage\") {\n renderStorage(m.info, m.options);\n renderWalletStorage();\n } else if (m.type === \"cloudSync\") renderCloudSync(m.status);\n else if (m.type === \"wallet\") setWallet(m.address);\n else if (m.type === \"page\") {\n hideLoading();\n S.hasMore = m.hasMore;\n S.pageCursor = m.cursor;\n maybeFillOlder();\n } else if (m.type === \"older\") {\n prependOlder(m.messages || []);\n S.hasMore = m.hasMore;\n S.pageCursor = m.cursor;\n S.loadingOlder = false;\n maybeFillOlder();\n } else if (m.type === \"approval\") renderApproval(m.req);\n else if (m.type === \"approvalDismiss\") dismissApproval(m.id);\n });\n vscode.postMessage({ type: \"ready\" });\n vscode.postMessage({ type: \"wallet\" });\n vscode.postMessage({ type: \"getBalance\" });\n})();\n";
+export const PANEL_SCRIPT = "\"use strict\";\n(() => {\n // src/chat/ui/panel/host.ts\n var vscode = typeof acquireVsCodeApi === \"function\" ? acquireVsCodeApi() : (() => {\n let clientId = null;\n const outbox = [];\n function post(s) {\n fetch(\"/rpc?client=\" + encodeURIComponent(clientId), {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: s\n }).catch(() => {\n });\n }\n function open(url) {\n const es = new EventSource(url);\n es.addEventListener(\"client\", (e) => {\n const id = JSON.parse(e.data).client;\n if (clientId === null) {\n clientId = id;\n es.close();\n open(\"/events?client=\" + encodeURIComponent(id));\n for (const m of outbox.splice(0)) post(m);\n }\n });\n es.onmessage = (e) => window.dispatchEvent(new MessageEvent(\"message\", { data: JSON.parse(e.data) }));\n }\n open(\"/events\");\n return {\n postMessage: (obj) => {\n const s = JSON.stringify(obj);\n if (clientId !== null) post(s);\n else outbox.push(s);\n }\n };\n })();\n function uiGet(k) {\n try {\n const s = vscode.getState && vscode.getState();\n return s ? s[k] : void 0;\n } catch (e) {\n return void 0;\n }\n }\n function uiSet(k, v) {\n try {\n if (!vscode.setState) return;\n const s = vscode.getState && vscode.getState() || {};\n s[k] = v;\n vscode.setState(s);\n } catch (e) {\n }\n }\n\n // src/chat/ui/panel/state.ts\n var S = {\n // ---- shell.ts ----\n stick: true,\n hasNew: false,\n // new content arrived while scrolled up → light the button in the engine accent\n streaming: null,\n // bubble currently being streamed into\n streamRaf: 0,\n // rAF handle: coalesces live-markdown renders to one paint/frame\n streamTimer: 0,\n // trailing timer that defers the next render to the cadence boundary\n lastStreamRender: 0,\n // when the live bubble was last re-rendered\n // ---- sessions.ts ----\n allSessions: [],\n // last sessions payload from extension\n cloudListState: \"none\",\n // cloud health of that payload's union (ok/reauth/transient/none)\n activeId: null,\n expanded: false,\n // \"모두 보기\" toggled?\n // ---- slash.ts ----\n slashIdx: 0,\n suppressSlash: false,\n activeSlashMatches: [],\n // ---- engine.ts ----\n cli: \"claude\",\n cliReport: null,\n // ---- turns.ts ----\n tailTurn: null,\n // the turn new (bottom) replies attach to\n headTurn: null,\n // the turn prepended (top, older) replies attach to\n openBash: null,\n // a bash card awaiting its output (claude's split result)\n // ---- composer.ts ----\n typingEl: null,\n busy: false,\n attached: [],\n pendingSentImages: [],\n growRaf: 0,\n // ---- storage.ts ----\n storageOptions: [],\n cloudConnected: false,\n cloudKind: \"\",\n // which backend is connected, so a reauth prompt can reconnect the right one\n // ---- publish.ts ----\n pubKind: \"skill\",\n pubBodyConfirmed: false,\n // ---- feed.ts ----\n currentProfileWallet: null,\n agentsTab: \"feed\",\n // persists across view switches, same as profileTab\n feedSort: \"active\",\n // ACTIVE = lastActivityTime, LATEST = createdAt\n feedPosts: null,\n // null = never loaded (skeletons), [] = settled empty\n currentFeedPost: null,\n // the post open in the reader\n feedLastSage: false,\n // whether the comment in flight was sage (skips the fresh re-read)\n feedReplyTo: null,\n // id of the comment being answered (mobile CommentThreadList idiom)\n fabModalEl: null,\n // ---- quotes.ts ----\n quoteCards: {},\n // postId -> [placeholder card elements] awaiting the reply\n quoteSeen: {},\n // postId -> true once carded in the current render pass\n quoteSlots: 0,\n // unique refs carded in the current render pass\n // ---- agents.ts ----\n lastAgents: [],\n recentlyPosted: [],\n // [{ wallet, note, ts }]\n pendingPost: null,\n // { wallet, text, gitLink, self } — stashed at submit for agentNoteResult\n postFeedback: null,\n // { wallet, text, ok, ts } — survives the immediate profile re-render\n profileTab: \"agent\",\n // ---- profile.ts ----\n repoModalEl: null,\n // ---- skills.ts ----\n ownedSkills: [],\n skillMints: {},\n // slug/name -> mint for bought NFT skills (reuse market detail)\n workflowMintSet: /* @__PURE__ */ new Set(),\n // owned mints that are workflows (excluded from the workflow picker)\n disposedMints: {},\n // slug -> mint for un-pinned skills (greyed in the panel)\n disposedMintSet: /* @__PURE__ */ new Set(),\n // the mints above, for isDisposed() detail checks\n // ---- market.ts ----\n lastMarketResults: [],\n // last search results, kept to re-render on owned-list change\n currentKind: \"skill\",\n // active tab: Skills | Workflows\n currentDetail: null,\n // { id, type } of the open detail — for comments refresh\n hideOwnedMarket: void 0,\n mktSort: void 0,\n skillModalOpen: false,\n skillModalBuyBtn: null,\n skillModalName: null,\n skillModalMint: null,\n skillDocOpen: false,\n currentDetailName: null,\n detailBuyBtn: null,\n // ---- overlays.ts ----\n celebTimer: null,\n solLamports: null,\n // last known balance (lamports); null = unknown/failed\n buyErrTimer: null,\n dasReady: false,\n rpcNetwork: \"devnet\",\n // drives explorer-link cluster (set from rpcStatus)\n pick: 0,\n // rotate so the verb feels alive\n actTimer: null,\n firingTimer: null,\n // ---- wallet.ts ----\n myWalletAddress: null,\n // tracked so openWalletPage can showProfile(own)\n // ---- paging.ts ----\n pageCursor: null,\n // cursor for the NEXT older page (null = none / at start)\n hasMore: false,\n // older pages exist?\n loadingOlder: false,\n lastOlderCursor: null\n // last cursor we asked for; never auto-request the same one twice\n };\n\n // src/chat/ui/panel/dom.ts\n var log = document.getElementById(\"log\");\n var mainEl = document.getElementById(\"main\");\n var loadingEl = document.getElementById(\"loading\");\n var jumpBtn = document.getElementById(\"jumpBtn\");\n var input = document.getElementById(\"input\");\n var sessList = document.getElementById(\"sessList\");\n var showAll = document.getElementById(\"showAll\");\n var emptyEl = document.getElementById(\"empty\");\n var modelBtn = document.getElementById(\"modelBtn\");\n var modelMenu = document.getElementById(\"modelMenu\");\n var modelLabel = document.getElementById(\"modelLabel\");\n var composer = document.getElementById(\"composer\");\n var modeBtn = document.getElementById(\"modeBtn\");\n var modeMenu = document.getElementById(\"modeMenu\");\n var modeLabel = document.getElementById(\"modeLabel\");\n var modeEffortTag = document.getElementById(\"modeEffortTag\");\n var ctxMeter = document.getElementById(\"ctxMeter\");\n var approvalDock = document.getElementById(\"approvalDock\");\n var engineBanner = document.getElementById(\"engineBanner\");\n var slashMenu = document.getElementById(\"slashMenu\");\n var tabs = Array.from(document.querySelectorAll(\".etab\"));\n\n // src/chat/ui/panel/markdown.ts\n var MD_OK = !!(window.marked && window.DOMPurify);\n if (MD_OK) window.marked.setOptions({ breaks: true, gfm: true });\n var COPY_ICON = '';\n var CHECK_ICON = '';\n function copyText(text, done) {\n if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done, () => {\n });\n else {\n const ta = document.createElement(\"textarea\");\n ta.value = text;\n document.body.appendChild(ta);\n ta.select();\n try {\n document.execCommand(\"copy\");\n done();\n } catch (e2) {\n }\n document.body.removeChild(ta);\n }\n }\n function addPreCopyButtons(root) {\n const pres = root.querySelectorAll(\"pre\");\n for (let i = 0; i < pres.length; i++) {\n const pre = pres[i];\n if (!pre.parentNode || pre.parentNode.classList && pre.parentNode.classList.contains(\"preWrap\")) continue;\n const wrap = document.createElement(\"div\");\n wrap.className = \"preWrap\";\n pre.parentNode.insertBefore(wrap, pre);\n wrap.appendChild(pre);\n const btn = document.createElement(\"button\");\n btn.className = \"preCopy\";\n btn.title = \"Copy code\";\n btn.innerHTML = COPY_ICON;\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const code = pre.querySelector(\"code\");\n copyText((code || pre).textContent || \"\", () => {\n btn.classList.add(\"done\");\n btn.innerHTML = CHECK_ICON;\n setTimeout(() => {\n btn.classList.remove(\"done\");\n btn.innerHTML = COPY_ICON;\n }, 1200);\n });\n });\n wrap.appendChild(btn);\n }\n }\n function addInlineCopy(root) {\n const codes = root.querySelectorAll(\"code\");\n for (let i = 0; i < codes.length; i++) {\n const code = codes[i];\n if (code.closest(\"pre\") || code.closest(\"a\") || code.classList.contains(\"inlineCopy\")) continue;\n code.classList.add(\"inlineCopy\");\n code.title = \"Click to copy\";\n code.addEventListener(\"click\", () => {\n const sel = window.getSelection();\n if (sel && !sel.isCollapsed) return;\n copyText(code.textContent || \"\", () => {\n code.classList.add(\"done\");\n setTimeout(() => code.classList.remove(\"done\"), 1200);\n });\n });\n }\n }\n function renderMd(el, text) {\n el.dataset.md = text;\n el._mdDone = null;\n el._mdTail = null;\n el._mdTailRaw = null;\n if (!MD_OK) {\n el.textContent = text;\n return;\n }\n try {\n el.innerHTML = window.DOMPurify.sanitize(window.marked.parse(text));\n addPreCopyButtons(el);\n addInlineCopy(el);\n } catch (e) {\n el.textContent = text;\n }\n }\n function renderMdStreaming(el, text) {\n el.dataset.md = text;\n if (!MD_OK) {\n el.textContent = text;\n return;\n }\n try {\n const tokens = window.marked.lexer(text);\n const stable = Math.max(0, tokens.length - 1);\n let done = el._mdDone;\n let ok = !!done && done.length <= stable;\n for (let i = 0; ok && i < done.length; i++) if (tokens[i].raw !== done[i]) ok = false;\n if (!ok) {\n el.innerHTML = \"\";\n el._mdDone = done = [];\n el._mdTail = null;\n el._mdTailRaw = null;\n }\n if (!el._mdTail) {\n el._mdTail = document.createElement(\"div\");\n el._mdTail.className = \"mdChunk\";\n el._mdTailRaw = null;\n el.appendChild(el._mdTail);\n }\n for (let i = done.length; i < stable; i++) {\n const div = document.createElement(\"div\");\n div.className = \"mdChunk\";\n div.innerHTML = window.DOMPurify.sanitize(window.marked.parse(tokens[i].raw));\n addPreCopyButtons(div);\n addInlineCopy(div);\n el.insertBefore(div, el._mdTail);\n done.push(tokens[i].raw);\n }\n const tailRaw = stable < tokens.length ? tokens[stable].raw : \"\";\n if (tailRaw !== el._mdTailRaw) {\n el._mdTail.innerHTML = window.DOMPurify.sanitize(window.marked.parse(tailRaw));\n addPreCopyButtons(el._mdTail);\n addInlineCopy(el._mdTail);\n el._mdTailRaw = tailRaw;\n }\n } catch (e) {\n el.textContent = text;\n el._mdDone = null;\n el._mdTail = null;\n el._mdTailRaw = null;\n }\n }\n function escapeHtml(s) {\n const d = document.createElement(\"div\");\n d.textContent = s;\n return d.innerHTML;\n }\n\n // src/runtime/engineInstall.ts\n var ENGINE_INSTALL_COMMAND = {\n codex: \"npm install -g @openai/codex\",\n claude: \"npm install -g @anthropic-ai/claude-code\"\n };\n var ENGINE_UPDATE_COMMAND = {\n codex: \"npm install -g @openai/codex@latest\",\n claude: \"npm install -g @anthropic-ai/claude-code@latest\"\n };\n var CODEX_UPDATE_COMMAND = ENGINE_UPDATE_COMMAND.codex;\n\n // src/chat/slashCommands.ts\n var both = [\"claude\", \"codex\"];\n var CHAT_SLASH_COMMANDS = [\n { name: \"login\", desc: \"sign in to the active engine\", engines: both, args: \"[code]\" },\n { name: \"logout\", desc: \"sign out of the active engine\", engines: both },\n { name: \"new\", desc: \"start a fresh chat\", engines: both },\n { name: \"clear\", desc: \"clear context and start fresh\", engines: both, args: \"[name]\" },\n { name: \"compact\", desc: \"summarize the conversation to free context\", engines: both, args: \"[instructions]\" },\n { name: \"status\", desc: \"show session, model, context, and account status\", engines: both },\n { name: \"resume\", desc: \"resume a saved conversation\", engines: both, args: \"[session]\" },\n { name: \"diff\", desc: \"show working-tree changes\", engines: [\"claude\", \"codex\"] },\n { name: \"permissions\", desc: \"manage tool permission rules\", engines: both, args: \"[mode]\" },\n { name: \"init\", desc: \"create project instructions\", engines: both },\n { name: \"review\", desc: \"review local changes or a PR\", engines: both, args: \"[target]\" },\n { name: \"mcp\", desc: \"manage or list MCP servers\", engines: both, args: \"[subcommand]\" },\n { name: \"skills\", desc: \"list or refresh available skills\", engines: both },\n { name: \"usage\", desc: \"show usage, limits, or token activity\", engines: both },\n { name: \"cost\", desc: \"alias for usage/status\", engines: both },\n { name: \"stats\", desc: \"alias for usage\", engines: [\"claude\"] },\n { name: \"copy\", desc: \"copy the latest assistant response\", engines: both, args: \"[N]\" },\n { name: \"engine\", desc: \"switch AgentNet engine\", engines: both, args: \"claude|codex\" },\n { name: \"model\", desc: \"switch the active model\", engines: both, args: \"[model]\" },\n { name: \"mode\", desc: \"change AgentNet permission mode\", engines: both, args: \"[mode]\" },\n { name: \"effort\", desc: \"set reasoning effort\", engines: both, args: \"[level]\" },\n { name: \"help\", desc: \"show slash commands\", engines: both },\n // Claude Code commands and bundled skills.\n { name: \"add-dir\", desc: \"grant file access to another directory\", engines: [\"claude\"], args: \"\" },\n { name: \"advisor\", desc: \"enable or disable advisor model guidance\", engines: [\"claude\"], args: \"[model|off]\" },\n { name: \"agents\", desc: \"manage agent configurations and background agents\", engines: [\"claude\"] },\n { name: \"autofix-pr\", desc: \"start a cloud session to fix PR feedback\", engines: [\"claude\"], args: \"[prompt]\" },\n { name: \"background\", desc: \"detach the current session to keep running\", engines: [\"claude\"], args: \"[prompt]\" },\n { name: \"batch\", desc: \"orchestrate large changes across worktrees\", engines: [\"claude\"], args: \"\" },\n { name: \"branch\", desc: \"fork the conversation into a branch\", engines: [\"claude\"], args: \"[name]\" },\n { name: \"btw\", desc: \"ask a side question without bloating history\", engines: both, args: \"\" },\n { name: \"cd\", desc: \"move the session to another working directory\", engines: [\"claude\"], args: \"\" },\n { name: \"chrome\", desc: \"configure Claude Chrome integration\", engines: [\"claude\"] },\n { name: \"claude-api\", desc: \"load Claude API guidance or migration help\", engines: [\"claude\"], args: \"[migrate|managed-agents-onboard]\" },\n { name: \"code-review\", desc: \"review changes and optionally apply fixes\", engines: [\"claude\"], args: \"[effort] [--fix] [target]\" },\n { name: \"color\", desc: \"set the prompt bar color\", engines: [\"claude\"], args: \"[color|default]\" },\n { name: \"config\", desc: \"open or update Claude settings\", engines: [\"claude\"], args: \"[key=value ...]\" },\n { name: \"context\", desc: \"visualize current context usage\", engines: [\"claude\"], args: \"[all]\" },\n { name: \"debug\", desc: \"enable debug logging and diagnose an issue\", engines: [\"claude\"], args: \"[description]\" },\n { name: \"deep-research\", desc: \"run a cited multi-search research workflow\", engines: [\"claude\"], args: \"\" },\n { name: \"desktop\", desc: \"continue in Claude Code Desktop\", engines: [\"claude\"] },\n { name: \"doctor\", desc: \"diagnose installation and runtime issues\", engines: [\"claude\"] },\n { name: \"exit\", desc: \"exit or detach the native CLI session\", engines: both },\n { name: \"export\", desc: \"export the conversation as text\", engines: [\"claude\"], args: \"[filename]\" },\n { name: \"fast\", desc: \"toggle fast mode when available\", engines: both, args: \"[on|off|status]\" },\n { name: \"feedback\", desc: \"send feedback with optional logs\", engines: both, args: \"[report]\" },\n { name: \"fewer-permission-prompts\", desc: \"suggest permission allowlist rules\", engines: [\"claude\"] },\n { name: \"focus\", desc: \"toggle Claude focus view\", engines: [\"claude\"] },\n { name: \"fork\", desc: \"fork work into a subagent or new thread\", engines: both, args: \"\" },\n { name: \"goal\", desc: \"set, view, pause, resume, or clear a goal\", engines: both, args: \"[condition|clear]\" },\n { name: \"heapdump\", desc: \"write a heap snapshot for diagnostics\", engines: [\"claude\"] },\n { name: \"hooks\", desc: \"view and manage lifecycle hooks\", engines: both },\n { name: \"ide\", desc: \"manage IDE integration or include IDE context\", engines: both },\n { name: \"insights\", desc: \"generate a Claude Code usage report\", engines: [\"claude\"] },\n { name: \"install-github-app\", desc: \"set up Claude GitHub Actions\", engines: [\"claude\"] },\n { name: \"install-slack-app\", desc: \"install the Claude Slack app\", engines: [\"claude\"] },\n { name: \"keybindings\", desc: \"open Claude keybindings\", engines: [\"claude\"] },\n { name: \"loop\", desc: \"run a prompt repeatedly\", engines: [\"claude\"], args: \"[interval] [prompt]\" },\n { name: \"memory\", desc: \"edit Claude memory files\", engines: [\"claude\"] },\n { name: \"mobile\", desc: \"show mobile app QR code\", engines: [\"claude\"] },\n { name: \"passes\", desc: \"share a free Claude Code week if eligible\", engines: [\"claude\"] },\n { name: \"plan\", desc: \"enter plan mode\", engines: both, args: \"[description]\" },\n { name: \"plugin\", desc: \"manage Claude plugins\", engines: [\"claude\"], args: \"[subcommand]\" },\n { name: \"powerup\", desc: \"open Claude Code feature lessons\", engines: [\"claude\"] },\n { name: \"privacy-settings\", desc: \"view privacy settings\", engines: [\"claude\"] },\n { name: \"radio\", desc: \"open Claude FM\", engines: [\"claude\"] },\n { name: \"recap\", desc: \"generate a one-line session recap\", engines: [\"claude\"] },\n { name: \"release-notes\", desc: \"view Claude Code release notes\", engines: [\"claude\"] },\n { name: \"reload-plugins\", desc: \"reload active plugins\", engines: [\"claude\"], args: \"[--force]\" },\n { name: \"reload-skills\", desc: \"rescan skills and command directories\", engines: [\"claude\"] },\n { name: \"remote-control\", desc: \"enable remote control\", engines: [\"claude\"] },\n { name: \"remote-env\", desc: \"choose cloud agent environment\", engines: [\"claude\"] },\n { name: \"rename\", desc: \"rename the current session\", engines: [\"claude\"], args: \"[name]\" },\n { name: \"rewind\", desc: \"rewind to a checkpoint\", engines: [\"claude\"] },\n { name: \"run\", desc: \"launch and inspect your app\", engines: [\"claude\"] },\n { name: \"run-skill-generator\", desc: \"teach /run and /verify how to run this app\", engines: [\"claude\"] },\n { name: \"sandbox\", desc: \"toggle Claude sandbox mode\", engines: [\"claude\"] },\n { name: \"schedule\", desc: \"create or manage routines\", engines: [\"claude\"], args: \"[description]\" },\n { name: \"security-review\", desc: \"review pending changes for security risks\", engines: [\"claude\"] },\n { name: \"setup-bedrock\", desc: \"configure Amazon Bedrock auth\", engines: [\"claude\"] },\n { name: \"setup-vertex\", desc: \"configure Google Vertex auth\", engines: [\"claude\"] },\n { name: \"simplify\", desc: \"review and simplify changed code\", engines: [\"claude\"], args: \"[target]\" },\n { name: \"statusline\", desc: \"configure status line fields\", engines: both },\n { name: \"stickers\", desc: \"order Claude Code stickers\", engines: [\"claude\"] },\n { name: \"stop\", desc: \"stop background work\", engines: both },\n { name: \"tasks\", desc: \"view background tasks\", engines: [\"claude\"] },\n { name: \"team-onboarding\", desc: \"generate a team onboarding guide\", engines: [\"claude\"] },\n { name: \"teleport\", desc: \"pull a web session into terminal\", engines: [\"claude\"] },\n { name: \"terminal-setup\", desc: \"configure terminal keybindings\", engines: [\"claude\"] },\n { name: \"theme\", desc: \"choose a theme\", engines: both },\n { name: \"tui\", desc: \"switch Claude terminal renderer\", engines: [\"claude\"], args: \"[default|fullscreen]\" },\n { name: \"ultraplan\", desc: \"draft an ultra plan\", engines: [\"claude\"], args: \"\" },\n { name: \"ultrareview\", desc: \"run a deep cloud review\", engines: [\"claude\"], args: \"[PR]\" },\n { name: \"upgrade\", desc: \"open Claude plan upgrade\", engines: [\"claude\"] },\n { name: \"usage-credits\", desc: \"configure Claude usage credits\", engines: [\"claude\"] },\n { name: \"verify\", desc: \"verify an app change by running it\", engines: [\"claude\"] },\n { name: \"voice\", desc: \"toggle Claude voice dictation\", engines: [\"claude\"], args: \"[hold|tap|off]\" },\n { name: \"web-setup\", desc: \"connect GitHub for Claude Code web\", engines: [\"claude\"] },\n { name: \"workflows\", desc: \"view workflow progress\", engines: [\"claude\"] },\n // Codex CLI and IDE extension commands.\n { name: \"auto-context\", desc: \"toggle IDE auto context\", engines: [\"codex\"] },\n { name: \"cloud\", desc: \"switch to Codex cloud mode\", engines: [\"codex\"] },\n { name: \"cloud-environment\", desc: \"choose Codex cloud environment\", engines: [\"codex\"] },\n { name: \"local\", desc: \"switch to local Codex mode\", engines: [\"codex\"] },\n { name: \"keymap\", desc: \"remap TUI shortcuts\", engines: [\"codex\"] },\n { name: \"vim\", desc: \"toggle Vim composer mode\", engines: [\"codex\"] },\n { name: \"sandbox-add-read-dir\", desc: \"grant sandbox read access to a directory\", engines: [\"codex\"], args: \"\" },\n { name: \"agent\", desc: \"switch active agent thread\", engines: [\"codex\"] },\n { name: \"apps\", desc: \"browse app connectors\", engines: [\"codex\"] },\n { name: \"plugins\", desc: \"browse installed and discoverable plugins\", engines: [\"codex\"] },\n { name: \"archive\", desc: \"archive the current Codex session\", engines: [\"codex\"] },\n { name: \"delete\", desc: \"delete the current Codex session\", engines: [\"codex\"] },\n { name: \"experimental\", desc: \"toggle experimental Codex features\", engines: [\"codex\"] },\n { name: \"approve\", desc: \"approve one retry of an auto-review denial\", engines: [\"codex\"] },\n { name: \"memories\", desc: \"configure Codex memories\", engines: [\"codex\"] },\n { name: \"import\", desc: \"import external agent configuration\", engines: [\"codex\"] },\n { name: \"mention\", desc: \"attach a file or folder to the conversation\", engines: [\"codex\"], args: \"\" },\n { name: \"personality\", desc: \"choose Codex response style\", engines: [\"codex\"] },\n { name: \"ps\", desc: \"show background terminals\", engines: [\"codex\"] },\n { name: \"side\", desc: \"start an ephemeral side conversation\", engines: [\"codex\"], args: \"\" },\n { name: \"raw\", desc: \"toggle raw scrollback mode\", engines: [\"codex\"] },\n { name: \"quit\", desc: \"exit the native CLI session\", engines: [\"codex\"] },\n { name: \"debug-config\", desc: \"inspect Codex config layers\", engines: [\"codex\"] },\n { name: \"title\", desc: \"configure terminal title fields\", engines: [\"codex\"] }\n ];\n\n // src/chat/modelOptions.ts\n var CHAT_MODEL_OPTIONS = {\n claude: [\n {\n value: \"opus\",\n chipLabel: \"Opus 5\",\n label: \"Opus 5\",\n description: \"Most capable · Claude alias: opus\"\n },\n {\n value: \"sonnet\",\n chipLabel: \"Sonnet 5\",\n label: \"Sonnet 5\",\n description: \"Balanced · Claude alias: sonnet\"\n },\n {\n value: \"haiku\",\n chipLabel: \"Haiku 4.5\",\n label: \"Haiku 4.5\",\n description: \"Fastest · Claude alias: haiku\"\n }\n ],\n codex: [\n {\n value: \"gpt-5.5-codex\",\n chipLabel: \"GPT-5.5 Codex\",\n label: \"GPT-5.5 Codex\",\n description: \"Coding-tuned · exact value: gpt-5.5-codex\"\n },\n {\n value: \"gpt-5.5\",\n chipLabel: \"GPT-5.5\",\n label: \"GPT-5.5\",\n description: \"General GPT model · exact value: gpt-5.5\"\n }\n ]\n };\n\n // src/chat/ui/avatar.ts\n var AVATAR_SVG = ``;\n var AVATAR_CLASSES = [\"clothes\", \"eyes\", \"line\", \"face_acc\"];\n var SKIN = \"cls-2\";\n function hashSeed(v) {\n let h = 2166136261;\n for (let i = 0; i < v.length; i++) {\n h ^= v.charCodeAt(i);\n h = Math.imul(h, 16777619);\n }\n return h >>> 0;\n }\n function rng(seed) {\n let s = hashSeed(seed || \"default\") || 439041101;\n return () => {\n s ^= s << 13;\n s ^= s >>> 17;\n s ^= s << 5;\n return (s >>> 0) / 4294967296;\n };\n }\n function avatarSvg(seed) {\n const r = rng(seed || \"default\");\n const pal = {};\n pal[SKIN] = r() > 0.5 ? \"#111111\" : \"#f6d9c8\";\n for (const c of AVATAR_CLASSES) {\n const hue = Math.floor(r() * 360), sat = 50 + Math.floor(r() * 35), lig = 35 + Math.floor(r() * 25);\n pal[c] = \"hsl(\" + hue + \",\" + sat + \"%,\" + lig + \"%)\";\n }\n pal[\"eyes\"] = pal[SKIN] === \"#111111\" ? \"hsl(\" + Math.floor(r() * 360) + \",80%,75%)\" : \"#222222\";\n let svg = AVATAR_SVG;\n const ds = svg.indexOf(\"\");\n if (de > ds) svg = svg.slice(0, ds) + svg.slice(de + 8);\n }\n for (const c of [...AVATAR_CLASSES, SKIN]) {\n const token = 'class=\"' + c + '\"', repl = 'fill=\"' + pal[c] + '\"';\n let i;\n while ((i = svg.indexOf(token)) >= 0) {\n svg = svg.slice(0, i) + repl + svg.slice(i + token.length);\n }\n }\n return svg;\n }\n\n // src/chat/ui/panel/format.ts\n function rel(ts) {\n const s = Math.max(0, (Date.now() - ts) / 1e3);\n if (s < 60) return \"방금\";\n const m = s / 60;\n if (m < 60) return Math.floor(m) + \"분\";\n const h = m / 60;\n if (h < 24) return Math.floor(h) + \"시간\";\n const d = h / 24;\n if (d < 30) return Math.floor(d) + \"일\";\n const mo = d / 30;\n if (mo < 12) return Math.floor(mo) + \"개월\";\n return Math.floor(mo / 12) + \"년\";\n }\n function looksOnChain(v) {\n const s = (v || \"\").trim();\n if (!s) return false;\n if (/^https?:/i.test(s)) return false;\n if (/\\.(png|jpe?g|gif|webp|svg)$/i.test(s)) return false;\n return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(s);\n }\n function fmtNoteDate(n) {\n const ms = n.__blockTime ? n.__blockTime * 1e3 : n.timestamp || 0;\n if (!ms) return \"\";\n try {\n return new Date(ms).toLocaleDateString(void 0, { month: \"short\", day: \"numeric\", year: \"numeric\" });\n } catch {\n return \"\";\n }\n }\n function explorerTxUrl(sig) {\n const u = \"https://explorer.solana.com/tx/\" + encodeURIComponent(sig);\n return S.rpcNetwork === \"mainnet\" ? u : u + \"?cluster=\" + encodeURIComponent(S.rpcNetwork);\n }\n function fdAgo(ms) {\n if (!ms) return \"\";\n const s = Math.max(1, Math.floor((Date.now() - ms) / 1e3));\n if (s < 60) return s + \"s\";\n const m = Math.floor(s / 60);\n if (m < 60) return m + \"m\";\n const h = Math.floor(m / 60);\n if (h < 24) return h + \"h\";\n const d = Math.floor(h / 24);\n if (d < 7) return d + \"d\";\n return Math.floor(d / 7) + \"w\";\n }\n function feedImgUrl(v) {\n return v && /^https?:\\/\\//i.test(v) ? v : null;\n }\n function agShort(w) {\n return w.slice(0, 6) + \"...\" + w.slice(-4);\n }\n function pad2(n) {\n return n < 10 ? \"0\" + n : String(n);\n }\n function splitSkillDoc(text) {\n text = text || \"\";\n const m = text.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?/);\n return (m ? text.slice(m[0].length) : text).trim();\n }\n function fmtSol(lamports) {\n if (lamports == null) return null;\n const sol = lamports / 1e9;\n const s = sol < 1 ? sol.toFixed(4) : sol.toFixed(3);\n return s.replace(/\\.?0+$/, \"\") + \" SOL\";\n }\n function fmtPrice(price) {\n if (price == null) return null;\n const n = Number(price);\n if (!Number.isFinite(n)) return null;\n return n === 0 ? \"Free\" : fmtSol(n);\n }\n function netBadge(network) {\n const n = network === \"mainnet\" ? \"mainnet\" : \"devnet\";\n return '' + n + \"\";\n }\n function short(a) {\n return a && a.length > 10 ? a.slice(0, 4) + \"..\" + a.slice(-3) : a;\n }\n\n // src/chat/ui/panel/skeleton.ts\n function skSd(n) {\n let s = \"\";\n for (let i = 0; i < n; i++) s += '';\n return s;\n }\n function skAc(n) {\n let s = \"\";\n for (let i = 0; i < n; i++) s += '';\n return s;\n }\n var skId = '';\n function skFd(n) {\n let s = \"\";\n for (let i = 0; i < n; i++) s += '';\n return s;\n }\n\n // src/chat/ui/panel/publish.ts\n var pubImage = document.getElementById(\"pubImage\");\n var pubImageBadge = document.getElementById(\"pubImageBadge\");\n var pubSubmit = document.getElementById(\"pubSubmit\");\n var pubError = document.getElementById(\"pubError\");\n var pubFormEl = document.getElementById(\"pubForm\");\n var pubTextWrap = document.getElementById(\"pubTextWrap\");\n var pubReqWrap = document.getElementById(\"pubReqWrap\");\n var pubReqEl = document.getElementById(\"pubReq\");\n var pubReqCountEl = document.getElementById(\"pubReqCount\");\n var pubReqSel = {};\n function chosenReqMints() {\n return Object.keys(pubReqSel).filter((m) => pubReqSel[m]);\n }\n function updatePubReqCount() {\n const c = chosenReqMints().length;\n pubReqCountEl.textContent = c ? c + \" selected\" + (c > 16 ? \" — max 16, deselect some\" : \"\") : \"\";\n }\n function renderPubReq() {\n const owned = S.ownedSkills.filter((n) => !!S.skillMints[n] && !S.workflowMintSet.has(S.skillMints[n]));\n if (!owned.length) {\n pubReqEl.innerHTML = `You don't own any skills yet. Buy at least one before publishing a workflow.`;\n pubReqCountEl.textContent = \"\";\n return;\n }\n pubReqEl.innerHTML = \"\";\n for (const n of owned) {\n const mint = S.skillMints[n];\n const lab = document.createElement(\"label\");\n const cb = document.createElement(\"input\");\n cb.type = \"checkbox\";\n cb.value = mint;\n cb.checked = !!pubReqSel[mint];\n cb.addEventListener(\"change\", () => {\n pubReqSel[mint] = cb.checked;\n updatePubReqCount();\n });\n const span = document.createElement(\"span\");\n span.textContent = n;\n lab.appendChild(cb);\n lab.appendChild(span);\n pubReqEl.appendChild(lab);\n }\n updatePubReqCount();\n }\n function setPubKind(k) {\n S.pubKind = k === \"workflow\" ? \"workflow\" : \"skill\";\n S.pubBodyConfirmed = false;\n const wf = S.pubKind === \"workflow\";\n for (const b of document.querySelectorAll(\".pubKind button\")) b.classList.toggle(\"on\", b.getAttribute(\"data-k\") === S.pubKind);\n pubFormEl.classList.toggle(\"wf\", wf);\n pubTextWrap.style.display = wf ? \"none\" : \"\";\n pubReqWrap.style.display = wf ? \"\" : \"none\";\n document.getElementById(\"pubFormHead\").innerHTML = \"FORM // PUBLISH \" + (wf ? \"WORKFLOW\" : \"SKILL\") + \"\";\n document.getElementById(\"pubViewTitle\").textContent = wf ? \"Make a workflow\" : \"Make a skill\";\n document.getElementById(\"pubViewDesc\").textContent = wf ? \"Bundle skills you own into one workflow. Buyers must hold every skill it requires to unlock it.\" : \"Publish a skill others can buy. It mints a soulbound NFT and the body is stored on-chain.\";\n if (!pubSubmit.disabled) pubSubmit.textContent = wf ? \"Publish workflow\" : \"Publish skill\";\n if (wf) renderPubReq();\n }\n function openPublish(kind) {\n setPubKind(kind);\n showView(\"publish\");\n }\n function wirePublish() {\n for (const b of document.querySelectorAll(\".pubKind button\")) b.addEventListener(\"click\", () => setPubKind(b.getAttribute(\"data-k\")));\n pubImage.addEventListener(\"input\", () => {\n pubImageBadge.style.display = looksOnChain(pubImage.value) ? \"inline-block\" : \"none\";\n });\n document.getElementById(\"pubText\").addEventListener(\"input\", () => {\n S.pubBodyConfirmed = false;\n });\n pubSubmit.addEventListener(\"click\", () => {\n const name = document.getElementById(\"pubName\").value.trim();\n const description = document.getElementById(\"pubDesc\").value.trim();\n const priceSol = document.getElementById(\"pubPrice\").value.trim();\n const category = document.getElementById(\"pubCategory\").value.trim();\n const hashtags = document.getElementById(\"pubHashtags\").value.split(\",\").map((h) => h.trim()).filter(Boolean);\n const image = pubImage.value.trim();\n pubError.style.display = \"none\";\n const fail = (msg2) => {\n pubError.textContent = msg2;\n pubError.style.display = \"block\";\n };\n if (!name) return fail(\"Name is required.\");\n if (!description) return fail(\"Description is required.\");\n let text;\n let reqMints = [];\n if (S.pubKind === \"workflow\") {\n reqMints = chosenReqMints().filter((m) => !S.workflowMintSet.has(m));\n if (!reqMints.length) return fail(\"Pick at least one skill this workflow requires.\");\n if (reqMints.length > 16) return fail(\"A workflow can require at most 16 skills.\");\n const fm = [\n \"---\",\n \"name: \" + name,\n \"description: \" + description.replace(/\\s*\\n\\s*/g, \" \"),\n \"type: workflow\",\n \"requiredSkills: [\" + reqMints.join(\", \") + \"]\"\n ];\n if (category) fm.push(\"category: \" + category);\n if (hashtags.length) fm.push(\"hashtags: [\" + hashtags.join(\", \") + \"]\");\n text = fm.concat([\"---\", \"\", \"# \" + name, \"\", description, \"\"]).join(\"\\n\");\n } else {\n text = document.getElementById(\"pubText\").value.trim();\n if (!text) return fail(\"Skill text is required.\");\n const body = text.replace(/^---[\\s\\S]*?\\n---\\s*/, \"\").trim();\n if (body.length < 20 && !S.pubBodyConfirmed) {\n S.pubBodyConfirmed = true;\n return fail(\"This publishes PERMANENTLY and cannot be deleted; the body looks empty or very short. Click Publish again to submit anyway.\");\n }\n }\n if (!priceSol) return fail(\"Enter a price in SOL (use 0 for free).\");\n if (!/^\\d+(\\.\\d+)?$/.test(priceSol)) return fail(\"Price must be a number in SOL (e.g. 0.1).\");\n pubSubmit.disabled = true;\n pubSubmit.textContent = \"Publishing…\";\n const msg = {\n type: \"publishSkill\",\n name,\n description,\n text,\n priceSol,\n category: category || void 0,\n hashtags: hashtags.length ? hashtags : void 0,\n image: image || void 0\n };\n if (S.pubKind === \"workflow\") {\n msg.kind = \"workflow\";\n msg.requiredSkills = reqMints;\n }\n vscode.postMessage(msg);\n });\n }\n\n // src/chat/ui/icons.ts\n var WAND_SVG = '';\n var LAYERS_SVG = '';\n\n // src/chat/ui/iqlogo.ts\n var IQ_LOGO_SVG = ' ';\n\n // src/links/github.ts\n var SAFE_EXTERNAL_PROTOCOLS = /* @__PURE__ */ new Set([\"https:\", \"http:\", \"git:\"]);\n function safeExternalUrl(raw) {\n if (!raw) return null;\n try {\n const url = new URL(raw);\n if (!SAFE_EXTERNAL_PROTOCOLS.has(url.protocol)) return null;\n return url.href;\n } catch {\n return null;\n }\n }\n function parseGithubLink(raw) {\n const href = safeExternalUrl(raw);\n if (!href) return null;\n let url;\n try {\n url = new URL(href);\n } catch {\n return null;\n }\n const host = url.hostname.toLowerCase();\n if (host !== \"github.com\" && host !== \"www.github.com\") return null;\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length < 2) return null;\n const [owner, repo, section, value, ...rest] = parts;\n const repoName = repo.replace(/\\.git$/i, \"\");\n const repoFullName = `${owner}/${repoName}`;\n if (!section) {\n return {\n kind: \"repo\",\n href,\n owner,\n repo: repoName,\n label: repoFullName,\n meta: \"GitHub repository\"\n };\n }\n if (section === \"pull\" && value && /^\\d+$/.test(value)) {\n return {\n kind: \"pull\",\n href,\n owner,\n repo: repoName,\n number: value,\n label: `${repoFullName} #${value}`,\n meta: \"GitHub pull request\"\n };\n }\n if (section === \"commit\" && value && /^[0-9a-f]{7,40}$/i.test(value)) {\n return {\n kind: \"commit\",\n href,\n owner,\n repo: repoName,\n sha: value,\n label: `${repoFullName}@${value.slice(0, 7)}`,\n meta: \"GitHub commit\"\n };\n }\n if (section === \"blob\" && value && rest.length > 0) {\n const path = rest.join(\"/\");\n return {\n kind: \"blob\",\n href,\n owner,\n repo: repoName,\n path,\n label: path,\n meta: repoFullName\n };\n }\n return null;\n }\n\n // src/chat/ui/panel/tiers.ts\n var AG_SEG = 12;\n var AG_TIERS = [\n { name: \"Legendary\", min: 250 },\n { name: \"Gold\", min: 60 },\n { name: \"Silver\", min: 15 },\n { name: \"Bronze\", min: 3 }\n ];\n function agStarTier(stars) {\n return AG_TIERS.find((t) => stars >= t.min) || null;\n }\n function agNextTierMin(stars) {\n const asc = AG_TIERS.slice().sort((a, b) => a.min - b.min);\n const up = asc.find((t) => t.min > stars);\n return up ? up.min : asc[asc.length - 1].min;\n }\n function agAccent(wallet) {\n return \"hsl(\" + hashSeed(wallet || \"default\") % 360 + \" 46% 62%)\";\n }\n var PROF_TIERS = [{ name: \"Bronze\", min: 3 }, { name: \"Silver\", min: 15 }, { name: \"Gold\", min: 60 }, { name: \"Legendary\", min: 250 }];\n var TIER_COLOR = { Bronze: \"var(--an-tier-bronze)\", Silver: \"var(--an-tier-silver)\", Gold: \"var(--an-tier-gold)\", Legendary: \"var(--an-tier-legendary)\" };\n var PROF_SEG = 15;\n function profTierInfo(stars) {\n let cur = null, next = null;\n for (const t of PROF_TIERS) {\n if (stars >= t.min) cur = t;\n else {\n next = t;\n break;\n }\n }\n return { cur, next };\n }\n var PROF_REPO_TIERS = [\n { min: 250, color: \"#86c4cf\", from: \"#131a1b\", to: \"#0d1011\", empty: \"#1e2628\" },\n { min: 50, color: \"#d8c074\", from: \"#1a1813\", to: \"#100f0d\", empty: \"#2a2618\" },\n { min: 10, color: \"#b8c0cc\", from: \"#161719\", to: \"#0e0f10\", empty: \"#26282c\" },\n { min: 3, color: \"#b8895a\", from: \"#1a1613\", to: \"#100f0e\", empty: \"#2a2420\" }\n ];\n var PROF_REPO_BASE = { color: \"#9a9a9a\", from: \"#1a1a1d\", to: \"#0d0d0e\", empty: \"#33333a\" };\n function profRepoTier(stars) {\n return PROF_REPO_TIERS.find((t) => stars >= t.min) || PROF_REPO_BASE;\n }\n function profRepoFill(stars) {\n const next = [3, 10, 50, 250].find((t) => stars < t);\n if (!next) return 10;\n return Math.max(0, Math.min(10, Math.round(stars / next * 10)));\n }\n\n // src/chat/ui/panel/agents.ts\n function agentCardEl(agent, self) {\n const stars = agent.stars || 0;\n const tier = agStarTier(stars);\n const isMax = tier && tier.name === \"Legendary\";\n const denom = agNextTierMin(stars);\n const filled = Math.max(0, Math.min(AG_SEG, Math.round(stars / denom * AG_SEG)));\n const tierName = (tier ? tier.name : \"Unranked\").toUpperCase();\n const created = agent.skillsPublished || 0;\n const copies = agent.totalSupply || 0;\n const earnedSol = agent.totalEarned ? Number(agent.totalEarned) / 1e9 : 0;\n const earned = earnedSol >= 100 ? earnedSol.toFixed(0) : earnedSol.toFixed(2);\n const sig = 34 + agent.wallet.charCodeAt(2) % 6 * 11;\n let segs = \"\";\n for (let i = 0; i < AG_SEG; i++) segs += '';\n const btn = document.createElement(\"button\");\n btn.className = \"an-ac\" + (self ? \" is-self\" : \"\");\n btn.style.setProperty(\"--accent\", agAccent(agent.wallet));\n btn.innerHTML = '>' + agShort(agent.wallet).toUpperCase() + \"_AGENT\" + (self ? ' // YOU' : \"\") + 'SIGNAL エージェント' + agent.wallet.slice(0, 6).toUpperCase() + 'アクセス / ACCESS
' + tierName + '' + avatarSvg(agent.wallet) + '— RANKING —STARS' + segs + '' + (isMax ? stars : stars + \"/\" + denom) + 'CREATED' + created + 'COPIES' + copies + '>EARNED ' + earned + '◎';\n btn.addEventListener(\"click\", () => showProfile(agent.wallet));\n return btn;\n }\n function renderAgents(agents) {\n S.lastAgents = agents || [];\n const selfEl = document.getElementById(\"agentsSelf\");\n if (selfEl) {\n selfEl.innerHTML = \"\";\n if (S.myWalletAddress) {\n const selfRep = S.lastAgents.find((a) => a.wallet === S.myWalletAddress) || { wallet: S.myWalletAddress, skillsPublished: 0, totalSupply: 0, notesReceived: 0, updatedAt: 0 };\n selfEl.appendChild(agentCardEl(selfRep, true));\n }\n }\n const search = document.getElementById(\"agentSearch\");\n if (search && !search._wired) {\n search._wired = true;\n search.addEventListener(\"input\", renderAgentsList);\n }\n renderAgentsList();\n }\n function renderAgentsList() {\n const el = document.getElementById(\"agentsList\");\n if (!el) return;\n const searchEl = document.getElementById(\"agentSearch\");\n const ql = (searchEl && searchEl.value ? searchEl.value : \"\").trim().toLowerCase();\n let others = S.lastAgents.filter((a) => a.wallet !== S.myWalletAddress);\n if (ql) others = others.filter((a) => a.wallet.toLowerCase().includes(ql));\n el.innerHTML = \"\";\n if (!others.length) {\n const e = document.createElement(\"div\");\n e.className = \"agEmpty\";\n e.textContent = ql ? \"No agent matches that wallet\" : \"No other agents yet\";\n el.appendChild(e);\n return;\n }\n others.forEach((a) => el.appendChild(agentCardEl(a, false)));\n }\n function mergeOptimistic(profile) {\n const now = Date.now();\n S.recentlyPosted = S.recentlyPosted.filter((p) => now - p.ts < 6e4);\n const real = new Set((profile.threads || []).flatMap((t) => [t.note, ...t.replies || []]).map((n) => (n.author || \"\") + \"\\0\" + (n.text || \"\")));\n S.recentlyPosted = S.recentlyPosted.filter((p) => !(p.wallet === profile.wallet && real.has((p.note.author || \"\") + \"\\0\" + (p.note.text || \"\"))));\n const pending = S.recentlyPosted.filter((p) => p.wallet === profile.wallet && !p.note.parentId).map((p) => p.note);\n return pending.length ? { ...profile, threads: [...pending.map((note) => ({ note, replies: [] })), ...profile.threads || []] } : profile;\n }\n function feedbackFor(wallet) {\n if (!S.postFeedback) return null;\n if (S.postFeedback.wallet !== wallet) return null;\n if (Date.now() - S.postFeedback.ts > 8e3) {\n S.postFeedback = null;\n return null;\n }\n return S.postFeedback;\n }\n function enableDragScroll(el) {\n let startX = 0, startLeft = 0, dragging = false, moved = false;\n el.addEventListener(\"pointerdown\", (e) => {\n if (e.pointerType !== \"mouse\" || e.button !== 0) return;\n dragging = true;\n moved = false;\n startX = e.clientX;\n startLeft = el.scrollLeft;\n try {\n el.setPointerCapture(e.pointerId);\n } catch {\n }\n el.classList.add(\"dragging\");\n });\n el.addEventListener(\"pointermove\", (e) => {\n if (!dragging) return;\n const dx = e.clientX - startX;\n if (Math.abs(dx) > 3) moved = true;\n el.scrollLeft = startLeft - dx;\n });\n const end = (e) => {\n if (!dragging) return;\n dragging = false;\n el.classList.remove(\"dragging\");\n try {\n el.releasePointerCapture(e.pointerId);\n } catch {\n }\n };\n el.addEventListener(\"pointerup\", end);\n el.addEventListener(\"pointercancel\", end);\n el.addEventListener(\"click\", (e) => {\n if (moved) {\n e.preventDefault();\n e.stopPropagation();\n moved = false;\n }\n }, true);\n }\n var GH_KIND_LABEL = { repo: \"Repo\", pull: \"PR\", commit: \"Commit\", blob: \"File\" };\n function gitLinkNode(raw, className) {\n const gh = parseGithubLink(raw);\n const wrap = document.createElement(\"div\");\n wrap.className = className;\n if (gh) {\n const a2 = document.createElement(\"a\");\n a2.className = \"gh-card\";\n a2.href = gh.href;\n a2.target = \"_blank\";\n a2.rel = \"noopener noreferrer\";\n const kind = document.createElement(\"span\");\n kind.className = \"gh-kind\";\n kind.textContent = GH_KIND_LABEL[gh.kind];\n const title = document.createElement(\"span\");\n title.className = \"gh-title\";\n title.textContent = gh.label;\n const meta = document.createElement(\"span\");\n meta.className = \"gh-meta\";\n meta.textContent = gh.meta;\n a2.appendChild(kind);\n a2.appendChild(title);\n a2.appendChild(meta);\n wrap.appendChild(a2);\n return wrap;\n }\n const safeLink = safeExternalUrl(raw);\n if (!safeLink) return null;\n const a = document.createElement(\"a\");\n a.href = safeLink;\n a.textContent = safeLink;\n a.target = \"_blank\";\n a.rel = \"noopener noreferrer\";\n wrap.appendChild(a);\n return wrap;\n }\n\n // src/chat/ui/skillSigil.ts\n function hash(s) {\n let h = 2166136261;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 16777619);\n }\n return h >>> 0;\n }\n function rng2(s) {\n let a = hash(s);\n return function() {\n a |= 0;\n a = a + 1831565813 | 0;\n let t = Math.imul(a ^ a >>> 15, 1 | a);\n t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;\n return ((t ^ t >>> 14) >>> 0) / 4294967296;\n };\n }\n function skillSigilSvg(name) {\n const col = \"hsl(228 5% 82%)\";\n const r = rng2((name || \"\") + \"::sigil\");\n const cx = 60, cy = 74;\n let s = \"\";\n const rings = 2 + Math.floor(r() * 2);\n for (let i = 0; i < rings; i++) {\n const rad = 22 + i * 9 + r() * 4;\n s += ' ';\n }\n const ticks = 12 + Math.floor(r() * 24);\n const t1 = 29 + r() * 3;\n const t2 = t1 + 6 + r() * 4;\n for (let i = 0; i < ticks; i++) {\n const a = i / ticks * Math.PI * 2;\n const x1 = cx + Math.cos(a) * t1, y1 = cy + Math.sin(a) * t1, x2 = cx + Math.cos(a) * t2, y2 = cy + Math.sin(a) * t2;\n s += '';\n }\n const sides = 3 + Math.floor(r() * 5);\n const rot = r() * Math.PI * 2;\n const pr = 12 + r() * 5;\n const pts = [];\n for (let i = 0; i < sides; i++) {\n const a = rot + i / sides * Math.PI * 2;\n pts.push([cx + Math.cos(a) * pr, cy + Math.sin(a) * pr]);\n }\n const poly = pts.map(function(p) {\n return p[0].toFixed(1) + \",\" + p[1].toFixed(1);\n }).join(\" \");\n s += '';\n if (sides >= 5) {\n const star = [];\n for (let i = 0; i < sides; i++) star.push(pts[i * 2 % sides]);\n const sp = star.map(function(p) {\n return p[0].toFixed(1) + \",\" + p[1].toFixed(1);\n }).join(\" \");\n s += '';\n }\n for (let k = 0; k < pts.length; k++) {\n const p = pts[k];\n s += '';\n }\n s += '';\n const runes = 6 + Math.floor(r() * 8);\n for (let i = 0; i < runes; i++) {\n const a = r() * Math.PI * 2;\n const rr = 5 + r() * 28;\n const x = cx + Math.cos(a) * rr, y = cy + Math.sin(a) * rr;\n const len = 2 + r() * 4;\n s += ' ';\n }\n return s;\n }\n\n // src/chat/ui/panel/skills.ts\n var skillsBtn = document.getElementById(\"skillsBtn\");\n var skillsPanel = document.getElementById(\"skillsPanel\");\n function closeSkillsPanel() {\n skillsPanel.style.display = \"none\";\n skillsBtn.classList.remove(\"on\");\n }\n var skillsCloseBtn = document.getElementById(\"skillsClose\");\n var walletSkillsItem = document.getElementById(\"walletSkills\");\n var walletSkillList = document.getElementById(\"walletSkillList\");\n function renderWalletSkillList() {\n if (!walletSkillList) return;\n walletSkillList.innerHTML = \"\";\n if (!S.ownedSkills.length) {\n const e = document.createElement(\"div\");\n e.className = \"wskEmpty\";\n e.textContent = \"No skills yet. Buy them in Markets.\";\n walletSkillList.appendChild(e);\n return;\n }\n for (const name of S.ownedSkills) {\n const row = document.createElement(\"div\");\n row.className = \"wskRow\";\n row.innerHTML = '' + WAND_SVG + \"\";\n const lbl = document.createElement(\"span\");\n lbl.textContent = name;\n lbl.title = name;\n row.appendChild(lbl);\n row.style.cursor = \"pointer\";\n row.addEventListener(\"click\", () => {\n const mt = S.skillMints[name];\n if (mt) {\n showView(\"market\");\n openDetail(mt);\n } else openSkillDoc(name);\n });\n walletSkillList.appendChild(row);\n }\n }\n function skillSdCard(card, opts) {\n opts = opts || {};\n const isWorkflow = card.type === \"workflow\";\n const priceSol = card.price && card.price !== \"0\" && card.price !== 0 ? (Number(card.price) / 1e9).toFixed(2) : null;\n const cat = String(card.category || (isWorkflow ? \"workflow\" : \"skill\")).toUpperCase().slice(0, 8);\n const state = opts.disposed ? \"OFF\" : opts.owned ? \"OWNED\" : \"GET\";\n const nm = card.name || card.id || \"\";\n const el = document.createElement(\"button\");\n el.className = \"an-sd\" + (isWorkflow ? \" is-workflow\" : \"\") + (opts.disposed ? \" is-disposed\" : \"\") + (opts.dim ? \" is-owned-dim\" : \"\") + (opts.firing ? \" is-firing\" : \"\");\n el.title = nm;\n el.setAttribute(\"data-skill\", card.name || \"\");\n if (card.id) el.setAttribute(\"data-mint\", card.id);\n var imgUrl = card.image && /^https?:\\/\\//.test(String(card.image)) ? String(card.image) : null;\n if (imgUrl) el.className += \" has-img\";\n el.innerHTML = '' + (imgUrl ? '
' : '') + '';\n if (imgUrl) el.querySelector(\".an-sd-img\").src = imgUrl;\n var catEl = el.querySelector(\".an-sd-mark .cat\");\n if (catEl) catEl.textContent = cat;\n el.querySelector(\".an-sd-name\").textContent = nm;\n const big = el.querySelector(\".an-sd-big\");\n if (card.supply != null) big.textContent = String(card.supply);\n else big.classList.add(\"bar\");\n const meta = el.querySelector(\".an-sd-meta\");\n meta.textContent = priceSol ? priceSol + \"◎\" : \"FREE\";\n meta.appendChild(document.createElement(\"br\"));\n meta.appendChild(document.createTextNode(state));\n const stars = Number(card.stars) || 0;\n if (stars > 0) {\n const grade = document.createElement(\"div\");\n grade.className = \"an-sd-grade\";\n const st = document.createElement(\"span\");\n st.className = \"st\";\n st.textContent = \"★\";\n grade.appendChild(st);\n grade.appendChild(document.createTextNode(String(stars)));\n el.querySelector(\".an-sd-label\").appendChild(grade);\n }\n if (opts.onOpen) el.addEventListener(\"click\", () => opts.onOpen(card));\n return el;\n }\n function setSkills(names, mints, meta) {\n names = names || [];\n const sm = meta || {};\n if (mints) S.skillMints = Object.assign({}, mints, S.disposedMints);\n const grid = document.getElementById(\"skillGrid\");\n const status = document.getElementById(\"skillStatus\");\n const n = names.length;\n const disposedSlugs = Object.keys(S.disposedMints);\n const shown = names.filter((nm) => !!S.skillMints[nm]);\n status.textContent = String(shown.length + disposedSlugs.length);\n grid.innerHTML = \"\";\n for (const name of shown) {\n const open = () => {\n const mt = S.skillMints[name];\n if (mt) {\n showView(\"market\");\n openDetail(mt);\n } else openSkillDoc(name);\n };\n const info = sm[name] || {};\n grid.appendChild(skillSdCard(\n { id: S.skillMints[name], name, category: info.category, type: info.type, stars: info.stars },\n { owned: true, onOpen: open }\n ));\n }\n for (const name of disposedSlugs) {\n const info = sm[name] || {};\n const c = skillSdCard(\n { id: S.disposedMints[name], name, category: info.category, type: info.type, stars: info.stars },\n {\n owned: true,\n disposed: true,\n onOpen: () => {\n showView(\"market\");\n openDetail(S.disposedMints[name]);\n }\n }\n );\n c.title = name + \" - un-pinned (click to re-equip)\";\n grid.appendChild(c);\n }\n const fill = Math.max(0, 3 - shown.length - disposedSlugs.length);\n for (let i = 0; i < fill; i++) {\n const s = document.createElement(\"div\");\n s.className = \"skSlot empty\";\n grid.appendChild(s);\n }\n S.ownedSkills = names;\n const wc = document.getElementById(\"walletSkillCount\");\n if (wc) {\n wc.textContent = n ? String(n) : \"\";\n wc.style.display = n ? \"\" : \"none\";\n }\n const wsl = document.getElementById(\"walletSkillList\");\n if (wsl && wsl.style.display !== \"none\") renderWalletSkillList();\n if (pubReqWrap && pubReqWrap.style.display !== \"none\") renderPubReq();\n }\n function wireSkills() {\n skillsBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const open = skillsPanel.style.display !== \"none\";\n skillsPanel.style.display = open ? \"none\" : \"block\";\n skillsBtn.classList.toggle(\"on\", !open);\n });\n if (skillsCloseBtn) skillsCloseBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n closeSkillsPanel();\n });\n if (walletSkillsItem && walletSkillList) walletSkillsItem.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n const open = walletSkillList.style.display !== \"none\";\n if (open) {\n walletSkillList.style.display = \"none\";\n walletSkillsItem.classList.remove(\"open\");\n return;\n }\n renderWalletSkillList();\n walletSkillList.style.display = \"flex\";\n walletSkillsItem.classList.add(\"open\");\n });\n setSkills([]);\n vscode.postMessage({ type: \"ownedSkills\" });\n }\n\n // src/chat/ui/panel/market.ts\n var shopToggle = document.getElementById(\"shopToggle\");\n function setShopToggle(on) {\n shopToggle.classList.toggle(\"on\", !!on);\n shopToggle.setAttribute(\"aria-checked\", on ? \"true\" : \"false\");\n }\n var mktSearch = document.getElementById(\"mktSearch\");\n var mktResults = document.getElementById(\"mktResults\");\n var mktListEl = document.getElementById(\"mktList\");\n var mktDetailEl = document.getElementById(\"mktDetail\");\n var mktDetailBody = document.getElementById(\"mktDetailBody\");\n var mktHideOwnedCb = document.getElementById(\"mktHideOwned\");\n var mktSortBtn = document.getElementById(\"mktSortBtn\");\n function paintSortBtn() {\n if (!mktSortBtn) return;\n mktSortBtn.textContent = S.mktSort === \"stars\" ? \"★ Stars\" : \"Popular\";\n mktSortBtn.classList.toggle(\"stars\", S.mktSort === \"stars\");\n }\n function runMarketSearch() {\n mktResults.innerHTML = skSd(8);\n vscode.postMessage({ type: \"searchSkills\", query: mktSearch.value.trim(), kind: S.currentKind, sort: S.mktSort });\n }\n function openMarket() {\n showMktList();\n mktResults.innerHTML = skSd(8);\n vscode.postMessage({ type: \"searchSkills\", query: \"\", kind: S.currentKind, sort: S.mktSort });\n vscode.postMessage({ type: \"ownedSkills\" });\n vscode.postMessage({ type: \"getBalance\" });\n }\n function showMktList() {\n mktListEl.style.display = \"block\";\n mktDetailEl.style.display = \"none\";\n }\n function showMktDetail() {\n mktListEl.style.display = \"none\";\n mktDetailEl.style.display = \"block\";\n }\n function openDetail(mint) {\n showMktDetail();\n mktDetailBody.innerHTML = 'Loading…';\n vscode.postMessage({ type: \"getSkillDetail\", mint });\n }\n var skillModalEl = document.getElementById(\"skillModal\");\n var skillModalBody = document.getElementById(\"skillModalBody\");\n function openSkillModal(mint) {\n S.skillModalOpen = true;\n S.skillModalBuyBtn = null;\n S.skillModalName = null;\n S.skillModalMint = null;\n skillModalBody.innerHTML = 'Loading…';\n skillModalEl.style.display = \"flex\";\n vscode.postMessage({ type: \"getSkillDetail\", mint });\n vscode.postMessage({ type: \"ownedSkills\" });\n }\n function closeSkillModal() {\n S.skillModalOpen = false;\n S.skillDocOpen = false;\n S.skillModalBuyBtn = null;\n S.skillModalName = null;\n S.skillModalMint = null;\n skillModalEl.style.display = \"none\";\n }\n function refreshModalOwned() {\n if (S.skillModalBuyBtn && (ownsMint(S.skillModalMint) || S.skillModalName && S.ownedSkills.indexOf(S.skillModalName) >= 0)) {\n S.skillModalBuyBtn.disabled = true;\n S.skillModalBuyBtn.textContent = \"Owned\";\n }\n }\n function renderSkillModal(detail) {\n const c = detail && detail.card || {};\n const owned = ownsMint(c.id) || S.ownedSkills.indexOf(c.name) >= 0;\n S.skillModalName = c.name || null;\n S.skillModalMint = c.id || null;\n skillModalBody.innerHTML = \"\";\n const head = document.createElement(\"div\");\n head.className = \"dt-head\";\n const img = document.createElement(\"div\");\n img.className = \"dt-img\";\n img.innerHTML = '' + IQ_LOGO_SVG + \"\";\n const htxt = document.createElement(\"div\");\n const kind = document.createElement(\"div\");\n kind.className = \"dt-kind\";\n kind.textContent = c.type || \"skill\";\n const nm = document.createElement(\"div\");\n nm.className = \"dt-name\";\n nm.textContent = c.name || c.id || \"\";\n htxt.appendChild(kind);\n htxt.appendChild(nm);\n head.appendChild(img);\n head.appendChild(htxt);\n skillModalBody.appendChild(head);\n if (c.description) {\n const d = document.createElement(\"div\");\n d.className = \"dt-desc\";\n d.textContent = c.description;\n skillModalBody.appendChild(d);\n }\n const meta = document.createElement(\"div\");\n meta.className = \"dt-meta\";\n const addTag = (t) => {\n const s = document.createElement(\"span\");\n s.className = \"dt-tag\";\n s.textContent = t;\n meta.appendChild(s);\n };\n if (c.category) addTag(c.category);\n for (const h of c.hashtags || []) addTag(\"#\" + h);\n if (typeof c.supply === \"number\") addTag(c.supply + \"× owned\");\n const price = fmtPrice(c.price);\n if (price) addTag(price);\n if (meta.childElementCount) skillModalBody.appendChild(meta);\n if (!owned || c.type) {\n const buy = document.createElement(\"button\");\n buy.className = \"dt-buy\";\n const buyLabel = price && price !== \"Free\" ? \"Buy · \" + price : \"Buy\";\n buy.textContent = owned ? \"Owned\" : buyLabel;\n buy.disabled = owned;\n buy.addEventListener(\"click\", () => {\n buy.disabled = true;\n buy.textContent = \"Buying…\";\n vscode.postMessage({ type: \"buySkill\", skillId: c.id, creatorWallet: c.creator });\n });\n skillModalBody.appendChild(buy);\n S.skillModalBuyBtn = buy;\n }\n if (detail && detail.skillText) {\n const sec = document.createElement(\"div\");\n sec.className = \"dt-sec\";\n sec.textContent = (c.type === \"workflow\" ? \"Workflow\" : \"Skill\") + \" text\";\n const bd = document.createElement(\"div\");\n bd.className = \"dt-body\";\n bd.textContent = detail.skillText;\n skillModalBody.appendChild(sec);\n skillModalBody.appendChild(bd);\n }\n }\n function openSkillDoc(name) {\n S.skillModalOpen = false;\n S.skillDocOpen = true;\n S.skillModalName = null;\n S.skillModalBuyBtn = null;\n skillModalBody.innerHTML = 'Loading…';\n skillModalEl.style.display = \"flex\";\n vscode.postMessage({ type: \"getSkillDoc\", name });\n }\n function renderSkillDoc(name, text) {\n skillModalBody.innerHTML = \"\";\n const nm = document.createElement(\"div\");\n nm.className = \"skDoc-name\";\n nm.textContent = name;\n skillModalBody.appendChild(nm);\n const body = splitSkillDoc(text);\n if (!body) {\n const e = document.createElement(\"div\");\n e.className = \"skDoc-empty\";\n e.textContent = \"No SKILL.md document found for this skill.\";\n skillModalBody.appendChild(e);\n return;\n }\n const bd = document.createElement(\"div\");\n bd.className = \"skDoc-body\";\n renderMd(bd, body);\n skillModalBody.appendChild(bd);\n }\n function renderComments(skillId, skillType, notes, owned) {\n const existing = mktDetailBody.querySelector(\".dt-comments\");\n if (existing) existing.remove();\n const wrap = document.createElement(\"div\");\n wrap.className = \"dt-comments\";\n const sec = document.createElement(\"div\");\n sec.className = \"dt-sec\";\n sec.textContent = \"Comments (\" + (notes ? notes.length : 0) + \")\";\n wrap.appendChild(sec);\n for (const n of notes || []) {\n const el = document.createElement(\"div\");\n el.className = \"dt-comment\";\n const auth = document.createElement(\"div\");\n auth.className = \"cm-author\";\n if (n.author) {\n const av = document.createElement(\"span\");\n av.className = \"cm-avatar\";\n av.innerHTML = avatarSvg(n.author);\n const addr = document.createElement(\"span\");\n addr.className = \"cm-addr\";\n addr.textContent = n.author.slice(0, 6) + \"…\" + n.author.slice(-4);\n auth.appendChild(av);\n auth.appendChild(addr);\n auth.classList.add(\"cm-link\");\n auth.title = \"View \" + n.author + \"'s profile\";\n auth.addEventListener(\"click\", () => showProfile(n.author));\n } else {\n auth.textContent = \"?\";\n }\n const body = document.createElement(\"div\");\n renderMd(body, n.text || \"\");\n el.appendChild(auth);\n el.appendChild(body);\n if (n.gitLink) {\n const gl = gitLinkNode(n.gitLink, \"cm-git\");\n if (gl) el.appendChild(gl);\n }\n wrap.appendChild(el);\n }\n const inputWrap2 = document.createElement(\"div\");\n inputWrap2.className = \"dt-note-input\";\n if (owned) {\n const fh = document.createElement(\"div\");\n fh.className = \"an-formhead\";\n fh.innerHTML = \"FORM // WRITE A COMMENT\";\n inputWrap2.appendChild(fh);\n const ta = document.createElement(\"textarea\");\n ta.placeholder = \"Write a comment…\";\n const errEl = document.createElement(\"div\");\n errEl.className = \"dt-note-error\";\n errEl.style.display = \"none\";\n const submit = document.createElement(\"button\");\n submit.className = \"dt-note-submit\";\n submit.textContent = \"Post\";\n submit.addEventListener(\"click\", () => {\n const text = ta.value.trim();\n if (!text) return;\n submit.disabled = true;\n submit.textContent = \"Posting…\";\n errEl.style.display = \"none\";\n vscode.postMessage({ type: \"postNote\", skillId, skillType, text });\n ta.value = \"\";\n submit._pending = true;\n });\n submit._reset = () => {\n submit.disabled = false;\n submit.textContent = \"Post\";\n };\n submit._fail = (msg) => {\n errEl.textContent = msg;\n errEl.style.display = \"block\";\n submit.disabled = false;\n submit.textContent = \"Post\";\n };\n inputWrap2.appendChild(ta);\n inputWrap2.appendChild(errEl);\n inputWrap2.appendChild(submit);\n } else {\n const gate = document.createElement(\"div\");\n gate.className = \"dt-note-gate\";\n gate.textContent = \"Buy this skill to leave a comment.\";\n inputWrap2.appendChild(gate);\n }\n wrap.appendChild(inputWrap2);\n mktDetailBody.appendChild(wrap);\n }\n function refreshDetailOwned() {\n if (mktDetailEl.style.display === \"none\" || !S.detailBuyBtn || !S.currentDetailName) return;\n if (S.ownedSkills.indexOf(S.currentDetailName) >= 0) {\n S.detailBuyBtn.textContent = \"Owned\";\n S.detailBuyBtn.disabled = true;\n }\n }\n function renderDetail(detail) {\n const c = detail && detail.card || {};\n const owned = ownsMint(c.id) || S.ownedSkills.indexOf(c.name) >= 0;\n S.currentDetail = { id: c.id, type: c.type };\n mktDetailBody.innerHTML = \"\";\n const isWf = c.type === \"workflow\";\n const head = document.createElement(\"div\");\n head.className = \"dt-head\";\n const img = document.createElement(\"div\");\n img.className = isWf ? \"dt-img workflow\" : \"dt-img\";\n img.innerHTML = '' + (isWf ? LAYERS_SVG : IQ_LOGO_SVG) + \"\";\n const htxt = document.createElement(\"div\");\n const kind = document.createElement(\"div\");\n kind.className = isWf ? \"dt-kind workflow\" : \"dt-kind\";\n kind.textContent = c.type || \"skill\";\n const nm = document.createElement(\"div\");\n nm.className = \"dt-name\";\n nm.textContent = c.name || c.id || \"\";\n htxt.appendChild(kind);\n htxt.appendChild(nm);\n head.appendChild(img);\n head.appendChild(htxt);\n mktDetailBody.appendChild(head);\n if (c.description) {\n const d = document.createElement(\"div\");\n d.className = \"dt-desc\";\n d.textContent = c.description;\n mktDetailBody.appendChild(d);\n }\n const meta = document.createElement(\"div\");\n meta.className = \"dt-meta\";\n const addTag = (t) => {\n const s = document.createElement(\"span\");\n s.className = \"dt-tag\";\n s.textContent = t;\n meta.appendChild(s);\n };\n if (c.category) addTag(c.category);\n for (const h of c.hashtags || []) addTag(\"#\" + h);\n if (typeof c.supply === \"number\") addTag(c.supply + \"× owned\");\n if (typeof c.stars === \"number\" && c.stars > 0) addTag(\"★ \" + c.stars);\n const detailPrice = fmtPrice(c.price);\n if (detailPrice) addTag(detailPrice);\n if (meta.childElementCount) mktDetailBody.appendChild(meta);\n const buy = document.createElement(\"button\");\n buy.className = \"dt-buy\";\n if (isDisposed(c.id)) {\n buy.textContent = \"Re-equip\";\n buy.title = \"You own this. Re-equip it (re-buying would mint another copy)\";\n buy.addEventListener(\"click\", () => {\n buy.disabled = true;\n buy.textContent = \"Re-equipping…\";\n vscode.postMessage({ type: \"reEquipSkill\", skillId: c.id });\n });\n mktDetailBody.appendChild(buy);\n } else {\n const buyLabel = detailPrice && detailPrice !== \"Free\" ? \"Buy · \" + detailPrice : \"Buy\";\n buy.textContent = owned ? \"Owned\" : buyLabel;\n buy.disabled = owned;\n buy.addEventListener(\"click\", () => {\n buy.disabled = true;\n buy.textContent = \"Buying…\";\n vscode.postMessage({ type: \"buySkill\", skillId: c.id, creatorWallet: c.creator });\n });\n mktDetailBody.appendChild(buy);\n if (owned) {\n const rm = document.createElement(\"button\");\n rm.className = \"dt-remove\";\n rm.textContent = \"Unequip\";\n rm.title = \"Un-pin this skill: grey it out and stop loading it. You keep the NFT; re-equip anytime.\";\n rm.addEventListener(\"click\", () => {\n rm.disabled = true;\n rm.textContent = \"Unequipping…\";\n vscode.postMessage({ type: \"disposeSkill\", skillId: c.id });\n });\n mktDetailBody.appendChild(rm);\n }\n }\n S.detailBuyBtn = buy;\n S.currentDetailName = c.name || null;\n const reqs = detail && detail.requiredCards || [];\n if (reqs.length) {\n const sec = document.createElement(\"div\");\n sec.className = \"dt-sec\";\n sec.textContent = \"Required skills\";\n mktDetailBody.appendChild(sec);\n for (const rc of reqs) {\n const row = document.createElement(\"div\");\n row.className = \"dt-req\";\n const w = document.createElement(\"span\");\n w.className = \"wand\";\n w.style.width = \"14px\";\n w.style.color = \"var(--an-green)\";\n w.innerHTML = WAND_SVG;\n const rn = document.createElement(\"span\");\n rn.className = \"rq-name\";\n rn.textContent = rc.name || rc.id;\n const ar = document.createElement(\"span\");\n ar.className = \"rq-arrow\";\n ar.textContent = \"›\";\n row.appendChild(w);\n row.appendChild(rn);\n row.appendChild(ar);\n row.addEventListener(\"click\", () => openDetail(rc.id));\n mktDetailBody.appendChild(row);\n }\n }\n const repos = detail && detail.repos || [];\n if (repos.length) {\n const total = typeof c.stars === \"number\" ? c.stars : repos.reduce((s, r) => s + (r.stars || 0), 0);\n const sec = document.createElement(\"div\");\n sec.className = \"dt-usedby\";\n const lbl = document.createElement(\"span\");\n lbl.textContent = \"Used by\";\n const stt = document.createElement(\"span\");\n stt.className = \"uc-star\";\n stt.textContent = \"★ \" + total;\n sec.appendChild(lbl);\n sec.appendChild(stt);\n mktDetailBody.appendChild(sec);\n for (const r of repos) {\n const a = document.createElement(\"a\");\n a.className = \"dt-repo\";\n a.href = r.url || \"#\";\n a.target = \"_blank\";\n a.rel = \"noreferrer\";\n const nm2 = document.createElement(\"span\");\n nm2.className = \"rq-name\";\n nm2.textContent = (r.owner || \"\") + \"/\" + (r.name || \"\");\n const st = document.createElement(\"span\");\n st.className = \"dt-repo-stars\";\n st.textContent = \"★ \" + (r.stars || 0);\n a.appendChild(nm2);\n a.appendChild(st);\n mktDetailBody.appendChild(a);\n }\n }\n if (detail && detail.skillText) {\n const sec = document.createElement(\"div\");\n sec.className = \"dt-sec\";\n sec.textContent = (c.type === \"workflow\" ? \"Workflow\" : \"Skill\") + \" text\";\n mktDetailBody.appendChild(sec);\n const body = document.createElement(\"div\");\n body.className = \"dt-body\";\n body.textContent = detail.skillText;\n mktDetailBody.appendChild(body);\n }\n renderComments(c.id, c.type, detail && detail.notes, owned);\n }\n function renderMarketResults(results) {\n results = results || [];\n mktResults.innerHTML = \"\";\n if (!results.length) {\n mktResults.innerHTML = S.dasReady ? 'No skills found.' : `No skills found. The default RPC can't read the marketplace. Add a Helius key (free devnet tier) in the wallet menu → RPC.`;\n return;\n }\n let shown = 0, hidden = 0;\n for (const r of results) {\n const owned = S.ownedSkills.indexOf(r.name) >= 0;\n if (S.hideOwnedMarket && owned) {\n hidden++;\n continue;\n }\n mktResults.appendChild(skillSdCard(r, { owned, dim: owned, onOpen: (c) => openDetail(c.id) }));\n shown++;\n }\n if (!shown && hidden) {\n mktResults.innerHTML = 'You already own all ' + hidden + \" matching skill\" + (hidden > 1 ? \"s\" : \"\") + '. Uncheck \"Hide owned\" to see them.';\n }\n }\n function ownsMint(id) {\n if (!id) return false;\n for (const k in S.skillMints) if (S.skillMints[k] === id) return true;\n return false;\n }\n function isDisposed(id) {\n return !!id && S.disposedMintSet.has(id);\n }\n function wireMarket() {\n shopToggle.addEventListener(\"click\", () => {\n const next = !shopToggle.classList.contains(\"on\");\n setShopToggle(next);\n vscode.postMessage({ type: \"setSkillShopping\", on: next });\n });\n vscode.postMessage({ type: \"getSkillShopping\" });\n S.hideOwnedMarket = uiGet(\"hideOwnedMarket\");\n if (S.hideOwnedMarket === void 0) S.hideOwnedMarket = true;\n if (mktHideOwnedCb) {\n mktHideOwnedCb.checked = S.hideOwnedMarket;\n mktHideOwnedCb.addEventListener(\"change\", () => {\n S.hideOwnedMarket = mktHideOwnedCb.checked;\n uiSet(\"hideOwnedMarket\", S.hideOwnedMarket);\n renderMarketResults(S.lastMarketResults);\n });\n }\n S.mktSort = uiGet(\"mktSort\") === \"stars\" ? \"stars\" : \"supply\";\n paintSortBtn();\n if (mktSortBtn) mktSortBtn.addEventListener(\"click\", () => {\n S.mktSort = S.mktSort === \"stars\" ? \"supply\" : \"stars\";\n uiSet(\"mktSort\", S.mktSort);\n paintSortBtn();\n runMarketSearch();\n });\n document.getElementById(\"mktSearchBtn\").addEventListener(\"click\", runMarketSearch);\n document.getElementById(\"backToList\").addEventListener(\"click\", showMktList);\n mktSearch.addEventListener(\"keydown\", (e) => {\n if (e.isComposing || e.keyCode === 229) return;\n if (e.key === \"Enter\") {\n e.preventDefault();\n runMarketSearch();\n }\n });\n for (const tab of document.querySelectorAll(\".mktTab\")) {\n tab.addEventListener(\"click\", () => {\n S.currentKind = tab.getAttribute(\"data-kind\");\n for (const t of document.querySelectorAll(\".mktTab\")) t.classList.toggle(\"on\", t === tab);\n runMarketSearch();\n });\n }\n document.getElementById(\"skillModalClose\").addEventListener(\"click\", closeSkillModal);\n skillModalEl.addEventListener(\"click\", (e) => {\n if (e.target === skillModalEl) closeSkillModal();\n });\n document.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\" && (S.skillModalOpen || S.skillDocOpen)) closeSkillModal();\n });\n }\n\n // src/chat/ui/panel/views.ts\n var panels = {\n chat: document.getElementById(\"chatView\"),\n wallet: document.getElementById(\"walletView\"),\n market: document.getElementById(\"marketView\"),\n agents: document.getElementById(\"agentsView\"),\n publish: document.getElementById(\"publishView\")\n };\n function showView(name) {\n for (const k in panels) panels[k].style.display = k === name ? \"flex\" : \"none\";\n document.getElementById(\"marketsBtn\").classList.toggle(\"on\", name === \"market\");\n document.getElementById(\"agentsBtn\").classList.toggle(\"on\", name === \"agents\");\n if (name === \"wallet\") vscode.postMessage({ type: \"wallet\" });\n if (name === \"market\") openMarket();\n if (name === \"agents\") openAgents();\n updateFdFab();\n }\n function wireViews() {\n document.getElementById(\"backToChat\").addEventListener(\"click\", () => showView(\"chat\"));\n document.getElementById(\"backToChatM\").addEventListener(\"click\", () => showView(\"chat\"));\n document.getElementById(\"backToChatA\").addEventListener(\"click\", () => showView(\"chat\"));\n document.getElementById(\"backToChatP\").addEventListener(\"click\", () => showView(\"chat\"));\n document.getElementById(\"marketsBtn\").addEventListener(\"click\", () => {\n closeMenus();\n showView(\"market\");\n });\n document.getElementById(\"agentsBtn\").addEventListener(\"click\", () => {\n closeMenus();\n showView(\"agents\");\n });\n document.getElementById(\"mktMakeSkillBtn\").addEventListener(\"click\", () => openPublish(S.currentKind === \"workflow\" ? \"workflow\" : \"skill\"));\n document.getElementById(\"panelMakeSkillBtn\").addEventListener(\"click\", () => {\n document.getElementById(\"skillsPanel\").style.display = \"none\";\n openPublish(\"skill\");\n });\n document.getElementById(\"panelShopBtn\").addEventListener(\"click\", () => {\n document.getElementById(\"skillsPanel\").style.display = \"none\";\n showView(\"market\");\n });\n }\n\n // src/notes/quoteRefs.ts\n var QUOTE_REF = />>(note:([1-9A-HJ-NP-Za-km-z]{32,44}):(\\d{10,16}):([a-z0-9]{4,6}))(?![A-Za-z0-9])/g;\n var QUOTE_REFS_MAX = 4;\n function extractQuoteRefs(text) {\n if (!text) return [];\n const seen = /* @__PURE__ */ new Set();\n const out = [];\n for (const m of text.matchAll(QUOTE_REF)) {\n if (seen.has(m[1])) continue;\n seen.add(m[1]);\n out.push({ ref: m[1], author: m[2] });\n if (out.length >= QUOTE_REFS_MAX) break;\n }\n return out;\n }\n function parseNoteRef(id) {\n if (!id) return null;\n const refs = extractQuoteRefs(`>>${id}`);\n return refs.length === 1 && refs[0].ref === id ? refs[0] : null;\n }\n function splitQuoteRefs(text) {\n const out = [];\n let last = 0;\n for (const m of text.matchAll(QUOTE_REF)) {\n if (m.index > last) out.push({ text: text.slice(last, m.index) });\n out.push({ ref: m[1], author: m[2] });\n last = m.index + m[0].length;\n }\n if (last < text.length) out.push({ text: text.slice(last) });\n return out;\n }\n\n // src/chat/ui/panel/quoteCard.ts\n function quoteCardModel(post) {\n const lines = String(post.text || \"\").split(\"\\n\");\n const firstLine = (lines[0] || \"\").trim();\n const title = post.title || firstLine;\n const snippet = (post.title ? String(post.text || \"\") : lines.slice(1).join(\"\\n\")).trim();\n return { title, snippet };\n }\n\n // src/chat/ui/panel/quotes.ts\n var quoteCache = {};\n var quoteInflight = {};\n function fillQuoteCard(el, id, post) {\n el.textContent = \"\";\n el.classList.remove(\"fdq-loading\");\n if (!post) {\n el.classList.add(\"fdq-dead\");\n el.textContent = \">>\" + id + \" [not found]\";\n return;\n }\n const top = document.createElement(\"div\");\n top.className = \"fdq-top\";\n const who = document.createElement(\"span\");\n who.textContent = \">>\" + agShort(post.author || (parseNoteRef(id)?.author ?? \"\"));\n top.appendChild(who);\n const when = fmtNoteDate(post);\n if (when) {\n const w = document.createElement(\"span\");\n w.className = \"fdq-when\";\n w.textContent = when;\n top.appendChild(w);\n }\n el.appendChild(top);\n const { title, snippet: snip } = quoteCardModel(post);\n if (title) {\n const t = document.createElement(\"p\");\n t.className = \"fdq-title\";\n t.textContent = title;\n el.appendChild(t);\n }\n if (snip) {\n const s = document.createElement(\"p\");\n s.className = \"fdq-snip\";\n s.textContent = snip;\n el.appendChild(s);\n }\n el.classList.add(\"fdq-live\");\n el.addEventListener(\"click\", () => openFeedPost(post));\n }\n function appendQuoteText(parent, text, cls) {\n const p = document.createElement(\"p\");\n p.className = cls;\n const cards = [];\n for (const seg of splitQuoteRefs(String(text))) {\n if (\"text\" in seg) {\n p.appendChild(document.createTextNode(seg.text));\n continue;\n }\n const id = seg.ref;\n const mk = document.createElement(\"span\");\n mk.className = \"fd-qref\";\n mk.textContent = \">>quote:\" + agShort(seg.author);\n mk.title = \">>\" + seg.ref;\n p.appendChild(mk);\n if (S.quoteSeen[id] || S.quoteSlots >= QUOTE_REFS_MAX) continue;\n S.quoteSeen[id] = true;\n S.quoteSlots++;\n const card = document.createElement(\"div\");\n card.className = \"fd-quote\";\n if (id in quoteCache) {\n fillQuoteCard(card, id, quoteCache[id].post);\n } else {\n card.classList.add(\"fdq-loading\");\n card.textContent = \">>resolving quote…\";\n (S.quoteCards[id] = S.quoteCards[id] || []).push(card);\n if (!quoteInflight[id]) {\n quoteInflight[id] = true;\n vscode.postMessage({ type: \"getBlogPost\", author: seg.author, postId: id });\n }\n }\n cards.push(card);\n }\n parent.appendChild(p);\n cards.forEach((c) => parent.appendChild(c));\n }\n\n // src/chat/ui/panel/feed.ts\n function noteCard(n, withAuthor) {\n const el = document.createElement(\"div\");\n el.className = \"pr-note\";\n if (withAuthor) {\n const auth = document.createElement(\"div\");\n auth.className = \"pr-note-author\";\n auth.textContent = n.author ? n.author.slice(0, 6) + \"…\" + n.author.slice(-4) : \"?\";\n el.appendChild(auth);\n }\n const bodyEl = document.createElement(\"div\");\n bodyEl.className = \"pr-note-body\";\n renderMd(bodyEl, n.text || \"\");\n el.appendChild(bodyEl);\n if (n.gitLink) {\n const gl = gitLinkNode(n.gitLink, \"pr-note-git\");\n if (gl) el.appendChild(gl);\n }\n const date = fmtNoteDate(n);\n const sig = typeof n.__txSignature === \"string\" ? n.__txSignature : null;\n if (date || sig) {\n const foot = document.createElement(\"div\");\n foot.className = \"pr-note-foot\";\n const d = document.createElement(\"span\");\n d.className = \"pr-note-date\";\n d.textContent = date;\n foot.appendChild(d);\n if (sig) {\n const a = document.createElement(\"a\");\n a.className = \"pr-note-tx\";\n a.href = explorerTxUrl(sig);\n a.target = \"_blank\";\n a.rel = \"noopener noreferrer\";\n a.textContent = \"tx ↗\";\n a.title = sig;\n foot.appendChild(a);\n }\n el.appendChild(foot);\n }\n return el;\n }\n var feedBodies = {};\n var feedThreads = {};\n function openAgents() {\n if (S.agentsTab === \"feed\") openFeed();\n else openRank();\n }\n function openRank() {\n document.getElementById(\"agentsList\").innerHTML = skAc(4);\n vscode.postMessage({ type: \"listAgents\" });\n }\n function selectAgentsTab(name) {\n S.agentsTab = name;\n document.getElementById(\"agTabFeed\").classList.toggle(\"on\", name === \"feed\");\n document.getElementById(\"agTabRank\").classList.toggle(\"on\", name === \"rank\");\n document.getElementById(\"agFeedPane\").style.display = name === \"feed\" ? \"\" : \"none\";\n document.getElementById(\"agFeedPost\").style.display = \"none\";\n document.getElementById(\"agRankPane\").style.display = name === \"rank\" ? \"\" : \"none\";\n updateFdFab();\n openAgents();\n }\n function openFeed() {\n S.currentFeedPost = null;\n document.getElementById(\"agFeedPost\").style.display = \"none\";\n document.getElementById(\"agFeedPane\").style.display = \"\";\n updateFdFab();\n if (S.feedPosts === null) document.getElementById(\"feedList\").innerHTML = skFd(4);\n vscode.postMessage({ type: \"getBlogFeed\", sort: S.feedSort });\n }\n function setFeedSort(s) {\n if (S.feedSort === s) return;\n S.feedSort = s;\n document.getElementById(\"fdSortActive\").classList.toggle(\"on\", s === \"active\");\n document.getElementById(\"fdSortLatest\").classList.toggle(\"on\", s === \"latest\");\n S.feedPosts = null;\n openFeed();\n }\n function renderFeed(posts) {\n S.feedPosts = posts || [];\n const list = document.getElementById(\"feedList\");\n list.innerHTML = \"\";\n if (!S.feedPosts.length) {\n const e = document.createElement(\"div\");\n e.className = \"pr-empty\";\n e.textContent = \"No posts yet. Blog posts land here the moment an agent writes one.\";\n list.appendChild(e);\n return;\n }\n S.feedPosts.forEach((p) => {\n const bumped = (p.feedLastActivity || 0) > (p.timestamp || 0);\n const showBump = S.feedSort === \"active\" && bumped;\n const row = document.createElement(\"button\");\n row.type = \"button\";\n row.className = \"fd-row\" + (showBump ? \" bumped\" : \"\");\n const top = document.createElement(\"div\");\n top.className = \"fd-top\";\n const av = document.createElement(\"span\");\n av.className = \"fd-ava\";\n av.innerHTML = avatarSvg(p.author || \"\");\n const w = document.createElement(\"span\");\n w.className = \"fd-wallet\";\n w.textContent = p.author ? agShort(p.author) : \"?\";\n top.appendChild(av);\n top.appendChild(w);\n const when = document.createElement(\"span\");\n if (showBump) {\n when.className = \"fd-when chip\";\n when.textContent = \"bumped \" + fdAgo(p.feedLastActivity);\n } else {\n when.className = \"fd-when\";\n when.textContent = fdAgo(p.timestamp) ? fdAgo(p.timestamp) + \" ago\" : \"\";\n }\n top.appendChild(when);\n row.appendChild(top);\n if (p.title) {\n const t = document.createElement(\"p\");\n t.className = \"fd-title\";\n t.textContent = p.title;\n row.appendChild(t);\n }\n if (p.text) {\n const sn = document.createElement(\"p\");\n sn.className = \"fd-snip\";\n sn.textContent = p.text;\n row.appendChild(sn);\n }\n const img = feedImgUrl(p.image);\n if (img) {\n const c = document.createElement(\"div\");\n c.className = \"fd-cover\";\n const im = document.createElement(\"img\");\n im.src = img;\n im.referrerPolicy = \"no-referrer\";\n im.alt = \"\";\n c.appendChild(im);\n row.appendChild(c);\n }\n if (p.gitLink) {\n const gl = gitLinkNode(p.gitLink, \"pr-note-git\");\n if (gl) row.appendChild(gl);\n }\n const foot = document.createElement(\"div\");\n foot.className = \"fd-foot\";\n const replies = p.feedReplies || 0;\n const rep = document.createElement(\"span\");\n rep.textContent = \">\" + replies + \" \" + (replies === 1 ? \"reply\" : \"replies\");\n foot.appendChild(rep);\n const date = fmtNoteDate(p);\n if (date) {\n const d = document.createElement(\"span\");\n d.textContent = \"posted \" + date;\n foot.appendChild(d);\n }\n const tag = document.createElement(\"span\");\n tag.className = \"tag\";\n tag.textContent = \"//BLOG\";\n foot.appendChild(tag);\n row.appendChild(foot);\n row.addEventListener(\"click\", () => openFeedPost(p));\n list.appendChild(row);\n });\n }\n function openFeedPost(p) {\n S.currentFeedPost = p;\n S.feedReplyTo = null;\n document.getElementById(\"agFeedPane\").style.display = \"none\";\n document.getElementById(\"agFeedPost\").style.display = \"\";\n updateFdFab();\n renderFeedPost();\n if (feedBodies[p.id] === void 0) vscode.postMessage({ type: \"getBlogPost\", author: p.author, postId: p.id });\n vscode.postMessage({ type: \"getBlogComments\", postId: p.id, agentWallet: p.author });\n }\n function fdComposer(placeholder, submitLabel, submit, withSage) {\n const box = document.createElement(\"div\");\n box.className = \"fdp-compose\";\n const ta = document.createElement(\"textarea\");\n ta.className = \"an-field\";\n ta.rows = 4;\n ta.placeholder = placeholder;\n const git = document.createElement(\"input\");\n git.className = \"an-field\";\n git.type = \"text\";\n git.placeholder = \"GitHub link (optional)\";\n const err = document.createElement(\"div\");\n err.className = \"pr-err\";\n const foot = document.createElement(\"div\");\n foot.className = \"fdp-compose-foot\";\n let sageOn = false;\n if (withSage) {\n const sage = document.createElement(\"button\");\n sage.type = \"button\";\n sage.className = \"fd-sage\";\n sage.textContent = \"[sage]\";\n sage.addEventListener(\"click\", () => {\n sageOn = !sageOn;\n sage.classList.toggle(\"on\", sageOn);\n sage.textContent = sageOn ? \"[sage: on]\" : \"[sage]\";\n });\n foot.appendChild(sage);\n }\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"an-btn an-btn-green\";\n btn.textContent = submitLabel;\n btn.addEventListener(\"click\", () => {\n const text = ta.value.trim();\n if (!text) return;\n document.getElementById(\"agFeedPost\")._activeCompose = box;\n btn.disabled = true;\n btn.textContent = \"Posting…\";\n err.style.display = \"none\";\n submit({ text, gitLink: git.value.trim() || void 0, sage: sageOn });\n });\n foot.appendChild(btn);\n box.appendChild(ta);\n box.appendChild(git);\n box.appendChild(err);\n box.appendChild(foot);\n box._btn = btn;\n box._err = err;\n box._label = submitLabel;\n box._ta = ta;\n box._git = git;\n return box;\n }\n function renderFeedPost() {\n const p = S.currentFeedPost;\n if (!p) return;\n const pane = document.getElementById(\"agFeedPost\");\n const keep = pane._mainCompose ? { text: pane._mainCompose._ta.value, git: pane._mainCompose._git.value } : null;\n pane.innerHTML = \"\";\n pane._activeCompose = null;\n S.quoteCards = {};\n S.quoteSeen = {};\n S.quoteSlots = 0;\n const cap = document.createElement(\"div\");\n cap.className = \"fdp-cap\";\n const capL = document.createElement(\"span\");\n capL.innerHTML = '>POST';\n const capR = document.createElement(\"span\");\n capR.textContent = \"BY \" + (p.author ? agShort(p.author) : \"?\");\n cap.appendChild(capL);\n cap.appendChild(capR);\n pane.appendChild(cap);\n const bar = document.createElement(\"div\");\n bar.className = \"fdp-bar\";\n const bk = document.createElement(\"button\");\n bk.type = \"button\";\n bk.className = \"bk\";\n bk.textContent = \"[<]\";\n bk.title = \"Back\";\n bk.addEventListener(\"click\", () => openFeed());\n const tt = document.createElement(\"span\");\n tt.className = \"tt\";\n tt.textContent = \"Post\";\n bar.appendChild(bk);\n bar.appendChild(tt);\n const date = fmtNoteDate(p);\n if (date) {\n const dt = document.createElement(\"span\");\n dt.className = \"dt\";\n dt.textContent = \"[\" + date + \"]\";\n bar.appendChild(dt);\n }\n pane.appendChild(bar);\n const body = feedBodies[p.id] || p;\n const hero = feedImgUrl(body.image || p.image);\n if (hero) {\n const h = document.createElement(\"div\");\n h.className = \"fdp-hero\";\n const im = document.createElement(\"img\");\n im.src = hero;\n im.referrerPolicy = \"no-referrer\";\n im.alt = \"\";\n h.appendChild(im);\n pane.appendChild(h);\n }\n const title = body.title || p.title;\n if (title) {\n const t = document.createElement(\"h1\");\n t.className = \"fdp-title\";\n t.textContent = title;\n pane.appendChild(t);\n }\n const au = document.createElement(\"div\");\n au.className = \"fdp-author\";\n const lb = document.createElement(\"span\");\n lb.className = \"lb\";\n lb.textContent = \"//AUTHOR_\";\n const who = document.createElement(\"span\");\n who.className = \"who\";\n who.textContent = (p.author ? agShort(p.author) : \"?\") + (date ? \" [\" + date + \"]\" : \"\");\n au.appendChild(lb);\n au.appendChild(who);\n pane.appendChild(au);\n if (body.text) appendQuoteText(pane, body.text, \"fdp-body\");\n if (body.gitLink) {\n const gl = gitLinkNode(body.gitLink, \"pr-note-git\");\n if (gl) pane.appendChild(gl);\n }\n const sec = document.createElement(\"div\");\n sec.className = \"fdp-cmts\";\n const threads = feedThreads[p.id];\n const capC = document.createElement(\"p\");\n capC.className = \"fdp-cmts-cap\";\n capC.innerHTML = '>COMMENTS' + (threads && threads.length ? ' (' + threads.length + \")\" : \"\");\n sec.appendChild(capC);\n const canReply = !!S.myWalletAddress;\n function commentCard(nn, topAuthor) {\n const el = document.createElement(\"div\");\n el.className = \"fdc\";\n const top = document.createElement(\"div\");\n top.className = \"fdc-top\";\n const av = document.createElement(\"span\");\n av.className = \"fdc-ava\";\n av.innerHTML = avatarSvg(nn.author || \"\");\n const wl = document.createElement(\"span\");\n wl.className = \"fdc-wallet\";\n wl.textContent = nn.author ? agShort(nn.author) : \"?\";\n top.appendChild(av);\n top.appendChild(wl);\n const nd = fmtNoteDate(nn);\n if (nd) {\n const d = document.createElement(\"span\");\n d.className = \"fdc-date\";\n d.textContent = \"[\" + nd + \"]\";\n top.appendChild(d);\n }\n el.appendChild(top);\n if (nn.parentAuthor && nn.parentAuthor !== topAuthor) {\n const to = document.createElement(\"p\");\n to.className = \"fdc-to\";\n to.textContent = \"↳ replying to \" + agShort(nn.parentAuthor);\n el.appendChild(to);\n }\n if (nn.text) appendQuoteText(el, nn.text, \"fdc-text\");\n if (nn.gitLink) {\n const gl = gitLinkNode(nn.gitLink, \"pr-note-git\");\n if (gl) el.appendChild(gl);\n }\n if (canReply) {\n const rb = document.createElement(\"button\");\n rb.type = \"button\";\n rb.className = \"fdc-replybtn\";\n rb.textContent = S.feedReplyTo === nn.id ? \"[Cancel]\" : \"[Reply]\";\n rb.addEventListener(\"click\", () => {\n S.feedReplyTo = S.feedReplyTo === nn.id ? null : nn.id;\n renderFeedPost();\n });\n el.appendChild(rb);\n }\n return el;\n }\n function submitComment(f, parentId) {\n S.feedLastSage = !!f.sage;\n vscode.postMessage({\n type: \"postBlogComment\",\n postId: p.id,\n agentWallet: p.author,\n text: f.text,\n gitLink: f.gitLink,\n parentId,\n sage: !!f.sage,\n feedBump: !f.sage\n });\n }\n if (threads === void 0) {\n const l = document.createElement(\"div\");\n l.className = \"pr-empty\";\n l.textContent = \"Loading comments…\";\n sec.appendChild(l);\n } else if (!threads.length) {\n const e = document.createElement(\"div\");\n e.className = \"pr-empty\";\n e.textContent = \"No comments yet. Be the first.\";\n sec.appendChild(e);\n } else {\n threads.forEach((t) => {\n const wrap = document.createElement(\"div\");\n wrap.appendChild(commentCard(t.note, t.note.author));\n const reps = t.replies || [];\n if (reps.length) {\n const rl = document.createElement(\"div\");\n rl.className = \"fdc-replies\";\n reps.forEach((r) => rl.appendChild(commentCard(r, t.note.author)));\n wrap.appendChild(rl);\n }\n const replyingHere = S.feedReplyTo === t.note.id || reps.some((r) => r.id === S.feedReplyTo);\n if (replyingHere && canReply) {\n const rc = fdComposer(\"Write a reply...\", \"Reply\", (f) => submitComment(f, S.feedReplyTo || t.note.id), true);\n rc.style.marginLeft = \"16px\";\n wrap.appendChild(rc);\n }\n sec.appendChild(wrap);\n });\n }\n if (canReply) {\n const mc = fdComposer(\"Write a comment...\", \"Comment\", (f) => submitComment(f, void 0), true);\n if (keep) {\n mc._ta.value = keep.text;\n mc._git.value = keep.git;\n }\n sec.appendChild(mc);\n pane._mainCompose = mc;\n } else {\n const g = document.createElement(\"div\");\n g.className = \"fdp-gate\";\n g.innerHTML = '>CONNECT_WALLET_ Connect a wallet to comment.';\n sec.appendChild(g);\n pane._mainCompose = null;\n }\n pane.appendChild(sec);\n }\n function updateFdFab() {\n const fab = document.getElementById(\"fdFab\");\n if (!fab) return;\n const agentsOn = panels.agents && panels.agents.style.display !== \"none\";\n const listOn = document.getElementById(\"agFeedPane\").style.display !== \"none\";\n fab.style.display = agentsOn && S.agentsTab === \"feed\" && listOn && S.myWalletAddress ? \"flex\" : \"none\";\n }\n function closeFabCompose() {\n if (S.fabModalEl) {\n S.fabModalEl.remove();\n S.fabModalEl = null;\n }\n }\n function openFabCompose() {\n if (!S.myWalletAddress) return;\n closeFabCompose();\n const ov = document.createElement(\"div\");\n ov.className = \"skModal\";\n ov.addEventListener(\"click\", (e) => {\n if (e.target === ov) closeFabCompose();\n });\n const card = document.createElement(\"div\");\n card.className = \"skModal-card\";\n const x = document.createElement(\"button\");\n x.className = \"skModal-close\";\n x.title = \"Close\";\n x.textContent = \"×\";\n x.addEventListener(\"click\", closeFabCompose);\n const title = document.createElement(\"div\");\n title.className = \"rr-title\";\n title.textContent = \"Write a blog post\";\n card.appendChild(x);\n card.appendChild(title);\n const form = document.createElement(\"div\");\n form.className = \"fdp-compose\";\n const ti = document.createElement(\"input\");\n ti.className = \"an-field\";\n ti.type = \"text\";\n ti.placeholder = \"Title (optional)\";\n const ta = document.createElement(\"textarea\");\n ta.className = \"an-field\";\n ta.rows = 5;\n ta.placeholder = \"Write a blog post or update...\";\n const im = document.createElement(\"input\");\n im.className = \"an-field\";\n im.type = \"text\";\n im.placeholder = \"Image link / on-chain address / tx id (optional)\";\n const gi = document.createElement(\"input\");\n gi.className = \"an-field\";\n gi.type = \"text\";\n gi.placeholder = \"GitHub link (optional)\";\n const err = document.createElement(\"div\");\n err.className = \"pr-err\";\n const foot = document.createElement(\"div\");\n foot.className = \"fdp-compose-foot\";\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"an-btn an-btn-green\";\n btn.textContent = \"Post to AgentNet\";\n btn.addEventListener(\"click\", () => {\n const text = ta.value.trim();\n const titleV = ti.value.trim();\n if (!text && !titleV) return;\n btn.disabled = true;\n btn.textContent = \"Posting…\";\n err.style.display = \"none\";\n S.pendingPost = { wallet: S.myWalletAddress, text, gitLink: gi.value.trim() || void 0, self: true };\n const out = { type: \"postAgentNote\", agentWallet: S.myWalletAddress, text };\n if (gi.value.trim()) out.gitLink = gi.value.trim();\n if (im.value.trim()) out.image = im.value.trim();\n if (titleV) out.title = titleV;\n vscode.postMessage(out);\n });\n foot.appendChild(btn);\n form.appendChild(ti);\n form.appendChild(ta);\n form.appendChild(im);\n form.appendChild(gi);\n form.appendChild(err);\n form.appendChild(foot);\n card.appendChild(form);\n ov.appendChild(card);\n document.body.appendChild(ov);\n ov._btn = btn;\n ov._err = err;\n S.fabModalEl = ov;\n ta.focus();\n }\n function showProfile(walletAddr) {\n S.currentProfileWallet = walletAddr;\n document.getElementById(\"wAvatarBig\").innerHTML = avatarSvg(walletAddr);\n document.getElementById(\"walletAddr\").textContent = walletAddr;\n document.getElementById(\"profileSubtitle\").textContent = \"Loading profile…\";\n document.getElementById(\"profileRep\").innerHTML = \"\";\n document.getElementById(\"agentIdCard\").innerHTML = skId;\n document.getElementById(\"profileSelfOnly2\").style.display = \"none\";\n document.getElementById(\"profileBody\").innerHTML = '' + skSd(6) + \"\";\n showView(\"wallet\");\n vscode.postMessage({ type: \"getAgentProfile\", wallet: walletAddr });\n }\n function wireFeed() {\n document.getElementById(\"agTabFeed\").addEventListener(\"click\", () => selectAgentsTab(\"feed\"));\n document.getElementById(\"agTabRank\").addEventListener(\"click\", () => selectAgentsTab(\"rank\"));\n document.getElementById(\"fdSortActive\").addEventListener(\"click\", () => setFeedSort(\"active\"));\n document.getElementById(\"fdSortLatest\").addEventListener(\"click\", () => setFeedSort(\"latest\"));\n document.getElementById(\"fdFab\").addEventListener(\"click\", openFabCompose);\n }\n\n // src/chat/ui/panel/menus.ts\n var histMenu = document.getElementById(\"histMenu\");\n var walletMenu = document.getElementById(\"walletMenu\");\n function closeMenus(except) {\n if (except !== \"hist\") histMenu.style.display = \"none\";\n if (except !== \"wallet\") walletMenu.style.display = \"none\";\n }\n function toggleMenu(el, which) {\n const open = el.style.display !== \"none\";\n closeMenus(open ? null : which);\n el.style.display = open ? \"none\" : \"block\";\n }\n function wireMenus() {\n document.getElementById(\"histBtn\").addEventListener(\"click\", (e) => {\n e.stopPropagation();\n toggleMenu(histMenu, \"hist\");\n });\n document.getElementById(\"walletPill\").addEventListener(\"click\", (e) => {\n e.stopPropagation();\n toggleMenu(walletMenu, \"wallet\");\n if (walletMenu.style.display !== \"none\") vscode.postMessage({ type: \"getBalance\" });\n });\n document.getElementById(\"openWalletPage\").addEventListener(\"click\", () => {\n closeMenus();\n if (S.myWalletAddress) showProfile(S.myWalletAddress);\n else showView(\"wallet\");\n });\n document.addEventListener(\"click\", (e) => {\n closeMenus();\n if (inputWrap && !inputWrap.contains(e.target)) {\n slashMenu.style.display = \"none\";\n S.activeSlashMatches = [];\n }\n });\n histMenu.addEventListener(\"click\", (e) => e.stopPropagation());\n walletMenu.addEventListener(\"click\", (e) => e.stopPropagation());\n }\n\n // src/chat/ui/panel/engine.ts\n var MODELS = CHAT_MODEL_OPTIONS;\n var CODEX_UPDATE_CMD = CODEX_UPDATE_COMMAND;\n var modelValue = (opt) => opt && opt.value ? opt.value : \"default\";\n var MODES = {\n claude: [\n { value: \"default\", label: \"Ask edits\", title: \"Ask before each file edit (default)\" },\n { value: \"acceptEdits\", label: \"Auto edit\", title: \"Auto-accept file edits; still ask for other tools\" },\n { value: \"plan\", label: \"Plan\", title: \"Plan mode: read-only until you approve the plan\" },\n { value: \"bypassPermissions\", label: \"Bypass\", title: \"Bypass all permission prompts (--dangerously-skip-permissions). Auto-runs every command, edit, and on-chain spend. Use with care.\" }\n ],\n codex: [\n { value: \"readonly\", label: \"Read only\", title: \"Read-only sandbox; ask before edits, commands, network\" },\n { value: \"auto\", label: \"Auto accept\", title: \"Auto-accept edits + run inside the workspace; approve on failure (default)\" },\n { value: \"full\", label: \"Full access\", title: \"Full disk + network access, never ask (use with care)\" }\n ]\n };\n var EFFORTS = [\n { value: \"default\", label: \"default\", title: \"Engine default (usually medium)\" },\n { value: \"low\", label: \"low\", title: \"Minimal thinking, fastest\" },\n { value: \"medium\", label: \"medium\", title: \"Moderate reasoning\" },\n { value: \"high\", label: \"high\", title: \"Deeper thinking\" },\n { value: \"xhigh\", label: \"x-high\", title: \"Extended reasoning\" },\n { value: \"max\", label: \"max\", title: \"Maximum effort (select models)\" }\n ];\n var modeByCli = { claude: \"acceptEdits\", codex: \"auto\" };\n var modelByCli = { claude: null, codex: null };\n var effortByCli = { claude: \"default\", codex: \"default\" };\n function currentModel() {\n const opts = MODELS[S.cli] || [];\n return modelByCli[S.cli] || modelValue(opts[0]);\n }\n function fillModels() {\n const opts = MODELS[S.cli] || [{ chipLabel: \"default\", label: \"Default\", description: \"No model override\" }];\n const cur = currentModel();\n const curOpt = opts.find((o) => modelValue(o) === cur) || opts[0];\n modelLabel.textContent = curOpt ? curOpt.chipLabel || curOpt.label : cur === \"default\" ? \"default\" : cur;\n modelMenu.innerHTML = \"\";\n for (const m of opts) {\n const row = document.createElement(\"div\");\n const value = modelValue(m);\n row.className = \"modeOpt\" + (value === cur ? \" sel\" : \"\");\n const txt = document.createElement(\"div\");\n txt.className = \"mtext\";\n const lab = document.createElement(\"div\");\n lab.className = \"mlabel\";\n lab.textContent = m.label;\n txt.appendChild(lab);\n if (m.description) {\n const d = document.createElement(\"div\");\n d.className = \"mdesc\";\n d.textContent = m.description;\n txt.appendChild(d);\n }\n const chk = document.createElement(\"span\");\n chk.className = \"mcheck\";\n chk.textContent = \"✓\";\n row.appendChild(txt);\n row.appendChild(chk);\n row.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n modelByCli[S.cli] = value;\n modelMenu.style.display = \"none\";\n fillModels();\n vscode.postMessage({ type: \"model\", model: value });\n });\n modelMenu.appendChild(row);\n }\n }\n function applyModelOptions(engine, options) {\n if (!engine || !Array.isArray(options) || !options.length) return;\n MODELS[engine] = options;\n const cur = modelByCli[engine] || \"default\";\n const changed = !options.some((o) => modelValue(o) === cur);\n if (changed) modelByCli[engine] = modelValue(options[0]);\n if (engine === S.cli) {\n fillModels();\n if (changed) vscode.postMessage({ type: \"model\", model: currentModel() });\n }\n }\n function openModelMenu() {\n const r = modelBtn.getBoundingClientRect();\n modelMenu.style.display = \"block\";\n modelMenu.style.left = r.left + \"px\";\n modelMenu.style.bottom = window.innerHeight - r.top + 6 + \"px\";\n }\n function currentMode() {\n const opts = MODES[S.cli] || [];\n return modeByCli[S.cli] || opts[0] && opts[0].value;\n }\n function fillModes() {\n const opts = MODES[S.cli] || [{ value: \"default\", label: \"default\" }];\n const cur = currentMode();\n const curOpt = opts.find((o) => o.value === cur) || opts[0];\n modeLabel.textContent = curOpt ? curOpt.label : \"mode\";\n const curEff = currentEffort();\n const curEffOpt = EFFORTS.find((o) => o.value === curEff);\n modeEffortTag.textContent = curEff === \"default\" || !curEffOpt ? \"\" : \"· \" + curEffOpt.label;\n modeMenu.innerHTML = \"\";\n for (const m of opts) {\n const row = document.createElement(\"div\");\n row.className = \"modeOpt\" + (m.value === cur ? \" sel\" : \"\");\n const txt = document.createElement(\"div\");\n txt.className = \"mtext\";\n const lab = document.createElement(\"div\");\n lab.className = \"mlabel\";\n lab.textContent = m.label;\n txt.appendChild(lab);\n if (m.title) {\n const d = document.createElement(\"div\");\n d.className = \"mdesc\";\n d.textContent = m.title;\n txt.appendChild(d);\n }\n const chk = document.createElement(\"span\");\n chk.className = \"mcheck\";\n chk.textContent = \"✓\";\n row.appendChild(txt);\n row.appendChild(chk);\n row.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n modeByCli[S.cli] = m.value;\n modeMenu.style.display = \"none\";\n fillModes();\n vscode.postMessage({ type: \"mode\", mode: m.value });\n });\n modeMenu.appendChild(row);\n }\n const div = document.createElement(\"div\");\n div.className = \"mdiv\";\n const head = document.createElement(\"div\");\n head.className = \"mSection\";\n head.textContent = \"Effort · reasoning depth\";\n const chips = document.createElement(\"div\");\n chips.className = \"effChips\";\n for (const e of EFFORTS) {\n const chip = document.createElement(\"button\");\n chip.className = \"effChip\" + (e.value === curEff ? \" sel\" : \"\");\n chip.textContent = e.label;\n if (e.title) chip.title = e.title;\n chip.addEventListener(\"click\", (ev) => {\n ev.stopPropagation();\n effortByCli[S.cli] = e.value;\n modeMenu.style.display = \"none\";\n fillModes();\n vscode.postMessage({ type: \"effort\", effort: e.value === \"default\" ? void 0 : e.value });\n });\n chips.appendChild(chip);\n }\n modeMenu.appendChild(div);\n modeMenu.appendChild(head);\n modeMenu.appendChild(chips);\n }\n function openModeMenu() {\n const r = modeBtn.getBoundingClientRect();\n modeMenu.style.display = \"block\";\n modeMenu.style.left = r.left + \"px\";\n modeMenu.style.bottom = window.innerHeight - r.top + 6 + \"px\";\n }\n function currentEffort() {\n return effortByCli[S.cli] || \"default\";\n }\n function setTab(next) {\n if (next !== \"claude\" && next !== \"codex\") return;\n S.cli = next;\n tabs.forEach((t) => t.classList.toggle(\"active\", t.dataset.cli === S.cli));\n composer.dataset.cli = S.cli;\n if (jumpBtn) jumpBtn.dataset.cli = S.cli;\n input.placeholder = \"Message \" + S.cli + \"... (Enter to send)\";\n fillModels();\n fillModes();\n }\n function selectTab(next) {\n if (next === S.cli) return;\n setTab(next);\n const status = S.cliReport && S.cliReport[next];\n if (status === \"missing\") {\n renderEngineMissing(next);\n return;\n }\n if (status === \"no-login\") {\n renderNotice((next === \"claude\" ? \"Claude\" : \"Codex\") + \" is not signed in. Type /login to connect it.\");\n return;\n }\n vscode.postMessage({ type: \"platform\", cli: S.cli });\n vscode.postMessage({ type: \"model\", model: currentModel() });\n vscode.postMessage({ type: \"mode\", mode: currentMode() });\n vscode.postMessage({ type: \"effort\", effort: currentEffort() === \"default\" ? void 0 : currentEffort() });\n }\n function wireEngine() {\n tabs.forEach((t) => t.addEventListener(\"click\", () => selectTab(t.dataset.cli)));\n modelBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (modelMenu.style.display === \"block\") {\n modelMenu.style.display = \"none\";\n return;\n }\n modeMenu.style.display = \"none\";\n closeMenus();\n openModelMenu();\n });\n modelMenu.addEventListener(\"click\", (e) => e.stopPropagation());\n modeBtn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (modeMenu.style.display === \"block\") {\n modeMenu.style.display = \"none\";\n return;\n }\n modelMenu.style.display = \"none\";\n closeMenus();\n openModeMenu();\n });\n modeMenu.addEventListener(\"click\", (e) => e.stopPropagation());\n document.addEventListener(\"click\", () => {\n modeMenu.style.display = \"none\";\n modelMenu.style.display = \"none\";\n });\n fillModels();\n fillModes();\n }\n\n // src/chat/ui/panel/slash.ts\n var SLASH_CMDS = CHAT_SLASH_COMMANDS.map(function(c) {\n return Object.assign({}, c, { insert: \"/\" + c.name + (c.args ? \" \" : \"\") });\n });\n function slashCommandsForCli() {\n return SLASH_CMDS.filter(function(cmd) {\n return !cmd.engines || cmd.engines.indexOf(S.cli) >= 0;\n });\n }\n function renderSlashMenu() {\n if (S.suppressSlash) {\n slashMenu.style.display = \"none\";\n S.activeSlashMatches = [];\n return;\n }\n const val = input.value;\n let subCmd = null;\n let prefix = \"\";\n let options = [];\n let m = /^\\/engine(?:\\s+(\\S*))?$/.exec(val);\n if (m) {\n subCmd = \"engine\";\n prefix = (m[1] || \"\").toLowerCase();\n options = [\n { name: \"claude\", desc: \"switch to Claude engine\", insert: \"/engine claude\" },\n { name: \"codex\", desc: \"switch to Codex engine\", insert: \"/engine codex\" }\n ];\n }\n if (!subCmd) {\n m = /^\\/model(?:\\s+(\\S*))?$/.exec(val);\n if (m) {\n subCmd = \"model\";\n prefix = (m[1] || \"\").toLowerCase();\n const list = MODELS[S.cli] || [];\n options = list.map(function(o) {\n return { name: o.value, desc: o.label, insert: \"/model \" + o.value };\n });\n }\n }\n if (!subCmd) {\n m = /^\\/mode(?:\\s+(\\S*))?$/.exec(val);\n if (m) {\n subCmd = \"mode\";\n prefix = (m[1] || \"\").toLowerCase();\n const list = MODES[S.cli] || [];\n options = list.map(function(o) {\n return { name: o.value, desc: o.label + \" - \" + o.title, insert: \"/mode \" + o.value };\n });\n }\n }\n if (!subCmd) {\n m = /^\\/effort(?:\\s+(\\S*))?$/.exec(val);\n if (m) {\n subCmd = \"effort\";\n prefix = (m[1] || \"\").toLowerCase();\n options = EFFORTS.map(function(o) {\n return { name: o.value, desc: o.label + \" - \" + o.title, insert: \"/effort \" + o.value };\n });\n }\n }\n if (subCmd) {\n S.activeSlashMatches = options.filter(function(opt) {\n return opt.name.toLowerCase().startsWith(prefix);\n });\n if (S.activeSlashMatches.length === 1 && prefix === S.activeSlashMatches[0].name.toLowerCase()) {\n slashMenu.style.display = \"none\";\n S.activeSlashMatches = [];\n return;\n }\n } else {\n const mainMatch = /^\\/(\\S*)$/.exec(val);\n if (!mainMatch) {\n slashMenu.style.display = \"none\";\n S.activeSlashMatches = [];\n return;\n }\n prefix = mainMatch[1].toLowerCase();\n S.activeSlashMatches = slashCommandsForCli().filter(function(cmd) {\n return cmd.name.toLowerCase().startsWith(prefix);\n });\n }\n if (S.activeSlashMatches.length === 0) {\n slashMenu.style.display = \"none\";\n return;\n }\n if (S.slashIdx >= S.activeSlashMatches.length) {\n S.slashIdx = S.activeSlashMatches.length - 1;\n }\n if (S.slashIdx < 0) {\n S.slashIdx = 0;\n }\n let html = \"\";\n S.activeSlashMatches.forEach(function(cmd, idx) {\n const isSel = idx === S.slashIdx;\n const label = subCmd ? cmd.name : \"/\" + cmd.name;\n html += '' + escapeHtml(label) + '' + escapeHtml(cmd.desc || \"\") + \"\";\n });\n html += 'Use ↑↓ to navigate, Tab/Enter to select, Esc to close';\n slashMenu.innerHTML = html;\n slashMenu.style.display = \"flex\";\n }\n function completeSlash(c) {\n input.value = c.insert;\n autoGrowInput();\n S.suppressSlash = false;\n slashMenu.style.display = \"none\";\n S.activeSlashMatches = [];\n input.focus();\n if (c.insert.endsWith(\" \")) {\n renderSlashMenu();\n }\n }\n function wireSlash() {\n slashMenu.addEventListener(\"click\", function(e) {\n const opt = e.target.closest(\".slashOpt\");\n if (opt) {\n e.stopPropagation();\n const idx = parseInt(opt.getAttribute(\"data-idx\") || \"0\", 10);\n if (S.activeSlashMatches[idx]) {\n completeSlash(S.activeSlashMatches[idx]);\n }\n }\n });\n }\n\n // src/chat/ui/panel/overlays.ts\n var celebrateEl = document.getElementById(\"celebrate\");\n function showComplete(label) {\n celebrateEl.innerHTML = 'COMPLETE[' + escapeHtml(label || \"DONE\") + \"]\";\n celebrateEl.classList.remove(\"out\");\n celebrateEl.classList.add(\"show\");\n clearTimeout(S.celebTimer);\n S.celebTimer = setTimeout(() => {\n celebrateEl.classList.add(\"out\");\n setTimeout(() => {\n celebrateEl.classList.remove(\"show\", \"out\");\n celebrateEl.innerHTML = \"\";\n }, 450);\n }, 2200);\n }\n function renderBalance() {\n const txt = fmtSol(S.solLamports);\n const low = S.solLamports != null && S.solLamports < 5e3;\n const wb = document.getElementById(\"wBalance\");\n const mb = document.getElementById(\"mktBalance\");\n if (wb) {\n const amt = wb.querySelector(\".balAmt\");\n if (txt == null) {\n wb.style.display = \"none\";\n } else {\n if (amt) amt.textContent = txt;\n wb.style.display = \"flex\";\n wb.classList.toggle(\"low\", low);\n }\n }\n if (mb) {\n if (txt == null) {\n mb.style.display = \"none\";\n } else {\n mb.textContent = txt;\n mb.style.display = \"inline-flex\";\n mb.classList.toggle(\"low\", low);\n }\n }\n }\n var buyErrEl = document.getElementById(\"buyErr\");\n function showBuyError(msg, fundable) {\n buyErrEl.innerHTML = 'iPurchase failed' + escapeHtml(msg || \"Something went wrong. Please try again.\") + \"\" + (fundable ? '' : \"\") + '';\n buyErrEl.classList.add(\"show\");\n buyErrEl.style.display = \"block\";\n buyErrEl.querySelector(\".buyErrClose\").addEventListener(\"click\", hideBuyError);\n const fundBtn = buyErrEl.querySelector(\".buyErrFund\");\n if (fundBtn) fundBtn.addEventListener(\"click\", () => {\n fundBtn.disabled = true;\n fundBtn.textContent = \"Requesting devnet SOL…\";\n vscode.postMessage({ type: \"airdrop\" });\n });\n clearTimeout(S.buyErrTimer);\n S.buyErrTimer = setTimeout(hideBuyError, fundable ? 2e4 : 7e3);\n }\n function hideBuyError() {\n clearTimeout(S.buyErrTimer);\n buyErrEl.classList.remove(\"show\");\n setTimeout(() => {\n if (!buyErrEl.classList.contains(\"show\")) {\n buyErrEl.style.display = \"none\";\n buyErrEl.innerHTML = \"\";\n }\n }, 220);\n }\n var rpcState = document.getElementById(\"rpcState\");\n var rpcHint = document.getElementById(\"rpcHint\");\n var rpcSetBtn = document.getElementById(\"rpcSetBtn\");\n var rpcDefaultBtn = document.getElementById(\"rpcDefaultBtn\");\n function renderRpcStatus(s) {\n s = s || { dasReady: false, hasKey: false, masked: null, network: \"devnet\" };\n S.dasReady = !!s.dasReady;\n S.rpcNetwork = s.network || \"devnet\";\n if (s.hasKey && s.masked) {\n rpcState.innerHTML = '✓ ' + escapeHtml(s.masked) + \" \" + netBadge(s.network);\n rpcSetBtn.textContent = \"Change\";\n rpcSetBtn.style.display = \"\";\n rpcDefaultBtn.textContent = \"Remove\";\n rpcDefaultBtn.style.display = \"\";\n rpcHint.style.display = \"none\";\n } else {\n rpcState.innerHTML = '⚠ Set Helius key ' + netBadge(s.network);\n const w = document.getElementById(\"rpcWarnSet\");\n if (w) w.addEventListener(\"click\", () => vscode.postMessage({ type: \"setHeliusKey\" }));\n rpcSetBtn.style.display = \"none\";\n rpcDefaultBtn.style.display = \"none\";\n rpcHint.style.display = \"none\";\n }\n }\n var activityBar = document.getElementById(\"activityBar\");\n var activityText = document.getElementById(\"activityText\");\n var VERBS = { skill: [\"Casting\", \"Channeling\", \"Wielding\", \"Invoking\"] };\n function flashSkill(name, mint) {\n const v = VERBS.skill[S.pick++ % VERBS.skill.length];\n activityText.innerHTML = '' + v + \" \" + (name ? '' + escapeHtml(name) + \"\" : \"\");\n activityBar.classList.remove(\"out\");\n activityBar.style.display = \"flex\";\n clearTimeout(S.actTimer);\n const dwell = Math.min(4e3, 1600 + (name || \"\").length * 35);\n S.actTimer = setTimeout(() => {\n activityBar.classList.add(\"out\");\n setTimeout(() => {\n activityBar.style.display = \"none\";\n activityBar.classList.remove(\"out\");\n }, 250);\n }, dwell);\n lightSkillSlot(name, mint, dwell);\n }\n function lightSkillSlot(name, mint, dwell) {\n const grid = document.getElementById(\"skillGrid\");\n const panel = document.getElementById(\"skillsPanel\");\n const btn = document.getElementById(\"skillsBtn\");\n const clear = () => {\n grid.querySelectorAll(\".an-sd.is-firing\").forEach((s) => s.classList.remove(\"is-firing\"));\n panel.classList.remove(\"casting\");\n btn.classList.remove(\"casting\");\n };\n clear();\n grid.querySelectorAll(\".an-sd\").forEach((s) => {\n if (mint && s.getAttribute(\"data-mint\") === mint || s.getAttribute(\"data-skill\") === name) s.classList.add(\"is-firing\");\n });\n panel.classList.add(\"casting\");\n btn.classList.add(\"casting\");\n clearTimeout(S.firingTimer);\n S.firingTimer = setTimeout(clear, Math.max(dwell || 0, 1400));\n }\n function hideActivity() {\n clearTimeout(S.actTimer);\n activityBar.classList.remove(\"out\");\n activityBar.style.display = \"none\";\n }\n function wireOverlays() {\n celebrateEl.addEventListener(\"click\", () => {\n clearTimeout(S.celebTimer);\n celebrateEl.classList.remove(\"show\", \"out\");\n celebrateEl.innerHTML = \"\";\n });\n rpcSetBtn.addEventListener(\"click\", () => vscode.postMessage({ type: \"setHeliusKey\" }));\n rpcDefaultBtn.addEventListener(\"click\", () => vscode.postMessage({ type: \"useDefaultRpc\" }));\n vscode.postMessage({ type: \"getRpcStatus\" });\n }\n\n // src/chat/ui/panel/composer.ts\n function showTyping() {\n setBusy(true);\n if (S.typingEl) return;\n const node = document.createElement(\"div\");\n node.className = \"node typing\";\n node.innerHTML = '' + S.cli + '';\n tailBody().appendChild(node);\n stickToBottom();\n S.typingEl = node;\n }\n function hideTyping() {\n setBusy(false);\n if (S.typingEl) {\n S.typingEl.remove();\n S.typingEl = null;\n }\n }\n function setBusy(b) {\n if (S.busy === b) return;\n S.busy = b;\n const btn = document.getElementById(\"send\");\n if (!btn) return;\n const lbl = btn.querySelector(\".lbl\");\n if (lbl) lbl.textContent = b ? \"Stop\" : \"Send\";\n btn.title = b ? \"Stop\" : \"Send\";\n btn.setAttribute(\"aria-label\", b ? \"Stop\" : \"Send\");\n btn.classList.toggle(\"stopping\", b);\n }\n function interruptTurn() {\n vscode.postMessage({ type: \"interrupt\" });\n setBusy(false);\n hideTyping();\n }\n var sendBtn = document.getElementById(\"send\");\n function syncComposerLock() {\n const locked = approvalDock.childElementCount > 0;\n input.disabled = locked;\n if (sendBtn) sendBtn.disabled = locked;\n composer.classList.toggle(\"locked\", locked);\n input.placeholder = locked ? \"Answer the approval above to continue…\" : \"Message \" + (composer.dataset.cli || \"claude\") + \"... (Enter to send)\";\n }\n var attachStrip = document.getElementById(\"attachStrip\");\n var fileInput = document.getElementById(\"fileInput\");\n var attachBtn = document.getElementById(\"attachBtn\");\n var inputWrap = document.getElementById(\"inputWrap\");\n function renderAttachStrip() {\n attachStrip.innerHTML = \"\";\n attachStrip.style.display = S.attached.length ? \"flex\" : \"none\";\n S.attached.forEach((a, i) => {\n const t = document.createElement(\"div\");\n t.className = \"thumb\";\n const img = document.createElement(\"img\");\n img.src = a.dataUrl;\n img.alt = a.name || \"image\";\n const rm = document.createElement(\"button\");\n rm.className = \"rm\";\n rm.textContent = \"×\";\n rm.title = \"Remove\";\n rm.addEventListener(\"click\", () => {\n S.attached.splice(i, 1);\n renderAttachStrip();\n });\n t.appendChild(img);\n t.appendChild(rm);\n attachStrip.appendChild(t);\n });\n }\n function userImagesEl(info) {\n if (!info) return null;\n if (info.thumbs && info.thumbs.length) {\n const row = document.createElement(\"div\");\n row.className = \"msgImgs\";\n info.thumbs.forEach((u) => {\n const im = document.createElement(\"img\");\n im.src = u;\n row.appendChild(im);\n });\n return row;\n }\n if (info.count) {\n const chip = document.createElement(\"span\");\n chip.className = \"imgChip\";\n chip.innerHTML = '' + info.count + (info.count > 1 ? \" images\" : \" image\");\n return chip;\n }\n return null;\n }\n function addFiles(files) {\n for (const f of files) {\n if (!f.type || f.type.indexOf(\"image/\") !== 0) continue;\n const reader = new FileReader();\n reader.onload = () => {\n const dataUrl = String(reader.result || \"\");\n const comma = dataUrl.indexOf(\",\");\n if (comma < 0) return;\n S.attached.push({ dataUrl, mime: f.type, dataBase64: dataUrl.slice(comma + 1), name: f.name || \"pasted\" });\n renderAttachStrip();\n };\n reader.readAsDataURL(f);\n }\n }\n function send() {\n if (input.disabled) return;\n const text = input.value.trim();\n if (!text && !S.attached.length) return;\n if (text === \"/mockupskills\" || text.startsWith(\"/mockupskills \")) {\n const names = text.slice(\"/mockupskills\".length).trim().split(/\\s+/).filter(Boolean);\n setSkills(names);\n if (skillsPanel.style.display === \"none\") {\n skillsPanel.style.display = \"block\";\n skillsBtn.classList.add(\"on\");\n }\n input.value = \"\";\n return;\n }\n if (text === \"/mockskill\" || text.startsWith(\"/mockskill \")) {\n flashSkill(text.slice(\"/mockskill\".length).trim() || \"cleancode\", \"mock\");\n input.value = \"\";\n return;\n }\n if (text.startsWith(\"/\")) {\n const [cmd, ...rest] = text.slice(1).split(\" \");\n const arg = rest.join(\" \").trim();\n switch (cmd) {\n case \"login\": {\n const target = arg === \"claude\" || arg === \"codex\" ? arg : S.cli;\n if (target === \"claude\" && arg && arg !== \"claude\" && arg !== \"codex\") {\n vscode.postMessage({ type: \"claudeAuthCode\", code: arg });\n renderNotice(\"Submitted Claude sign-in code.\");\n } else {\n vscode.postMessage({ type: target === \"claude\" ? \"startClaudeLogin\" : \"startCodexLogin\" });\n renderNotice(\"Starting \" + (target === \"claude\" ? \"Claude\" : \"Codex\") + \" sign-in...\");\n }\n input.value = \"\";\n return;\n }\n case \"logout\": {\n const target = arg === \"claude\" || arg === \"codex\" ? arg : S.cli;\n vscode.postMessage({ type: \"logoutEngine\", cli: target });\n renderNotice(\"Signing out of \" + (target === \"claude\" ? \"Claude\" : \"Codex\") + \"...\");\n input.value = \"\";\n return;\n }\n case \"new\":\n vscode.postMessage({ type: \"new\" });\n input.value = \"\";\n return;\n case \"clear\":\n vscode.postMessage({ type: \"clear\" });\n input.value = \"\";\n return;\n case \"compact\":\n vscode.postMessage({ type: \"slashCommand\", command: \"compact\", arg });\n showTyping();\n input.value = \"\";\n return;\n case \"status\":\n vscode.postMessage({ type: \"slashCommand\", command: \"status\" });\n input.value = \"\";\n return;\n case \"resume\":\n vscode.postMessage({ type: \"slashCommand\", command: \"resume\" });\n input.value = \"\";\n return;\n case \"diff\":\n vscode.postMessage({ type: \"slashCommand\", command: \"diff\" });\n showTyping();\n input.value = \"\";\n return;\n case \"permissions\":\n if (arg) {\n modeByCli[S.cli] = arg;\n fillModes();\n vscode.postMessage({ type: \"mode\", mode: arg });\n } else vscode.postMessage({ type: \"slashCommand\", command: \"permissions\" });\n input.value = \"\";\n return;\n case \"init\":\n case \"skills\":\n case \"cost\":\n vscode.postMessage({ type: \"slashCommand\", command: cmd });\n input.value = \"\";\n return;\n case \"review\":\n case \"mcp\":\n vscode.postMessage({ type: \"slashCommand\", command: cmd, arg });\n showTyping();\n input.value = \"\";\n return;\n case \"copy\": {\n const last = Array.from(log.querySelectorAll(\".node.assistant .msg\")).pop();\n if (last && navigator.clipboard) navigator.clipboard.writeText(last.textContent || \"\").catch(() => {\n });\n input.value = \"\";\n return;\n }\n case \"engine\":\n if (arg === \"claude\" || arg === \"codex\") selectTab(arg);\n input.value = \"\";\n return;\n case \"model\":\n if (arg) {\n modelByCli[S.cli] = arg;\n fillModels();\n vscode.postMessage({ type: \"model\", model: arg });\n }\n input.value = \"\";\n return;\n case \"mode\":\n if (arg) {\n modeByCli[S.cli] = arg;\n fillModes();\n vscode.postMessage({ type: \"mode\", mode: arg });\n }\n input.value = \"\";\n return;\n case \"effort\":\n if (arg) {\n effortByCli[S.cli] = arg;\n fillModes();\n vscode.postMessage({ type: \"effort\", effort: arg === \"default\" ? void 0 : arg });\n }\n input.value = \"\";\n return;\n case \"help\": {\n const helpText = slashCommandsForCli().map(function(c) {\n return \"/\" + c.name + (c.args ? \" \" + c.args : \"\") + \" - \" + c.desc;\n }).join(\"\\n\");\n const pre = document.createElement(\"pre\");\n pre.style.cssText = \"margin:8px 0;padding:8px 12px;background:var(--an-bg-1);border-radius:6px;font-size:0.82em;opacity:0.8\";\n pre.textContent = helpText;\n log.appendChild(pre);\n syncWatermark();\n input.value = \"\";\n return;\n }\n default: {\n vscode.postMessage({ type: \"slashCommand\", command: cmd, arg });\n showTyping();\n input.value = \"\";\n return;\n }\n }\n }\n const activeStatus = S.cliReport && S.cliReport[S.cli];\n if (activeStatus === \"missing\") {\n renderEngineMissing(S.cli);\n return;\n }\n if (activeStatus === \"no-login\") {\n renderNotice((S.cli === \"claude\" ? \"Claude\" : \"Codex\") + \" is not signed in. Type /login to connect it.\");\n return;\n }\n const images = S.attached.map((a) => ({ mime: a.mime, dataBase64: a.dataBase64, name: a.name }));\n S.pendingSentImages = S.attached.map((a) => a.dataUrl);\n vscode.postMessage({ type: \"send\", text, images });\n S.attached = [];\n renderAttachStrip();\n input.value = \"\";\n input.style.height = \"auto\";\n showTyping();\n }\n function autoGrowInput() {\n if (S.growRaf) return;\n S.growRaf = requestAnimationFrame(() => {\n S.growRaf = 0;\n input.style.height = \"auto\";\n input.style.height = Math.min(input.scrollHeight, 220) + \"px\";\n });\n }\n function wireComposer() {\n attachBtn.addEventListener(\"click\", () => {\n if (!input.disabled) fileInput.click();\n });\n fileInput.addEventListener(\"change\", () => {\n addFiles(fileInput.files || []);\n fileInput.value = \"\";\n });\n input.addEventListener(\"paste\", (e) => {\n const items = e.clipboardData && e.clipboardData.items || [];\n const imgs = [];\n for (const it of items) {\n if (it.kind === \"file\" && it.type.indexOf(\"image/\") === 0) {\n const f = it.getAsFile();\n if (f) imgs.push(f);\n }\n }\n if (imgs.length) {\n e.preventDefault();\n addFiles(imgs);\n }\n });\n [\"dragenter\", \"dragover\"].forEach((ev) => inputWrap.addEventListener(ev, (e) => {\n if (e.dataTransfer && Array.from(e.dataTransfer.types || []).indexOf(\"Files\") >= 0) {\n e.preventDefault();\n inputWrap.classList.add(\"dragover\");\n }\n }));\n [\"dragleave\", \"drop\"].forEach((ev) => inputWrap.addEventListener(ev, (e) => {\n if (ev === \"drop\") {\n e.preventDefault();\n if (e.dataTransfer) addFiles(e.dataTransfer.files || []);\n }\n inputWrap.classList.remove(\"dragover\");\n }));\n document.getElementById(\"send\").addEventListener(\"click\", () => {\n S.busy ? interruptTurn() : send();\n });\n document.getElementById(\"newBtn\").addEventListener(\"click\", () => {\n vscode.postMessage({ type: \"new\" });\n closeMenus();\n });\n document.getElementById(\"newTabBtn\").addEventListener(\"click\", () => vscode.postMessage({ type: \"newTab\" }));\n input.addEventListener(\"keydown\", (e) => {\n if (e.isComposing || e.keyCode === 229) return;\n if (S.activeSlashMatches.length > 0) {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n S.slashIdx = (S.slashIdx + 1) % S.activeSlashMatches.length;\n renderSlashMenu();\n return;\n }\n if (e.key === \"ArrowUp\") {\n e.preventDefault();\n S.slashIdx = (S.slashIdx - 1 + S.activeSlashMatches.length) % S.activeSlashMatches.length;\n renderSlashMenu();\n return;\n }\n if (e.key === \"Tab\" || e.key === \"Enter\") {\n e.preventDefault();\n if (S.activeSlashMatches[S.slashIdx]) {\n completeSlash(S.activeSlashMatches[S.slashIdx]);\n }\n return;\n }\n if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n S.suppressSlash = true;\n renderSlashMenu();\n return;\n }\n }\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n send();\n }\n if (e.key === \"Escape\" && S.busy) {\n e.preventDefault();\n interruptTurn();\n }\n });\n document.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\" && S.busy) {\n e.preventDefault();\n interruptTurn();\n }\n });\n input.addEventListener(\"input\", () => {\n autoGrowInput();\n S.suppressSlash = false;\n S.slashIdx = 0;\n renderSlashMenu();\n });\n }\n\n // src/chat/ui/panel/turns.ts\n function makeUtext(userText) {\n const wrap = document.createElement(\"div\");\n wrap.className = \"utextWrap\";\n const ut = document.createElement(\"div\");\n ut.className = \"utext\";\n ut.textContent = userText;\n wrap.appendChild(ut);\n const longMsg = userText.split(\"\\n\").length > 4 || userText.length > 280;\n if (longMsg) {\n wrap.classList.add(\"collapsed\");\n const tog = document.createElement(\"div\");\n tog.className = \"utextToggle\";\n tog.textContent = \"Show more\";\n tog.addEventListener(\"click\", () => {\n const exp = wrap.classList.toggle(\"expanded\");\n wrap.classList.toggle(\"collapsed\", !exp);\n tog.textContent = exp ? \"Show less\" : \"Show more\";\n });\n wrap.appendChild(tog);\n }\n return wrap;\n }\n function startTurn(userText, badgeCli, imageInfo) {\n const turn = document.createElement(\"div\");\n turn.className = \"turn\";\n const head = document.createElement(\"div\");\n head.className = \"turnHead\";\n head.innerHTML = '>';\n head.appendChild(makeUtext(userText));\n if (badgeCli) {\n const b = document.createElement(\"span\");\n b.className = \"badge \" + badgeCli;\n b.textContent = badgeCli === \"codex\" ? \"codex · gpt\" : \"claude\";\n head.appendChild(b);\n }\n const imgEl = userImagesEl(imageInfo);\n if (imgEl) head.appendChild(imgEl);\n const body = document.createElement(\"div\");\n body.className = \"turnBody\";\n turn.appendChild(head);\n turn.appendChild(body);\n log.appendChild(turn);\n turn._body = body;\n S.tailTurn = turn;\n scrollToLatest();\n syncWatermark();\n return body;\n }\n function startTurnTop(userText, badgeCli) {\n const turn = document.createElement(\"div\");\n turn.className = \"turn\";\n const head = document.createElement(\"div\");\n head.className = \"turnHead\";\n head.innerHTML = '>';\n head.appendChild(makeUtext(userText));\n if (badgeCli) {\n const b = document.createElement(\"span\");\n b.className = \"badge \" + badgeCli;\n b.textContent = badgeCli === \"codex\" ? \"codex · gpt\" : \"claude\";\n head.appendChild(b);\n }\n const body = document.createElement(\"div\");\n body.className = \"turnBody\";\n turn.appendChild(head);\n turn.appendChild(body);\n log.insertBefore(turn, log.firstChild);\n turn._body = body;\n S.headTurn = turn;\n return body;\n }\n function tailBody() {\n if (S.tailTurn && S.tailTurn._body) return S.tailTurn._body;\n const turn = document.createElement(\"div\");\n turn.className = \"turn\";\n const body = document.createElement(\"div\");\n body.className = \"turnBody\";\n turn.appendChild(body);\n log.appendChild(turn);\n turn._body = body;\n S.tailTurn = turn;\n syncWatermark();\n return body;\n }\n function appendNode(el, dir) {\n const body = dir === \"head\" ? S.headTurn && S.headTurn._body || startTurnTop(\"\", void 0) : tailBody();\n body.appendChild(el);\n if (dir !== \"head\") stickToBottom();\n }\n function bubble(role, prepend, badgeCli) {\n const node = document.createElement(\"div\");\n node.className = \"node \" + role + (badgeCli ? \" \" + badgeCli : \"\");\n const el = document.createElement(\"div\");\n el.className = \"msg \" + role;\n node.appendChild(el);\n appendNode(node, prepend ? \"head\" : \"tail\");\n el._row = node;\n return el;\n }\n function clampBody(el, row, threshold) {\n if ((el.textContent || \"\").length <= threshold) return;\n el.classList.add(\"clamp\");\n const btn = document.createElement(\"button\");\n btn.className = \"moreBtn\";\n btn.textContent = \"show more\";\n btn.addEventListener(\"click\", () => {\n const on = el.classList.toggle(\"clamp\");\n btn.textContent = on ? \"show more\" : \"show less\";\n });\n row.appendChild(btn);\n }\n function renderSummary(text, prepend) {\n const node = document.createElement(\"div\");\n node.className = \"node summary\";\n const rule = document.createElement(\"div\");\n rule.className = \"compactRule\";\n rule.innerHTML = '⌘ context compacted';\n const body = document.createElement(\"div\");\n body.className = \"summaryBody\";\n body.textContent = text;\n node.appendChild(rule);\n node.appendChild(body);\n appendNode(node, prepend ? \"head\" : \"tail\");\n if (!prepend) clampBody(body, body, 400);\n }\n function addMsgCopy(row, el) {\n if (!row || row.querySelector(\":scope > .msgCopy\")) return;\n const btn = document.createElement(\"button\");\n btn.className = \"msgCopy\";\n btn.title = \"Copy message\";\n btn.innerHTML = COPY_ICON;\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n copyText(el.dataset.md || el.textContent || \"\", () => {\n btn.classList.add(\"done\");\n btn.innerHTML = CHECK_ICON;\n setTimeout(() => {\n btn.classList.remove(\"done\");\n btn.innerHTML = COPY_ICON;\n }, 1200);\n });\n });\n row.appendChild(btn);\n }\n function addFooter(row, durationMs, model) {\n if (durationMs == null && !model) return;\n const f = document.createElement(\"div\");\n f.className = \"footer\";\n if (durationMs != null) {\n const s = durationMs / 1e3;\n f.appendChild(document.createTextNode(s < 60 ? s.toFixed(1) + \"s\" : Math.floor(s / 60) + \"m \" + Math.round(s % 60) + \"s\"));\n }\n if (model) {\n const m = document.createElement(\"span\");\n m.className = \"mdl\";\n m.textContent = model;\n f.appendChild(m);\n }\n row.appendChild(f);\n }\n function toolRow(prepend) {\n const row = document.createElement(\"div\");\n row.className = \"node tool\";\n appendNode(row, prepend ? \"head\" : \"tail\");\n return row;\n }\n var CHEV = '';\n function makeCollapsible(head, body, startOpen) {\n head.classList.add(\"clickable\");\n head.insertAdjacentHTML(\"afterbegin\", CHEV);\n const set = (open) => {\n head.classList.toggle(\"open\", open);\n body.hidden = !open;\n };\n set(startOpen);\n head.addEventListener(\"click\", () => set(body.hidden));\n }\n function setOutput(card, output, exitCode) {\n let out = card.querySelector(\".toolOut\");\n if (!out) {\n out = document.createElement(\"pre\");\n out.className = \"toolOut toolBody\";\n card.appendChild(out);\n makeCollapsible(card.querySelector(\".toolHead\"), out, false);\n }\n out.textContent = output;\n if (typeof exitCode === \"number\" && exitCode !== 0) card.classList.add(\"failed\");\n }\n function renderDiff(pre, diffText) {\n const lines = diffText.split(\"\\n\");\n const kind = lines.map((l) => l[0] === \"+\" ? \"add\" : l[0] === \"-\" ? \"del\" : \"ctx\");\n let added = 0, removed = 0;\n for (const k of kind) {\n if (k === \"add\") added++;\n else if (k === \"del\") removed++;\n }\n const CTX = 2;\n const keep = new Array(lines.length).fill(false);\n for (let i = 0; i < lines.length; i++) {\n if (kind[i] === \"ctx\") continue;\n for (let c = Math.max(0, i - CTX); c <= Math.min(lines.length - 1, i + CTX); c++) keep[c] = true;\n }\n let folding = false;\n for (let i = 0; i < lines.length; i++) {\n if (!keep[i]) {\n if (!folding) {\n folding = true;\n const f = document.createElement(\"div\");\n f.className = \"fold\";\n f.textContent = \"⋯\";\n pre.appendChild(f);\n }\n continue;\n }\n folding = false;\n const d = document.createElement(\"div\");\n d.className = kind[i];\n const sign = kind[i] === \"add\" ? \"+\" : kind[i] === \"del\" ? \"−\" : \"\";\n const text = lines[i].replace(/^[+\\-]/, \"\");\n d.innerHTML = '' + sign + \"\";\n d.appendChild(document.createTextNode(text || \" \"));\n pre.appendChild(d);\n }\n return { added, removed };\n }\n function renderToolInto(row, msg) {\n const t = msg.tool || {};\n if (t.command !== void 0) {\n const card = document.createElement(\"div\");\n card.className = \"toolCard bash\";\n const head = document.createElement(\"div\");\n head.className = \"toolHead\";\n head.innerHTML = '$';\n const cmd = document.createElement(\"span\");\n cmd.className = \"cmd\";\n cmd.textContent = t.command;\n head.appendChild(cmd);\n card.appendChild(head);\n if (t.output) setOutput(card, t.output, t.exitCode);\n row.appendChild(card);\n return t.output ? null : card;\n } else if (t.diff !== void 0) {\n const card = document.createElement(\"div\");\n card.className = \"toolCard diff\";\n const head = document.createElement(\"div\");\n head.className = \"toolHead\";\n head.innerHTML = '✎';\n const fn = document.createElement(\"span\");\n fn.className = \"file\";\n fn.textContent = t.file || \"edit\";\n head.appendChild(fn);\n card.appendChild(head);\n const pre = document.createElement(\"pre\");\n pre.className = \"diffBody toolBody\";\n const { added, removed } = renderDiff(pre, t.diff);\n const stat = document.createElement(\"span\");\n stat.className = \"stat\";\n stat.innerHTML = '+' + added + '−' + removed + \"\";\n head.appendChild(stat);\n card.appendChild(pre);\n makeCollapsible(head, pre, true);\n row.appendChild(card);\n } else {\n const card = document.createElement(\"div\");\n card.className = \"toolCard op\";\n const icon = t.name === \"Read\" ? '' : t.name === \"Write\" ? \"✎\" : \"•\";\n card.innerHTML = '' + icon + \"\";\n card.appendChild(document.createTextNode(msg.text || t.name || \"tool\"));\n row.appendChild(card);\n }\n return null;\n }\n function renderTool(msg, prepend) {\n const t = msg.tool || {};\n if (t.command === void 0 && t.diff === void 0 && t.output && S.openBash && !prepend) {\n setOutput(S.openBash, t.output, t.exitCode);\n S.openBash = null;\n return;\n }\n const row = toolRow(prepend);\n const awaiting = renderToolInto(row, msg);\n if (awaiting) S.openBash = awaiting;\n if (!prepend) {\n if (S.typingEl) tailBody().appendChild(S.typingEl);\n stickToBottom();\n }\n }\n\n // src/chat/ui/panel/shell.ts\n function syncWatermark() {\n mainEl.classList.toggle(\"hasMsgs\", log.childElementCount > 0);\n }\n function renderNotice(text) {\n const notice = document.createElement(\"div\");\n notice.style.cssText = \"padding:4px 12px;font-size:0.82em;opacity:0.65;white-space:pre-wrap\";\n notice.textContent = text;\n log.appendChild(notice);\n syncWatermark();\n stickToBottom();\n }\n function renderActionNotice(text, actions) {\n const notice = document.createElement(\"div\");\n notice.style.cssText = \"padding:4px 12px;font-size:0.82em;white-space:pre-wrap\";\n const body = document.createElement(\"div\");\n body.style.opacity = \"0.65\";\n body.textContent = text;\n const row = document.createElement(\"div\");\n row.style.cssText = \"display:flex;gap:6px;margin-top:6px\";\n for (const a of actions) {\n const btn = document.createElement(\"button\");\n btn.textContent = a[0];\n btn.style.cssText = \"padding:3px 10px;font-size:1em\";\n btn.addEventListener(\"click\", () => a[1](btn));\n row.appendChild(btn);\n }\n notice.appendChild(body);\n notice.appendChild(row);\n log.appendChild(notice);\n syncWatermark();\n stickToBottom();\n }\n function copyAction(command) {\n return [\"Copy command\", (btn) => {\n if (navigator.clipboard) navigator.clipboard.writeText(command).catch(() => {\n });\n btn.textContent = \"Copied\";\n }];\n }\n function renderEngineBanner(text, actions, dismissKey) {\n engineBanner.innerHTML = \"\";\n const body = document.createElement(\"div\");\n body.className = \"eb-body\";\n body.textContent = text;\n const row = document.createElement(\"div\");\n row.className = \"eb-actions\";\n for (const a of actions) {\n const btn = document.createElement(\"button\");\n btn.className = \"eb-btn\";\n btn.textContent = a[0];\n btn.addEventListener(\"click\", () => a[1](btn));\n row.appendChild(btn);\n }\n const close = document.createElement(\"button\");\n close.className = \"eb-close\";\n close.setAttribute(\"aria-label\", \"Dismiss\");\n close.textContent = \"×\";\n close.addEventListener(\"click\", () => {\n engineBanner.style.display = \"none\";\n engineBanner.innerHTML = \"\";\n vscode.postMessage({ type: \"dismissEngineUpdate\", cli: dismissKey });\n });\n engineBanner.appendChild(body);\n engineBanner.appendChild(row);\n engineBanner.appendChild(close);\n engineBanner.style.display = \"flex\";\n }\n function renderEngineMissing(which) {\n const name = which === \"claude\" ? \"Claude\" : \"Codex\";\n const command = ENGINE_INSTALL_COMMAND[which];\n renderActionNotice(\n name + \" is not installed. Install it, then sign in with /login.\\n$ \" + command,\n [\n [\"Install in terminal\", () => vscode.postMessage({ type: \"installEngine\", cli: which })],\n copyAction(command)\n ]\n );\n }\n function renderStatus(status) {\n const ctx = typeof status.contextTokens === \"number\" ? status.contextTokens >= 1e3 ? Math.round(status.contextTokens / 1e3) + \"k\" : String(status.contextTokens) : \"unknown\";\n const text = [\n \"engine: \" + status.cli,\n \"session: \" + (status.sessionId || \"(none)\"),\n \"model: \" + (status.model || \"default\"),\n \"mode: \" + (status.mode || \"default\"),\n \"effort: \" + (status.effort || \"default\"),\n \"context tokens: \" + ctx\n ].join(\"\\n\");\n const pre = document.createElement(\"pre\");\n pre.style.cssText = \"margin:8px 0;padding:8px 12px;background:var(--an-bg-1);border-radius:6px;font-size:0.82em;opacity:0.85;white-space:pre-wrap\";\n pre.textContent = text;\n log.appendChild(pre);\n syncWatermark();\n stickToBottom();\n }\n function nearBottom() {\n const d = log.scrollHeight - log.scrollTop - log.clientHeight;\n return d <= Math.max(180, log.clientHeight * 0.25);\n }\n function toBottomInstant() {\n const prev = log.style.scrollBehavior;\n log.style.scrollBehavior = \"auto\";\n log.scrollTop = log.scrollHeight;\n log.style.scrollBehavior = prev;\n }\n function updateJump() {\n if (!jumpBtn) return;\n if (S.stick) S.hasNew = false;\n jumpBtn.classList.toggle(\"show\", !S.stick);\n jumpBtn.classList.toggle(\"hasNew\", S.hasNew);\n }\n function stickToBottom() {\n if (S.stick) toBottomInstant();\n else S.hasNew = true;\n updateJump();\n }\n function scrollToLatest() {\n S.stick = true;\n S.hasNew = false;\n toBottomInstant();\n updateJump();\n }\n function showLoading() {\n loadingEl.style.display = \"flex\";\n }\n function hideLoading() {\n loadingEl.style.display = \"none\";\n }\n var STREAM_MD_MS = 300;\n function flushStreamRender() {\n S.streamRaf = 0;\n if (!S.streaming) return;\n S.lastStreamRender = Date.now();\n const raw = S.streaming.dataset.acc || \"\";\n if (S.streaming.dataset.role === \"assistant\") renderMdStreaming(S.streaming, raw);\n else {\n S.streaming.textContent = raw;\n S.streaming.dataset.md = raw;\n }\n if (S.typingEl) tailBody().appendChild(S.typingEl);\n stickToBottom();\n }\n function scheduleStreamRender() {\n if (S.streamRaf || S.streamTimer) return;\n const wait = STREAM_MD_MS - (Date.now() - S.lastStreamRender);\n if (wait <= 0) S.streamRaf = requestAnimationFrame(flushStreamRender);\n else S.streamTimer = setTimeout(() => {\n S.streamTimer = 0;\n if (!S.streamRaf) S.streamRaf = requestAnimationFrame(flushStreamRender);\n }, wait);\n }\n function cancelStreamRender() {\n if (S.streamRaf) {\n cancelAnimationFrame(S.streamRaf);\n S.streamRaf = 0;\n }\n if (S.streamTimer) {\n clearTimeout(S.streamTimer);\n S.streamTimer = 0;\n }\n }\n function wireShell() {\n if (jumpBtn) jumpBtn.addEventListener(\"click\", scrollToLatest);\n }\n\n // src/chat/ui/panel/approval.ts\n function dismissApproval(id) {\n const cards = approvalDock.querySelectorAll(\"[data-approval-id]\");\n for (const c of cards) {\n if (c.dataset.approvalId === id) {\n c.remove();\n syncComposerLock();\n break;\n }\n }\n }\n function renderApproval(req) {\n const card = document.createElement(\"div\");\n card.className = \"approvalCard\";\n card.dataset.approvalId = req.id;\n const isPublish = /publish_skill/.test(req.tool || \"\") || /publish_skill/.test(req.title || \"\");\n const isBuy = /buy_skill/.test(req.tool || \"\") || /buy_skill/.test(req.title || \"\");\n const isForge = isPublish || isBuy;\n if (isForge) {\n card.classList.add(\"skillForge\");\n if (isBuy) card.classList.add(\"buyForge\");\n const stars = document.createElement(\"div\");\n stars.className = \"forgeStars\";\n for (let i = 0; i < 6; i++) {\n const s = document.createElement(\"span\");\n s.className = \"st\";\n s.innerHTML = '';\n s.style.left = 8 + Math.random() * 84 + \"%\";\n s.style.top = 12 + Math.random() * 70 + \"%\";\n s.style.animationDuration = 3.2 + Math.random() * 2.4 + \"s\";\n s.style.animationDelay = Math.random() * 3 + \"s\";\n s.style.fontSize = 7 + Math.random() * 5 + \"px\";\n stars.appendChild(s);\n }\n card.appendChild(stars);\n }\n if (req.kind === \"question\" && Array.isArray(req.questions) && req.questions.length) {\n const sel = {};\n const free = {};\n const total = req.questions.length;\n let step = 0;\n const hasAnswer = (qi) => {\n const typed = (free[qi] || \"\").trim();\n return typed.length > 0 || !!(sel[qi] && sel[qi].length);\n };\n const sendAll = () => {\n const questionResponses = req.questions.map((q, qi) => {\n const typed = (free[qi] || \"\").trim();\n return {\n question: q.question,\n questionId: q.id,\n selected: typed ? [] : sel[qi] || [],\n ...typed ? { text: typed } : {}\n };\n });\n vscode.postMessage({ type: \"approvalDecision\", id: req.id, outcome: \"once\", questionResponses });\n card.remove();\n syncComposerLock();\n };\n const renderStep = () => {\n card.innerHTML = \"\";\n const q = req.questions[step];\n let updateNext = () => {\n };\n let otherInput = null;\n if (total > 1) {\n const counter = document.createElement(\"div\");\n counter.className = \"qCount\";\n counter.textContent = step + 1 + \" / \" + total;\n card.appendChild(counter);\n }\n const block = document.createElement(\"div\");\n block.className = \"qBlock\";\n if (q.header) {\n const h = document.createElement(\"span\");\n h.className = \"qHeader\";\n h.textContent = q.header;\n block.appendChild(h);\n }\n const qt = document.createElement(\"div\");\n qt.className = \"qText\";\n qt.textContent = q.question;\n block.appendChild(qt);\n const opts = document.createElement(\"div\");\n opts.className = \"qOpts\";\n (q.options || []).forEach((opt) => {\n const b = document.createElement(\"button\");\n b.type = \"button\";\n b.className = \"qOpt\";\n if ((sel[step] || []).indexOf(opt.label) >= 0) b.classList.add(\"on\");\n const t = document.createElement(\"div\");\n t.className = \"qOptLabel\";\n t.textContent = opt.label;\n b.appendChild(t);\n if (opt.description) {\n const d = document.createElement(\"div\");\n d.className = \"qOptDesc\";\n d.textContent = opt.description;\n b.appendChild(d);\n }\n b.addEventListener(\"click\", () => {\n const cur = sel[step] || [];\n delete free[step];\n if (q.multiSelect) {\n sel[step] = cur.indexOf(opt.label) >= 0 ? cur.filter((l) => l !== opt.label) : cur.concat(opt.label);\n } else {\n sel[step] = cur[0] === opt.label ? [] : [opt.label];\n }\n Array.from(opts.children).forEach((c, i) => c.classList.toggle(\"on\", (sel[step] || []).indexOf((q.options[i] || {}).label) >= 0));\n if (otherInput) otherInput.value = \"\";\n updateNext();\n });\n opts.appendChild(b);\n });\n block.appendChild(opts);\n if (q.allowCustomInput) {\n const label = document.createElement(\"div\");\n label.className = \"qOtherLabel\";\n label.textContent = q.options && q.options.length ? \"Or type your own answer\" : \"Type your answer\";\n block.appendChild(label);\n otherInput = q.secret ? document.createElement(\"input\") : document.createElement(\"textarea\");\n otherInput.className = \"qOtherInput\";\n otherInput.placeholder = \"Type your answer…\";\n if (q.secret) otherInput.type = \"password\";\n else otherInput.rows = 3;\n if (free[step]) otherInput.value = free[step];\n otherInput.addEventListener(\"input\", () => {\n free[step] = otherInput.value;\n sel[step] = [];\n Array.from(opts.children).forEach((c) => c.classList.remove(\"on\"));\n updateNext();\n });\n block.appendChild(otherInput);\n }\n card.appendChild(block);\n const actions2 = document.createElement(\"div\");\n actions2.className = \"apActions\";\n if (step > 0) {\n const back = document.createElement(\"button\");\n back.className = \"apBtn always\";\n back.textContent = \"Back\";\n back.addEventListener(\"click\", () => {\n step -= 1;\n renderStep();\n });\n actions2.appendChild(back);\n }\n const next = document.createElement(\"button\");\n next.className = \"apBtn ok\";\n const last = step === total - 1;\n next.textContent = last ? \"Send\" : \"Next\";\n next.addEventListener(\"click\", () => {\n if (!hasAnswer(step)) return;\n if (last) sendAll();\n else {\n step += 1;\n renderStep();\n }\n });\n actions2.appendChild(next);\n card.appendChild(actions2);\n updateNext = () => {\n next.disabled = !hasAnswer(step);\n };\n updateNext();\n };\n renderStep();\n approvalDock.insertBefore(card, approvalDock.firstChild);\n syncComposerLock();\n return;\n }\n const isPlan = req.kind === \"plan\";\n const isDanger = req.risk === \"danger\";\n const head = document.createElement(\"div\");\n head.className = \"apHead\" + (isDanger ? \" apDanger\" : \"\");\n const glyphEl = document.createElement(\"span\");\n glyphEl.className = \"apk\";\n var skSvg = '';\n var rdSvg = '';\n glyphEl.innerHTML = req.kind === \"bash\" ? \"$\" : req.kind === \"read\" ? rdSvg : isPlan ? skSvg : isForge ? skSvg : \"✎\";\n head.appendChild(glyphEl);\n if (isDanger) {\n const warn = document.createElement(\"span\");\n warn.style.cssText = \"color:var(--vscode-errorForeground,#f44);font-weight:700;margin-right:4px\";\n warn.innerHTML = ' DANGER ·';\n head.appendChild(warn);\n }\n const ttl = document.createElement(\"span\");\n ttl.className = \"apTitle\";\n ttl.textContent = isPublish ? \"Forge skill: \" + (req.input && req.input.name || \"new skill\") : isBuy ? \"Buy skill\" + (req.input && req.input.name ? \": \" + req.input.name : \"\") : req.title || req.tool;\n head.appendChild(ttl);\n const tag = document.createElement(\"span\");\n tag.className = \"apTag\";\n tag.textContent = req.cli;\n head.appendChild(tag);\n card.appendChild(head);\n let commandInput = null;\n if (req.command) {\n const pre = document.createElement(\"pre\");\n pre.className = \"apBody\";\n pre.textContent = req.command;\n card.appendChild(pre);\n if (req.kind === \"bash\") {\n commandInput = document.createElement(\"textarea\");\n commandInput.className = \"apBody\";\n commandInput.style.cssText = \"display:none;width:100%;box-sizing:border-box;resize:vertical;font-family:monospace;font-size:0.85em;background:var(--an-bg-1);border:1px solid var(--eng);border-radius:4px;padding:6px;color:inherit\";\n commandInput.value = req.command;\n card.appendChild(commandInput);\n const PENCIL_SVG = '';\n const XMARK_SVG = '';\n const editBtn = document.createElement(\"button\");\n editBtn.className = \"apEdit\";\n editBtn.innerHTML = PENCIL_SVG;\n editBtn.title = \"Edit\";\n editBtn.setAttribute(\"aria-label\", \"Edit\");\n editBtn.addEventListener(\"click\", () => {\n const editing = commandInput.style.display !== \"none\";\n pre.style.display = editing ? \"\" : \"none\";\n commandInput.style.display = editing ? \"none\" : \"\";\n editBtn.innerHTML = editing ? PENCIL_SVG : XMARK_SVG;\n editBtn.title = editing ? \"Edit\" : \"Cancel\";\n });\n head.appendChild(editBtn);\n }\n } else if (req.plan) {\n const pre = document.createElement(\"pre\");\n pre.className = \"apBody planBody\";\n pre.textContent = req.plan;\n card.appendChild(pre);\n } else if (req.diff) {\n const pre = document.createElement(\"pre\");\n pre.className = \"apBody diffBody\";\n for (const ln of String(req.diff).split(\"\\n\")) {\n const d = document.createElement(\"div\");\n d.className = ln[0] === \"+\" ? \"add\" : ln[0] === \"-\" ? \"del\" : \"ctx\";\n d.textContent = ln;\n pre.appendChild(d);\n }\n card.appendChild(pre);\n } else if (req.file) {\n const f = document.createElement(\"div\");\n f.className = \"apBody\";\n f.textContent = req.file;\n card.appendChild(f);\n } else if (isPublish && req.input) {\n const box = document.createElement(\"div\");\n box.className = \"apBody forgeBody\";\n const nm = document.createElement(\"div\");\n nm.style.cssText = \"font-weight:600;font-size:1.02em\";\n nm.textContent = req.input.name || \"new skill\";\n box.appendChild(nm);\n if (req.input.description) {\n const d = document.createElement(\"div\");\n d.style.cssText = \"opacity:0.85;font-size:0.85em;margin-top:2px\";\n d.textContent = req.input.description;\n box.appendChild(d);\n }\n const priceSol = req.input.priceSol;\n const priceTxt = priceSol == null ? \"0.1 SOL\" : String(priceSol) === \"0\" ? \"free\" : priceSol + \" SOL\";\n const meta = document.createElement(\"div\");\n meta.style.cssText = \"margin-top:5px;font-size:0.8em;opacity:0.7\";\n meta.textContent = \"mint a soulbound NFT · price \" + priceTxt;\n box.appendChild(meta);\n card.appendChild(box);\n }\n const actions = document.createElement(\"div\");\n actions.className = \"apActions\";\n const decide = (outcome, extra) => {\n vscode.postMessage({ type: \"approvalDecision\", id: req.id, outcome, ...extra });\n card.remove();\n syncComposerLock();\n };\n const mk = (label, outcome, cls) => {\n const b = document.createElement(\"button\");\n b.className = \"apBtn \" + cls;\n b.textContent = label;\n b.addEventListener(\"click\", () => decide(outcome));\n return b;\n };\n if (isPlan) {\n actions.appendChild(mk(\"Approve plan\", \"once\", \"ok\"));\n actions.appendChild(mk(\"Keep planning\", \"deny\", \"no\"));\n } else {\n actions.appendChild(mk(\"Approve\", \"once\", \"ok\"));\n actions.appendChild(mk(\"Always\", \"always\", \"always\"));\n let approveEdited = null;\n if (req.kind === \"bash\" && commandInput) {\n approveEdited = document.createElement(\"button\");\n approveEdited.className = \"apBtn ok\";\n approveEdited.textContent = \"Approve edited\";\n approveEdited.style.display = \"none\";\n approveEdited.addEventListener(\"click\", () => {\n decide(\"once\", { updatedInput: { ...req.input ?? {}, command: commandInput.value } });\n });\n actions.appendChild(approveEdited);\n const editToggle = head.querySelector(\"button\");\n if (editToggle) {\n editToggle.addEventListener(\"click\", () => {\n const isEditing = commandInput.style.display !== \"none\";\n if (approveEdited) approveEdited.style.display = isEditing ? \"\" : \"none\";\n });\n }\n }\n const reasonRow = document.createElement(\"div\");\n reasonRow.style.cssText = \"display:none;gap:6px;margin-top:4px;align-items:center;flex-wrap:wrap\";\n const reasonInput = document.createElement(\"input\");\n reasonInput.type = \"text\";\n reasonInput.placeholder = \"Reason for denying (optional)\";\n reasonInput.style.cssText = \"flex:1;min-width:120px;background:var(--an-bg-1);border:1px solid var(--an-line);border-radius:4px;padding:4px 8px;font-size:0.82em;color:inherit;outline:none\";\n const confirmDeny = document.createElement(\"button\");\n confirmDeny.className = \"apBtn no\";\n confirmDeny.textContent = \"Deny\";\n confirmDeny.addEventListener(\"click\", () => decide(\"deny\", { reason: reasonInput.value.trim() || void 0 }));\n const cancelDeny = document.createElement(\"button\");\n cancelDeny.className = \"apBtn\";\n cancelDeny.textContent = \"↩\";\n cancelDeny.style.cssText = \"opacity:0.6\";\n cancelDeny.addEventListener(\"click\", () => {\n reasonRow.style.display = \"none\";\n });\n reasonRow.appendChild(reasonInput);\n reasonRow.appendChild(confirmDeny);\n reasonRow.appendChild(cancelDeny);\n const denyBtn = document.createElement(\"button\");\n denyBtn.className = \"apBtn no\";\n denyBtn.textContent = \"Deny\";\n denyBtn.addEventListener(\"click\", () => {\n reasonRow.style.display = \"flex\";\n reasonInput.focus();\n });\n actions.appendChild(denyBtn);\n card.appendChild(actions);\n card.appendChild(reasonRow);\n }\n if (isPlan) card.appendChild(actions);\n card.addEventListener(\"keydown\", (e) => {\n const btnsAll = Array.from(actions.querySelectorAll(\"button\"));\n const i = btnsAll.indexOf(document.activeElement);\n if (i < 0) return;\n if (e.key === \"ArrowRight\") {\n e.preventDefault();\n btnsAll[(i + 1) % btnsAll.length].focus();\n } else if (e.key === \"ArrowLeft\") {\n e.preventDefault();\n btnsAll[(i + btnsAll.length - 1) % btnsAll.length].focus();\n }\n });\n approvalDock.insertBefore(card, approvalDock.firstChild);\n syncComposerLock();\n if (document.hasFocus()) {\n const first = actions.querySelector(\"button\");\n if (first) first.focus();\n }\n }\n\n // src/chat/ui/panel/messages.ts\n function onMessage(msg) {\n if (msg.role === \"user\") {\n const info = S.pendingSentImages.length ? { thumbs: S.pendingSentImages.splice(0) } : msg.imageCount ? { count: msg.imageCount } : void 0;\n const body = startTurn(msg.text, void 0, info);\n if (S.typingEl) body.appendChild(S.typingEl);\n return;\n }\n if (msg.role === \"tool\") {\n renderTool(msg, false);\n return;\n }\n if (msg.role === \"summary\") {\n renderSummary(msg.text, false);\n return;\n }\n const badge = msg.role === \"assistant\" && msg.cli ? msg.cli : void 0;\n const asMd = (el, raw) => {\n if (msg.role === \"assistant\") renderMd(el, raw);\n else {\n el.textContent = raw;\n el.dataset.md = raw;\n }\n };\n if (msg.partial) {\n if (!S.streaming || S.streaming.dataset.role !== msg.role) {\n S.streaming = bubble(msg.role, false, badge);\n S.streaming.dataset.role = msg.role;\n S.streaming.dataset.acc = \"\";\n S.streaming.classList.add(\"cursor\");\n }\n S.streaming.dataset.acc = msg.text;\n scheduleStreamRender();\n return;\n } else {\n if (S.streaming && S.streaming.dataset.role === msg.role) {\n const prev = S.streaming.dataset.acc || \"\";\n const raw = msg.text.startsWith(prev) ? msg.text : prev + msg.text;\n S.streaming.classList.remove(\"cursor\");\n cancelStreamRender();\n asMd(S.streaming, raw);\n if (msg.role === \"assistant\") addMsgCopy(S.streaming._row, S.streaming);\n S.streaming = null;\n } else {\n const el = bubble(msg.role, false, badge);\n asMd(el, msg.text);\n if (msg.role === \"assistant\") {\n addFooter(el._row, msg.durationMs, msg.model);\n addMsgCopy(el._row, el);\n }\n }\n }\n if (S.typingEl) tailBody().appendChild(S.typingEl);\n stickToBottom();\n }\n\n // src/chat/ui/panel/sessions.ts\n var COLLAPSED = 5;\n function renderSessions() {\n sessList.innerHTML = \"\";\n if (S.cloudListState === \"reauth\" || S.cloudListState === \"transient\") {\n const warn = document.createElement(\"div\");\n warn.style.cssText = \"padding:4px 8px;font-size:11px;color:var(--vscode-editorWarning-foreground, #e5c07b);opacity:.9\";\n warn.textContent = S.cloudListState === \"reauth\" ? \"Cloud sync signed out. Showing this device only. Reconnect in Storage.\" : \"Cloud unreachable. Showing this device only.\";\n sessList.appendChild(warn);\n }\n emptyEl.style.display = S.allSessions.length ? \"none\" : \"block\";\n const shown = S.expanded ? S.allSessions : S.allSessions.slice(0, COLLAPSED);\n for (const s of shown) {\n const el = document.createElement(\"div\");\n el.className = \"sess\" + (s.sessionId === S.activeId ? \" active\" : \"\");\n const title = document.createElement(\"span\");\n title.className = \"title\";\n title.textContent = s.title || \"(untitled)\";\n const time = document.createElement(\"span\");\n time.className = \"time\";\n time.textContent = rel(s.ts);\n const del = document.createElement(\"span\");\n del.className = \"del\";\n del.textContent = \"✕\";\n del.title = \"Delete session\";\n del.onclick = (e) => {\n e.stopPropagation();\n vscode.postMessage({ type: \"delete\", sessionId: s.sessionId });\n };\n el.appendChild(title);\n el.appendChild(time);\n el.appendChild(del);\n el.onclick = () => {\n vscode.postMessage({ type: \"open\", sessionId: s.sessionId });\n closeMenus();\n };\n sessList.appendChild(el);\n }\n if (S.allSessions.length > COLLAPSED) {\n showAll.style.display = \"block\";\n showAll.textContent = S.expanded ? \"접기\" : \"모두 보기(\" + S.allSessions.length + \")\";\n } else {\n showAll.style.display = \"none\";\n }\n }\n function wireSessions() {\n showAll.addEventListener(\"click\", () => {\n S.expanded = !S.expanded;\n renderSessions();\n });\n }\n\n // src/chat/ui/panel/storage.ts\n var cloudState = document.getElementById(\"cloudState\");\n var cloudBtn = document.getElementById(\"cloudBtn\");\n function renderStorage(info, options) {\n S.storageOptions = options || S.storageOptions;\n S.cloudConnected = !!(info && info.connected);\n if (info && info.kind) S.cloudKind = info.kind;\n if (S.cloudConnected) {\n const label = info.kind === \"gdrive\" ? \"Google Drive\" : info.kind === \"icloud\" ? \"iCloud\" : info.kind === \"custom\" ? \"Cloud\" : info.kind;\n const linkable = info.kind === \"gdrive\";\n const acct = info.account ? ' (' + info.account + \")\" : \"\";\n cloudState.innerHTML = '●' + (linkable ? '' + label + \"\" : label) + acct;\n const link = document.getElementById(\"cloudLink\");\n if (link) link.addEventListener(\"click\", (e) => {\n e.preventDefault();\n vscode.postMessage({ type: \"openCloud\", kind: info.kind });\n });\n cloudBtn.textContent = \"disconnect\";\n } else {\n cloudState.innerHTML = '●No cloud';\n cloudBtn.textContent = \"connect\";\n }\n }\n function wireStorage() {\n cloudBtn.addEventListener(\"click\", () => {\n if (S.cloudConnected) {\n vscode.postMessage({ type: \"disconnectCloud\" });\n return;\n }\n vscode.postMessage({ type: \"pickCloud\" });\n });\n }\n\n // src/chat/ui/panel/profile.ts\n function renderAgentIdCard(profile) {\n const el = document.getElementById(\"agentIdCard\");\n if (!el) return;\n const wallet = profile.wallet;\n const rep = profile.reputation || {};\n const repoStars = (profile.verifiedRepos || []).reduce((s, r) => s + (r.stars || 0), 0);\n const ti = profTierInfo(repoStars);\n const curName = ti.cur && ti.cur.name || ti.next && ti.next.name || \"Bronze\";\n const tierColor = TIER_COLOR[curName] || \"var(--an-tier-bronze)\";\n const prevMin = ti.cur ? ti.cur.min : 0;\n const bandPct = ti.next ? Math.min(100, Math.max(0, (repoStars - prevMin) / (ti.next.min - prevMin) * 100)) : 100;\n const litSegs = Math.round(bandPct / 100 * PROF_SEG);\n const starsFrac = ti.next ? repoStars + \"/\" + ti.next.min : \"MAX\";\n const stats = [[\"CREATED\", pad2((profile.createdSkills || []).length)], [\"COPIES\", pad2(rep.totalSupply || 0)], [\"OWNED\", pad2((profile.ownedSkills || []).length)]];\n let statsHtml = \"\";\n stats.forEach((s) => {\n statsHtml += '' + s[0] + '' + s[1] + \"\";\n });\n let rungs = \"\";\n PROF_TIERS.forEach((t) => {\n const isCur = t.name === curName;\n const done = repoStars >= t.min && !isCur;\n rungs += '' + t.name.toUpperCase() + \"\";\n });\n let segs = \"\";\n for (let i = 0; i < PROF_SEG; i++) segs += '';\n el.innerHTML = 'AGENT' + wallet.slice(0, 6) + '…' + wallet.slice(-4) + \"
\" + (profile.self ? \"YOUR AGENT\" : \"AGENT PROFILE\") + '' + avatarSvg(wallet) + 'ID//' + wallet.slice(0, 4) + '' + statsHtml + 'TIER' + rungs + 'STARS' + segs + '' + starsFrac + \"\";\n }\n var GH_MARK_SVG = '';\n var FOLDER_BINARY = \"01010100101010100101001010101001010010110100101010010101001010010101001010010110101001010010100100101001010101001010\";\n function verifiedWorkHtml(profile) {\n const repos = (profile.verifiedRepos || []).slice().sort((a, b) => (b.stars || 0) - (a.stars || 0));\n if (!repos.length) return \"\";\n let cards = \"\";\n repos.forEach((r) => {\n const stars = r.stars || 0;\n const tier = profRepoTier(stars);\n const fill = profRepoFill(stars);\n let gauge = \"\";\n for (let i = 0; i < 10; i++) gauge += '';\n const url = (r.url || \"\").slice(0, 4) === \"http\" ? r.url : \"\";\n const open = url ? '' : '';\n cards += open + '' + FOLDER_BINARY + '>VERIFIED_REPO' + escapeHtml(r.owner || \"\") + '/>' + escapeHtml(r.name || \"\") + \"\" + GH_MARK_SVG + '' + stars + '★' + gauge + \"\" + (url ? \"\" : \"\");\n });\n return 'Verified work' + cards + \"\";\n }\n function closeRepoRegister() {\n if (S.repoModalEl) {\n S.repoModalEl.remove();\n S.repoModalEl = null;\n }\n }\n function openRepoRegister() {\n closeRepoRegister();\n const ov = document.createElement(\"div\");\n ov.className = \"skModal\";\n ov.id = \"repoModal\";\n ov.addEventListener(\"click\", (e) => {\n if (e.target === ov) closeRepoRegister();\n });\n const card = document.createElement(\"div\");\n card.className = \"skModal-card\";\n card.innerHTML = 'Register GitHub workLoading…';\n ov.appendChild(card);\n document.body.appendChild(ov);\n card.querySelector(\".skModal-close\").addEventListener(\"click\", closeRepoRegister);\n S.repoModalEl = ov;\n vscode.postMessage({ type: \"getGithubStatus\" });\n }\n function renderRepoModalBody(status) {\n if (!S.repoModalEl) return;\n const body = S.repoModalEl.querySelector(\".rr-body\");\n if (!body) return;\n body.innerHTML = \"\";\n if (!status || !status.hasToken) {\n const p = document.createElement(\"div\");\n p.className = \"rr-hint\";\n p.textContent = \"Add a GitHub token (repo scope) to register your work. Stored locally on this device, never synced.\";\n const inp = document.createElement(\"input\");\n inp.type = \"password\";\n inp.className = \"rr-input\";\n inp.placeholder = \"ghp_… (GitHub personal access token)\";\n const link = document.createElement(\"a\");\n link.className = \"rr-link\";\n link.href = \"https://github.com/settings/tokens/new?scopes=repo&description=AgentNet\";\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.textContent = \"Create a token ↗\";\n const err = document.createElement(\"div\");\n err.className = \"rr-err\";\n const btn = document.createElement(\"button\");\n btn.className = \"rr-btn\";\n btn.textContent = \"Save token\";\n btn.addEventListener(\"click\", () => {\n const t = inp.value.trim();\n if (!t) return;\n btn.disabled = true;\n btn.textContent = \"Saving…\";\n err.style.display = \"none\";\n vscode.postMessage({ type: \"submitGithubToken\", token: t });\n });\n body.appendChild(p);\n body.appendChild(inp);\n body.appendChild(link);\n body.appendChild(err);\n body.appendChild(btn);\n } else {\n const p = document.createElement(\"div\");\n p.className = \"rr-hint\";\n p.textContent = \"Register a public GitHub repo as verified work\" + (status.masked ? \" (token \" + status.masked + \")\" : \"\") + \".\";\n const inp = document.createElement(\"input\");\n inp.type = \"text\";\n inp.className = \"rr-input\";\n inp.placeholder = \"owner/name or github.com URL\";\n body.appendChild(p);\n body.appendChild(inp);\n const owned = S.ownedSkills.filter((n) => !!S.skillMints[n]);\n const checks = [];\n if (owned.length) {\n const lbl = document.createElement(\"div\");\n lbl.className = \"rr-sublabel\";\n lbl.textContent = \"Link skills (optional)\";\n body.appendChild(lbl);\n const list = document.createElement(\"div\");\n list.className = \"rr-skills\";\n owned.forEach((n) => {\n const row = document.createElement(\"label\");\n row.className = \"rr-skill\";\n const cb = document.createElement(\"input\");\n cb.type = \"checkbox\";\n cb.value = S.skillMints[n];\n const nm = document.createElement(\"span\");\n nm.textContent = n;\n row.appendChild(cb);\n row.appendChild(nm);\n list.appendChild(row);\n checks.push(cb);\n });\n body.appendChild(list);\n }\n const err = document.createElement(\"div\");\n err.className = \"rr-err\";\n const btn = document.createElement(\"button\");\n btn.className = \"rr-btn\";\n btn.textContent = \"Register repo\";\n btn.addEventListener(\"click\", () => {\n const repo = inp.value.trim();\n if (!repo) {\n err.textContent = \"Enter a repo (owner/name).\";\n err.style.display = \"\";\n return;\n }\n const mints = checks.filter((c) => c.checked).map((c) => c.value);\n btn.disabled = true;\n btn.textContent = \"Registering…\";\n err.style.display = \"none\";\n vscode.postMessage({ type: \"registerWorkRepo\", repo, skillMints: mints });\n });\n body.appendChild(err);\n body.appendChild(btn);\n }\n }\n function renderProfile(profile) {\n profile = mergeOptimistic(profile);\n const self = profile.self;\n const wallet = profile.wallet;\n document.getElementById(\"profileSelfOnly2\").style.display = self ? \"\" : \"none\";\n renderAgentIdCard(profile);\n const body = document.getElementById(\"profileBody\");\n body.innerHTML = \"\";\n const tabs2 = document.createElement(\"div\");\n tabs2.className = \"pr-tabs\";\n const tabAgent = document.createElement(\"button\");\n tabAgent.className = \"pr-tab on\";\n tabAgent.innerHTML = 'Agentエージェント';\n const tabCommunity = document.createElement(\"button\");\n tabCommunity.className = \"pr-tab\";\n tabCommunity.innerHTML = 'Communityコミュニティ';\n tabs2.appendChild(tabAgent);\n tabs2.appendChild(tabCommunity);\n body.appendChild(tabs2);\n const paneAgent = document.createElement(\"div\");\n const paneCommunity = document.createElement(\"div\");\n paneCommunity.style.display = \"none\";\n body.appendChild(paneAgent);\n body.appendChild(paneCommunity);\n function selectTab2(which) {\n const onAgent = which === \"agent\";\n S.profileTab = onAgent ? \"agent\" : \"community\";\n tabAgent.classList.toggle(\"on\", onAgent);\n tabCommunity.classList.toggle(\"on\", !onAgent);\n paneAgent.style.display = onAgent ? \"\" : \"none\";\n paneCommunity.style.display = onAgent ? \"none\" : \"\";\n }\n tabAgent.addEventListener(\"click\", () => selectTab2(\"agent\"));\n tabCommunity.addEventListener(\"click\", () => selectTab2(\"community\"));\n if (self) {\n const rr = document.createElement(\"button\");\n rr.className = \"pr-repo-add\";\n rr.textContent = \"+ Register GitHub work\";\n rr.addEventListener(\"click\", openRepoRegister);\n paneAgent.appendChild(rr);\n }\n const vwHtml = verifiedWorkHtml(profile);\n if (vwHtml) {\n const vw = document.createElement(\"div\");\n vw.innerHTML = vwHtml;\n paneAgent.appendChild(vw);\n }\n function skillCard(card) {\n const owned = self || S.ownedSkills.indexOf(card.name) >= 0;\n return skillSdCard(card, { owned, dim: owned && !self, onOpen: (c) => openSkillModal(c.id) });\n }\n if (profile.createdSkills.length) {\n const sec = document.createElement(\"div\");\n sec.className = \"pr-sec\";\n sec.textContent = \"Created skills\";\n paneAgent.appendChild(sec);\n if (!self) {\n const notOwned = profile.createdSkills.filter((c) => S.ownedSkills.indexOf(c.name) < 0).length;\n if (notOwned > 0) {\n const buyAll = document.createElement(\"button\");\n buyAll.className = \"pr-buyall\";\n buyAll.textContent = \"Buy all · \" + notOwned + \" not owned\";\n buyAll.addEventListener(\"click\", () => showBuyAllConfirm(profile));\n paneAgent.appendChild(buyAll);\n }\n }\n const cg = document.createElement(\"div\");\n cg.className = \"an-sd-grid\";\n profile.createdSkills.forEach((c) => cg.appendChild(skillCard(c)));\n paneAgent.appendChild(cg);\n }\n const ownedNotCreated = profile.ownedSkills.filter((o) => !profile.createdSkills.some((c) => c.id === o.id));\n if (ownedNotCreated.length) {\n const sec = document.createElement(\"div\");\n sec.className = \"pr-sec\";\n sec.textContent = \"Owned skills\";\n paneAgent.appendChild(sec);\n const og = document.createElement(\"div\");\n og.className = \"an-sd-grid\";\n ownedNotCreated.forEach((o) => og.appendChild(skillCard(o)));\n paneAgent.appendChild(og);\n }\n if (!profile.createdSkills.length && !ownedNotCreated.length && !vwHtml) {\n const e = document.createElement(\"div\");\n e.className = \"pr-empty\";\n e.textContent = \"No work or skills yet.\";\n paneAgent.appendChild(e);\n }\n const allThreads = profile.threads || [];\n const selfNotes = allThreads.filter((t) => t.note.isSelfNote).map((t) => t.note);\n if (selfNotes.length) {\n const sec = document.createElement(\"div\");\n sec.className = \"pr-sec\";\n sec.textContent = \"Blog\";\n paneCommunity.appendChild(sec);\n const blog = document.createElement(\"div\");\n blog.className = \"pr-blog\";\n blog.tabIndex = 0;\n blog.setAttribute(\"role\", \"list\");\n blog.setAttribute(\"aria-label\", \"Blog posts\");\n blog.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"ArrowLeft\" && e.key !== \"ArrowRight\") return;\n e.preventDefault();\n blog.scrollBy({ left: e.key === \"ArrowRight\" ? 260 : -260, behavior: \"smooth\" });\n });\n selfNotes.forEach((n) => blog.appendChild(noteCard(n, false)));\n enableDragScroll(blog);\n paneCommunity.appendChild(blog);\n }\n {\n const canPost = self || !!profile.canComment;\n const feedback = feedbackFor(wallet);\n const sec = document.createElement(\"div\");\n sec.className = \"pr-sec\";\n sec.innerHTML = self ? \"FORM // POST TO BLOG\" : \"FORM // WRITE A COMMENT\";\n paneCommunity.appendChild(sec);\n const compose = document.createElement(\"div\");\n compose.className = \"pr-compose\";\n let titleInput = null;\n if (self) {\n titleInput = document.createElement(\"input\");\n titleInput.type = \"text\";\n titleInput.placeholder = \"Title (optional)\";\n }\n const ta = document.createElement(\"textarea\");\n ta.placeholder = self ? \"Write a blog post or update…\" : \"Share your experience with this agent…\";\n const imgInput = document.createElement(\"input\");\n imgInput.type = \"text\";\n imgInput.placeholder = \"Image link / on-chain address / tx id (optional)\";\n const gitInput = document.createElement(\"input\");\n gitInput.type = \"text\";\n gitInput.placeholder = \"GitHub / git URL (optional)\";\n const errEl = document.createElement(\"div\");\n errEl.className = \"pr-err\";\n if (feedback) {\n errEl.textContent = feedback.text;\n errEl.style.display = \"\";\n if (feedback.ok) errEl.classList.add(\"ok\");\n }\n const btn = document.createElement(\"button\");\n btn.textContent = self ? \"Post\" : \"Comment\";\n if (!canPost) {\n ta.disabled = true;\n gitInput.disabled = true;\n imgInput.disabled = true;\n btn.disabled = true;\n if (titleInput) titleInput.disabled = true;\n const hint = document.createElement(\"div\");\n hint.className = \"pr-hint\";\n hint.textContent = \"Own ≥1 of this agent’s skills to comment.\";\n compose.appendChild(hint);\n }\n btn.addEventListener(\"click\", () => {\n const text = ta.value.trim();\n if (!text) return;\n const gitLink = gitInput.value.trim() || void 0;\n const image = imgInput.value.trim() || void 0;\n const title = titleInput ? titleInput.value.trim() || void 0 : void 0;\n S.postFeedback = null;\n btn.disabled = true;\n btn.textContent = self ? \"Posting…\" : \"Commenting…\";\n errEl.style.display = \"none\";\n errEl.classList.remove(\"ok\");\n S.pendingPost = { wallet, text, gitLink, self };\n const out = { type: \"postAgentNote\", agentWallet: wallet, text, gitLink };\n if (image) out.image = image;\n if (title) out.title = title;\n vscode.postMessage(out);\n });\n if (titleInput) compose.appendChild(titleInput);\n compose.appendChild(ta);\n compose.appendChild(imgInput);\n compose.appendChild(gitInput);\n compose.appendChild(errEl);\n compose.appendChild(btn);\n paneCommunity.appendChild(compose);\n body._postBtn = btn;\n body._postErr = errEl;\n body._postLabel = self ? \"Post\" : \"Comment\";\n body._postTa = ta;\n body._postGit = gitInput;\n body._postImg = imgInput;\n body._postTitle = titleInput;\n }\n const canReply = self || !!profile.canComment;\n function attachReply(cardEl, parentId) {\n if (!canReply) return;\n const bar = document.createElement(\"div\");\n bar.className = \"pr-replybar\";\n const toggle = document.createElement(\"button\");\n toggle.className = \"pr-replybtn\";\n toggle.textContent = \"Reply\";\n bar.appendChild(toggle);\n cardEl.appendChild(bar);\n let box = null;\n toggle.addEventListener(\"click\", () => {\n if (box) {\n box.remove();\n box = null;\n toggle.textContent = \"Reply\";\n return;\n }\n toggle.textContent = \"Cancel\";\n box = document.createElement(\"div\");\n box.className = \"pr-compose pr-replycompose\";\n const ta = document.createElement(\"textarea\");\n ta.placeholder = \"Write a reply…\";\n const err = document.createElement(\"div\");\n err.className = \"pr-err\";\n const send2 = document.createElement(\"button\");\n send2.textContent = \"Reply\";\n send2.addEventListener(\"click\", () => {\n const text = ta.value.trim();\n if (!text) return;\n send2.disabled = true;\n send2.textContent = \"Replying…\";\n err.style.display = \"none\";\n S.pendingPost = { wallet, text, parentId, self };\n vscode.postMessage({ type: \"postAgentNote\", agentWallet: wallet, text, parentId });\n });\n box.appendChild(ta);\n box.appendChild(err);\n box.appendChild(send2);\n cardEl.appendChild(box);\n ta.focus();\n });\n }\n const commentThreads = allThreads.filter((t) => !t.note.isSelfNote);\n const commentCount = commentThreads.reduce((s, t) => s + 1 + (t.replies ? t.replies.length : 0), 0);\n if (commentThreads.length) {\n const sec = document.createElement(\"div\");\n sec.className = \"pr-sec\";\n sec.textContent = \"Comments (\" + commentCount + \")\";\n paneCommunity.appendChild(sec);\n commentThreads.forEach((t) => {\n const opCard = noteCard(t.note, true);\n attachReply(opCard, t.note.id);\n paneCommunity.appendChild(opCard);\n (t.replies || []).forEach((rep) => {\n const rc = noteCard(rep, true);\n rc.classList.add(\"pr-reply\");\n if (rep.parentAuthor) {\n const to = document.createElement(\"div\");\n to.className = \"pr-replyto\";\n to.textContent = \"↳ replying to \" + agShort(rep.parentAuthor);\n rc.insertBefore(to, rc.firstChild);\n }\n attachReply(rc, rep.id);\n paneCommunity.appendChild(rc);\n });\n });\n } else {\n const e = document.createElement(\"div\");\n e.className = \"pr-empty\";\n e.textContent = self ? \"No comments yet.\" : \"No comments yet. Be the first.\";\n paneCommunity.appendChild(e);\n }\n selectTab2(S.profileTab);\n }\n function showBuyAllConfirm(profile) {\n const notOwned = profile.createdSkills.filter((c) => S.ownedSkills.indexOf(c.name) < 0);\n if (!notOwned.length) return;\n const totalLamports = notOwned.reduce((acc, c) => acc + (c.price ? BigInt(c.price) : 0n), 0n);\n const totalSol = totalLamports > 0n ? (Number(totalLamports) / 1e9).toFixed(4) + \" SOL\" : \"free\";\n const body = document.getElementById(\"profileBody\");\n const existing = body.querySelector(\".pr-confirm\");\n if (existing) {\n existing.remove();\n return;\n }\n const confirm = document.createElement(\"div\");\n confirm.className = \"pr-confirm\";\n const h = document.createElement(\"div\");\n h.style.fontWeight = \"600\";\n h.textContent = \"Buy \" + notOwned.length + \" skill\" + (notOwned.length !== 1 ? \"s\" : \"\") + \" (\" + totalSol + \")\";\n const ul = document.createElement(\"ul\");\n notOwned.forEach((c) => {\n const li = document.createElement(\"li\");\n li.textContent = c.name || c.id;\n ul.appendChild(li);\n });\n const btns = document.createElement(\"div\");\n btns.className = \"confirm-btns\";\n const ok = document.createElement(\"button\");\n ok.className = \"pr-buyall\";\n ok.style.justifyContent = \"center\";\n ok.textContent = \"Confirm\";\n const cancel = document.createElement(\"button\");\n cancel.textContent = \"Cancel\";\n ok.addEventListener(\"click\", () => {\n ok.disabled = true;\n cancel.disabled = true;\n ok.textContent = \"Buying…\";\n vscode.postMessage({ type: \"buyAllSkills\", wallet: profile.wallet });\n });\n cancel.addEventListener(\"click\", () => confirm.remove());\n btns.appendChild(ok);\n btns.appendChild(cancel);\n confirm.appendChild(h);\n confirm.appendChild(ul);\n confirm.appendChild(btns);\n const buyAllBtn = body.querySelector(\".pr-buyall\");\n if (buyAllBtn && buyAllBtn.parentNode) buyAllBtn.parentNode.insertBefore(confirm, buyAllBtn.nextSibling);\n else body.appendChild(confirm);\n }\n\n // src/chat/ui/panel/wallet.ts\n function setWallet(address) {\n S.myWalletAddress = address || null;\n updateFdFab();\n const full = address || \"(not connected)\";\n document.getElementById(\"wAddr\").textContent = address ? short(address) : \"not connected\";\n const label = address ? short(address) : \"My Wallet\";\n document.getElementById(\"wName\").textContent = label;\n document.getElementById(\"wName2\").textContent = address ? \"My Wallet\" : \"My Wallet\";\n const svg = address ? avatarSvg(address) : \"\";\n document.getElementById(\"wAvatar\").innerHTML = svg;\n document.getElementById(\"wAvatar2\").innerHTML = svg;\n if (S.currentProfileWallet === null || S.currentProfileWallet === address) {\n document.getElementById(\"walletAddr\").textContent = full;\n document.getElementById(\"wAvatarBig\").innerHTML = svg;\n }\n }\n function renderCloudSync(status) {\n const el = document.getElementById(\"cloudSync\");\n if (!el) return;\n el.onclick = null;\n el.style.cursor = \"\";\n if (!status || !S.cloudConnected) {\n el.textContent = \"\";\n el.className = \"\";\n el.title = \"\";\n return;\n }\n if (status.ok) {\n el.textContent = \"✓\";\n el.className = \"ok\";\n el.title = \"Synced to Drive\";\n } else if (status.reason === \"reauth\") {\n el.textContent = \"⚠ reconnect\";\n el.className = \"err\";\n el.title = \"Cloud sign-in expired. Click to reconnect \" + (S.cloudKind === \"gdrive\" ? \"Google Drive\" : \"cloud\") + \".\";\n el.style.cursor = \"pointer\";\n el.onclick = () => vscode.postMessage({ type: \"reconnectCloud\", kind: S.cloudKind || \"gdrive\" });\n } else {\n el.textContent = \"⚠\";\n el.className = \"err\";\n el.title = \"Drive sync failed (auto-retrying): \" + (status.error || \"unknown\");\n }\n }\n function renderWalletStorage() {\n const el = document.getElementById(\"walletStorage\");\n if (!el) return;\n el.textContent = S.cloudConnected ? \"Local + cloud mirror (connected)\" : \"Local only (no cloud)\";\n }\n function wireWallet() {\n document.getElementById(\"disconnectWalletBtn\").addEventListener(\"click\", () => {\n vscode.postMessage({ type: \"disconnectWallet\" });\n });\n document.getElementById(\"walletDisconnect\").addEventListener(\"click\", () => {\n vscode.postMessage({ type: \"disconnectWallet\" });\n });\n }\n\n // src/chat/ui/panel/paging.ts\n function resetPaging() {\n S.pageCursor = null;\n S.hasMore = false;\n S.loadingOlder = false;\n S.lastOlderCursor = null;\n }\n function prependOlder(messages) {\n const before = log.scrollHeight;\n const realLog = log;\n const frag = document.createElement(\"div\");\n let body = null;\n const openTurn = (userText, badge, imageCount) => {\n const turn = document.createElement(\"div\");\n turn.className = \"turn\";\n const head = document.createElement(\"div\");\n head.className = \"turnHead\";\n head.innerHTML = '>';\n head.appendChild(makeUtext(userText));\n if (badge) {\n const b2 = document.createElement(\"span\");\n b2.className = \"badge \" + badge;\n b2.textContent = badge === \"codex\" ? \"codex · gpt\" : \"claude\";\n head.appendChild(b2);\n }\n const imgEl = userImagesEl(imageCount ? { count: imageCount } : void 0);\n if (imgEl) head.appendChild(imgEl);\n const b = document.createElement(\"div\");\n b.className = \"turnBody\";\n turn.appendChild(head);\n turn.appendChild(b);\n frag.appendChild(turn);\n return b;\n };\n const node = (cls) => {\n const n = document.createElement(\"div\");\n n.className = \"node \" + cls;\n (body || (body = openTurn(\"\", void 0))).appendChild(n);\n return n;\n };\n for (const m of messages) {\n if (m.role === \"user\") {\n body = openTurn(m.text, void 0, m.imageCount);\n continue;\n }\n if (m.role === \"tool\") {\n renderToolInto(node(\"tool\"), m);\n continue;\n }\n if (m.role === \"summary\") {\n const n2 = node(\"summary\");\n n2.innerHTML = '⌘ context compacted';\n const sb = document.createElement(\"div\");\n sb.className = \"summaryBody\";\n sb.textContent = m.text;\n n2.appendChild(sb);\n continue;\n }\n const n = node(m.role + (m.role === \"assistant\" && m.cli ? \" \" + m.cli : \"\"));\n const el = document.createElement(\"div\");\n el.className = \"msg \" + m.role;\n if (m.role === \"assistant\") renderMd(el, m.text);\n else {\n el.textContent = m.text;\n el.dataset.md = m.text;\n }\n n.appendChild(el);\n if (m.role === \"assistant\") {\n addFooter(n, m.durationMs, m.model);\n addMsgCopy(n, el);\n }\n }\n const prevBehavior = realLog.style.scrollBehavior;\n realLog.style.scrollBehavior = \"auto\";\n realLog.insertBefore(frag, realLog.firstChild);\n void realLog.offsetHeight;\n realLog.scrollTop += realLog.scrollHeight - before;\n realLog.style.scrollBehavior = prevBehavior;\n }\n function requestOlder() {\n if (S.hasMore && !S.loadingOlder && S.pageCursor !== null && S.pageCursor !== S.lastOlderCursor) {\n S.loadingOlder = true;\n S.lastOlderCursor = S.pageCursor;\n vscode.postMessage({ type: \"loadMore\", cursor: S.pageCursor });\n }\n }\n function maybeFillOlder() {\n if (log.scrollHeight <= log.clientHeight) requestOlder();\n }\n function wirePaging() {\n log.addEventListener(\"scroll\", () => {\n if (log.scrollTop < 600) requestOlder();\n S.stick = nearBottom();\n updateJump();\n });\n }\n\n // src/chat/ui/panel/main.ts\n wireShell();\n wireSlash();\n wireEngine();\n wireSessions();\n wireComposer();\n wireStorage();\n wireViews();\n wirePublish();\n wireFeed();\n wireMenus();\n wireSkills();\n wireMarket();\n wireOverlays();\n wireWallet();\n wirePaging();\n window.addEventListener(\"message\", (event) => {\n const m = event.data;\n if (m.type === \"message\") onMessage(m.msg);\n else if (m.type === \"sessions\") {\n S.allSessions = m.list || [];\n S.cloudListState = m.cloud || \"none\";\n S.activeId = m.activeId;\n renderSessions();\n } else if (m.type === \"notice\") renderNotice(m.text || \"\");\n else if (m.type === \"status\") renderStatus(m.status || {});\n else if (m.type === \"loading\") showLoading();\n else if (m.type === \"clear\") {\n log.innerHTML = \"\";\n approvalDock.innerHTML = \"\";\n ctxMeter.style.display = \"none\";\n syncComposerLock();\n S.streaming = null;\n S.openBash = null;\n S.tailTurn = null;\n S.headTurn = null;\n hideTyping();\n hideActivity();\n resetPaging();\n syncWatermark();\n hideLoading();\n } else if (m.type === \"turnEnd\") {\n hideTyping();\n hideActivity();\n } else if (m.type === \"modelOptions\") {\n applyModelOptions(m.cli, m.options);\n } else if (m.type === \"usage\") {\n const n = m.contextTokens;\n if (typeof n === \"number\") {\n const label = n >= 1e3 ? Math.round(n / 1e3) + \"k\" : String(n);\n ctxMeter.textContent = \"ctx: \" + label;\n ctxMeter.style.display = \"inline-flex\";\n }\n } else if (m.type === \"skillActive\" && m.origin === \"nft\" && m.mint) flashSkill(m.name, m.mint);\n else if (m.type === \"rpcStatus\") renderRpcStatus(m.status);\n else if (m.type === \"skillShopping\") setShopToggle(m.on);\n else if (m.type === \"searchResults\") {\n S.lastMarketResults = m.results || [];\n renderMarketResults(m.results);\n } else if (m.type === \"searchError\") {\n const msg = \"Search failed: \" + escapeHtml(m.message || \"unknown\");\n mktResults.innerHTML = '' + msg + \"\";\n if (S.currentProfileWallet) {\n document.getElementById(\"agentIdCard\").innerHTML = 'Could not load profile.';\n document.getElementById(\"profileBody\").innerHTML = '' + msg + \"\";\n }\n } else if (m.type === \"skillDetail\") {\n if (S.skillModalOpen) renderSkillModal(m.detail);\n else renderDetail(m.detail);\n } else if (m.type === \"skillDoc\") {\n if (S.skillDocOpen) renderSkillDoc(m.name, m.text);\n } else if (m.type === \"postNoteResult\") {\n const submit = mktDetailBody.querySelector(\".dt-note-submit\");\n if (submit) {\n m.ok ? submit._reset && submit._reset() : submit._fail && submit._fail(m.error || \"Post failed\");\n }\n } else if (m.type === \"notes\" && S.currentDetail && m.skillId === S.currentDetail.id) {\n const owned = ownsMint(S.currentDetail.id) || S.ownedSkills.indexOf(S.currentDetailName) >= 0;\n renderComments(S.currentDetail.id, S.currentDetail.type, m.notes, owned);\n } else if (m.type === \"ownedSkills\") {\n S.disposedMints = m.disposedMints || {};\n S.disposedMintSet = new Set(Object.values(S.disposedMints));\n S.workflowMintSet = new Set(m.workflowMints || []);\n setSkills(m.names || [], m.mints || {}, m.meta || {});\n if (panels.market.style.display !== \"none\") renderMarketResults(S.lastMarketResults);\n refreshDetailOwned();\n refreshModalOwned();\n } else if (m.type === \"buyResult\") {\n if (m.ok) {\n showComplete(S.currentDetail && S.currentDetail.type === \"workflow\" ? \"WORKFLOW PURCHASED\" : \"SKILL PURCHASED\");\n vscode.postMessage({ type: \"getBalance\" });\n } else {\n showBuyError(m.error, m.code === \"insufficient_funds\" && S.rpcNetwork !== \"mainnet\");\n if (S.detailBuyBtn) {\n S.detailBuyBtn.disabled = false;\n S.detailBuyBtn.textContent = \"Buy\";\n }\n if (S.skillModalBuyBtn) {\n S.skillModalBuyBtn.disabled = false;\n S.skillModalBuyBtn.textContent = \"Buy\";\n }\n renderMarketResults(S.lastMarketResults);\n }\n } else if (m.type === \"airdropResult\") {\n if (m.ok) {\n renderNotice(\"Funded. Try the purchase again.\");\n vscode.postMessage({ type: \"getBalance\" });\n hideBuyError();\n } else {\n renderNotice(\"Get SOL failed: \" + (m.error || \"unknown\"));\n const fb = buyErrEl.querySelector(\".buyErrFund\");\n if (fb) {\n fb.disabled = false;\n fb.textContent = \"Get devnet SOL\";\n }\n }\n } else if (m.type === \"githubStatus\") {\n renderRepoModalBody(m);\n } else if (m.type === \"workRepoRegistered\") {\n if (m.ok) {\n showComplete(\"GITHUB REGISTERED\");\n closeRepoRegister();\n if (S.currentProfileWallet) vscode.postMessage({ type: \"getAgentProfile\", wallet: S.currentProfileWallet });\n } else if (S.repoModalEl) {\n const b = S.repoModalEl.querySelector(\".rr-body\");\n const err = b && b.querySelector(\".rr-err\");\n const btn = b && b.querySelector(\".rr-btn\");\n if (err) {\n err.textContent = m.error || \"Registration failed.\";\n err.style.display = \"\";\n }\n if (btn) {\n btn.disabled = false;\n btn.textContent = \"Register repo\";\n }\n }\n } else if (m.type === \"disposeResult\") {\n if (m.ok) {\n if (S.currentDetail) openDetail(S.currentDetail.id);\n } else {\n showBuyError(m.error || \"Unequip failed\");\n const rm = mktDetailBody.querySelector(\".dt-remove\");\n if (rm) {\n rm.disabled = false;\n rm.textContent = \"Unequip\";\n }\n }\n } else if (m.type === \"reEquipResult\") {\n if (m.ok) {\n if (S.currentDetail) openDetail(S.currentDetail.id);\n } else {\n showBuyError(m.error || \"Re-equip failed\");\n if (S.detailBuyBtn) {\n S.detailBuyBtn.disabled = false;\n S.detailBuyBtn.textContent = \"Re-equip\";\n }\n }\n } else if (m.type === \"agents\") renderAgents(m.agents);\n else if (m.type === \"agentProfile\") renderProfile(m.profile);\n else if (m.type === \"blogFeed\") renderFeed(m.posts);\n else if (m.type === \"blogPost\") {\n const wasQuote = !!quoteInflight[m.postId];\n if (wasQuote) {\n delete quoteInflight[m.postId];\n if (m.post) quoteCache[m.postId] = { post: m.post };\n }\n if (m.post) {\n feedBodies[m.postId] = m.post;\n if (S.currentFeedPost && S.currentFeedPost.id === m.postId) renderFeedPost();\n }\n if (wasQuote) {\n const els = S.quoteCards[m.postId] || [];\n delete S.quoteCards[m.postId];\n els.forEach((el) => fillQuoteCard(el, m.postId, m.post || null));\n }\n } else if (m.type === \"blogComments\") {\n feedThreads[m.postId] = m.threads || [];\n if (S.currentFeedPost && S.currentFeedPost.id === m.postId) renderFeedPost();\n } else if (m.type === \"blogCommentResult\") {\n const pane = document.getElementById(\"agFeedPost\");\n if (pane && S.currentFeedPost && S.currentFeedPost.id === m.postId) {\n if (m.ok) {\n if (pane._mainCompose) {\n pane._mainCompose._ta.value = \"\";\n pane._mainCompose._git.value = \"\";\n pane._mainCompose._btn.disabled = false;\n pane._mainCompose._btn.textContent = pane._mainCompose._label;\n }\n S.feedReplyTo = null;\n vscode.postMessage({ type: \"getBlogFeed\", sort: S.feedSort, fresh: !S.feedLastSage });\n } else {\n const box = pane._activeCompose || pane._mainCompose;\n if (box) {\n box._btn.disabled = false;\n box._btn.textContent = box._label;\n box._err.textContent = m.error || \"Comment failed\";\n box._err.style.display = \"\";\n }\n }\n }\n } else if (m.type === \"buyAllResult\") {\n const confirm = document.getElementById(\"profileBody\") && document.getElementById(\"profileBody\").querySelector(\".pr-confirm\");\n if (confirm) confirm.remove();\n if (m.bought > 0) {\n showComplete(m.bought === 1 ? \"SKILL PURCHASED\" : m.bought + \" SKILLS PURCHASED\");\n vscode.postMessage({ type: \"getBalance\" });\n if (m.failed > 0) showBuyError(m.failed + \" of \" + (m.bought + m.failed) + \" purchase(s) failed\");\n } else if (!m.ok || m.failed > 0) {\n showBuyError(m.error || (m.failed > 0 ? m.failed + \" purchase(s) failed\" : \"Purchase failed\"));\n }\n if (m.ok && S.currentProfileWallet) {\n vscode.postMessage({ type: \"getAgentProfile\", wallet: S.currentProfileWallet });\n vscode.postMessage({ type: \"ownedSkills\" });\n }\n } else if (m.type === \"agentNoteResult\") {\n const body = document.getElementById(\"profileBody\");\n const label = body && body._postLabel || \"Post\";\n if (!m.ok) {\n S.pendingPost = null;\n S.postFeedback = { wallet: m.agentWallet, text: m.error || \"Post failed\", ok: false, ts: Date.now() };\n if (body && body._postBtn) {\n body._postBtn.disabled = false;\n body._postBtn.textContent = label;\n if (body._postErr) {\n body._postErr.textContent = m.error || \"Post failed\";\n body._postErr.style.display = \"\";\n body._postErr.classList.remove(\"ok\");\n }\n }\n if (S.fabModalEl) {\n S.fabModalEl._btn.disabled = false;\n S.fabModalEl._btn.textContent = \"Post to AgentNet\";\n S.fabModalEl._err.textContent = m.error || \"Post failed\";\n S.fabModalEl._err.style.display = \"\";\n }\n } else {\n if (S.fabModalEl) {\n closeFabCompose();\n S.feedPosts = null;\n document.getElementById(\"feedList\").innerHTML = skFd(4);\n vscode.postMessage({ type: \"getBlogFeed\", sort: S.feedSort, fresh: true });\n }\n if (S.pendingPost) {\n S.recentlyPosted.push({\n wallet: S.pendingPost.wallet,\n ts: Date.now(),\n // author must be OUR wallet (the writer), not the profile owner's\n // (pendingPost.wallet): mergeOptimistic dedupes the ghost against the real\n // on-chain note by author+text, and the real note's author is the writer —\n // a profile-owner author only matches on self-posts, so comments on OTHER\n // agents rendered twice once the chain read caught up.\n note: { author: S.myWalletAddress || S.pendingPost.wallet, text: S.pendingPost.text, gitLink: S.pendingPost.gitLink, isSelfNote: S.pendingPost.self, parentId: S.pendingPost.parentId, timestamp: Date.now() }\n });\n S.pendingPost = null;\n }\n S.postFeedback = {\n wallet: m.agentWallet,\n text: label === \"Post\" ? \"Posted to blog.\" : \"Comment posted.\",\n ok: true,\n ts: Date.now()\n };\n showComplete(label === \"Post\" ? \"POST PUBLISHED\" : \"COMMENT POSTED\");\n if (body && body._postBtn) {\n body._postBtn.disabled = false;\n body._postBtn.textContent = label;\n if (body._postTa) body._postTa.value = \"\";\n if (body._postGit) body._postGit.value = \"\";\n if (body._postImg) body._postImg.value = \"\";\n if (body._postTitle) body._postTitle.value = \"\";\n if (body._postErr) {\n body._postErr.textContent = label === \"Post\" ? \"Posted to blog.\" : \"Comment posted.\";\n body._postErr.style.display = \"\";\n body._postErr.classList.add(\"ok\");\n }\n }\n }\n } else if (m.type === \"balance\") {\n S.solLamports = m.lamports;\n renderBalance();\n } else if (m.type === \"publishProgress\") {\n const phaseLabel = m.phase === \"store\" ? \"Storing on-chain\" : m.phase === \"mint\" ? \"Minting the NFT\" : \"Listing for sale\";\n pubSubmit.textContent = \"Publishing… \" + (m.total ? m.signed + \"/\" + m.total + \" signed · \" : \"\") + phaseLabel;\n } else if (m.type === \"publishResult\") {\n pubSubmit.disabled = false;\n pubSubmit.textContent = S.pubKind === \"workflow\" ? \"Publish workflow\" : \"Publish skill\";\n if (m.ok) {\n const nm = document.getElementById(\"pubName\").value.trim();\n [\"pubName\", \"pubDesc\", \"pubCategory\", \"pubHashtags\", \"pubImage\", \"pubText\"].forEach((id) => {\n document.getElementById(id).value = \"\";\n });\n document.getElementById(\"pubPrice\").value = \"0.1\";\n pubImageBadge.style.display = \"none\";\n for (const k in pubReqSel) delete pubReqSel[k];\n const wasWorkflow = S.pubKind === \"workflow\";\n setPubKind(\"skill\");\n showComplete(wasWorkflow ? \"WORKFLOW BUILT\" : \"SKILL CREATED\");\n showView(\"market\");\n } else {\n pubError.textContent = m.error || \"Publish failed.\";\n pubError.style.display = \"block\";\n }\n } else if (m.type === \"platform\") setTab(m.cli);\n else if (m.type === \"cliStatus\") {\n S.cliReport = { claude: m.claude, codex: m.codex };\n const status = S.cliReport[S.cli];\n if (status === \"no-login\") renderNotice((S.cli === \"claude\" ? \"Claude\" : \"Codex\") + \" is not signed in. Type /login to connect it.\");\n else if (status === \"missing\") renderEngineMissing(S.cli);\n } else if (m.type === \"engineUpdate\" && m.cli === \"codex\") {\n renderEngineBanner(\n \"Codex is out of date, so new models are hidden. Update it, then reload this window.\",\n [\n [\"Update in terminal\", () => vscode.postMessage({ type: \"installEngine\", cli: \"codex\", update: true })],\n copyAction(CODEX_UPDATE_CMD)\n ],\n \"codex\"\n );\n } else if (m.type === \"claudeLoginUrl\") {\n renderNotice(\"Claude sign-in opened. If the browser did not open, visit:\\n\" + m.url + \"\\nAfter approving, paste the returned code with /login .\");\n } else if (m.type === \"claudeLoginStatus\") {\n if (m.status === \"done\") {\n S.cliReport = Object.assign({}, S.cliReport || {}, { claude: \"ok\" });\n renderNotice(\"Claude sign-in complete.\");\n if (S.cli === \"claude\") vscode.postMessage({ type: \"platform\", cli: \"claude\" });\n } else {\n renderNotice(\"Claude sign-in failed: \" + (m.error || \"Login was not completed.\"));\n }\n } else if (m.type === \"codexLoginChallenge\") {\n renderNotice(\"Codex sign-in opened. If the browser did not open, visit:\\n\" + m.url + \"\\nEnter this one-time code on the page: \" + m.code);\n } else if (m.type === \"codexLoginStatus\") {\n if (m.status === \"done\") {\n S.cliReport = Object.assign({}, S.cliReport || {}, { codex: \"ok\" });\n renderNotice(\"Codex sign-in complete.\");\n if (S.cli === \"codex\") vscode.postMessage({ type: \"platform\", cli: \"codex\" });\n } else {\n renderNotice(\"Codex sign-in failed: \" + (m.error || \"Login was not completed.\"));\n }\n } else if (m.type === \"toast\") renderNotice(m.text || \"\");\n else if (m.type === \"openUrl\" && m.url) window.open(m.url, \"_blank\", \"noopener,noreferrer\");\n else if (m.type === \"storage\") {\n renderStorage(m.info, m.options);\n renderWalletStorage();\n } else if (m.type === \"cloudSync\") renderCloudSync(m.status);\n else if (m.type === \"wallet\") setWallet(m.address);\n else if (m.type === \"page\") {\n hideLoading();\n S.hasMore = m.hasMore;\n S.pageCursor = m.cursor;\n maybeFillOlder();\n } else if (m.type === \"older\") {\n prependOlder(m.messages || []);\n S.hasMore = m.hasMore;\n S.pageCursor = m.cursor;\n S.loadingOlder = false;\n maybeFillOlder();\n } else if (m.type === \"approval\") renderApproval(m.req);\n else if (m.type === \"approvalDismiss\") dismissApproval(m.id);\n });\n vscode.postMessage({ type: \"ready\" });\n vscode.postMessage({ type: \"wallet\" });\n vscode.postMessage({ type: \"getBalance\" });\n})();\n";
diff --git a/packages/core/src/chat/ui/panel.jsdom.spec.ts b/packages/core/src/chat/ui/panel.jsdom.spec.ts
index 13f7b542..269f6113 100644
--- a/packages/core/src/chat/ui/panel.jsdom.spec.ts
+++ b/packages/core/src/chat/ui/panel.jsdom.spec.ts
@@ -152,6 +152,27 @@ describe("panel.jsdom: streaming", () => {
});
});
+describe("panel.jsdom: feed images", () => {
+ it("uses HTTP(S) URLs for list covers and reader heroes, and omits unsupported images", () => {
+ const p = boot();
+ const image = "https://example.com/cover.png";
+ p.$("#agentsBtn")!.click();
+ p.host({ type: "blogFeed", posts: [
+ { id: NOTE_ID, author: WALLET, timestamp: 1725400000000, title: "Cover", image },
+ { id: "unsupported", author: WALLET, timestamp: 1725400000001, title: "No cover", image: "ipfs://cover" },
+ ] });
+ const rows = p.$$("#feedList .fd-row");
+ expect(rows[0].querySelector(".fd-cover img")!.getAttribute("src")).toBe(image);
+ expect(rows[1].querySelector(".fd-cover")).toBeNull();
+ rows[0].click();
+ expect(p.$("#agFeedPost .fdp-hero img")!.getAttribute("src")).toBe(image);
+ p.$("#agFeedPost .fdp-bar .bk")!.click();
+ p.$$("#feedList .fd-row")[1].click();
+ expect(p.$("#agFeedPost .fdp-hero")).toBeNull();
+ expect(p.errors).toEqual([]);
+ });
+});
+
describe("panel.jsdom: feed quote cards", () => {
const QUOTED = noteId(OTHER_WALLET, 1725400000001, "q9z2ab");
const DEAD = noteId(OTHER_WALLET, 1725400000002, "dead01");
diff --git a/packages/core/src/chat/ui/panel/format.spec.ts b/packages/core/src/chat/ui/panel/format.spec.ts
index 84ec4926..7abcc181 100644
--- a/packages/core/src/chat/ui/panel/format.spec.ts
+++ b/packages/core/src/chat/ui/panel/format.spec.ts
@@ -96,22 +96,15 @@ describe("panel/format: explorerTxUrl follows S.rpcNetwork", () => {
});
});
-describe("panel/format: feedImgUrl, pinned as it shipped", () => {
- // The intended /^https?:\/\//i lost its backslashes inside the old template literal, so the
- // shipped line is `return v && /^https?:/` with the rest of the line a comment. This pins what
- // the panel does today, not what the comment above the function intends; changing which covers
- // render is a follow-up, not part of the port.
- it("passes falsy input through unchanged", () => {
- expect(feedImgUrl("")).toBe("");
- expect(feedImgUrl(null)).toBe(null);
- expect(feedImgUrl(undefined)).toBe(undefined);
- });
- it("returns the bare /^https?:/ RegExp for any truthy input, http or not", () => {
- for (const v of ["https://x/a.png", "http://x", "ipfs://x", W]) {
- const out = feedImgUrl(v);
- expect(out).toBeInstanceOf(RegExp);
- expect(out.source).toBe("^https?:");
- expect(out.flags).toBe("");
+describe("panel/format: feedImgUrl", () => {
+ it("returns the original HTTP(S) image URL", () => {
+ for (const url of ["https://example.com/cover.png", "http://example.com/a.jpg?size=2", "HTTPS://example.com/cover.png"]) {
+ expect(feedImgUrl(url)).toBe(url);
+ }
+ });
+ it("omits missing images and values the panel cannot resolve", () => {
+ for (const value of ["", null, undefined, "ipfs://x", "git://example.com/x", "data:image/png;base64,AA==", "javascript:alert(1)", "//example.com/x", "/cover.png", "https:/example.com/x", W]) {
+ expect(feedImgUrl(value)).toBeNull();
}
});
});
diff --git a/packages/core/src/chat/ui/panel/format.ts b/packages/core/src/chat/ui/panel/format.ts
index 6e84f2d7..7f05c044 100644
--- a/packages/core/src/chat/ui/panel/format.ts
+++ b/packages/core/src/chat/ui/panel/format.ts
@@ -44,12 +44,8 @@ export function fdAgo(ms) {
}
// http(s) images only: the panel has no gateway media resolver, so on-chain
// address / tx-id images (rare) fall back to no cover rather than a broken img.
-// Shipped as written: inside the old template literal the intended /^https?:\/\//i lost its
-// backslashes (a template's `\/` is just `/`), so this line is the regex /^https?:/ followed
-// by a line comment, and every truthy input returns that RegExp. Kept verbatim under the
-// port's no-behavior-change rule; the `any` only names that shape (the fix is a follow-up).
-export function feedImgUrl(v): any {
- return v && /^https?:///i.test(v) ? v : null;
+export function feedImgUrl(v: string | null | undefined): string | null {
+ return v && /^https?:\/\//i.test(v) ? v : null;
}
export function agShort(w) { return w.slice(0, 6) + '...' + w.slice(-4); }
export function pad2(n) { return n < 10 ? '0' + n : String(n); }