pi-libtui 0.3.2

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.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/package.json +55 -0
  4. package/src/activity-presentation.ts +75 -0
  5. package/src/appearance.ts +632 -0
  6. package/src/background-surface.ts +125 -0
  7. package/src/color/palette.ts +186 -0
  8. package/src/color/preview.ts +157 -0
  9. package/src/color/resolver.ts +75 -0
  10. package/src/color/theme.ts +531 -0
  11. package/src/component-stack.ts +247 -0
  12. package/src/content/markdown-code.ts +60 -0
  13. package/src/content/terminal-text.ts +135 -0
  14. package/src/content/text.ts +105 -0
  15. package/src/controls/action-panel.ts +420 -0
  16. package/src/controls/dialog-button-bar.ts +287 -0
  17. package/src/controls/multi-select.ts +467 -0
  18. package/src/controls/picker-panel.ts +447 -0
  19. package/src/controls/screen-icon-actions.ts +134 -0
  20. package/src/controls/searchable-select.ts +394 -0
  21. package/src/controls/select-box.ts +224 -0
  22. package/src/controls/selectable-list.ts +433 -0
  23. package/src/controls/selection-action-bar.ts +382 -0
  24. package/src/controls/semantic-input.ts +23 -0
  25. package/src/controls/tab.ts +189 -0
  26. package/src/cursor.ts +288 -0
  27. package/src/decoration/editor-pills.ts +156 -0
  28. package/src/decoration/glyphs.ts +326 -0
  29. package/src/decoration/pointer-interaction.ts +123 -0
  30. package/src/decoration/powerline-pill.ts +104 -0
  31. package/src/decoration/status.ts +185 -0
  32. package/src/decoration/transient-pill.ts +176 -0
  33. package/src/diff/index.ts +21 -0
  34. package/src/diff/model.ts +76 -0
  35. package/src/diff/parse.ts +310 -0
  36. package/src/diff/pierre-syntax.ts +86 -0
  37. package/src/diff/render.ts +504 -0
  38. package/src/diff/structured.ts +264 -0
  39. package/src/diff/view.ts +75 -0
  40. package/src/editor/chrome.ts +341 -0
  41. package/src/editor/composition.ts +53 -0
  42. package/src/editor/layout.ts +81 -0
  43. package/src/editor/presentation.ts +35 -0
  44. package/src/editor/protocol.ts +188 -0
  45. package/src/editor.ts +41 -0
  46. package/src/extension.ts +30 -0
  47. package/src/folding.ts +192 -0
  48. package/src/host/cursor-bridge.ts +210 -0
  49. package/src/host/editor-bridge.ts +148 -0
  50. package/src/host/extension-host.ts +231 -0
  51. package/src/host/mouse-bridge.ts +212 -0
  52. package/src/host/mouse-dispatch.ts +400 -0
  53. package/src/host/mouse-input.ts +85 -0
  54. package/src/host/mouse-targets.ts +238 -0
  55. package/src/host/pi-layout-adapter.ts +298 -0
  56. package/src/host/pi-native-selection.ts +227 -0
  57. package/src/host/split-pane-bridge.ts +720 -0
  58. package/src/host/transcript-bridge.ts +236 -0
  59. package/src/index.ts +334 -0
  60. package/src/line-layout.ts +6 -0
  61. package/src/motion.ts +1409 -0
  62. package/src/mouse/events.ts +55 -0
  63. package/src/mouse/fullscreen-layout.ts +260 -0
  64. package/src/mouse/registry.ts +348 -0
  65. package/src/mouse.ts +37 -0
  66. package/src/native-binary.ts +189 -0
  67. package/src/overlay/anchored.ts +91 -0
  68. package/src/overlay/detail-card.ts +158 -0
  69. package/src/overlay/dialog.ts +266 -0
  70. package/src/overlay/floating.ts +106 -0
  71. package/src/overlay/framed-editor.ts +236 -0
  72. package/src/overlay/fullscreen.ts +170 -0
  73. package/src/overlay/hover-tooltip.ts +109 -0
  74. package/src/overlay/modal-mount.ts +118 -0
  75. package/src/panels.ts +207 -0
  76. package/src/render-cache.ts +80 -0
  77. package/src/render-epoch.ts +48 -0
  78. package/src/request-animation.ts +160 -0
  79. package/src/scrollbar.ts +27 -0
  80. package/src/selection.ts +148 -0
  81. package/src/split-pane.ts +232 -0
  82. package/src/status-presentation.ts +411 -0
  83. package/src/stream.ts +543 -0
  84. package/src/syntax.ts +316 -0
  85. package/src/terminal/bridge-client.ts +243 -0
  86. package/src/terminal/embedding.ts +6 -0
  87. package/src/terminal/output.ts +151 -0
  88. package/src/terminal/projection.ts +362 -0
  89. package/src/terminal/pty-host.ts +263 -0
  90. package/src/terminal/pty-pane.ts +308 -0
  91. package/src/terminal-colors.ts +334 -0
  92. package/src/tool/action.ts +170 -0
  93. package/src/tool/activity.ts +500 -0
  94. package/src/tool/call-preview.ts +44 -0
  95. package/src/tool/disclosure-action.ts +89 -0
  96. package/src/tool/expanded-region-viewport.ts +103 -0
  97. package/src/tool/index.ts +26 -0
  98. package/src/tool/output.ts +271 -0
  99. package/src/tool/transcript.ts +150 -0
  100. package/src/tool/view-region.ts +581 -0
  101. package/test/appearance.test.ts +183 -0
  102. package/test/bounded-stream.test.ts +150 -0
  103. package/test/bridge-client.test.ts +42 -0
  104. package/test/color-preview.test.ts +23 -0
  105. package/test/component-stack.test.ts +206 -0
  106. package/test/components.test.ts +1165 -0
  107. package/test/content-text.test.ts +21 -0
  108. package/test/cursor-bridge.test.ts +151 -0
  109. package/test/cursor.test.ts +147 -0
  110. package/test/editor-chrome.test.ts +119 -0
  111. package/test/editor-layout.test.ts +30 -0
  112. package/test/editor-registry.test.ts +203 -0
  113. package/test/editor-theme.test.ts +33 -0
  114. package/test/editor-token-pill.test.ts +72 -0
  115. package/test/floating-overlay.test.ts +57 -0
  116. package/test/folding.test.ts +147 -0
  117. package/test/harmonious-theme.test.ts +23 -0
  118. package/test/interaction.test.ts +104 -0
  119. package/test/key-icons.test.ts +62 -0
  120. package/test/markdown-code.test.ts +24 -0
  121. package/test/modal-overlay-mount.test.ts +94 -0
  122. package/test/motion.test.ts +649 -0
  123. package/test/mouse/bridge.test.ts +1302 -0
  124. package/test/mouse/extension.test.ts +205 -0
  125. package/test/mouse/registry.test.ts +140 -0
  126. package/test/mouse/screen-decoration.test.ts +167 -0
  127. package/test/native-binary.test.ts +52 -0
  128. package/test/palette.test.ts +32 -0
  129. package/test/panels.test.ts +74 -0
  130. package/test/pointer-components.test.ts +151 -0
  131. package/test/progress.test.ts +54 -0
  132. package/test/pty-host.test.ts +126 -0
  133. package/test/pty-pane.test.ts +62 -0
  134. package/test/public-surface.test.ts +75 -0
  135. package/test/render-cache.test.ts +25 -0
  136. package/test/render-epoch.test.ts +119 -0
  137. package/test/request-animation.test.ts +193 -0
  138. package/test/screen-icon-actions.test.ts +69 -0
  139. package/test/select-box.test.ts +86 -0
  140. package/test/select-pointer.test.ts +369 -0
  141. package/test/selectable-list.test.ts +258 -0
  142. package/test/selection/registry.test.ts +44 -0
  143. package/test/selection-action-bar.test.ts +204 -0
  144. package/test/split-pane-bridge.test.ts +944 -0
  145. package/test/split-pane.test.ts +134 -0
  146. package/test/status-presentation.test.ts +39 -0
  147. package/test/syntax.test.ts +59 -0
  148. package/test/terminal-colors.test.ts +115 -0
  149. package/test/terminal-embedding.test.ts +12 -0
  150. package/test/terminal-projection.test.ts +312 -0
  151. package/test/theme-colors.test.ts +175 -0
  152. package/test/tool-output.test.ts +160 -0
  153. package/test/tool-transcript.test.ts +1190 -0
  154. package/test/unified-diff.test.ts +691 -0
  155. package/themes/harmonious.json +61 -0
  156. 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,205 @@
1
+ # pi-libtui
2
+
3
+ Shared terminal UI for Pi extensions: layouts, split panes, dialogs, pickers,
4
+ selection actions, semantic colors, icons, cursors, syntax highlighting,
5
+ animated tool surfaces, streamed output, diffs, terminal projection, and the
6
+ protocols those pieces need. The primary audience is extension authors.
7
+
8
+ The package has two surfaces. `import "pi-libtui"` is a
9
+ side-effect-free library: it does not start Pi, probe the terminal, register a
10
+ tool, or install UI. `src/extension.ts` is a Pi extension (listed in the
11
+ package's `pi.extensions`) that installs the generic mouse, cursor, and
12
+ editor-token bridges, keeps the shared native PTY host alive, measures terminal
13
+ colors, applies the `harmonious` theme fallback, drives Pi's streaming status
14
+ row, and registers the `/libtui:colors` 256-color palette diagnostic. It
15
+ registers no model-facing tools, keybindings, or feature-specific UI.
16
+
17
+ ## Preview
18
+
19
+ The native palette diagnostic and shared picker components in Xsettings.
20
+
21
+ ![pi-libtui in Bootty](https://github.com/luan/agents/releases/download/v0.3.2/pi-libtui.png)
22
+
23
+ [Watch the demo](https://github.com/luan/agents/releases/download/v0.3.2/pi-libtui.mp4).
24
+
25
+ ## Install
26
+
27
+ ```sh
28
+ pi install npm:pi-libtui
29
+ ```
30
+
31
+ This loads the host extension and the `harmonious` theme on its own. Feature
32
+ packages normally bundle their own copy instead: add `pi-libtui` to
33
+ both `dependencies` and `bundledDependencies` in `package.json`, and list
34
+ `"./node_modules/pi-libtui/src/extension.ts"` in the package's
35
+ `pi.extensions` so the mouse/cursor bridge is active. The host claims itself
36
+ once per process, so several installed copies do not conflict.
37
+
38
+ Optional companion: `pi install npm:pi-xsettings` adds the `/xsettings`
39
+ UI that publishes the appearance settings below; without it the compiled
40
+ defaults apply.
41
+
42
+ ## Themes
43
+
44
+ The manifest exposes `themes/harmonious.json`, which relies on the terminal's
45
+ indexed palette. If `harmonious` is active and the measured terminal reports
46
+ neither a generated 256-color palette nor an ANSI base-16 palette, the host
47
+ switches to Pi's built-in theme for the detected light or dark scheme (dark if
48
+ the measurement fails).
49
+
50
+ ## Public modules
51
+
52
+ Every entry point is a side-effect-free import. "Host required" means the
53
+ Pi-native behaviour also needs the extension loaded.
54
+
55
+ | Import path | Principal exports / capability | Host required |
56
+ | --- | --- | --- |
57
+ | `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 |
58
+ | `pi-libtui/diff` | `createUnifiedDiffModel`, `parseUnifiedDiff`, `renderUnifiedDiff`, `UnifiedDiffView`, bounded diff models/viewports | No |
59
+ | `pi-libtui/editor` | `ensureEditorRegistry`, `dispatchEditorPaste`, `dispatchEditorRender`, `SemanticEditor`, `semanticEditorTheme`, editor registry contracts | Only to connect the registry to Pi's editor |
60
+ | `pi-libtui/folding` | `ensureFoldingRegistry`, `foldTargetAt`, `clearFoldingCurrent`, fold-target contracts | Only for copy-mode keyboard integration |
61
+ | `pi-libtui/mouse` | `ensureMouseRegistry`, `registerModalPointerShield`, viewport handlers, pointer contracts, `getFullscreenLayoutCapability`, `publishFullscreenLayoutCapability`, `resolveFullscreenLayout` | Yes for terminal pointer events and layout geometry |
62
+ | `pi-libtui/selection` | `ensureSelectionRegistry`, native selection geometry, completion events, action contracts | Yes for Pi-native selection events |
63
+ | `pi-libtui/stream` | `BoundedStreamBuffer`, bounded UTF-8/ANSI-safe stream snapshots | No |
64
+ | `pi-libtui/terminal` | `TerminalProjection`, incremental `TerminalOutput` for bounded PTY/ANSI projection | No |
65
+ | `pi-libtui/tool` | `ToolAction`, `LiveToolAction`, `ToolDisclosureAction`, `ToolActivity`, `ToolOutput`, `ToolTranscript`, `ToolViewRegion`, tool-call preview helpers | No for rendering |
66
+
67
+ The `ensure*Registry` functions create or reuse a process-global capability
68
+ keyed by `Symbol.for`, so feature packages and the host share one instance.
69
+ Tool presentation has three layers: `ToolTranscript` (copy-friendly action plus
70
+ payload), `ToolActivity` (streaming, diff, terminal, and viewport state for a
71
+ live surface), and `ToolOutput` for text streams. `mountTranscriptProjection`
72
+ exposes native transcript entries to a feature-owned component through a
73
+ guarded Pi 0.84–0.85 adapter; unsupported hosts keep their native transcript.
74
+
75
+ ## Native binaries
76
+
77
+ Feature packages shell out to Rust binaries such as `terminal_bridge` (the
78
+ exported `TERMINAL_BRIDGE` descriptor). Requires a Rust toolchain
79
+ (https://rustup.rs). The `terminal_bridge` binary builds itself on first use
80
+ under Pi's agent directory (`native/terminal-bridge/v<version>/`), where
81
+ `<version>` is this package's version. Set `PI_TERMINAL_BRIDGE_BINARY` to use
82
+ a prebuilt binary; it must point at an executable file.
83
+
84
+ `ensureNativeBinary(binary, hooks?)` resolves in this order: the descriptor's
85
+ env override, then `<agentDir>/native/<crate>/v<version>/bin/<name>`, building
86
+ it on first use when absent. Builds are keyed by crate and version, so every
87
+ installed copy shares them, and concurrent requests within one process share
88
+ one build. Pass `onBuild` to show the delay in the UI. The extension host keeps
89
+ the shared PTY host alive across an extension reload; a session switch or quit
90
+ shuts it down.
91
+
92
+ ## Fullscreen split panes
93
+
94
+ `mountSplitPane()` composes one extension-owned pane beside Pi's complete
95
+ fullscreen layout. Pi's transcript, editor, widgets, status, and footer stay in
96
+ the main pane and reflow to its width. The highest-priority contribution is
97
+ visible, with the latest mount breaking ties; disposing it restores the
98
+ previous contribution or Pi's unwrapped layout.
99
+
100
+ ```ts
101
+ const unmount = mountSplitPane({
102
+ id: "example.details",
103
+ position: "right",
104
+ size: 32,
105
+ initialRatio: 0.4,
106
+ minMainSize: 1,
107
+ priority: 10,
108
+ onResize: (size) => saveCommittedWidth(size),
109
+ component: (host, theme) => new DetailsPane(host, theme),
110
+ });
111
+ ```
112
+
113
+ A draggable vertical border separates the pane from the main surface; Pi keeps
114
+ only `minMainSize`. `initialRatio` derives the first width when none is
115
+ restored, and `onResize` runs once when a drag commits. The pane hides when the
116
+ terminal cannot fit one pane cell, the border and gap, and the minimum main
117
+ size. The factory receives the active `tui`, viewport size, render requests,
118
+ and `focus()`, `blur()`, `isFocused()`; clicking either pane focuses it without
119
+ consuming the click. Split panes exist only in fullscreen mode. Pi 0.84.x has
120
+ `setLayoutRoot()` but no getter, so the host validates one private field
121
+ through a ref-counted prototype lease and leaves the layout unchanged if the
122
+ shape is absent.
123
+
124
+ ## Appearance settings
125
+
126
+ This package has no settings store of its own. It exposes an appearance
127
+ registry with compiled defaults (`DEFAULT_TUI_APPEARANCE`) that a settings host
128
+ such as `pi-xsettings` overrides through `configureTuiAppearance()`;
129
+ with pi-xsettings installed they are edited live via `/xsettings`. Keys and
130
+ defaults:
131
+
132
+ | Key | Default | Values |
133
+ | --- | --- | --- |
134
+ | `iconPack` | `unicode` | `unicode`, `nerd-fonts`, `emoji` |
135
+ | `activityIndicator` | `spinner` | `off`, `spinner`, `static`, and the Unicode/ASCII/Braille/Nerd Font animations in `TUI_ACTIVITY_INDICATOR_OPTIONS` |
136
+ | `activityMessage` | `phase` | `phase`, `typewriter` |
137
+ | `textEffect` | `off` | `off`, `sweep`, `glow`, `rainbow`, `rainbow-glow`, `lightning`, `aurora`, `glitch`, `crush` |
138
+ | `textEffectScope` | `message` | message only or the whole indicator, separator, and message unit |
139
+ | `pulseEffect` | `off` | dim-to-bright or contrasting-color pulse over any indicator/text effect |
140
+ | `statusPresentation` | `standard` | `standard`, mixed compositions such as `brainstorm`, or exclusive scenes in `TUI_STATUS_PRESENTATION_OPTIONS` |
141
+ | `animationSpeed` | `normal` | `slow`, `relaxed`, `normal`, `fast`, `very-fast` |
142
+ | `animationSmoothness` | `balanced` | `economy`, `balanced`, `smooth`, `ultra` (roughly 13 to 60 redraws per second) |
143
+ | `thinking*`, `working*`, `tool*` (`Indicator`, `Message`, `TextEffect`, `PulseEffect`, `Presentation`) | `inherit` | per-phase overrides of the general value |
144
+ | `powerline`, `powerlineButtons`, `softCursor` | `false` | Powerline separators, button caps, softer virtual cursor |
145
+ | `insertionCursor`, `navigationCursor`, `selectionCursor` | `virtual` | cursor styles |
146
+
147
+ Inline activity is composed as `indicator + message`, then the effect scope is
148
+ painted; an exclusive scene replaces that composition. The extension applies
149
+ the same renderer to Pi's streaming status row: Thinking takes priority over
150
+ Tool, which takes priority over Working. Feature surfaces may pass
151
+ `ActivityAnimationOverrides` to `activityFrame()` and
152
+ `mountConfiguredAnimation()`; omitted fields inherit the live appearance.
153
+
154
+ ## Architecture
155
+
156
+ | Responsibility | Owner |
157
+ | --- | --- |
158
+ | Tool definition | None; the package adds no model-facing tool |
159
+ | Execution | None in the library; `src/extension.ts` owns host setup |
160
+ | State | Components own local state; `MouseBridgeHost` owns bridge state; `RequestAnimationController` owns request phase state |
161
+ | 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` |
162
+ | Native boundary | Mouse/cursor compatibility, terminal color queries, PTY host, host bridges in `src/host/` |
163
+
164
+ The host knows only generic TUI mechanics; feature labels, settings, actions,
165
+ and workflows stay in their owning packages. Color resolution has one path:
166
+ `src/color/theme.ts` owns the semantic-token table and resolves every paint
167
+ through `src/color/resolver.ts`. Feature code uses only the root color API:
168
+ `tuiTheme(theme)`, `createTuiThemeVariation(theme, name)`,
169
+ `tuiThemeAppearance(theme)`, `TuiForegroundToken`/`TuiBackgroundToken`,
170
+ `TuiSwatch` (seven ramps at shades `0`–`5`), opaque `TuiColor` handles, and
171
+ `TuiTheme.mixForeground()`.
172
+
173
+ ## Layout
174
+
175
+ | Responsibility | Source |
176
+ | --- | --- |
177
+ | Extension entry and host | `src/extension.ts`, `src/host/` |
178
+ | Layout and rendering infrastructure | `src/background-surface.ts`, `src/component-stack.ts`, `src/line-layout.ts`, `src/render-cache.ts`, `src/scrollbar.ts` |
179
+ | Split panes and side panel | `src/split-pane.ts`, `src/host/split-pane-bridge.ts`, `src/panels.ts` |
180
+ | Overlays | `src/overlay/` |
181
+ | Controls | `src/controls/` |
182
+ | Text content, glyphs, status, pills, editor | `src/content/`, `src/decoration/`, `src/editor.ts`, `src/editor/` |
183
+ | Appearance, motion, request animation | `src/appearance.ts`, `src/motion.ts`, `src/request-animation.ts` |
184
+ | Colors and syntax | `src/color/`, `src/terminal-colors.ts`, `src/syntax.ts` |
185
+ | Streams, terminal projection, PTY host, diffs, tools | `src/stream.ts`, `src/terminal/`, `src/diff/`, `src/tool/` |
186
+ | Native binary discovery and first-use builds | `src/native-binary.ts` |
187
+
188
+ ## Troubleshooting
189
+
190
+ - A feature renders but clicks do nothing: the host extension is not loaded.
191
+ Install `pi-libtui` as a Pi package or list its `src/extension.ts`
192
+ in the feature package's `pi.extensions`.
193
+ - Nerd Font or Powerline glyphs are missing: set the icon pack to `unicode` and
194
+ disable Powerline in `/xsettings`. Those are also the compiled defaults.
195
+ - `cargo was not found`: install Rust from https://rustup.rs, or set the
196
+ binary's env override to a prebuilt executable.
197
+
198
+ ## Develop
199
+
200
+ Source: https://github.com/luan/agents, directory
201
+ harnesses/pi/agent/packages/pi-libtui. Run `bun run typecheck` and
202
+ `bun test test` in that directory. When running from that checkout,
203
+ `ensureNativeBinary` also looks for `target/{release,debug}/<name>` in the Cargo
204
+ workspace and reports an unbuilt checkout instead of building; builds are pinned
205
+ to the `v<version>` git tag, so publishing requires a matching tag.
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "pi-libtui",
3
+ "version": "0.3.2",
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
+ }
@@ -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
+ }