@luan.sh/pi-libtui 0.3.8
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 +21 -0
- package/README.md +208 -0
- package/native-revision +1 -0
- package/package.json +58 -0
- package/src/activity-presentation.ts +75 -0
- package/src/appearance.ts +632 -0
- package/src/background-surface.ts +125 -0
- package/src/color/palette.ts +186 -0
- package/src/color/preview.ts +157 -0
- package/src/color/resolver.ts +75 -0
- package/src/color/theme.ts +531 -0
- package/src/component-stack.ts +247 -0
- package/src/content/markdown-code.ts +60 -0
- package/src/content/terminal-text.ts +135 -0
- package/src/content/text.ts +105 -0
- package/src/controls/action-panel.ts +420 -0
- package/src/controls/dialog-button-bar.ts +287 -0
- package/src/controls/multi-select.ts +467 -0
- package/src/controls/picker-panel.ts +447 -0
- package/src/controls/screen-icon-actions.ts +134 -0
- package/src/controls/searchable-select.ts +394 -0
- package/src/controls/select-box.ts +224 -0
- package/src/controls/selectable-list.ts +433 -0
- package/src/controls/selection-action-bar.ts +382 -0
- package/src/controls/semantic-input.ts +23 -0
- package/src/controls/tab.ts +189 -0
- package/src/cursor.ts +288 -0
- package/src/decoration/editor-pills.ts +156 -0
- package/src/decoration/glyphs.ts +326 -0
- package/src/decoration/pointer-interaction.ts +123 -0
- package/src/decoration/powerline-pill.ts +104 -0
- package/src/decoration/status.ts +185 -0
- package/src/decoration/transient-pill.ts +176 -0
- package/src/diff/index.ts +21 -0
- package/src/diff/model.ts +76 -0
- package/src/diff/parse.ts +310 -0
- package/src/diff/pierre-syntax.ts +86 -0
- package/src/diff/render.ts +504 -0
- package/src/diff/structured.ts +264 -0
- package/src/diff/view.ts +75 -0
- package/src/editor/chrome.ts +341 -0
- package/src/editor/composition.ts +53 -0
- package/src/editor/layout.ts +81 -0
- package/src/editor/presentation.ts +35 -0
- package/src/editor/protocol.ts +188 -0
- package/src/editor.ts +41 -0
- package/src/extension.ts +30 -0
- package/src/folding.ts +192 -0
- package/src/host/cursor-bridge.ts +210 -0
- package/src/host/editor-bridge.ts +148 -0
- package/src/host/extension-host.ts +231 -0
- package/src/host/mouse-bridge.ts +212 -0
- package/src/host/mouse-dispatch.ts +400 -0
- package/src/host/mouse-input.ts +85 -0
- package/src/host/mouse-targets.ts +238 -0
- package/src/host/pi-layout-adapter.ts +298 -0
- package/src/host/pi-native-selection.ts +227 -0
- package/src/host/split-pane-bridge.ts +720 -0
- package/src/host/transcript-bridge.ts +236 -0
- package/src/index.ts +334 -0
- package/src/line-layout.ts +6 -0
- package/src/motion.ts +1409 -0
- package/src/mouse/events.ts +55 -0
- package/src/mouse/fullscreen-layout.ts +260 -0
- package/src/mouse/registry.ts +348 -0
- package/src/mouse.ts +37 -0
- package/src/native-binary.ts +189 -0
- package/src/overlay/anchored.ts +91 -0
- package/src/overlay/detail-card.ts +158 -0
- package/src/overlay/dialog.ts +266 -0
- package/src/overlay/floating.ts +106 -0
- package/src/overlay/framed-editor.ts +236 -0
- package/src/overlay/fullscreen.ts +170 -0
- package/src/overlay/hover-tooltip.ts +109 -0
- package/src/overlay/modal-mount.ts +118 -0
- package/src/panels.ts +207 -0
- package/src/render-cache.ts +80 -0
- package/src/render-epoch.ts +48 -0
- package/src/request-animation.ts +160 -0
- package/src/scrollbar.ts +27 -0
- package/src/selection.ts +148 -0
- package/src/split-pane.ts +232 -0
- package/src/status-presentation.ts +411 -0
- package/src/stream.ts +543 -0
- package/src/syntax.ts +316 -0
- package/src/terminal/bridge-client.ts +243 -0
- package/src/terminal/embedding.ts +6 -0
- package/src/terminal/output.ts +151 -0
- package/src/terminal/projection.ts +362 -0
- package/src/terminal/pty-host.ts +263 -0
- package/src/terminal/pty-pane.ts +308 -0
- package/src/terminal-colors.ts +334 -0
- package/src/tool/action.ts +170 -0
- package/src/tool/activity.ts +500 -0
- package/src/tool/call-preview.ts +44 -0
- package/src/tool/disclosure-action.ts +89 -0
- package/src/tool/expanded-region-viewport.ts +103 -0
- package/src/tool/index.ts +26 -0
- package/src/tool/output.ts +271 -0
- package/src/tool/transcript.ts +150 -0
- package/src/tool/view-region.ts +581 -0
- package/test/appearance.test.ts +183 -0
- package/test/bounded-stream.test.ts +150 -0
- package/test/bridge-client.test.ts +42 -0
- package/test/color-preview.test.ts +23 -0
- package/test/component-stack.test.ts +206 -0
- package/test/components.test.ts +1165 -0
- package/test/content-text.test.ts +21 -0
- package/test/cursor-bridge.test.ts +151 -0
- package/test/cursor.test.ts +147 -0
- package/test/editor-chrome.test.ts +119 -0
- package/test/editor-layout.test.ts +30 -0
- package/test/editor-registry.test.ts +203 -0
- package/test/editor-theme.test.ts +33 -0
- package/test/editor-token-pill.test.ts +72 -0
- package/test/floating-overlay.test.ts +57 -0
- package/test/folding.test.ts +147 -0
- package/test/harmonious-theme.test.ts +23 -0
- package/test/interaction.test.ts +104 -0
- package/test/key-icons.test.ts +62 -0
- package/test/markdown-code.test.ts +24 -0
- package/test/modal-overlay-mount.test.ts +94 -0
- package/test/motion.test.ts +649 -0
- package/test/mouse/bridge.test.ts +1302 -0
- package/test/mouse/extension.test.ts +205 -0
- package/test/mouse/registry.test.ts +140 -0
- package/test/mouse/screen-decoration.test.ts +167 -0
- package/test/native-binary.test.ts +52 -0
- package/test/palette.test.ts +32 -0
- package/test/panels.test.ts +74 -0
- package/test/pointer-components.test.ts +151 -0
- package/test/progress.test.ts +54 -0
- package/test/pty-host.test.ts +126 -0
- package/test/pty-pane.test.ts +62 -0
- package/test/public-surface.test.ts +75 -0
- package/test/render-cache.test.ts +25 -0
- package/test/render-epoch.test.ts +119 -0
- package/test/request-animation.test.ts +193 -0
- package/test/screen-icon-actions.test.ts +69 -0
- package/test/select-box.test.ts +86 -0
- package/test/select-pointer.test.ts +369 -0
- package/test/selectable-list.test.ts +258 -0
- package/test/selection/registry.test.ts +44 -0
- package/test/selection-action-bar.test.ts +204 -0
- package/test/split-pane-bridge.test.ts +944 -0
- package/test/split-pane.test.ts +134 -0
- package/test/status-presentation.test.ts +39 -0
- package/test/syntax.test.ts +59 -0
- package/test/terminal-colors.test.ts +115 -0
- package/test/terminal-embedding.test.ts +12 -0
- package/test/terminal-projection.test.ts +312 -0
- package/test/theme-colors.test.ts +175 -0
- package/test/tool-output.test.ts +160 -0
- package/test/tool-transcript.test.ts +1190 -0
- package/test/unified-diff.test.ts +691 -0
- package/themes/harmonious.json +61 -0
- package/tsconfig.json +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luan Santos
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# @luan.sh/pi-libtui
|
|
2
|
+
|
|
3
|
+
[Pi gallery](https://pi.dev/packages/@luan.sh/pi-libtui)
|
|
4
|
+
|
|
5
|
+
Shared terminal UI for Pi extensions: layouts, split panes, dialogs, pickers,
|
|
6
|
+
selection actions, semantic colors, icons, cursors, syntax highlighting,
|
|
7
|
+
animated tool surfaces, streamed output, diffs, terminal projection, and the
|
|
8
|
+
protocols those pieces need. The primary audience is extension authors.
|
|
9
|
+
|
|
10
|
+
The package has two surfaces. `import "@luan.sh/pi-libtui"` is a
|
|
11
|
+
side-effect-free library: it does not start Pi, probe the terminal, register a
|
|
12
|
+
tool, or install UI. `src/extension.ts` is a Pi extension (listed in the
|
|
13
|
+
package's `pi.extensions`) that installs the generic mouse, cursor, and
|
|
14
|
+
editor-token bridges, keeps the shared native PTY host alive, measures terminal
|
|
15
|
+
colors, applies the `harmonious` theme fallback, drives Pi's streaming status
|
|
16
|
+
row, and registers the `/libtui:colors` 256-color palette diagnostic. It
|
|
17
|
+
registers no model-facing tools, keybindings, or feature-specific UI.
|
|
18
|
+
|
|
19
|
+
## Preview
|
|
20
|
+
|
|
21
|
+
The native palette diagnostic and shared picker components in Xsettings.
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
[Watch the demo](https://github.com/luan/agents/releases/download/v0.3.2/pi-libtui.mp4).
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pi install npm:@luan.sh/pi-libtui
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This loads the host extension and the `harmonious` theme on its own. Feature
|
|
34
|
+
packages normally bundle their own copy instead: add `@luan.sh/pi-libtui` to
|
|
35
|
+
both `dependencies` and `bundledDependencies` in `package.json`, and list
|
|
36
|
+
`"./node_modules/pi-libtui/src/extension.ts"` in the package's
|
|
37
|
+
`pi.extensions` so the mouse/cursor bridge is active. The host claims itself
|
|
38
|
+
once per process, so several installed copies do not conflict.
|
|
39
|
+
|
|
40
|
+
Optional companion: `pi install npm:@luan.sh/pi-xsettings` adds the `/xsettings`
|
|
41
|
+
UI that publishes the appearance settings below; without it the compiled
|
|
42
|
+
defaults apply.
|
|
43
|
+
|
|
44
|
+
## Themes
|
|
45
|
+
|
|
46
|
+
The manifest exposes `themes/harmonious.json`, which relies on the terminal's
|
|
47
|
+
indexed palette. If `harmonious` is active and the measured terminal reports
|
|
48
|
+
neither a generated 256-color palette nor an ANSI base-16 palette, the host
|
|
49
|
+
switches to Pi's built-in theme for the detected light or dark scheme (dark if
|
|
50
|
+
the measurement fails).
|
|
51
|
+
|
|
52
|
+
## Public modules
|
|
53
|
+
|
|
54
|
+
Every entry point is a side-effect-free import. "Host required" means the
|
|
55
|
+
Pi-native behaviour also needs the extension loaded.
|
|
56
|
+
|
|
57
|
+
| Import path | Principal exports / capability | Host required |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `@luan.sh/pi-libtui` | Layouts, split panes, side-panel protocol, dialogs, pickers, inputs, selection actions, semantic colors, icons, cursors, `PtyProcess`, `PtyPane`, `applyScrollbar`, `PointerInteractionController`, `RenderedLinesCache`, `SyntaxText`, motion/progress, appearance (`getTuiAppearance`, `configureTuiAppearance`, `subscribeTuiAppearance`), `ensureNativeBinary` | Rendering no; panes, PTYs, and bridges yes |
|
|
60
|
+
| `@luan.sh/pi-libtui/diff` | `createUnifiedDiffModel`, `parseUnifiedDiff`, `renderUnifiedDiff`, `UnifiedDiffView`, bounded diff models/viewports | No |
|
|
61
|
+
| `@luan.sh/pi-libtui/editor` | `ensureEditorRegistry`, `dispatchEditorPaste`, `dispatchEditorRender`, `SemanticEditor`, `semanticEditorTheme`, editor registry contracts | Only to connect the registry to Pi's editor |
|
|
62
|
+
| `@luan.sh/pi-libtui/folding` | `ensureFoldingRegistry`, `foldTargetAt`, `clearFoldingCurrent`, fold-target contracts | Only for copy-mode keyboard integration |
|
|
63
|
+
| `@luan.sh/pi-libtui/mouse` | `ensureMouseRegistry`, `registerModalPointerShield`, viewport handlers, pointer contracts, `getFullscreenLayoutCapability`, `publishFullscreenLayoutCapability`, `resolveFullscreenLayout` | Yes for terminal pointer events and layout geometry |
|
|
64
|
+
| `@luan.sh/pi-libtui/selection` | `ensureSelectionRegistry`, native selection geometry, completion events, action contracts | Yes for Pi-native selection events |
|
|
65
|
+
| `@luan.sh/pi-libtui/stream` | `BoundedStreamBuffer`, bounded UTF-8/ANSI-safe stream snapshots | No |
|
|
66
|
+
| `@luan.sh/pi-libtui/terminal` | `TerminalProjection`, incremental `TerminalOutput` for bounded PTY/ANSI projection | No |
|
|
67
|
+
| `@luan.sh/pi-libtui/tool` | `ToolAction`, `LiveToolAction`, `ToolDisclosureAction`, `ToolActivity`, `ToolOutput`, `ToolTranscript`, `ToolViewRegion`, tool-call preview helpers | No for rendering |
|
|
68
|
+
|
|
69
|
+
The `ensure*Registry` functions create or reuse a process-global capability
|
|
70
|
+
keyed by `Symbol.for`, so feature packages and the host share one instance.
|
|
71
|
+
Tool presentation has three layers: `ToolTranscript` (copy-friendly action plus
|
|
72
|
+
payload), `ToolActivity` (streaming, diff, terminal, and viewport state for a
|
|
73
|
+
live surface), and `ToolOutput` for text streams. `mountTranscriptProjection`
|
|
74
|
+
exposes native transcript entries to a feature-owned component through a
|
|
75
|
+
guarded Pi 0.84–0.85 adapter; unsupported hosts keep their native transcript.
|
|
76
|
+
|
|
77
|
+
## Native binaries
|
|
78
|
+
|
|
79
|
+
Feature packages shell out to Rust binaries such as `terminal_bridge` (the
|
|
80
|
+
exported `TERMINAL_BRIDGE` descriptor). Requires a Rust toolchain
|
|
81
|
+
(https://rustup.rs). The `terminal_bridge` binary builds itself on first use
|
|
82
|
+
under Pi's agent directory (`native/terminal-bridge/<revision>/`), where
|
|
83
|
+
`<revision>` is the source commit recorded in the packaged `native-revision` file. Set `PI_TERMINAL_BRIDGE_BINARY` to use
|
|
84
|
+
a prebuilt binary; it must point at an executable file.
|
|
85
|
+
|
|
86
|
+
`ensureNativeBinary(binary, hooks?)` resolves in this order: the descriptor's
|
|
87
|
+
env override, then `<agentDir>/native/<crate>/<revision>/bin/<name>`, building
|
|
88
|
+
it on first use when absent. Builds are keyed by crate and source commit, so every
|
|
89
|
+
installed copy shares them, and concurrent requests within one process share
|
|
90
|
+
one build. Pass `onBuild` to show the delay in the UI. The extension host keeps
|
|
91
|
+
the shared PTY host alive across an extension reload; a session switch or quit
|
|
92
|
+
shuts it down.
|
|
93
|
+
|
|
94
|
+
## Fullscreen split panes
|
|
95
|
+
|
|
96
|
+
`mountSplitPane()` composes one extension-owned pane beside Pi's complete
|
|
97
|
+
fullscreen layout. Pi's transcript, editor, widgets, status, and footer stay in
|
|
98
|
+
the main pane and reflow to its width. The highest-priority contribution is
|
|
99
|
+
visible, with the latest mount breaking ties; disposing it restores the
|
|
100
|
+
previous contribution or Pi's unwrapped layout.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const unmount = mountSplitPane({
|
|
104
|
+
id: "example.details",
|
|
105
|
+
position: "right",
|
|
106
|
+
size: 32,
|
|
107
|
+
initialRatio: 0.4,
|
|
108
|
+
minMainSize: 1,
|
|
109
|
+
priority: 10,
|
|
110
|
+
onResize: (size) => saveCommittedWidth(size),
|
|
111
|
+
component: (host, theme) => new DetailsPane(host, theme),
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A draggable vertical border separates the pane from the main surface; Pi keeps
|
|
116
|
+
only `minMainSize`. `initialRatio` derives the first width when none is
|
|
117
|
+
restored, and `onResize` runs once when a drag commits. The pane hides when the
|
|
118
|
+
terminal cannot fit one pane cell, the border and gap, and the minimum main
|
|
119
|
+
size. The factory receives the active `tui`, viewport size, render requests,
|
|
120
|
+
and `focus()`, `blur()`, `isFocused()`; clicking either pane focuses it without
|
|
121
|
+
consuming the click. Split panes exist only in fullscreen mode. Pi 0.84.x has
|
|
122
|
+
`setLayoutRoot()` but no getter, so the host validates one private field
|
|
123
|
+
through a ref-counted prototype lease and leaves the layout unchanged if the
|
|
124
|
+
shape is absent.
|
|
125
|
+
|
|
126
|
+
## Appearance settings
|
|
127
|
+
|
|
128
|
+
This package has no settings store of its own. It exposes an appearance
|
|
129
|
+
registry with compiled defaults (`DEFAULT_TUI_APPEARANCE`) that a settings host
|
|
130
|
+
such as `@luan.sh/pi-xsettings` overrides through `configureTuiAppearance()`;
|
|
131
|
+
with @luan.sh/pi-xsettings installed they are edited live via `/xsettings`. Keys and
|
|
132
|
+
defaults:
|
|
133
|
+
|
|
134
|
+
| Key | Default | Values |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| `iconPack` | `unicode` | `unicode`, `nerd-fonts`, `emoji` |
|
|
137
|
+
| `activityIndicator` | `spinner` | `off`, `spinner`, `static`, and the Unicode/ASCII/Braille/Nerd Font animations in `TUI_ACTIVITY_INDICATOR_OPTIONS` |
|
|
138
|
+
| `activityMessage` | `phase` | `phase`, `typewriter` |
|
|
139
|
+
| `textEffect` | `off` | `off`, `sweep`, `glow`, `rainbow`, `rainbow-glow`, `lightning`, `aurora`, `glitch`, `crush` |
|
|
140
|
+
| `textEffectScope` | `message` | message only or the whole indicator, separator, and message unit |
|
|
141
|
+
| `pulseEffect` | `off` | dim-to-bright or contrasting-color pulse over any indicator/text effect |
|
|
142
|
+
| `statusPresentation` | `standard` | `standard`, mixed compositions such as `brainstorm`, or exclusive scenes in `TUI_STATUS_PRESENTATION_OPTIONS` |
|
|
143
|
+
| `animationSpeed` | `normal` | `slow`, `relaxed`, `normal`, `fast`, `very-fast` |
|
|
144
|
+
| `animationSmoothness` | `balanced` | `economy`, `balanced`, `smooth`, `ultra` (roughly 13 to 60 redraws per second) |
|
|
145
|
+
| `thinking*`, `working*`, `tool*` (`Indicator`, `Message`, `TextEffect`, `PulseEffect`, `Presentation`) | `inherit` | per-phase overrides of the general value |
|
|
146
|
+
| `powerline`, `powerlineButtons`, `softCursor` | `false` | Powerline separators, button caps, softer virtual cursor |
|
|
147
|
+
| `insertionCursor`, `navigationCursor`, `selectionCursor` | `virtual` | cursor styles |
|
|
148
|
+
|
|
149
|
+
Inline activity is composed as `indicator + message`, then the effect scope is
|
|
150
|
+
painted; an exclusive scene replaces that composition. The extension applies
|
|
151
|
+
the same renderer to Pi's streaming status row: Thinking takes priority over
|
|
152
|
+
Tool, which takes priority over Working. Feature surfaces may pass
|
|
153
|
+
`ActivityAnimationOverrides` to `activityFrame()` and
|
|
154
|
+
`mountConfiguredAnimation()`; omitted fields inherit the live appearance.
|
|
155
|
+
|
|
156
|
+
## Architecture
|
|
157
|
+
|
|
158
|
+
| Responsibility | Owner |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| Tool definition | None; the package adds no model-facing tool |
|
|
161
|
+
| Execution | None in the library; `src/extension.ts` owns host setup |
|
|
162
|
+
| State | Components own local state; `MouseBridgeHost` owns bridge state; `RequestAnimationController` owns request phase state |
|
|
163
|
+
| Shared contracts | `src/editor/protocol.ts` (via `src/editor.ts`), `src/folding.ts`, `src/selection.ts`, `src/decoration/pointer-interaction.ts`, the public contracts selected by `src/mouse.ts` |
|
|
164
|
+
| Native boundary | Mouse/cursor compatibility, terminal color queries, PTY host, host bridges in `src/host/` |
|
|
165
|
+
|
|
166
|
+
The host knows only generic TUI mechanics; feature labels, settings, actions,
|
|
167
|
+
and workflows stay in their owning packages. Color resolution has one path:
|
|
168
|
+
`src/color/theme.ts` owns the semantic-token table and resolves every paint
|
|
169
|
+
through `src/color/resolver.ts`. Feature code uses only the root color API:
|
|
170
|
+
`tuiTheme(theme)`, `createTuiThemeVariation(theme, name)`,
|
|
171
|
+
`tuiThemeAppearance(theme)`, `TuiForegroundToken`/`TuiBackgroundToken`,
|
|
172
|
+
`TuiSwatch` (seven ramps at shades `0`–`5`), opaque `TuiColor` handles, and
|
|
173
|
+
`TuiTheme.mixForeground()`.
|
|
174
|
+
|
|
175
|
+
## Layout
|
|
176
|
+
|
|
177
|
+
| Responsibility | Source |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| Extension entry and host | `src/extension.ts`, `src/host/` |
|
|
180
|
+
| Layout and rendering infrastructure | `src/background-surface.ts`, `src/component-stack.ts`, `src/line-layout.ts`, `src/render-cache.ts`, `src/scrollbar.ts` |
|
|
181
|
+
| Split panes and side panel | `src/split-pane.ts`, `src/host/split-pane-bridge.ts`, `src/panels.ts` |
|
|
182
|
+
| Overlays | `src/overlay/` |
|
|
183
|
+
| Controls | `src/controls/` |
|
|
184
|
+
| Text content, glyphs, status, pills, editor | `src/content/`, `src/decoration/`, `src/editor.ts`, `src/editor/` |
|
|
185
|
+
| Appearance, motion, request animation | `src/appearance.ts`, `src/motion.ts`, `src/request-animation.ts` |
|
|
186
|
+
| Colors and syntax | `src/color/`, `src/terminal-colors.ts`, `src/syntax.ts` |
|
|
187
|
+
| Streams, terminal projection, PTY host, diffs, tools | `src/stream.ts`, `src/terminal/`, `src/diff/`, `src/tool/` |
|
|
188
|
+
| Native binary discovery and first-use builds | `src/native-binary.ts` |
|
|
189
|
+
|
|
190
|
+
## Troubleshooting
|
|
191
|
+
|
|
192
|
+
- A feature renders but clicks do nothing: the host extension is not loaded.
|
|
193
|
+
Install `@luan.sh/pi-libtui` as a Pi package or list its `src/extension.ts`
|
|
194
|
+
in the feature package's `pi.extensions`.
|
|
195
|
+
- Nerd Font or Powerline glyphs are missing: set the icon pack to `unicode` and
|
|
196
|
+
disable Powerline in `/xsettings`. Those are also the compiled defaults.
|
|
197
|
+
- `cargo was not found`: install Rust from https://rustup.rs, or set the
|
|
198
|
+
binary's env override to a prebuilt executable.
|
|
199
|
+
|
|
200
|
+
## Develop
|
|
201
|
+
|
|
202
|
+
Source: https://github.com/luan/agents, directory
|
|
203
|
+
harnesses/pi/agent/packages/pi-libtui. Run `bun run typecheck` and
|
|
204
|
+
`bun test test` in that directory. When running from that checkout,
|
|
205
|
+
`ensureNativeBinary` also looks for `target/{release,debug}/<name>` in the Cargo
|
|
206
|
+
workspace and reports an unbuilt checkout instead of building. `just pi-pack`
|
|
207
|
+
records the checkout commit in every bundled `@luan.sh/pi-libtui/native-revision` file,
|
|
208
|
+
so native builds do not depend on npm versions or repository-wide release tags.
|
package/native-revision
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6ed73340be70e909918ec92adc31ba920bfc8cd4
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@luan.sh/pi-libtui",
|
|
3
|
+
"version": "0.3.8",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/luan/agents.git",
|
|
8
|
+
"directory": "harnesses/pi/agent/packages/pi-libtui"
|
|
9
|
+
},
|
|
10
|
+
"description": "Shared TUI components and generic mouse compatibility for local Pi extensions",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"pi-package"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/index.ts",
|
|
17
|
+
"./diff": "./src/diff/index.ts",
|
|
18
|
+
"./editor": "./src/editor.ts",
|
|
19
|
+
"./folding": "./src/folding.ts",
|
|
20
|
+
"./mouse": "./src/mouse.ts",
|
|
21
|
+
"./selection": "./src/selection.ts",
|
|
22
|
+
"./stream": "./src/stream.ts",
|
|
23
|
+
"./terminal": "./src/terminal/output.ts",
|
|
24
|
+
"./tool": "./src/tool/index.ts"
|
|
25
|
+
},
|
|
26
|
+
"pi": {
|
|
27
|
+
"extensions": [
|
|
28
|
+
"./src/extension.ts"
|
|
29
|
+
],
|
|
30
|
+
"themes": [
|
|
31
|
+
"./themes"
|
|
32
|
+
],
|
|
33
|
+
"image": "https://github.com/luan/agents/releases/download/v0.3.2/pi-libtui.png",
|
|
34
|
+
"video": "https://github.com/luan/agents/releases/download/v0.3.2/pi-libtui.mp4"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
38
|
+
"@earendil-works/pi-tui": "*"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@pierre/diffs": "1.3.5",
|
|
42
|
+
"@xterm/headless": "6.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@biomejs/biome": "2.5.10",
|
|
46
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
47
|
+
"@earendil-works/pi-tui": "0.84.2",
|
|
48
|
+
"@types/bun": "^1.3.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"typecheck": "tsc --build",
|
|
52
|
+
"test": "bun test --only-failures --timeout 15000 test"
|
|
53
|
+
},
|
|
54
|
+
"bundledDependencies": [],
|
|
55
|
+
"publishAliases": [
|
|
56
|
+
"pi-libtui"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { statusPresentationFrame, activityMessageFrame } from "./status-presentation.ts";
|
|
2
|
+
import type {
|
|
3
|
+
TuiActivityPresentation,
|
|
4
|
+
TuiAnimationSmoothness,
|
|
5
|
+
TuiAnimationSpeed,
|
|
6
|
+
TuiRequestPhase,
|
|
7
|
+
} from "./appearance.ts";
|
|
8
|
+
import type { TuiTheme } from "./color/theme.ts";
|
|
9
|
+
import {
|
|
10
|
+
activityFrame,
|
|
11
|
+
animationSmoothnessCadenceMs,
|
|
12
|
+
configuredAnimationCadenceMs,
|
|
13
|
+
type ActivityFrame,
|
|
14
|
+
} from "./motion.ts";
|
|
15
|
+
|
|
16
|
+
export interface ActivityPresentationOptions {
|
|
17
|
+
animationSpeed?: TuiAnimationSpeed;
|
|
18
|
+
animationSmoothness?: TuiAnimationSmoothness;
|
|
19
|
+
reducedMotion?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Render an inline activity composition or one exclusive status presentation. */
|
|
23
|
+
export function activityPresentationFrame(
|
|
24
|
+
colors: TuiTheme,
|
|
25
|
+
presentation: TuiActivityPresentation,
|
|
26
|
+
phase: TuiRequestPhase,
|
|
27
|
+
phaseLabel: string,
|
|
28
|
+
elapsedMs: number,
|
|
29
|
+
width: number,
|
|
30
|
+
options: ActivityPresentationOptions = {},
|
|
31
|
+
): ActivityFrame {
|
|
32
|
+
if (presentation.kind === "inline") {
|
|
33
|
+
const text =
|
|
34
|
+
presentation.messageStyle === "phase"
|
|
35
|
+
? phaseLabel
|
|
36
|
+
: activityMessageFrame(elapsedMs, {
|
|
37
|
+
animationSpeed: options.animationSpeed,
|
|
38
|
+
reducedMotion: options.reducedMotion,
|
|
39
|
+
});
|
|
40
|
+
return activityFrame(colors, text, elapsedMs, {
|
|
41
|
+
indicatorStyle: presentation.indicatorStyle,
|
|
42
|
+
textEffectStyle: presentation.textEffectStyle,
|
|
43
|
+
textEffectScope: presentation.textEffectScope,
|
|
44
|
+
pulseEffectStyle: presentation.pulseEffectStyle,
|
|
45
|
+
animationSpeed: options.animationSpeed,
|
|
46
|
+
animationSmoothness: options.animationSmoothness,
|
|
47
|
+
reducedMotion: options.reducedMotion,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
marker: "",
|
|
52
|
+
text: statusPresentationFrame(colors, presentation.style, elapsedMs, width, {
|
|
53
|
+
animationSpeed: options.animationSpeed,
|
|
54
|
+
phase,
|
|
55
|
+
reducedMotion: options.reducedMotion,
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Resolve the repaint cadence from the same presentation plan used to render frames. */
|
|
61
|
+
export function activityPresentationCadenceMs(
|
|
62
|
+
presentation: TuiActivityPresentation,
|
|
63
|
+
smoothness: TuiAnimationSmoothness,
|
|
64
|
+
speed: TuiAnimationSpeed,
|
|
65
|
+
): number | undefined {
|
|
66
|
+
return presentation.kind === "inline"
|
|
67
|
+
? configuredAnimationCadenceMs(
|
|
68
|
+
presentation.indicatorStyle,
|
|
69
|
+
presentation.textEffectStyle,
|
|
70
|
+
smoothness,
|
|
71
|
+
speed,
|
|
72
|
+
presentation.pulseEffectStyle,
|
|
73
|
+
)
|
|
74
|
+
: animationSmoothnessCadenceMs(smoothness);
|
|
75
|
+
}
|