@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
@@ -1,43 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { EditorPerfProfiler, readEditorPerfOptions } from "../editor-performance.ts";
4
-
5
- test("editor profiling and A/B switches are opt-in", () => {
6
- assert.deepEqual(readEditorPerfOptions({}), {
7
- enabled: false,
8
- fastRender: true,
9
- editorChrome: true,
10
- widgets: true,
11
- bashWidgets: true,
12
- lastPrompt: true,
13
- });
14
-
15
- assert.deepEqual(readEditorPerfOptions({
16
- POWERLINE_DEBUG_PERF: "1",
17
- POWERLINE_PERF_FAST_RENDER: "0",
18
- POWERLINE_PERF_EDITOR_CHROME: "0",
19
- POWERLINE_PERF_WIDGETS: "0",
20
- POWERLINE_PERF_BASH_WIDGETS: "0",
21
- POWERLINE_PERF_LAST_PROMPT: "0",
22
- }), {
23
- enabled: true,
24
- fastRender: false,
25
- editorChrome: false,
26
- widgets: false,
27
- bashWidgets: false,
28
- lastPrompt: false,
29
- });
30
- });
31
-
32
- test("editor profiler reports timings, counts, and draft size", () => {
33
- const profiler = new EditorPerfProfiler(readEditorPerfOptions({ POWERLINE_DEBUG_PERF: "1" }));
34
-
35
- profiler.measure("input.total", () => 42);
36
- profiler.count("editor.render.fast-hit");
37
- profiler.observeDraft(["hello", "world"]);
38
-
39
- const report = profiler.report();
40
- assert.match(report, /inputs=1, editor-renders=0, renders\/input=0\.00, max-draft=11/);
41
- assert.match(report, /editor\.render\.fast-hit: n=1/);
42
- assert.match(report, /input\.total: n=1, avg=/);
43
- });
@@ -1,136 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { createRenderScheduler } from "../render-scheduler.ts";
4
-
5
- test("render scheduler coalesces pending status renders", () => {
6
- const originalSetTimeout = globalThis.setTimeout;
7
- const callbacks: Array<() => void> = [];
8
- const delays: number[] = [];
9
- let renderCount = 0;
10
-
11
- globalThis.setTimeout = ((callback: () => void, delay?: number) => {
12
- callbacks.push(callback);
13
- delays.push(delay ?? 0);
14
- return { id: callbacks.length } as ReturnType<typeof setTimeout>;
15
- }) as typeof setTimeout;
16
-
17
- try {
18
- const scheduler = createRenderScheduler(() => { renderCount += 1; }, 33);
19
-
20
- scheduler.schedule();
21
- scheduler.schedule(150);
22
- scheduler.schedule();
23
-
24
- assert.equal(callbacks.length, 1);
25
- assert.equal(delays[0], 33);
26
- callbacks[0]?.();
27
- assert.equal(renderCount, 1);
28
-
29
- scheduler.schedule(150);
30
- assert.equal(callbacks.length, 2);
31
- assert.equal(delays[1], 150);
32
- } finally {
33
- globalThis.setTimeout = originalSetTimeout;
34
- }
35
- });
36
-
37
- test("render scheduler pulls pending work forward for an earlier deadline", () => {
38
- const originalSetTimeout = globalThis.setTimeout;
39
- const originalClearTimeout = globalThis.clearTimeout;
40
- const callbacks: Array<() => void> = [];
41
- const delays: number[] = [];
42
- const cleared = new Set<object>();
43
- let renderCount = 0;
44
-
45
- globalThis.setTimeout = ((callback: () => void, delay?: number) => {
46
- const handle = { id: callbacks.length + 1 };
47
- callbacks.push(callback);
48
- delays.push(delay ?? 0);
49
- return handle as ReturnType<typeof setTimeout>;
50
- }) as typeof setTimeout;
51
- globalThis.clearTimeout = ((handle?: ReturnType<typeof setTimeout>) => {
52
- if (handle && typeof handle === "object") cleared.add(handle);
53
- }) as typeof clearTimeout;
54
-
55
- try {
56
- const scheduler = createRenderScheduler(() => { renderCount += 1; }, 33);
57
-
58
- scheduler.schedule(250);
59
- scheduler.schedule(33);
60
-
61
- assert.deepEqual(delays, [250, 33]);
62
- assert.equal(cleared.size, 1);
63
- callbacks[1]?.();
64
- assert.equal(renderCount, 1);
65
- } finally {
66
- globalThis.setTimeout = originalSetTimeout;
67
- globalThis.clearTimeout = originalClearTimeout;
68
- }
69
- });
70
-
71
- test("render scheduler allows callbacks to schedule follow-up renders", () => {
72
- const originalSetTimeout = globalThis.setTimeout;
73
- const callbacks: Array<() => void> = [];
74
- const delays: number[] = [];
75
- let renderCount = 0;
76
- let scheduler: ReturnType<typeof createRenderScheduler>;
77
-
78
- globalThis.setTimeout = ((callback: () => void, delay?: number) => {
79
- callbacks.push(callback);
80
- delays.push(delay ?? 0);
81
- return { id: callbacks.length } as ReturnType<typeof setTimeout>;
82
- }) as typeof setTimeout;
83
-
84
- try {
85
- scheduler = createRenderScheduler(() => {
86
- renderCount += 1;
87
- if (renderCount === 1) {
88
- scheduler.schedule(150);
89
- }
90
- }, 33);
91
-
92
- scheduler.schedule();
93
- callbacks[0]?.();
94
-
95
- assert.equal(renderCount, 1);
96
- assert.equal(callbacks.length, 2);
97
- assert.equal(delays[1], 150);
98
-
99
- callbacks[1]?.();
100
- assert.equal(renderCount, 2);
101
- } finally {
102
- globalThis.setTimeout = originalSetTimeout;
103
- }
104
- });
105
-
106
- test("render scheduler cancels pending status renders", () => {
107
- const originalSetTimeout = globalThis.setTimeout;
108
- const originalClearTimeout = globalThis.clearTimeout;
109
- const callbacks: Array<() => void> = [];
110
- const cleared = new Set<object>();
111
- let renderCount = 0;
112
-
113
- globalThis.setTimeout = ((callback: () => void) => {
114
- const handle = { id: callbacks.length + 1 };
115
- callbacks.push(callback);
116
- return handle as ReturnType<typeof setTimeout>;
117
- }) as typeof setTimeout;
118
- globalThis.clearTimeout = ((handle?: ReturnType<typeof setTimeout>) => {
119
- if (handle && typeof handle === "object") {
120
- cleared.add(handle);
121
- }
122
- }) as typeof clearTimeout;
123
-
124
- try {
125
- const scheduler = createRenderScheduler(() => { renderCount += 1; }, 33);
126
-
127
- scheduler.schedule();
128
- scheduler.cancel();
129
-
130
- assert.equal(cleared.size, 1);
131
- assert.equal(renderCount, 0);
132
- } finally {
133
- globalThis.setTimeout = originalSetTimeout;
134
- globalThis.clearTimeout = originalClearTimeout;
135
- }
136
- });
@@ -1,39 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { initTheme, theme } from "../../../modes/interactive/theme/theme.ts";
3
-
4
- /**
5
- * The powerline editor render draws its own border. It must respect the
6
- * editor's `borderColor` (set by interactive-mode to the bash-mode / thinking
7
- * color) instead of always using the gray "sep" color, so typing `!` visibly
8
- * switches the input field to bash mode.
9
- */
10
- function powerlineBorderColor(editor: { borderColor: unknown }, s: string): string {
11
- return typeof editor.borderColor === "function"
12
- ? (editor.borderColor as (str: string) => string)(s)
13
- : `\x1b[38;5;244m${s}\x1b[0m`;
14
- }
15
-
16
- describe("powerline editor border respects editor.borderColor", () => {
17
- it("uses the bash-mode color when interactive-mode sets it (typing !)", () => {
18
- initTheme("dark");
19
- const editorWithBashBorder = {
20
- borderColor: theme.getBashModeBorderColor(),
21
- };
22
- const border = powerlineBorderColor(editorWithBashBorder, "─".repeat(5));
23
- // Bash mode is green in the theme; the border must carry a color code.
24
- expect(border).toContain("\x1b[");
25
- expect(border).toMatch(/[0-9]+;[0-9]+;[0-9]+m/);
26
- // And it must NOT be the plain gray "sep" code.
27
- expect(border).not.toContain("\x1b[38;5;244m");
28
- });
29
-
30
- it("falls back to the gray sep color when borderColor is not a function", () => {
31
- const border = powerlineBorderColor({ borderColor: "x" }, "─".repeat(5));
32
- expect(border).toBe("\x1b[38;5;244m─────\x1b[0m");
33
- });
34
- });
35
-
36
- function getBashModeBorderColor(): (str: string) => string {
37
- // Mirrors theme.getBashModeBorderColor(): colors with the theme's "bashMode" color.
38
- return (str: string) => `\x1b[38;5;34m${str}\x1b[39m`;
39
- }
@@ -1,64 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
-
7
- import { readOnlyGitEnv } from "../git-status.ts";
8
-
9
- const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
10
-
11
- // Background fetches spawn git with up to 500ms timeouts; give them room.
12
- const FETCH_SETTLE_MS = 1200;
13
-
14
- test("read-only git commands opt out of git's optional index lock", () => {
15
- const env = readOnlyGitEnv({ PATH: "/usr/bin" });
16
- assert.equal(env.GIT_OPTIONAL_LOCKS, "0");
17
- assert.equal(env.PATH, "/usr/bin", "must extend the ambient environment, not replace it");
18
- });
19
-
20
- test("readOnlyGitEnv overrides an inherited GIT_OPTIONAL_LOCKS=1", () => {
21
- assert.equal(readOnlyGitEnv({ GIT_OPTIONAL_LOCKS: "1" }).GIT_OPTIONAL_LOCKS, "0");
22
- });
23
-
24
- // Guards the regression end to end, via a `git` shim on PATH: the footer polls
25
- // every repo the user visits, so it must never take `.git/index.lock`.
26
- test("the git process the footer spawns receives GIT_OPTIONAL_LOCKS=0", { skip: process.platform === "win32" ? "POSIX shim" : false }, async () => {
27
- const dir = mkdtempSync(join(tmpdir(), "powerline-git-locks-"));
28
- const bin = join(dir, "bin");
29
- mkdirSync(bin);
30
- const log = join(dir, "calls.log");
31
-
32
- writeFileSync(
33
- join(bin, "git"),
34
- `#!/bin/sh\necho "GIT_OPTIONAL_LOCKS=\${GIT_OPTIONAL_LOCKS-unset} ARGS=$*" >> ${JSON.stringify(log)}\nexit 0\n`,
35
- { mode: 0o755 },
36
- );
37
-
38
- const originalPath = process.env.PATH;
39
- const originalOptionalLocks = process.env.GIT_OPTIONAL_LOCKS;
40
- process.env.PATH = `${bin}:${originalPath ?? ""}`;
41
- // Some environments export GIT_OPTIONAL_LOCKS=0 as a machine-wide workaround
42
- // for this bug; force the opposite so we can't inherit a false pass.
43
- process.env.GIT_OPTIONAL_LOCKS = "1";
44
- try {
45
- // Imported here so the module reads the shimmed PATH when it spawns.
46
- const { getGitStatus, invalidateGitStatus, invalidateGitBranch } = await import("../git-status.ts");
47
- invalidateGitStatus();
48
- invalidateGitBranch();
49
- getGitStatus("main");
50
- await sleep(FETCH_SETTLE_MS);
51
- } finally {
52
- process.env.PATH = originalPath;
53
- if (originalOptionalLocks === undefined) delete process.env.GIT_OPTIONAL_LOCKS;
54
- else process.env.GIT_OPTIONAL_LOCKS = originalOptionalLocks;
55
- }
56
-
57
- assert.ok(existsSync(log), "expected the footer to spawn git");
58
- const calls = readFileSync(log, "utf8").trim().split("\n");
59
- const status = calls.filter((line) => line.includes("ARGS=status --porcelain"));
60
- assert.ok(status.length > 0, `expected a status call, got:\n${calls.join("\n")}`);
61
- for (const call of calls) {
62
- assert.match(call, /GIT_OPTIONAL_LOCKS=0/, `git spawned without the lock opt-out: ${call}`);
63
- }
64
- });
@@ -1,92 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { detectGitHost, getCurrentBranch, getGitStatus, invalidateGitBranch, invalidateGitStatus, subscribeGitUpdates } from "../git-status.ts";
4
-
5
- const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
6
-
7
- // Background fetches spawn git with up to 500ms timeouts; give them room.
8
- const FETCH_SETTLE_MS = 1200;
9
-
10
- test("git status supports disabling extension git polling", () => {
11
- assert.deepEqual(getGitStatus("main", "off"), {
12
- branch: "main",
13
- staged: 0,
14
- unstaged: 0,
15
- untracked: 0,
16
- });
17
- });
18
-
19
- test("invalidateGitStatus serves stale counts while refreshing (no flicker to zeros)", async () => {
20
- getGitStatus("provider-branch"); // kick off the initial background fetch
21
- await sleep(FETCH_SETTLE_MS);
22
-
23
- const seeded = getGitStatus("provider-branch");
24
-
25
- invalidateGitStatus();
26
- const afterInvalidate = getGitStatus("provider-branch");
27
- assert.deepEqual(
28
- { staged: afterInvalidate.staged, unstaged: afterInvalidate.unstaged, untracked: afterInvalidate.untracked },
29
- { staged: seeded.staged, unstaged: seeded.unstaged, untracked: seeded.untracked },
30
- );
31
-
32
- // The background refresh converges to real data again (repo is unchanged).
33
- await sleep(FETCH_SETTLE_MS);
34
- const refreshed = getGitStatus("provider-branch");
35
- assert.deepEqual(
36
- { staged: refreshed.staged, unstaged: refreshed.unstaged, untracked: refreshed.untracked },
37
- { staged: seeded.staged, unstaged: seeded.unstaged, untracked: seeded.untracked },
38
- );
39
- });
40
-
41
- test("invalidateGitBranch keeps serving the last known branch while refreshing", async () => {
42
- getGitStatus("provider-fallback"); // seed branch cache
43
- await sleep(FETCH_SETTLE_MS);
44
-
45
- const realBranch = getCurrentBranch("provider-fallback");
46
- // Once fetched, the cached branch no longer falls back to the provider
47
- // (null in non-git directories, the actual branch name inside a repo).
48
- assert.notEqual(realBranch, "provider-fallback");
49
-
50
- invalidateGitBranch();
51
- assert.equal(getCurrentBranch("provider-fallback"), realBranch);
52
- });
53
-
54
- test("git cache completion notifies active footer subscribers", async () => {
55
- let updates = 0;
56
- const unsubscribe = subscribeGitUpdates(() => { updates += 1; });
57
- try {
58
- invalidateGitStatus();
59
- invalidateGitBranch();
60
- getGitStatus("provider-fallback");
61
- await sleep(FETCH_SETTLE_MS);
62
- assert.ok(updates > 0);
63
- } finally {
64
- unsubscribe();
65
- }
66
- });
67
-
68
- test("detectGitHost recognizes known hosts over SSH and HTTPS", () => {
69
- assert.equal(detectGitHost("git@github.com:owner/repo.git"), "github");
70
- assert.equal(detectGitHost("https://github.com/owner/repo.git"), "github");
71
- assert.equal(detectGitHost("ssh://git@gitlab.com/owner/repo.git"), "gitlab");
72
- assert.equal(detectGitHost("https://gitlab.com/owner/repo"), "gitlab");
73
- assert.equal(detectGitHost("git@bitbucket.org:owner/repo.git"), "bitbucket");
74
- assert.equal(detectGitHost("https://user@bitbucket.org/owner/repo.git"), "bitbucket");
75
- });
76
-
77
- test("detectGitHost normalizes www and sub-domains", () => {
78
- assert.equal(detectGitHost("https://www.github.com/owner/repo"), "github");
79
- assert.equal(detectGitHost("git@ssh.github.com:owner/repo.git"), "github");
80
- });
81
-
82
- test("detectGitHost treats unknown or self-hosted remotes as a generic host", () => {
83
- assert.equal(detectGitHost("git@git.example.com:owner/repo.git"), "other");
84
- assert.equal(detectGitHost("https://gitea.mycorp.dev/owner/repo.git"), "other");
85
- assert.equal(detectGitHost("/srv/git/local.git"), "other");
86
- });
87
-
88
- test("detectGitHost returns null when there is no remote", () => {
89
- assert.equal(detectGitHost(null), null);
90
- assert.equal(detectGitHost(""), null);
91
- assert.equal(detectGitHost(" "), null);
92
- });
@@ -1,129 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { mkdtempSync, readFileSync, rmSync } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import {
7
- GUIDE_COMPACT_LINES,
8
- GUIDE_DISMISS_HINT,
9
- GUIDE_FEATURES,
10
- getGuideFeatureLines,
11
- getGuidePreferences,
12
- getNewGuideFeatures,
13
- markGuideVersionSeen,
14
- resolveGuideDisplayMode,
15
- saveGuidePreferences,
16
- type GuideFeature,
17
- } from "../guide.ts";
18
-
19
- const features: GuideFeature[] = [
20
- { id: "old", title: "Old", example: "/old", introducedIn: "0.5.0" },
21
- { id: "new", title: "New", example: "/new", introducedIn: "0.5.3" },
22
- { id: "future", title: "Future", example: "/future", introducedIn: "0.6.0" },
23
- ];
24
-
25
-
26
- test("explicit full guide opens for each real session start", () => {
27
- for (const reason of ["startup", "new", "resume", "fork"]) {
28
- assert.equal(resolveGuideDisplayMode({ reason, hasUI: true, guideMode: "full" }), "full");
29
- }
30
- });
31
-
32
- test("guide dismissal and compact preferences change automatic display", () => {
33
- assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: true, guideMode: "compact" }), "compact");
34
- assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: true, guideMode: "off" }), "none");
35
- assert.equal(resolveGuideDisplayMode({ reason: "reload", hasUI: true, guideMode: "full" }), "none");
36
- assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: false, guideMode: "full" }), "none");
37
- });
38
-
39
- test("guide defaults to a compact header so the native dock stays uncluttered", () => {
40
- const preferences = getGuidePreferences({});
41
- assert.equal(preferences.mode, "compact");
42
- assert.equal(resolveGuideDisplayMode({ reason: "startup", hasUI: true, guideMode: preferences.mode }), "compact");
43
- });
44
-
45
- test("full and compact guide content expose README commands and examples", () => {
46
- const full = getGuideFeatureLines()
47
- .map((line) => line.kind === "section" ? line.text : `${line.feature.title}: ${line.feature.example}`)
48
- .join("\n");
49
- const compact = GUIDE_COMPACT_LINES.join("\n");
50
-
51
- assert.match(full, /\/settings/);
52
- assert.match(full, /\$workflow/);
53
- assert.match(full, /Ask the architect to challenge this plan/);
54
- assert.match(full, /Ask the researcher to research <topic> and cite sources/);
55
- assert.match(full, /\/skill:\*/);
56
- assert.match(full, /\/handoff-new Next: fix <problem>; start with a coding plan/);
57
- assert.doesNotMatch(full, /question\(|\/intercom|powerline/);
58
- assert.match(compact, /\/settings/);
59
- assert.match(compact, /\$workflow/);
60
- assert.match(compact, /Ask the researcher to research <topic>/);
61
- });
62
-
63
- test("blocking guide dismisses only from its overlay input handler", () => {
64
- const source = readFileSync(new URL("../index.ts", import.meta.url), "utf8");
65
-
66
- assert.equal(GUIDE_DISMISS_HINT, "Press any key to continue");
67
- assert.match(source, /guideOverlayBlocking = true;[\s\S]*handleInput: \(\) => dismiss\(\),/);
68
- // The blocking guide cannot be dismissed by the welcome scheduler: dismissWelcome
69
- // bails while guideOverlayBlocking is set, so only the overlay's input handler
70
- // (which clears the flag first) can close it.
71
- assert.match(source, /function dismissWelcome\(ctx: any\) \{[\s\S]*?if \(guideOverlayBlocking\) return;/);
72
- });
73
-
74
- test("README-derived guide entries stay in the feature map", () => {
75
- assert.deepEqual(
76
- GUIDE_FEATURES.filter((feature) => feature.id === "settings" || feature.id === "handoff").map((feature) => feature.example),
77
- ["/settings · /guide compact|off|reset · /reload", "/handoff-new Next: fix <problem>; start with a coding plan."],
78
- );
79
- });
80
-
81
- test("new feature detection is version bounded and empty on first install", () => {
82
- assert.deepEqual(getNewGuideFeatures(undefined, "0.5.3", features), []);
83
- assert.deepEqual(getNewGuideFeatures("0.5.0", "0.5.3", features).map((feature) => feature.id), ["new"]);
84
- assert.deepEqual(getNewGuideFeatures("0.5.3", "0.5.3", features), []);
85
- assert.deepEqual(getNewGuideFeatures("0.5.0", "0.5.2", features), []);
86
- });
87
-
88
- test("guide dismissal persists only the mode and version marker", () => {
89
- const dir = mkdtempSync(join(tmpdir(), "selesai-guide-"));
90
- const settingsPath = join(dir, "settings.json");
91
-
92
- try {
93
- assert.equal(saveGuidePreferences({ mode: "off", lastSeenVersion: "0.5.3" }, settingsPath), true);
94
- const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as Record<string, unknown>;
95
- assert.deepEqual(settings.selesaiGuide, { mode: "off", lastSeenVersion: "0.5.3" });
96
- assert.equal(getGuidePreferences(settings).mode, "off");
97
- } finally {
98
- rmSync(dir, { recursive: true, force: true });
99
- }
100
- });
101
-
102
- test("version marking never persists an implicit guide mode", () => {
103
- const dir = mkdtempSync(join(tmpdir(), "selesai-guide-"));
104
- const settingsPath = join(dir, "settings.json");
105
-
106
- try {
107
- // markGuideVersionSeen writes only the version marker; the mode stays
108
- // unset so the default (compact) applies instead of being locked in.
109
- assert.equal(markGuideVersionSeen("0.5.3", settingsPath), true);
110
- const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as Record<string, unknown>;
111
- assert.deepEqual(settings.selesaiGuide, { lastSeenVersion: "0.5.3" });
112
- assert.equal(getGuidePreferences(settings).mode, "compact");
113
-
114
- // An explicitly chosen mode is preserved across version-only writes.
115
- assert.equal(saveGuidePreferences({ mode: "off" }, settingsPath), true);
116
- assert.equal(saveGuidePreferences({ lastSeenVersion: "0.6.0" }, settingsPath), true);
117
- const after = JSON.parse(readFileSync(settingsPath, "utf8")) as Record<string, unknown>;
118
- assert.deepEqual(after.selesaiGuide, { mode: "off", lastSeenVersion: "0.6.0" });
119
- } finally {
120
- rmSync(dir, { recursive: true, force: true });
121
- }
122
- });
123
-
124
- test("invalid persisted guide settings fall back to the compact guide", () => {
125
- assert.deepEqual(getGuidePreferences({ selesaiGuide: { mode: "unexpected", lastSeenVersion: 42 } }), {
126
- mode: "compact",
127
- lastSeenVersion: undefined,
128
- });
129
- });
@@ -1,116 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { CURSOR_MARKER } from "@earendil-works/pi-tui";
4
- import { isSupportedSuperShortcut, matchesConfiguredShortcut, shortcutConflictKey } from "../shortcuts.ts";
5
- import { parseBashModeSettings, renderFastPowerlineEditor, resolveShortcutConfig } from "../index.ts";
6
-
7
- test("surviving editor shortcuts resolve without app-owned chat scrolling", () => {
8
- const resolved = resolveShortcutConfig({});
9
- assert.equal(resolved.stashHistory, "ctrl+alt+h");
10
- assert.equal(resolved.copyEditor, "ctrl+alt+c");
11
- assert.equal(resolved.cutEditor, "ctrl+alt+x");
12
- assert.equal(resolved.editorStart, "super+shift+up");
13
- assert.equal(resolved.editorEnd, "super+shift+down");
14
- assert.equal(Object.keys(resolved).some((key) => key.startsWith("scroll")), false);
15
- assert.equal(Object.keys(resolved).some((key) => key.startsWith("jump")), false);
16
- });
17
-
18
- test("super shortcut matching and conflict normalization remain supported", () => {
19
- assert.equal(matchesConfiguredShortcut("\x1b[1;9A", "super+up"), true);
20
- assert.equal(matchesConfiguredShortcut("c", "super+c"), false);
21
- assert.equal(isSupportedSuperShortcut("super+up"), true);
22
- assert.equal(isSupportedSuperShortcut("super+z"), false);
23
- assert.equal(shortcutConflictKey("super+home"), "super+up");
24
- });
25
-
26
-
27
- test("editor boundary shortcuts remain configurable", () => {
28
- const resolved = resolveShortcutConfig({
29
- powerlineShortcuts: {
30
- editorStart: "ctrl+shift+u",
31
- editorEnd: "ctrl+shift+d",
32
- },
33
- });
34
-
35
- assert.equal(resolved.editorStart, "ctrl+shift+u");
36
- assert.equal(resolved.editorEnd, "ctrl+shift+d");
37
- });
38
-
39
- test("bash completions are opt-in", () => {
40
- assert.equal(parseBashModeSettings({}).completions, false);
41
- assert.equal(parseBashModeSettings({ bashMode: { completions: true } }).completions, true);
42
- assert.equal(parseBashModeSettings({ bashMode: { completions: false } }).completions, false);
43
- });
44
-
45
- test("fast editor render keeps Powerline chrome for large drafts", () => {
46
- const editor = {
47
- focused: true,
48
- isShowingAutocomplete: () => false,
49
- tui: { terminal: { rows: 24 } },
50
- };
51
- Reflect.set(editor, "state", {
52
- lines: ["intro", "x".repeat(5000)],
53
- cursorLine: 1,
54
- cursorCol: 5000,
55
- });
56
-
57
- const rendered = renderFastPowerlineEditor(editor, 80, {
58
- bashModeActive: false,
59
- completionsEnabled: false,
60
- });
61
-
62
- assert.ok(rendered);
63
- assert.ok(rendered[0]?.includes("↑"));
64
- assert.ok(rendered.some((line) => line.includes(CURSOR_MARKER)));
65
- // Selesai defaults to an empty editor prompt glyph, so no ">" is injected.
66
- });
67
-
68
- test("fast editor render falls back for short drafts and enabled completions", () => {
69
- const editor = {
70
- focused: true,
71
- isShowingAutocomplete: () => false,
72
- tui: { terminal: { rows: 24 } },
73
- };
74
- Reflect.set(editor, "state", { lines: ["short"], cursorLine: 0, cursorCol: 5 });
75
-
76
- assert.equal(renderFastPowerlineEditor(editor, 80, {
77
- bashModeActive: false,
78
- completionsEnabled: false,
79
- }), null);
80
-
81
- Reflect.set(editor, "state", { lines: ["x".repeat(5000)], cursorLine: 0, cursorCol: 5000 });
82
- assert.equal(renderFastPowerlineEditor(editor, 80, {
83
- bashModeActive: false,
84
- completionsEnabled: true,
85
- }), null);
86
- });
87
-
88
- test("fast editor render falls back for wide characters", () => {
89
- const editor = {
90
- focused: true,
91
- isShowingAutocomplete: () => false,
92
- tui: { terminal: { rows: 24 } },
93
- };
94
- Reflect.set(editor, "state", { lines: ["漢".repeat(1300)], cursorLine: 0, cursorCol: 1291 });
95
-
96
- assert.equal(renderFastPowerlineEditor(editor, 80, {
97
- bashModeActive: false,
98
- completionsEnabled: false,
99
- }), null);
100
- });
101
-
102
- test("fast editor render updates the editor navigation width", () => {
103
- const editor = {
104
- focused: true,
105
- isShowingAutocomplete: () => false,
106
- lastWidth: 12,
107
- tui: { terminal: { rows: 24 } },
108
- };
109
- Reflect.set(editor, "state", { lines: ["x".repeat(5000)], cursorLine: 0, cursorCol: 5000 });
110
-
111
- assert.ok(renderFastPowerlineEditor(editor, 80, {
112
- bashModeActive: false,
113
- completionsEnabled: false,
114
- }));
115
- assert.equal(Reflect.get(editor, "lastWidth"), 76);
116
- });