micro-models-agent 2.23.0 → 2.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,107 @@ All notable changes to Micro Models Agent (MMA) will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [2.28.0] - 2026-10-07
8
+
9
+ Tool-produced images (browser screenshots, `attach_image`) are now visible in the
10
+ `mma web` feed — a thumbnail with a full-screen lightbox — and survive reload via
11
+ per-session persistence with rotation. The old static placeholder card that never
12
+ filled is gone.
13
+
14
+ ### Added
15
+
16
+ - **Tool images in the web feed** (`src/modules/webui/assets/js/lightbox.ts`, `feed/image-card.ts`, `feed/turn.ts`, `feed/history.ts`): `browser` screenshots and `attach_image` render as lazy thumbnails; click opens a lightbox (Esc/backdrop close, "open in a new tab", "copy link"). Shown in every verbosity, including quiet.
17
+ - **Persistence + replay** (`src/modules/session/image-store.ts`, `src/core/session-logger.ts`): full-size assets are written to `<sessionDir>/images/` and served via `GET /api/sessions/:id/images/:name`; history replay renders the same thumbnails. When a file was evicted by rotation, the card shows a muted "image removed (rotation)".
18
+ - **Rotation** (`images.maxCount` default 30, `images.maxBytes` default 100 MB in `src/config/registry-extra.ts`): oldest images are evicted first; both keys in `/config` and Web UI settings (UI → Tool images). Labels `config.key.images.*` (en+ru).
19
+ - **Delivery contract** (`src/tools/types.ts`, `src/core/types.ts`): `ToolResult.imageFiles`/`imageRefs`, `ToolEvent.imageRefs`/`images`, and `tool_result.images` in `session.jsonl`.
20
+
21
+ ### Changed
22
+
23
+ - **`attach_image`** (`src/tools/attach-image.ts`): returns the image via `result.images` (unified path with the browser tool) instead of queueing it directly on the context manager.
24
+ - **SSE `tool_end`** (`src/modules/webui/session/forward.ts`): carries an `images` URL list; for an encrypted session (no disk write) it falls back to inline data URLs for live view.
25
+ - **Browser screenshots for text-only models** (`src/tools/browser.ts`): the screenshot is always produced and delivered to the web feed; a non-vision model still receives the text note.
26
+
27
+ ### Removed
28
+
29
+ - **Static screenshot placeholder** (`src/modules/webui/assets/js/feed/turn.ts`): the misleading "screenshot" card that was created on every `browser`/`attach_image` call and never filled.
30
+
31
+ ## [2.27.0] - 2026-10-07
32
+
33
+ MoE is now usable out of the box: `moe.enabled: true` starts on the active
34
+ model instead of silently falling back when no planner model is set, the
35
+ planner keys are configurable from `/config` and Web UI, and model/provider
36
+ references render as dropdowns instead of free-text/JSON fields.
37
+
38
+ ### Added
39
+
40
+ - **Orchestrator config keys** (`src/config/registry-extra.ts`): `orchestrator.model`, `orchestrator.provider`, `orchestrator.contextWindow` (domain `provider`). Previously reachable only by hand-editing `config.json`; now in `/config`, `mma config set` and Web UI settings (Agent → Mixture of experts).
41
+ - **Dynamic `select` editor** (`src/config/registry.ts`, `src/modules/webui/assets/js/settings/{controls,editors}.ts`): `ConfigEditorSpec.kind: "select"` with `optionsSource: "models" | "providers"` renders a dropdown fed by the live model/provider lists, with text-input fallback when the list is unavailable. Applied to `critic.model`, `orchestrator.model` and the `experts[*].model` table column.
42
+ - **`resolveProviderRef`** (`src/modules/providers/resolve-ref.ts`): resolves a provider name (`provider.entries[]` label/type) to its entry.
43
+ - **Per-expert models** (`src/modules/execution/expert-provider.ts`): `experts[*].model` now takes effect — an expert whose model differs from the active one runs on a dedicated provider built on the active provider (`buildManagedProvider`); a matching/empty model reuses the shared provider, a build error fails open. Applies both to MoE subtasks and to the `subagent` tool called directly with `expert_tag`.
44
+
45
+ ### Changed
46
+
47
+ - **Shared managed-provider builder** (`src/modules/providers/managed.ts`): the ProviderManager-based builder (per-entry contextWindow/retry/rateLimits + failover) is extracted and reused by the MoE orchestrator, the critic and per-expert providers instead of being duplicated in three places.
48
+
49
+ - **MoE default-model fallback** (`src/llm/orchestrator.ts`, `src/core/agent-moe.ts`): `OrchestratorClient` reuses the active agent provider when `orchestrator.model` is unset, and builds a dedicated provider on the active provider when `orchestrator.model` differs — so enabling MoE no longer requires hand-configuring a planner model.
50
+ - **`critic.provider` / `orchestrator.provider`** (`src/config/types.ts`, `src/modules/critic/provider.ts`): accept a provider name string (reference) as well as a full `ProviderConfig`; the Web UI shows a provider dropdown.
51
+ - **`ConfigKeyDef.allowEmpty`** (`src/config/registry.ts`): optional string keys accept an empty value (clears to unset), so a dropdown can restore "use active".
52
+ - **Cert MoE scenarios** (`src/modules/certification/scenarios.ts`): dropped the `orchestrator: { model: "" }` override that disabled MoE and let the file-level checks pass without ever exercising the MoE path.
53
+
54
+ ## [2.26.0] - 2026-10-07
55
+
56
+ Web UI polish: the sidebar now has a fixed width with the detail panel on the
57
+ left, and message attachments can be up to 50 MB each (up to 10 per message).
58
+
59
+ ### Added
60
+
61
+ - **Up to 10 attachments of 50 MB each** (`src/modules/webui`): `MAX_IMAGE_BYTES` 6 MB → 50 MB (client per file), `MAX_IMAGE_DATA_URL_BYTES` 6 MB → 70 MB (server, base64 of a 50 MB file + header), `MAX_IMAGES` 3 → 10 (client, server and the `web-ui` example plugin), `MAX_BODY_BYTES` 24 MB → 768 MB so ten base64 images fit in a single request. i18n `web.attach.too_large` updated.
62
+
63
+ ### Changed
64
+
65
+ - **Fixed-width sidebar, detail on the left** (`src/modules/webui/assets`): `.sb` is a constant 679 px and the auto-expanding `.sb-expanded` width transition is gone, so the sidebar no longer resizes itself on external events. The Trace detail opens to the left of the list (`flex-direction: row-reverse`); the Files viewer takes the full sidebar width with wrapped lines. The now-unused `sb-expanded` class was removed from CSS/JS.
66
+
67
+ ## [2.25.0] - 2026-10-07
68
+
69
+ Multiple subagent calls emitted in one assistant turn now actually run in
70
+ parallel, instead of strictly one after another. The batch executor gains a
71
+ generic "parallelizable tool" capability; every hook, UI event and `role:"tool"`
72
+ message stays serial and ordered.
73
+
74
+ ### Added
75
+
76
+ - **Parallelizable tool capability** (`src/tools/types.ts`): `ToolDefinition.parallelizable` opts a tool into concurrent batch execution. `subagent` sets it.
77
+ - **`ToolExecutor.fork()`** (`src/tools/executor.ts`): isolated executor for one concurrent call — clones the `ToolContext` by descriptors (live getters preserved), gives it its own `scope`/`recursionDepth`/`cleanup`/`activeCallId`, and shares the memo/registry/plugin manager so a subagent's mutation still invalidates the parent's cached reads.
78
+ - **Parallel run in `ToolBatchExecutor`** (`src/core/agent/tool-batch.ts`): consecutive `parallelizable` calls (≥2) are prepared serially, executed concurrently — each on `toolExecutor.fork()`, capped by `security.rateLimits.maxParallelTasks` (default 5) — then finalized serially in original call order. The per-call lifecycle is split into `prepareCall`/`executeCall`/`finalizeCall` so the serial and parallel paths share all bookkeeping.
79
+ - **Plugin-hook serialization** (`src/tools/executor.ts`): `onBeforeTool`/`onAfterTool` dispatch runs under a shared FIFO lock (one per root executor, inherited by forks), so stateful plugins (execution stuck-detector, lint-on-write, LSP) never run concurrently across sibling forks; the tool handler itself stays concurrent.
80
+
81
+ ### Changed
82
+
83
+ - **`subagent` runs in parallel** (`src/tools/subagent.ts`): sibling subagents no longer clobber each other's workspace scope or recursion depth (each runs on its own fork).
84
+ - **MoE subtasks get isolated executors** (`src/modules/execution/moe-executor.ts`): each subtask runs against its own `fork()`, removing the scope/recursion-depth race between concurrent wave tasks.
85
+ - **Live session footgun closed**: the tool description's "use for parallel work" is now true; the harness no longer implies concurrency it does not provide.
86
+
87
+ ## [2.24.0] - 2026-10-07
88
+
89
+ The `mma web` sidebar is now event-sourced: the SSE `state` event carries the full
90
+ session snapshot, each tab owns its snapshot, and the client-side poll timers are
91
+ gone.
92
+
93
+ ### Added
94
+
95
+ - **Full snapshot in the `state` event** (`src/modules/webui/session/session-status.ts`): the new `pushState(s, sessionId?)` helper broadcasts the `HostStateSnapshot` (built from the existing `Host.getState`) with the owning `sessionId`; every state change (turn start/end, queue, plan, reconfigure, re-index) goes through it.
96
+ - **`sessions` SSE event** (`src/modules/webui/session/sessions-api.ts`): create/activate/delete broadcast the sessions list; the client renders it directly.
97
+ - **Per-tab snapshot store** (`src/modules/webui/assets/js/state-sync.ts`): pure routing of `state`/`usage` payloads into `tab.state` (active-only render, `sessionId` guard, usage merge); the switched-to tab renders instantly from memory.
98
+
99
+ ### Changed
100
+
101
+ - **Client no longer polls** (`src/modules/webui/assets/app.ts`): the `state` event applies the snapshot via `applyState`, `ensureTabState` renders a tab from cache or one REST fetch, and the `2s /api/state` + `15s /api/sessions` intervals are removed; reconnect re-fetches every open session tab.
102
+ - **Accent toggles** (`src/modules/webui/assets/styles.css`): on/off switches now fill with the theme accent color instead of a muted gray, in both dark and light themes.
103
+
104
+ ### Fixed
105
+
106
+ - **Auto-advance blocked by timestamp skew** (`src/modules/execution/module.ts`): `provablyStale` compares a step file's mtime against the plan's `createdAt`; the filesystem can record a file written right after plan creation a fraction of a millisecond *earlier* than `createdAt`, which falsely marked the file "pre-existing" and silently suppressed step auto-advance. A small tolerance now absorbs the skew (real stale files stay stale).
107
+
7
108
  ## [2.23.0] - 2026-10-07
8
109
 
9
110
  A new **Files** sidebar tab in `mma web`: browse the workspace as a lazy tree,
@@ -1 +1 @@
1
- import{msgs as W,inp as m,connStatus as x,setIcon as T,icon as C}from"./js/dom.js";import{api as j,token as k}from"./js/api.js";import{I18N as D,t as a,loadI18n as z}from"./js/i18n.js";import{tabs as u,session as o,settings as f,hooks as h}from"./js/store.js";import{onText as J,onReasoning as Q,onMeta as X,onPhase as G,onToolStart as K,onToolEnd as V,onTurnEnd as Y,onErrorEvent as Z,onCommandOutput as $,onLog as ee,onPlan as te,onQuestion as ne,onRetract as se,showEmptyFeed as S,renderPlan as oe,renderUsage as w,renderEnvironment as ie,renderWorkspace as ae,feedHasContent as re,loadHistory as de}from"./js/feed.js";import{openTab as A,updateTabName as ce,renderSessionsDropdown as R,reflectBusy as q,loadSessions as _,loadCommands as le,setTabStatus as p,reconcileTabStatuses as P}from"./js/session/tabs.js";import{renderSelectors as I,renderSettings as ue,loadConfig as fe,loadModels as be,loadProviders as pe,loadMemory as ge,loadSkills as me,loadPlugins as he,applyTheme as Se,currentTheme as we,applyFontSize as Ie,setConnStatus as v,setStaleBar as B}from"./js/settings.js";import{initRail as ve,setPlan as N,railEvent as ye,applyRailI18n as Ee}from"./js/rail.js";import{initSidebar as Le,sidebarSessionChanged as xe,sidebarMapState as Te,sidebarTraceEvent as y,sidebarTurnEnd as Ce,sidebarBusy as M,sidebarMapIndicator as ke,sidebarPullLog as Ae,sidebarSessionId as Re,setSessionLoading as qe,sidebarLspState as _e,sidebarLspEvent as Pe,applySidebarI18n as H,initFiles as Be,sidebarFilesState as Ne,sidebarFilesChanged as O}from"./js/sidebar.js";import{insertIntoDraft as Me}from"./js/session/composer.js";document.querySelectorAll("[data-collapse]").forEach(n=>{const s=n.querySelector(".arrow");s&&(s.textContent="",s.appendChild(C("chevron-down"))),n.addEventListener("click",()=>{const d=n.nextElementSibling;if(!d)return;const i=d.classList.toggle("collapsed");n.setAttribute("aria-expanded",i?"false":"true");const r=n.querySelector(".arrow");r&&(r.textContent="",r.appendChild(C(i?"chevron-right":"chevron-down")))})});const g=document.getElementById("toggleSidebar");g&&(g.addEventListener("click",()=>{const n=document.body.classList.toggle("sb-open");g.setAttribute("aria-expanded",n?"true":"false")}),document.addEventListener("keydown",n=>{if(n.key!=="Escape"||!document.body.classList.contains("sb-open"))return;const s=document.activeElement?String(document.activeElement.tagName):"";s==="TEXTAREA"||s==="INPUT"||(document.body.classList.remove("sb-open"),g.setAttribute("aria-expanded","false"))}));let E=-1;function F(n,s,d){o.host=n||{},o.busy=!!s,o.runningSessionId=d&&d.runningSessionId||null,o.runningSessionIds=d&&d.runningSessionIds||(o.runningSessionId?[o.runningSessionId]:[]),o.queued=d&&d.queued||{},q(),P();const i=o.host.sessionId||null;o.activeSessionId=i,i&&!u.tabs.some(c=>c.id===i)&&A(i,o.host.sessionName||String(i),"session",!1),i&&ce(i,o.host.sessionName),u.tabs.length||S(),oe(o.host.plan),N(s&&n?n.plan:null),w(o.host),n&&typeof n.contextWindow=="number"&&f.config&&f.config.contextWindow!==n.contextWindow&&(f.config.contextWindow=n.contextWindow,I()),ie(o.host),Te(o.host),ke(o.host),_e(),Ne(o.host);const r=n&&typeof n.filesRevision=="number"?n.filesRevision:void 0;if(r!==void 0&&r!==E){const c=E===-1;E=r,!c&&r>0&&O()}xe(i),ae(o.host),R(),qe(!1),B(!!(n&&n.i18nStale))}async function b(){const n=u.activeTabId,s=n?"/api/state?session="+encodeURIComponent(n):"/api/state",d=await j(s);!d.ok||!d.data||u.activeTabId===n&&F(d.data.state||{},d.data.busy,d.data)}h.applyState=F,h.refreshState=b;function He(){b();const n=u.tabs.find(s=>s.id===u.activeTabId);n&&n.kind==="session"&&!re(n.feed)&&(n.historyLoaded=!1,de(n))}function Oe(){const n="/events"+(k?"?token="+encodeURIComponent(k):""),s=new EventSource(n);let d=!1;s.onopen=()=>{d&&(d=!1,v(!1),He())},s.onerror=e=>{e&&typeof e.data=="string"||(d=!0,v(!0))};const i=e=>{try{return JSON.parse(e.data)}catch{return null}},r=e=>!e.sessionId||e.sessionId===Re(),c=[["text",e=>J(e.chunk||"",e.sessionId)],["meta",e=>X(e.chunk||"",e.sessionId,e.kind)],["reasoning",e=>Q(e.chunk||"",e.sessionId)],["phase",e=>G(e.phase,e.sessionId)],["tool_start",K],["tool_end",V],["plan",te],["question",ne],["turn_end",e=>{Y(e),r(e)&&(Ce(e.turnSeq),M(!1),Ae())}],["log",ee],["retract",e=>se(e.reason||"",e.sessionId)]];for(const[e,t]of c)s.addEventListener(e,l=>{const L=i(l);L&&t(L)});for(const e of["tool_start","tool_end","text","plan"])s.addEventListener(e,t=>{const l=i(t);l&&(e==="plan"?N(l):ye(e,l.name))});for(const e of["text","reasoning","phase","tool_start","plan"])s.addEventListener(e,t=>{const l=i(t);l&&l.sessionId&&p(l.sessionId,"working")});s.addEventListener("question",e=>{const t=i(e);t&&t.sessionId&&p(t.sessionId,"waiting")}),s.addEventListener("turn_end",e=>{const t=i(e);t&&t.sessionId&&p(t.sessionId,t.success===!1?"failed":"idle")});for(const e of["tool_start","tool_end","phase","plan","question","meta","log","retract","index"])s.addEventListener(e,t=>{const l=i(t);l&&r(l)&&y(e,l)});s.addEventListener("text",e=>{const t=i(e);t&&r(t)&&y("text",t)}),s.addEventListener("lsp",e=>{const t=i(e);t&&Pe(t)}),s.addEventListener("files",()=>O()),s.addEventListener("command",e=>{const t=i(e);t&&typeof t.text=="string"&&$(t.text,t.sessionId)}),s.addEventListener("state",e=>{const t=i(e);t&&(o.busy=!!t.busy,o.runningSessionId=t.runningSessionId||null,o.runningSessionIds=t.runningSessionIds||(o.runningSessionId?[o.runningSessionId]:[]),o.queued=t.queued||{},q(),P(),M(!!t.busy),b(),t.usage&&typeof t.usage=="object"&&(t.usageSessionId==null||t.usageSessionId===o.activeSessionId)&&(o.host=Object.assign({},o.host,t.usage),w(o.host)))}),s.addEventListener("usage",e=>{const t=i(e);t&&t.usage&&typeof t.usage=="object"&&(t.usageSessionId==null||t.usageSessionId===o.activeSessionId)&&(o.host=Object.assign({},o.host,t.usage),w(o.host))}),s.addEventListener("error",e=>{if(!e.data)return;const t=i(e);t&&(t.sessionId&&p(t.sessionId,"failed"),Z(t),y("error",t))})}function U(){document.documentElement.lang=D.locale;const n=(r,c)=>{const e=document.getElementById(r);e&&(e.textContent=c)};n("planHeaderLabel",a("web.sidebar.plan")),n("usageHeaderLabel",a("web.sidebar.session")+" · "+a("web.sidebar.usage")),n("envHeaderLabel",a("web.sidebar.env")),n("settingsTitle",a("web.settings.title")),n("sessActiveHeader",a("web.sessions.active")),n("sessRecentHeader",a("web.sessions.recent")),n("hintEnter",a("web.input.enter")),n("hintShift",a("web.input.shift_enter")),n("hintEsc",a("web.input.esc")),n("hintAttach",a("web.input.attach"));const s={newChat:["plus",a("web.topbar.new_chat")],sessBtn:["menu",a("web.topbar.all_sessions")],toggleSidebar:["panel",a("web.topbar.sidebar")],openSettings:["settings",a("web.topbar.settings")],attachBtn:["image",a("web.attach.title")],closeSettings:["x",a("web.topbar.close")],sessNew:["plus",a("web.sessions.new")],sessRefresh:["refresh",a("web.sessions.refresh")],sessPrev:["chevron-left",a("web.sessbar.prev")],sessNext:["chevron-right",a("web.sessbar.next")],sessClose:["x",a("web.topbar.close")]};Object.keys(s).forEach(r=>{const c=document.getElementById(r);if(!c)return;const[e,t]=s[r];T(c,e,r==="closeSettings"||r==="sessNew"||r==="sessRefresh"?t:null),c.title=t,c.setAttribute("aria-label",t)});const d=document.getElementById("settingsIcon");d&&T(d,"settings",null),m&&(m.placeholder=a("web.input.placeholder"),m.setAttribute("aria-label",a("web.input.placeholder"))),document.querySelectorAll("#setMenu .mi").forEach(r=>{const c=r.dataset.p;c&&(r.textContent=a("web.settings."+c))});const i=document.getElementById("setSearch");i&&(i.setAttribute("placeholder",a("web.settings.search_placeholder")),i.setAttribute("aria-label",a("web.settings.search_placeholder"))),document.querySelectorAll(".content-col .panel").forEach(r=>{const c=r.querySelector("h3"),e=r.id.replace(/^p-/,"");c&&e&&(c.textContent=a("web.settings."+e))}),I(),R(),Ee(),H(),W.querySelector(".empty")&&S(),x&&!x.hidden&&v(!0),B(void 0,!0)}async function Fe(){const n=new URLSearchParams(location.search).get("session");if(window.MmaSetup)try{if(await window.MmaSetup.maybeOpen())return}catch{}await z(),await le(),Se(we()),Ie(),U(),S(),I(),ue(),await fe(),ve(f.config&&f.config.ui?f.config.ui.activityRail:void 0),Le(),H(),Be({insertPath:s=>Me("`"+s+"` ")}),await be(),await pe(),await _(),await ge(),await me(),await he();try{await b()}catch(s){console.error("initial refresh failed",s)}if(n){const s=o.sessions.find(d=>d.id===n);s&&A(n,s.name||String(n),"session",!0)}Oe(),setInterval(()=>{b().catch(s=>console.error("refresh failed",s))},2e3),setInterval(()=>{_()},15e3)}h.applyStaticI18n=U,Fe();
1
+ import{msgs as D,inp as g,connStatus as L,setIcon as x,icon as T}from"./js/dom.js";import{api as z,token as A}from"./js/api.js";import{I18N as J,t as i,loadI18n as Q}from"./js/i18n.js";import{tabs as l,session as f,settings as u,hooks as C}from"./js/store.js";import{routeState as X,mergeUsage as G,storeSnapshot as k,applyCounters as R}from"./js/state-sync.js";import{onText as K,onReasoning as V,onMeta as Y,onPhase as Z,onToolStart as $,onToolEnd as ee,onTurnEnd as te,onErrorEvent as se,onCommandOutput as ne,onLog as oe,onPlan as ae,onQuestion as ie,onRetract as re,showEmptyFeed as w,renderPlan as de,renderUsage as _,renderEnvironment as ce,renderWorkspace as le,feedHasContent as fe,loadHistory as ue}from"./js/feed.js";import{openTab as P,updateTabName as pe,renderSessionsDropdown as h,reflectBusy as be,loadSessions as q,loadCommands as me,setTabStatus as p,reconcileTabStatuses as ge}from"./js/session/tabs.js";import{renderSelectors as S,renderSettings as we,loadConfig as he,loadModels as Se,loadProviders as ve,loadMemory as ye,loadSkills as Ie,loadPlugins as Ee,applyTheme as Le,currentTheme as xe,applyFontSize as Te,setConnStatus as v,setStaleBar as B}from"./js/settings.js";import{initRail as Ae,setPlan as N,railEvent as Ce,applyRailI18n as ke}from"./js/rail.js";import{initSidebar as Re,sidebarSessionChanged as _e,sidebarMapState as Pe,sidebarTraceEvent as y,sidebarTurnEnd as qe,sidebarBusy as M,sidebarMapIndicator as Be,sidebarPullLog as Ne,sidebarSessionId as Me,setSessionLoading as He,sidebarLspState as Ue,sidebarLspEvent as Fe,applySidebarI18n as H,initFiles as We,sidebarFilesState as Oe,sidebarFilesChanged as U}from"./js/sidebar.js";import{insertIntoDraft as je}from"./js/session/composer.js";document.querySelectorAll("[data-collapse]").forEach(s=>{const e=s.querySelector(".arrow");e&&(e.textContent="",e.appendChild(T("chevron-down"))),s.addEventListener("click",()=>{const o=s.nextElementSibling;if(!o)return;const a=o.classList.toggle("collapsed");s.setAttribute("aria-expanded",a?"false":"true");const r=s.querySelector(".arrow");r&&(r.textContent="",r.appendChild(T(a?"chevron-right":"chevron-down")))})});const b=document.getElementById("toggleSidebar");b&&(b.addEventListener("click",()=>{const s=document.body.classList.toggle("sb-open");b.setAttribute("aria-expanded",s?"true":"false")}),document.addEventListener("keydown",s=>{if(s.key!=="Escape"||!document.body.classList.contains("sb-open"))return;const e=document.activeElement?String(document.activeElement.tagName):"";e==="TEXTAREA"||e==="INPUT"||(document.body.classList.remove("sb-open"),b.setAttribute("aria-expanded","false"))}));let I=-1;function m(){const s=l.tabs.find(r=>r.id===l.activeTabId)||null,e=s&&s.state||{};f.host=e;const o=e.sessionId||s&&s.id||null;f.activeSessionId=o,l.tabs.length||w(),de(e.plan),N(f.busy&&e?e.plan:null),_(e),typeof e.contextWindow=="number"&&u.config&&u.config.contextWindow!==e.contextWindow&&(u.config.contextWindow=e.contextWindow,S()),ce(e),Pe(e),Be(e),Ue(),Oe(e);const a=typeof e.filesRevision=="number"?e.filesRevision:void 0;if(a!==void 0&&a!==I){const r=I===-1;I=a,!r&&a>0&&U()}_e(o),le(e),h(),He(!1),B(!!e.i18nStale)}function De(s){const e=X(s);be(),ge(),s.state&&typeof s.state.sessionId=="string"&&pe(s.state.sessionId,s.state.sessionName),e&&m()}async function F(s){const e=s?"/api/state?session="+encodeURIComponent(s):"/api/state",o=await z(e);return o.ok&&o.data?o.data:null}async function W(s){const e=l.tabs.find(a=>a.id===s);if(!e)return;if(e.state){l.activeTabId===s&&m();return}const o=await F(s);o&&(k(o.state),l.activeTabId===s&&(R(o),m()))}async function O(){const s=l.activeTabId,e=await F(s);if(!e||l.activeTabId!==s)return;const o=e.state;!s&&o&&o.sessionId&&!l.tabs.some(a=>a.id===o.sessionId)&&P(o.sessionId,o.sessionName||String(o.sessionId),"session",!1),k(o),R(e),m()}C.ensureTabState=W;function ze(){for(const e of l.tabs)e.kind==="session"&&(e.state=null,W(e.id));O(),q();const s=l.tabs.find(e=>e.id===l.activeTabId);s&&s.kind==="session"&&!fe(s.feed)&&(s.historyLoaded=!1,ue(s))}function Je(){const s="/events"+(A?"?token="+encodeURIComponent(A):""),e=new EventSource(s);let o=!1;e.onopen=()=>{o&&(o=!1,v(!1),ze())},e.onerror=t=>{t&&typeof t.data=="string"||(o=!0,v(!0))};const a=t=>{try{return JSON.parse(t.data)}catch{return null}},r=t=>!t.sessionId||t.sessionId===Me(),c=[["text",t=>K(t.chunk||"",t.sessionId)],["meta",t=>Y(t.chunk||"",t.sessionId,t.kind)],["reasoning",t=>V(t.chunk||"",t.sessionId)],["phase",t=>Z(t.phase,t.sessionId)],["tool_start",$],["tool_end",ee],["plan",ae],["question",ie],["turn_end",t=>{te(t),r(t)&&(qe(t.turnSeq),M(!1),Ne())}],["log",oe],["retract",t=>re(t.reason||"",t.sessionId)]];for(const[t,n]of c)e.addEventListener(t,d=>{const E=a(d);E&&n(E)});for(const t of["tool_start","tool_end","text","plan"])e.addEventListener(t,n=>{const d=a(n);d&&(t==="plan"?N(d):Ce(t,d.name))});for(const t of["text","reasoning","phase","tool_start","plan"])e.addEventListener(t,n=>{const d=a(n);d&&d.sessionId&&p(d.sessionId,"working")});e.addEventListener("question",t=>{const n=a(t);n&&n.sessionId&&p(n.sessionId,"waiting")}),e.addEventListener("turn_end",t=>{const n=a(t);n&&n.sessionId&&p(n.sessionId,n.success===!1?"failed":"idle")});for(const t of["tool_start","tool_end","phase","plan","question","meta","log","retract","index"])e.addEventListener(t,n=>{const d=a(n);d&&r(d)&&y(t,d)});e.addEventListener("text",t=>{const n=a(t);n&&r(n)&&y("text",n)}),e.addEventListener("lsp",t=>{const n=a(t);n&&Fe(n)}),e.addEventListener("files",()=>U()),e.addEventListener("command",t=>{const n=a(t);n&&typeof n.text=="string"&&ne(n.text,n.sessionId)}),e.addEventListener("state",t=>{const n=a(t);n&&(M(!!n.busy),De(n))}),e.addEventListener("sessions",t=>{const n=a(t);n&&Array.isArray(n.sessions)&&(f.sessions=n.sessions,h())}),e.addEventListener("usage",t=>{const n=a(t);if(!n||!n.usage||typeof n.usage!="object")return;const d=n.usageSessionId||n.sessionId||null;G(d,n.usage)&&_(f.host)}),e.addEventListener("error",t=>{if(!t.data)return;const n=a(t);n&&(n.sessionId&&p(n.sessionId,"failed"),se(n),y("error",n))})}function j(){document.documentElement.lang=J.locale;const s=(r,c)=>{const t=document.getElementById(r);t&&(t.textContent=c)};s("planHeaderLabel",i("web.sidebar.plan")),s("usageHeaderLabel",i("web.sidebar.session")+" · "+i("web.sidebar.usage")),s("envHeaderLabel",i("web.sidebar.env")),s("settingsTitle",i("web.settings.title")),s("sessActiveHeader",i("web.sessions.active")),s("sessRecentHeader",i("web.sessions.recent")),s("hintEnter",i("web.input.enter")),s("hintShift",i("web.input.shift_enter")),s("hintEsc",i("web.input.esc")),s("hintAttach",i("web.input.attach"));const e={newChat:["plus",i("web.topbar.new_chat")],sessBtn:["menu",i("web.topbar.all_sessions")],toggleSidebar:["panel",i("web.topbar.sidebar")],openSettings:["settings",i("web.topbar.settings")],attachBtn:["image",i("web.attach.title")],closeSettings:["x",i("web.topbar.close")],sessNew:["plus",i("web.sessions.new")],sessRefresh:["refresh",i("web.sessions.refresh")],sessPrev:["chevron-left",i("web.sessbar.prev")],sessNext:["chevron-right",i("web.sessbar.next")],sessClose:["x",i("web.topbar.close")]};Object.keys(e).forEach(r=>{const c=document.getElementById(r);if(!c)return;const[t,n]=e[r];x(c,t,r==="closeSettings"||r==="sessNew"||r==="sessRefresh"?n:null),c.title=n,c.setAttribute("aria-label",n)});const o=document.getElementById("settingsIcon");o&&x(o,"settings",null),g&&(g.placeholder=i("web.input.placeholder"),g.setAttribute("aria-label",i("web.input.placeholder"))),document.querySelectorAll("#setMenu .mi").forEach(r=>{const c=r.dataset.p;c&&(r.textContent=i("web.settings."+c))});const a=document.getElementById("setSearch");a&&(a.setAttribute("placeholder",i("web.settings.search_placeholder")),a.setAttribute("aria-label",i("web.settings.search_placeholder"))),document.querySelectorAll(".content-col .panel").forEach(r=>{const c=r.querySelector("h3"),t=r.id.replace(/^p-/,"");c&&t&&(c.textContent=i("web.settings."+t))}),S(),h(),ke(),H(),D.querySelector(".empty")&&w(),L&&!L.hidden&&v(!0),B(void 0,!0)}async function Qe(){const s=new URLSearchParams(location.search).get("session");if(window.MmaSetup)try{if(await window.MmaSetup.maybeOpen())return}catch{}await Q(),await me(),Le(xe()),Te(),j(),w(),S(),we(),await he(),Ae(u.config&&u.config.ui?u.config.ui.activityRail:void 0),Re(),H(),We({insertPath:e=>je("`"+e+"` ")}),await Se(),await ve(),await q(),await ye(),await Ie(),await Ee();try{await O()}catch(e){console.error("initial refresh failed",e)}if(s){const e=f.sessions.find(o=>o.id===s);e&&P(s,e.name||String(s),"session",!0)}Je()}C.applyStaticI18n=j,Qe();
@@ -1 +1 @@
1
- import{feed as s}from"./feed.js";import{tabs as c}from"./tabs.js";import{settings as f}from"./settings.js";import{session as i}from"./session.js";export{s as feed,c as tabs,f as settings,i as session};export const THEME_KEY="mma-theme",MAX_IMAGES=3,MAX_IMAGE_BYTES=6*1024*1024,hooks={applyStaticI18n:null};export function setPath(r,n,p){const t=n.split(".");let o=r;for(let e=0;e<t.length-1;e++)(o[t[e]]==null||typeof o[t[e]]!="object")&&(o[t[e]]={}),o=o[t[e]];o[t[t.length-1]]=p}export function getPath(r,n){const p=n.split(".");let t=r;for(const o of p){if(t==null||typeof t!="object")return;t=t[o]}return t}
1
+ import{feed as s}from"./feed.js";import{tabs as c}from"./tabs.js";import{settings as f}from"./settings.js";import{session as i}from"./session.js";export{s as feed,c as tabs,f as settings,i as session};export const THEME_KEY="mma-theme",MAX_IMAGES=10,MAX_IMAGE_BYTES=50*1024*1024,hooks={applyStaticI18n:null};export function setPath(r,n,p){const t=n.split(".");let o=r;for(let e=0;e<t.length-1;e++)(o[t[e]]==null||typeof o[t[e]]!="object")&&(o[t[e]]={}),o=o[t[e]];o[t[t.length-1]]=p}export function getPath(r,n){const p=n.split(".");let t=r;for(const o of p){if(t==null||typeof t!="object")return;t=t[o]}return t}
@@ -1 +1 @@
1
- import{msgs as s,el as i,setIcon as C}from"../dom.js";import{t as l,pluralCount as w}from"../i18n.js";import{feed as o,tabs as a}from"../store.js";import{formatMs as x,verbosity as T}from"../format.js";import{copyText as v}from"../../md.js";export function mdOpts(t){return{copyLabel:l("web.code.copy"),copiedLabel:l("web.code.copied"),copyFailedLabel:l("web.code.copy_failed"),animate:t===!0}}export function buildThinkBlock(t,e){const n=i("div","think"+(e===!1?"":" live")),r=i("button","think-h");r.type="button",r.setAttribute("aria-expanded","false"),r.appendChild(i("span","g","✦")),r.appendChild(i("span","lab",l("web.action.thinking"))),t&&r.appendChild(i("span","dur",""));const d=i("span","tog");C(d,"chevron-right",l("web.action.show")),r.appendChild(d);const p=i("div","think-body hidden");return n.appendChild(r),n.appendChild(p),{wrap:n,head:r,body:p}}export function copyAction(t,e){const n=i("button","act-btn",l(e));return n.type="button",n.addEventListener("click",()=>{v(t).then(r=>{n.textContent=r?"✓ "+l("web.code.copied"):"⚠ "+l("web.code.copy_failed"),window.setTimeout(()=>{n.textContent=l(e)},1400)})}),n}export function appendActionsRow(t,e,n){const r=String(e??""),d=String(n??"");if(!t||!r.trim()&&!d.trim())return;const p=i("div","msg-actions");r.trim()&&p.appendChild(copyAction(r,"web.msg.copy")),d.trim()&&p.appendChild(copyAction(d,"web.msg.copy_md")),appendTo(t,p)}export function insertAfter(t,e){t&&t.parentNode?t.parentNode.insertBefore(e,t.nextSibling):appendTo(liveFeed(),e)}function b(){const t=i("div","empty");return t.appendChild(i("span","g","✦")),t.appendChild(document.createTextNode(" "+l("web.empty.title"))),t.appendChild(document.createElement("br")),t.appendChild(i("span","hint",l("web.empty.hint"))),t}export function makeFeed(){const t=i("div","feed");return t.appendChild(b()),t}export function showEmptyFeed(){s.replaceChildren(b()),u=!0,f(),syncUrl()}export function clearFeed(t){if(!t)return;t.replaceChildren(b()),u=!0,f();const e=a.tabs.find(n=>n.feed===t);e&&(e.planCard=null),t===activeFeed()&&scroll()}export function activeFeed(){const t=a.tabs.find(e=>e.id===a.activeTabId);return t?t.feed:null}export function liveFeed(){const t=a.tabs.find(e=>e.id===a.liveTabId)||a.tabs.find(e=>e.id===a.activeTabId);return t?t.feed:null}export function feedForEvent(t){if(t){const e=a.tabs.find(n=>n.id===t);if(e)return e.feed}return liveFeed()}function h(){return s.scrollHeight-s.scrollTop-s.clientHeight<140}let u=!0;export function scroll(t){t||u||h()?(s.scrollTop=s.scrollHeight,u=!0,f()):m()}export function scrollToBottom(t){if(u=!0,f(),t&&typeof s.scrollTo=="function")try{s.scrollTo({top:s.scrollHeight,behavior:"smooth"});return}catch{}s.scrollTop=s.scrollHeight}export function isFollowing(){return u}export function feedHasContent(t){return!!t.querySelector(".u, .md, .ans, .code, .act, .think, .metaln, .errline, .sysline, .detail")}export function syncUrl(){const t=new URL(location.href),e=a.tabs.find(n=>n.id===a.activeTabId);e&&e.kind==="session"?t.searchParams.set("session",e.id):t.searchParams.delete("session"),history.replaceState(null,"",t)}let c=null;function g(){if(c)return c;const t=document.querySelector(".chat");return t?(c=i("button","to-latest"),c.type="button",c.hidden=!0,c.addEventListener("click",()=>scrollToBottom(!0)),t.appendChild(c),c):null}function m(){const t=g();t&&(t.textContent="↓ "+l("web.scroll.latest"),t.hidden=!1,s.classList.add("unfollowed"))}function f(){c&&(c.hidden=!0),s.classList.remove("unfollowed")}s.addEventListener("scroll",()=>{const t=h();u=t,s.classList.toggle("scrolled",s.scrollTop>24),t?f():liveFeed()&&feedHasContent(liveFeed())&&m()});export function appendTo(t,e){return t&&(t.appendChild(e),t===activeFeed()&&scroll()),e}export function clearEmpty(t){const e=t&&t.querySelector(".empty");e&&e.remove()}export function clearBusy(t){if(o.busyTimer&&(clearInterval(o.busyTimer),o.busyTimer=null),t=t||liveFeed(),!t)return;const e=t.querySelector(".busy");e&&e.remove()}export function showBusy(t){if(t=t||liveFeed(),!t||o.curMd||o.curThink||(clearEmpty(t),t.querySelector(".busy")))return;o.busyTimer&&(clearInterval(o.busyTimer),o.busyTimer=null);const e=i("div","busy");e.appendChild(i("span","spin","✦")),e.appendChild(i("span","lab",l("web.action.thinking")));const n=i("span","busytime","· 0s");e.appendChild(n),e.appendChild(i("span","","· "+l("web.input.esc"))),appendTo(t,e);const r=Date.now();o.busyTimer=setInterval(()=>{if(!e.parentNode){clearInterval(o.busyTimer),o.busyTimer=null;return}n.textContent="· "+Math.round((Date.now()-r)/1e3)+"s"},1e3)}export function flushRun(){if(o.runCount<=0){y();return}if(T()==="quiet"&&o.runReconOnly&&o.runFails===0&&o.runCount>1){const t=o.runNodes.slice();t.forEach(r=>r.classList.add("fold-out")),window.setTimeout(()=>t.forEach(r=>r.remove()),160);const e=i("div","grouped");e.appendChild(i("span","pipe","│"));const n=x(o.runDuration);e.appendChild(document.createTextNode(" "+w("web.action.actions",o.runCount)+" ✓"+(n?" · "+n:""))),appendTo(liveFeed(),e)}y()}function y(){o.runCount=0,o.runFails=0,o.runReconOnly=!0,o.runNodes=[],o.runDuration=0}
1
+ import{msgs as s,el as i,setIcon as C}from"../dom.js";import{t as l,pluralCount as w}from"../i18n.js";import{feed as o,tabs as a}from"../store.js";import{formatMs as x,verbosity as T}from"../format.js";import{copyText as v}from"../../md.js";export function mdOpts(t){return{copyLabel:l("web.code.copy"),copiedLabel:l("web.code.copied"),copyFailedLabel:l("web.code.copy_failed"),animate:t===!0}}export function buildThinkBlock(t,e){const n=i("div","think"+(e===!1?"":" live")),r=i("button","think-h");r.type="button",r.setAttribute("aria-expanded","false"),r.appendChild(i("span","g","✦")),r.appendChild(i("span","lab",l("web.action.thinking"))),t&&r.appendChild(i("span","dur",""));const d=i("span","tog");C(d,"chevron-right",l("web.action.show")),r.appendChild(d);const p=i("div","think-body hidden");return n.appendChild(r),n.appendChild(p),{wrap:n,head:r,body:p}}export function copyAction(t,e){const n=i("button","act-btn",l(e));return n.type="button",n.addEventListener("click",()=>{v(t).then(r=>{n.textContent=r?"✓ "+l("web.code.copied"):"⚠ "+l("web.code.copy_failed"),window.setTimeout(()=>{n.textContent=l(e)},1400)})}),n}export function appendActionsRow(t,e,n){const r=String(e??""),d=String(n??"");if(!t||!r.trim()&&!d.trim())return;const p=i("div","msg-actions");r.trim()&&p.appendChild(copyAction(r,"web.msg.copy")),d.trim()&&p.appendChild(copyAction(d,"web.msg.copy_md")),appendTo(t,p)}export function insertAfter(t,e){t&&t.parentNode?t.parentNode.insertBefore(e,t.nextSibling):appendTo(liveFeed(),e)}function b(){const t=i("div","empty");return t.appendChild(i("span","g","✦")),t.appendChild(document.createTextNode(" "+l("web.empty.title"))),t.appendChild(document.createElement("br")),t.appendChild(i("span","hint",l("web.empty.hint"))),t}export function makeFeed(){const t=i("div","feed");return t.appendChild(b()),t}export function showEmptyFeed(){s.replaceChildren(b()),u=!0,f(),syncUrl()}export function clearFeed(t){if(!t)return;t.replaceChildren(b()),u=!0,f();const e=a.tabs.find(n=>n.feed===t);e&&(e.planCard=null),t===activeFeed()&&scroll()}export function activeFeed(){const t=a.tabs.find(e=>e.id===a.activeTabId);return t?t.feed:null}export function liveFeed(){const t=a.tabs.find(e=>e.id===a.liveTabId)||a.tabs.find(e=>e.id===a.activeTabId);return t?t.feed:null}export function feedForEvent(t){if(t){const e=a.tabs.find(n=>n.id===t);if(e)return e.feed}return liveFeed()}function h(){return s.scrollHeight-s.scrollTop-s.clientHeight<140}let u=!0;export function scroll(t){t||u||h()?(s.scrollTop=s.scrollHeight,u=!0,f()):m()}export function scrollToBottom(t){if(u=!0,f(),t&&typeof s.scrollTo=="function")try{s.scrollTo({top:s.scrollHeight,behavior:"smooth"});return}catch{}s.scrollTop=s.scrollHeight}export function isFollowing(){return u}export function feedHasContent(t){return!!t.querySelector(".u, .md, .ans, .code, .act, .think, .metaln, .errline, .sysline, .detail")}export function syncUrl(){const t=new URL(location.href),e=a.tabs.find(n=>n.id===a.activeTabId);e&&e.kind==="session"?t.searchParams.set("session",e.id):t.searchParams.delete("session"),history.replaceState(null,"",t)}let c=null;function g(){if(c)return c;const t=document.querySelector(".chat");return t?(c=i("button","to-latest"),c.type="button",c.hidden=!0,c.addEventListener("click",()=>scrollToBottom(!0)),t.appendChild(c),c):null}function m(){const t=g();t&&(t.textContent="↓ "+l("web.scroll.latest"),t.hidden=!1,s.classList.add("unfollowed"))}function f(){c&&(c.hidden=!0),s.classList.remove("unfollowed")}s?.addEventListener("scroll",()=>{const t=h();u=t,s.classList.toggle("scrolled",s.scrollTop>24),t?f():liveFeed()&&feedHasContent(liveFeed())&&m()});export function appendTo(t,e){return t&&(t.appendChild(e),t===activeFeed()&&scroll()),e}export function clearEmpty(t){const e=t&&t.querySelector(".empty");e&&e.remove()}export function clearBusy(t){if(o.busyTimer&&(clearInterval(o.busyTimer),o.busyTimer=null),t=t||liveFeed(),!t)return;const e=t.querySelector(".busy");e&&e.remove()}export function showBusy(t){if(t=t||liveFeed(),!t||o.curMd||o.curThink||(clearEmpty(t),t.querySelector(".busy")))return;o.busyTimer&&(clearInterval(o.busyTimer),o.busyTimer=null);const e=i("div","busy");e.appendChild(i("span","spin","✦")),e.appendChild(i("span","lab",l("web.action.thinking")));const n=i("span","busytime","· 0s");e.appendChild(n),e.appendChild(i("span","","· "+l("web.input.esc"))),appendTo(t,e);const r=Date.now();o.busyTimer=setInterval(()=>{if(!e.parentNode){clearInterval(o.busyTimer),o.busyTimer=null;return}n.textContent="· "+Math.round((Date.now()-r)/1e3)+"s"},1e3)}export function flushRun(){if(o.runCount<=0){y();return}if(T()==="quiet"&&o.runReconOnly&&o.runFails===0&&o.runCount>1){const t=o.runNodes.slice();t.forEach(r=>r.classList.add("fold-out")),window.setTimeout(()=>t.forEach(r=>r.remove()),160);const e=i("div","grouped");e.appendChild(i("span","pipe","│"));const n=x(o.runDuration);e.appendChild(document.createTextNode(" "+w("web.action.actions",o.runCount)+" ✓"+(n?" · "+n:""))),appendTo(liveFeed(),e)}y()}function y(){o.runCount=0,o.runFails=0,o.runReconOnly=!0,o.runNodes=[],o.runDuration=0}
@@ -1 +1 @@
1
- import{el as s}from"../dom.js";import{t as l}from"../i18n.js";import{categoryOf as p,labelOf as c,argText as h,formatMs as u}from"../format.js";import{renderMarkdown as C}from"../../md.js";import{api as m}from"../api.js";import{feedHasContent as r,clearEmpty as g,scrollToBottom as f,activeFeed as k,liveFeed as x,mdOpts as v,buildThinkBlock as w,appendActionsRow as I}from"./core.js";function L(e,o){const a=String(o??"");if(!a.trim())return;const i=C(e,a,v());I(e,i.innerText||i.textContent||"",a)}function T(e,o){const{wrap:a,body:i}=w(!1,!1);i.textContent=o==null?"":String(o),e.appendChild(a)}function A(e,o,a){const i=s("div","act "+p(o.name));i.appendChild(s("span","g","◇")),i.appendChild(s("span","lab",c(o.name)));const d=h(o.args);d&&(i.appendChild(s("span","sep","·")),i.appendChild(s("span","args",d)));const t=s("span","ok","");i.appendChild(t),e.appendChild(i),o.toolCallId&&a.set(o.toolCallId,t)}function H(e,o,a){const i=u(o.duration),d=(o.success?" ✓":" ✗")+(i?" "+i:""),t=o.toolCallId?a.get(o.toolCallId):null;if(t){t.textContent=d,o.success||t.classList.add("fail"),a.delete(o.toolCallId);return}const n=s("div","act "+p(o.tool));n.appendChild(s("span","g","↳")),n.appendChild(s("span","lab",c(o.tool))),n.appendChild(s("span","ok"+(o.success?"":" fail"),d)),e.appendChild(n)}function F(e,o){const a=new Map;o.forEach(i=>{if(!(!i||typeof i.kind!="string"))switch(i.kind){case"user":e.appendChild(s("div","u",i.text||""));break;case"assistant":L(e,i.text||"");break;case"reasoning":T(e,i.text||"");break;case"tool_call":A(e,i,a);break;case"tool_result":H(e,i,a);break;case"plan":i.text&&e.appendChild(s("div","sysline","▸ "+i.text));break;case"context":i.text&&e.appendChild(s("div","detail","↳ "+i.text));break;case"error":e.appendChild(s("div","errline","✗ "+(i.text||"")));break;case"system":i.text&&e.appendChild(s("div","sysline",i.text));break}})}export async function loadHistory(e){if(!e||e.historyLoaded||e.historyLoading)return{gone:!!e.dead};e.historyLoading=!0;const o=s("div","detail",l("web.history.loading"));r(e.feed)||(g(e.feed),e.feed.appendChild(o));const a=await m("/api/sessions/"+encodeURIComponent(e.id)+"/history");if(e.historyLoading=!1,o.remove(),e.historyLoaded=!0,e.historyFailed=!1,!a.ok||!a.data||!Array.isArray(a.data.history))return a.status===404?{gone:!0}:(e.historyFailed=!0,r(e.feed)||e.feed.appendChild(s("div","detail",l("web.history.unavailable"))),{gone:!1});r(e.feed)||(F(e.feed,a.data.history),a.data.history.length||e.feed.appendChild(s("div","detail",l("web.history.empty"))),e.feed===k()?f():e.feed===x()&&f())}
1
+ import{el as a}from"../dom.js";import{t as n}from"../i18n.js";import{categoryOf as c,labelOf as f,argText as u,formatMs as C}from"../format.js";import{renderMarkdown as m}from"../../md.js";import{api as g}from"../api.js";import{imageCard as k}from"./image-card.js";import{feedHasContent as r,clearEmpty as x,scrollToBottom as h,activeFeed as v,liveFeed as w,mdOpts as A,buildThinkBlock as I,appendActionsRow as L}from"./core.js";function T(i,e){const s=String(e??"");if(!s.trim())return;const o=m(i,s,A());L(i,o.innerText||o.textContent||"",s)}function H(i,e){const{wrap:s,body:o}=I(!1,!1);o.textContent=e==null?"":String(e),i.appendChild(s)}function F(i,e,s){const o=a("div","act "+c(e.name));o.appendChild(a("span","g","◇")),o.appendChild(a("span","lab",f(e.name)));const l=u(e.args);l&&(o.appendChild(a("span","sep","·")),o.appendChild(a("span","args",l)));const t=a("span","ok","");o.appendChild(t),i.appendChild(o),e.toolCallId&&s.set(e.toolCallId,t)}function S(i,e,s){const o=C(e.duration),l=(e.success?" ✓":" ✗")+(o?" "+o:""),t=e.toolCallId?s.get(e.toolCallId):null;if(t)t.textContent=l,e.success||t.classList.add("fail"),s.delete(e.toolCallId);else{const d=a("div","act "+c(e.tool));d.appendChild(a("span","g","↳")),d.appendChild(a("span","lab",f(e.tool))),d.appendChild(a("span","ok"+(e.success?"":" fail"),l)),i.appendChild(d)}if(e.success&&Array.isArray(e.images)&&e.images.length)for(const d of e.images){const p=k(String(e.tool||""),String(d));p&&i.appendChild(p)}}function B(i,e){const s=new Map;e.forEach(o=>{if(!(!o||typeof o.kind!="string"))switch(o.kind){case"user":i.appendChild(a("div","u",o.text||""));break;case"assistant":T(i,o.text||"");break;case"reasoning":H(i,o.text||"");break;case"tool_call":F(i,o,s);break;case"tool_result":S(i,o,s);break;case"plan":o.text&&i.appendChild(a("div","sysline","▸ "+o.text));break;case"context":o.text&&i.appendChild(a("div","detail","↳ "+o.text));break;case"error":i.appendChild(a("div","errline","✗ "+(o.text||"")));break;case"system":o.text&&i.appendChild(a("div","sysline",o.text));break}})}export async function loadHistory(i){if(!i||i.historyLoaded||i.historyLoading)return{gone:!!i.dead};i.historyLoading=!0;const e=a("div","detail",n("web.history.loading"));r(i.feed)||(x(i.feed),i.feed.appendChild(e));const s=await g("/api/sessions/"+encodeURIComponent(i.id)+"/history");if(i.historyLoading=!1,e.remove(),i.historyLoaded=!0,i.historyFailed=!1,!s.ok||!s.data||!Array.isArray(s.data.history))return s.status===404?{gone:!0}:(i.historyFailed=!0,r(i.feed)||i.feed.appendChild(a("div","detail",n("web.history.unavailable"))),{gone:!1});r(i.feed)||(B(i.feed,s.data.history),s.data.history.length||i.feed.appendChild(a("div","detail",n("web.history.empty"))),i.feed===v()?h():i.feed===w()&&h())}
@@ -0,0 +1 @@
1
+ import{el as r}from"../dom.js";import{t as d}from"../i18n.js";import{openLightbox as o}from"../lightbox.js";function c(e){return e==="attach_image"?d("web.card.attached_image"):d("web.card.browser_viewport")}function m(e,t){e.className="imgcard-missing",e.replaceChildren(),e.appendChild(document.createTextNode("◧ "+d("web.card.image_removed"))),e.appendChild(r("div","cap",t))}export function imageCard(e,t){if(!t)return null;const a=c(e),n=r("div","imgcard"),i=document.createElement("img");return i.className="imgthumb",i.alt="",i.loading="lazy",i.src=t,i.addEventListener("error",()=>m(n,a)),i.addEventListener("click",()=>o(t,a)),n.appendChild(i),n.appendChild(r("div","cap",a)),n}
@@ -1 +1 @@
1
- import{msgs as g,el as w,setIcon as k}from"../dom.js";import{t as v}from"../i18n.js";import{feed as t}from"../store.js";import{verbosity as y}from"../format.js";import{mountMarkdown as b}from"../../md.js";import{activeFeed as l,feedForEvent as d,clearBusy as f,clearEmpty as x,flushRun as M,showBusy as R,appendTo as m,scroll as a,mdOpts as A,buildThinkBlock as F}from"./core.js";let s="",c=null,p=!1;function h(){if(p=!1,!s||!t.curMd){s="",c=null;return}const n=s,o=c;s="",c=null,t.curMd.write(n),o&&o===l()&&a()}function L(){p||(p=!0,typeof window<"u"&&typeof window.requestAnimationFrame=="function"?window.requestAnimationFrame(h):h())}export function finishMd(){h(),t.curMd&&(t.curMd.root.classList.remove("streaming"),t.curMd.end(),t.curMd=null),t.curText=null}export function onText(n,o){const e=d(o);f(e),e&&(x(e),t.curThink&&finishThink(),t.curText==null&&(M(),t.curText="",t.curMd=b(e,A(!0)),t.curMd.root.classList.add("streaming"),t.turnRoots.push(t.curMd.root)),t.curText+=n,t.turnMd+=n,c&&c!==e&&h(),c=e,s+=n,L())}export function finishThink(){if(t.curThink&&(t.curThink.wrap&&t.curThink.wrap.classList.remove("live"),t.curThink.startedAt)){const n=t.curThink.head.querySelector(".dur");n&&(n.textContent="· "+((Date.now()-t.curThink.startedAt)/1e3).toFixed(1)+"s")}t.curThink=null}export function onReasoning(n,o){const e=d(o);if(f(e),!!e){if(x(e),finishMd(),!t.curThink){M();const{wrap:r,head:i,body:u}=F(!0);m(e,r),t.curThink={wrap:r,head:i,body:u,startedAt:Date.now()}}t.curThink.body.textContent+=n,e===l()&&a()}}export function onMeta(n,o,e){if(e==="tool"&&y()==="quiet")return;const r=d(o);f(r);const i=(n||"").trim();if(!r||!i)return;x(r),finishMd();const u=i.indexOf("⚠")!==-1||i.indexOf("✗")!==-1;m(r,w("div",u?"metaln warn":"metaln",i)),r===l()&&a()}export function onRetract(n,o){const e=d(o);if(!e)return;f(e);const r=t.curMd?t.curMd.root:null,i=!!r||t.curText!=null&&t.curText!=="",u=t.curText?t.curText.length:0;if(finishMd(),finishThink(),r){const T=t.turnRoots.indexOf(r);T!==-1&&t.turnRoots.splice(T,1),r.remove()}u>0&&(t.turnMd=t.turnMd.slice(0,Math.max(0,t.turnMd.length-u))),i&&(n&&m(e,w("div","metaln",n)),e===l()&&a())}export function onPhase(n,o){const e=d(o);n==="thinking"?R(e):f(e)}g.addEventListener("click",n=>{const e=n.target.closest(".think-h");if(!e)return;const r=e.nextElementSibling;if(!r)return;const i=r.classList.toggle("hidden");e.setAttribute("aria-expanded",i?"false":"true");const u=e.querySelector(".tog");u&&k(u,i?"chevron-right":"chevron-down",v(i?"web.action.show":"web.action.hide"))});
1
+ import{msgs as g,el as w,setIcon as k}from"../dom.js";import{t as v}from"../i18n.js";import{feed as t}from"../store.js";import{verbosity as y}from"../format.js";import{mountMarkdown as b}from"../../md.js";import{activeFeed as l,feedForEvent as d,clearBusy as f,clearEmpty as x,flushRun as M,showBusy as R,appendTo as m,scroll as a,mdOpts as A,buildThinkBlock as F}from"./core.js";let s="",c=null,p=!1;function h(){if(p=!1,!s||!t.curMd){s="",c=null;return}const n=s,o=c;s="",c=null,t.curMd.write(n),o&&o===l()&&a()}function L(){p||(p=!0,typeof window<"u"&&typeof window.requestAnimationFrame=="function"?window.requestAnimationFrame(h):h())}export function finishMd(){h(),t.curMd&&(t.curMd.root.classList.remove("streaming"),t.curMd.end(),t.curMd=null),t.curText=null}export function onText(n,o){const e=d(o);f(e),e&&(x(e),t.curThink&&finishThink(),t.curText==null&&(M(),t.curText="",t.curMd=b(e,A(!0)),t.curMd.root.classList.add("streaming"),t.turnRoots.push(t.curMd.root)),t.curText+=n,t.turnMd+=n,c&&c!==e&&h(),c=e,s+=n,L())}export function finishThink(){if(t.curThink&&(t.curThink.wrap&&t.curThink.wrap.classList.remove("live"),t.curThink.startedAt)){const n=t.curThink.head.querySelector(".dur");n&&(n.textContent="· "+((Date.now()-t.curThink.startedAt)/1e3).toFixed(1)+"s")}t.curThink=null}export function onReasoning(n,o){const e=d(o);if(f(e),!!e){if(x(e),finishMd(),!t.curThink){M();const{wrap:r,head:i,body:u}=F(!0);m(e,r),t.curThink={wrap:r,head:i,body:u,startedAt:Date.now()}}t.curThink.body.textContent+=n,e===l()&&a()}}export function onMeta(n,o,e){if(e==="tool"&&y()==="quiet")return;const r=d(o);f(r);const i=(n||"").trim();if(!r||!i)return;x(r),finishMd();const u=i.indexOf("⚠")!==-1||i.indexOf("✗")!==-1;m(r,w("div",u?"metaln warn":"metaln",i)),r===l()&&a()}export function onRetract(n,o){const e=d(o);if(!e)return;f(e);const r=t.curMd?t.curMd.root:null,i=!!r||t.curText!=null&&t.curText!=="",u=t.curText?t.curText.length:0;if(finishMd(),finishThink(),r){const T=t.turnRoots.indexOf(r);T!==-1&&t.turnRoots.splice(T,1),r.remove()}u>0&&(t.turnMd=t.turnMd.slice(0,Math.max(0,t.turnMd.length-u))),i&&(n&&m(e,w("div","metaln",n)),e===l()&&a())}export function onPhase(n,o){const e=d(o);n==="thinking"?R(e):f(e)}g?.addEventListener("click",n=>{const e=n.target.closest(".think-h");if(!e)return;const r=e.nextElementSibling;if(!r)return;const i=r.classList.toggle("hidden");e.setAttribute("aria-expanded",i?"false":"true");const u=e.querySelector(".tog");u&&k(u,i?"chevron-right":"chevron-down",v(i?"web.action.show":"web.action.hide"))});
@@ -1,5 +1,5 @@
1
- import{el as r,icon as w,announce as g}from"../dom.js";import{t as u,pluralCount as k}from"../i18n.js";import{feed as c,tabs as A,session as C}from"../store.js";import{FILE_TOOLS as x,OPENABLE_TOOLS as F,IMAGE_TOOLS as B,LINK_TOOLS as U,categoryOf as v,labelOf as O,argText as q,formatMs as L,verbosity as b,firstOutputLine as I}from"../format.js";import{copyText as P}from"../../md.js";import{activeFeed as S,liveFeed as j,feedForEvent as h,clearBusy as T,clearEmpty as N,appendTo as a,insertAfter as E,appendActionsRow as D,flushRun as $}from"./core.js";import{finishMd as _,finishThink as M}from"./stream.js";export function onToolStart(e){const n=h(e.sessionId);if(T(n),!n)return;N(n),_(),M(),c.pendingArgs.set(e.toolCallId,e.args||{});const t=r("div","act "+v(e.name));t.appendChild(r("span","g","◇")),t.appendChild(r("span","lab",O(e.name)));const s=q(e.args);s&&(t.appendChild(r("span","sep","·")),t.appendChild(r("span","args",s)));const i=r("span","ok","");t.appendChild(i),t.classList.add("running"),a(n,t),c.toolNodes.set(e.toolCallId,{status:i,line:t}),v(e.name)!=="recon"&&(c.runReconOnly=!1),c.runCount++}function R(e,n){if(typeof n!="string"||!n)return;const t=r("div","toolout"),s=r("div","code");if(s.textContent=n,t.appendChild(s),n.length>600||n.split(`
2
- `).length>12){t.classList.add("collapsed");const p="▸ "+k("web.tool.output",n.length),l=r("span","more",p);l.addEventListener("click",()=>{const f=t.classList.toggle("collapsed");l.textContent=f?p:"▾ "+u("web.tool.collapse")}),t.appendChild(l)}const o=e&&e.parentNode?null:j();e&&e.parentNode?e.parentNode.insertBefore(t,e.nextSibling):a(o,t)}function G(e){if(typeof e!="string"||!e)return null;try{const n=new URL(e,location.href);if(n.protocol==="http:"||n.protocol==="https:"||n.protocol==="file:")return n.href}catch{return null}return null}function K(e){const n=String(e??"").replace(/\\/g,"/");try{const t=C&&C.host&&C.host.workspace?String(C.host.workspace).replace(/\\/g,"/").replace(/\/+$/,""):"";if(t&&(n===t||n.indexOf(t+"/")===0)){const s=n===t?"":n.slice(t.length+1);if(s)return s}}catch{}return n}function m(e,n,t,s){const i=h(t);if(!i)return null;if(x.indexOf(e)!==-1){const o=n.path||n.file||n.dir;if(!o)return null;const p=K(o),l=r("div","filerow "+v(e));if(l.appendChild(r("span","g","◇")),l.appendChild(r("span","lab",O(e))),l.appendChild(r("span","sep"," · ")),F.indexOf(e)!==-1){const d=r("a","path",p||String(o));d.href="/api/file?path="+encodeURIComponent(String(o)),d.target="_blank",d.rel="noopener noreferrer",d.title=u("web.card.open_title")+" · "+String(o),l.appendChild(d)}else{const d=r("span","tail",p||String(o));d.title=String(o),l.appendChild(d)}const f=r("button","copy-btn");return f.type="button",f.title=u("web.card.copy_title"),f.setAttribute("aria-label",u("web.card.copy_title")),f.appendChild(w("copy")),f.addEventListener("click",()=>{P(String(o)).then(d=>{if(f.replaceChildren(w(d?"check":"copy")),!d){const y=l.querySelector(".st");y&&(y.textContent=" ⚠")}window.setTimeout(()=>f.replaceChildren(w("copy")),1400)})}),l.appendChild(f),l.appendChild(r("span","st",s===!1?" ✗":s===!0?" ✓":"")),s===!1&&(l.dataset.fail="1"),/\.drawio(\.xml)?$/i.test(String(o))&&b()==="verbose"&&l.appendChild(r("span","detail","↳ "+u("web.card.drawio_hint"))),a(i,l),l}if(U.indexOf(e)!==-1&&n.url){const o=r("div","linkline");o.appendChild(document.createTextNode("↳ "));const p=G(n.url);if(p){const l=r("a","",n.url);l.href=p,l.target="_blank",l.rel="noopener noreferrer",o.appendChild(l);try{const f=new URL(p).hostname;f&&o.appendChild(r("span","dom"," · "+f))}catch{}}else o.appendChild(r("span","",n.url));return a(i,o),o}if(B.indexOf(e)!==-1){const o=r("div","imgcard");return o.appendChild(r("div","ph","◧ "+u("web.card.screenshot"))),o.appendChild(r("div","cap",e==="attach_image"?u("web.card.attached_image"):u("web.card.browser_viewport"))),a(i,o),o}return null}export function onToolEnd(e){const n=c.toolNodes.get(e.toolCallId),t=n?n.line:null;if(n){n.line.classList.remove("running");const o=L(e.durationMs);n.status.textContent=e.success?" ✓"+(o?" "+o:""):" ✗"+(o?" "+o:""),e.success||(n.status.classList.add("fail"),c.runFails++,c.runReconOnly=!1)}c.toolNodes.delete(e.toolCallId);const s=c.pendingArgs.get(e.toolCallId)||{};c.pendingArgs.delete(e.toolCallId);const i=s.path||s.file||s.dir;if(x.indexOf(e.name)!==-1&&i){if(t&&t.parentNode&&t.remove(),b()==="quiet"){if(e.success){const p=m(e.name,s,e.sessionId,e.success);p&&c.runNodes.push(p),c.runDuration+=typeof e.durationMs=="number"?e.durationMs:0}else{const p=m(e.name,s,e.sessionId,e.success),l=I(e.output);l&&E(p||t,r("div","detail","↳ "+l)),p&&c.runNodes.push(p)}return}const o=m(e.name,s,e.sessionId,e.success);R(o||t,e.output);return}if(b()==="quiet"){if(e.success){t&&c.runNodes.push(t);const o=m(e.name,s,e.sessionId,e.success);o&&c.runNodes.push(o),c.runDuration+=typeof e.durationMs=="number"?e.durationMs:0}else{const o=I(e.output);o&&E(t,r("div","detail","↳ "+o))}return}e.success&&m(e.name,s,e.sessionId,e.success),R(t,e.output)}export function onTurnEnd(e){C.busy=!1;const n=h(e.sessionId);T(n),M(),_(),$(),c.toolNodes.forEach(i=>{i&&i.line&&n&&i.line.parentNode===n&&i.line.classList.remove("running")});const t=A.tabs.find(i=>i.id===e.sessionId);if(t&&(t.planCard=null),!n){resetTurnText();return}if(e.success===!1)e.interrupted?(a(n,r("div","warnline","⚠ "+u("web.turn.stopped"))),g(u("web.turn.stopped"))):(a(n,r("div","errline","✗ "+u("web.turn.failed"))),g(u("web.turn.failed")));else{const i=[];typeof e.toolCalls=="number"&&e.toolCalls>0&&i.push(u("web.turn.tools",{count:e.toolCalls})),typeof e.durationMs=="number"&&i.push(L(e.durationMs)),i.length&&a(n,r("div","metric","⟡ "+i.join(" · "))),g(u("web.turn.end",{count:e.toolCalls||0}))}const s=c.turnRoots.map(i=>i.innerText||i.textContent||"").join(`
1
+ import{el as r,icon as w,announce as g}from"../dom.js";import{t as d,pluralCount as _}from"../i18n.js";import{feed as c,tabs as k,session as m}from"../store.js";import{FILE_TOOLS as x,OPENABLE_TOOLS as F,LINK_TOOLS as B,categoryOf as y,labelOf as I,argText as U,formatMs as S,verbosity as b,firstOutputLine as L}from"../format.js";import{copyText as q}from"../../md.js";import{imageCard as P}from"./image-card.js";import{activeFeed as O,liveFeed as j,feedForEvent as a,clearBusy as T,clearEmpty as N,appendTo as h,insertAfter as E,appendActionsRow as D,flushRun as $}from"./core.js";import{finishMd as M,finishThink as R}from"./stream.js";export function onToolStart(e){const n=a(e.sessionId);if(T(n),!n)return;N(n),M(),R(),c.pendingArgs.set(e.toolCallId,e.args||{});const o=r("div","act "+y(e.name));o.appendChild(r("span","g","◇")),o.appendChild(r("span","lab",I(e.name)));const s=U(e.args);s&&(o.appendChild(r("span","sep","·")),o.appendChild(r("span","args",s)));const i=r("span","ok","");o.appendChild(i),o.classList.add("running"),h(n,o),c.toolNodes.set(e.toolCallId,{status:i,line:o}),y(e.name)!=="recon"&&(c.runReconOnly=!1),c.runCount++}function A(e,n){if(typeof n!="string"||!n)return;const o=r("div","toolout"),s=r("div","code");if(s.textContent=n,o.appendChild(s),n.length>600||n.split(`
2
+ `).length>12){o.classList.add("collapsed");const p="▸ "+_("web.tool.output",n.length),l=r("span","more",p);l.addEventListener("click",()=>{const u=o.classList.toggle("collapsed");l.textContent=u?p:"▾ "+d("web.tool.collapse")}),o.appendChild(l)}const t=e&&e.parentNode?null:j();e&&e.parentNode?e.parentNode.insertBefore(o,e.nextSibling):h(t,o)}function K(e){if(typeof e!="string"||!e)return null;try{const n=new URL(e,location.href);if(n.protocol==="http:"||n.protocol==="https:"||n.protocol==="file:")return n.href}catch{return null}return null}function z(e){const n=String(e??"").replace(/\\/g,"/");try{const o=m&&m.host&&m.host.workspace?String(m.host.workspace).replace(/\\/g,"/").replace(/\/+$/,""):"";if(o&&(n===o||n.indexOf(o+"/")===0)){const s=n===o?"":n.slice(o.length+1);if(s)return s}}catch{}return n}function C(e,n,o,s){const i=a(o);if(!i)return null;if(x.indexOf(e)!==-1){const t=n.path||n.file||n.dir;if(!t)return null;const p=z(t),l=r("div","filerow "+y(e));if(l.appendChild(r("span","g","◇")),l.appendChild(r("span","lab",I(e))),l.appendChild(r("span","sep"," · ")),F.indexOf(e)!==-1){const f=r("a","path",p||String(t));f.href="/api/file?path="+encodeURIComponent(String(t)),f.target="_blank",f.rel="noopener noreferrer",f.title=d("web.card.open_title")+" · "+String(t),l.appendChild(f)}else{const f=r("span","tail",p||String(t));f.title=String(t),l.appendChild(f)}const u=r("button","copy-btn");return u.type="button",u.title=d("web.card.copy_title"),u.setAttribute("aria-label",d("web.card.copy_title")),u.appendChild(w("copy")),u.addEventListener("click",()=>{q(String(t)).then(f=>{if(u.replaceChildren(w(f?"check":"copy")),!f){const v=l.querySelector(".st");v&&(v.textContent=" ⚠")}window.setTimeout(()=>u.replaceChildren(w("copy")),1400)})}),l.appendChild(u),l.appendChild(r("span","st",s===!1?" ✗":s===!0?" ✓":"")),s===!1&&(l.dataset.fail="1"),/\.drawio(\.xml)?$/i.test(String(t))&&b()==="verbose"&&l.appendChild(r("span","detail","↳ "+d("web.card.drawio_hint"))),h(i,l),l}if(B.indexOf(e)!==-1&&n.url){const t=r("div","linkline");t.appendChild(document.createTextNode("↳ "));const p=K(n.url);if(p){const l=r("a","",n.url);l.href=p,l.target="_blank",l.rel="noopener noreferrer",t.appendChild(l);try{const u=new URL(p).hostname;u&&t.appendChild(r("span","dom"," · "+u))}catch{}}else t.appendChild(r("span","",n.url));return h(i,t),t}return null}function G(e){if(!e.success||!Array.isArray(e.images)||!e.images.length)return;const n=a(e.sessionId);if(n)for(const o of e.images){const s=P(String(e.name||""),String(o));s&&h(n,s)}}export function onToolEnd(e){H(e),G(e)}function H(e){const n=c.toolNodes.get(e.toolCallId),o=n?n.line:null;if(n){n.line.classList.remove("running");const t=S(e.durationMs);n.status.textContent=e.success?" ✓"+(t?" "+t:""):" ✗"+(t?" "+t:""),e.success||(n.status.classList.add("fail"),c.runFails++,c.runReconOnly=!1)}c.toolNodes.delete(e.toolCallId);const s=c.pendingArgs.get(e.toolCallId)||{};c.pendingArgs.delete(e.toolCallId);const i=s.path||s.file||s.dir;if(x.indexOf(e.name)!==-1&&i){if(o&&o.parentNode&&o.remove(),b()==="quiet"){if(e.success){const p=C(e.name,s,e.sessionId,e.success);p&&c.runNodes.push(p),c.runDuration+=typeof e.durationMs=="number"?e.durationMs:0}else{const p=C(e.name,s,e.sessionId,e.success),l=L(e.output);l&&E(p||o,r("div","detail","↳ "+l)),p&&c.runNodes.push(p)}return}const t=C(e.name,s,e.sessionId,e.success);A(t||o,e.output);return}if(b()==="quiet"){if(e.success){o&&c.runNodes.push(o);const t=C(e.name,s,e.sessionId,e.success);t&&c.runNodes.push(t),c.runDuration+=typeof e.durationMs=="number"?e.durationMs:0}else{const t=L(e.output);t&&E(o,r("div","detail","↳ "+t))}return}e.success&&C(e.name,s,e.sessionId,e.success),A(o,e.output)}export function onTurnEnd(e){m.busy=!1;const n=a(e.sessionId);T(n),R(),M(),$(),c.toolNodes.forEach(i=>{i&&i.line&&n&&i.line.parentNode===n&&i.line.classList.remove("running")});const o=k.tabs.find(i=>i.id===e.sessionId);if(o&&(o.planCard=null),!n){resetTurnText();return}if(e.success===!1)e.interrupted?(h(n,r("div","warnline","⚠ "+d("web.turn.stopped"))),g(d("web.turn.stopped"))):(h(n,r("div","errline","✗ "+d("web.turn.failed"))),g(d("web.turn.failed")));else{const i=[];typeof e.toolCalls=="number"&&e.toolCalls>0&&i.push(d("web.turn.tools",{count:e.toolCalls})),typeof e.durationMs=="number"&&i.push(S(e.durationMs)),i.length&&h(n,r("div","metric","⟡ "+i.join(" · "))),g(d("web.turn.end",{count:e.toolCalls||0}))}const s=c.turnRoots.map(i=>i.innerText||i.textContent||"").join(`
3
3
 
4
- `);D(n,s,c.turnMd),resetTurnText()}export function resetTurnText(){c.turnMd="",c.turnRoots=[]}export function onErrorEvent(e){if(!e||!e.error)return;const n=h(e.sessionId);n&&a(n,r("div","errline","✗ "+e.error)),g(String(e.error))}export function onCommandOutput(e,n){const t=h(n)||S();t&&(N(t),String(e).split(`
5
- `).forEach(s=>a(t,r("div","cmdout",s))))}export function onLog(e){if(!e||typeof e.text!="string"||!e.text||e.diagnostic&&b()!=="verbose")return;const n=h(e.sessionId)||S();if(!n)return;const t=e.level,s=t==="error"?"errline":t==="warn"?"warnline":"detail",i=t==="error"?"✗ ":t==="warn"?"⚠ ":"↳ ",o=r("div",s);o.textContent=i+e.text,a(n,o),(t==="error"||t==="warn")&&g(e.text)}
4
+ `);D(n,s,c.turnMd),resetTurnText()}export function resetTurnText(){c.turnMd="",c.turnRoots=[]}export function onErrorEvent(e){if(!e||!e.error)return;const n=a(e.sessionId);n&&h(n,r("div","errline","✗ "+e.error)),g(String(e.error))}export function onCommandOutput(e,n){const o=a(n)||O();o&&(N(o),String(e).split(`
5
+ `).forEach(s=>h(o,r("div","cmdout",s))))}export function onLog(e){if(!e||typeof e.text!="string"||!e.text||e.diagnostic&&b()!=="verbose")return;const n=a(e.sessionId)||O();if(!n)return;const o=e.level,s=o==="error"?"errline":o==="warn"?"warnline":"detail",i=o==="error"?"✗ ":o==="warn"?"⚠ ":"↳ ",t=r("div",s);t.textContent=i+e.text,h(n,t),(o==="error"||o==="warn")&&g(e.text)}
@@ -1,2 +1,2 @@
1
- import{t as c}from"./i18n.js";import{settings as f}from"./store.js";export const CATEGORIES={recon:["read_file","list_dir","glob","grep","file_info","project_map","chunk_query","web_search","web_fetch","history","recall","process_list","process_log"],mutate:["write_file","edit_file","create_dir","delete_file","move_file","attach_image"],execute:["bash","process_kill","pipeline_run","browser","download_file","subagent","mcp_call"],meta:["plan","plan_step","todo","verify","lsp_check","enable_tools","set_thinking","load_skill","remember"]},FILE_TOOLS=["read_file","write_file","edit_file","list_dir","delete_file","move_file","file_info"],OPENABLE_TOOLS=["read_file","write_file","edit_file","file_info"],IMAGE_TOOLS=["attach_image","browser"],LINK_TOOLS=["web_fetch","download_file"];export function categoryOf(e){for(const t of Object.keys(CATEGORIES))if(CATEGORIES[t].indexOf(e)!==-1)return t;return"meta"}export function labelOf(e){const t="tool.friendly."+e,r=c(t);if(r!==t)return r;const o=String(e).replace(/_/g," ");return o.charAt(0).toUpperCase()+o.slice(1)}export function argText(e){if(!e||typeof e!="object")return"";const t=e,r=["path","file","dir","command","query","url","pattern","name","id","text","prompt"];for(const i of r)if(typeof t[i]=="string"&&t[i])return t[i];const o=Object.keys(t);if(!o.length)return"";const n=t[o[0]];if(typeof n=="string")return n;if(typeof n=="number"||typeof n=="boolean")return String(n);try{const i=JSON.stringify(e);return i==="{}"?"":i.slice(0,120)}catch{return""}}export function formatMs(e){return typeof e!="number"||!isFinite(e)?"":e>=1e3?(e/1e3).toFixed(1)+"s":Math.round(e)+"ms"}export function verbosity(){const e=f.config&&f.config.ui||{};return e.verbosity?e.verbosity:e.verbose?"verbose":"quiet"}export function firstOutputLine(e){if(typeof e!="string"||!e)return"";for(const t of e.split(`
2
- `)){const r=t.trim();if(r)return r.length>200?r.slice(0,197)+"…":r}return""}export function matchesQuery(e,t){const r=String(t??"").trim().toLowerCase();return r?String(e??"").toLowerCase().indexOf(r)!==-1:!0}export function relTime(e){if(!e)return"";const t=Date.now()-new Date(e).getTime();if(!isFinite(t)||t<0)return"";const r=Math.floor(t/6e4);if(r<1)return"now";if(r<60)return r+"m";const o=Math.floor(r/60);return o<24?o+"h":Math.floor(o/24)+"d"}export function fmtTokens(e){return e>=1e3?(e/1e3).toFixed(1)+"K":String(e)}export function contextLabel(){const e=f.config&&f.config.contextWindow||0;return e?Math.round(e/1024)+"K":"—"}
1
+ import{t as c}from"./i18n.js";import{settings as f}from"./store.js";export const CATEGORIES={recon:["read_file","list_dir","glob","grep","file_info","project_map","chunk_query","web_search","web_fetch","history","recall","process_list","process_log"],mutate:["write_file","edit_file","create_dir","delete_file","move_file","attach_image"],execute:["bash","process_kill","pipeline_run","browser","download_file","subagent","mcp_call"],meta:["plan","plan_step","todo","verify","lsp_check","enable_tools","set_thinking","load_skill","remember"]},FILE_TOOLS=["read_file","write_file","edit_file","list_dir","delete_file","move_file","file_info"],OPENABLE_TOOLS=["read_file","write_file","edit_file","file_info"],LINK_TOOLS=["web_fetch","download_file"];export function categoryOf(e){for(const t of Object.keys(CATEGORIES))if(CATEGORIES[t].indexOf(e)!==-1)return t;return"meta"}export function labelOf(e){const t="tool.friendly."+e,r=c(t);if(r!==t)return r;const n=String(e).replace(/_/g," ");return n.charAt(0).toUpperCase()+n.slice(1)}export function argText(e){if(!e||typeof e!="object")return"";const t=e,r=["path","file","dir","command","query","url","pattern","name","id","text","prompt"];for(const i of r)if(typeof t[i]=="string"&&t[i])return t[i];const n=Object.keys(t);if(!n.length)return"";const o=t[n[0]];if(typeof o=="string")return o;if(typeof o=="number"||typeof o=="boolean")return String(o);try{const i=JSON.stringify(e);return i==="{}"?"":i.slice(0,120)}catch{return""}}export function formatMs(e){return typeof e!="number"||!isFinite(e)?"":e>=1e3?(e/1e3).toFixed(1)+"s":Math.round(e)+"ms"}export function verbosity(){const e=f.config&&f.config.ui||{};return e.verbosity?e.verbosity:e.verbose?"verbose":"quiet"}export function firstOutputLine(e){if(typeof e!="string"||!e)return"";for(const t of e.split(`
2
+ `)){const r=t.trim();if(r)return r.length>200?r.slice(0,197)+"…":r}return""}export function matchesQuery(e,t){const r=String(t??"").trim().toLowerCase();return r?String(e??"").toLowerCase().indexOf(r)!==-1:!0}export function relTime(e){if(!e)return"";const t=Date.now()-new Date(e).getTime();if(!isFinite(t)||t<0)return"";const r=Math.floor(t/6e4);if(r<1)return"now";if(r<60)return r+"m";const n=Math.floor(r/60);return n<24?n+"h":Math.floor(n/24)+"d"}export function fmtTokens(e){return e>=1e3?(e/1e3).toFixed(1)+"K":String(e)}export function contextLabel(){const e=f.config&&f.config.contextWindow||0;return e?Math.round(e/1024)+"K":"—"}
@@ -0,0 +1 @@
1
+ import{el as r}from"./dom.js";import{t as c}from"./i18n.js";import{setIndicator as f,trapTab as h}from"./dialog.js";import{copyText as g}from"../md.js";let t=null,n=null,p=null,l=null,a=null,u=null;function m(){const e=r("div","lightbox");e.hidden=!0,e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true");const o=r("button","lb-close","✕");o.type="button",o.title=c("web.lightbox.close"),o.setAttribute("aria-label",c("web.lightbox.close"));const d=r("figure","lb-figure");n=document.createElement("img"),n.className="lb-img",n.alt="",d.appendChild(n),p=r("figcaption","lb-cap"),d.appendChild(p);const b=r("div","lb-actions");return l=r("a","lb-open"),l.textContent=c("web.lightbox.open_tab"),l.target="_blank",l.rel="noopener noreferrer",a=r("button","lb-copy",c("web.lightbox.copy")),a.type="button",b.appendChild(l),b.appendChild(a),e.appendChild(o),e.appendChild(d),e.appendChild(b),o.addEventListener("click",closeLightbox),e.addEventListener("click",i=>{i.target===e&&closeLightbox()}),e.addEventListener("keydown",i=>{if(i.key==="Escape"){i.stopPropagation(),closeLightbox();return}h(e,i)}),a.addEventListener("click",()=>{const i=l?.href||"";g(i).then(s=>f(c("web.lightbox.copied"),!s))}),document.body.appendChild(e),e}export function openLightbox(e,o){if(!e||(t||(t=m()),!n||!p||!l||!a))return;n.src=e,p.textContent=o||"",p.hidden=!o,l.href=e,a.textContent=c("web.lightbox.copy"),u=document.activeElement,t.hidden=!1,t.classList.add("open");const d=t.querySelector(".lb-close");d&&d.focus()}export function closeLightbox(){!t||t.hidden||(t.hidden=!0,t.classList.remove("open"),n&&n.removeAttribute("src"),u&&u.focus&&u.focus(),u=null)}
@@ -1 +1 @@
1
- import{msgs as k,inp as S,tabstripEl as l,el as u,icon as E,link as A}from"../dom.js";import{api as m}from"../api.js";import{t as o}from"../i18n.js";import{tabs as a,session as b,hooks as x}from"../store.js";import{makeFeed as q,showEmptyFeed as D,activeFeed as F,appendTo as C,feedHasContent as B,loadHistory as O,syncUrl as h,scrollToBottom as w}from"../feed.js";import{setIndicator as M}from"../dialog.js";import{sidebarSessionChanged as P,setSessionLoading as v}from"../sidebar.js";import{setSessOpen as H}from"../core/menus.js";import{closeCmdk as W}from"./cmdk.js";import{saveComposer as L,restoreComposer as I,renderAttachments as Y,renderSessBar as _,initComposer as N}from"./composer.js";import{renderSessionsDropdown as p,initSessionsMenu as U}from"./sessions-menu.js";export{reflectBusy}from"./composer.js";export{loadCommands}from"./cmdk.js";export{renderSessionsDropdown}from"./sessions-menu.js";const V={working:"●",waiting:"◉",queued:"○",failed:"✗",dead:"✗"};function T(e){return e==="working"?o("web.tab.working"):e==="waiting"?o("web.tab.waiting"):e==="queued"?o("web.tab.queued"):e==="failed"?o("web.tab.failed"):e==="dead"?o("web.tab.gone"):""}export function activeTab(){return a.tabs.find(e=>e.id===a.activeTabId)||null}export function setTabStatus(e,t){if(!e)return;const i=a.tabs.find(n=>n.id===e);!i||i.status===t||i.dead||(i.status=t,c())}export function markTabDead(e){const t=a.tabs.find(s=>s.id===e);if(!t||t.dead)return;t.dead=!0,t.status="dead",t.historyLoaded=!0,t.historyFailed=!1,t.planCard=null,v(!1),t.feed.replaceChildren();const i=u("div","warnline","✗ "+o("web.session.gone"));C(t.feed,i);const n=A(o("web.topbar.close"),"",()=>closeTab(e));C(t.feed,n),t.feed===F()&&w(),c()}function c(){l.textContent="",a.tabs.forEach(e=>{const t=e.id===a.activeTabId,i=e.name||e.id,n=e.kind==="session"?String(e.id):i,s=u("div","stab"+(t?" on":""));if(s.setAttribute("role","tab"),s.setAttribute("tabindex",t?"0":"-1"),s.setAttribute("aria-selected",t?"true":"false"),s.setAttribute("aria-label",i),s.title=n,e.status&&e.status!=="idle"){const d=u("span","dot st-"+e.status,V[e.status]||"●");d.title=T(e.status),d.setAttribute("aria-label",T(e.status)),s.appendChild(d)}const r=u("span","name",i);r.title=n,s.appendChild(r);const f=u("button","stab-x");f.type="button",f.title=o("web.topbar.close"),f.setAttribute("aria-label",o("web.topbar.close")+": "+i),f.appendChild(E("x")),f.addEventListener("click",d=>{d.stopPropagation(),closeTab(e.id)}),s.appendChild(f);const g=()=>openTab(e.id,e.name,e.kind,!0);s.addEventListener("click",g),s.addEventListener("keydown",d=>{(d.key==="Enter"||d.key===" ")&&(d.preventDefault(),g())}),l.appendChild(s)}),j(),_()}function j(){const e=l.querySelector(".stab.on");if(!e)return;const t=Math.max(0,e.offsetLeft-4);(t<l.scrollLeft||t+e.offsetWidth>l.scrollLeft+l.clientWidth)&&(l.scrollLeft=t)}l.addEventListener("wheel",e=>{e.deltaY!==0&&(e.preventDefault(),l.scrollLeft+=e.deltaY)},{passive:!1});export function updateTabName(e,t){if(!e||!t)return;const i=a.tabs.find(s=>s.id===e);i&&i.name!==t&&(i.name=t,c());const n=b.sessions.find(s=>s.id===e);n&&n.name!==t&&(n.name=t,p())}export function openTab(e,t,i,n){L();let s=a.tabs.find(r=>r.id===e);if(s?t&&(s.name=t):(s={id:e,name:t||e,kind:i||"session",feed:q(),historyLoaded:!1,historyLoading:!1,historyFailed:!1,dead:!1,planCard:null,draft:"",attachments:[],status:"idle"},a.tabs.push(s)),a.activeTabId=e,a.liveTabId=e,(i||"session")==="session"&&(b.activeSessionId=e,P(e)),k.replaceChildren(s.feed),I(s),c(),p(),w(),s.kind==="session"&&!n&&(s.historyLoaded=!0),n&&s.kind==="session"){if(s.dead){h();return}(!B(s.feed)||s.historyFailed)&&(s.historyLoaded=!1),v(!0),m("/api/sessions/activate",{method:"POST",body:{id:e}}).then(r=>{if(r.status===404){markTabDead(e),loadSessions();return}loadSessions(),a.activeTabId===e&&x.refreshState?.()}),O(s).then(r=>{r&&r.gone&&markTabDead(s.id)})}h()}export function closeTab(e){const t=a.tabs.findIndex(n=>n.id===e);if(t===-1)return;const i=a.activeTabId===e;if(i&&L(),a.tabs=a.tabs.filter(n=>n.id!==e),i){const n=a.tabs[Math.min(t,a.tabs.length-1)];n?(a.activeTabId=n.id,a.liveTabId=n.id,k.replaceChildren(n.feed),I(n),w()):(a.activeTabId=null,a.liveTabId=null,S&&(S.value=""),a.attachments=[],Y(),D())}c(),p(),h()}async function y(){const e=await m("/api/sessions",{method:"POST",body:{}});if(e.ok&&e.data&&e.data.session&&e.data.session.id){const t=e.data.session;openTab(t.id,t.name||String(t.id),"session",!1),await loadSessions(),v(!0),a.activeTabId===t.id&&x.refreshState?.();return}a.newSeq++,openTab("new"+a.newSeq,o("web.empty.title"),"new",!1),M(o("web.sessions.create_failed"),!0)}document.getElementById("newChat")?.addEventListener("click",()=>{y()});export function reconcileTabStatuses(){const e=b.runningSessionIds||[],t=b.queued||{};let i=!1;a.tabs.forEach(n=>{if(n.kind!=="session"||n.status==="waiting"||n.status==="failed"||n.status==="dead")return;const s=e.includes(n.id)?"working":t[n.id]>0?"queued":"idle";n.status!==s&&(n.status=s,i=!0)}),i&&c()}export async function loadSessions(){const e=await m("/api/sessions");!e.ok||!e.data||(b.sessions=Array.isArray(e.data.sessions)?e.data.sessions:[],p())}document.addEventListener("click",()=>{document.querySelectorAll("[data-sel]").forEach(e=>e.classList.remove("open")),H(!1),W()}),N({activeTab,setTabStatus,newChatTab:y,markTabDead,openTab,closeTab,statusTitle:T}),U({openTab,closeTab,loadSessions,newChatTab:y});
1
+ import{msgs as k,inp as S,tabstripEl as l,el as u,icon as I,link as E}from"../dom.js";import{api as m}from"../api.js";import{t as o}from"../i18n.js";import{tabs as a,session as b,hooks as A}from"../store.js";import{makeFeed as q,showEmptyFeed as D,activeFeed as F,appendTo as x,feedHasContent as B,loadHistory as O,syncUrl as h,scrollToBottom as w}from"../feed.js";import{setIndicator as M}from"../dialog.js";import{sidebarSessionChanged as P,setSessionLoading as v}from"../sidebar.js";import{setSessOpen as H}from"../core/menus.js";import{closeCmdk as W}from"./cmdk.js";import{saveComposer as C,restoreComposer as L,renderAttachments as Y,renderSessBar as _,initComposer as N}from"./composer.js";import{renderSessionsDropdown as p,initSessionsMenu as U}from"./sessions-menu.js";export{reflectBusy}from"./composer.js";export{loadCommands}from"./cmdk.js";export{renderSessionsDropdown}from"./sessions-menu.js";const V={working:"●",waiting:"◉",queued:"○",failed:"✗",dead:"✗"};function T(e){return e==="working"?o("web.tab.working"):e==="waiting"?o("web.tab.waiting"):e==="queued"?o("web.tab.queued"):e==="failed"?o("web.tab.failed"):e==="dead"?o("web.tab.gone"):""}export function activeTab(){return a.tabs.find(e=>e.id===a.activeTabId)||null}export function setTabStatus(e,t){if(!e)return;const i=a.tabs.find(n=>n.id===e);!i||i.status===t||i.dead||(i.status=t,c())}export function markTabDead(e){const t=a.tabs.find(s=>s.id===e);if(!t||t.dead)return;t.dead=!0,t.status="dead",t.historyLoaded=!0,t.historyFailed=!1,t.planCard=null,v(!1),t.feed.replaceChildren();const i=u("div","warnline","✗ "+o("web.session.gone"));x(t.feed,i);const n=E(o("web.topbar.close"),"",()=>closeTab(e));x(t.feed,n),t.feed===F()&&w(),c()}function c(){l.textContent="",a.tabs.forEach(e=>{const t=e.id===a.activeTabId,i=e.name||e.id,n=e.kind==="session"?String(e.id):i,s=u("div","stab"+(t?" on":""));if(s.setAttribute("role","tab"),s.setAttribute("tabindex",t?"0":"-1"),s.setAttribute("aria-selected",t?"true":"false"),s.setAttribute("aria-label",i),s.title=n,e.status&&e.status!=="idle"){const d=u("span","dot st-"+e.status,V[e.status]||"●");d.title=T(e.status),d.setAttribute("aria-label",T(e.status)),s.appendChild(d)}const r=u("span","name",i);r.title=n,s.appendChild(r);const f=u("button","stab-x");f.type="button",f.title=o("web.topbar.close"),f.setAttribute("aria-label",o("web.topbar.close")+": "+i),f.appendChild(I("x")),f.addEventListener("click",d=>{d.stopPropagation(),closeTab(e.id)}),s.appendChild(f);const g=()=>openTab(e.id,e.name,e.kind,!0);s.addEventListener("click",g),s.addEventListener("keydown",d=>{(d.key==="Enter"||d.key===" ")&&(d.preventDefault(),g())}),l.appendChild(s)}),j(),_()}function j(){const e=l.querySelector(".stab.on");if(!e)return;const t=Math.max(0,e.offsetLeft-4);(t<l.scrollLeft||t+e.offsetWidth>l.scrollLeft+l.clientWidth)&&(l.scrollLeft=t)}l.addEventListener("wheel",e=>{e.deltaY!==0&&(e.preventDefault(),l.scrollLeft+=e.deltaY)},{passive:!1});export function updateTabName(e,t){if(!e||!t)return;const i=a.tabs.find(s=>s.id===e);i&&i.name!==t&&(i.name=t,c());const n=b.sessions.find(s=>s.id===e);n&&n.name!==t&&(n.name=t,p())}export function openTab(e,t,i,n){C();let s=a.tabs.find(r=>r.id===e);if(s?t&&(s.name=t):(s={id:e,name:t||e,kind:i||"session",feed:q(),historyLoaded:!1,historyLoading:!1,historyFailed:!1,dead:!1,planCard:null,draft:"",attachments:[],status:"idle"},a.tabs.push(s)),a.activeTabId=e,a.liveTabId=e,(i||"session")==="session"&&(b.activeSessionId=e,P(e),A.ensureTabState?.(e)),k.replaceChildren(s.feed),L(s),c(),p(),w(),s.kind==="session"&&!n&&(s.historyLoaded=!0),n&&s.kind==="session"){if(s.dead){h();return}(!B(s.feed)||s.historyFailed)&&(s.historyLoaded=!1),v(!0),m("/api/sessions/activate",{method:"POST",body:{id:e}}).then(r=>{if(r.status===404){markTabDead(e),loadSessions();return}loadSessions()}),O(s).then(r=>{r&&r.gone&&markTabDead(s.id)})}h()}export function closeTab(e){const t=a.tabs.findIndex(n=>n.id===e);if(t===-1)return;const i=a.activeTabId===e;if(i&&C(),a.tabs=a.tabs.filter(n=>n.id!==e),i){const n=a.tabs[Math.min(t,a.tabs.length-1)];n?(a.activeTabId=n.id,a.liveTabId=n.id,k.replaceChildren(n.feed),L(n),w()):(a.activeTabId=null,a.liveTabId=null,S&&(S.value=""),a.attachments=[],Y(),D())}c(),p(),h()}async function y(){const e=await m("/api/sessions",{method:"POST",body:{}});if(e.ok&&e.data&&e.data.session&&e.data.session.id){const t=e.data.session;openTab(t.id,t.name||String(t.id),"session",!1),await loadSessions(),v(!0);return}a.newSeq++,openTab("new"+a.newSeq,o("web.empty.title"),"new",!1),M(o("web.sessions.create_failed"),!0)}document.getElementById("newChat")?.addEventListener("click",()=>{y()});export function reconcileTabStatuses(){const e=b.runningSessionIds||[],t=b.queued||{};let i=!1;a.tabs.forEach(n=>{if(n.kind!=="session"||n.status==="waiting"||n.status==="failed"||n.status==="dead")return;const s=e.includes(n.id)?"working":t[n.id]>0?"queued":"idle";n.status!==s&&(n.status=s,i=!0)}),i&&c()}export async function loadSessions(){const e=await m("/api/sessions");!e.ok||!e.data||(b.sessions=Array.isArray(e.data.sessions)?e.data.sessions:[],p())}document.addEventListener("click",()=>{document.querySelectorAll("[data-sel]").forEach(e=>e.classList.remove("open")),H(!1),W()}),N({activeTab,setTabStatus,newChatTab:y,markTabDead,openTab,closeTab,statusTitle:T}),U({openTab,closeTab,loadSessions,newChatTab:y});
@@ -1 +1 @@
1
- import{el as c,makeToggle as y}from"../dom.js";import{tagsEditor as u,checkboxEditor as g,tableEditor as b}from"./editors.js";export function validateJson(i){const e=String(i??"").trim();if(!e)return{ok:!1,error:"required"};try{return JSON.parse(e),{ok:!0}}catch(s){return{ok:!1,error:s instanceof Error?s.message:"invalid JSON"}}}function l(i,e){const s=c("input");return s.setAttribute("aria-label",i),s.value=e,s}export function controlFor(i,e,s){if(i.readOnly){const t=c("span","val dim",e==null?"—":String(e));return e!=null&&(t.title=String(e)),t}if(i.valueType==="bool")return y(!!e,t=>(s.post(t),!0));if(i.valueType==="enum"){const t=c("select");return t.setAttribute("aria-label",s.label),(i.enum||[]).forEach(n=>{const o=c("option","",s.enumLabel(n));o.value=n,n===e&&(o.selected=!0),t.appendChild(o)}),t.addEventListener("change",()=>s.post(t.value)),t}if(i.valueType==="int"){const t=l(s.label,e==null?"":String(e));return t.type="number",i.min!=null&&(t.min=String(i.min)),i.max!=null&&(t.max=String(i.max)),i.step!=null&&(t.step=String(i.step)),t.addEventListener("change",()=>s.post(t.value)),t}if(i.valueType==="share"){const t=l(s.label,e==null?"":Math.round(Number(e)*100)+"%");return t.addEventListener("change",()=>s.post(t.value)),t}const r=i.editor;if(r&&r.kind==="tags"&&(i.valueType==="list"||i.valueType==="json"))return u(e,s,r);if(r&&r.kind==="checkboxes")return g(e,s,r);if(r&&r.kind==="table")return b(e,s,r);if(i.valueType==="list"){const t=Array.isArray(e)?e.join(", "):e==null?"":String(e),n=l(s.label,t);return n.placeholder="a, b, c",n.addEventListener("change",()=>s.post(n.value)),n}if(i.valueType==="json"){const t=c("div","jsonctl"),n=c("textarea");n.setAttribute("aria-label",s.label),n.rows=3,n.value=e==null?"":JSON.stringify(e,null,2);const o=c("div","json-err");return o.hidden=!0,n.addEventListener("input",()=>{const d=validateJson(n.value);o.hidden=d.ok,o.textContent=d.ok?"":String(d.error||"invalid JSON")}),n.addEventListener("change",()=>{validateJson(n.value).ok?s.post(n.value.trim()):n.focus()}),t.appendChild(n),t.appendChild(o),t}const a=l(s.label,e==null?"":String(e));return a.addEventListener("change",()=>s.post(a.value)),a}export const PANEL_SECTIONS={general:[{id:"general.interface",titleKey:"web.settings.sec.general.interface",keys:["locale","ui.verbosity"]},{id:"general.agent",titleKey:"web.settings.sec.general.agent",descKey:"web.settings.sec.general.agent.desc",keys:["maxToolIterations","stuckThreshold","autoPlan","execution.autoAdvance"]},{id:"general.provider",titleKey:"web.settings.sec.general.provider",keys:["provider.fallback"]}],model:[{id:"model.tuning",titleKey:"web.settings.sec.model.tuning",descKey:"web.settings.sec.model.tuning.desc",keys:["provider.maxCompletionTokens","modelLoad.autoLoad","modelLoad.flashAttention","modelLoad.evalBatchSize","modelLoad.offloadKvCacheToGpu"]},{id:"model.capabilities",titleKey:"web.settings.sec.model.capabilities",descKey:"web.settings.sec.model.capabilities.desc",keys:["modelCapabilities"]}],context:[{id:"context.window",titleKey:"web.settings.sec.context.window",descKey:"web.settings.sec.context.window.desc",keys:["contextWindow","contextBudget.systemPrompt","contextBudget.responseReserve"]},{id:"context.compaction",titleKey:"web.settings.sec.context.compaction",descKey:"web.settings.sec.context.compaction.desc",keys:["contextBudget.compactionThreshold","contextBudget.compactionMinLoad","contextBudget.policy","contextBudget.compactionSummary","contextBudget.overflowCooldown","contextBudget.toolResultKeep","contextBudget.calibrationWarmup","contextBudget.ratioFloor"]},{id:"context.instructions",titleKey:"web.settings.sec.context.instructions",descKey:"web.settings.sec.context.instructions.desc",keys:["instructions.summarize"]},{id:"context.indexer",titleKey:"web.settings.sec.context.indexer",descKey:"web.settings.sec.context.indexer.desc",keys:["indexer.maxFiles","indexer.respectGitignore","indexer.mapTokenBudget"]},{id:"context.log",titleKey:"web.settings.sec.context.log",descKey:"web.settings.sec.context.log.desc",keys:["contextLog.enabled","contextLog.redact"]}],reasoning:[{id:"reasoning.display",titleKey:"web.settings.sec.reasoning.display",keys:["showReasoning"]},{id:"reasoning.effort",titleKey:"web.settings.sec.reasoning.effort",descKey:"web.settings.sec.reasoning.effort.desc",keys:["reasoning.mode","reasoning.min","reasoning.max","reasoning.baseline","reasoning.overrideCooldown"]}],agent:[{id:"agent.moe",titleKey:"web.settings.sec.agent.moe",descKey:"web.settings.sec.agent.moe.desc",keys:["moe.enabled","moe.maxReplanCycles","experts"]},{id:"agent.critic",titleKey:"web.settings.sec.agent.critic",descKey:"web.settings.sec.agent.critic.desc",keys:["critic.enabled","critic.model","critic.provider","critic.tools","critic.maxCalls","critic.maxRepairCycles","critic.timeoutMs"]},{id:"agent.tools",titleKey:"web.settings.sec.agent.tools",descKey:"web.settings.sec.agent.tools.desc",keys:["tools.enableOnDemand","tools.defaultTags","tools.grepEngine","skills.budget"]},{id:"agent.subagent",titleKey:"web.settings.sec.agent.subagent",descKey:"web.settings.sec.agent.subagent.desc",keys:["subagent.resultMode","subagent.maxSummaryChars","subagent.maxParallelChunkQueries","subagent.stableSystemPrompt","subagent.timeoutMs"]},{id:"agent.hallucination",titleKey:"web.settings.sec.agent.hallucination",descKey:"web.settings.sec.agent.hallucination.desc",keys:["hallucination.judge.enabled"]}],network:[{id:"network.web",titleKey:"web.settings.sec.network.web",descKey:"web.settings.sec.network.web.desc",keys:["webSearch.enabled"]},{id:"network.error",titleKey:"web.settings.sec.network.error",descKey:"web.settings.sec.network.error.desc",keys:["errorWebSearch.enabled","errorWebSearch.threshold","errorWebSearch.maxResults","errorWebSearch.maxQueryChars","errorWebSearch.maxSearchesPerSession","errorWebSearch.requestTimeoutMs"]}],browser:[{id:"browser.main",titleKey:"web.settings.sec.browser.main",keys:["browser.enabled","browser.headless"]},{id:"browser.limits",titleKey:"web.settings.sec.browser.limits",descKey:"web.settings.sec.browser.limits.desc",keys:["browser.maxElements","browser.maxContentChars","browser.maxConsoleEntries","browser.maxConsoleLineChars"]},{id:"browser.viewport",titleKey:"web.settings.sec.browser.viewport",keys:["browser.viewportWidth","browser.viewportHeight","browser.navigationTimeout"]}],sessions:[{id:"sessions.main",titleKey:"web.settings.sec.sessions.main",descKey:"web.settings.sec.sessions.main.desc",keys:["session.autoSave","session.autoName","session.maxSessions","session.isolateMemory"]},{id:"sessions.isolation",titleKey:"web.settings.sec.sessions.isolation",descKey:"web.settings.sec.sessions.isolation.desc",keys:["sessionIsolation.enabled","sessionIsolation.isolatePlugins","sessionIsolation.isolateTempFiles"]}],ui:[{id:"ui.chat",titleKey:"web.settings.sec.ui.chat",descKey:"web.settings.sec.ui.chat.desc",keys:["ui.fontSize","ui.activityRail","ui.spinner","ui.showContextStats","ui.showCompaction"]}],security:[{id:"security.switches",titleKey:"web.settings.sec.security.switches",descKey:"web.settings.sec.security.switches.desc",keys:["security.enabled","security.bash.enabled","security.bash.blockDangerousFlags","security.paths.enabled","security.contentScan.enabled","security.network.enabled","security.sessionEncryption.enabled"]},{id:"security.limits",titleKey:"web.settings.sec.security.limits",descKey:"web.settings.sec.security.limits.desc",keys:["security.maxRecursionDepth","security.maxFileOperations","security.rateLimits.maxRequestsPerMinute","security.rateLimits.maxParallelTasks","security.network.requestTimeout","security.network.maxResponseSize"]},{id:"security.bash",titleKey:"web.settings.sec.security.bash",descKey:"web.settings.sec.security.bash.desc",keys:["security.bash.blacklist","security.bash.whitelist","security.bash.dangerousFlags","security.bash.dangerousOperators"]},{id:"security.paths",titleKey:"web.settings.sec.security.paths",descKey:"web.settings.sec.security.paths.desc",keys:["security.paths.denied","security.paths.allowed"]},{id:"security.network",titleKey:"web.settings.sec.security.network",descKey:"web.settings.sec.security.network.desc",keys:["security.network.deniedDomains","security.network.allowedDomains"]},{id:"security.scan",titleKey:"web.settings.sec.security.scan",descKey:"web.settings.sec.security.scan.desc",keys:["security.contentScan.dangerousPatterns"]},{id:"security.audit",titleKey:"web.settings.sec.security.audit",descKey:"web.settings.sec.security.audit.desc",keys:["security.auditNotifier.enabled","security.auditNotifier.filePath","security.auditNotifier.webhookUrl","security.auditNotifier.webhookTimeout","security.auditNotifier.minSeverity","security.auditNotifier.maxRetries","security.auditNotifier.eventTypes"]},{id:"security.encryption",titleKey:"web.settings.sec.security.encryption",descKey:"web.settings.sec.security.encryption.desc",keys:["security.sessionEncryption.encryptHistory","security.sessionEncryption.encryptSessionLog","security.sessionEncryption.keyPath"]}],advanced:[{id:"advanced.diagnostics",titleKey:"web.settings.sec.advanced.diagnostics",descKey:"web.settings.sec.advanced.diagnostics.desc",keys:["logLevel","version"]},{id:"advanced.retry",titleKey:"web.settings.sec.advanced.retry",descKey:"web.settings.sec.advanced.retry.desc",keys:["retry.maxRetries","retry.baseDelay","retry.maxDelay","retry.maxStreamRetries","retry.noDataTimeoutMs"]},{id:"advanced.service",titleKey:"web.settings.sec.advanced.service",descKey:"web.settings.sec.advanced.service.desc",keys:["pricing.enabled","pricing.overrides","updater.enabled","updater.checkOnStart","updater.autoInstall","updater.intervalMs"]}],plugins:[{id:"plugins.lsp",titleKey:"web.settings.sec.plugins.lsp",descKey:"web.settings.sec.plugins.lsp.desc",keys:["lsp.enabled","lsp.offline"]}]};export function keyToPanel(){const i=new Map;for(const[e,s]of Object.entries(PANEL_SECTIONS))for(const r of s)for(const a of r.keys)i.set(a,e);return i}
1
+ import{el as a,makeToggle as g}from"../dom.js";import{tagsEditor as b,checkboxEditor as p,tableEditor as w}from"./editors.js";export function validateJson(i){const e=String(i??"").trim();if(!e)return{ok:!1,error:"required"};try{return JSON.parse(e),{ok:!0}}catch(s){return{ok:!1,error:s instanceof Error?s.message:"invalid JSON"}}}function u(i,e){const s=a("input");return s.setAttribute("aria-label",i),s.value=e,s}function m(i,e,s){const l=s.optionsSource==="providers"?e.providerOptions||[]:e.modelOptions||[],t=typeof i=="string"?i:"";if(!l.length){const o=u(e.label,t);return o.addEventListener("change",()=>e.post(o.value)),o}const n=a("select");n.setAttribute("aria-label",e.label);const c=a("option","",e.emptyLabel||"—");c.value="",n.appendChild(c);const d=new Set([""]);if(l.forEach(o=>{if(d.has(o))return;d.add(o);const y=a("option","",o);y.value=o,o===t&&(y.selected=!0),n.appendChild(y)}),t&&!d.has(t)){const o=a("option","",t);o.value=t,o.selected=!0,n.appendChild(o)}return n.value=t||"",n.addEventListener("change",()=>e.post(n.value)),n}export function controlFor(i,e,s){if(i.readOnly){const t=a("span","val dim",e==null?"—":String(e));return e!=null&&(t.title=String(e)),t}if(i.valueType==="bool")return g(!!e,t=>(s.post(t),!0));if(i.valueType==="enum"){const t=a("select");return t.setAttribute("aria-label",s.label),(i.enum||[]).forEach(n=>{const c=a("option","",s.enumLabel(n));c.value=n,n===e&&(c.selected=!0),t.appendChild(c)}),t.addEventListener("change",()=>s.post(t.value)),t}if(i.valueType==="int"){const t=u(s.label,e==null?"":String(e));return t.type="number",i.min!=null&&(t.min=String(i.min)),i.max!=null&&(t.max=String(i.max)),i.step!=null&&(t.step=String(i.step)),t.addEventListener("change",()=>s.post(t.value)),t}if(i.valueType==="share"){const t=u(s.label,e==null?"":Math.round(Number(e)*100)+"%");return t.addEventListener("change",()=>s.post(t.value)),t}const r=i.editor;if(r&&r.kind==="select")return m(e,s,r);if(r&&r.kind==="tags"&&(i.valueType==="list"||i.valueType==="json"))return b(e,s,r);if(r&&r.kind==="checkboxes")return p(e,s,r);if(r&&r.kind==="table")return w(e,s,r);if(i.valueType==="list"){const t=Array.isArray(e)?e.join(", "):e==null?"":String(e),n=u(s.label,t);return n.placeholder="a, b, c",n.addEventListener("change",()=>s.post(n.value)),n}if(i.valueType==="json"){const t=a("div","jsonctl"),n=a("textarea");n.setAttribute("aria-label",s.label),n.rows=3,n.value=e==null?"":JSON.stringify(e,null,2);const c=a("div","json-err");return c.hidden=!0,n.addEventListener("input",()=>{const d=validateJson(n.value);c.hidden=d.ok,c.textContent=d.ok?"":String(d.error||"invalid JSON")}),n.addEventListener("change",()=>{validateJson(n.value).ok?s.post(n.value.trim()):n.focus()}),t.appendChild(n),t.appendChild(c),t}const l=u(s.label,e==null?"":String(e));return l.addEventListener("change",()=>s.post(l.value)),l}export const PANEL_SECTIONS={general:[{id:"general.interface",titleKey:"web.settings.sec.general.interface",keys:["locale","ui.verbosity"]},{id:"general.agent",titleKey:"web.settings.sec.general.agent",descKey:"web.settings.sec.general.agent.desc",keys:["maxToolIterations","stuckThreshold","autoPlan","execution.autoAdvance"]},{id:"general.provider",titleKey:"web.settings.sec.general.provider",keys:["provider.fallback"]}],model:[{id:"model.tuning",titleKey:"web.settings.sec.model.tuning",descKey:"web.settings.sec.model.tuning.desc",keys:["provider.maxCompletionTokens","modelLoad.autoLoad","modelLoad.flashAttention","modelLoad.evalBatchSize","modelLoad.offloadKvCacheToGpu"]},{id:"model.capabilities",titleKey:"web.settings.sec.model.capabilities",descKey:"web.settings.sec.model.capabilities.desc",keys:["modelCapabilities"]}],context:[{id:"context.window",titleKey:"web.settings.sec.context.window",descKey:"web.settings.sec.context.window.desc",keys:["contextWindow","contextBudget.systemPrompt","contextBudget.responseReserve"]},{id:"context.compaction",titleKey:"web.settings.sec.context.compaction",descKey:"web.settings.sec.context.compaction.desc",keys:["contextBudget.compactionThreshold","contextBudget.compactionMinLoad","contextBudget.policy","contextBudget.compactionSummary","contextBudget.overflowCooldown","contextBudget.toolResultKeep","contextBudget.calibrationWarmup","contextBudget.ratioFloor"]},{id:"context.instructions",titleKey:"web.settings.sec.context.instructions",descKey:"web.settings.sec.context.instructions.desc",keys:["instructions.summarize"]},{id:"context.indexer",titleKey:"web.settings.sec.context.indexer",descKey:"web.settings.sec.context.indexer.desc",keys:["indexer.maxFiles","indexer.respectGitignore","indexer.mapTokenBudget"]},{id:"context.log",titleKey:"web.settings.sec.context.log",descKey:"web.settings.sec.context.log.desc",keys:["contextLog.enabled","contextLog.redact"]}],reasoning:[{id:"reasoning.display",titleKey:"web.settings.sec.reasoning.display",keys:["showReasoning"]},{id:"reasoning.effort",titleKey:"web.settings.sec.reasoning.effort",descKey:"web.settings.sec.reasoning.effort.desc",keys:["reasoning.mode","reasoning.min","reasoning.max","reasoning.baseline","reasoning.overrideCooldown"]}],agent:[{id:"agent.moe",titleKey:"web.settings.sec.agent.moe",descKey:"web.settings.sec.agent.moe.desc",keys:["moe.enabled","moe.maxReplanCycles","orchestrator.model","orchestrator.provider","orchestrator.contextWindow","experts"]},{id:"agent.critic",titleKey:"web.settings.sec.agent.critic",descKey:"web.settings.sec.agent.critic.desc",keys:["critic.enabled","critic.model","critic.provider","critic.tools","critic.maxCalls","critic.maxRepairCycles","critic.timeoutMs"]},{id:"agent.tools",titleKey:"web.settings.sec.agent.tools",descKey:"web.settings.sec.agent.tools.desc",keys:["tools.enableOnDemand","tools.defaultTags","tools.grepEngine","skills.budget"]},{id:"agent.subagent",titleKey:"web.settings.sec.agent.subagent",descKey:"web.settings.sec.agent.subagent.desc",keys:["subagent.resultMode","subagent.maxSummaryChars","subagent.maxParallelChunkQueries","subagent.stableSystemPrompt","subagent.timeoutMs"]},{id:"agent.hallucination",titleKey:"web.settings.sec.agent.hallucination",descKey:"web.settings.sec.agent.hallucination.desc",keys:["hallucination.judge.enabled"]}],network:[{id:"network.web",titleKey:"web.settings.sec.network.web",descKey:"web.settings.sec.network.web.desc",keys:["webSearch.enabled"]},{id:"network.error",titleKey:"web.settings.sec.network.error",descKey:"web.settings.sec.network.error.desc",keys:["errorWebSearch.enabled","errorWebSearch.threshold","errorWebSearch.maxResults","errorWebSearch.maxQueryChars","errorWebSearch.maxSearchesPerSession","errorWebSearch.requestTimeoutMs"]}],browser:[{id:"browser.main",titleKey:"web.settings.sec.browser.main",keys:["browser.enabled","browser.headless"]},{id:"browser.limits",titleKey:"web.settings.sec.browser.limits",descKey:"web.settings.sec.browser.limits.desc",keys:["browser.maxElements","browser.maxContentChars","browser.maxConsoleEntries","browser.maxConsoleLineChars"]},{id:"browser.viewport",titleKey:"web.settings.sec.browser.viewport",keys:["browser.viewportWidth","browser.viewportHeight","browser.navigationTimeout"]}],sessions:[{id:"sessions.main",titleKey:"web.settings.sec.sessions.main",descKey:"web.settings.sec.sessions.main.desc",keys:["session.autoSave","session.autoName","session.maxSessions","session.isolateMemory"]},{id:"sessions.isolation",titleKey:"web.settings.sec.sessions.isolation",descKey:"web.settings.sec.sessions.isolation.desc",keys:["sessionIsolation.enabled","sessionIsolation.isolatePlugins","sessionIsolation.isolateTempFiles"]}],ui:[{id:"ui.chat",titleKey:"web.settings.sec.ui.chat",descKey:"web.settings.sec.ui.chat.desc",keys:["ui.fontSize","ui.activityRail","ui.spinner","ui.showContextStats","ui.showCompaction"]},{id:"ui.images",titleKey:"web.settings.sec.ui.images",descKey:"web.settings.sec.ui.images.desc",keys:["images.maxCount","images.maxBytes"]}],security:[{id:"security.switches",titleKey:"web.settings.sec.security.switches",descKey:"web.settings.sec.security.switches.desc",keys:["security.enabled","security.bash.enabled","security.bash.blockDangerousFlags","security.paths.enabled","security.contentScan.enabled","security.network.enabled","security.sessionEncryption.enabled"]},{id:"security.limits",titleKey:"web.settings.sec.security.limits",descKey:"web.settings.sec.security.limits.desc",keys:["security.maxRecursionDepth","security.maxFileOperations","security.rateLimits.maxRequestsPerMinute","security.rateLimits.maxParallelTasks","security.network.requestTimeout","security.network.maxResponseSize"]},{id:"security.bash",titleKey:"web.settings.sec.security.bash",descKey:"web.settings.sec.security.bash.desc",keys:["security.bash.blacklist","security.bash.whitelist","security.bash.dangerousFlags","security.bash.dangerousOperators"]},{id:"security.paths",titleKey:"web.settings.sec.security.paths",descKey:"web.settings.sec.security.paths.desc",keys:["security.paths.denied","security.paths.allowed"]},{id:"security.network",titleKey:"web.settings.sec.security.network",descKey:"web.settings.sec.security.network.desc",keys:["security.network.deniedDomains","security.network.allowedDomains"]},{id:"security.scan",titleKey:"web.settings.sec.security.scan",descKey:"web.settings.sec.security.scan.desc",keys:["security.contentScan.dangerousPatterns"]},{id:"security.audit",titleKey:"web.settings.sec.security.audit",descKey:"web.settings.sec.security.audit.desc",keys:["security.auditNotifier.enabled","security.auditNotifier.filePath","security.auditNotifier.webhookUrl","security.auditNotifier.webhookTimeout","security.auditNotifier.minSeverity","security.auditNotifier.maxRetries","security.auditNotifier.eventTypes"]},{id:"security.encryption",titleKey:"web.settings.sec.security.encryption",descKey:"web.settings.sec.security.encryption.desc",keys:["security.sessionEncryption.encryptHistory","security.sessionEncryption.encryptSessionLog","security.sessionEncryption.keyPath"]}],advanced:[{id:"advanced.diagnostics",titleKey:"web.settings.sec.advanced.diagnostics",descKey:"web.settings.sec.advanced.diagnostics.desc",keys:["logLevel","version"]},{id:"advanced.retry",titleKey:"web.settings.sec.advanced.retry",descKey:"web.settings.sec.advanced.retry.desc",keys:["retry.maxRetries","retry.baseDelay","retry.maxDelay","retry.maxStreamRetries","retry.noDataTimeoutMs"]},{id:"advanced.service",titleKey:"web.settings.sec.advanced.service",descKey:"web.settings.sec.advanced.service.desc",keys:["pricing.enabled","pricing.overrides","updater.enabled","updater.checkOnStart","updater.autoInstall","updater.intervalMs"]}],plugins:[{id:"plugins.lsp",titleKey:"web.settings.sec.plugins.lsp",descKey:"web.settings.sec.plugins.lsp.desc",keys:["lsp.enabled","lsp.offline"]}]};export function keyToPanel(){const i=new Map;for(const[e,s]of Object.entries(PANEL_SECTIONS))for(const r of s)for(const l of r.keys)i.set(l,e);return i}
@@ -1 +1 @@
1
- import{el as l}from"../dom.js";import{t as w}from"../i18n.js";export function tagsEditor(y,i,g){const s=Array.isArray(y)?y.map(String):[],u=l("div","tagsctl ctl-block"),d=l("div","tags-list"),h=()=>i.post(JSON.stringify(s)),r=()=>{d.textContent="",s.forEach((o,E)=>{const A=l("span","tag-chip"),v=l("span","tag-text",o);v.title=o,A.appendChild(v);const C=l("button","tag-rm linkbtn","✕");C.type="button",C.setAttribute("aria-label",`${i.label}: remove ${o}`),C.addEventListener("click",()=>{s.splice(E,1),h(),r()}),A.appendChild(C),d.appendChild(A)}),s.length||d.appendChild(l("span","tags-empty",i.emptyLabel||"—"))};r(),u.appendChild(d);const f=l("div","tags-add"),p=l("input");p.type="text",p.placeholder=g.placeholder||"…",p.setAttribute("aria-label",i.label);const k=()=>{const o=p.value.trim();if(!o||s.includes(o)){p.select();return}s.push(o),h(),r(),p.value="",p.focus()};p.addEventListener("keydown",o=>{o.key==="Enter"&&(o.preventDefault(),k())}),f.appendChild(p);const b=l("button","tags-plus linkbtn","+");return b.type="button",b.setAttribute("aria-label",`${i.label}: add`),b.addEventListener("click",k),f.appendChild(b),u.appendChild(f),u}export function checkboxEditor(y,i,g){const s=new Set(Array.isArray(y)?y.map(String):[]),u=l("div","checksctl ctl-block");return(g.options||[]).forEach(d=>{const h=l("label","check-line"),r=l("input");r.type="checkbox",r.checked=s.has(d),r.addEventListener("change",()=>{r.checked?s.add(d):s.delete(d),i.post(JSON.stringify([...s]))}),h.appendChild(r),h.appendChild(l("span","",i.enumLabel?i.enumLabel(d):d)),u.appendChild(h)}),u}export function tableEditor(y,i,g){const s=g.columns||[],u=s[0],d=s.slice(1),h=y&&typeof y=="object"&&!Array.isArray(y)?y:{},r=Object.entries(h).map(([e,t])=>({name:e,val:t&&typeof t=="object"?{...t}:{},error:""})),f=l("div","tablectl ctl-block"),p=l("div","table-head");s.forEach(e=>p.appendChild(l("span","table-th",i.fieldLabel(e.key)))),f.appendChild(p);const k=l("div","table-body"),b=l("div","json-err");b.hidden=!0;const o=e=>e.array===!0,E=e=>e===void 0||e===""||Array.isArray(e)&&e.length===0,A=()=>{const e={};for(const t of r){if(!t.name)return{ok:!1,reason:"empty name"};if(e[t.name]!==void 0)return{ok:!1,reason:`duplicate: ${t.name}`};const a={};for(const n of d){const c=t.val[n.key];if(!(n.optional&&E(c))){if(!n.optional&&E(c))return{ok:!1,reason:`${t.name}: ${i.fieldLabel(n.key)}`};if(n.type==="number"){const N=Number(c);if(!Number.isFinite(N)||N<0)return{ok:!1,reason:`${t.name}: ${i.fieldLabel(n.key)}`};a[n.key]=N}else n.type==="bool"?c===!0&&(a[n.key]=!0):a[n.key]=o(n)?c:String(c)}}e[t.name]=a}return{ok:!0,value:e}},v=()=>{const e=A();return e.ok?(b.hidden=!0,b.textContent="",i.post(JSON.stringify(e.value)),!0):(b.hidden=!1,b.textContent=w("web.settings.table_invalid",{field:e.reason||""}),!1)},C=(e,t,a)=>{if(t.type==="bool")a.checked?e.val[t.key]=!0:delete e.val[t.key];else if(t.type==="number"){const n=a.value.trim();n===""?delete e.val[t.key]:e.val[t.key]=Number(n)}else{const n=a.value.trim();n===""?delete e.val[t.key]:e.val[t.key]=o(t)?n.split(",").map(c=>c.trim()).filter(Boolean):n}},O=(e,t)=>{const a=l("input"),n=e.val[t.key];return t.type==="bool"?(a.type="checkbox",a.checked=n===!0):t.type==="number"?(a.type="number",a.value=n==null?"":String(n)):(a.type="text",a.value=Array.isArray(n)?n.join(", "):n==null?"":String(n)),a.setAttribute("aria-label",`${e.name}: ${i.fieldLabel(t.key)}`),a.addEventListener("change",()=>{C(e,t,a),v()}),a},$=()=>{k.textContent="",r.forEach(e=>{const t=l("div","table-row");t.dataset.key=e.name;const a=l("input");a.type="text",a.value=e.name,a.placeholder=i.fieldLabel(u.key),a.setAttribute("aria-label",i.fieldLabel(u.key)),a.addEventListener("change",()=>{e.name=a.value.trim(),v()&&(t.dataset.key=e.name)}),t.appendChild(a),d.forEach(c=>t.appendChild(O(e,c)));const n=l("button","table-rm linkbtn","✕");n.type="button",n.setAttribute("aria-label",`${i.label}: remove ${e.name}`),n.addEventListener("click",()=>{r.splice(r.indexOf(e),1),v()&&$()}),t.appendChild(n),k.appendChild(t)})};$(),f.appendChild(k),f.appendChild(b);const S=l("div","table-add"),m=l("input");m.type="text",m.placeholder=i.fieldLabel(u.key),m.setAttribute("aria-label",`${i.label}: new ${i.fieldLabel(u.key)}`);const j=()=>{const e=m.value.trim();if(!e||r.some(c=>c.name===e)){m.select();return}const t={};r.push({name:e,val:t,error:""}),m.value="",$();const a=k.lastElementChild,n=a&&a.querySelectorAll("input")[1];n&&n.focus()};m.addEventListener("keydown",e=>{e.key==="Enter"&&(e.preventDefault(),j())}),S.appendChild(m);const L=l("button","table-plus linkbtn","+");return L.type="button",L.setAttribute("aria-label",`${i.label}: add`),L.addEventListener("click",j),S.appendChild(L),f.appendChild(S),f}
1
+ import{el as l}from"../dom.js";import{t as q}from"../i18n.js";export function tagsEditor(f,i,A){const r=Array.isArray(f)?f.map(String):[],u=l("div","tagsctl ctl-block"),d=l("div","tags-list"),k=()=>i.post(JSON.stringify(r)),s=()=>{d.textContent="",r.forEach((o,O)=>{const $=l("span","tag-chip"),g=l("span","tag-text",o);g.title=o,$.appendChild(g);const E=l("button","tag-rm linkbtn","✕");E.type="button",E.setAttribute("aria-label",`${i.label}: remove ${o}`),E.addEventListener("click",()=>{r.splice(O,1),k(),s()}),$.appendChild(E),d.appendChild($)}),r.length||d.appendChild(l("span","tags-empty",i.emptyLabel||"—"))};s(),u.appendChild(d);const y=l("div","tags-add"),c=l("input");c.type="text",c.placeholder=A.placeholder||"…",c.setAttribute("aria-label",i.label);const C=()=>{const o=c.value.trim();if(!o||r.includes(o)){c.select();return}r.push(o),k(),s(),c.value="",c.focus()};c.addEventListener("keydown",o=>{o.key==="Enter"&&(o.preventDefault(),C())}),y.appendChild(c);const b=l("button","tags-plus linkbtn","+");return b.type="button",b.setAttribute("aria-label",`${i.label}: add`),b.addEventListener("click",C),y.appendChild(b),u.appendChild(y),u}export function checkboxEditor(f,i,A){const r=new Set(Array.isArray(f)?f.map(String):[]),u=l("div","checksctl ctl-block");return(A.options||[]).forEach(d=>{const k=l("label","check-line"),s=l("input");s.type="checkbox",s.checked=r.has(d),s.addEventListener("change",()=>{s.checked?r.add(d):r.delete(d),i.post(JSON.stringify([...r]))}),k.appendChild(s),k.appendChild(l("span","",i.enumLabel?i.enumLabel(d):d)),u.appendChild(k)}),u}export function tableEditor(f,i,A){const r=A.columns||[],u=r[0],d=r.slice(1),k=f&&typeof f=="object"&&!Array.isArray(f)?f:{},s=Object.entries(k).map(([e,t])=>({name:e,val:t&&typeof t=="object"?{...t}:{},error:""})),y=l("div","tablectl ctl-block"),c=l("div","table-head");r.forEach(e=>c.appendChild(l("span","table-th",i.fieldLabel(e.key)))),y.appendChild(c);const C=l("div","table-body"),b=l("div","json-err");b.hidden=!0;const o=e=>e.array===!0,O=e=>e===void 0||e===""||Array.isArray(e)&&e.length===0,$=()=>{const e={};for(const t of s){if(!t.name)return{ok:!1,reason:"empty name"};if(e[t.name]!==void 0)return{ok:!1,reason:`duplicate: ${t.name}`};const a={};for(const n of d){const p=t.val[n.key];if(!(n.optional&&O(p))){if(!n.optional&&O(p))return{ok:!1,reason:`${t.name}: ${i.fieldLabel(n.key)}`};if(n.type==="number"){const S=Number(p);if(!Number.isFinite(S)||S<0)return{ok:!1,reason:`${t.name}: ${i.fieldLabel(n.key)}`};a[n.key]=S}else n.type==="bool"?p===!0&&(a[n.key]=!0):a[n.key]=o(n)?p:String(p)}}e[t.name]=a}return{ok:!0,value:e}},g=()=>{const e=$();return e.ok?(b.hidden=!0,b.textContent="",i.post(JSON.stringify(e.value)),!0):(b.hidden=!1,b.textContent=q("web.settings.table_invalid",{field:e.reason||""}),!1)},E=(e,t,a)=>{if(t.type==="bool")a.checked?e.val[t.key]=!0:delete e.val[t.key];else if(t.type==="number"){const n=a.value.trim();n===""?delete e.val[t.key]:e.val[t.key]=Number(n)}else{const n=a.value.trim();n===""?delete e.val[t.key]:e.val[t.key]=o(t)?n.split(",").map(p=>p.trim()).filter(Boolean):n}},F=(e,t)=>{const a=e.val[t.key];if(t.optionsSource){const S=t.optionsSource==="providers"?i.providerOptions||[]:i.modelOptions||[],L=a==null?"":String(a),h=l("select"),D=l("option","","—");D.value="",h.appendChild(D);const J=new Set([""]);if(S.forEach(m=>{if(J.has(m))return;J.add(m);const w=l("option","",m);w.value=m,m===L&&(w.selected=!0),h.appendChild(w)}),L&&!J.has(L)){const m=l("option","",L);m.value=L,m.selected=!0,h.appendChild(m)}return h.value=L||"",h.setAttribute("aria-label",`${e.name}: ${i.fieldLabel(t.key)}`),h.addEventListener("change",()=>{h.value?e.val[t.key]=h.value:delete e.val[t.key],g()}),h}const n=l("input");return t.type==="bool"?(n.type="checkbox",n.checked=a===!0):t.type==="number"?(n.type="number",n.value=a==null?"":String(a)):(n.type="text",n.value=Array.isArray(a)?a.join(", "):a==null?"":String(a)),n.setAttribute("aria-label",`${e.name}: ${i.fieldLabel(t.key)}`),n.addEventListener("change",()=>{E(e,t,n),g()}),n},j=()=>{C.textContent="",s.forEach(e=>{const t=l("div","table-row");t.dataset.key=e.name;const a=l("input");a.type="text",a.value=e.name,a.placeholder=i.fieldLabel(u.key),a.setAttribute("aria-label",i.fieldLabel(u.key)),a.addEventListener("change",()=>{e.name=a.value.trim(),g()&&(t.dataset.key=e.name)}),t.appendChild(a),d.forEach(p=>t.appendChild(F(e,p)));const n=l("button","table-rm linkbtn","✕");n.type="button",n.setAttribute("aria-label",`${i.label}: remove ${e.name}`),n.addEventListener("click",()=>{s.splice(s.indexOf(e),1),g()&&j()}),t.appendChild(n),C.appendChild(t)})};j(),y.appendChild(C),y.appendChild(b);const I=l("div","table-add"),v=l("input");v.type="text",v.placeholder=i.fieldLabel(u.key),v.setAttribute("aria-label",`${i.label}: new ${i.fieldLabel(u.key)}`);const B=()=>{const e=v.value.trim();if(!e||s.some(p=>p.name===e)){v.select();return}const t={};s.push({name:e,val:t,error:""}),v.value="",j();const a=C.lastElementChild,n=a&&a.querySelectorAll("input,select")[1];n&&n.focus()};v.addEventListener("keydown",e=>{e.key==="Enter"&&(e.preventDefault(),B())}),I.appendChild(v);const N=l("button","table-plus linkbtn","+");return N.type="button",N.setAttribute("aria-label",`${i.label}: add`),N.addEventListener("click",B),I.appendChild(N),y.appendChild(I),y}