@mjasnikovs/pi-task 0.17.22 → 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 +9 -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 +16 -5
- package/dist/remote/session-state.js +44 -8
- package/dist/remote/ui-render.js +6 -2
- package/dist/remote/ui-script.js +373 -23
- package/dist/remote/ui-styles.d.ts +1 -1
- package/dist/remote/ui-styles.js +116 -3
- package/dist/remote/ui.js +13 -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 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 .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 /* 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 #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); }\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 }";
|
|
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);
|
|
@@ -127,10 +206,16 @@ export const STYLES = ` :root {
|
|
|
127
206
|
border-radius: 6px; overflow: hidden; margin: 4px 0;
|
|
128
207
|
align-self: stretch; max-width: 100%; font-size: 12px;
|
|
129
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
|
+
}
|
|
130
215
|
.code-lang {
|
|
131
|
-
|
|
132
|
-
font-size: 10px; padding: 3px 10px; letter-spacing: 0.05em;
|
|
216
|
+
color: var(--subtext0); font-size: 10px; padding: 3px 10px; letter-spacing: 0.05em;
|
|
133
217
|
}
|
|
218
|
+
.code-head .copy-btn { padding: 3px 10px; }
|
|
134
219
|
.code-block code {
|
|
135
220
|
display: block; padding: 10px 12px; overflow-x: auto;
|
|
136
221
|
color: var(--text); white-space: pre; line-height: 1.55;
|
|
@@ -205,6 +290,13 @@ export const STYLES = ` :root {
|
|
|
205
290
|
white-space: nowrap; align-self: flex-end;
|
|
206
291
|
}
|
|
207
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
|
+
}
|
|
208
300
|
#reconnect-overlay {
|
|
209
301
|
display: none; position: fixed; inset: 0;
|
|
210
302
|
background: rgba(30,30,46,0.88); color: var(--subtext1);
|
|
@@ -220,6 +312,27 @@ export const STYLES = ` :root {
|
|
|
220
312
|
}
|
|
221
313
|
#status-panel { padding: 6px 12px; border-bottom: 1px solid var(--surface1);
|
|
222
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; }
|
|
223
336
|
#prompt-card { position: fixed; left: 0; right: 0; bottom: 0; background: var(--mantle);
|
|
224
337
|
border-top: 2px solid var(--mauve); padding: 16px 14px calc(16px + env(safe-area-inset-bottom, 0px));
|
|
225
338
|
display: none; z-index: 50; max-height: 80dvh; overflow-y: auto; }
|
package/dist/remote/ui.js
CHANGED
|
@@ -38,9 +38,21 @@ ${STYLES}
|
|
|
38
38
|
<div id="header">
|
|
39
39
|
<span class="title">pi-task remote</span>
|
|
40
40
|
<div class="hgroup">
|
|
41
|
-
<
|
|
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>
|
|
42
47
|
</div>
|
|
43
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>
|
|
44
56
|
<div id="chat-wrap">
|
|
45
57
|
<div id="chat-log"></div>
|
|
46
58
|
<button id="scroll-bottom" aria-label="Scroll to latest" title="Scroll to latest">↓</button>
|
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",
|