@mjasnikovs/pi-task 0.17.21 → 0.17.23
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/dist/remote/bridge.d.ts +8 -0
- package/dist/remote/bridge.js +10 -0
- package/dist/remote/events.js +10 -4
- package/dist/remote/history.d.ts +11 -1
- package/dist/remote/history.js +1 -1
- package/dist/remote/protocol.d.ts +23 -2
- package/dist/remote/protocol.js +2 -0
- package/dist/remote/register.js +7 -3
- package/dist/remote/server.d.ts +1 -1
- package/dist/remote/server.js +5 -1
- package/dist/remote/session-state.d.ts +19 -5
- package/dist/remote/session-state.js +55 -9
- package/dist/remote/ui-highlight.d.ts +2 -0
- package/dist/remote/ui-highlight.js +119 -0
- package/dist/remote/ui-render.d.ts +2 -0
- package/dist/remote/ui-render.js +173 -0
- package/dist/remote/ui-script.js +425 -146
- package/dist/remote/ui-styles.d.ts +1 -1
- package/dist/remote/ui-styles.js +175 -8
- package/dist/remote/ui-tools.d.ts +2 -0
- package/dist/remote/ui-tools.js +113 -0
- package/dist/remote/ui.js +19 -1
- package/dist/task/impl-widget.js +2 -2
- package/dist/task/widget.d.ts +9 -0
- package/dist/task/widget.js +42 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const STYLES = " :root {\n --base: #1e1e2e; --mantle: #181825; --crust: #11111b;\n --surface0: #313244; --surface1: #45475a; --surface2: #585b70;\n --text: #cdd6f4; --subtext1: #a6adc8; --subtext0: #7f849c;\n --mauve: #cba6f7; --blue: #89b4fa; --green: #a6e3a1; --red: #f38ba8;\n --yellow: #f9e2af; --peach: #fab387; --teal: #94e2d5;\n }\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: var(--base); color: var(--text);\n font-family: ui-monospace, monospace;\n /* --app-h is set from window.innerHeight (see setAppHeight) so the column\n height is a stable pixel value across an orientation change. 100dvh is a\n fallback for first paint / no-JS: iOS Safari interpolates dvh during the\n rotation animation, which makes the whole flex column resize repeatedly\n (\"spazzing out\") \u2014 a fixed px height does not. */\n height: var(--app-h, 100dvh);\n display: flex; flex-direction: column; overflow: hidden;\n padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)\n 0px env(safe-area-inset-left, 0px);\n }\n #context-bar { height: 4px; background: var(--surface0); flex-shrink: 0; }\n #context-bar-fill { height: 100%; background: var(--mauve); width: 0%; transition: width 0.4s ease; }\n #header {\n background: var(--mantle); padding: 8px 16px;\n display: flex; justify-content: space-between; align-items: center;\n font-size: 13px; flex-shrink: 0; border-bottom: 1px solid var(--surface0);\n }\n #header .title { font-weight: bold; color: var(--mauve); letter-spacing: 0.05em;\n position: relative; animation: glitch 5s steps(1) infinite; }\n @keyframes glitch {\n 0%, 88%, 100% { text-shadow: none; transform: translate(0, 0); }\n 90% { text-shadow: -1px 0 var(--red), 1px 0 var(--teal); transform: translate(1px, -1px); }\n 92% { text-shadow: 1px 0 var(--red), -1px 0 var(--blue); transform: translate(-1px, 1px); }\n 94% { text-shadow: -1px 0 var(--blue), 1px 0 var(--red); transform: translate(1px, 0); }\n 96% { text-shadow: 1px 0 var(--teal), -1px 0 var(--red); transform: translate(-1px, 0); }\n }\n @media (prefers-reduced-motion: reduce) { #header .title { animation: none; } }\n #header .hgroup { display: flex; align-items: center; gap: 10px; }\n #bell {\n background: none; border: none; color: var(--subtext1); cursor: pointer;\n font-size: 15px; line-height: 1; padding: 2px; font-family: inherit;\n }\n #bell:hover { color: var(--text); }\n #bell.on { color: var(--mauve); }\n #chat-wrap { position: relative; flex: 1; min-height: 0; display: flex; }\n #chat-log {\n flex: 1; min-width: 0; overflow-y: auto; overflow-x: hidden; padding: 16px;\n display: flex; flex-direction: column; gap: 8px;\n }\n /* Floating jump-to-latest button \u2014 only shown when scrolled away from the\n bottom (toggled via .visible from the scroll handler). */\n #scroll-bottom {\n display: none; position: absolute; bottom: 16px; right: 16px; z-index: 40;\n width: 36px; height: 36px; border-radius: 50%; cursor: pointer;\n background: var(--surface1); color: var(--text); border: 1px solid var(--surface2);\n font-family: inherit; font-size: 18px; line-height: 1; padding: 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);\n }\n #scroll-bottom:hover { background: var(--surface2); color: var(--mauve); }\n #scroll-bottom.visible { display: block; }\n #chat-log::-webkit-scrollbar { width: 6px; }\n #chat-log::-webkit-scrollbar-track { background: transparent; }\n #chat-log::-webkit-scrollbar-thumb { background: var(--surface2); border-radius: 3px; }\n .bubble {\n max-width: 82%; padding: 8px 12px; border-radius: 8px;\n line-height: 1.6; white-space: pre-wrap; word-break: break-word; font-size: 13px;\n }\n .bubble.user { background: var(--surface1); color: var(--text); align-self: flex-end; }\n .bubble.assistant { background: var(--surface0); color: var(--text); align-self: flex-start; }\n .bubble.error {\n background: var(--crust); color: var(--red); align-self: stretch;\n max-width: 100%; border: 1px solid var(--red); font-size: 12px;\n }\n /* Persistent inline system note (e.g. context compaction) \u2014 a muted centered\n divider, distinct from chat bubbles. */\n .sysnote {\n align-self: center; color: var(--subtext0); font-size: 11px;\n font-family: ui-monospace, monospace; letter-spacing: 0.5px;\n padding: 2px 10px; opacity: 0.85;\n }\n .bubble.thinking {\n display: flex; gap: 5px; align-items: center; padding: 10px 14px;\n }\n .bubble.thinking .spinner {\n color: var(--mauve); font-size: 15px; line-height: 1;\n font-family: ui-monospace, monospace;\n }\n .tool-call {\n background: var(--crust); border-radius: 6px; align-self: flex-start;\n max-width: 90%; font-size: 12px; border: 1px solid var(--surface0);\n }\n .tool-call summary {\n padding: 6px 10px; color: var(--subtext1); cursor: pointer;\n user-select: none; list-style: none;\n overflow-wrap: anywhere; word-break: break-word;\n }\n .tool-call summary::-webkit-details-marker { display: none; }\n .tool-call summary::before { content: \"\u25B6 \"; }\n .tool-call[open] > summary::before { content: \"\u25BC \"; }\n .tool-call.error > summary { color: var(--red); }\n .tool-call pre {\n padding: 8px 12px; overflow-y: auto;\n color: var(--subtext1); font-size: 11px; max-height: 280px;\n border-top: 1px solid var(--surface0);\n white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;\n }\n .tool-spin { color: var(--mauve); margin-left: 6px; font-family: ui-monospace, monospace; font-size: 13px; }\n .code-block {\n background: var(--crust); border: 1px solid var(--surface0);\n border-radius: 6px; overflow: hidden; margin: 4px 0;\n align-self: stretch; max-width: 100%; font-size: 12px;\n }\n .code-lang {\n background: var(--surface0); color: var(--subtext0);\n font-size: 10px; padding: 3px 10px; letter-spacing: 0.05em;\n }\n .code-block code {\n display: block; padding: 10px 12px; overflow-x: auto;\n color: var(--text); white-space: pre; line-height: 1.55;\n }\n .hl-kw { color: var(--mauve); }\n .hl-str { color: var(--green); }\n .hl-cmt { color: var(--subtext0); font-style: italic; }\n .hl-num { color: var(--blue); }\n .hl-fn { color: var(--yellow); }\n #input-bar {\n background: var(--mantle); padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));\n display: flex; gap: 8px; flex-shrink: 0;\n border-top: 1px solid var(--surface0);\n position: relative;\n }\n #cmd-suggestions {\n display: none; position: absolute; bottom: 100%; left: 16px; right: 16px;\n background: var(--mantle); border: 1px solid var(--surface1);\n border-bottom: none; border-radius: 8px 8px 0 0;\n overflow: hidden; z-index: 10;\n }\n .cmd-item {\n display: flex; align-items: baseline; gap: 10px;\n padding: 7px 12px; cursor: pointer; font-size: 12px;\n border-bottom: 1px solid var(--surface0);\n }\n .cmd-item:last-child { border-bottom: none; }\n .cmd-item:hover, .cmd-item.active { background: var(--surface0); }\n .cmd-item .cmd-name { color: var(--blue); font-weight: bold; flex-shrink: 0; }\n .cmd-item .cmd-desc { color: var(--subtext0); }\n #input {\n flex: 1; background: var(--surface0); color: var(--text);\n border: none; border-radius: 6px; padding: 8px 12px;\n font-family: inherit; font-size: 13px; resize: none;\n outline: none; line-height: 1.5; min-height: 36px; max-height: 120px;\n }\n #input::placeholder { color: var(--subtext0); }\n #input:focus { box-shadow: 0 0 0 1px var(--mauve); }\n #send {\n background: var(--blue); color: var(--crust); border: none;\n border-radius: 6px; padding: 8px 16px; font-weight: bold;\n cursor: pointer; font-size: 13px; font-family: inherit;\n white-space: nowrap; align-self: flex-end;\n }\n #send:disabled, #input:disabled { opacity: 0.45; cursor: not-allowed; }\n #reconnect-overlay {\n display: none; position: fixed; inset: 0;\n background: rgba(30,30,46,0.88); color: var(--subtext1);\n justify-content: center; align-items: center;\n font-size: 13px; z-index: 100; letter-spacing: 0.03em;\n }\n #reconnect-overlay.visible { display: flex; }\n /* Trailing stream indicator: the same braille spinner as the thinking bubble,\n inline at the end of the streaming text (not a green blinking block). */\n .cursor {\n color: var(--mauve); margin-left: 2px;\n font-family: ui-monospace, monospace;\n }\n #status-panel { padding: 6px 12px; border-bottom: 1px solid var(--surface1);\n color: var(--subtext1); white-space: pre-wrap; font-size: 13px; display: none; }\n #prompt-card { position: fixed; left: 0; right: 0; bottom: 0; background: var(--mantle);\n border-top: 2px solid var(--mauve); padding: 16px 14px calc(16px + env(safe-area-inset-bottom, 0px));\n display: none; z-index: 50; max-height: 80dvh; overflow-y: auto; }\n #prompt-card .q-label { color: var(--mauve); font-size: 11px; font-weight: 700;\n text-transform: uppercase; letter-spacing: .6px; margin-bottom: 6px; }\n #prompt-card .q { color: var(--text); margin-bottom: 12px; white-space: pre-wrap;\n font-size: 15px; line-height: 1.5; }\n #prompt-card .rec-panel { background: var(--surface0); border-left: 3px solid var(--green);\n border-radius: 6px; padding: 10px 12px; margin-bottom: 12px; }\n #prompt-card .rec-label { color: var(--green); font-size: 11px; font-weight: 700;\n text-transform: uppercase; letter-spacing: .5px; margin-bottom: 4px; }\n #prompt-card .rec-text { color: var(--text); font-size: 15px; line-height: 1.5;\n white-space: pre-wrap; overflow-wrap: anywhere; }\n #prompt-card textarea { width: 100%; background: var(--surface0); color: var(--text);\n border: 1px solid var(--surface2); border-radius: 6px; padding: 10px; font-size: 15px;\n font-family: inherit; line-height: 1.5; resize: vertical; margin-bottom: 4px; }\n #prompt-card .row { display: flex; gap: 8px; margin-top: 12px; align-items: stretch;\n flex-wrap: wrap; }\n /* Recommendation answers can be long sentences, so stack them as a readable list. */\n #prompt-card .row.stacked { flex-direction: column; align-items: stretch; }\n #prompt-card .row.stacked button { flex: none; text-align: left; }\n #prompt-card .row.stacked button.cancel { align-self: center; text-align: center; }\n #prompt-card button { padding: 11px 16px; border-radius: 8px; border: none; cursor: pointer;\n font-family: inherit; font-size: 14px; font-weight: 600; transition: filter .15s ease; }\n #prompt-card button:hover { filter: brightness(1.08); }\n #prompt-card button.primary { background: var(--green); color: var(--crust);\n font-weight: 700; flex: 1; min-width: 160px; }\n #prompt-card button.secondary { background: var(--surface1); color: var(--text);\n flex: 1; min-width: 160px; }\n #prompt-card button.cancel { margin-left: auto; align-self: center; background: transparent;\n color: var(--subtext0); font-size: 12px; font-weight: 500; padding: 8px 10px; }\n #prompt-card button.cancel:hover { color: var(--red); filter: none; }\n #prompt-card button.cancel.armed { background: var(--red); color: var(--crust); font-weight: 700; }\n .toast { position: fixed; top: calc(env(safe-area-inset-top, 0px) + 12px);\n right: calc(env(safe-area-inset-right, 0px) + 12px); max-width: calc(100vw - 24px);\n padding: 8px 12px; border-radius: 6px; overflow-wrap: anywhere; word-break: break-word;\n background: var(--surface1); color: var(--text); z-index: 60; }\n .toast.warning { background: var(--peach); color: var(--crust); }\n .toast.error { background: var(--red); color: var(--crust); }\n #viewer { position: fixed; inset: 24px; background: var(--mantle); border: 1px solid var(--surface2);\n border-radius: 8px; padding: 16px; overflow: auto; white-space: pre-wrap;\n overflow-wrap: anywhere; word-break: break-word; display: none; z-index: 70; }\n #viewer .close { position: absolute; top: 8px; right: 12px; cursor: pointer; color: var(--subtext0); }";
|
|
1
|
+
export declare const STYLES = " :root {\n --base: #1e1e2e; --mantle: #181825; --crust: #11111b;\n --surface0: #313244; --surface1: #45475a; --surface2: #585b70;\n --text: #cdd6f4; --subtext1: #a6adc8; --subtext0: #7f849c;\n --mauve: #cba6f7; --blue: #89b4fa; --green: #a6e3a1; --red: #f38ba8;\n --yellow: #f9e2af; --peach: #fab387; --teal: #94e2d5;\n }\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: var(--base); color: var(--text);\n font-family: ui-monospace, monospace;\n /* --app-h is set from window.innerHeight (see setAppHeight) so the column\n height is a stable pixel value across an orientation change. 100dvh is a\n fallback for first paint / no-JS: iOS Safari interpolates dvh during the\n rotation animation, which makes the whole flex column resize repeatedly\n (\"spazzing out\") \u2014 a fixed px height does not. */\n height: var(--app-h, 100dvh);\n display: flex; flex-direction: column; overflow: hidden;\n padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)\n 0px env(safe-area-inset-left, 0px);\n }\n #context-bar { height: 4px; background: var(--surface0); flex-shrink: 0; }\n #context-bar-fill { height: 100%; background: var(--mauve); width: 0%; transition: width 0.4s ease; }\n #header {\n background: var(--mantle); padding: 8px 16px;\n display: flex; justify-content: space-between; align-items: center;\n font-size: 13px; flex-shrink: 0; border-bottom: 1px solid var(--surface0);\n }\n #header .title { font-weight: bold; color: var(--mauve); letter-spacing: 0.05em;\n position: relative; animation: glitch 5s steps(1) infinite; }\n @keyframes glitch {\n 0%, 88%, 100% { text-shadow: none; transform: translate(0, 0); }\n 90% { text-shadow: -1px 0 var(--red), 1px 0 var(--teal); transform: translate(1px, -1px); }\n 92% { text-shadow: 1px 0 var(--red), -1px 0 var(--blue); transform: translate(-1px, 1px); }\n 94% { text-shadow: -1px 0 var(--blue), 1px 0 var(--red); transform: translate(1px, 0); }\n 96% { text-shadow: 1px 0 var(--teal), -1px 0 var(--red); transform: translate(-1px, 0); }\n }\n @media (prefers-reduced-motion: reduce) { #header .title { animation: none; } }\n #header .hgroup { display: flex; align-items: center; gap: 10px; }\n #bell {\n background: none; border: none; color: var(--subtext1); cursor: pointer;\n font-size: 15px; line-height: 1; padding: 2px; font-family: inherit;\n }\n #bell:hover { color: var(--text); }\n #bell.on { color: var(--mauve); }\n /* Header status chip: connection dot + model name + context usage. */\n #status-chip { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--subtext0); }\n #status-dot {\n width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;\n background: var(--surface2); transition: background 0.2s ease;\n }\n #status-dot.idle { background: var(--green); }\n #status-dot.running { background: var(--mauve); animation: dot-pulse 1.2s ease-in-out infinite; }\n #status-dot.disconnected { background: var(--red); }\n @keyframes dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }\n @media (prefers-reduced-motion: reduce) { #status-dot.running { animation: none; } }\n #status-model { color: var(--subtext1); }\n #status-model:empty { display: none; }\n #status-ctx { color: var(--subtext0); font-variant-numeric: tabular-nums; }\n #status-ctx:empty { display: none; }\n /* Notification bell dropdown: a push toggle row + the recent-toast history. */\n #notif-panel {\n display: none; position: fixed; z-index: 80;\n top: calc(env(safe-area-inset-top, 0px) + 42px);\n right: calc(env(safe-area-inset-right, 0px) + 12px);\n width: min(320px, calc(100vw - 24px));\n background: var(--mantle); border: 1px solid var(--surface1); border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); overflow: hidden;\n }\n #notif-panel.open { display: block; }\n #notif-toggle-row {\n display: flex; align-items: center; justify-content: space-between;\n padding: 8px 12px; border-bottom: 1px solid var(--surface0);\n }\n #notif-title { font-size: 12px; color: var(--subtext1); font-weight: 700; }\n #notif-toggle {\n background: var(--surface1); color: var(--text); border: none; border-radius: 6px;\n padding: 4px 10px; font-family: inherit; font-size: 11px; cursor: pointer;\n }\n #notif-toggle:hover { filter: brightness(1.1); }\n #notif-toggle.on { background: var(--mauve); color: var(--crust); font-weight: 700; }\n #notif-list { max-height: 40dvh; overflow-y: auto; }\n #notif-empty { padding: 14px 12px; color: var(--subtext0); font-size: 11px; text-align: center; }\n .notif-item {\n display: flex; align-items: baseline; gap: 8px; padding: 7px 12px;\n border-bottom: 1px solid var(--surface0); font-size: 12px;\n }\n .notif-item:last-child { border-bottom: none; }\n .notif-item .notif-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--blue); align-self: center; }\n .notif-item.warning .notif-dot { background: var(--peach); }\n .notif-item.error .notif-dot { background: var(--red); }\n .notif-item .notif-msg { flex: 1; min-width: 0; color: var(--text); overflow-wrap: anywhere; word-break: break-word; }\n .notif-item .notif-time { flex-shrink: 0; color: var(--subtext0); font-size: 10px; font-variant-numeric: tabular-nums; }\n #chat-wrap { position: relative; flex: 1; min-height: 0; display: flex; }\n #chat-log {\n flex: 1; min-width: 0; overflow-y: auto; overflow-x: hidden; padding: 16px;\n display: flex; flex-direction: column; gap: 8px;\n }\n /* Floating jump-to-latest button \u2014 only shown when scrolled away from the\n bottom (toggled via .visible from the scroll handler). */\n #scroll-bottom {\n display: none; position: absolute; bottom: 16px; right: 16px; z-index: 40;\n width: 36px; height: 36px; border-radius: 50%; cursor: pointer;\n background: var(--surface1); color: var(--text); border: 1px solid var(--surface2);\n font-family: inherit; font-size: 18px; line-height: 1; padding: 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);\n }\n #scroll-bottom:hover { background: var(--surface2); color: var(--mauve); }\n #scroll-bottom.visible { display: block; }\n #chat-log::-webkit-scrollbar { width: 6px; }\n #chat-log::-webkit-scrollbar-track { background: transparent; }\n #chat-log::-webkit-scrollbar-thumb { background: var(--surface2); border-radius: 3px; }\n .bubble {\n max-width: 82%; padding: 8px 12px; border-radius: 8px;\n line-height: 1.6; white-space: pre-wrap; word-break: break-word; font-size: 13px;\n }\n .bubble.user { background: var(--surface1); color: var(--text); align-self: flex-end; }\n .bubble.assistant { background: var(--surface0); color: var(--text); align-self: flex-start; position: relative; }\n .bubble.error {\n background: var(--crust); color: var(--red); align-self: stretch;\n max-width: 100%; border: 1px solid var(--red); font-size: 12px;\n }\n /* Persistent inline system note (e.g. context compaction) \u2014 a muted centered\n divider, distinct from chat bubbles. */\n .sysnote {\n align-self: center; color: var(--subtext0); font-size: 11px;\n font-family: ui-monospace, monospace; letter-spacing: 0.5px;\n padding: 2px 10px; opacity: 0.85;\n }\n .bubble.thinking {\n display: flex; gap: 5px; align-items: center; padding: 10px 14px;\n }\n .bubble.thinking .spinner {\n color: var(--mauve); font-size: 15px; line-height: 1;\n font-family: ui-monospace, monospace;\n }\n /* Collapsed reasoning block (\"\u273B Thinking\u2026 (n lines)\"), muted + italic. */\n .thinking-block { align-self: flex-start; max-width: 90%; font-size: 12px; }\n .thinking-block > summary {\n color: var(--subtext0); font-style: italic; cursor: pointer; list-style: none;\n user-select: none; display: flex; align-items: center; gap: 8px; padding: 2px 0;\n }\n .thinking-block > summary::-webkit-details-marker { display: none; }\n .thinking-block .thinking-spin {\n color: var(--mauve); font-style: normal; font-family: ui-monospace, monospace;\n }\n .thinking-block .thinking-body {\n color: var(--subtext0); font-style: italic; white-space: pre-wrap;\n word-break: break-word; line-height: 1.5; margin: 4px 0 0 4px;\n padding: 4px 0 2px 12px; border-left: 2px solid var(--surface1);\n }\n /* Copy buttons: on code-block headers and (floating) on finished assistant\n bubbles. Wired by one delegated click handler in the client script. */\n .copy-btn {\n background: transparent; border: none; color: var(--subtext0); cursor: pointer;\n font-family: inherit; font-size: 11px; padding: 2px 6px; border-radius: 4px;\n line-height: 1.4;\n }\n .copy-btn:hover { color: var(--text); background: var(--surface1); }\n .copy-btn.copied { color: var(--green); }\n .bubble-copy {\n position: absolute; top: 4px; right: 4px; opacity: 0;\n background: var(--surface1); transition: opacity 0.12s ease;\n }\n .bubble.assistant:hover .bubble-copy { opacity: 1; }\n /* Touch devices have no hover \u2014 keep the button faintly visible. */\n @media (hover: none) { .bubble-copy { opacity: 0.55; } }\n .tool-call {\n background: var(--crust); border-radius: 6px; align-self: flex-start;\n max-width: 90%; font-size: 12px; border: 1px solid var(--surface0);\n }\n .tool-call summary {\n padding: 6px 10px; color: var(--subtext1); cursor: pointer;\n user-select: none; list-style: none;\n display: flex; align-items: center; gap: 8px;\n }\n .tool-call summary::-webkit-details-marker { display: none; }\n .tool-call summary::before { content: \"\u25B6\"; flex-shrink: 0; font-size: 9px; color: var(--subtext0); }\n .tool-call[open] > summary::before { content: \"\u25BC\"; }\n .tool-call.error > summary { color: var(--red); }\n /* The summary is a single line: the label ellipsizes (full text in the title\n tooltip / on expand), badges and timing stay pinned to the right. */\n .tool-label {\n flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-family: ui-monospace, monospace;\n }\n .tool-badge { flex-shrink: 0; color: var(--subtext0); font-size: 11px; }\n .tool-elapsed { flex-shrink: 0; color: var(--subtext0); font-size: 11px; }\n .tool-call pre {\n padding: 8px 12px; overflow-y: auto;\n color: var(--subtext1); font-size: 11px; max-height: 280px;\n border-top: 1px solid var(--surface0);\n white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;\n }\n .tool-spin { color: var(--mauve); flex-shrink: 0; font-family: ui-monospace, monospace; font-size: 13px; }\n /* Line diff for edit/write tools (tints derived from --green/--red). */\n .tool-diff { border-top: 1px solid var(--surface0); overflow-x: auto; }\n .diff { font-family: ui-monospace, monospace; font-size: 11px; padding: 4px 0; }\n .diff-line { white-space: pre; padding: 0 10px; }\n .diff-sign { display: inline-block; width: 1ch; margin-right: 8px; color: var(--subtext0); }\n .diff-add { background: color-mix(in srgb, var(--green) 14%, transparent); color: var(--green); }\n .diff-del { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--red); }\n .diff-ctx { color: var(--subtext1); }\n .code-block {\n background: var(--crust); border: 1px solid var(--surface0);\n border-radius: 6px; overflow: hidden; margin: 4px 0;\n align-self: stretch; max-width: 100%; font-size: 12px;\n }\n /* Header row above a code block: language label on the left, copy button on\n the right (the surface bar that used to live on .code-lang). */\n .code-head {\n display: flex; align-items: center; justify-content: space-between;\n background: var(--surface0);\n }\n .code-lang {\n color: var(--subtext0); font-size: 10px; padding: 3px 10px; letter-spacing: 0.05em;\n }\n .code-head .copy-btn { padding: 3px 10px; }\n .code-block code {\n display: block; padding: 10px 12px; overflow-x: auto;\n color: var(--text); white-space: pre; line-height: 1.55;\n }\n .hl-kw { color: var(--mauve); }\n .hl-str { color: var(--green); }\n .hl-cmt { color: var(--subtext0); font-style: italic; }\n .hl-num { color: var(--blue); }\n .hl-fn { color: var(--yellow); }\n /* Hand-rolled markdown, applied only to assistant bubbles + the recommendation\n panel (both get the .md class from setContent). Block elements lay themselves\n out, so switch off the container's pre-wrap for these. */\n .md { white-space: normal; }\n .md > :first-child { margin-top: 0; }\n .md > :last-child { margin-bottom: 0; }\n .md .md-h { color: var(--mauve); font-weight: 700; line-height: 1.3; margin: 10px 0 4px; }\n .md .md-h1 { font-size: 1.35em; }\n .md .md-h2 { font-size: 1.2em; }\n .md .md-h3 { font-size: 1.08em; }\n .md .md-h4, .md .md-h5, .md .md-h6 { font-size: 1em; }\n .md .md-p { margin: 6px 0; }\n .md .md-list { margin: 6px 0; padding-left: 22px; }\n .md .md-list li { margin: 2px 0; }\n .md .md-task { list-style: none; margin-left: -22px; }\n .md .md-check { color: var(--green); }\n .md .md-quote { border-left: 3px solid var(--surface2); margin: 6px 0; padding: 2px 0 2px 10px; color: var(--subtext1); }\n .md .md-hr { border: none; border-top: 1px solid var(--surface1); margin: 10px 0; }\n .md a { color: var(--blue); text-decoration: underline; }\n .md strong { color: var(--text); font-weight: 700; }\n .md em { font-style: italic; }\n .md del { color: var(--subtext0); }\n .md .md-code {\n background: var(--crust); border: 1px solid var(--surface0);\n border-radius: 4px; padding: 1px 4px; font-size: 0.92em;\n }\n .md .md-table { border-collapse: collapse; margin: 8px 0; font-size: 0.95em; display: block; overflow-x: auto; }\n .md .md-table th, .md .md-table td { border: 1px solid var(--surface1); padding: 4px 8px; text-align: left; }\n .md .md-table th { background: var(--surface0); color: var(--subtext1); }\n #input-bar {\n background: var(--mantle); padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));\n display: flex; gap: 8px; flex-shrink: 0;\n border-top: 1px solid var(--surface0);\n position: relative;\n }\n #cmd-suggestions {\n display: none; position: absolute; bottom: 100%; left: 16px; right: 16px;\n background: var(--mantle); border: 1px solid var(--surface1);\n border-bottom: none; border-radius: 8px 8px 0 0;\n overflow: hidden; z-index: 10;\n }\n .cmd-item {\n display: flex; align-items: baseline; gap: 10px;\n padding: 7px 12px; cursor: pointer; font-size: 12px;\n border-bottom: 1px solid var(--surface0);\n }\n .cmd-item:last-child { border-bottom: none; }\n .cmd-item:hover, .cmd-item.active { background: var(--surface0); }\n .cmd-item .cmd-name { color: var(--blue); font-weight: bold; flex-shrink: 0; }\n .cmd-item .cmd-desc { color: var(--subtext0); }\n #input {\n flex: 1; background: var(--surface0); color: var(--text);\n border: none; border-radius: 6px; padding: 8px 12px;\n font-family: inherit; font-size: 13px; resize: none;\n outline: none; line-height: 1.5; min-height: 36px; max-height: 120px;\n }\n #input::placeholder { color: var(--subtext0); }\n #input:focus { box-shadow: 0 0 0 1px var(--mauve); }\n #send {\n background: var(--blue); color: var(--crust); border: none;\n border-radius: 6px; padding: 8px 16px; font-weight: bold;\n cursor: pointer; font-size: 13px; font-family: inherit;\n white-space: nowrap; align-self: flex-end;\n }\n #send:disabled, #input:disabled { opacity: 0.45; cursor: not-allowed; }\n /* While the agent runs, Send becomes a red Stop; the armed (tap-to-confirm)\n state brightens it and adds a halo, mirroring the prompt card's cancel. */\n #send.stop { background: var(--red); color: var(--crust); }\n #send.stop.armed {\n filter: brightness(1.12);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 45%, transparent);\n }\n #reconnect-overlay {\n display: none; position: fixed; inset: 0;\n background: rgba(30,30,46,0.88); color: var(--subtext1);\n justify-content: center; align-items: center;\n font-size: 13px; z-index: 100; letter-spacing: 0.03em;\n }\n #reconnect-overlay.visible { display: flex; }\n /* Trailing stream indicator: the same braille spinner as the thinking bubble,\n inline at the end of the streaming text (not a green blinking block). */\n .cursor {\n color: var(--mauve); margin-left: 2px;\n font-family: ui-monospace, monospace;\n }\n #status-panel { padding: 6px 12px; border-bottom: 1px solid var(--surface1);\n color: var(--subtext1); white-space: pre-wrap; font-size: 13px; display: none; }\n /* Structured task widget (progress bar + phase badge + elapsed). Replaces the\n plain text lines when the server sends a structured data payload. */\n #status-panel.structured { white-space: normal; display: block; }\n .widget-top { display: flex; align-items: center; gap: 8px; }\n .widget-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;\n white-space: nowrap; color: var(--text); font-size: 13px; }\n .widget-phase { flex-shrink: 0; background: var(--surface0); color: var(--mauve);\n border-radius: 4px; padding: 1px 7px; font-size: 10px; letter-spacing: 0.03em;\n text-transform: uppercase; }\n .widget-elapsed { flex-shrink: 0; color: var(--subtext0); font-size: 11px;\n font-variant-numeric: tabular-nums; }\n .widget-bar { height: 4px; background: var(--surface0); border-radius: 2px;\n overflow: hidden; margin-top: 6px; }\n .widget-bar-fill { height: 100%; background: var(--mauve); border-radius: 2px;\n transition: width 0.3s ease; }\n /* Dim per-turn timestamp shown under a committed turn's bubble. */\n .turn-time { font-size: 10px; color: var(--subtext0); opacity: 0.55; padding: 0 4px;\n margin-top: -2px; }\n .turn-time.user { align-self: flex-end; }\n .turn-time.assistant, .turn-time.system { align-self: flex-start; }\n .turn-time.system { align-self: center; }\n #prompt-card { position: fixed; left: 0; right: 0; bottom: 0; background: var(--mantle);\n border-top: 2px solid var(--mauve); padding: 16px 14px calc(16px + env(safe-area-inset-bottom, 0px));\n display: none; z-index: 50; max-height: 80dvh; overflow-y: auto; }\n #prompt-card .q-label { color: var(--mauve); font-size: 11px; font-weight: 700;\n text-transform: uppercase; letter-spacing: .6px; margin-bottom: 6px; }\n #prompt-card .q { color: var(--text); margin-bottom: 12px; white-space: pre-wrap;\n font-size: 15px; line-height: 1.5; }\n #prompt-card .rec-panel { background: var(--surface0); border-left: 3px solid var(--green);\n border-radius: 6px; padding: 10px 12px; margin-bottom: 12px; }\n #prompt-card .rec-label { color: var(--green); font-size: 11px; font-weight: 700;\n text-transform: uppercase; letter-spacing: .5px; margin-bottom: 4px; }\n #prompt-card .rec-text { color: var(--text); font-size: 15px; line-height: 1.5;\n white-space: pre-wrap; overflow-wrap: anywhere; }\n #prompt-card textarea { width: 100%; background: var(--surface0); color: var(--text);\n border: 1px solid var(--surface2); border-radius: 6px; padding: 10px; font-size: 15px;\n font-family: inherit; line-height: 1.5; resize: vertical; margin-bottom: 4px; }\n #prompt-card .row { display: flex; gap: 8px; margin-top: 12px; align-items: stretch;\n flex-wrap: wrap; }\n /* Recommendation answers can be long sentences, so stack them as a readable list. */\n #prompt-card .row.stacked { flex-direction: column; align-items: stretch; }\n #prompt-card .row.stacked button { flex: none; text-align: left; }\n #prompt-card .row.stacked button.cancel { align-self: center; text-align: center; }\n #prompt-card button { padding: 11px 16px; border-radius: 8px; border: none; cursor: pointer;\n font-family: inherit; font-size: 14px; font-weight: 600; transition: filter .15s ease; }\n #prompt-card button:hover { filter: brightness(1.08); }\n #prompt-card button.primary { background: var(--green); color: var(--crust);\n font-weight: 700; flex: 1; min-width: 160px; }\n #prompt-card button.secondary { background: var(--surface1); color: var(--text);\n flex: 1; min-width: 160px; }\n #prompt-card button.cancel { margin-left: auto; align-self: center; background: transparent;\n color: var(--subtext0); font-size: 12px; font-weight: 500; padding: 8px 10px; }\n #prompt-card button.cancel:hover { color: var(--red); filter: none; }\n #prompt-card button.cancel.armed { background: var(--red); color: var(--crust); font-weight: 700; }\n .toast { position: fixed; top: calc(env(safe-area-inset-top, 0px) + 12px);\n right: calc(env(safe-area-inset-right, 0px) + 12px); max-width: calc(100vw - 24px);\n padding: 8px 12px; border-radius: 6px; overflow-wrap: anywhere; word-break: break-word;\n background: var(--surface1); color: var(--text); z-index: 60; }\n .toast.warning { background: var(--peach); color: var(--crust); }\n .toast.error { background: var(--red); color: var(--crust); }\n #viewer { position: fixed; inset: 24px; background: var(--mantle); border: 1px solid var(--surface2);\n border-radius: 8px; padding: 16px; overflow: auto; white-space: pre-wrap;\n overflow-wrap: anywhere; word-break: break-word; display: none; z-index: 70; }\n #viewer .close { position: absolute; top: 8px; right: 12px; cursor: pointer; color: var(--subtext0); }\n /* Desktop: center the transcript/status/input in a readable column instead of\n hugging the left edge. The scrollbar stays at the true window edge; only the\n content is inset. Mobile (below 960px) is unchanged. */\n @media (min-width: 960px) {\n #chat-log { padding-left: calc((100% - 920px) / 2); padding-right: calc((100% - 920px) / 2); }\n #status-panel { padding-left: calc((100% - 920px) / 2 + 12px); padding-right: calc((100% - 920px) / 2 + 12px); }\n #input-bar { padding-left: calc((100% - 920px) / 2); padding-right: calc((100% - 920px) / 2); }\n #cmd-suggestions { left: calc((100% - 920px) / 2 + 16px); right: calc((100% - 920px) / 2 + 16px); }\n }";
|
package/dist/remote/ui-styles.js
CHANGED
|
@@ -43,6 +43,54 @@ export const STYLES = ` :root {
|
|
|
43
43
|
}
|
|
44
44
|
#bell:hover { color: var(--text); }
|
|
45
45
|
#bell.on { color: var(--mauve); }
|
|
46
|
+
/* Header status chip: connection dot + model name + context usage. */
|
|
47
|
+
#status-chip { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--subtext0); }
|
|
48
|
+
#status-dot {
|
|
49
|
+
width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
|
|
50
|
+
background: var(--surface2); transition: background 0.2s ease;
|
|
51
|
+
}
|
|
52
|
+
#status-dot.idle { background: var(--green); }
|
|
53
|
+
#status-dot.running { background: var(--mauve); animation: dot-pulse 1.2s ease-in-out infinite; }
|
|
54
|
+
#status-dot.disconnected { background: var(--red); }
|
|
55
|
+
@keyframes dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
|
|
56
|
+
@media (prefers-reduced-motion: reduce) { #status-dot.running { animation: none; } }
|
|
57
|
+
#status-model { color: var(--subtext1); }
|
|
58
|
+
#status-model:empty { display: none; }
|
|
59
|
+
#status-ctx { color: var(--subtext0); font-variant-numeric: tabular-nums; }
|
|
60
|
+
#status-ctx:empty { display: none; }
|
|
61
|
+
/* Notification bell dropdown: a push toggle row + the recent-toast history. */
|
|
62
|
+
#notif-panel {
|
|
63
|
+
display: none; position: fixed; z-index: 80;
|
|
64
|
+
top: calc(env(safe-area-inset-top, 0px) + 42px);
|
|
65
|
+
right: calc(env(safe-area-inset-right, 0px) + 12px);
|
|
66
|
+
width: min(320px, calc(100vw - 24px));
|
|
67
|
+
background: var(--mantle); border: 1px solid var(--surface1); border-radius: 8px;
|
|
68
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); overflow: hidden;
|
|
69
|
+
}
|
|
70
|
+
#notif-panel.open { display: block; }
|
|
71
|
+
#notif-toggle-row {
|
|
72
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
73
|
+
padding: 8px 12px; border-bottom: 1px solid var(--surface0);
|
|
74
|
+
}
|
|
75
|
+
#notif-title { font-size: 12px; color: var(--subtext1); font-weight: 700; }
|
|
76
|
+
#notif-toggle {
|
|
77
|
+
background: var(--surface1); color: var(--text); border: none; border-radius: 6px;
|
|
78
|
+
padding: 4px 10px; font-family: inherit; font-size: 11px; cursor: pointer;
|
|
79
|
+
}
|
|
80
|
+
#notif-toggle:hover { filter: brightness(1.1); }
|
|
81
|
+
#notif-toggle.on { background: var(--mauve); color: var(--crust); font-weight: 700; }
|
|
82
|
+
#notif-list { max-height: 40dvh; overflow-y: auto; }
|
|
83
|
+
#notif-empty { padding: 14px 12px; color: var(--subtext0); font-size: 11px; text-align: center; }
|
|
84
|
+
.notif-item {
|
|
85
|
+
display: flex; align-items: baseline; gap: 8px; padding: 7px 12px;
|
|
86
|
+
border-bottom: 1px solid var(--surface0); font-size: 12px;
|
|
87
|
+
}
|
|
88
|
+
.notif-item:last-child { border-bottom: none; }
|
|
89
|
+
.notif-item .notif-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--blue); align-self: center; }
|
|
90
|
+
.notif-item.warning .notif-dot { background: var(--peach); }
|
|
91
|
+
.notif-item.error .notif-dot { background: var(--red); }
|
|
92
|
+
.notif-item .notif-msg { flex: 1; min-width: 0; color: var(--text); overflow-wrap: anywhere; word-break: break-word; }
|
|
93
|
+
.notif-item .notif-time { flex-shrink: 0; color: var(--subtext0); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
46
94
|
#chat-wrap { position: relative; flex: 1; min-height: 0; display: flex; }
|
|
47
95
|
#chat-log {
|
|
48
96
|
flex: 1; min-width: 0; overflow-y: auto; overflow-x: hidden; padding: 16px;
|
|
@@ -67,7 +115,7 @@ export const STYLES = ` :root {
|
|
|
67
115
|
line-height: 1.6; white-space: pre-wrap; word-break: break-word; font-size: 13px;
|
|
68
116
|
}
|
|
69
117
|
.bubble.user { background: var(--surface1); color: var(--text); align-self: flex-end; }
|
|
70
|
-
.bubble.assistant { background: var(--surface0); color: var(--text); align-self: flex-start; }
|
|
118
|
+
.bubble.assistant { background: var(--surface0); color: var(--text); align-self: flex-start; position: relative; }
|
|
71
119
|
.bubble.error {
|
|
72
120
|
background: var(--crust); color: var(--red); align-self: stretch;
|
|
73
121
|
max-width: 100%; border: 1px solid var(--red); font-size: 12px;
|
|
@@ -86,6 +134,37 @@ export const STYLES = ` :root {
|
|
|
86
134
|
color: var(--mauve); font-size: 15px; line-height: 1;
|
|
87
135
|
font-family: ui-monospace, monospace;
|
|
88
136
|
}
|
|
137
|
+
/* Collapsed reasoning block ("✻ Thinking… (n lines)"), muted + italic. */
|
|
138
|
+
.thinking-block { align-self: flex-start; max-width: 90%; font-size: 12px; }
|
|
139
|
+
.thinking-block > summary {
|
|
140
|
+
color: var(--subtext0); font-style: italic; cursor: pointer; list-style: none;
|
|
141
|
+
user-select: none; display: flex; align-items: center; gap: 8px; padding: 2px 0;
|
|
142
|
+
}
|
|
143
|
+
.thinking-block > summary::-webkit-details-marker { display: none; }
|
|
144
|
+
.thinking-block .thinking-spin {
|
|
145
|
+
color: var(--mauve); font-style: normal; font-family: ui-monospace, monospace;
|
|
146
|
+
}
|
|
147
|
+
.thinking-block .thinking-body {
|
|
148
|
+
color: var(--subtext0); font-style: italic; white-space: pre-wrap;
|
|
149
|
+
word-break: break-word; line-height: 1.5; margin: 4px 0 0 4px;
|
|
150
|
+
padding: 4px 0 2px 12px; border-left: 2px solid var(--surface1);
|
|
151
|
+
}
|
|
152
|
+
/* Copy buttons: on code-block headers and (floating) on finished assistant
|
|
153
|
+
bubbles. Wired by one delegated click handler in the client script. */
|
|
154
|
+
.copy-btn {
|
|
155
|
+
background: transparent; border: none; color: var(--subtext0); cursor: pointer;
|
|
156
|
+
font-family: inherit; font-size: 11px; padding: 2px 6px; border-radius: 4px;
|
|
157
|
+
line-height: 1.4;
|
|
158
|
+
}
|
|
159
|
+
.copy-btn:hover { color: var(--text); background: var(--surface1); }
|
|
160
|
+
.copy-btn.copied { color: var(--green); }
|
|
161
|
+
.bubble-copy {
|
|
162
|
+
position: absolute; top: 4px; right: 4px; opacity: 0;
|
|
163
|
+
background: var(--surface1); transition: opacity 0.12s ease;
|
|
164
|
+
}
|
|
165
|
+
.bubble.assistant:hover .bubble-copy { opacity: 1; }
|
|
166
|
+
/* Touch devices have no hover — keep the button faintly visible. */
|
|
167
|
+
@media (hover: none) { .bubble-copy { opacity: 0.55; } }
|
|
89
168
|
.tool-call {
|
|
90
169
|
background: var(--crust); border-radius: 6px; align-self: flex-start;
|
|
91
170
|
max-width: 90%; font-size: 12px; border: 1px solid var(--surface0);
|
|
@@ -93,28 +172,50 @@ export const STYLES = ` :root {
|
|
|
93
172
|
.tool-call summary {
|
|
94
173
|
padding: 6px 10px; color: var(--subtext1); cursor: pointer;
|
|
95
174
|
user-select: none; list-style: none;
|
|
96
|
-
|
|
175
|
+
display: flex; align-items: center; gap: 8px;
|
|
97
176
|
}
|
|
98
177
|
.tool-call summary::-webkit-details-marker { display: none; }
|
|
99
|
-
.tool-call summary::before { content: "▶
|
|
100
|
-
.tool-call[open] > summary::before { content: "▼
|
|
178
|
+
.tool-call summary::before { content: "▶"; flex-shrink: 0; font-size: 9px; color: var(--subtext0); }
|
|
179
|
+
.tool-call[open] > summary::before { content: "▼"; }
|
|
101
180
|
.tool-call.error > summary { color: var(--red); }
|
|
181
|
+
/* The summary is a single line: the label ellipsizes (full text in the title
|
|
182
|
+
tooltip / on expand), badges and timing stay pinned to the right. */
|
|
183
|
+
.tool-label {
|
|
184
|
+
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
185
|
+
font-family: ui-monospace, monospace;
|
|
186
|
+
}
|
|
187
|
+
.tool-badge { flex-shrink: 0; color: var(--subtext0); font-size: 11px; }
|
|
188
|
+
.tool-elapsed { flex-shrink: 0; color: var(--subtext0); font-size: 11px; }
|
|
102
189
|
.tool-call pre {
|
|
103
190
|
padding: 8px 12px; overflow-y: auto;
|
|
104
191
|
color: var(--subtext1); font-size: 11px; max-height: 280px;
|
|
105
192
|
border-top: 1px solid var(--surface0);
|
|
106
193
|
white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
|
|
107
194
|
}
|
|
108
|
-
.tool-spin { color: var(--mauve);
|
|
195
|
+
.tool-spin { color: var(--mauve); flex-shrink: 0; font-family: ui-monospace, monospace; font-size: 13px; }
|
|
196
|
+
/* Line diff for edit/write tools (tints derived from --green/--red). */
|
|
197
|
+
.tool-diff { border-top: 1px solid var(--surface0); overflow-x: auto; }
|
|
198
|
+
.diff { font-family: ui-monospace, monospace; font-size: 11px; padding: 4px 0; }
|
|
199
|
+
.diff-line { white-space: pre; padding: 0 10px; }
|
|
200
|
+
.diff-sign { display: inline-block; width: 1ch; margin-right: 8px; color: var(--subtext0); }
|
|
201
|
+
.diff-add { background: color-mix(in srgb, var(--green) 14%, transparent); color: var(--green); }
|
|
202
|
+
.diff-del { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--red); }
|
|
203
|
+
.diff-ctx { color: var(--subtext1); }
|
|
109
204
|
.code-block {
|
|
110
205
|
background: var(--crust); border: 1px solid var(--surface0);
|
|
111
206
|
border-radius: 6px; overflow: hidden; margin: 4px 0;
|
|
112
207
|
align-self: stretch; max-width: 100%; font-size: 12px;
|
|
113
208
|
}
|
|
209
|
+
/* Header row above a code block: language label on the left, copy button on
|
|
210
|
+
the right (the surface bar that used to live on .code-lang). */
|
|
211
|
+
.code-head {
|
|
212
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
213
|
+
background: var(--surface0);
|
|
214
|
+
}
|
|
114
215
|
.code-lang {
|
|
115
|
-
|
|
116
|
-
font-size: 10px; padding: 3px 10px; letter-spacing: 0.05em;
|
|
216
|
+
color: var(--subtext0); font-size: 10px; padding: 3px 10px; letter-spacing: 0.05em;
|
|
117
217
|
}
|
|
218
|
+
.code-head .copy-btn { padding: 3px 10px; }
|
|
118
219
|
.code-block code {
|
|
119
220
|
display: block; padding: 10px 12px; overflow-x: auto;
|
|
120
221
|
color: var(--text); white-space: pre; line-height: 1.55;
|
|
@@ -124,6 +225,35 @@ export const STYLES = ` :root {
|
|
|
124
225
|
.hl-cmt { color: var(--subtext0); font-style: italic; }
|
|
125
226
|
.hl-num { color: var(--blue); }
|
|
126
227
|
.hl-fn { color: var(--yellow); }
|
|
228
|
+
/* Hand-rolled markdown, applied only to assistant bubbles + the recommendation
|
|
229
|
+
panel (both get the .md class from setContent). Block elements lay themselves
|
|
230
|
+
out, so switch off the container's pre-wrap for these. */
|
|
231
|
+
.md { white-space: normal; }
|
|
232
|
+
.md > :first-child { margin-top: 0; }
|
|
233
|
+
.md > :last-child { margin-bottom: 0; }
|
|
234
|
+
.md .md-h { color: var(--mauve); font-weight: 700; line-height: 1.3; margin: 10px 0 4px; }
|
|
235
|
+
.md .md-h1 { font-size: 1.35em; }
|
|
236
|
+
.md .md-h2 { font-size: 1.2em; }
|
|
237
|
+
.md .md-h3 { font-size: 1.08em; }
|
|
238
|
+
.md .md-h4, .md .md-h5, .md .md-h6 { font-size: 1em; }
|
|
239
|
+
.md .md-p { margin: 6px 0; }
|
|
240
|
+
.md .md-list { margin: 6px 0; padding-left: 22px; }
|
|
241
|
+
.md .md-list li { margin: 2px 0; }
|
|
242
|
+
.md .md-task { list-style: none; margin-left: -22px; }
|
|
243
|
+
.md .md-check { color: var(--green); }
|
|
244
|
+
.md .md-quote { border-left: 3px solid var(--surface2); margin: 6px 0; padding: 2px 0 2px 10px; color: var(--subtext1); }
|
|
245
|
+
.md .md-hr { border: none; border-top: 1px solid var(--surface1); margin: 10px 0; }
|
|
246
|
+
.md a { color: var(--blue); text-decoration: underline; }
|
|
247
|
+
.md strong { color: var(--text); font-weight: 700; }
|
|
248
|
+
.md em { font-style: italic; }
|
|
249
|
+
.md del { color: var(--subtext0); }
|
|
250
|
+
.md .md-code {
|
|
251
|
+
background: var(--crust); border: 1px solid var(--surface0);
|
|
252
|
+
border-radius: 4px; padding: 1px 4px; font-size: 0.92em;
|
|
253
|
+
}
|
|
254
|
+
.md .md-table { border-collapse: collapse; margin: 8px 0; font-size: 0.95em; display: block; overflow-x: auto; }
|
|
255
|
+
.md .md-table th, .md .md-table td { border: 1px solid var(--surface1); padding: 4px 8px; text-align: left; }
|
|
256
|
+
.md .md-table th { background: var(--surface0); color: var(--subtext1); }
|
|
127
257
|
#input-bar {
|
|
128
258
|
background: var(--mantle); padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
|
|
129
259
|
display: flex; gap: 8px; flex-shrink: 0;
|
|
@@ -160,6 +290,13 @@ export const STYLES = ` :root {
|
|
|
160
290
|
white-space: nowrap; align-self: flex-end;
|
|
161
291
|
}
|
|
162
292
|
#send:disabled, #input:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
293
|
+
/* While the agent runs, Send becomes a red Stop; the armed (tap-to-confirm)
|
|
294
|
+
state brightens it and adds a halo, mirroring the prompt card's cancel. */
|
|
295
|
+
#send.stop { background: var(--red); color: var(--crust); }
|
|
296
|
+
#send.stop.armed {
|
|
297
|
+
filter: brightness(1.12);
|
|
298
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 45%, transparent);
|
|
299
|
+
}
|
|
163
300
|
#reconnect-overlay {
|
|
164
301
|
display: none; position: fixed; inset: 0;
|
|
165
302
|
background: rgba(30,30,46,0.88); color: var(--subtext1);
|
|
@@ -175,6 +312,27 @@ export const STYLES = ` :root {
|
|
|
175
312
|
}
|
|
176
313
|
#status-panel { padding: 6px 12px; border-bottom: 1px solid var(--surface1);
|
|
177
314
|
color: var(--subtext1); white-space: pre-wrap; font-size: 13px; display: none; }
|
|
315
|
+
/* Structured task widget (progress bar + phase badge + elapsed). Replaces the
|
|
316
|
+
plain text lines when the server sends a structured data payload. */
|
|
317
|
+
#status-panel.structured { white-space: normal; display: block; }
|
|
318
|
+
.widget-top { display: flex; align-items: center; gap: 8px; }
|
|
319
|
+
.widget-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
|
|
320
|
+
white-space: nowrap; color: var(--text); font-size: 13px; }
|
|
321
|
+
.widget-phase { flex-shrink: 0; background: var(--surface0); color: var(--mauve);
|
|
322
|
+
border-radius: 4px; padding: 1px 7px; font-size: 10px; letter-spacing: 0.03em;
|
|
323
|
+
text-transform: uppercase; }
|
|
324
|
+
.widget-elapsed { flex-shrink: 0; color: var(--subtext0); font-size: 11px;
|
|
325
|
+
font-variant-numeric: tabular-nums; }
|
|
326
|
+
.widget-bar { height: 4px; background: var(--surface0); border-radius: 2px;
|
|
327
|
+
overflow: hidden; margin-top: 6px; }
|
|
328
|
+
.widget-bar-fill { height: 100%; background: var(--mauve); border-radius: 2px;
|
|
329
|
+
transition: width 0.3s ease; }
|
|
330
|
+
/* Dim per-turn timestamp shown under a committed turn's bubble. */
|
|
331
|
+
.turn-time { font-size: 10px; color: var(--subtext0); opacity: 0.55; padding: 0 4px;
|
|
332
|
+
margin-top: -2px; }
|
|
333
|
+
.turn-time.user { align-self: flex-end; }
|
|
334
|
+
.turn-time.assistant, .turn-time.system { align-self: flex-start; }
|
|
335
|
+
.turn-time.system { align-self: center; }
|
|
178
336
|
#prompt-card { position: fixed; left: 0; right: 0; bottom: 0; background: var(--mantle);
|
|
179
337
|
border-top: 2px solid var(--mauve); padding: 16px 14px calc(16px + env(safe-area-inset-bottom, 0px));
|
|
180
338
|
display: none; z-index: 50; max-height: 80dvh; overflow-y: auto; }
|
|
@@ -217,4 +375,13 @@ export const STYLES = ` :root {
|
|
|
217
375
|
#viewer { position: fixed; inset: 24px; background: var(--mantle); border: 1px solid var(--surface2);
|
|
218
376
|
border-radius: 8px; padding: 16px; overflow: auto; white-space: pre-wrap;
|
|
219
377
|
overflow-wrap: anywhere; word-break: break-word; display: none; z-index: 70; }
|
|
220
|
-
#viewer .close { position: absolute; top: 8px; right: 12px; cursor: pointer; color: var(--subtext0); }
|
|
378
|
+
#viewer .close { position: absolute; top: 8px; right: 12px; cursor: pointer; color: var(--subtext0); }
|
|
379
|
+
/* Desktop: center the transcript/status/input in a readable column instead of
|
|
380
|
+
hugging the left edge. The scrollbar stays at the true window edge; only the
|
|
381
|
+
content is inset. Mobile (below 960px) is unchanged. */
|
|
382
|
+
@media (min-width: 960px) {
|
|
383
|
+
#chat-log { padding-left: calc((100% - 920px) / 2); padding-right: calc((100% - 920px) / 2); }
|
|
384
|
+
#status-panel { padding-left: calc((100% - 920px) / 2 + 12px); padding-right: calc((100% - 920px) / 2 + 12px); }
|
|
385
|
+
#input-bar { padding-left: calc((100% - 920px) / 2); padding-right: calc((100% - 920px) / 2); }
|
|
386
|
+
#cmd-suggestions { left: calc((100% - 920px) / 2 + 16px); right: calc((100% - 920px) / 2 + 16px); }
|
|
387
|
+
}`;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Tool-call presentation helpers, shipped as a JS string and concatenated into
|
|
2
|
+
// the page by ui.ts. Turns raw {toolName, args} into a readable one-line summary,
|
|
3
|
+
// a +N −M diff badge, and an expandable line-diff body for edit/write tools —
|
|
4
|
+
// replacing the old "name + JSON.stringify(args) hard-sliced at 64 chars".
|
|
5
|
+
//
|
|
6
|
+
// Depends on escHtml (ui-render.ts) — both land in the same <script>.
|
|
7
|
+
/** Emitted client JS (top-level function declarations). */
|
|
8
|
+
export function toolsModule() {
|
|
9
|
+
return ` // args arrives as an object (live tool_start / snapshot part) or a JSON string.
|
|
10
|
+
function toolArgs(args) {
|
|
11
|
+
if (args && typeof args === 'object') return args;
|
|
12
|
+
if (typeof args === 'string') { try { return JSON.parse(args); } catch (e) { return null; } }
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// A readable one-line summary. Falls back to the old name+JSON form (but with NO
|
|
17
|
+
// hard 64-char slice — the summary line ellipsizes via CSS, full text on hover).
|
|
18
|
+
function toolSummary(toolName, args) {
|
|
19
|
+
var name = (toolName || '').toLowerCase();
|
|
20
|
+
var a = toolArgs(args);
|
|
21
|
+
function s(v) { return v == null ? '' : String(v); }
|
|
22
|
+
if (a) {
|
|
23
|
+
var path = a.path != null ? a.path : a.file_path;
|
|
24
|
+
if (name.indexOf('bash') >= 0 && a.command != null) return '$ ' + s(a.command);
|
|
25
|
+
if (name === 'read' && path != null) return 'read ' + s(path);
|
|
26
|
+
if ((name === 'edit' || name === 'multiedit' || name === 'write' || name === 'str_replace') && path != null) return s(path);
|
|
27
|
+
if (name === 'grep' || name === 'find' || name === 'glob' || name === 'search') {
|
|
28
|
+
var pat = a.pattern != null ? a.pattern : (a.query != null ? a.query : a.glob);
|
|
29
|
+
var loc = a.path != null ? a.path : a.glob;
|
|
30
|
+
return name + ' ' + s(pat) + (loc && loc !== pat ? ' ' + s(loc) : '');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
var argStr = typeof args === 'string' ? args : (args === undefined ? '' : JSON.stringify(args));
|
|
34
|
+
return toolName + (argStr ? ': ' + argStr : '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Line diff via LCS. Bounded: a huge pair falls back to "all removed / all added"
|
|
38
|
+
// rather than allocating an O(m*n) table that could hang the tab.
|
|
39
|
+
function lcsDiff(oldText, newText) {
|
|
40
|
+
// Treat '' / null as ZERO lines (not one empty line) so a new-file write shows
|
|
41
|
+
// as all-added with no phantom "removed blank line".
|
|
42
|
+
var a = (oldText == null || oldText === '') ? [] : String(oldText).split('\\n');
|
|
43
|
+
var b = (newText == null || newText === '') ? [] : String(newText).split('\\n');
|
|
44
|
+
var m = a.length, n = b.length;
|
|
45
|
+
var rows = [];
|
|
46
|
+
if (m * n > 250000) {
|
|
47
|
+
for (var x = 0; x < m; x++) rows.push({t: '-', s: a[x]});
|
|
48
|
+
for (var y = 0; y < n; y++) rows.push({t: '+', s: b[y]});
|
|
49
|
+
return rows;
|
|
50
|
+
}
|
|
51
|
+
var dp = [];
|
|
52
|
+
for (var i = 0; i <= m; i++) dp.push(new Array(n + 1).fill(0));
|
|
53
|
+
for (var i2 = m - 1; i2 >= 0; i2--)
|
|
54
|
+
for (var j2 = n - 1; j2 >= 0; j2--)
|
|
55
|
+
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
56
|
+
var i = 0, j = 0;
|
|
57
|
+
while (i < m && j < n) {
|
|
58
|
+
if (a[i] === b[j]) { rows.push({t: ' ', s: a[i]}); i++; j++; }
|
|
59
|
+
else if (dp[i + 1][j] >= dp[i][j + 1]) { rows.push({t: '-', s: a[i]}); i++; }
|
|
60
|
+
else { rows.push({t: '+', s: b[j]}); j++; }
|
|
61
|
+
}
|
|
62
|
+
while (i < m) { rows.push({t: '-', s: a[i++]}); }
|
|
63
|
+
while (j < n) { rows.push({t: '+', s: b[j++]}); }
|
|
64
|
+
return rows;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function diffStats(oldText, newText) {
|
|
68
|
+
var rows = lcsDiff(oldText, newText), add = 0, rem = 0;
|
|
69
|
+
for (var k = 0; k < rows.length; k++) { if (rows[k].t === '+') add++; else if (rows[k].t === '-') rem++; }
|
|
70
|
+
return {added: add, removed: rem};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function renderDiff(oldText, newText) {
|
|
74
|
+
var rows = lcsDiff(oldText, newText);
|
|
75
|
+
var html = '<div class="diff">';
|
|
76
|
+
for (var k = 0; k < rows.length; k++) {
|
|
77
|
+
var r = rows[k];
|
|
78
|
+
var cls = r.t === '+' ? 'diff-add' : (r.t === '-' ? 'diff-del' : 'diff-ctx');
|
|
79
|
+
html += '<div class="diff-line ' + cls + '"><span class="diff-sign">' + (r.t === ' ' ? ' ' : r.t) + '</span>' + escHtml(r.s) + '</div>';
|
|
80
|
+
}
|
|
81
|
+
return html + '</div>';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The old/new text for an edit/write tool's diff, or null if not a diffable tool.
|
|
85
|
+
function toolDiffPair(toolName, args) {
|
|
86
|
+
var name = (toolName || '').toLowerCase();
|
|
87
|
+
var a = toolArgs(args);
|
|
88
|
+
if (!a) return null;
|
|
89
|
+
if (name === 'edit' || name === 'multiedit' || name === 'str_replace') {
|
|
90
|
+
if (a.old_string != null || a.new_string != null) return {old: a.old_string, neu: a.new_string};
|
|
91
|
+
}
|
|
92
|
+
if (name === 'write' && a.content != null) return {old: '', neu: a.content};
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function toolBadge(toolName, args) {
|
|
97
|
+
var pair = toolDiffPair(toolName, args);
|
|
98
|
+
return pair ? diffStats(pair.old, pair.neu) : null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function toolDiffHtml(toolName, args) {
|
|
102
|
+
var pair = toolDiffPair(toolName, args);
|
|
103
|
+
return pair ? renderDiff(pair.old, pair.neu) : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function fmtElapsed(ms) {
|
|
107
|
+
if (ms == null || isNaN(ms)) return '';
|
|
108
|
+
if (ms < 1000) return Math.round(ms) + 'ms';
|
|
109
|
+
var sec = ms / 1000;
|
|
110
|
+
return (sec < 10 ? sec.toFixed(1) : Math.round(sec)) + 's';
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
}
|
package/dist/remote/ui.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { STYLES } from './ui-styles.js';
|
|
2
2
|
import { clientScript } from './ui-script.js';
|
|
3
|
+
import { renderModule } from './ui-render.js';
|
|
4
|
+
import { highlightModule } from './ui-highlight.js';
|
|
5
|
+
import { toolsModule } from './ui-tools.js';
|
|
3
6
|
export function html(wsUrl) {
|
|
4
7
|
const iconSvg = encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">`
|
|
5
8
|
+ `<rect width="180" height="180" rx="38" fill="#1e1e2e"/>`
|
|
@@ -35,9 +38,21 @@ ${STYLES}
|
|
|
35
38
|
<div id="header">
|
|
36
39
|
<span class="title">pi-task remote</span>
|
|
37
40
|
<div class="hgroup">
|
|
38
|
-
<
|
|
41
|
+
<span id="status-chip" title="Connection · model · context">
|
|
42
|
+
<span id="status-dot" class="disconnected"></span>
|
|
43
|
+
<span id="status-model"></span>
|
|
44
|
+
<span id="status-ctx"></span>
|
|
45
|
+
</span>
|
|
46
|
+
<button id="bell" aria-label="Notifications" title="Notifications">◯</button>
|
|
39
47
|
</div>
|
|
40
48
|
</div>
|
|
49
|
+
<div id="notif-panel" aria-hidden="true">
|
|
50
|
+
<div id="notif-toggle-row">
|
|
51
|
+
<span id="notif-title">Notifications</span>
|
|
52
|
+
<button id="notif-toggle" type="button"></button>
|
|
53
|
+
</div>
|
|
54
|
+
<div id="notif-list"></div>
|
|
55
|
+
</div>
|
|
41
56
|
<div id="chat-wrap">
|
|
42
57
|
<div id="chat-log"></div>
|
|
43
58
|
<button id="scroll-bottom" aria-label="Scroll to latest" title="Scroll to latest">↓</button>
|
|
@@ -61,6 +76,9 @@ ${STYLES}
|
|
|
61
76
|
</div>
|
|
62
77
|
<div id="viewer"><span class="close" id="viewer-close">✕</span><div id="viewer-body"></div></div>
|
|
63
78
|
<script>
|
|
79
|
+
${renderModule()}
|
|
80
|
+
${highlightModule()}
|
|
81
|
+
${toolsModule()}
|
|
64
82
|
${clientScript(wsUrl)}
|
|
65
83
|
</script>
|
|
66
84
|
</body>
|
package/dist/task/impl-widget.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Only one task runs at a time (single active task), so a single module-level slot
|
|
21
21
|
* is sufficient.
|
|
22
22
|
*/
|
|
23
|
-
import { WIDGET_KEY, WIDGET_REFRESH_MS, buildImplLines } from './widget.js';
|
|
23
|
+
import { WIDGET_KEY, WIDGET_REFRESH_MS, buildImplLines, buildImplData } from './widget.js';
|
|
24
24
|
import { setTaskWidget } from '../remote/session-state.js';
|
|
25
25
|
let armed = null;
|
|
26
26
|
let lastLine;
|
|
@@ -52,7 +52,7 @@ function render() {
|
|
|
52
52
|
catch {
|
|
53
53
|
/* stale ctx */
|
|
54
54
|
}
|
|
55
|
-
setTaskWidget(buildImplLines(state, undefined)); // un-themed for the wire
|
|
55
|
+
setTaskWidget(buildImplLines(state, undefined), buildImplData(state)); // un-themed for the wire
|
|
56
56
|
}
|
|
57
57
|
function startTimer() {
|
|
58
58
|
if (timer)
|
package/dist/task/widget.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ExtensionCommandContext } from '@earendil-works/pi-coding-agent';
|
|
8
8
|
import { type PhaseName, type TaskState } from './task-types.js';
|
|
9
|
+
import type { WidgetData } from '../remote/protocol.js';
|
|
9
10
|
export interface WidgetState {
|
|
10
11
|
taskId: string;
|
|
11
12
|
title: string;
|
|
@@ -40,6 +41,8 @@ export declare function contextThresholdColor(theme: WidgetTheme, percent: numbe
|
|
|
40
41
|
/** Render the `tokens/window [bar]` context suffix, or null when there's nothing to show. */
|
|
41
42
|
export declare function formatContextDetail(usage: ContextSnapshot, theme?: WidgetTheme): string | null;
|
|
42
43
|
export declare function buildWidgetLines(s: WidgetState, theme?: WidgetTheme): string[];
|
|
44
|
+
/** Structured mirror of buildWidgetLines for the browser's progress widget. */
|
|
45
|
+
export declare function buildWidgetData(s: WidgetState): WidgetData;
|
|
43
46
|
export declare function startWidget(ctx: ExtensionCommandContext, getState: () => WidgetState | null): () => void;
|
|
44
47
|
export interface AutoLoaderState {
|
|
45
48
|
title: string;
|
|
@@ -58,6 +61,9 @@ export interface AutoLoaderState {
|
|
|
58
61
|
kind?: 'planning' | 'enforce' | 'verify' | 'recommend' | 'lint-fix';
|
|
59
62
|
}
|
|
60
63
|
export declare function buildAutoLoaderLines(s: AutoLoaderState, theme?: WidgetTheme): string[];
|
|
64
|
+
/** Structured mirror of buildAutoLoaderLines. Only the numbered planning stage
|
|
65
|
+
* carries done/total; the enforce/verify/recommend/lint-fix passes are unnumbered. */
|
|
66
|
+
export declare function buildAutoLoaderData(s: AutoLoaderState): WidgetData;
|
|
61
67
|
/**
|
|
62
68
|
* Start the planning loader widget (same cadence/look as the phase widget).
|
|
63
69
|
* Returns a disposer that stops the refresh and clears the widget. No-op
|
|
@@ -74,4 +80,7 @@ export interface ImplState {
|
|
|
74
80
|
contextUsage?: ContextSnapshot;
|
|
75
81
|
}
|
|
76
82
|
export declare function buildImplLines(s: ImplState, theme?: WidgetTheme): string[];
|
|
83
|
+
/** Structured mirror of buildImplLines (the host implementation turn — no step
|
|
84
|
+
* numbering, so no progress bar; just the phase badge and elapsed clock). */
|
|
85
|
+
export declare function buildImplData(s: ImplState): WidgetData;
|
|
77
86
|
export declare function flashTerminalWidget(ctx: ExtensionCommandContext, state: Exclude<TaskState, 'pending' | 'in_progress' | 'completed'>, taskId: string, reason: string | undefined): void;
|
package/dist/task/widget.js
CHANGED
|
@@ -88,6 +88,18 @@ export function buildWidgetLines(s, theme) {
|
|
|
88
88
|
lines.push(trailer);
|
|
89
89
|
return lines;
|
|
90
90
|
}
|
|
91
|
+
/** Structured mirror of buildWidgetLines for the browser's progress widget. */
|
|
92
|
+
export function buildWidgetData(s) {
|
|
93
|
+
const idx = PHASE_INDEX[s.phase];
|
|
94
|
+
const total = PHASE_ORDER.length;
|
|
95
|
+
return {
|
|
96
|
+
title: `${s.taskId} · ${titleForDisplay(s)}`,
|
|
97
|
+
phase: s.phase,
|
|
98
|
+
done: Math.min(idx + 1, total),
|
|
99
|
+
total,
|
|
100
|
+
elapsed: formatDuration(Date.now() - s.startedAt)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
91
103
|
// ─── Widget lifecycle ────────────────────────────────────────────────────────
|
|
92
104
|
export function startWidget(ctx, getState) {
|
|
93
105
|
if (!ctx.hasUI)
|
|
@@ -102,7 +114,7 @@ export function startWidget(ctx, getState) {
|
|
|
102
114
|
catch {
|
|
103
115
|
/* stale ctx */
|
|
104
116
|
}
|
|
105
|
-
setTaskWidget(plain);
|
|
117
|
+
setTaskWidget(plain, s ? buildWidgetData(s) : null);
|
|
106
118
|
};
|
|
107
119
|
render();
|
|
108
120
|
const timer = setInterval(render, WIDGET_REFRESH_MS);
|
|
@@ -137,6 +149,25 @@ export function buildAutoLoaderLines(s, theme) {
|
|
|
137
149
|
lines.push(trailer);
|
|
138
150
|
return lines;
|
|
139
151
|
}
|
|
152
|
+
/** Structured mirror of buildAutoLoaderLines. Only the numbered planning stage
|
|
153
|
+
* carries done/total; the enforce/verify/recommend/lint-fix passes are unnumbered. */
|
|
154
|
+
export function buildAutoLoaderData(s) {
|
|
155
|
+
const phase = s.kind === 'enforce' ? 'enforcing guidelines'
|
|
156
|
+
: s.kind === 'verify' ? 'verifying work'
|
|
157
|
+
: s.kind === 'recommend' ? 'assessing the failure'
|
|
158
|
+
: s.kind === 'lint-fix' ? 'fixing static findings'
|
|
159
|
+
: s.step;
|
|
160
|
+
const d = {
|
|
161
|
+
title: `/task-auto · ${s.title}`,
|
|
162
|
+
phase,
|
|
163
|
+
elapsed: formatDuration(Date.now() - s.startedAt)
|
|
164
|
+
};
|
|
165
|
+
if (!s.kind || s.kind === 'planning') {
|
|
166
|
+
d.done = s.stepNum;
|
|
167
|
+
d.total = s.stepTotal;
|
|
168
|
+
}
|
|
169
|
+
return d;
|
|
170
|
+
}
|
|
140
171
|
/**
|
|
141
172
|
* Start the planning loader widget (same cadence/look as the phase widget).
|
|
142
173
|
* Returns a disposer that stops the refresh and clears the widget. No-op
|
|
@@ -155,7 +186,7 @@ export function startAutoLoader(ctx, getState) {
|
|
|
155
186
|
catch {
|
|
156
187
|
/* stale ctx */
|
|
157
188
|
}
|
|
158
|
-
setTaskWidget(plain);
|
|
189
|
+
setTaskWidget(plain, s ? buildAutoLoaderData(s) : null);
|
|
159
190
|
};
|
|
160
191
|
render();
|
|
161
192
|
const timer = setInterval(render, WIDGET_REFRESH_MS);
|
|
@@ -186,6 +217,15 @@ export function buildImplLines(s, theme) {
|
|
|
186
217
|
lines.push(trailer);
|
|
187
218
|
return lines;
|
|
188
219
|
}
|
|
220
|
+
/** Structured mirror of buildImplLines (the host implementation turn — no step
|
|
221
|
+
* numbering, so no progress bar; just the phase badge and elapsed clock). */
|
|
222
|
+
export function buildImplData(s) {
|
|
223
|
+
return {
|
|
224
|
+
title: `${s.taskId} · ${titleForDisplay(s)}`,
|
|
225
|
+
phase: 'implementing',
|
|
226
|
+
elapsed: formatDuration(Date.now() - s.startedAt)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
189
229
|
export function flashTerminalWidget(ctx, state, taskId, reason) {
|
|
190
230
|
if (!ctx.hasUI)
|
|
191
231
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.23",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|