fisi-dock 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +5 -3
- package/docs/reference.md +7 -6
- package/lib/label-wave.ts +9 -6
- package/lib/palette.ts +84 -46
- package/lib/panel.ts +6 -5
- package/lib/prompt-box.ts +3 -3
- package/lib/prompt-line.ts +6 -5
- package/package.json +1 -1
- package/themes/Fisi-Dock.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Vivid labels
|
|
6
|
+
|
|
7
|
+
- The labels take the vividness of the cover title `#F43888`, each on its own hue: HSL saturation 90% and lightness 59%, lighter in steps of 0.01 (at most 0.80) only where a hue would read under 4.5:1 on the `#161616` background. One helper, `vivid` in `lib/palette.ts`.
|
|
8
|
+
- Panel section titles (`session`, `heads-up`, `ODD`, `RDD`, `CI`, `agents`, `todo`, `changes`) and the counters on their top rule (`3/7`, `on`, the agents card's count) draw vivid on the rail, in `/dock` and on the narrow-screen todo and agents cards. Frames stay gray, rows keep their colors, and gray counters (`1 to commit`, the CI sha, `off`) stay muted.
|
|
9
|
+
- On the prompt box, the permission mode and its dot (vivid red), the effort (vivid blue) and the clock wave on the working label (`ODD checking…`, vivid pink and lilac) draw vivid; the model stays muted. The line under the prompt paints the same labels, and the petal Fisi Dock turns with the wave takes the vivid theme tones, like the one Gentle Shell paints from the theme. Gentle Shell's own label and hint keep its colors; words, compaction and layout are unchanged.
|
|
10
|
+
- The `Fisi-Dock` theme's accents `pink`, `deepPink`, `teal`, `coral`, `amber`, `lilac`, `green`, `blue` and `red` take their vivid values (amber `#e0af68` -> `#f5a838`, blue `#7b9cff` -> `#4c76f6`, and the rest); neutrals and backgrounds stay.
|
|
11
|
+
|
|
3
12
|
## 0.2.1
|
|
4
13
|
|
|
5
14
|
### Project
|
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ It is a Pi package (an extension and a theme) that paints its own panel in Gentl
|
|
|
45
45
|
- **Prompt box**: Gentle Shell's prompt framed, with the working label (`ODD exploring…`), the permission mode, the model and the effort on its top border.
|
|
46
46
|
- **Narrow screens and phones**: a one-line top row, a strip above the prompt, and compact agents and todo cards.
|
|
47
47
|
- **`/dock`** and **`/fisi-dock`**: the whole panel over the screen, and an on/off switch that needs no uninstall ([Commands](#commands)).
|
|
48
|
-
- **`Fisi-Dock` theme**: the panel's
|
|
48
|
+
- **`Fisi-Dock` theme**: the panel's hues, vivid, for the whole of Pi, applied once on the first start with your own theme remembered for `/fisi-dock off` ([Theme](#theme)).
|
|
49
49
|
|
|
50
50
|
Every section, row and data source is described in [docs/reference.md](docs/reference.md).
|
|
51
51
|
|
|
@@ -80,9 +80,9 @@ Install it into Gentle Shell from GitHub, so it loads every time Gentle Shell st
|
|
|
80
80
|
gentle-shell install git:github.com/fisidj/fisi-dock
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Without a tag you follow the `main` branch. Releases are tagged `vX.Y.Z` and listed on the [Releases page](https://github.com/fisidj/fisi-dock/releases); the latest is `v0.
|
|
83
|
+
Without a tag you follow the `main` branch. Releases are tagged `vX.Y.Z` and listed on the [Releases page](https://github.com/fisidj/fisi-dock/releases); the latest is `v0.3.0`. To pin one, add `@<tag>` to the source: `gentle-shell install git:github.com/fisidj/fisi-dock@v0.3.0`. Pi keeps a pinned tag: updates do not move it.
|
|
84
84
|
|
|
85
|
-
Fisi Dock is also on npm: `gentle-shell install npm:fisi-dock`, pinned with `npm:fisi-dock@0.
|
|
85
|
+
Fisi Dock is also on npm: `gentle-shell install npm:fisi-dock`, pinned with `npm:fisi-dock@0.3.0`.
|
|
86
86
|
|
|
87
87
|
If you run Gentle Shell with `--link` (Pi's own home instead of the launcher's isolated one), install it there instead: `gentle-shell --link install git:github.com/fisidj/fisi-dock`.
|
|
88
88
|
|
|
@@ -129,6 +129,8 @@ Gentle Shell and Pi controls that change what Fisi Dock shows:
|
|
|
129
129
|
|
|
130
130
|
`Fisi-Dock` is a Pi theme with the panel's palette and a pink-tinted selection. It colors the whole of Pi, not only the panel. It is optional: the panel keeps its own colors with any theme.
|
|
131
131
|
|
|
132
|
+
**Vivid labels.** The labels wear the vividness of the cover title `#F43888`, each on its own hue: the theme's accents (pink, deep pink, teal, coral, amber, lilac, green, blue, red), the panel's section titles and the counters on their top rule (`3/7`, `on`), and the labels Fisi Dock paints on the prompt box (`● SUPER-YOLO`, the effort, the `ODD exploring…` wave). Each color keeps its hue at HSL saturation 90% and lightness 59%, lighter where a hue would read under 4.5:1 on the `#161616` background (blue, lilac). The frames, the rows inside the sections, gray labels (the model, `1 to commit`), the neutrals and the backgrounds keep their colors.
|
|
133
|
+
|
|
132
134
|
**The first start applies it once.** The first session with Fisi Dock on, inside Gentle Shell, remembers the theme you use, then switches Pi to `Fisi-Dock` and says so: `Fisi Dock applied its theme · /fisi-dock off puts your theme dark back`. This happens once, also on an install from before this step; every later start leaves your theme alone. It waits for the next start when Pi does not know the `Fisi-Dock` theme or cannot set it, and it never changes your theme when Fisi Dock cannot save the one you had. It never runs in a delegated subagent session, and a Fisi Dock that is off does nothing at start: `/fisi-dock on` sets the theme instead.
|
|
133
135
|
|
|
134
136
|
| How | What happens |
|
package/docs/reference.md
CHANGED
|
@@ -6,17 +6,18 @@ Everything Fisi Dock draws, where each piece of data comes from, and how it fits
|
|
|
6
6
|
|
|
7
7
|
While Gentle Shell's fullscreen rail is on screen (fullscreen mode, terminal at least 140 columns wide, Status placement `auto` or `right`):
|
|
8
8
|
|
|
9
|
-
- **Side panel**: replaces the rail's Status section with the Fisi Dock panel: the GENTLESHELL wordmark, then framed `session`, `heads-up` (only when something is left open), `ODD` (always on screen), `RDD`, `CI` (only while the branch has GitHub Actions runs), `agents`, `todo` (always on screen) and `changes` sections, in that order, each section in its own color. Context, cost, the model profile, Engram memory and MCP servers are rows inside `session`; reviews have their own `RDD` section; there is no footer under the panel. Gentle Shell builds that wrap rail parts in their own cards (the transparent chrome surface on its `main` branch) get every panel row as a loose row, so the panel keeps its own frames instead of sitting inside a host card that would cut its right border.
|
|
9
|
+
- **Side panel**: replaces the rail's Status section with the Fisi Dock panel: the GENTLESHELL wordmark, then framed `session`, `heads-up` (only when something is left open), `ODD` (always on screen), `RDD`, `CI` (only while the branch has GitHub Actions runs), `agents`, `todo` (always on screen) and `changes` sections, in that order, each section in its own color: its title and the counters on its top rule (`3/7`, `on`) in that color made vivid (see **Vivid labels** below), the frame gray and the rows in their own colors. Context, cost, the model profile, Engram memory and MCP servers are rows inside `session`; reviews have their own `RDD` section; there is no footer under the panel. Gentle Shell builds that wrap rail parts in their own cards (the transparent chrome surface on its `main` branch) get every panel row as a loose row, so the panel keeps its own frames instead of sitting inside a host card that would cut its right border.
|
|
10
10
|
- **Only this panel in the rail**: Gentle Shell's own `todo`, `jobs` and `agents` rail sections and the status bar it paints under the rail (branch, permission mode, model, effort, context, cost) are taken over with parts that paint nothing, the same way the Status section and the top row are, so nothing repeats; Gentle Shell gets every original back when Fisi Dock unloads.
|
|
11
11
|
- **Short screens**: when the panel is taller than the terminal (its rows minus the top row and the gap under it), it leaves out, in this order and only as far as needed, the `session` section's three group labels (`where ──`, `usage ──`, `connections ──`), then the `│` spacer rows between ODD steps and between RDD reviews (the gray lines under finished ODD steps and finished RDD reviews drop with them; the active step and a running review keep their line; the gray lines under the `CI` rows drop with them too), then the `changes` section, then the `CI` section when every check passed, then the `CI` section while a check runs, waits or failed, then the `RDD` section together with every ODD row but the latest step while a request runs. The wordmark, `session`, `heads-up`, `ODD` (at least its title and one row), `agents` and `todo` are never left out; if the panel is still too tall, the rail scrolls. `/dock` is not affected: it scrolls and shows everything.
|
|
12
12
|
- **Start screen**: when Gentle Shell's `gentle:banner` command exists, the header Pi shows above the chat at startup is Fisi Dock's welcome card instead of Gentle Shell's rose banner (design 08): a gray frame with `Gentle Shell <version>` and a pink `with Fisi Dock`, `Ready in <project> on <branch>.`, the five commands to begin with (`/resume`, `/new`, `/changelog`, `/hotkeys`, `/quit`), and `Tip: alt+k opens the command palette.` under it, centered, 63 columns wide or the screen's width; under 34 columns only the title and ready lines show. The version is Gentle Shell's, from the `provisioned` record for this agent home in the `config.json` one folder above it (`4.6.0-main.dd37ffe` reads `4.6.0-main`); without it the title is `Gentle Shell`. It goes through Pi's `ctx.ui.setHeader`: Gentle Shell sets its banner a little after the session starts, which disposes the card, so the card takes the header back on the next microtask (at most 5 times per session). Without that command (no Gentle Shell) there is no card and no `loaded` box: Pi keeps its own header and listing.
|
|
13
13
|
- **Loaded box**: under the welcome card, Pi's list of loaded skills, prompts and extensions (and context files, when Pi lists them) is one box in the same frame (design start-screen/02): `loaded` on its top rule, one row per kind with the kind, its count right-aligned and as many names as fit, cut with `…` (`skills 24 btw, chained-pr, cognitive-doc-design, g…`), and `ctrl+o lists them` on its bottom rule, naming the key Pi binds to `app.tools.expand`: the one Pi's editor holds, live; when Fisi Dock cannot reach the editor's keybindings, the one in Pi's `keybindings.json` (in `PI_CODING_AGENT_DIR`, else `~/.pi/agent`), read once when the box installs, with the legacy `expandTools` id counted and a list of keys joined with `/` (`ctrl+e/alt+o`); with no override there, or a missing or malformed file, Pi's default `ctrl+o`. A key bound to nothing (`[]`) leaves the rule without a hint. A kind with nothing loaded is left out. The box shrinks with the card, and under 34 columns its rows print without a frame or hint. Pressing that key shows Pi's own full listing, grouped by scope with paths, and pressing it again brings the box back. Pi's `[Skill warnings]`, `[Prompt conflicts]`, `[Extension issues]` and `[Theme conflicts]` always print under the box as Pi prints them. The box shows only while the welcome card holds the header (while Gentle Shell's rose briefly replaces it, Pi's listing shows), and only when Pi prints its listing (not with `quietStartup`). How: Fisi Dock finds Pi's loaded-resources container next to the card in the TUI tree and wraps the render of that one instance (never a shared prototype), reading the names from Pi's collapsed sections on each render, so Pi refilling the list on `/reload` keeps the box. `/fisi-dock off` and session shutdown put Pi's own render back. When Pi's tree or its sections have a shape Fisi Dock does not recognize, Pi's listing stays as it is.
|
|
14
14
|
- **Top row**: shows only the project path. Model, effort, context and cost are in the panel and on the prompt box, so nothing repeats.
|
|
15
|
-
- **Prompt box**: Gentle Shell's prompt is closed into a box with the labels on its top rule: `╭─ ✿ waiting for input ──── ● SUPER-YOLO opus-5.5 · high ─╮`, then `│ › … │` rows, then a `╰────╯` bottom rule. The left label is Gentle Shell's own (`waiting for input`, `working…`, or the ODD phase while a request runs); when it is an ODD phase (`exploring…`, `implementing…`, `checking…` and the rest as Gentle Shell writes them) the box puts `ODD ` before it: `╭─ ✿ ODD exploring… ──── ● SUPER-YOLO opus-5.5 · high ─╮`, never for `waiting for input`, `working…` or `queued`. `ODD ` takes 4 columns: the right part still gives way first (it compacts, then drops), and only where even the bare box would not fit with it does the label show without it; the right part is the permission mode after a red dot (only when it is not `NORMAL`, the rule of Gentle Shell's own status bar), then the model and effort. Gentle Shell's own hint on that rule (`esc again to clear`, the vim mode) stays after the labels; its `[ Panel ]` button (the phone sheet with its rail sections, empty while Fisi Dock paints them) is left out, and a tap on the rule row does nothing. The full panel on the phone is `/dock`. The box paints whenever Fisi Dock paints: Gentle Shell's sidebar state on the terminal, in the wide rail chrome and in the narrow fullscreen chrome. How: Fisi Dock wraps Gentle Shell's editor factory (the wrapper carries Gentle Shell's own `gentle-pi.prompt-owner` marker, so Gentle Shell's shutdown and reinstall treat it as their own), renders Gentle Shell's editor four columns narrower and rebuilds only its frame rows: any rows above it and Pi's autocomplete rows under it stay untouched, the editor's invisible cursor markers (APC sequences) count no columns, and mouse clicks are shifted back by the border. Gentle Shell's own work row and agent-question row above the prompt do not show while Fisi Dock empties the `agents` part they read: a question still opens Gentle Shell's own selector, the `agents` card marks it with `!`, and `/gentle:agents` opens the list. The factory it replaced comes back when Fisi Dock unloads. A Gentle Shell update that changes the shape of its panel rule (`─ ✿ label ──── hint ─`), its blank bottom row or its editor's `topRowCount` can break the box: it then falls back to Gentle Shell's own rows and the line below, and `tests/gentle-shell-keys.test.ts` fails against the installed package when `gentle-pi.prompt-owner`, `framePromptLines`, `panelPromptRule` or `topRowCount` are gone.
|
|
15
|
+
- **Prompt box**: Gentle Shell's prompt is closed into a box with the labels on its top rule: `╭─ ✿ waiting for input ──── ● SUPER-YOLO opus-5.5 · high ─╮`, then `│ › … │` rows, then a `╰────╯` bottom rule. The left label is Gentle Shell's own (`waiting for input`, `working…`, or the ODD phase while a request runs); when it is an ODD phase (`exploring…`, `implementing…`, `checking…` and the rest as Gentle Shell writes them) the box puts `ODD ` before it: `╭─ ✿ ODD exploring… ──── ● SUPER-YOLO opus-5.5 · high ─╮`, never for `waiting for input`, `working…` or `queued`. `ODD ` takes 4 columns: the right part still gives way first (it compacts, then drops), and only where even the bare box would not fit with it does the label show without it; the right part is the permission mode after a red dot (only when it is not `NORMAL`, the rule of Gentle Shell's own status bar), then the model and effort: the dot and the mode bold in vivid red, the model muted, the effort in vivid blue (the line under the prompt paints them the same). Gentle Shell's own hint on that rule (`esc again to clear`, the vim mode) stays after the labels; its `[ Panel ]` button (the phone sheet with its rail sections, empty while Fisi Dock paints them) is left out, and a tap on the rule row does nothing. The full panel on the phone is `/dock`. The box paints whenever Fisi Dock paints: Gentle Shell's sidebar state on the terminal, in the wide rail chrome and in the narrow fullscreen chrome. How: Fisi Dock wraps Gentle Shell's editor factory (the wrapper carries Gentle Shell's own `gentle-pi.prompt-owner` marker, so Gentle Shell's shutdown and reinstall treat it as their own), renders Gentle Shell's editor four columns narrower and rebuilds only its frame rows: any rows above it and Pi's autocomplete rows under it stay untouched, the editor's invisible cursor markers (APC sequences) count no columns, and mouse clicks are shifted back by the border. Gentle Shell's own work row and agent-question row above the prompt do not show while Fisi Dock empties the `agents` part they read: a question still opens Gentle Shell's own selector, the `agents` card marks it with `!`, and `/gentle:agents` opens the list. The factory it replaced comes back when Fisi Dock unloads. A Gentle Shell update that changes the shape of its panel rule (`─ ✿ label ──── hint ─`), its blank bottom row or its editor's `topRowCount` can break the box: it then falls back to Gentle Shell's own rows and the line below, and `tests/gentle-shell-keys.test.ts` fails against the installed package when `gentle-pi.prompt-owner`, `framePromptLines`, `panelPromptRule` or `topRowCount` are gone.
|
|
16
16
|
- **Prompt box on a narrow rule**: when the width cannot hold the labels beside the idle label (a phone, idle), the label's words drop first and the petal alone keeps them: `╭─ ✿ ──── ● SUPER-YOLO opus-5.5 · high ─╮`, with Gentle Shell's hint after them when it fits too. The idle label is told by how Gentle Shell paints it, not by its words, so a translated, custom or clipped one (`waiting for i…`) gets the same rule: a label Gentle Shell animates (the working scan) is never idle, and one painted whole is idle unless it is `queued`, `working…` or an ODD phase (whole or clipped with `…`) or the editor's scroll indicator (`↑ 3 more`, text out of view). `working…`, `queued`, the ODD phase and the scroll indicator always keep their words. Still too tight, the labels compact step by step instead of dropping: tight separators (`● SUPER-YOLO opus-5.5·high`), then without the effort (`● SUPER-YOLO opus-5.5`), then the mode alone (`● SUPER-YOLO`; the model alone in normal mode), so working on a phone reads `╭─ ✿ working… ─ ● SUPER-YOLO opus-5.5 ─╮` at 40 columns. Each step is tried beside Gentle Shell's hint first (`● SUPER-YOLO INSERT`); with no room for both, the hint wins beside a working label, as it does over the full labels, and our labels win beside the idle one. Only when not even the smallest step fits do the labels drop, then the hint; under 20 columns Gentle Shell's rows stay as they are.
|
|
17
|
-
- **Working label**: the words come from Gentle Shell (`exploring…`, `implementing…`), with `ODD ` before a phase. In the box the color moves across the whole `ODD exploring…` by the clock, at a steady pace, however often the screen is redrawn, and the petal before it turns through Gentle Shell's four flowers (`✿ ❀ ❁ ✾`, in its tones) on the same clock, one flower per wave step (120 ms), each one column wide so nothing shifts. Gentle Shell's `potato` animation keeps its own label and petal, with `ODD ` in the label's gray; under `performance` the box moves the wave and the petal by the clock too, since Gentle Shell's own there step once a second.
|
|
17
|
+
- **Working label**: the words come from Gentle Shell (`exploring…`, `implementing…`), with `ODD ` before a phase. In the box the color (vivid pink at the head, vivid lilac beside it, gray elsewhere) moves across the whole `ODD exploring…` by the clock, at a steady pace, however often the screen is redrawn, and the petal before it turns through Gentle Shell's four flowers (`✿ ❀ ❁ ✾`, in its tones) on the same clock, one flower per wave step (120 ms), each one column wide so nothing shifts. Gentle Shell's `potato` animation keeps its own label and petal, with `ODD ` in the label's gray; under `performance` the box moves the wave and the petal by the clock too, since Gentle Shell's own there step once a second.
|
|
18
18
|
- **Under the prompt** (fallback): when the box could not be installed (no Gentle Shell editor yet, someone else's editor) or did not fit, one line with the same labels: `SUPER-YOLO · opus-5.5 · high` (`opus-5.5 · high` in normal mode). Too narrow for it, the labels compact the same way (`SUPER-YOLO·opus-5.5·high`, `SUPER-YOLO·opus-5.5`, `SUPER-YOLO`) before the line is cut with `…`. The mode, on the box and on the line, comes from Gentle Shell's permission-mode adapter, asked over `pi.events` on `gentle:yolo:host-ui` like its `/gentle:customize` does, and follows `/gentle:mode` and `/gentle:yolo`; without an answer the labels are model and effort only. The prompt editor itself stays Gentle Shell's.
|
|
19
|
-
- **
|
|
19
|
+
- **Vivid labels**: section titles, the counters on their top rule and the labels Fisi Dock paints on the prompt box take the vividness of the cover title `#F43888` on their own hue: HSL saturation 90% and lightness 59%, and only for a hue that then reads under 4.5:1 on the `#161616` background, lightness up in steps of 0.01 until it does, at most 0.80 (`vivid` in `lib/palette.ts`). Amber `#e0af68` becomes `#f5a838`, lilac `#c3a6ff` `#9464f7`, coral `#ff9b85` `#f55a38`, teal `#5fd4e0` `#38e3f5`, pink `#f2a7c9` `#f5388e`, deep pink `#c9779f` `#f53894`, green `#8fc98f` `#38f538`, blue `#7b9cff` `#4c76f6`, red `#ff6b6b` `#f53838`, purple `#b48cff` `#945ff7`, rose `#ff76c3` `#f538a2`. Gray has no hue to raise: muted counters (`1 to commit`, the CI sha, `off`) and the model stay muted. Gentle Shell's own label, petal and hint keep the colors Gentle Shell paints them.
|
|
20
|
+
- **Theme**: ships the `Fisi-Dock` Pi theme with the design's palette and a pink-tinted selection: its accents (`pink`, `deepPink`, `teal`, `coral`, `amber`, `lilac`, `green`, `blue`, `red`) are the vivid values above, its neutrals and backgrounds the panel's own. The first start with Fisi Dock on applies it once and remembers the theme you had, for `/fisi-dock off` to put back; later starts never call Pi's theme setter (see [Turning Fisi Dock off and on](#turning-fisi-dock-off-and-on)). It is also one more choice in `/settings`.
|
|
20
21
|
|
|
21
22
|
### Narrow screens
|
|
22
23
|
|
|
@@ -26,7 +27,7 @@ On narrow or non-fullscreen terminals (a phone over SSH, a split pane) the rail
|
|
|
26
27
|
|
|
27
28
|
- **Strip above the prompt**: one line with what the panel would show and the top row does not, each part in its section color and only when it has data: `✓ ODD 2:45` once the request finished (while a phase is active, the prompt box's working label names it, so the strip does not repeat it), `todo 3/7` (only when no todo card shows, see below) and changed files (`±1 file`). Running agents are not counted here: the agents card lists them. A second line appears only for heads-up warnings (`! 1 commit not pushed · ! ODD plan fisi-dock`). On a tight screen trailing parts drop whole; with nothing to show the strip takes no space. It disappears as soon as the rail shows the panel.
|
|
28
29
|
- **Agents card under the strip**: while an agent, a 4.6.0 subagent task or background job is running (or a subagent task waits for you), an `agents` card framed like the panel sections, with the number running on the right of its top border and one row per running or waiting agent: its turning spinner (an amber `!` for a task that waits for you), name, task (cut to fit) and the time it has been running, `m:ss` (`h:mm:ss` past an hour), ticking each second on the spinner's repaints (with `potato` or `FISI_DOCK_ORBS=off` there is no timer, so it moves only when something else repaints). A job's time counts from the start Gentle Shell reports for it; a job that reports no start counts from when Fisi Dock first saw it running, so one already running when Fisi Dock loaded shows less than its real time. A subagent task's time counts from its first `running` event (until then from its tool call), or from the start in the presence file when it was seeded after a `/reload` (see the `agents` row below). An agent with a model gets the same model label as in the side panel, after its time (`0:42 opus · high`). Up to 4 agents, then `+N more /dock all`. Queued, finished and failed agents are not listed (the side panel still shows them for a while), and with nothing running the card takes no space.
|
|
29
|
-
- **Todo card under the agents card**: while there is a todo list, Fisi Dock's compact `todo` section, framed to the full width: the coral title with `8/10` on the right, `✓ 8 done` with its bar, the active task highlighted with its spinner, the next task and `+N more /dock all`. It keeps a fixed height however long the list is (the side panel lists the tasks instead); `/dock` shows every task. Its header already says `d/t`, so the strip leaves `todo 3/7` out while the card shows. Gentle Shell's own Todos card stays hidden. The card goes away with the strip when the rail shows the panel.
|
|
30
|
+
- **Todo card under the agents card**: while there is a todo list, Fisi Dock's compact `todo` section, framed to the full width: the vivid coral title with `8/10` on the right, `✓ 8 done` with its bar, the active task highlighted with its spinner, the next task and `+N more /dock all`. It keeps a fixed height however long the list is (the side panel lists the tasks instead); `/dock` shows every task. Its header already says `d/t`, so the strip leaves `todo 3/7` out while the card shows. Gentle Shell's own Todos card stays hidden. The card goes away with the strip when the rail shows the panel.
|
|
30
31
|
- **Prompt box**: the same closed box as on wide screens; on a narrow rule its labels compact step by step (see Prompt box on a narrow rule above).
|
|
31
32
|
- **`/dock`**: the side panel over the whole screen, scrolled with the arrows or a swipe (see Commands).
|
|
32
33
|
|
|
@@ -43,7 +44,7 @@ Without Gentle Shell, Fisi Dock paints nothing on its own (no rail parts, no str
|
|
|
43
44
|
| session | design session-section/02 (Grouped): three groups, each named by a muted label with a short faint stub (`where ──`, `usage ──`, `connections ──`, no full-width rule; a section with one group only has no label), and one muted key column (`project`, `context`, `profile`, `memory`, `mcp`) so every value starts at the same place. **where**: `project fisi-dock main ↑2`, the project folder in bold, the git branch and the commits to push in green (no `↑N` while the heads-up says it). **usage**: the `context` row from Pi: a meter whose filled cells are a heavy `━` in the state color (teal, amber from 60%, red from 85%) and whose empty cells are a thin faint `─`, with a faint `┼` on the cells holding 60% and 85%, then the readout flush right on the same row, `5% · 53.5k/1M` (the percent bold in the state color, the tokens in context and the window size; `5% used` when Pi gives no token counts); the readout keeps at least 13 columns, and when that would leave the bar under 16 cells (the 34-column rail) the bar takes the whole row and the readout moves right-aligned to the row under it; no row when Pi does not know the percent. Then the `profile` row with the cost flush right (below); without a profile the cost keeps a row of its own (`cost $0.012`). **connections**: the `memory` row for Engram (see Memory below), then one row per MCP server, at most 4 rows on the rail (see MCP servers below), or a muted `mcp none configured` without MCP config. The `profile` row: the agent-model profile this session's launches use (`/gentle:profiles`), the label Gentle Shell's own Status section and top row show, which Fisi Dock takes over: `claude` for the global profile, `claude (session)` for one chosen with Use in this session, `team (local)` or `team (repo)` for a pinned one; read from Gentle Shell's in-process profile registries with each Pi read (session start, every turn) and, while no run is active and the panel is on screen, every 10 s; no profile row when the session has no profile or that state cannot be read. The cost: the session cost (`$0.012`), or, when that cost is 0 but the session used tokens (a subscription such as pi-claude-bridge records tokens at cost 0), the session tokens and an API-price estimate: `12.4M tok · ≈$38` (see Tokens and the ≈ estimate below); a long profile is cut before the cost is. The connection rows put the names and the state words in their own columns (names past 12 cells are cut), with one glyph per state so none rests on color alone: green `● connected`, amber `◌ connecting`, red `✗ failed` or `✗ unreachable`, muted `○ not installed`; the gap between name and word closes to one space when two would not fit, and the saved count is the first thing to drop |
|
|
44
45
|
| heads-up | red warnings, drawn only when there is one (at most 5 rows, then `+N more`): other open git worktrees, todo tasks still open after a run ended, unfinished ODD plans in the project's `odd/tasks/*.md`, commits not pushed (replaces the branch's `↑N`), local branches not merged into the default branch |
|
|
45
46
|
| ODD | always drawn: before the first request one muted row `○ ready · phases show while I work`; while a request runs, the timeline of ODD phases with times (latest 6 steps), where the active step shows an animated braille orb in its phase color and its phase name in gray with a white shine sweeping across it, like the prompt box; when the agent's turn ends while this session's subagents still run (queued, running or waiting), the active step stays active and keeps counting until the last of them finishes or the request settles; under each step one gray line, aligned with its name, says what it did: under the active step what its last tool is doing (`editing …/tables/table-row.ts`, `running npm test`), under each finished step a summary (`edited lib/tables/ · 2 files`, `ran npm test · +2 more`, `read 3 files · 1 search`; past 256 different files it says `256+ files`). Paths are relative to the project folder (without regard to case only on Windows drive and network paths); a line too long for the rail keeps its count whole and cuts the path in the middle at a folder (`edited …/tables/ · 12 files`). Commands, paths and labels come from the model, so escape sequences, control characters and bidi overrides are removed before they reach the terminal; once the request is over, one row `✓ last request 2:45 · 4 phases` (the request's wall time and how many phases it went through). `/dock` keeps listing every step after the request |
|
|
46
|
-
| RDD | the session's RDD reviews from Gentle Shell's review events, one row per review, newest last (latest 6, then `+N earlier`), named by the first reviewed file and ` +N` more. A review whose files are not known yet shows as `changes` until they arrive; if the answer is that no review runs (an inspect or status check), that row goes away. On the title's right the RDD switch for the project folder: `on` in green or `off` muted, read from Gentle AI itself with its read-only `gentle-ai review mode status --cwd <folder> --json` at session start and after each turn, one call at a time with a 5 s timeout; a failed, timed-out or unreadable read keeps the last known switch (unknown at first: no switch). The binary is `gentle-ai` on PATH, else the one bundled in the `gentle-pi` package (`.gentle-ai/<version>/gentle-ai.exe` on Windows, `gentle-ai` elsewhere): the package the `gentle-shell` launcher names in `GENTLE_SHELL_CHILD_PACKAGE_INJECTION`, else the global npm one (`%APPDATA%\npm\node_modules\gentle-pi` on Windows; the npm prefix, node's own prefix, `/usr/local`, `/usr` and `~/.npm-global` elsewhere). A running review is highlighted with a turning teal spinner, `reviewing…` (or `fixing…` while findings are fixed) and its time; one waiting for you shows an amber `!` and `needs your OK`; finished ones show `✓ approved`, `✗ outdated · code changed` (changed after approval), `✗ unavailable` or a muted `skipped`, with the local time it ended (`23:05`). Under each review one gray line, aligned with its name, read from the review tools the agent calls (never from the opaque bindings): while it runs, what it is doing (`starting · readability, reliability`, `asks to review · 2 lenses`, `preparing 2 reviewers`, `running 2 reviewers · 1 done`, `fixing · up to 38 lines`, `validating the fix`, `closing · 7 findings, none blocking`, `stopped · 2 findings need your decision`); once it ends with its findings known, what it found (`7 findings · none blocking · 2 lenses`, `3 findings · 1 fix · 2 lenses`, `no findings · 1 lens`); a review that ends without findings known (outdated, unavailable, skipped) has no line. With no review yet: a green `●` and `watching · reviews high-risk changes` when on, `○ off · changes are not reviewed` when off. Not drawn when neither the switch nor any review is known (no Gentle AI) |
|
|
47
|
+
| RDD | the session's RDD reviews from Gentle Shell's review events, one row per review, newest last (latest 6, then `+N earlier`), named by the first reviewed file and ` +N` more. A review whose files are not known yet shows as `changes` until they arrive; if the answer is that no review runs (an inspect or status check), that row goes away. On the title's right the RDD switch for the project folder: `on` in vivid green or `off` muted, read from Gentle AI itself with its read-only `gentle-ai review mode status --cwd <folder> --json` at session start and after each turn, one call at a time with a 5 s timeout; a failed, timed-out or unreadable read keeps the last known switch (unknown at first: no switch). The binary is `gentle-ai` on PATH, else the one bundled in the `gentle-pi` package (`.gentle-ai/<version>/gentle-ai.exe` on Windows, `gentle-ai` elsewhere): the package the `gentle-shell` launcher names in `GENTLE_SHELL_CHILD_PACKAGE_INJECTION`, else the global npm one (`%APPDATA%\npm\node_modules\gentle-pi` on Windows; the npm prefix, node's own prefix, `/usr/local`, `/usr` and `~/.npm-global` elsewhere). A running review is highlighted with a turning teal spinner, `reviewing…` (or `fixing…` while findings are fixed) and its time; one waiting for you shows an amber `!` and `needs your OK`; finished ones show `✓ approved`, `✗ outdated · code changed` (changed after approval), `✗ unavailable` or a muted `skipped`, with the local time it ended (`23:05`). Under each review one gray line, aligned with its name, read from the review tools the agent calls (never from the opaque bindings): while it runs, what it is doing (`starting · readability, reliability`, `asks to review · 2 lenses`, `preparing 2 reviewers`, `running 2 reviewers · 1 done`, `fixing · up to 38 lines`, `validating the fix`, `closing · 7 findings, none blocking`, `stopped · 2 findings need your decision`); once it ends with its findings known, what it found (`7 findings · none blocking · 2 lenses`, `3 findings · 1 fix · 2 lenses`, `no findings · 1 lens`); a review that ends without findings known (outdated, unavailable, skipped) has no line. With no review yet: a green `●` and `watching · reviews high-risk changes` when on, `○ off · changes are not reviewed` when off. Not drawn when neither the switch nor any review is known (no Gentle AI) |
|
|
47
48
|
| CI | the GitHub Actions checks of the current branch's last pushed commit, read with `gh` in the session folder (`gh run list --branch <branch> --limit 20 --json …`, the branch from the git read): the commit is the newest run's, one row per workflow of that commit (its newest run). On the title's right the commit's short sha, muted. While a check runs, waits or failed: one row per workflow, sorted by name (at most 6, then `+N more`): a running one highlighted with a turning blue spinner, its name bold, `running…` and the time since it started, ticking with the spinner (when no gh read has succeeded for over 90 s, its time stays at the last successful read and its spinner holds still until the next one); a queued one with a faint `○`, its name muted and `queued` on the right; `✓ lint passed` and `✗ tests failed` (red) with their duration, a failed one with a gray line naming the failed job and its first failed step (`node-22 · Run npm test`, from `gh run view <id> --json jobs`, read again only when the run is updated, for example a re-run; a view that names no failed job leaves no line and is not read again, a failed view is retried on the next read); a cancelled or stale one muted with `-`. Once every check passed, one row `✓ 3 checks passed` (`1 check passed` for one) with the local time of the last update (`14:02`) and a gray line with the workflow names and the longest run (`build, lint, tests · longest 2:41`; only the names when gh gave no durations). gh is read at session start, every 30 s while a check runs or waits, every 3 min otherwise, 5 s and 20 s after a `bash` command with `git push` ends, and when the branch changes to another known branch (the shown checks go at once and the new branch is read; a git read that fails to name the branch changes nothing); never while drawing. Every name from gh is cut to 80 characters and loses escape sequences, control characters and bidi overrides. A failed or timed-out gh read keeps what is shown; no `gh`, no GitHub remote, no workflows or no branch: no section |
|
|
48
49
|
| agents | Gentle Shell's background jobs, from its `gentle-ai:jobs-sidebar` event (finished ones ride the snapshot a short while, as `✓` or `✗`), then Gentle Shell 4.6.0's own subagent tasks (its `subagent_run` / `subagent_continue` tools) as rows of the same shape, in the order they started; at most 4 agents, then `+N more`. A task's state comes from the `gentle-pi:notification:source` events Gentle Shell publishes for this session (`queued` `○`, `running` spinner, `waiting` `!` on a highlighted row when the subagent asks you something, `completed` `✓`, `failed`/`cancelled`/`timed_out` `✗`), ordered by their sequence; its name (the agent) and task text from the tool call's arguments, and its label, model and effort from the tool result (`details.gentleAgents`), which names the task id. A `subagent_run` / `subagent_continue` call in flight is a running row from the moment it starts, before any event, so a running subagent shows even if its events never arrive: the first event of a new task takes the oldest call still waiting (a continue, its own task), the result sets the state while no event has, a refused launch leaves no row, a refused continue puts the row back as it was, and a call whose end was lost goes when the run settles. A task only events name shows as `subagent` with `task <id>` until details arrive. With calls in parallel, two tasks can show each other's text until their results arrive. Its time counts from its first `running` event, until then from the call. Finished tasks stay 60 s, the tail of Gentle Shell's finished jobs, and fold on the next event, tool end or idle refresh. Limits: Gentle Shell's task store is private, so there are no live tokens, cost, turns or tool counts per task; a task started before Fisi Dock loaded (after a `/reload`) shows from one read of Gentle Shell's presence activity file (`<agent home>/gentle-agents/presence/<sha256(session id)>.<incarnation>.activity.json`, agent home `GENTLE_PI_AGENT_HOME`, else `PI_CODING_AGENT_DIR`, else `~/.pi/agent`) at session start, with its own start time; a missing or unreadable file changes nothing. Tool arguments and results are model text, so they are made printable and bounded (escapes, controls and bidi overrides removed, 120 characters). An agent with a model gets a label at the right end of its row: the model family (`opus`, `sonnet`, `haiku`; a model with none of these shows its short name, `gpt-5.1`, any `provider/` prefix and `claude-` dropped) in its effort's color (`low`/`minimal` amber, `medium` blue, `high`/`xhigh`/`max` purple, any other or no effort gray), then ` · <effort>` in gray (`opus · high`); no effort: the family alone; no model: no label. Each agent stays one row: when it does not fit, the task is cut first, then ` · <effort>` drops, then the label. The label never plays the entrance |
|
|
49
50
|
| todo | always drawn: with no todo list or 0 tasks one muted row `○ no tasks · the list shows while I plan` and nothing on the title's right (the todo card above the prompt and the strip's `todo d/t` still show only with tasks). Otherwise Gentle Shell's todo list; on the title's right a progress bar (`▰▱`, up to 10 cells) and `done/total`. Every task in order while there are at most 8 (`✓` done muted, the active one highlighted with its spinner, `○` pending, `◌` blocked, `-` dropped). Past 8, a window of 7 tasks starting at the task before the active one (else the first open task, else the last task), after a `✓ N earlier done` row (`N earlier` when some of them are not done) and before `+N more` with a `/dock all` hint. Without the task list (only counts known yet), at most 4 rows: done count with its bar, active task, next task, `+N more`. `/dock` lists every task (see Commands) |
|
package/lib/label-wave.ts
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
// wave one step per render, so it races while text streams and nearly stops during
|
|
3
3
|
// a quiet tool run. The box repaints the same words by the clock instead, with the
|
|
4
4
|
// same shape: the head and its two neighbors in the theme's accent, its
|
|
5
|
-
// softer tone one further out, muted elsewhere
|
|
6
|
-
// in quality and performance (S93: there
|
|
5
|
+
// softer tone one further out, muted elsewhere; both tones vivid (vivid labels S3).
|
|
6
|
+
// Only while Gentle Shell itself scans, in quality and performance (S93: there
|
|
7
|
+
// Gentle Shell steps its wave once a second).
|
|
7
8
|
// The petal before the label turns on the same clock (S97).
|
|
8
9
|
import type { OrbMotion } from "./orbs.ts";
|
|
9
|
-
import { bold, fg, palette } from "./palette.ts";
|
|
10
|
+
import { bold, fg, palette, vivid } from "./palette.ts";
|
|
10
11
|
|
|
11
12
|
export const WAVE_STEP_MS = 120;
|
|
13
|
+
const WAVE_HEAD = vivid(palette.pink);
|
|
14
|
+
const WAVE_SOFT = vivid(palette.lilac);
|
|
12
15
|
/** Gentle Shell's wave enters three columns before the label; a short label rests between sweeps. */
|
|
13
16
|
const ENTRY_STEPS = 3;
|
|
14
17
|
const MIN_STEPS = 15;
|
|
@@ -23,15 +26,15 @@ export function waveLabel(label: string, now: number): string {
|
|
|
23
26
|
return chars
|
|
24
27
|
.map((char, index) => {
|
|
25
28
|
const distance = Math.abs(head - index);
|
|
26
|
-
return fg(distance <= 1 ?
|
|
29
|
+
return fg(distance <= 1 ? WAVE_HEAD : distance === 2 ? WAVE_SOFT : palette.muted, char);
|
|
27
30
|
})
|
|
28
31
|
.join("");
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/** Gentle Shell's working petal (lib/shell-prompt.ts PETAL_FRAMES): four flowers, one column each. */
|
|
32
35
|
const PETAL_FRAMES = ["✿", "❀", "❁", "✾"] as const;
|
|
33
|
-
/** Its tones (PETAL_TONE_FRAMES: mdQuoteBorder, thinkingHigh, accent, borderAccent, accent, thinkingHigh, mdQuoteBorder twice) in the Fisi-Dock theme's colors. */
|
|
34
|
-
const PETAL_TONES = [palette.deepPink, palette.lilac, palette.pink, palette.pink, palette.pink, palette.lilac, palette.deepPink, palette.deepPink]
|
|
36
|
+
/** Its tones (PETAL_TONE_FRAMES: mdQuoteBorder, thinkingHigh, accent, borderAccent, accent, thinkingHigh, mdQuoteBorder twice) in the Fisi-Dock theme's vivid colors. */
|
|
37
|
+
const PETAL_TONES = [palette.deepPink, palette.lilac, palette.pink, palette.pink, palette.pink, palette.lilac, palette.deepPink, palette.deepPink].map(vivid);
|
|
35
38
|
|
|
36
39
|
/** Gentle Shell's working petal at the moment `now`. Gentle Shell turns the petal and the wave on one tick, so one frame per wave step. */
|
|
37
40
|
export function wavePetal(now: number): string {
|
package/lib/palette.ts
CHANGED
|
@@ -1,46 +1,84 @@
|
|
|
1
|
-
// Design 07 palette in 24-bit color. The panel paints with its own accents
|
|
2
|
-
// instead of Pi theme roles so each section keeps its hue (S9).
|
|
3
|
-
|
|
4
|
-
export type Rgb = readonly [number, number, number];
|
|
5
|
-
|
|
6
|
-
export const BACKGROUND: Rgb = [0x16, 0x16, 0x16];
|
|
7
|
-
|
|
8
|
-
export const palette = {
|
|
9
|
-
pink: [0xf2, 0xa7, 0xc9],
|
|
10
|
-
/** The theme's deepPink (mdQuoteBorder): the deepest tone of Gentle Shell's petal. */
|
|
11
|
-
deepPink: [0xc9, 0x77, 0x9f],
|
|
12
|
-
teal: [0x5f, 0xd4, 0xe0],
|
|
13
|
-
coral: [0xff, 0x9b, 0x85],
|
|
14
|
-
amber: [0xe0, 0xaf, 0x68],
|
|
15
|
-
lilac: [0xc3, 0xa6, 0xff],
|
|
16
|
-
green: [0x8f, 0xc9, 0x8f],
|
|
17
|
-
blue: [0x7b, 0x9c, 0xff],
|
|
18
|
-
red: [0xff, 0x6b, 0x6b],
|
|
19
|
-
/** High agent effort (S56). */
|
|
20
|
-
purple: [0xb4, 0x8c, 0xff],
|
|
21
|
-
/** Gentle Shell's Gentleman-Sexy theme accent and its deepPink: the wordmark's SHELL. */
|
|
22
|
-
sexyPink: [0xf4, 0x38, 0x88],
|
|
23
|
-
sexyDeepPink: [0xbf, 0x0f, 0x50],
|
|
24
|
-
/** Gentle Shell's banner rose. */
|
|
25
|
-
rose: [0xff, 0x76, 0xc3],
|
|
26
|
-
text: [0xe1, 0xe1, 0xe1],
|
|
27
|
-
muted: [0x6c, 0x6c, 0x6c],
|
|
28
|
-
faint: [0x4a, 0x4a, 0x4a],
|
|
29
|
-
/** Section frames: one neutral gray, Grok-style, same as Gentle Shell's welcome card frame; titles carry the hue. */
|
|
30
|
-
frame: [0x45, 0x45, 0x45],
|
|
31
|
-
highlight: [0x26, 0x26, 0x26],
|
|
32
|
-
} as const satisfies Record<string, Rgb>;
|
|
33
|
-
|
|
34
|
-
export const RESET = "\x1b[0m";
|
|
35
|
-
|
|
36
|
-
/** Blend `color` over the panel background, keeping `amount` of the color. */
|
|
37
|
-
export function dim(color: Rgb, amount = 0.4): Rgb {
|
|
38
|
-
return color.map((channel, index) => Math.round(channel * amount + BACKGROUND[index] * (1 - amount))) as unknown as Rgb;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
// Design 07 palette in 24-bit color. The panel paints with its own accents
|
|
2
|
+
// instead of Pi theme roles so each section keeps its hue (S9).
|
|
3
|
+
|
|
4
|
+
export type Rgb = readonly [number, number, number];
|
|
5
|
+
|
|
6
|
+
export const BACKGROUND: Rgb = [0x16, 0x16, 0x16];
|
|
7
|
+
|
|
8
|
+
export const palette = {
|
|
9
|
+
pink: [0xf2, 0xa7, 0xc9],
|
|
10
|
+
/** The theme's deepPink (mdQuoteBorder): the deepest tone of Gentle Shell's petal. */
|
|
11
|
+
deepPink: [0xc9, 0x77, 0x9f],
|
|
12
|
+
teal: [0x5f, 0xd4, 0xe0],
|
|
13
|
+
coral: [0xff, 0x9b, 0x85],
|
|
14
|
+
amber: [0xe0, 0xaf, 0x68],
|
|
15
|
+
lilac: [0xc3, 0xa6, 0xff],
|
|
16
|
+
green: [0x8f, 0xc9, 0x8f],
|
|
17
|
+
blue: [0x7b, 0x9c, 0xff],
|
|
18
|
+
red: [0xff, 0x6b, 0x6b],
|
|
19
|
+
/** High agent effort (S56). */
|
|
20
|
+
purple: [0xb4, 0x8c, 0xff],
|
|
21
|
+
/** Gentle Shell's Gentleman-Sexy theme accent and its deepPink: the wordmark's SHELL. */
|
|
22
|
+
sexyPink: [0xf4, 0x38, 0x88],
|
|
23
|
+
sexyDeepPink: [0xbf, 0x0f, 0x50],
|
|
24
|
+
/** Gentle Shell's banner rose. */
|
|
25
|
+
rose: [0xff, 0x76, 0xc3],
|
|
26
|
+
text: [0xe1, 0xe1, 0xe1],
|
|
27
|
+
muted: [0x6c, 0x6c, 0x6c],
|
|
28
|
+
faint: [0x4a, 0x4a, 0x4a],
|
|
29
|
+
/** Section frames: one neutral gray, Grok-style, same as Gentle Shell's welcome card frame; titles carry the hue. */
|
|
30
|
+
frame: [0x45, 0x45, 0x45],
|
|
31
|
+
highlight: [0x26, 0x26, 0x26],
|
|
32
|
+
} as const satisfies Record<string, Rgb>;
|
|
33
|
+
|
|
34
|
+
export const RESET = "\x1b[0m";
|
|
35
|
+
|
|
36
|
+
/** Blend `color` over the panel background, keeping `amount` of the color. */
|
|
37
|
+
export function dim(color: Rgb, amount = 0.4): Rgb {
|
|
38
|
+
return color.map((channel, index) => Math.round(channel * amount + BACKGROUND[index] * (1 - amount))) as unknown as Rgb;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The WCAG contrast of `color` on the panel background. */
|
|
42
|
+
function contrast(color: Rgb): number {
|
|
43
|
+
const luminance = (rgb: Rgb) => {
|
|
44
|
+
const [r, g, b] = rgb.map((channel) => channel / 255).map((value) => (value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4));
|
|
45
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
46
|
+
};
|
|
47
|
+
return (luminance(color) + 0.05) / (luminance(BACKGROUND) + 0.05);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** `color`'s hue at HSL saturation 0.9 and `lightness`. */
|
|
51
|
+
function withLightness(color: Rgb, lightness: number): Rgb {
|
|
52
|
+
const [r, g, b] = color.map((channel) => channel / 255);
|
|
53
|
+
const max = Math.max(r, g, b);
|
|
54
|
+
const spread = max - Math.min(r, g, b);
|
|
55
|
+
let hue = 0;
|
|
56
|
+
if (spread) hue = max === r ? ((g - b) / spread) % 6 : max === g ? (b - r) / spread + 2 : (r - g) / spread + 4;
|
|
57
|
+
hue = (hue * 60 + 360) % 360;
|
|
58
|
+
const chroma = (1 - Math.abs(2 * lightness - 1)) * 0.9;
|
|
59
|
+
const x = chroma * (1 - Math.abs(((hue / 60) % 2) - 1));
|
|
60
|
+
const m = lightness - chroma / 2;
|
|
61
|
+
const sextant = Math.floor(hue / 60);
|
|
62
|
+
const [r1, g1, b1] = [[chroma, x, 0], [x, chroma, 0], [0, chroma, x], [0, x, chroma], [x, 0, chroma], [chroma, 0, x]][sextant];
|
|
63
|
+
return [r1, g1, b1].map((value) => Math.round((value + m) * 255)) as unknown as Rgb;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The vividness of the cover title #f43888 on `color`'s own hue (vivid labels S1):
|
|
68
|
+
* HSL saturation 90%, lightness 59%; a hue that then reads under 4.5:1 on the
|
|
69
|
+
* background gets lighter in steps of 0.01 until it does, at most 0.80.
|
|
70
|
+
*/
|
|
71
|
+
export function vivid(color: Rgb): Rgb {
|
|
72
|
+
for (let step = 59; step < 80; step++) {
|
|
73
|
+
const out = withLightness(color, step / 100);
|
|
74
|
+
if (contrast(out) >= 4.5) return out;
|
|
75
|
+
}
|
|
76
|
+
return withLightness(color, 0.8);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const fgCode = ([r, g, b]: Rgb) => `\x1b[38;2;${r};${g};${b}m`;
|
|
80
|
+
export const bgCode = ([r, g, b]: Rgb) => `\x1b[48;2;${r};${g};${b}m`;
|
|
81
|
+
|
|
82
|
+
// Style closers reset only their own attribute, so a row background survives.
|
|
83
|
+
export const fg = (color: Rgb, text: string) => (text ? `${fgCode(color)}${text}\x1b[39m` : "");
|
|
84
|
+
export const bold = (text: string) => (text ? `\x1b[1m${text}\x1b[22m` : "");
|
package/lib/panel.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { fitDetail } from "./odd-detail.ts";
|
|
|
5
5
|
import { orbAt, orbOf, orbWidth, shimmerText, spinnerAt } from "./orbs.ts";
|
|
6
6
|
import type { AgentInfo, ChangeInfo, CiCheck, CiInfo, FooterInfo, HeadsUpWarning, McpServerInfo, OddInfo, PanelModel, RddInfo, RddReview, SessionInfo, TodoInfo, TodoTask } from "./panel-model.ts";
|
|
7
7
|
import { shortAgentModel } from "./prompt-line.ts";
|
|
8
|
-
import { RESET, bgCode, bold, dim, fg, palette, type Rgb } from "./palette.ts";
|
|
8
|
+
import { RESET, bgCode, bold, dim, fg, palette, vivid, type Rgb } from "./palette.ts";
|
|
9
9
|
import { ellipsize, padEnd, truncateToWidth, visibleWidth } from "./text.ts";
|
|
10
10
|
|
|
11
11
|
// Pure model-to-lines renderer for the rail (design 07, S2/S8/S9). Gentle Shell
|
|
@@ -362,7 +362,7 @@ function emptyTodoSection(width: number): Section {
|
|
|
362
362
|
function todoSection(todo: TodoInfo, width: number, expanded = false, spinner?: number, compact = false): Section | undefined {
|
|
363
363
|
if (todo.total <= 0) return undefined;
|
|
364
364
|
const color = palette.coral;
|
|
365
|
-
const right = fg(color, `${todo.done}/${todo.total}`);
|
|
365
|
+
const right = fg(vivid(color), `${todo.done}/${todo.total}`);
|
|
366
366
|
if (expanded && todo.tasks?.length) return { title: "todo", color, right, rows: taskRows(todo.tasks, width, spinner, todo.entrance) };
|
|
367
367
|
if (!compact && todo.tasks?.length) {
|
|
368
368
|
// S58: the listed rail keeps the progress bar, on the title before done/total.
|
|
@@ -476,7 +476,7 @@ function rddRow(review: RddReview, width: number, spinner?: number): Row {
|
|
|
476
476
|
function rddSection(rdd: RddInfo | undefined, mode: FooterInfo["rdd"], width: number, spacers = true, spinner?: number, doneDetails = true): Section | undefined {
|
|
477
477
|
const reviews = rdd?.reviews ?? [];
|
|
478
478
|
if (reviews.length === 0 && !mode) return undefined;
|
|
479
|
-
const right = mode === "on" ? bold(fg(palette.green, "on")) : mode === "off" ? bold(fg(palette.muted, "off")) : undefined;
|
|
479
|
+
const right = mode === "on" ? bold(fg(vivid(palette.green), "on")) : mode === "off" ? bold(fg(palette.muted, "off")) : undefined;
|
|
480
480
|
const section = (rows: Row[]): Section => ({ title: "RDD", color: palette.rose, ...(right ? { right } : {}), rows });
|
|
481
481
|
if (reviews.length === 0) {
|
|
482
482
|
// One-column dot (S57): the 🌹 emoji has no fixed width across terminals and broke the frame.
|
|
@@ -669,7 +669,8 @@ function changesSection(changes: ChangeInfo[], width: number, cap = LIST_CAP): S
|
|
|
669
669
|
|
|
670
670
|
function topBorder(section: Section, width: number): string {
|
|
671
671
|
const frame = palette.frame;
|
|
672
|
-
|
|
672
|
+
// Titles wear the cover title's vividness on their own hue (vivid labels S2).
|
|
673
|
+
const title = (text: string) => bold(fg(vivid(section.color), text));
|
|
673
674
|
const titleWidth = visibleWidth(section.title);
|
|
674
675
|
// "╭─ " title " " fill [" " right " ─"] "╮", with at least one fill dash.
|
|
675
676
|
const bare = 3 + titleWidth + 1 + 1 + 1;
|
|
@@ -815,7 +816,7 @@ export function renderAgentsCard(agents: AgentInfo[] | undefined, width: number,
|
|
|
815
816
|
});
|
|
816
817
|
const hidden = running.length - shown.length;
|
|
817
818
|
if (hidden > 0) rows.push({ text: item(" ", (rest) => spread(muted(`+${hidden} more`), `${plainText("/dock")}${muted(" all")}`, rest), inner) });
|
|
818
|
-
const section: Section = { title: "agents", color: palette.lilac, right: fg(palette.lilac, String(running.length)), rows };
|
|
819
|
+
const section: Section = { title: "agents", color: palette.lilac, right: fg(vivid(palette.lilac), String(running.length)), rows };
|
|
819
820
|
return drawSection(section, columns).map((line) => truncateToWidth(line, columns));
|
|
820
821
|
}
|
|
821
822
|
|
package/lib/prompt-box.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// before it (S96); with the clock the wave sweeps both and the petal turns by
|
|
14
14
|
// the same clock (S97).
|
|
15
15
|
import { waveLabel, wavePetal } from "./label-wave.ts";
|
|
16
|
-
import { fg, palette } from "./palette.ts";
|
|
16
|
+
import { fg, palette, vivid } from "./palette.ts";
|
|
17
17
|
import { promptLineSteps, type PromptLineModel, type PromptLinePart } from "./prompt-line.ts";
|
|
18
18
|
import { ellipsize, padEnd, sliceColumns, truncateToWidth, visibleWidth } from "./text.ts";
|
|
19
19
|
|
|
@@ -67,13 +67,13 @@ const join = (segments: Segment[], plain: string, styled = plain): Segment => ({
|
|
|
67
67
|
styled: segments.map((segment) => segment.styled).join(styled),
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
/** `● SUPER-YOLO opus-5.5 · high`, or tight `● SUPER-YOLO opus-5.5·high`: the dot in red, the rest as the prompt line paints it. */
|
|
70
|
+
/** `● SUPER-YOLO opus-5.5 · high`, or tight `● SUPER-YOLO opus-5.5·high`: the dot in vivid red, the rest as the prompt line paints it. */
|
|
71
71
|
function ownSegment(parts: PromptLinePart[], tight: boolean): Segment {
|
|
72
72
|
const mode = parts.find((part) => part.kind === "mode");
|
|
73
73
|
const rest = parts.filter((part) => part.kind !== "mode");
|
|
74
74
|
const dot = tight ? "·" : " · ";
|
|
75
75
|
const segments: Segment[] = [
|
|
76
|
-
...(mode ? [{ plain: `● ${mode.plain}`, styled: `${fg(palette.red, "●")} ${mode.styled}` }] : []),
|
|
76
|
+
...(mode ? [{ plain: `● ${mode.plain}`, styled: `${fg(vivid(palette.red), "●")} ${mode.styled}` }] : []),
|
|
77
77
|
...(rest.length ? [join(rest, dot, fg(palette.faint, dot))] : []),
|
|
78
78
|
];
|
|
79
79
|
return join(segments, tight ? " " : " ");
|
package/lib/prompt-line.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bold, fg, palette } from "./palette.ts";
|
|
1
|
+
import { bold, fg, palette, vivid } from "./palette.ts";
|
|
2
2
|
import { ellipsize, visibleWidth } from "./text.ts";
|
|
3
3
|
|
|
4
4
|
// Design 07 shows the model and effort beside the prompt. The prompt editor is
|
|
@@ -38,8 +38,9 @@ export interface PromptLinePart {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* The labels in order, each plain and styled: the mode bold in red, like its dot (only
|
|
42
|
-
* it is not normal), the model muted, the effort in blue
|
|
41
|
+
* The labels in order, each plain and styled: the mode bold in vivid red, like its dot (only
|
|
42
|
+
* when it is not normal), the model muted, the effort in vivid blue (vivid labels S3).
|
|
43
|
+
* Nothing without a model.
|
|
43
44
|
* The line under the prompt and the prompt box (S88) paint the same parts.
|
|
44
45
|
*/
|
|
45
46
|
export function promptLineParts(model: PromptLineModel): PromptLinePart[] {
|
|
@@ -47,9 +48,9 @@ export function promptLineParts(model: PromptLineModel): PromptLinePart[] {
|
|
|
47
48
|
const mode = permissionModeLabel(model.mode);
|
|
48
49
|
const name = shortModelName(model.modelId);
|
|
49
50
|
return [
|
|
50
|
-
...(mode ? [{ kind: "mode" as const, plain: mode, styled: bold(fg(palette.red, mode)) }] : []),
|
|
51
|
+
...(mode ? [{ kind: "mode" as const, plain: mode, styled: bold(fg(vivid(palette.red), mode)) }] : []),
|
|
51
52
|
{ kind: "model" as const, plain: name, styled: fg(palette.muted, name) },
|
|
52
|
-
...(model.effort ? [{ kind: "effort" as const, plain: model.effort, styled: fg(palette.blue, model.effort) }] : []),
|
|
53
|
+
...(model.effort ? [{ kind: "effort" as const, plain: model.effort, styled: fg(vivid(palette.blue), model.effort) }] : []),
|
|
53
54
|
];
|
|
54
55
|
}
|
|
55
56
|
|
package/package.json
CHANGED
package/themes/Fisi-Dock.json
CHANGED
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
"text": "#e1e1e1",
|
|
12
12
|
"muted": "#6c6c6c",
|
|
13
13
|
"dim": "#6b6b6b",
|
|
14
|
-
"pink": "#
|
|
15
|
-
"deepPink": "#
|
|
14
|
+
"pink": "#f5388e",
|
|
15
|
+
"deepPink": "#f53894",
|
|
16
16
|
"pinkSelection": "#2a2228",
|
|
17
|
-
"teal": "#
|
|
18
|
-
"coral": "#
|
|
19
|
-
"amber": "#
|
|
20
|
-
"lilac": "#
|
|
21
|
-
"green": "#
|
|
22
|
-
"blue": "#
|
|
23
|
-
"red": "#
|
|
17
|
+
"teal": "#38e3f5",
|
|
18
|
+
"coral": "#f55a38",
|
|
19
|
+
"amber": "#f5a838",
|
|
20
|
+
"lilac": "#9464f7",
|
|
21
|
+
"green": "#38f538",
|
|
22
|
+
"blue": "#4c76f6",
|
|
23
|
+
"red": "#f53838",
|
|
24
24
|
"diffAddedBg": "#24321f",
|
|
25
25
|
"toolErrorBg": "#2e1a1d"
|
|
26
26
|
},
|