@mjasnikovs/pi-task 0.17.20 → 0.17.22
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/LICENSE +661 -21
- package/README.md +10 -2
- package/dist/remote/history.d.ts +2 -0
- package/dist/remote/session-state.d.ts +3 -0
- package/dist/remote/session-state.js +11 -1
- 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 +169 -0
- package/dist/remote/ui-script.js +53 -124
- package/dist/remote/ui-styles.d.ts +1 -1
- package/dist/remote/ui-styles.js +59 -5
- package/dist/remote/ui-tools.d.ts +2 -0
- package/dist/remote/ui-tools.js +113 -0
- package/dist/remote/ui.js +6 -0
- package/package.json +2 -2
|
@@ -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 #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 }";
|
package/dist/remote/ui-styles.js
CHANGED
|
@@ -93,19 +93,35 @@ export const STYLES = ` :root {
|
|
|
93
93
|
.tool-call summary {
|
|
94
94
|
padding: 6px 10px; color: var(--subtext1); cursor: pointer;
|
|
95
95
|
user-select: none; list-style: none;
|
|
96
|
-
|
|
96
|
+
display: flex; align-items: center; gap: 8px;
|
|
97
97
|
}
|
|
98
98
|
.tool-call summary::-webkit-details-marker { display: none; }
|
|
99
|
-
.tool-call summary::before { content: "▶
|
|
100
|
-
.tool-call[open] > summary::before { content: "▼
|
|
99
|
+
.tool-call summary::before { content: "▶"; flex-shrink: 0; font-size: 9px; color: var(--subtext0); }
|
|
100
|
+
.tool-call[open] > summary::before { content: "▼"; }
|
|
101
101
|
.tool-call.error > summary { color: var(--red); }
|
|
102
|
+
/* The summary is a single line: the label ellipsizes (full text in the title
|
|
103
|
+
tooltip / on expand), badges and timing stay pinned to the right. */
|
|
104
|
+
.tool-label {
|
|
105
|
+
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
106
|
+
font-family: ui-monospace, monospace;
|
|
107
|
+
}
|
|
108
|
+
.tool-badge { flex-shrink: 0; color: var(--subtext0); font-size: 11px; }
|
|
109
|
+
.tool-elapsed { flex-shrink: 0; color: var(--subtext0); font-size: 11px; }
|
|
102
110
|
.tool-call pre {
|
|
103
111
|
padding: 8px 12px; overflow-y: auto;
|
|
104
112
|
color: var(--subtext1); font-size: 11px; max-height: 280px;
|
|
105
113
|
border-top: 1px solid var(--surface0);
|
|
106
114
|
white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
|
|
107
115
|
}
|
|
108
|
-
.tool-spin { color: var(--mauve);
|
|
116
|
+
.tool-spin { color: var(--mauve); flex-shrink: 0; font-family: ui-monospace, monospace; font-size: 13px; }
|
|
117
|
+
/* Line diff for edit/write tools (tints derived from --green/--red). */
|
|
118
|
+
.tool-diff { border-top: 1px solid var(--surface0); overflow-x: auto; }
|
|
119
|
+
.diff { font-family: ui-monospace, monospace; font-size: 11px; padding: 4px 0; }
|
|
120
|
+
.diff-line { white-space: pre; padding: 0 10px; }
|
|
121
|
+
.diff-sign { display: inline-block; width: 1ch; margin-right: 8px; color: var(--subtext0); }
|
|
122
|
+
.diff-add { background: color-mix(in srgb, var(--green) 14%, transparent); color: var(--green); }
|
|
123
|
+
.diff-del { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--red); }
|
|
124
|
+
.diff-ctx { color: var(--subtext1); }
|
|
109
125
|
.code-block {
|
|
110
126
|
background: var(--crust); border: 1px solid var(--surface0);
|
|
111
127
|
border-radius: 6px; overflow: hidden; margin: 4px 0;
|
|
@@ -124,6 +140,35 @@ export const STYLES = ` :root {
|
|
|
124
140
|
.hl-cmt { color: var(--subtext0); font-style: italic; }
|
|
125
141
|
.hl-num { color: var(--blue); }
|
|
126
142
|
.hl-fn { color: var(--yellow); }
|
|
143
|
+
/* Hand-rolled markdown, applied only to assistant bubbles + the recommendation
|
|
144
|
+
panel (both get the .md class from setContent). Block elements lay themselves
|
|
145
|
+
out, so switch off the container's pre-wrap for these. */
|
|
146
|
+
.md { white-space: normal; }
|
|
147
|
+
.md > :first-child { margin-top: 0; }
|
|
148
|
+
.md > :last-child { margin-bottom: 0; }
|
|
149
|
+
.md .md-h { color: var(--mauve); font-weight: 700; line-height: 1.3; margin: 10px 0 4px; }
|
|
150
|
+
.md .md-h1 { font-size: 1.35em; }
|
|
151
|
+
.md .md-h2 { font-size: 1.2em; }
|
|
152
|
+
.md .md-h3 { font-size: 1.08em; }
|
|
153
|
+
.md .md-h4, .md .md-h5, .md .md-h6 { font-size: 1em; }
|
|
154
|
+
.md .md-p { margin: 6px 0; }
|
|
155
|
+
.md .md-list { margin: 6px 0; padding-left: 22px; }
|
|
156
|
+
.md .md-list li { margin: 2px 0; }
|
|
157
|
+
.md .md-task { list-style: none; margin-left: -22px; }
|
|
158
|
+
.md .md-check { color: var(--green); }
|
|
159
|
+
.md .md-quote { border-left: 3px solid var(--surface2); margin: 6px 0; padding: 2px 0 2px 10px; color: var(--subtext1); }
|
|
160
|
+
.md .md-hr { border: none; border-top: 1px solid var(--surface1); margin: 10px 0; }
|
|
161
|
+
.md a { color: var(--blue); text-decoration: underline; }
|
|
162
|
+
.md strong { color: var(--text); font-weight: 700; }
|
|
163
|
+
.md em { font-style: italic; }
|
|
164
|
+
.md del { color: var(--subtext0); }
|
|
165
|
+
.md .md-code {
|
|
166
|
+
background: var(--crust); border: 1px solid var(--surface0);
|
|
167
|
+
border-radius: 4px; padding: 1px 4px; font-size: 0.92em;
|
|
168
|
+
}
|
|
169
|
+
.md .md-table { border-collapse: collapse; margin: 8px 0; font-size: 0.95em; display: block; overflow-x: auto; }
|
|
170
|
+
.md .md-table th, .md .md-table td { border: 1px solid var(--surface1); padding: 4px 8px; text-align: left; }
|
|
171
|
+
.md .md-table th { background: var(--surface0); color: var(--subtext1); }
|
|
127
172
|
#input-bar {
|
|
128
173
|
background: var(--mantle); padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
|
|
129
174
|
display: flex; gap: 8px; flex-shrink: 0;
|
|
@@ -217,4 +262,13 @@ export const STYLES = ` :root {
|
|
|
217
262
|
#viewer { position: fixed; inset: 24px; background: var(--mantle); border: 1px solid var(--surface2);
|
|
218
263
|
border-radius: 8px; padding: 16px; overflow: auto; white-space: pre-wrap;
|
|
219
264
|
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); }
|
|
265
|
+
#viewer .close { position: absolute; top: 8px; right: 12px; cursor: pointer; color: var(--subtext0); }
|
|
266
|
+
/* Desktop: center the transcript/status/input in a readable column instead of
|
|
267
|
+
hugging the left edge. The scrollbar stays at the true window edge; only the
|
|
268
|
+
content is inset. Mobile (below 960px) is unchanged. */
|
|
269
|
+
@media (min-width: 960px) {
|
|
270
|
+
#chat-log { padding-left: calc((100% - 920px) / 2); padding-right: calc((100% - 920px) / 2); }
|
|
271
|
+
#status-panel { padding-left: calc((100% - 920px) / 2 + 12px); padding-right: calc((100% - 920px) / 2 + 12px); }
|
|
272
|
+
#input-bar { padding-left: calc((100% - 920px) / 2); padding-right: calc((100% - 920px) / 2); }
|
|
273
|
+
#cmd-suggestions { left: calc((100% - 920px) / 2 + 16px); right: calc((100% - 920px) / 2 + 16px); }
|
|
274
|
+
}`;
|
|
@@ -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"/>`
|
|
@@ -61,6 +64,9 @@ ${STYLES}
|
|
|
61
64
|
</div>
|
|
62
65
|
<div id="viewer"><span class="close" id="viewer-close">✕</span><div id="viewer-body"></div></div>
|
|
63
66
|
<script>
|
|
67
|
+
${renderModule()}
|
|
68
|
+
${highlightModule()}
|
|
69
|
+
${toolsModule()}
|
|
64
70
|
${clientScript(wsUrl)}
|
|
65
71
|
</script>
|
|
66
72
|
</body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.22",
|
|
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",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"type": "git",
|
|
81
81
|
"url": "git+https://github.com/mjasnikovs/pi-task.git"
|
|
82
82
|
},
|
|
83
|
-
"license": "
|
|
83
|
+
"license": "AGPL-3.0-only",
|
|
84
84
|
"pi": {
|
|
85
85
|
"extensions": [
|
|
86
86
|
"dist/index.js"
|