@herbertgao/pi-extensions 2026.9.18 → 2026.9.20

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 (226) hide show
  1. package/README.md +20 -28
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
  5. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
  32. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
  33. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
  34. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
  35. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
  36. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
  37. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
  38. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
  39. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
  45. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
  46. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
  47. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
  48. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
  49. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
  50. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
  51. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
  52. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
  53. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
  54. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
  55. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
  56. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
  57. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
  58. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
  59. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
  60. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
  61. package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
  62. package/node_modules/pi-antigravity/README.md +29 -6
  63. package/node_modules/pi-antigravity/package.json +12 -7
  64. package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
  65. package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
  66. package/node_modules/pi-antigravity/src/client/client.ts +1 -1
  67. package/node_modules/pi-antigravity/src/image/image.ts +4 -4
  68. package/node_modules/pi-antigravity/src/index.ts +203 -13
  69. package/node_modules/pi-antigravity/src/search/index.ts +1 -0
  70. package/node_modules/pi-antigravity/src/search/search.ts +325 -0
  71. package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
  72. package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
  73. package/node_modules/pi-antigravity/src/types/types.ts +5 -12
  74. package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
  75. package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
  76. package/node_modules/pi-typesafe/README.md +2 -0
  77. package/node_modules/pi-typesafe/package.json +2 -1
  78. package/node_modules/pi-web-access/CHANGELOG.md +72 -0
  79. package/node_modules/pi-web-access/README.md +34 -13
  80. package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
  81. package/node_modules/pi-web-access/curator-page.ts +5 -1
  82. package/node_modules/pi-web-access/curator-server.ts +2 -0
  83. package/node_modules/pi-web-access/dist/index.js +1337 -1009
  84. package/node_modules/pi-web-access/exa.ts +20 -52
  85. package/node_modules/pi-web-access/extract.ts +41 -4
  86. package/node_modules/pi-web-access/gemini-search.ts +12 -4
  87. package/node_modules/pi-web-access/index.ts +29 -37
  88. package/node_modules/pi-web-access/openai-search.ts +35 -7
  89. package/node_modules/pi-web-access/package.json +6 -5
  90. package/node_modules/pi-web-access/page-query.ts +2 -2
  91. package/node_modules/pi-web-access/query-rewrite.ts +2 -3
  92. package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
  93. package/node_modules/pi-web-access/storage.ts +48 -77
  94. package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
  95. package/node_modules/pi-web-access/summary-review.ts +3 -4
  96. package/node_modules/pi-web-access/tavily.ts +67 -20
  97. package/node_modules/pi-web-access/tool-activation.ts +53 -33
  98. package/node_modules/pi-web-access/utils.ts +1 -1
  99. package/node_modules/pi-web-access/you.ts +164 -0
  100. package/node_modules/pi-web-access/zai.ts +161 -0
  101. package/package.json +7 -11
  102. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
  103. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
  104. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
  105. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
  106. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
  107. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
  108. package/node_modules/pi-footer/CHANGELOG.md +0 -145
  109. package/node_modules/pi-footer/LICENSE +0 -21
  110. package/node_modules/pi-footer/README.md +0 -354
  111. package/node_modules/pi-footer/package.json +0 -68
  112. package/node_modules/pi-footer/src/cache.ts +0 -106
  113. package/node_modules/pi-footer/src/colors.ts +0 -237
  114. package/node_modules/pi-footer/src/config.ts +0 -202
  115. package/node_modules/pi-footer/src/event-widgets.ts +0 -41
  116. package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
  117. package/node_modules/pi-footer/src/git.ts +0 -135
  118. package/node_modules/pi-footer/src/index.ts +0 -268
  119. package/node_modules/pi-footer/src/metrics.ts +0 -141
  120. package/node_modules/pi-footer/src/presets.ts +0 -383
  121. package/node_modules/pi-footer/src/render.ts +0 -114
  122. package/node_modules/pi-footer/src/separators.ts +0 -52
  123. package/node_modules/pi-footer/src/types.ts +0 -122
  124. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
  125. package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
  126. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
  127. package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
  128. package/node_modules/pi-footer/src/ui/events.ts +0 -35
  129. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
  130. package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
  131. package/node_modules/pi-footer/src/ui/fields.ts +0 -252
  132. package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
  133. package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
  134. package/node_modules/pi-footer/src/ui/layout.ts +0 -17
  135. package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
  136. package/node_modules/pi-footer/src/ui/model.ts +0 -64
  137. package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
  138. package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
  139. package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
  140. package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
  141. package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
  142. package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
  143. package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
  144. package/node_modules/pi-footer/src/ui/screen.ts +0 -243
  145. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
  146. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
  147. package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
  148. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
  149. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
  150. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
  151. package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
  152. package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
  153. package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
  154. package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
  155. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
  156. package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
  157. package/node_modules/pi-footer/src/ui/theme.ts +0 -48
  158. package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
  159. package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
  160. package/node_modules/pi-footer/src/ui.ts +0 -66
  161. package/node_modules/pi-footer/src/widgets/context.ts +0 -28
  162. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
  163. package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
  164. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
  165. package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
  166. package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
  167. package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
  168. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
  169. package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
  170. package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
  171. package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
  172. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
  173. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
  174. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
  175. package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
  176. package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
  177. package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
  178. package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
  179. package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
  180. package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
  181. package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
  182. package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
  183. package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
  184. package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
  185. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
  186. package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
  187. package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
  188. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
  189. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
  190. package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
  191. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
  192. package/node_modules/pi-footer/src/widgets/options.ts +0 -122
  193. package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
  194. package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
  195. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
  196. package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
  197. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
  198. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
  199. package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
  200. package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
  201. package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
  202. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
  203. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
  204. package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
  205. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
  206. package/node_modules/pi-footer/src/widgets/store.ts +0 -26
  207. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
  208. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
  209. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
  210. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
  211. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
  212. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
  213. package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
  214. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
  215. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
  216. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
  217. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
  218. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
  219. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
  220. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
  221. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
  222. package/node_modules/pi-footer/src/widgets/types.ts +0 -181
  223. package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
  224. package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
  225. package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
  226. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
@@ -1,243 +0,0 @@
1
- import { Key, matchesKey } from "@earendil-works/pi-tui";
2
-
3
- import { cloneConfig } from "../config.js";
4
- import type { GetExtensionStatuses } from "../extension-statuses.js";
5
- import type { StatuslineConfig, StatuslineData } from "../types.js";
6
- import { WidgetStore } from "../widgets/store.js";
7
- import type { Widget } from "../widgets/types.js";
8
- import { ConfigLifecycle, type ConfigUiResult } from "./config-lifecycle.js";
9
- import { escapeTarget } from "./helpers.js";
10
- import { activeLineCount, visibleRowCount } from "./layout.js";
11
- import { CONFIG_UI_HEIGHT_RATIO, type ScreenView } from "./model.js";
12
- import { OverlayRender } from "./overlay-render.js";
13
- import type { ScreenContext } from "./screen-context.js";
14
- import { ScreenController } from "./screen-controller.js";
15
- import { ScreenRender } from "./screen-render.js";
16
- import { createScreenState, type ScreenState } from "./screen-state.js";
17
- import { AddWidgetScreen } from "./screens/add-widget.js";
18
- import { ConfirmExitScreen } from "./screens/confirm-exit.js";
19
- import { EditColorsScreen } from "./screens/edit-colors.js";
20
- import { EditWidgetScreen } from "./screens/edit-widget.js";
21
- import { ExtensionStatusRowScreen } from "./screens/extension-status-row.js";
22
- import { GlobalScreen } from "./screens/global.js";
23
- import { LineListScreen } from "./screens/line-list.js";
24
- import { MainScreen } from "./screens/main.js";
25
- import { ColorLevelConfirmScreen, TerminalScreen, TerminalState } from "./screens/terminal.js";
26
- import { WidgetListScreen } from "./screens/widget-list.js";
27
- import { createUiTheme, type ThemeProvider, type UiTheme } from "./theme.js";
28
- import { RETRO_FRAME_INTERVAL_MS } from "./title-bar.js";
29
-
30
- export class StatuslineConfigScreen {
31
- private readonly state: ScreenState;
32
- private readonly lifecycle: ConfigLifecycle;
33
- private readonly theme: UiTheme;
34
- private readonly screenRender: ScreenRender;
35
- private readonly overlayRender: OverlayRender;
36
- private readonly screenContext: ScreenContext;
37
- private readonly screenController: ScreenController;
38
- private saving = false;
39
- private readonly animationTimer: ReturnType<typeof setInterval>;
40
-
41
- constructor(
42
- config: StatuslineConfig,
43
- private readonly previewData: StatuslineData,
44
- private readonly getExtensionStatuses: GetExtensionStatuses,
45
- private readonly requestRender: () => void,
46
- private readonly getTerminalRows: () => number,
47
- private readonly props: {
48
- onChange(config: StatuslineConfig): void;
49
- onSave(config: StatuslineConfig): Promise<void>;
50
- onClose(result: ConfigUiResult): void;
51
- getTheme: ThemeProvider;
52
- },
53
- ) {
54
- this.state = createScreenState(WidgetStore.fromConfig(cloneConfig(config)));
55
- this.lifecycle = new ConfigLifecycle(config);
56
- this.theme = createUiTheme(props.getTheme);
57
- this.screenRender = new ScreenRender(this.theme);
58
- this.overlayRender = new OverlayRender(this.theme, this.screenRender);
59
- this.screenContext = this.createScreenContext();
60
- this.screenController = new ScreenController(this.screenContext);
61
- this.registerScreens();
62
- this.animationTimer = setInterval(this.requestRender, RETRO_FRAME_INTERVAL_MS);
63
- }
64
-
65
- render(width: number): string[] {
66
- return this.overlayRender.render({
67
- width,
68
- terminalRows: this.getTerminalRows(),
69
- activeLineCount: this.getActiveLineCount(),
70
- visibleRowCount: this.getVisibleRowCount(),
71
- store: this.state.store,
72
- previewData: this.previewData,
73
- getExtensionStatuses: this.getExtensionStatuses,
74
- theme: this.props.getTheme(),
75
- requestRender: this.requestRender,
76
- configStateText: this.lifecycle.label
77
- ? this.theme.configStateLabel(this.lifecycle.state, this.lifecycle.label)
78
- : "",
79
- body: this.screenController.renderScreen(width),
80
- });
81
- }
82
-
83
- invalidate(): void {}
84
-
85
- dispose(): void {
86
- clearInterval(this.animationTimer);
87
- }
88
-
89
- handleInput(data: string): void {
90
- if (matchesKey(data, Key.ctrl("s"))) {
91
- void this.saveConfig(false);
92
- return;
93
- }
94
-
95
- if (this.state.view === "confirm-color-level" || this.state.view === "confirm-exit") {
96
- this.screenController.handleInput(data);
97
- return;
98
- }
99
-
100
- if (matchesKey(data, Key.escape)) {
101
- const target = escapeTarget(this.state.view);
102
- if (target === "close") this.requestExit();
103
- else this.show(target);
104
- return;
105
- }
106
-
107
- this.screenController.handleInput(data);
108
- }
109
-
110
- private registerScreens(): void {
111
- const terminalState = new TerminalState();
112
- this.screenController.register("main", new MainScreen(this.screenContext, this.screenRender));
113
- this.screenController.register(
114
- "line-list",
115
- new LineListScreen(this.screenContext, this.screenRender, "Edit lines", "widget-list"),
116
- );
117
- this.screenController.register(
118
- "color-line-list",
119
- new LineListScreen(this.screenContext, this.screenRender, "Edit colors", "color-widget-list"),
120
- );
121
- this.screenController.register(
122
- "widget-list",
123
- new WidgetListScreen(this.screenContext, this.screenRender, false),
124
- );
125
- this.screenController.register(
126
- "color-widget-list",
127
- new WidgetListScreen(this.screenContext, this.screenRender, true),
128
- );
129
- this.screenController.register(
130
- "add-widget",
131
- new AddWidgetScreen(this.screenContext, this.screenRender),
132
- );
133
- this.screenController.register(
134
- "edit-widget",
135
- new EditWidgetScreen(this.screenContext, this.screenRender),
136
- );
137
- this.screenController.register(
138
- "edit-colors",
139
- new EditColorsScreen(this.screenContext, this.screenRender),
140
- );
141
- this.screenController.register(
142
- "terminal",
143
- new TerminalScreen(this.screenContext, this.screenRender, terminalState),
144
- );
145
- this.screenController.register(
146
- "confirm-color-level",
147
- new ColorLevelConfirmScreen(this.screenContext, this.screenRender, terminalState),
148
- );
149
- this.screenController.register(
150
- "confirm-exit",
151
- new ConfirmExitScreen(this.screenContext, this.screenRender),
152
- );
153
- this.screenController.register(
154
- "global",
155
- new GlobalScreen(this.screenContext, this.screenRender),
156
- );
157
- this.screenController.register(
158
- "extension-status-row",
159
- new ExtensionStatusRowScreen(this.screenContext, this.screenRender),
160
- );
161
- }
162
-
163
- private createScreenContext(): ScreenContext {
164
- return {
165
- state: this.state,
166
- theme: this.theme,
167
- getExtensionStatuses: this.getExtensionStatuses,
168
- currentLine: () => this.currentLine(),
169
- currentWidget: () => this.currentWidget(),
170
- visibleRowCount: () => this.getVisibleRowCount(),
171
- show: (view) => this.show(view),
172
- emitChange: () => this.emitChange(),
173
- save: (exitAfterSave) => void this.saveConfig(exitAfterSave),
174
- exitWithoutSaving: () => this.exitWithoutSaving(),
175
- };
176
- }
177
-
178
- private show(view: ScreenView): void {
179
- this.state.view = view;
180
- }
181
-
182
- private currentLine(): Widget[] {
183
- return this.state.store.lines[this.state.selectedLine] ?? this.state.store.lines[0] ?? [];
184
- }
185
-
186
- private currentWidget(): Widget | undefined {
187
- return this.currentLine()[this.state.selectedWidget];
188
- }
189
-
190
- private getVisibleRowCount(): number {
191
- return visibleRowCount(
192
- this.getTerminalRows(),
193
- CONFIG_UI_HEIGHT_RATIO,
194
- this.getActiveLineCount(),
195
- );
196
- }
197
-
198
- private getActiveLineCount(): number {
199
- return activeLineCount(this.state.store.lines);
200
- }
201
-
202
- private requestExit(): void {
203
- if (!this.lifecycle.dirty) {
204
- this.closeWithResult(this.lifecycle.closeResult(false));
205
- return;
206
- }
207
- this.state.viewBeforeConfirmExit = this.state.view;
208
- this.show("confirm-exit");
209
- }
210
-
211
- private exitWithoutSaving(): void {
212
- this.closeWithResult(this.lifecycle.closeResult(false));
213
- }
214
-
215
- private async saveConfig(exitAfterSave: boolean): Promise<void> {
216
- if (this.saving) return;
217
- this.saving = true;
218
- this.lifecycle.beginSave();
219
- this.requestRender();
220
- try {
221
- const config = cloneConfig(this.state.store.toConfig());
222
- await this.props.onSave(config);
223
- this.lifecycle.markSaved(config);
224
- if (exitAfterSave) this.closeWithResult(this.lifecycle.closeResult(true));
225
- } catch {
226
- this.lifecycle.markSaveFailed();
227
- // Error notification is handled by the save callback.
228
- } finally {
229
- this.saving = false;
230
- this.requestRender();
231
- }
232
- }
233
-
234
- private closeWithResult(result: ConfigUiResult): void {
235
- this.props.onChange(result.config);
236
- this.props.onClose(result);
237
- }
238
-
239
- private emitChange(): void {
240
- this.lifecycle.markChanged();
241
- this.props.onChange(cloneConfig(this.state.store.toConfig()));
242
- }
243
- }
@@ -1,92 +0,0 @@
1
- import { Key, matchesKey } from "@earendil-works/pi-tui";
2
-
3
- import { registry, type WidgetDefinition } from "../../widgets/registry.js";
4
- import { clamp, isPrintable, wrap } from "../helpers.js";
5
- import { pageSelection } from "../navigation.js";
6
- import { Controller } from "./controller.js";
7
-
8
- export class AddWidgetScreen extends Controller {
9
- private selected = 0;
10
- private filter = "";
11
-
12
- renderScreen(width: number): string[] {
13
- const items = this.filterWidgets();
14
- const visibleCount = this.ctx.visibleRowCount();
15
- const start = clamp(
16
- this.selected - Math.floor(visibleCount / 2),
17
- 0,
18
- Math.max(0, items.length - visibleCount),
19
- );
20
- const visible = items.slice(start, start + visibleCount);
21
- const lines = [
22
- this.render.line(
23
- this.render.menuTitle("Add Widget", "Search or select a widget to add"),
24
- width,
25
- ),
26
- this.render.line(this.ctx.theme.dim(this.hint()), width),
27
- this.render.line(
28
- this.ctx.theme.dim(this.countLabel(items.length, start, visible.length)),
29
- width,
30
- ),
31
- ];
32
- visible.forEach((definition, offset) => {
33
- const index = start + offset;
34
- const text = this.itemLabel(definition);
35
- lines.push(this.render.menuLine(index === this.selected, text, width));
36
- });
37
- return lines;
38
- }
39
-
40
- handleInput(data: string): void {
41
- const items = this.filterWidgets();
42
- if (matchesKey(data, Key.up))
43
- this.selected = wrap(this.selected - 1, Math.max(1, items.length));
44
- else if (matchesKey(data, Key.down))
45
- this.selected = wrap(this.selected + 1, Math.max(1, items.length));
46
- else if (matchesKey(data, Key.pageUp)) this.page(items.length, -1);
47
- else if (matchesKey(data, Key.pageDown)) this.page(items.length, 1);
48
- else if (matchesKey(data, Key.backspace)) {
49
- this.filter = this.filter.slice(0, -1);
50
- this.selected = 0;
51
- } else if (matchesKey(data, Key.enter)) {
52
- const definition = items[this.selected];
53
- if (!definition) return;
54
- this.ctx
55
- .currentLine()
56
- .splice(this.ctx.state.selectedWidget + 1, 0, registry.createWidget(definition.type));
57
- this.ctx.state.selectedWidget += 1;
58
- this.ctx.show("widget-list");
59
- this.ctx.emitChange();
60
- } else if (isPrintable(data)) {
61
- this.filter += data.toLowerCase();
62
- this.selected = 0;
63
- }
64
- }
65
-
66
- private hint(): string {
67
- return `type to filter • ↑/↓ select • pgup/pgdn jump • enter add • esc back • filter: ${this.filter || "(none)"}`;
68
- }
69
-
70
- private countLabel(total: number, start: number, visibleLength: number): string {
71
- const range = total === 0 ? "0-0" : `${start + 1}-${Math.min(total, start + visibleLength)}`;
72
- return `${total} result(s), showing ${range}`;
73
- }
74
-
75
- private filterWidgets(): WidgetDefinition[] {
76
- const filter = this.filter.trim();
77
- if (!filter) return [...registry.definitions];
78
- return registry.definitions.filter((definition) =>
79
- `${definition.category} ${definition.label} ${definition.type}`
80
- .toLowerCase()
81
- .includes(filter.toLowerCase()),
82
- );
83
- }
84
-
85
- private itemLabel(definition: WidgetDefinition): string {
86
- return `${definition.category} / ${definition.label} ${this.ctx.theme.dim(definition.description)}`;
87
- }
88
-
89
- private page(length: number, delta: -1 | 1): void {
90
- this.selected = pageSelection(this.selected, length, this.ctx.visibleRowCount(), delta);
91
- }
92
- }
@@ -1,52 +0,0 @@
1
- import { Key, matchesKey } from "@earendil-works/pi-tui";
2
-
3
- import { wrap } from "../helpers.js";
4
- import { Controller } from "./controller.js";
5
-
6
- const ITEMS = ["Save & Exit", "Exit without saving", "Return to config UI"] as const;
7
- const HINT = "↑/↓ select • enter confirm • s save • x discard • esc/r back";
8
-
9
- export class ConfirmExitScreen extends Controller {
10
- private selected = 0;
11
-
12
- renderScreen(width: number): string[] {
13
- return [
14
- this.render.line(
15
- this.render.menuTitle("Unsaved changes", "Choose how to close configuration"),
16
- width,
17
- ),
18
- this.render.line(this.ctx.theme.dim(HINT), width),
19
- this.render.line(
20
- this.ctx.theme.warning("You have unsaved pi-footer configuration changes."),
21
- width,
22
- ),
23
- ...ITEMS.map((item, index) => this.render.menuLine(index === this.selected, item, width)),
24
- ];
25
- }
26
-
27
- handleInput(data: string): void {
28
- if (matchesKey(data, Key.escape)) this.returnToConfigUi();
29
- else if (matchesKey(data, Key.up) || matchesKey(data, Key.left))
30
- this.selected = wrap(this.selected - 1, ITEMS.length);
31
- else if (matchesKey(data, Key.down) || matchesKey(data, Key.right))
32
- this.selected = wrap(this.selected + 1, ITEMS.length);
33
- else if (matchesKey(data, Key.enter)) this.selectedAction();
34
- else this.shortcutAction(data);
35
- }
36
-
37
- private selectedAction(): void {
38
- if (this.selected === 0) return this.ctx.save(true);
39
- if (this.selected === 1) return this.ctx.exitWithoutSaving();
40
- return this.returnToConfigUi();
41
- }
42
-
43
- private shortcutAction(data: string): void {
44
- if (data === "s") return this.ctx.save(true);
45
- if (data === "x") return this.ctx.exitWithoutSaving();
46
- if (data === "r") return this.returnToConfigUi();
47
- }
48
-
49
- private returnToConfigUi(): void {
50
- this.ctx.show(this.ctx.state.viewBeforeConfirmExit);
51
- }
52
- }
@@ -1,12 +0,0 @@
1
- import type { ScreenContext } from "../screen-context.js";
2
- import type { ScreenRender } from "../screen-render.js";
3
-
4
- export abstract class Controller {
5
- constructor(
6
- protected ctx: ScreenContext,
7
- protected readonly render: ScreenRender,
8
- ) {}
9
-
10
- abstract renderScreen(width: number): string[];
11
- abstract handleInput(data: string): void;
12
- }
@@ -1,68 +0,0 @@
1
- import { Key, matchesKey } from "@earendil-works/pi-tui";
2
-
3
- import type { Widget } from "../../widgets/types.js";
4
- import {
5
- applyColorDigit,
6
- applyColorOptionField,
7
- deleteColorDigit,
8
- EDIT_COLORS_HINT,
9
- } from "../color-options.js";
10
- import { editColorsFieldRows, editColorsTitle } from "../edit-colors.js";
11
- import { colorFields } from "../fields.js";
12
- import { isPrintable, wrap } from "../helpers.js";
13
- import type { ColorOptionField } from "../model.js";
14
- import { Controller } from "./controller.js";
15
-
16
- export class EditColorsScreen extends Controller {
17
- private selected = 0;
18
-
19
- renderScreen(width: number): string[] {
20
- const widget = this.ctx.currentWidget();
21
- if (!widget) return [this.render.line(this.ctx.theme.warning("No widget selected"), width)];
22
- const fields = editColorsFieldRows(widget);
23
- this.clampSelection(fields.length);
24
- return [
25
- this.render.line(
26
- this.render.menuTitle(editColorsTitle(widget), "Select a field to edit its colors"),
27
- width,
28
- ),
29
- this.render.line(this.ctx.theme.dim(EDIT_COLORS_HINT), width),
30
- ...fields.map((field, index) => this.render.menuLine(index === this.selected, field, width)),
31
- ];
32
- }
33
-
34
- handleInput(data: string): void {
35
- const widget = this.ctx.currentWidget();
36
- if (!widget) return;
37
- const fields = colorFields(widget);
38
- this.clampSelection(fields.length);
39
- const field = fields[this.selected];
40
- if (!field) return;
41
- if (matchesKey(data, Key.up)) this.selected = wrap(this.selected - 1, fields.length);
42
- else if (matchesKey(data, Key.down)) this.selected = wrap(this.selected + 1, fields.length);
43
- else if (matchesKey(data, Key.left)) this.adjustColorField(widget, field, -1);
44
- else if (matchesKey(data, Key.right) || matchesKey(data, Key.enter))
45
- this.adjustColorField(widget, field, 1);
46
- else if (matchesKey(data, Key.backspace)) this.deleteColorDigit(widget, field);
47
- else if (isPrintable(data)) this.applyColorDigit(widget, field, data);
48
- }
49
-
50
- private clampSelection(fieldCount: number): void {
51
- this.selected = Math.min(Math.max(this.selected, 0), Math.max(0, fieldCount - 1));
52
- }
53
-
54
- private applyColorDigit(widget: Widget, field: ColorOptionField, digit: string): void {
55
- if (!applyColorDigit(widget, field, digit)) return;
56
- this.ctx.emitChange();
57
- }
58
-
59
- private deleteColorDigit(widget: Widget, field: ColorOptionField): void {
60
- if (!deleteColorDigit(widget, field)) return;
61
- this.ctx.emitChange();
62
- }
63
-
64
- private adjustColorField(widget: Widget, field: ColorOptionField, delta: number): void {
65
- applyColorOptionField(widget, field, delta);
66
- this.ctx.emitChange();
67
- }
68
- }
@@ -1,115 +0,0 @@
1
- import { Key, matchesKey } from "@earendil-works/pi-tui";
2
-
3
- import { registry } from "../../widgets/registry.js";
4
- import type { Widget } from "../../widgets/types.js";
5
- import { eventWidgetUsageLines } from "../events.js";
6
- import { cycleExternalStatusKey, statusKeyPickerLines } from "../extension-status-picker.js";
7
- import { fieldsForWidget, fieldValue } from "../fields.js";
8
- import { isPrintable, wrap } from "../helpers.js";
9
- import type { OptionField } from "../model.js";
10
- import { applyOptionField, applyOptionFieldEdit } from "../option-edit.js";
11
- import { Controller } from "./controller.js";
12
-
13
- const HINT = "↑/↓ field • ←/→ change • type text • backspace delete • esc back";
14
-
15
- export class EditWidgetScreen extends Controller {
16
- private selected = 0;
17
-
18
- renderScreen(width: number): string[] {
19
- const widget = this.ctx.currentWidget();
20
- if (!widget) return [this.render.line(this.ctx.theme.warning("No widget selected"), width)];
21
- const fields = fieldsForWidget(widget);
22
- this.clampSelection(fields.length);
23
- const selectedField = fields[this.selected];
24
- const lines = [
25
- this.render.line(
26
- this.render.menuTitle(this.widgetTitle(widget), "Select a field to edit its options"),
27
- width,
28
- ),
29
- this.render.line(this.ctx.theme.dim(HINT), width),
30
- ...fields.map((field, index) =>
31
- this.render.menuLine(
32
- index === this.selected,
33
- `${field.label}: ${fieldValue(widget, field)}`,
34
- width,
35
- ),
36
- ),
37
- ];
38
-
39
- if (selectedField?.editAction === "external-status-key") {
40
- lines.push(
41
- ...statusKeyPickerLines(
42
- this.ctx.getExtensionStatuses,
43
- this.ctx.state.store.settings.extensionStatusRow,
44
- width,
45
- (content, lineWidth) => this.render.line(content, lineWidth),
46
- (value) => this.ctx.theme.dim(value),
47
- ),
48
- );
49
- }
50
-
51
- if (widget.type === "event") {
52
- lines.push(
53
- ...eventWidgetUsageLines(
54
- widget,
55
- width,
56
- (content, lineWidth) => this.render.line(content, lineWidth),
57
- (value) => this.ctx.theme.dim(value),
58
- ),
59
- );
60
- }
61
-
62
- return lines;
63
- }
64
-
65
- handleInput(data: string): void {
66
- const widget = this.ctx.currentWidget();
67
- if (!widget) return;
68
- const fields = fieldsForWidget(widget);
69
- this.clampSelection(fields.length);
70
- const field = fields[this.selected];
71
- if (!field) return;
72
- if (matchesKey(data, Key.up)) this.selected = wrap(this.selected - 1, fields.length);
73
- else if (matchesKey(data, Key.down)) this.selected = wrap(this.selected + 1, fields.length);
74
- else if (matchesKey(data, Key.left)) this.adjustField(widget, field, -1);
75
- else if (matchesKey(data, Key.right) || matchesKey(data, Key.enter))
76
- this.adjustField(widget, field, 1);
77
- else if (matchesKey(data, Key.backspace) && field.kind === "text")
78
- this.applyTextFieldEdit(widget, field, { kind: "deleteText" });
79
- else if (isPrintable(data) && field.kind === "text")
80
- this.applyTextFieldEdit(widget, field, { kind: "appendText", text: data });
81
- }
82
-
83
- private widgetTitle(widget: Widget): string {
84
- return `Edit ${registry.spec(widget.type).label}`;
85
- }
86
-
87
- private clampSelection(fieldCount: number): void {
88
- this.selected = Math.min(Math.max(this.selected, 0), Math.max(0, fieldCount - 1));
89
- }
90
-
91
- private applyTextFieldEdit(
92
- widget: Widget,
93
- field: OptionField,
94
- edit: { kind: "appendText"; text: string } | { kind: "deleteText" },
95
- ): void {
96
- if (applyOptionFieldEdit(widget, field, edit) !== "changed") return;
97
- this.ctx.emitChange();
98
- }
99
-
100
- private adjustField(widget: Widget, field: OptionField, delta: number): void {
101
- const result = applyOptionField(widget, field, delta);
102
- if (result === "external-status-key") {
103
- if (
104
- !cycleExternalStatusKey(
105
- widget,
106
- this.ctx.getExtensionStatuses,
107
- this.ctx.state.store.settings.extensionStatusRow,
108
- delta,
109
- )
110
- )
111
- return;
112
- } else if (result === "unchanged") return;
113
- this.ctx.emitChange();
114
- }
115
- }
@@ -1,61 +0,0 @@
1
- import { Key, matchesKey } from "@earendil-works/pi-tui";
2
-
3
- import {
4
- extensionStatusRowCount,
5
- extensionStatusRowLines,
6
- toggleExtensionStatusRowSelection,
7
- } from "../extension-statuses.js";
8
- import { wrap } from "../helpers.js";
9
- import { pageSelection } from "../navigation.js";
10
- import { Controller } from "./controller.js";
11
-
12
- export class ExtensionStatusRowScreen extends Controller {
13
- private selected = 0;
14
-
15
- renderScreen(width: number): string[] {
16
- return extensionStatusRowLines(
17
- this.ctx.state.store.settings,
18
- this.ctx.getExtensionStatuses,
19
- this.selected,
20
- width,
21
- (title, subtitle) => this.render.menuTitle(title, subtitle),
22
- (content, lineWidth) => this.render.line(content, lineWidth),
23
- (selected, content, lineWidth) => this.render.menuLine(selected, content, lineWidth),
24
- (value) => this.ctx.theme.dim(value),
25
- (value) => this.ctx.theme.success(value),
26
- (value) => this.ctx.theme.warning(value),
27
- );
28
- }
29
-
30
- handleInput(data: string): void {
31
- const count = extensionStatusRowCount(
32
- this.ctx.state.store.settings,
33
- this.ctx.getExtensionStatuses,
34
- );
35
- if (matchesKey(data, Key.up)) this.selected = wrap(this.selected - 1, Math.max(1, count));
36
- else if (matchesKey(data, Key.down))
37
- this.selected = wrap(this.selected + 1, Math.max(1, count));
38
- else if (matchesKey(data, Key.pageUp)) this.page(count, -1);
39
- else if (matchesKey(data, Key.pageDown)) this.page(count, 1);
40
- else if (
41
- matchesKey(data, Key.space) ||
42
- matchesKey(data, Key.left) ||
43
- matchesKey(data, Key.right) ||
44
- matchesKey(data, Key.enter)
45
- ) {
46
- if (
47
- toggleExtensionStatusRowSelection(
48
- this.ctx.state.store.settings,
49
- this.ctx.getExtensionStatuses,
50
- this.selected,
51
- )
52
- ) {
53
- this.ctx.emitChange();
54
- }
55
- }
56
- }
57
-
58
- private page(count: number, delta: -1 | 1): void {
59
- this.selected = pageSelection(this.selected, count, this.ctx.visibleRowCount(), delta);
60
- }
61
- }