@selesai/code 0.9.15 → 0.10.1

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 (269) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +6 -9
  3. package/dist/defaults/models.json +2 -2
  4. package/dist/defaults/settings.json +3 -7
  5. package/dist/extensions/package.json +2 -4
  6. package/dist/extensions/pi-zentui/.editorconfig +12 -0
  7. package/dist/extensions/pi-zentui/.github/CODEOWNERS +1 -0
  8. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/bug_report.yml +110 -0
  9. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config.yml +8 -0
  10. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/config_question.yml +56 -0
  11. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/feature_request.yml +63 -0
  12. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/rendering_issue.yml +106 -0
  13. package/dist/extensions/pi-zentui/.github/ISSUE_TEMPLATE/runtime_detection.yml +103 -0
  14. package/dist/extensions/pi-zentui/.github/dependabot.yml +47 -0
  15. package/dist/extensions/pi-zentui/.github/pull_request_template.md +34 -0
  16. package/dist/extensions/pi-zentui/.github/release.yml +18 -0
  17. package/dist/extensions/pi-zentui/.github/workflows/ci.yml +79 -0
  18. package/dist/extensions/pi-zentui/.github/workflows/publish.yml +63 -0
  19. package/dist/extensions/pi-zentui/AGENTS.md +107 -0
  20. package/dist/extensions/pi-zentui/CONTRIBUTING.md +68 -0
  21. package/dist/extensions/pi-zentui/LICENSE +21 -0
  22. package/dist/extensions/pi-zentui/README.md +188 -0
  23. package/dist/extensions/pi-zentui/SECURITY.md +18 -0
  24. package/dist/extensions/pi-zentui/assets/main-cover.png +0 -0
  25. package/dist/extensions/pi-zentui/assets/screenshots/editor-accent-rail.png +0 -0
  26. package/dist/extensions/pi-zentui/assets/screenshots/editor-minimalist.png +0 -0
  27. package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode-copy-friendly.png +0 -0
  28. package/dist/extensions/pi-zentui/assets/screenshots/editor-opencode.png +0 -0
  29. package/dist/extensions/pi-zentui/assets/screenshots/user-message-compact.png +0 -0
  30. package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed-copy-friendly.png +0 -0
  31. package/dist/extensions/pi-zentui/assets/screenshots/user-message-framed.png +0 -0
  32. package/dist/extensions/pi-zentui/assets/screenshots/user-message-labeled.png +0 -0
  33. package/dist/extensions/pi-zentui/assets/zentui.png +0 -0
  34. package/dist/extensions/pi-zentui/biome.json +31 -0
  35. package/dist/extensions/pi-zentui/docs/configuration.md +509 -0
  36. package/dist/extensions/pi-zentui/docs/footer-format.md +162 -0
  37. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-editor.ts +99 -0
  38. package/dist/extensions/pi-zentui/extensions/zentui/accent-rail-layout-patch.ts +457 -0
  39. package/dist/extensions/pi-zentui/extensions/zentui/completion-menu.ts +157 -0
  40. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +2100 -0
  41. package/dist/extensions/pi-zentui/extensions/zentui/editor-metadata-format.ts +321 -0
  42. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +73 -0
  43. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +89 -0
  44. package/dist/extensions/pi-zentui/extensions/zentui/footer-format.ts +364 -0
  45. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +122 -0
  46. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +812 -0
  47. package/dist/extensions/pi-zentui/extensions/zentui/format.ts +568 -0
  48. package/dist/extensions/pi-zentui/extensions/zentui/git.ts +384 -0
  49. package/dist/extensions/pi-zentui/extensions/zentui/icons.ts +252 -0
  50. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +1406 -0
  51. package/dist/extensions/pi-zentui/extensions/zentui/interaction-summary.ts +1049 -0
  52. package/dist/extensions/pi-zentui/extensions/zentui/live-context.ts +75 -0
  53. package/dist/extensions/pi-zentui/extensions/zentui/minimalist-editor.ts +463 -0
  54. package/dist/extensions/pi-zentui/extensions/zentui/package-version.ts +651 -0
  55. package/dist/extensions/pi-zentui/extensions/zentui/project-refresh.ts +120 -0
  56. package/dist/extensions/pi-zentui/extensions/zentui/project-state.ts +59 -0
  57. package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +166 -0
  58. package/dist/extensions/pi-zentui/extensions/zentui/repository-root.ts +98 -0
  59. package/dist/extensions/pi-zentui/extensions/zentui/runtime.ts +841 -0
  60. package/dist/extensions/pi-zentui/extensions/zentui/selector-border.ts +93 -0
  61. package/dist/extensions/pi-zentui/extensions/zentui/session-lifecycle.ts +60 -0
  62. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +1796 -0
  63. package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +128 -0
  64. package/dist/extensions/pi-zentui/extensions/zentui/state.ts +94 -0
  65. package/dist/extensions/pi-zentui/extensions/zentui/style.ts +424 -0
  66. package/dist/extensions/pi-zentui/extensions/zentui/telemetry.ts +82 -0
  67. package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +1290 -0
  68. package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +248 -0
  69. package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +215 -0
  70. package/dist/extensions/pi-zentui/extensions/zentui/user-message.ts +206 -0
  71. package/dist/extensions/pi-zentui/extensions/zentui/working-line-messages.ts +24 -0
  72. package/dist/extensions/pi-zentui/extensions/zentui/working-line-spinners.ts +54 -0
  73. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +1433 -0
  74. package/dist/extensions/pi-zentui/package-lock.json +5311 -0
  75. package/dist/extensions/pi-zentui/package.json +60 -0
  76. package/dist/extensions/pi-zentui/test/accent-rail-editor.test.ts +232 -0
  77. package/dist/extensions/pi-zentui/test/accent-rail-layout-patch.test.ts +669 -0
  78. package/dist/extensions/pi-zentui/test/completion-menu.test.ts +265 -0
  79. package/dist/extensions/pi-zentui/test/config.test.ts +3119 -0
  80. package/dist/extensions/pi-zentui/test/editor-metadata-format.test.ts +405 -0
  81. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +260 -0
  82. package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +1286 -0
  83. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +7196 -0
  84. package/dist/extensions/pi-zentui/test/extension-status.test.ts +182 -0
  85. package/dist/extensions/pi-zentui/test/fixtures/starship-nerd-font-symbols.toml +172 -0
  86. package/dist/extensions/pi-zentui/test/footer-format.test.ts +431 -0
  87. package/dist/extensions/pi-zentui/test/footer-layout.test.ts +113 -0
  88. package/dist/extensions/pi-zentui/test/footer-repository-path.test.ts +123 -0
  89. package/dist/extensions/pi-zentui/test/format.test.ts +865 -0
  90. package/dist/extensions/pi-zentui/test/git.test.ts +212 -0
  91. package/dist/extensions/pi-zentui/test/icons.test.ts +69 -0
  92. package/dist/extensions/pi-zentui/test/interaction-summary.test.ts +1402 -0
  93. package/dist/extensions/pi-zentui/test/live-context-integration.test.ts +378 -0
  94. package/dist/extensions/pi-zentui/test/live-context.test.ts +139 -0
  95. package/dist/extensions/pi-zentui/test/minimalist-editor.test.ts +811 -0
  96. package/dist/extensions/pi-zentui/test/package-version.test.ts +321 -0
  97. package/dist/extensions/pi-zentui/test/project-refresh.test.ts +250 -0
  98. package/dist/extensions/pi-zentui/test/project-state.test.ts +159 -0
  99. package/dist/extensions/pi-zentui/test/repository-root.test.ts +108 -0
  100. package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +825 -0
  101. package/dist/extensions/pi-zentui/test/runtime.test.ts +183 -0
  102. package/dist/extensions/pi-zentui/test/session-lifecycle.test.ts +40 -0
  103. package/dist/extensions/pi-zentui/test/settings-command.test.ts +1381 -0
  104. package/dist/extensions/pi-zentui/test/settings-previews.test.ts +347 -0
  105. package/dist/extensions/pi-zentui/test/state.test.ts +84 -0
  106. package/dist/extensions/pi-zentui/test/telemetry-lifecycle-integration.test.ts +276 -0
  107. package/dist/extensions/pi-zentui/test/telemetry.test.ts +105 -0
  108. package/dist/extensions/pi-zentui/test/turn-summary-lifecycle.integration.test.ts +327 -0
  109. package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +164 -0
  110. package/dist/extensions/pi-zentui/test/user-message-styles.test.ts +292 -0
  111. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +730 -0
  112. package/dist/extensions/pi-zentui/test/working-line-messages.test.ts +34 -0
  113. package/dist/extensions/pi-zentui/test/working-line.test.ts +1990 -0
  114. package/dist/extensions/pi-zentui/tsconfig.json +16 -0
  115. package/dist/extensions/tps.test.ts +38 -0
  116. package/dist/extensions/{pi-powerline-footer/tps.ts → tps.ts} +4 -0
  117. package/dist/modes/interactive/components/settings-selector.d.ts +1 -1
  118. package/dist/modes/interactive/interactive-mode.js +1 -1
  119. package/dist/skills/design-references/SKILL.md +31 -0
  120. package/dist/skills/design-references/references/clickhouse/DESIGN.md +544 -0
  121. package/dist/themes/adventure.json +84 -0
  122. package/dist/themes/adwaita-dark.json +84 -0
  123. package/dist/themes/arcoiris.json +84 -0
  124. package/dist/themes/arthur.json +84 -0
  125. package/dist/themes/atom.json +84 -0
  126. package/dist/themes/aura.json +84 -0
  127. package/dist/themes/black-metal-bathory.json +84 -0
  128. package/dist/themes/black-metal-burzum.json +84 -0
  129. package/dist/themes/black-metal-khold.json +84 -0
  130. package/dist/themes/box.json +84 -0
  131. package/dist/themes/brogrammer.json +84 -0
  132. package/dist/themes/carbonfox.json +84 -0
  133. package/dist/themes/catppuccin-mocha.json +84 -0
  134. package/dist/themes/citruszest.json +84 -0
  135. package/dist/themes/cursor-dark.json +84 -0
  136. package/dist/themes/cutie-pro.json +84 -0
  137. package/dist/themes/dark-modern.json +84 -0
  138. package/dist/themes/dark-pastel.json +84 -0
  139. package/dist/themes/dimmed-monokai.json +84 -0
  140. package/dist/themes/doom-peacock.json +84 -0
  141. package/dist/themes/dracula-plus.json +84 -0
  142. package/dist/themes/dracula.json +80 -0
  143. package/dist/themes/earthsong.json +84 -0
  144. package/dist/themes/everforest-dark-hard.json +84 -0
  145. package/dist/themes/everforest-dark.json +80 -0
  146. package/dist/themes/fahrenheit.json +84 -0
  147. package/dist/themes/flatland.json +84 -0
  148. package/dist/themes/flexoki-dark.json +84 -0
  149. package/dist/themes/front-end-delight.json +84 -0
  150. package/dist/themes/fun-forrest.json +84 -0
  151. package/dist/themes/galizur.json +84 -0
  152. package/dist/themes/github-dark-colorblind.json +84 -0
  153. package/dist/themes/github-dark-high-contrast.json +84 -0
  154. package/dist/themes/glacier.json +84 -0
  155. package/dist/themes/gruber-darker.json +84 -0
  156. package/dist/themes/gruvbox-dark-hard.json +84 -0
  157. package/dist/themes/gruvbox-dark.json +84 -0
  158. package/dist/themes/gruvbox-material.json +84 -0
  159. package/dist/themes/guezwhoz.json +84 -0
  160. package/dist/themes/hacktober.json +84 -0
  161. package/dist/themes/hardcore.json +84 -0
  162. package/dist/themes/havn-skumring.json +84 -0
  163. package/dist/themes/ic-orange-ppl.json +84 -0
  164. package/dist/themes/iterm2-smoooooth.json +84 -0
  165. package/dist/themes/iterm2-tango-dark.json +84 -0
  166. package/dist/themes/japanesque.json +84 -0
  167. package/dist/themes/jellybeans.json +84 -0
  168. package/dist/themes/kanagawa-wave.json +84 -0
  169. package/dist/themes/kurokula.json +84 -0
  170. package/dist/themes/later-this-evening.json +84 -0
  171. package/dist/themes/lovelace.json +84 -0
  172. package/dist/themes/material-darker.json +84 -0
  173. package/dist/themes/matte-black.json +84 -0
  174. package/dist/themes/mellow.json +84 -0
  175. package/dist/themes/miasma.json +84 -0
  176. package/dist/themes/nord.json +80 -0
  177. package/dist/themes/nvim-dark.json +84 -0
  178. package/dist/themes/one-dark.json +80 -0
  179. package/dist/themes/popping-and-locking.json +84 -0
  180. package/dist/themes/rose-pine.json +80 -0
  181. package/dist/themes/sea-shells.json +84 -0
  182. package/dist/themes/sleepy-hollow.json +84 -0
  183. package/dist/themes/smyck.json +84 -0
  184. package/dist/themes/solarized-dark.json +80 -0
  185. package/dist/themes/tokyo-night.json +80 -0
  186. package/dist/themes/tomorrow-night-bright.json +84 -0
  187. package/dist/themes/tomorrow-night-burns.json +84 -0
  188. package/dist/themes/tomorrow-night.json +84 -0
  189. package/dist/themes/twilight.json +84 -0
  190. package/dist/themes/vague.json +84 -0
  191. package/dist/themes/vesper.json +84 -0
  192. package/dist/themes/xcode-dark-hc.json +84 -0
  193. package/docs/custom-provider.md +0 -1
  194. package/package.json +3 -3
  195. package/dist/extensions/auto-model/auto-model.test.ts +0 -18
  196. package/dist/extensions/auto-model/classifier.ts +0 -23
  197. package/dist/extensions/auto-model/config.ts +0 -51
  198. package/dist/extensions/auto-model/index.ts +0 -22
  199. package/dist/extensions/auto-model/lifecycle.test.ts +0 -24
  200. package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +0 -27
  201. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +0 -687
  202. package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +0 -16
  203. package/dist/extensions/pi-powerline-footer/README.md +0 -513
  204. package/dist/extensions/pi-powerline-footer/banner.png +0 -0
  205. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +0 -586
  206. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +0 -583
  207. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +0 -204
  208. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +0 -286
  209. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +0 -108
  210. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +0 -60
  211. package/dist/extensions/pi-powerline-footer/cd-command.ts +0 -190
  212. package/dist/extensions/pi-powerline-footer/colors.ts +0 -70
  213. package/dist/extensions/pi-powerline-footer/context-usage.ts +0 -156
  214. package/dist/extensions/pi-powerline-footer/currency-rates.ts +0 -142
  215. package/dist/extensions/pi-powerline-footer/editor-composition.ts +0 -32
  216. package/dist/extensions/pi-powerline-footer/editor-performance.ts +0 -99
  217. package/dist/extensions/pi-powerline-footer/git-status.ts +0 -329
  218. package/dist/extensions/pi-powerline-footer/guide.ts +0 -220
  219. package/dist/extensions/pi-powerline-footer/icons.ts +0 -190
  220. package/dist/extensions/pi-powerline-footer/index.ts +0 -3428
  221. package/dist/extensions/pi-powerline-footer/lifecycle.ts +0 -10
  222. package/dist/extensions/pi-powerline-footer/package.json +0 -49
  223. package/dist/extensions/pi-powerline-footer/paths.ts +0 -22
  224. package/dist/extensions/pi-powerline-footer/powerline-config.ts +0 -446
  225. package/dist/extensions/pi-powerline-footer/presets.ts +0 -102
  226. package/dist/extensions/pi-powerline-footer/queue/store.ts +0 -313
  227. package/dist/extensions/pi-powerline-footer/queue/types.ts +0 -53
  228. package/dist/extensions/pi-powerline-footer/render-scheduler.ts +0 -29
  229. package/dist/extensions/pi-powerline-footer/segments.ts +0 -570
  230. package/dist/extensions/pi-powerline-footer/separators.ts +0 -57
  231. package/dist/extensions/pi-powerline-footer/session-usage.ts +0 -44
  232. package/dist/extensions/pi-powerline-footer/shortcuts.ts +0 -61
  233. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +0 -1905
  234. package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +0 -144
  235. package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +0 -156
  236. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +0 -344
  237. package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +0 -48
  238. package/dist/extensions/pi-powerline-footer/tests/editor-performance.test.ts +0 -43
  239. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -136
  240. package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +0 -39
  241. package/dist/extensions/pi-powerline-footer/tests/git-optional-locks.test.ts +0 -64
  242. package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +0 -92
  243. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +0 -129
  244. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +0 -116
  245. package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +0 -79
  246. package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +0 -63
  247. package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +0 -65
  248. package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +0 -14
  249. package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +0 -173
  250. package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +0 -47
  251. package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +0 -37
  252. package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +0 -76
  253. package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +0 -249
  254. package/dist/extensions/pi-powerline-footer/tests/tps.test.ts +0 -39
  255. package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +0 -237
  256. package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +0 -199
  257. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +0 -317
  258. package/dist/extensions/pi-powerline-footer/theme.example.json +0 -24
  259. package/dist/extensions/pi-powerline-footer/theme.json +0 -12
  260. package/dist/extensions/pi-powerline-footer/theme.ts +0 -241
  261. package/dist/extensions/pi-powerline-footer/token-stats.ts +0 -304
  262. package/dist/extensions/pi-powerline-footer/tsconfig.json +0 -13
  263. package/dist/extensions/pi-powerline-footer/types.ts +0 -235
  264. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
  265. package/dist/extensions/pi-powerline-footer/welcome.ts +0 -709
  266. package/dist/extensions/pi-powerline-footer/working-vibes.ts +0 -721
  267. package/dist/extensions/preview-tools-disabled.test.ts +0 -37
  268. package/dist/extensions/preview-tools-disabled.ts +0 -11
  269. package/dist/themes/powerline-footer/theme.json +0 -33
@@ -0,0 +1,457 @@
1
+ import { readFileSync, realpathSync, statSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join, parse } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+
6
+ export const ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR = Symbol.for("pi-zentui.accent-rail-layout-editor");
7
+ const ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY = Symbol.for("pi-zentui.accent-rail-layout-registry");
8
+ const LAYOUT_NODE = Symbol.for("@earendil-works/pi-tui/layout-node");
9
+
10
+ const SUPPORTED_PI_TUI_VERSION = /^0\.84\.\d+$/;
11
+
12
+ type AccentRailEditorMarker = {
13
+ owner: symbol;
14
+ active: () => boolean;
15
+ };
16
+
17
+ type MarkedEditor = {
18
+ [ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR]?: AccentRailEditorMarker;
19
+ };
20
+
21
+ type StackEntry = {
22
+ component?: unknown;
23
+ minSize?: number;
24
+ shrink?: number;
25
+ [key: PropertyKey]: unknown;
26
+ };
27
+
28
+ type LayoutNode = {
29
+ type?: unknown;
30
+ entries?: unknown;
31
+ [key: PropertyKey]: unknown;
32
+ };
33
+
34
+ type LayoutMethod = (this: unknown, ...args: unknown[]) => unknown;
35
+
36
+ type LayoutPatchRegistry = {
37
+ method: symbol;
38
+ predecessor: LayoutMethod;
39
+ predecessorDescriptor?: PropertyDescriptor;
40
+ wrapper: LayoutMethod;
41
+ owners: Map<symbol, number>;
42
+ active: boolean;
43
+ };
44
+
45
+ type PatchPrototype = Record<PropertyKey, unknown>;
46
+
47
+ type LayoutComponent = {
48
+ render: (...args: unknown[]) => unknown;
49
+ invalidate?: (...args: unknown[]) => unknown;
50
+ };
51
+
52
+ const forwardingComponents = new WeakMap<object, LayoutComponent>();
53
+
54
+ function forwardingLayoutComponent(component: object): LayoutComponent | undefined {
55
+ const cached = forwardingComponents.get(component);
56
+ if (cached) return cached;
57
+ const render = (component as Partial<LayoutComponent>).render;
58
+ if (typeof render !== "function") return undefined;
59
+ try {
60
+ const wrapper: LayoutComponent = {
61
+ render: (...args: unknown[]) => {
62
+ const rows = Reflect.apply(render, component, args);
63
+ return Array.isArray(rows) && rows.length === 1 ? ["", rows[0]] : rows;
64
+ },
65
+ invalidate: (...args: unknown[]) => {
66
+ const invalidate = (component as Partial<LayoutComponent>).invalidate;
67
+ return typeof invalidate === "function"
68
+ ? Reflect.apply(invalidate, component, args)
69
+ : undefined;
70
+ },
71
+ };
72
+ forwardingComponents.set(component, wrapper);
73
+ return wrapper;
74
+ } catch {
75
+ return undefined;
76
+ }
77
+ }
78
+
79
+ export type AccentRailLayoutPatchTarget = {
80
+ prototype: object;
81
+ version: string;
82
+ canonicalEntrypoint?: string;
83
+ resolvedModulePath?: string;
84
+ localModulePath?: string;
85
+ };
86
+
87
+ export type AccentRailLayoutPatchDiagnostic =
88
+ | "installed"
89
+ | "reused"
90
+ | "displaced"
91
+ | "unsupported-version"
92
+ | "unsupported-shape"
93
+ | "host-module-unavailable";
94
+
95
+ export type AccentRailLayoutPatchInstallation = {
96
+ cleanup: () => void;
97
+ diagnostic: AccentRailLayoutPatchDiagnostic;
98
+ version?: string;
99
+ };
100
+
101
+ export type AccentRailLayoutPatchRetention = "retained" | "stale" | "failed";
102
+
103
+ export async function retainAccentRailLayoutPatchInstallation(
104
+ install: () => Promise<AccentRailLayoutPatchInstallation>,
105
+ isCurrent: () => boolean,
106
+ retain: (installation: AccentRailLayoutPatchInstallation) => void,
107
+ ): Promise<AccentRailLayoutPatchRetention> {
108
+ let installation: AccentRailLayoutPatchInstallation;
109
+ try {
110
+ installation = await install();
111
+ } catch {
112
+ return "failed";
113
+ }
114
+ if (!isCurrent()) {
115
+ installation.cleanup();
116
+ return "stale";
117
+ }
118
+ try {
119
+ retain(installation);
120
+ } catch {
121
+ installation.cleanup();
122
+ return "failed";
123
+ }
124
+ return "retained";
125
+ }
126
+
127
+ function ownMarker(value: unknown): AccentRailEditorMarker | undefined {
128
+ if (!value || typeof value !== "object") return undefined;
129
+ if (!Object.hasOwn(value, ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR)) {
130
+ return undefined;
131
+ }
132
+ const marker = (value as MarkedEditor)[ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR];
133
+ return marker && typeof marker.owner === "symbol" && typeof marker.active === "function"
134
+ ? marker
135
+ : undefined;
136
+ }
137
+
138
+ function entryMarker(entry: StackEntry): AccentRailEditorMarker | undefined {
139
+ const component = entry.component;
140
+ if (!component || typeof component !== "object") return undefined;
141
+ if (!Object.hasOwn(component, "children")) return undefined;
142
+ const children = (component as { children?: unknown }).children;
143
+ if (!Array.isArray(children) || children.length !== 1) return undefined;
144
+ return ownMarker(children[0]);
145
+ }
146
+
147
+ export function adjustAccentRailLayoutNode(
148
+ node: unknown,
149
+ activeOwners: ReadonlySet<symbol>,
150
+ ): unknown {
151
+ if (!node || typeof node !== "object") return node;
152
+ const candidate = node as LayoutNode;
153
+ if (candidate.type !== "vstack" || !Array.isArray(candidate.entries)) return node;
154
+
155
+ const entries = candidate.entries as StackEntry[];
156
+ const matches: number[] = [];
157
+ for (let index = 0; index < entries.length; index += 1) {
158
+ const entry = entries[index];
159
+ if (entry?.minSize !== 3 || entry.shrink !== 1) continue;
160
+ const marker = entryMarker(entry);
161
+ if (!marker || !activeOwners.has(marker.owner)) continue;
162
+ let active = false;
163
+ try {
164
+ active = marker.active() === true;
165
+ } catch {
166
+ return node;
167
+ }
168
+ if (active) matches.push(index);
169
+ }
170
+ if (matches.length !== 1) return node;
171
+
172
+ const match = matches[0];
173
+ const matchedEntry = entries[match];
174
+ if (!matchedEntry?.component || typeof matchedEntry.component !== "object") return node;
175
+ const component = forwardingLayoutComponent(matchedEntry.component);
176
+ if (!component) return node;
177
+ const nextEntries = entries.map((entry, index) =>
178
+ index === match ? { ...entry, component } : entry,
179
+ );
180
+ return { ...candidate, entries: nextEntries };
181
+ }
182
+
183
+ export function markAccentRailLayoutEditor(
184
+ editor: object,
185
+ owner: symbol,
186
+ active: () => boolean,
187
+ ): boolean {
188
+ try {
189
+ Object.defineProperty(editor, ZENTUI_ACCENT_RAIL_LAYOUT_EDITOR, {
190
+ value: { owner, active } satisfies AccentRailEditorMarker,
191
+ configurable: true,
192
+ enumerable: false,
193
+ writable: false,
194
+ });
195
+ return true;
196
+ } catch {
197
+ return false;
198
+ }
199
+ }
200
+
201
+ function registryOn(prototype: PatchPrototype): LayoutPatchRegistry | undefined {
202
+ const value = prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
203
+ if (!value || typeof value !== "object") return undefined;
204
+ const registry = value as Partial<LayoutPatchRegistry>;
205
+ return registry.owners instanceof Map && typeof registry.wrapper === "function"
206
+ ? (value as LayoutPatchRegistry)
207
+ : undefined;
208
+ }
209
+
210
+ function discoverLayoutMethod(prototype: PatchPrototype): symbol | undefined {
211
+ const symbols = new Set<PropertyKey>([LAYOUT_NODE, ...Object.getOwnPropertySymbols(prototype)]);
212
+ for (const key of symbols) {
213
+ const method = prototype[key];
214
+ if (typeof method !== "function") continue;
215
+ try {
216
+ const receiver = Object.create(prototype) as Record<PropertyKey, unknown>;
217
+ receiver.entries = [];
218
+ receiver.layoutType = "vstack";
219
+ receiver.gap = 0;
220
+ receiver.align = "stretch";
221
+ const node = Reflect.apply(method as LayoutMethod, receiver, []);
222
+ if (
223
+ node &&
224
+ typeof node === "object" &&
225
+ (node as LayoutNode).type === "vstack" &&
226
+ Array.isArray((node as LayoutNode).entries)
227
+ ) {
228
+ return key as symbol;
229
+ }
230
+ } catch {
231
+ // Probe candidates fail open.
232
+ }
233
+ }
234
+ return undefined;
235
+ }
236
+
237
+ function restorePredecessor(prototype: PatchPrototype, registry: LayoutPatchRegistry): void {
238
+ if (prototype[registry.method] !== registry.wrapper) return;
239
+ if (registry.predecessorDescriptor) {
240
+ Object.defineProperty(prototype, registry.method, registry.predecessorDescriptor);
241
+ } else {
242
+ delete prototype[registry.method];
243
+ }
244
+ }
245
+
246
+ function cleanupOwner(
247
+ prototype: PatchPrototype,
248
+ registry: LayoutPatchRegistry,
249
+ owner: symbol,
250
+ ): () => void {
251
+ let cleaned = false;
252
+ return () => {
253
+ if (cleaned) return;
254
+ cleaned = true;
255
+ const count = registry.owners.get(owner) ?? 0;
256
+ if (count <= 1) registry.owners.delete(owner);
257
+ else registry.owners.set(owner, count - 1);
258
+ if (registry.owners.size > 0) return;
259
+
260
+ // Deactivate before restoration. If a successor patch captured and later
261
+ // restores our wrapper, the ownerless wrapper remains an inert pass-through.
262
+ registry.active = false;
263
+ if (registryOn(prototype) !== registry) return;
264
+ restorePredecessor(prototype, registry);
265
+ if (prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY] === registry) {
266
+ delete prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
267
+ }
268
+ };
269
+ }
270
+
271
+ export function installAccentRailLayoutPatchOnTarget(
272
+ target: AccentRailLayoutPatchTarget,
273
+ owner: symbol,
274
+ ): AccentRailLayoutPatchInstallation {
275
+ if (!SUPPORTED_PI_TUI_VERSION.test(target.version)) {
276
+ return { cleanup: () => {}, diagnostic: "unsupported-version", version: target.version };
277
+ }
278
+ const prototype = target.prototype as PatchPrototype;
279
+ const existing = registryOn(prototype);
280
+ if (existing) {
281
+ if (existing.active && prototype[existing.method] === existing.wrapper) {
282
+ existing.owners.set(owner, (existing.owners.get(owner) ?? 0) + 1);
283
+ return {
284
+ cleanup: cleanupOwner(prototype, existing, owner),
285
+ diagnostic: "reused",
286
+ version: target.version,
287
+ };
288
+ }
289
+
290
+ // A later extension displaced the method. Never wrap that successor and
291
+ // never leave our old wrapper active if the successor restores it later.
292
+ existing.active = false;
293
+ if (prototype[existing.method] !== existing.wrapper) {
294
+ if (prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY] === existing) {
295
+ delete prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
296
+ }
297
+ return { cleanup: () => {}, diagnostic: "displaced", version: target.version };
298
+ }
299
+
300
+ // An inactive wrapper restored by a former successor can be safely reduced
301
+ // to its original predecessor before a fresh installation.
302
+ restorePredecessor(prototype, existing);
303
+ if (prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY] === existing) {
304
+ delete prototype[ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY];
305
+ }
306
+ }
307
+
308
+ const method = discoverLayoutMethod(prototype);
309
+ if (!method) {
310
+ return { cleanup: () => {}, diagnostic: "unsupported-shape", version: target.version };
311
+ }
312
+ const predecessorDescriptor = Object.getOwnPropertyDescriptor(prototype, method);
313
+ const predecessor = prototype[method];
314
+ if (typeof predecessor !== "function" || predecessorDescriptor?.configurable === false) {
315
+ return { cleanup: () => {}, diagnostic: "unsupported-shape", version: target.version };
316
+ }
317
+
318
+ const registry: LayoutPatchRegistry = {
319
+ method,
320
+ predecessor: predecessor as LayoutMethod,
321
+ predecessorDescriptor,
322
+ wrapper: () => undefined,
323
+ owners: new Map([[owner, 1]]),
324
+ active: true,
325
+ };
326
+ registry.wrapper = function accentRailLayoutNodePatch(this: unknown, ...args: unknown[]) {
327
+ const node = Reflect.apply(registry.predecessor, this, args);
328
+ if (!registry.active || registry.owners.size === 0) return node;
329
+ try {
330
+ return adjustAccentRailLayoutNode(node, new Set(registry.owners.keys()));
331
+ } catch {
332
+ return node;
333
+ }
334
+ };
335
+ try {
336
+ Object.defineProperty(prototype, method, {
337
+ ...(predecessorDescriptor ?? {
338
+ configurable: true,
339
+ enumerable: false,
340
+ writable: true,
341
+ }),
342
+ value: registry.wrapper,
343
+ });
344
+ Object.defineProperty(prototype, ZENTUI_ACCENT_RAIL_LAYOUT_REGISTRY, {
345
+ value: registry,
346
+ configurable: true,
347
+ enumerable: false,
348
+ });
349
+ } catch {
350
+ registry.active = false;
351
+ restorePredecessor(prototype, registry);
352
+ return { cleanup: () => {}, diagnostic: "unsupported-shape", version: target.version };
353
+ }
354
+ return {
355
+ cleanup: cleanupOwner(prototype, registry, owner),
356
+ diagnostic: "installed",
357
+ version: target.version,
358
+ };
359
+ }
360
+
361
+ type PackageLocation = {
362
+ root: string;
363
+ version: string;
364
+ };
365
+
366
+ function packageLocationFromEntry(
367
+ entry: string,
368
+ expectedName: string,
369
+ ): PackageLocation | undefined {
370
+ const root = parse(entry).root;
371
+ let current = dirname(entry);
372
+ while (current !== root) {
373
+ try {
374
+ const packageJson = JSON.parse(readFileSync(join(current, "package.json"), "utf8")) as {
375
+ name?: unknown;
376
+ version?: unknown;
377
+ };
378
+ if (packageJson.name === expectedName && typeof packageJson.version === "string") {
379
+ return { root: current, version: packageJson.version };
380
+ }
381
+ } catch {
382
+ // Continue walking to the package root.
383
+ }
384
+ current = dirname(current);
385
+ }
386
+ return undefined;
387
+ }
388
+
389
+ function canonicalFile(path: string): string | undefined {
390
+ try {
391
+ const canonical = realpathSync(path);
392
+ return statSync(canonical).isFile() ? canonical : undefined;
393
+ } catch {
394
+ return undefined;
395
+ }
396
+ }
397
+
398
+ function localPiTuiPath(): string | undefined {
399
+ try {
400
+ return canonicalFile(createRequire(import.meta.url).resolve("@earendil-works/pi-tui"));
401
+ } catch {
402
+ return undefined;
403
+ }
404
+ }
405
+
406
+ export async function discoverAccentRailLayoutPatchTargetFromEntrypoint(
407
+ entrypoint: string,
408
+ ): Promise<AccentRailLayoutPatchTarget | undefined> {
409
+ try {
410
+ const canonicalEntrypoint = canonicalFile(entrypoint);
411
+ if (!canonicalEntrypoint) return undefined;
412
+ if (!packageLocationFromEntry(canonicalEntrypoint, "@earendil-works/pi-coding-agent")) {
413
+ return undefined;
414
+ }
415
+
416
+ const hostRequire = createRequire(canonicalEntrypoint);
417
+ const unresolved = hostRequire.resolve("@earendil-works/pi-tui");
418
+ const resolvedModulePath = canonicalFile(unresolved);
419
+ if (!resolvedModulePath) return undefined;
420
+ const packageLocation = packageLocationFromEntry(resolvedModulePath, "@earendil-works/pi-tui");
421
+ if (!packageLocation || !SUPPORTED_PI_TUI_VERSION.test(packageLocation.version)) {
422
+ return undefined;
423
+ }
424
+
425
+ const module = (await import(pathToFileURL(resolvedModulePath).href)) as {
426
+ VStack?: { prototype?: object };
427
+ };
428
+ const prototype = module.VStack?.prototype;
429
+ if (!prototype || !discoverLayoutMethod(prototype as PatchPrototype)) return undefined;
430
+
431
+ return {
432
+ prototype,
433
+ version: packageLocation.version,
434
+ canonicalEntrypoint,
435
+ resolvedModulePath,
436
+ localModulePath: localPiTuiPath(),
437
+ };
438
+ } catch {
439
+ return undefined;
440
+ }
441
+ }
442
+
443
+ export async function discoverHostAccentRailLayoutPatchTarget(): Promise<
444
+ AccentRailLayoutPatchTarget | undefined
445
+ > {
446
+ const entrypoint = process.argv[1];
447
+ return entrypoint ? discoverAccentRailLayoutPatchTargetFromEntrypoint(entrypoint) : undefined;
448
+ }
449
+
450
+ export async function installHostAccentRailLayoutPatch(
451
+ owner: symbol,
452
+ ): Promise<AccentRailLayoutPatchInstallation> {
453
+ const target = await discoverHostAccentRailLayoutPatchTarget();
454
+ return target
455
+ ? installAccentRailLayoutPatchOnTarget(target, owner)
456
+ : { cleanup: () => {}, diagnostic: "host-module-unavailable" };
457
+ }
@@ -0,0 +1,157 @@
1
+ import type { Theme } from "@earendil-works/pi-coding-agent";
2
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
+
4
+ export type CompletionRowsOptions = {
5
+ lines: string[];
6
+ width: number;
7
+ theme: Theme;
8
+ selectedPrefix: string;
9
+ ownedBackground: boolean;
10
+ };
11
+
12
+ export type CompletionPaletteOptions = {
13
+ lines: string[];
14
+ width: number;
15
+ theme: Theme;
16
+ renderSeparator: (text: string) => string;
17
+ ownedBackground: boolean;
18
+ };
19
+
20
+ const SGR_SEQUENCE = /(?:\x1b\[|\u009b)([0-9:;]*)m/g;
21
+
22
+ function normalizeC1Csi(content: string): string {
23
+ return content.replace(/\u009b([0-?]*[ -/]*[@-~])/g, "\x1b[$1");
24
+ }
25
+
26
+ function sgrBackgroundAction(parameters: string): "reset" | "set" | undefined {
27
+ let action: "reset" | "set" | undefined;
28
+ const values = (parameters || "0").split(";");
29
+ for (let index = 0; index < values.length; index++) {
30
+ const parameter = values[index] ?? "";
31
+ const primary = parameter === "" ? 0 : Number.parseInt(parameter.split(":", 1)[0] ?? "", 10);
32
+ if (primary === 38 || primary === 48 || primary === 58) {
33
+ if (primary === 48) action = "set";
34
+ if (!parameter.includes(":")) {
35
+ const mode = Number.parseInt(values[index + 1] ?? "", 10);
36
+ if (mode === 5) index += 2;
37
+ else if (mode === 2) index += 4;
38
+ }
39
+ continue;
40
+ }
41
+ if (primary === 0 || primary === 49) action = "reset";
42
+ else if ((primary >= 40 && primary <= 47) || (primary >= 100 && primary <= 107)) action = "set";
43
+ }
44
+ return action;
45
+ }
46
+
47
+ function restoresOwnedBackground(sequence: string): boolean {
48
+ const parameters = sequence.slice(sequence.startsWith("\u009b") ? 1 : 2, -1);
49
+ return sgrBackgroundAction(parameters) === "reset";
50
+ }
51
+
52
+ function truncateTerminalLine(content: string, width: number): string {
53
+ const safeWidth = Math.max(0, width);
54
+ const normalized = normalizeC1Csi(content);
55
+ return visibleWidth(normalized) <= safeWidth
56
+ ? content
57
+ : truncateToWidth(normalized, safeWidth, "");
58
+ }
59
+
60
+ export function fillTerminalLine(content: string, width: number): string {
61
+ const truncated = truncateTerminalLine(content, width);
62
+ const renderedWidth = visibleWidth(normalizeC1Csi(truncated));
63
+ return `${truncated}${" ".repeat(Math.max(0, width - renderedWidth))}`;
64
+ }
65
+
66
+ export function applyOwnedSurfaceBackground(theme: Theme, text: string): string {
67
+ try {
68
+ const background = theme.getBgAnsi("userMessageBg");
69
+ const restored = text.replace(SGR_SEQUENCE, (sequence) =>
70
+ restoresOwnedBackground(sequence) ? `${sequence}${background}` : sequence,
71
+ );
72
+ return `${background}${restored}\x1b[49m`;
73
+ } catch {
74
+ try {
75
+ let cursor = 0;
76
+ let rendered = "";
77
+ for (const match of text.matchAll(SGR_SEQUENCE)) {
78
+ if (!restoresOwnedBackground(match[0])) continue;
79
+ const before = text.slice(cursor, match.index);
80
+ if (before) rendered += theme.bg("userMessageBg", before);
81
+ rendered += match[0];
82
+ cursor = (match.index ?? 0) + match[0].length;
83
+ }
84
+ const trailing = text.slice(cursor);
85
+ if (trailing) rendered += theme.bg("userMessageBg", trailing);
86
+ return rendered || theme.bg("userMessageBg", text);
87
+ } catch {
88
+ return text;
89
+ }
90
+ }
91
+ }
92
+
93
+ export function replaceNativeSelectedPrefix(line: string, replacement: string): string {
94
+ const selectedPrefix = /^((?:(?:\x1b\[|\u009b)[0-9:;]*m)*)→ /;
95
+ const match = line.match(selectedPrefix);
96
+ if (!match) return line;
97
+ return `${replacement}${match[1]}${line.slice(match[0].length)}`;
98
+ }
99
+
100
+ export function isNativeCompletionCountRow(line: string): boolean {
101
+ const withoutSgr = line.replace(/(?:\x1b\[|\u009b)[0-?]*[ -/]*m/g, "");
102
+ return /^\s*\(\d+\/\d+\)\s*$/.test(withoutSgr);
103
+ }
104
+
105
+ export function omitTrailingNativeCompletionCountRow(lines: string[]): string[] {
106
+ return lines.length > 0 && isNativeCompletionCountRow(lines.at(-1) ?? "")
107
+ ? lines.slice(0, -1)
108
+ : lines;
109
+ }
110
+
111
+ export function renderCompletionRows({
112
+ lines,
113
+ width,
114
+ theme,
115
+ selectedPrefix,
116
+ ownedBackground,
117
+ }: CompletionRowsOptions): string[] {
118
+ return lines.map((line) => {
119
+ const content = fillTerminalLine(replaceNativeSelectedPrefix(line, selectedPrefix), width);
120
+ const surfaced = ownedBackground ? applyOwnedSurfaceBackground(theme, content) : content;
121
+ return truncateTerminalLine(surfaced, width);
122
+ });
123
+ }
124
+
125
+ function helpLabel(width: number): string {
126
+ const labels = [
127
+ " ↑↓ Navigate Enter Use Esc Close",
128
+ " ↑↓ Nav Enter Use Esc",
129
+ " ↑↓ Enter Esc",
130
+ ];
131
+ return labels.find((label) => visibleWidth(label) <= width) ?? labels.at(-1) ?? "";
132
+ }
133
+
134
+ /** Full-width, provenance-preserving completion shell for Opencode editor styles. */
135
+ export function renderCompletionPalette({
136
+ lines,
137
+ width,
138
+ theme,
139
+ renderSeparator,
140
+ ownedBackground,
141
+ }: CompletionPaletteOptions): string[] {
142
+ const resultLines = omitTrailingNativeCompletionCountRow(lines);
143
+ if (resultLines.length === 0) return [];
144
+ const safeWidth = Math.max(0, width);
145
+ const rows = renderCompletionRows({
146
+ lines: resultLines,
147
+ width: safeWidth,
148
+ theme,
149
+ selectedPrefix: " ",
150
+ ownedBackground,
151
+ });
152
+ const separator = renderSeparator("─".repeat(safeWidth));
153
+ if (safeWidth < 3) return [...rows, truncateToWidth(separator, safeWidth, "")];
154
+ const helpText = fillTerminalLine(helpLabel(safeWidth), safeWidth);
155
+ const help = ownedBackground ? applyOwnedSurfaceBackground(theme, helpText) : helpText;
156
+ return [...rows, truncateToWidth(separator, safeWidth, ""), truncateToWidth(help, safeWidth, "")];
157
+ }