@supatype/cli 0.1.1 → 0.1.3

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 (334) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +126 -99
  3. package/.turbo/turbo-typecheck.log +2 -2
  4. package/dist/binary-cache.d.ts +2 -0
  5. package/dist/binary-cache.d.ts.map +1 -1
  6. package/dist/binary-cache.js +36 -0
  7. package/dist/binary-cache.js.map +1 -1
  8. package/dist/binary-download-lock.d.ts +12 -0
  9. package/dist/binary-download-lock.d.ts.map +1 -0
  10. package/dist/binary-download-lock.js +106 -0
  11. package/dist/binary-download-lock.js.map +1 -0
  12. package/dist/cli-package-version.d.ts +3 -0
  13. package/dist/cli-package-version.d.ts.map +1 -0
  14. package/dist/cli-package-version.js +15 -0
  15. package/dist/cli-package-version.js.map +1 -0
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/cli.js +5 -2
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/add.js +25 -19
  20. package/dist/commands/add.js.map +1 -1
  21. package/dist/commands/admin.d.ts +12 -0
  22. package/dist/commands/admin.d.ts.map +1 -1
  23. package/dist/commands/admin.js +116 -26
  24. package/dist/commands/admin.js.map +1 -1
  25. package/dist/commands/cache.d.ts +1 -1
  26. package/dist/commands/cache.d.ts.map +1 -1
  27. package/dist/commands/cache.js +98 -5
  28. package/dist/commands/cache.js.map +1 -1
  29. package/dist/commands/dev.d.ts.map +1 -1
  30. package/dist/commands/dev.js +26 -22
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/commands/init.d.ts +3 -4
  33. package/dist/commands/init.d.ts.map +1 -1
  34. package/dist/commands/init.js +296 -121
  35. package/dist/commands/init.js.map +1 -1
  36. package/dist/commands/link-helpers.d.ts.map +1 -1
  37. package/dist/commands/link-helpers.js +26 -25
  38. package/dist/commands/link-helpers.js.map +1 -1
  39. package/dist/compose-local-server-image.d.ts +13 -0
  40. package/dist/compose-local-server-image.d.ts.map +1 -0
  41. package/dist/compose-local-server-image.js +57 -0
  42. package/dist/compose-local-server-image.js.map +1 -0
  43. package/dist/compose-rename.d.ts.map +1 -1
  44. package/dist/compose-rename.js +5 -8
  45. package/dist/compose-rename.js.map +1 -1
  46. package/dist/config.d.ts.map +1 -1
  47. package/dist/config.js +12 -1
  48. package/dist/config.js.map +1 -1
  49. package/dist/dev-compose.d.ts.map +1 -1
  50. package/dist/dev-compose.js +113 -56
  51. package/dist/dev-compose.js.map +1 -1
  52. package/dist/dev-log-bus.d.ts +5 -0
  53. package/dist/dev-log-bus.d.ts.map +1 -1
  54. package/dist/dev-log-bus.js +16 -0
  55. package/dist/dev-log-bus.js.map +1 -1
  56. package/dist/dev-log-filter.d.ts +4 -0
  57. package/dist/dev-log-filter.d.ts.map +1 -1
  58. package/dist/dev-log-filter.js +52 -0
  59. package/dist/dev-log-filter.js.map +1 -1
  60. package/dist/dev-ports.d.ts.map +1 -1
  61. package/dist/dev-ports.js +19 -12
  62. package/dist/dev-ports.js.map +1 -1
  63. package/dist/dev-ready-panel.d.ts +21 -0
  64. package/dist/dev-ready-panel.d.ts.map +1 -0
  65. package/dist/dev-ready-panel.js +67 -0
  66. package/dist/dev-ready-panel.js.map +1 -0
  67. package/dist/dev-session-lock.d.ts.map +1 -1
  68. package/dist/dev-session-lock.js +5 -8
  69. package/dist/dev-session-lock.js.map +1 -1
  70. package/dist/dev-session.d.ts +18 -5
  71. package/dist/dev-session.d.ts.map +1 -1
  72. package/dist/dev-session.js +91 -18
  73. package/dist/dev-session.js.map +1 -1
  74. package/dist/dev-shutdown.d.ts +2 -0
  75. package/dist/dev-shutdown.d.ts.map +1 -1
  76. package/dist/dev-shutdown.js +29 -15
  77. package/dist/dev-shutdown.js.map +1 -1
  78. package/dist/dev-task-colors.d.ts +3 -1
  79. package/dist/dev-task-colors.d.ts.map +1 -1
  80. package/dist/dev-task-colors.js +23 -3
  81. package/dist/dev-task-colors.js.map +1 -1
  82. package/dist/docker-runtime.js +1 -1
  83. package/dist/docker-runtime.js.map +1 -1
  84. package/dist/engine-push-output.d.ts +1 -0
  85. package/dist/engine-push-output.d.ts.map +1 -1
  86. package/dist/engine-push-output.js +4 -1
  87. package/dist/engine-push-output.js.map +1 -1
  88. package/dist/ensure-binary.d.ts.map +1 -1
  89. package/dist/ensure-binary.js +14 -2
  90. package/dist/ensure-binary.js.map +1 -1
  91. package/dist/ensure-component-binaries.d.ts +17 -0
  92. package/dist/ensure-component-binaries.d.ts.map +1 -0
  93. package/dist/ensure-component-binaries.js +45 -0
  94. package/dist/ensure-component-binaries.js.map +1 -0
  95. package/dist/ensure-dev-api-config.d.ts +3 -0
  96. package/dist/ensure-dev-api-config.d.ts.map +1 -0
  97. package/dist/ensure-dev-api-config.js +17 -0
  98. package/dist/ensure-dev-api-config.js.map +1 -0
  99. package/dist/init-dependency-versions.d.ts +13 -0
  100. package/dist/init-dependency-versions.d.ts.map +1 -0
  101. package/dist/init-dependency-versions.js +53 -0
  102. package/dist/init-dependency-versions.js.map +1 -0
  103. package/dist/init-package-json.d.ts +21 -0
  104. package/dist/init-package-json.d.ts.map +1 -0
  105. package/dist/init-package-json.js +46 -0
  106. package/dist/init-package-json.js.map +1 -0
  107. package/dist/init-project-detect.d.ts +14 -0
  108. package/dist/init-project-detect.d.ts.map +1 -0
  109. package/dist/init-project-detect.js +89 -0
  110. package/dist/init-project-detect.js.map +1 -0
  111. package/dist/postgres-ctl.d.ts.map +1 -1
  112. package/dist/postgres-ctl.js +2 -1
  113. package/dist/postgres-ctl.js.map +1 -1
  114. package/dist/required-host-components.d.ts +11 -0
  115. package/dist/required-host-components.d.ts.map +1 -0
  116. package/dist/required-host-components.js +25 -0
  117. package/dist/required-host-components.js.map +1 -0
  118. package/dist/resolve-api-url.d.ts +6 -0
  119. package/dist/resolve-api-url.d.ts.map +1 -0
  120. package/dist/resolve-api-url.js +39 -0
  121. package/dist/resolve-api-url.js.map +1 -0
  122. package/dist/rest-cache-admin.d.ts +33 -0
  123. package/dist/rest-cache-admin.d.ts.map +1 -0
  124. package/dist/rest-cache-admin.js +96 -0
  125. package/dist/rest-cache-admin.js.map +1 -0
  126. package/dist/scripts/postinstall.d.ts +4 -6
  127. package/dist/scripts/postinstall.d.ts.map +1 -1
  128. package/dist/scripts/postinstall.js +8 -41
  129. package/dist/scripts/postinstall.js.map +1 -1
  130. package/dist/self-host-compose.d.ts.map +1 -1
  131. package/dist/self-host-compose.js +23 -13
  132. package/dist/self-host-compose.js.map +1 -1
  133. package/dist/studio-dev-server.d.ts +4 -2
  134. package/dist/studio-dev-server.d.ts.map +1 -1
  135. package/dist/studio-dev-server.js +3 -2
  136. package/dist/studio-dev-server.js.map +1 -1
  137. package/dist/ui/brand.d.ts +2 -5
  138. package/dist/ui/brand.d.ts.map +1 -1
  139. package/dist/ui/brand.js +2 -7
  140. package/dist/ui/brand.js.map +1 -1
  141. package/dist/ui/clack-api.d.ts +44 -0
  142. package/dist/ui/clack-api.d.ts.map +1 -0
  143. package/dist/ui/clack-api.js +76 -0
  144. package/dist/ui/clack-api.js.map +1 -0
  145. package/dist/ui/clack.d.ts +14 -0
  146. package/dist/ui/clack.d.ts.map +1 -0
  147. package/dist/ui/clack.js +147 -0
  148. package/dist/ui/clack.js.map +1 -0
  149. package/dist/ui/components/FlowSpinner.d.ts +5 -0
  150. package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
  151. package/dist/ui/components/FlowSpinner.js +8 -0
  152. package/dist/ui/components/FlowSpinner.js.map +1 -0
  153. package/dist/ui/components/LogoWordmark.d.ts +9 -0
  154. package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
  155. package/dist/ui/components/LogoWordmark.js +17 -0
  156. package/dist/ui/components/LogoWordmark.js.map +1 -0
  157. package/dist/ui/components/PromptPanel.d.ts +5 -0
  158. package/dist/ui/components/PromptPanel.d.ts.map +1 -0
  159. package/dist/ui/components/PromptPanel.js +7 -0
  160. package/dist/ui/components/PromptPanel.js.map +1 -0
  161. package/dist/ui/components/SelectField.d.ts +11 -0
  162. package/dist/ui/components/SelectField.d.ts.map +1 -0
  163. package/dist/ui/components/SelectField.js +19 -0
  164. package/dist/ui/components/SelectField.js.map +1 -0
  165. package/dist/ui/confirm.d.ts +2 -1
  166. package/dist/ui/confirm.d.ts.map +1 -1
  167. package/dist/ui/confirm.js +4 -4
  168. package/dist/ui/confirm.js.map +1 -1
  169. package/dist/ui/dev/DevDashboard.d.ts +8 -0
  170. package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
  171. package/dist/ui/dev/DevDashboard.js +158 -0
  172. package/dist/ui/dev/DevDashboard.js.map +1 -0
  173. package/dist/ui/dev/DevReadyPanelView.d.ts +8 -0
  174. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
  175. package/dist/ui/dev/DevReadyPanelView.js +48 -0
  176. package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
  177. package/dist/ui/fatal.d.ts.map +1 -1
  178. package/dist/ui/fatal.js +5 -11
  179. package/dist/ui/fatal.js.map +1 -1
  180. package/dist/ui/flows/FlowApp.d.ts +8 -0
  181. package/dist/ui/flows/FlowApp.d.ts.map +1 -0
  182. package/dist/ui/flows/FlowApp.js +44 -0
  183. package/dist/ui/flows/FlowApp.js.map +1 -0
  184. package/dist/ui/flows/prompt-fields.d.ts +48 -0
  185. package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
  186. package/dist/ui/flows/prompt-fields.js +92 -0
  187. package/dist/ui/flows/prompt-fields.js.map +1 -0
  188. package/dist/ui/flows/types.d.ts +41 -0
  189. package/dist/ui/flows/types.d.ts.map +1 -0
  190. package/dist/ui/flows/types.js +2 -0
  191. package/dist/ui/flows/types.js.map +1 -0
  192. package/dist/ui/index.d.ts +1 -1
  193. package/dist/ui/index.js +1 -1
  194. package/dist/ui/messages.d.ts +1 -0
  195. package/dist/ui/messages.d.ts.map +1 -1
  196. package/dist/ui/messages.js +48 -8
  197. package/dist/ui/messages.js.map +1 -1
  198. package/dist/ui/progress.d.ts +5 -1
  199. package/dist/ui/progress.d.ts.map +1 -1
  200. package/dist/ui/progress.js +12 -3
  201. package/dist/ui/progress.js.map +1 -1
  202. package/dist/ui/prompts.d.ts +7 -5
  203. package/dist/ui/prompts.d.ts.map +1 -1
  204. package/dist/ui/prompts.js +15 -8
  205. package/dist/ui/prompts.js.map +1 -1
  206. package/dist/ui/runtime/cancel.d.ts +4 -0
  207. package/dist/ui/runtime/cancel.d.ts.map +1 -0
  208. package/dist/ui/runtime/cancel.js +6 -0
  209. package/dist/ui/runtime/cancel.js.map +1 -0
  210. package/dist/ui/runtime/command-chrome.d.ts +17 -0
  211. package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
  212. package/dist/ui/runtime/command-chrome.js +123 -0
  213. package/dist/ui/runtime/command-chrome.js.map +1 -0
  214. package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
  215. package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
  216. package/dist/ui/runtime/dev-prompt-queue.js +57 -0
  217. package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
  218. package/dist/ui/runtime/flow-session.d.ts +4 -0
  219. package/dist/ui/runtime/flow-session.d.ts.map +1 -0
  220. package/dist/ui/runtime/flow-session.js +8 -0
  221. package/dist/ui/runtime/flow-session.js.map +1 -0
  222. package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
  223. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
  224. package/dist/ui/runtime/run-clack-flow.js +46 -0
  225. package/dist/ui/runtime/run-clack-flow.js.map +1 -0
  226. package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
  227. package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
  228. package/dist/ui/runtime/stdin-after-ink.js +16 -0
  229. package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
  230. package/dist/ui/theme.d.ts +24 -0
  231. package/dist/ui/theme.d.ts.map +1 -0
  232. package/dist/ui/theme.js +34 -0
  233. package/dist/ui/theme.js.map +1 -0
  234. package/dist/valkey-sidecar.d.ts +17 -0
  235. package/dist/valkey-sidecar.d.ts.map +1 -0
  236. package/dist/valkey-sidecar.js +94 -0
  237. package/dist/valkey-sidecar.js.map +1 -0
  238. package/package.json +9 -3
  239. package/src/binary-cache.ts +61 -0
  240. package/src/binary-download-lock.ts +124 -0
  241. package/src/cli-package-version.ts +15 -0
  242. package/src/cli.ts +6 -2
  243. package/src/commands/add.ts +26 -19
  244. package/src/commands/admin.ts +156 -29
  245. package/src/commands/cache.ts +105 -5
  246. package/src/commands/dev.ts +26 -21
  247. package/src/commands/init.ts +473 -247
  248. package/src/commands/link-helpers.ts +30 -29
  249. package/src/compose-local-server-image.ts +68 -0
  250. package/src/compose-rename.ts +5 -8
  251. package/src/config.ts +13 -1
  252. package/src/dev-compose.ts +139 -54
  253. package/src/dev-log-bus.ts +20 -0
  254. package/src/dev-log-filter.ts +42 -0
  255. package/src/dev-ports.ts +22 -11
  256. package/src/dev-ready-panel.ts +80 -0
  257. package/src/dev-session-lock.ts +5 -8
  258. package/src/dev-session.ts +98 -18
  259. package/src/dev-shutdown.ts +29 -17
  260. package/src/dev-task-colors.ts +22 -3
  261. package/src/docker-runtime.ts +1 -1
  262. package/src/engine-push-output.ts +5 -1
  263. package/src/ensure-binary.ts +21 -1
  264. package/src/ensure-component-binaries.ts +71 -0
  265. package/src/ensure-dev-api-config.ts +18 -0
  266. package/src/init-dependency-versions.ts +69 -0
  267. package/src/init-package-json.ts +63 -0
  268. package/src/init-project-detect.ts +99 -0
  269. package/src/postgres-ctl.ts +2 -1
  270. package/src/required-host-components.ts +37 -0
  271. package/src/resolve-api-url.ts +43 -0
  272. package/src/rest-cache-admin.ts +135 -0
  273. package/src/scripts/postinstall.ts +9 -46
  274. package/src/self-host-compose.ts +22 -12
  275. package/src/studio-dev-server.ts +5 -4
  276. package/src/ui/README.md +13 -9
  277. package/src/ui/brand.ts +2 -8
  278. package/src/ui/clack-api.ts +123 -0
  279. package/src/ui/clack.ts +153 -0
  280. package/src/ui/components/FlowSpinner.tsx +15 -0
  281. package/src/ui/components/LogoWordmark.tsx +36 -0
  282. package/src/ui/components/PromptPanel.tsx +17 -0
  283. package/src/ui/components/SelectField.tsx +58 -0
  284. package/src/ui/confirm.ts +5 -4
  285. package/src/ui/dev/DevDashboard.tsx +223 -0
  286. package/src/ui/dev/DevReadyPanelView.tsx +134 -0
  287. package/src/ui/fatal.ts +5 -10
  288. package/src/ui/flows/FlowApp.tsx +69 -0
  289. package/src/ui/flows/prompt-fields.tsx +198 -0
  290. package/src/ui/flows/types.ts +50 -0
  291. package/src/ui/index.ts +1 -1
  292. package/src/ui/messages.ts +50 -8
  293. package/src/ui/progress.ts +14 -3
  294. package/src/ui/prompts.ts +17 -9
  295. package/src/ui/runtime/cancel.ts +6 -0
  296. package/src/ui/runtime/command-chrome.ts +147 -0
  297. package/src/ui/runtime/dev-prompt-queue.ts +84 -0
  298. package/src/ui/runtime/flow-session.ts +11 -0
  299. package/src/ui/runtime/run-clack-flow.tsx +65 -0
  300. package/src/ui/runtime/stdin-after-ink.ts +18 -0
  301. package/src/ui/theme.ts +41 -0
  302. package/src/valkey-sidecar.ts +126 -0
  303. package/tests/admin-ensure.test.ts +80 -0
  304. package/tests/binary-cache-cloud-overrides.test.ts +2 -2
  305. package/tests/binary-download-lock.test.ts +41 -0
  306. package/tests/cli-help.test.ts +7 -4
  307. package/tests/cli-package-version.test.ts +15 -0
  308. package/tests/command-chrome-coverage.test.ts +149 -0
  309. package/tests/command-chrome-wrap.test.ts +51 -0
  310. package/tests/config.test.ts +27 -4
  311. package/tests/dev-ready-panel.test.ts +37 -0
  312. package/tests/dev-ui.test.ts +42 -2
  313. package/tests/docker-runtime.test.ts +9 -20
  314. package/tests/ensure-dev-api-config.test.ts +22 -0
  315. package/tests/init-dependency-versions.test.ts +62 -0
  316. package/tests/init-package-json.test.ts +18 -0
  317. package/tests/init-project-detect.test.ts +53 -0
  318. package/tests/init.test.ts +30 -3
  319. package/tests/required-host-components.test.ts +51 -0
  320. package/tests/resolve-api-url.test.ts +23 -0
  321. package/tests/rest-cache-admin.test.ts +28 -0
  322. package/tests/runtime-contract.test.ts +12 -3
  323. package/tests/stdin-after-ink.test.ts +8 -0
  324. package/tests/tsconfig.json +8 -2
  325. package/tests/ui-confirm.test.ts +13 -9
  326. package/tests/ui-messages.test.ts +4 -18
  327. package/tsconfig.json +1 -0
  328. package/tsconfig.tsbuildinfo +1 -1
  329. package/vitest.config.ts +3 -0
  330. package/dist/dev-tui.d.ts +0 -24
  331. package/dist/dev-tui.d.ts.map +0 -1
  332. package/dist/dev-tui.js +0 -188
  333. package/dist/dev-tui.js.map +0 -1
  334. package/src/dev-tui.ts +0 -232
package/src/ui/brand.ts CHANGED
@@ -1,12 +1,6 @@
1
1
  /**
2
2
  * Shared ANSI styling for Supatype CLI output and the dev TUI.
3
+ * @deprecated Prefer `theme.ts` for new code — re-exports kept for dev-logo / task colors.
3
4
  */
4
5
 
5
- export const RESET = "\x1b[0m"
6
- export const DIM = "\x1b[2m"
7
- export const BOLD = "\x1b[1m"
8
- export const BRAND_COLOR = "\x1b[35m"
9
-
10
- export function brandStyle(text: string): string {
11
- return `${BRAND_COLOR}${BOLD}${text}${RESET}`
12
- }
6
+ export { BOLD, BRAND_COLOR, brandStyle, DIM, RESET } from "./theme.js"
@@ -0,0 +1,123 @@
1
+ import type { FlowController } from "./flows/types.js"
2
+ import type { DevSelectOption } from "./runtime/dev-prompt-queue.js"
3
+ import { CLACK_CANCEL } from "./runtime/cancel.js"
4
+
5
+ export interface ClackTextOptions {
6
+ message: string
7
+ defaultValue?: string
8
+ placeholder?: string
9
+ validate?: (value: string) => string | undefined
10
+ }
11
+
12
+ export interface ClackSelectOptions<T extends string = string> {
13
+ message: string
14
+ options: DevSelectOption<T>[]
15
+ initialValue?: T
16
+ }
17
+
18
+ export interface ClackConfirmOptions {
19
+ message: string
20
+ initialValue?: boolean
21
+ }
22
+
23
+ export interface ClackPasswordOptions {
24
+ message: string
25
+ }
26
+
27
+ export interface ClackSpinner {
28
+ start(message: string): void
29
+ stop(message: string): void
30
+ }
31
+
32
+ export interface ClackApi {
33
+ intro(message: string): void
34
+ outro(message: string): void
35
+ note(message: string): void
36
+ cancel(message: string): never
37
+ text(options: ClackTextOptions): Promise<string | typeof CLACK_CANCEL>
38
+ password(options: ClackPasswordOptions): Promise<string | typeof CLACK_CANCEL>
39
+ select<T extends string>(options: ClackSelectOptions<T>): Promise<T | typeof CLACK_CANCEL>
40
+ confirm(options: ClackConfirmOptions): Promise<boolean | typeof CLACK_CANCEL>
41
+ spinner(): ClackSpinner
42
+ log: {
43
+ info(message: string): void
44
+ warn(message: string): void
45
+ error(message: string): void
46
+ success(message: string): void
47
+ }
48
+ }
49
+
50
+ export function createClackApi(controller: FlowController): ClackApi {
51
+ return {
52
+ intro(message) {
53
+ controller.appendLog("plain", message)
54
+ },
55
+ outro(message) {
56
+ controller.appendLog("success", message)
57
+ },
58
+ note(message) {
59
+ for (const line of message.split("\n")) {
60
+ if (line.trim()) controller.appendPlain(line)
61
+ }
62
+ },
63
+ cancel(message): never {
64
+ controller.appendLog("warn", message)
65
+ process.exit(0)
66
+ },
67
+ async text(options) {
68
+ return controller.waitForPrompt<string>({
69
+ kind: "text",
70
+ message: options.message,
71
+ ...(options.defaultValue !== undefined ? { defaultValue: options.defaultValue } : {}),
72
+ ...(options.placeholder !== undefined ? { placeholder: options.placeholder } : {}),
73
+ ...(options.validate !== undefined ? { validate: options.validate } : {}),
74
+ })
75
+ },
76
+ async password(options) {
77
+ return controller.waitForPrompt<string>({
78
+ kind: "password",
79
+ message: options.message,
80
+ })
81
+ },
82
+ async select<T extends string>(options: ClackSelectOptions<T>) {
83
+ return controller.waitForPrompt<T>({
84
+ kind: "select",
85
+ message: options.message,
86
+ options: options.options,
87
+ ...(options.initialValue !== undefined ? { initialValue: options.initialValue } : {}),
88
+ })
89
+ },
90
+ async confirm(options) {
91
+ return controller.waitForPrompt<boolean>({
92
+ kind: "confirm",
93
+ message: options.message,
94
+ initialValue: options.initialValue ?? false,
95
+ })
96
+ },
97
+ spinner() {
98
+ return {
99
+ start(message: string) {
100
+ controller.setSpinner(message)
101
+ },
102
+ stop(message: string) {
103
+ controller.setSpinner(null)
104
+ controller.appendLog("success", message)
105
+ },
106
+ }
107
+ },
108
+ log: {
109
+ info(message) {
110
+ controller.appendLog("info", message)
111
+ },
112
+ warn(message) {
113
+ controller.appendLog("warn", message)
114
+ },
115
+ error(message) {
116
+ controller.appendLog("error", message)
117
+ },
118
+ success(message) {
119
+ controller.appendLog("success", message)
120
+ },
121
+ },
122
+ }
123
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Clack-compatible prompt API backed by Ink.
3
+ * Use inside `runClackFlow()` for multi-step wizards, or via `confirm()` / `promptText()` helpers.
4
+ */
5
+
6
+ import { getActiveDevSession } from "../dev-session.js"
7
+ import { createClackApi, type ClackApi } from "./clack-api.js"
8
+ import { isInteractive } from "./interactive.js"
9
+ import { CLACK_CANCEL, isCancel } from "./runtime/cancel.js"
10
+ import { enqueueDevPrompt } from "./runtime/dev-prompt-queue.js"
11
+ import { runClackFlow as mountClackFlow } from "./runtime/run-clack-flow.js"
12
+ import type { DevSelectOption } from "./runtime/dev-prompt-queue.js"
13
+
14
+ export { CLACK_CANCEL, isCancel }
15
+ export type { ClackApi } from "./clack-api.js"
16
+
17
+ import { getActiveFlowApi, setActiveFlowApi } from "./runtime/flow-session.js"
18
+
19
+ function inDevTui(): boolean {
20
+ const session = getActiveDevSession()
21
+ return Boolean(session?.isTui() && session.isInkMounted())
22
+ }
23
+
24
+ async function devOrFlow<T>(
25
+ spec: Parameters<typeof enqueueDevPrompt>[0],
26
+ flowFn: (api: ClackApi) => Promise<T>,
27
+ ): Promise<T | typeof CLACK_CANCEL> {
28
+ if (inDevTui()) {
29
+ return enqueueDevPrompt(spec) as Promise<T | typeof CLACK_CANCEL>
30
+ }
31
+ const api = getActiveFlowApi()
32
+ if (api) {
33
+ return flowFn(api)
34
+ }
35
+ return mountClackFlow(async (flowApi) => flowFn(flowApi))
36
+ }
37
+
38
+ export async function runClackFlow<T>(run: (api: ClackApi) => Promise<T>): Promise<T> {
39
+ return mountClackFlow(async (api) => {
40
+ setActiveFlowApi(api)
41
+ try {
42
+ return await run(api)
43
+ } finally {
44
+ setActiveFlowApi(null)
45
+ }
46
+ })
47
+ }
48
+
49
+ export { getActiveFlowApi } from "./runtime/flow-session.js"
50
+
51
+ export const p: ClackApi = {
52
+ intro(message) {
53
+ const api = getActiveFlowApi()
54
+ if (api) api.intro(message)
55
+ else console.log(`\n${message}\n`)
56
+ },
57
+ outro(message) {
58
+ const api = getActiveFlowApi()
59
+ if (api) api.outro(message)
60
+ else console.log(`\n${message}\n`)
61
+ },
62
+ note(message) {
63
+ const api = getActiveFlowApi()
64
+ if (api) api.note(message)
65
+ else {
66
+ for (const line of message.split("\n")) {
67
+ if (line.trim()) console.log(` ${line}`)
68
+ }
69
+ }
70
+ },
71
+ cancel(message): never {
72
+ const api = getActiveFlowApi()
73
+ if (api) {
74
+ api.cancel(message)
75
+ }
76
+ console.log(message)
77
+ process.exit(0)
78
+ },
79
+ async text(options) {
80
+ return devOrFlow(
81
+ {
82
+ kind: "text",
83
+ message: options.message,
84
+ ...(options.defaultValue !== undefined ? { defaultValue: options.defaultValue } : {}),
85
+ ...(options.placeholder !== undefined ? { placeholder: options.placeholder } : {}),
86
+ ...(options.validate !== undefined ? { validate: options.validate } : {}),
87
+ },
88
+ (api) => api.text(options),
89
+ )
90
+ },
91
+ async password(options) {
92
+ return devOrFlow({ kind: "password", message: options.message }, (api) => api.password(options))
93
+ },
94
+ async select<T extends string>(options: { message: string; options: DevSelectOption<T>[]; initialValue?: T }) {
95
+ return devOrFlow(
96
+ {
97
+ kind: "select",
98
+ message: options.message,
99
+ options: options.options,
100
+ ...(options.initialValue !== undefined ? { initialValue: options.initialValue as string | boolean } : {}),
101
+ },
102
+ (api) => api.select(options),
103
+ )
104
+ },
105
+ async confirm(options) {
106
+ return devOrFlow(
107
+ {
108
+ kind: "confirm",
109
+ message: options.message,
110
+ initialValue: options.initialValue ?? false,
111
+ },
112
+ (api) => api.confirm(options),
113
+ )
114
+ },
115
+ spinner() {
116
+ const api = getActiveFlowApi()
117
+ if (api) return api.spinner()
118
+ return {
119
+ start(message: string) {
120
+ console.log(`${message}...`)
121
+ },
122
+ stop(message: string) {
123
+ console.log(message)
124
+ },
125
+ }
126
+ },
127
+ log: {
128
+ info(message) {
129
+ const api = getActiveFlowApi()
130
+ if (api) api.log.info(message)
131
+ else if (isInteractive()) console.log(message)
132
+ else console.log(`[supatype] ${message}`)
133
+ },
134
+ warn(message) {
135
+ const api = getActiveFlowApi()
136
+ if (api) api.log.warn(message)
137
+ else console.warn(`[supatype] ${message}`)
138
+ },
139
+ error(message) {
140
+ const api = getActiveFlowApi()
141
+ if (api) api.log.error(message)
142
+ else console.error(`[supatype] ${message}`)
143
+ },
144
+ success(message) {
145
+ const api = getActiveFlowApi()
146
+ if (api) api.log.success(message)
147
+ else console.log(`[supatype] ${message}`)
148
+ },
149
+ },
150
+ }
151
+
152
+ /** @deprecated Use `p` from this module inside `runClackFlow`. */
153
+ export { p as clack }
@@ -0,0 +1,15 @@
1
+ import React from "react"
2
+ import { Box, Text } from "ink"
3
+ import Spinner from "ink-spinner"
4
+ import { theme } from "../theme.js"
5
+
6
+ export function FlowSpinner({ message }: { message: string }): React.ReactElement {
7
+ return (
8
+ <Box marginBottom={1}>
9
+ <Text color={theme.brand}>
10
+ <Spinner type="dots" />
11
+ {` ${message}`}
12
+ </Text>
13
+ </Box>
14
+ )
15
+ }
@@ -0,0 +1,36 @@
1
+ import React from "react"
2
+ import { Box, Text } from "ink"
3
+ import { layoutLogoBlock, pickLogoLines } from "../../dev-logo.js"
4
+ import { theme } from "../theme.js"
5
+
6
+ function truncatePlain(text: string, width: number): string {
7
+ if (text.length <= width) return text
8
+ if (width <= 1) return text.slice(0, width)
9
+ return `${text.slice(0, width - 1)}…`
10
+ }
11
+
12
+ export interface LogoWordmarkProps {
13
+ maxWidth?: number
14
+ /** Small caption under the figlet wordmark. */
15
+ tagline?: string
16
+ }
17
+
18
+ /** Figlet slant wordmark — native Ink colours (no embedded ANSI). */
19
+ export function LogoWordmark({ maxWidth, tagline }: LogoWordmarkProps): React.ReactElement {
20
+ const lines = layoutLogoBlock(pickLogoLines())
21
+
22
+ return (
23
+ <Box flexDirection="column" marginBottom={tagline ? 0 : 1}>
24
+ {lines.map((line, index) => (
25
+ <Text key={`logo-${index}`} bold color={theme.brand}>
26
+ {maxWidth !== undefined ? truncatePlain(line, maxWidth) : line}
27
+ </Text>
28
+ ))}
29
+ {tagline ? (
30
+ <Text dimColor color={theme.brand}>
31
+ {tagline}
32
+ </Text>
33
+ ) : null}
34
+ </Box>
35
+ )
36
+ }
@@ -0,0 +1,17 @@
1
+ import React, { type ReactNode } from "react"
2
+ import { Box } from "ink"
3
+ import { theme } from "../theme.js"
4
+
5
+ export function PromptPanel({ children }: { children: ReactNode }): React.ReactElement {
6
+ return (
7
+ <Box
8
+ marginTop={1}
9
+ borderStyle="round"
10
+ borderColor={theme.brand}
11
+ paddingX={1}
12
+ flexDirection="column"
13
+ >
14
+ {children}
15
+ </Box>
16
+ )
17
+ }
@@ -0,0 +1,58 @@
1
+ import React, { type FC } from "react"
2
+ import { Box, Text, useInput } from "ink"
3
+ import SelectInput from "ink-select-input"
4
+ import { theme } from "../theme.js"
5
+ import type { DevSelectOption } from "../runtime/dev-prompt-queue.js"
6
+
7
+ export interface SelectFieldProps {
8
+ message: string
9
+ options: DevSelectOption[]
10
+ initialValue?: string
11
+ onSubmit: (value: string) => void
12
+ onCancel: () => void
13
+ }
14
+
15
+ const BrandIndicator: FC<{ isSelected?: boolean }> = ({ isSelected }) => (
16
+ <Text {...(isSelected ? { color: theme.brand, bold: true } : { dimColor: true })}>
17
+ {isSelected ? "▸ " : " "}
18
+ </Text>
19
+ )
20
+
21
+ const BrandItem: FC<{ isSelected?: boolean; label: string }> = ({ isSelected, label }) => (
22
+ <Text {...(isSelected ? { color: theme.brand, bold: true } : {})}>{label}</Text>
23
+ )
24
+
25
+ export function SelectField({
26
+ message,
27
+ options,
28
+ initialValue,
29
+ onSubmit,
30
+ onCancel,
31
+ }: SelectFieldProps): React.ReactElement {
32
+ useInput((_input, key) => {
33
+ if (key.escape) onCancel()
34
+ })
35
+
36
+ const items = options.map((opt) => ({
37
+ label: opt.hint ? `${opt.label} — ${opt.hint}` : opt.label,
38
+ value: opt.value,
39
+ }))
40
+
41
+ const foundIndex = items.findIndex((item) => item.value === initialValue)
42
+
43
+ return (
44
+ <Box flexDirection="column">
45
+ <Text bold color={theme.brand}>
46
+ {message}
47
+ </Text>
48
+ <SelectInput
49
+ items={items}
50
+ {...(foundIndex >= 0 ? { initialIndex: foundIndex } : {})}
51
+ indicatorComponent={BrandIndicator}
52
+ itemComponent={BrandItem}
53
+ onSelect={(item) => onSubmit(item.value)}
54
+ />
55
+ <Text dimColor>↑/↓ or j/k · enter · esc cancel · 1-9 quick pick</Text>
56
+ </Box>
57
+ )
58
+ }
package/src/ui/confirm.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as p from "@clack/prompts"
2
1
  import { isInteractive } from "./interactive.js"
2
+ import { CLACK_CANCEL, isCancel, p } from "./clack.js"
3
3
  import { plain } from "./messages.js"
4
4
 
5
5
  export interface ConfirmOptions {
@@ -9,7 +9,7 @@ export interface ConfirmOptions {
9
9
  }
10
10
 
11
11
  /**
12
- * Yes/no confirmation — Clack in TTY, plain prompt or default otherwise.
12
+ * Yes/no confirmation — Ink overlay in dev, Ink flow in TTY, default otherwise.
13
13
  */
14
14
  export async function confirm(message: string, opts: ConfirmOptions = {}): Promise<boolean> {
15
15
  const fallback = opts.nonInteractive ?? opts.default ?? false
@@ -24,9 +24,8 @@ export async function confirm(message: string, opts: ConfirmOptions = {}): Promi
24
24
  initialValue: opts.default ?? false,
25
25
  })
26
26
 
27
- if (p.isCancel(value)) {
27
+ if (isCancel(value)) {
28
28
  p.cancel("Cancelled.")
29
- process.exit(0)
30
29
  }
31
30
 
32
31
  return value
@@ -36,3 +35,5 @@ export async function confirm(message: string, opts: ConfirmOptions = {}): Promi
36
35
  export function logSkippedConfirm(reason: string): void {
37
36
  plain(`${reason} (use --yes to skip confirmation)`)
38
37
  }
38
+
39
+ export { isCancel, CLACK_CANCEL } from "./clack.js"
@@ -0,0 +1,223 @@
1
+ import React, { useEffect, useState } from "react"
2
+ import { Box, Text, useInput, useStdout } from "ink"
3
+ import type { DevLogBus } from "../../dev-log-bus.js"
4
+ import { stripAnsi } from "../../dev-log-filter.js"
5
+ import { logoRowCount } from "../../dev-logo.js"
6
+ import { logInkColor, taskInkColor } from "../../dev-task-colors.js"
7
+ import { LogoWordmark } from "../components/LogoWordmark.js"
8
+ import { PromptPanel } from "../components/PromptPanel.js"
9
+ import { DevReadyPanelView } from "./DevReadyPanelView.js"
10
+ import { theme } from "../theme.js"
11
+ import { requestDevShutdown } from "../../dev-shutdown.js"
12
+ import {
13
+ getActiveDevPrompt,
14
+ resolveDevPrompt,
15
+ subscribeDevPrompts,
16
+ } from "../runtime/dev-prompt-queue.js"
17
+ import { CLACK_CANCEL } from "../runtime/cancel.js"
18
+ import { ActiveFlowPromptView } from "../flows/prompt-fields.js"
19
+ import type { ActiveFlowPrompt } from "../flows/types.js"
20
+
21
+ const TASK_COL_WIDTH = 22
22
+ const MIN_WIDTH = 60
23
+ const MIN_HEIGHT = 14
24
+ const TAGLINE = "local development"
25
+ const CHROME_ABOVE_LOG = 2 // separator + keybindings
26
+ const CHROME_BELOW_LOG = 2 // footer separator + focused line
27
+ const MIN_LOG_ROWS = 3
28
+
29
+ function truncate(text: string, width: number): string {
30
+ if (width <= 0) return ""
31
+ if (text.length <= width) return text
32
+ if (width <= 1) return text.slice(0, width)
33
+ return `${text.slice(0, width - 1)}…`
34
+ }
35
+
36
+ function padPlain(text: string, width: number): string {
37
+ if (text.length >= width) return text
38
+ return text + " ".repeat(width - text.length)
39
+ }
40
+
41
+ function devPromptToFlowPrompt(prompt: NonNullable<ReturnType<typeof getActiveDevPrompt>>): ActiveFlowPrompt {
42
+ switch (prompt.kind) {
43
+ case "confirm":
44
+ return {
45
+ kind: "confirm",
46
+ message: prompt.message,
47
+ initialValue: typeof prompt.initialValue === "boolean" ? prompt.initialValue : false,
48
+ }
49
+ case "text":
50
+ return {
51
+ kind: "text",
52
+ message: prompt.message,
53
+ ...(prompt.defaultValue !== undefined ? { defaultValue: prompt.defaultValue } : {}),
54
+ ...(prompt.placeholder !== undefined ? { placeholder: prompt.placeholder } : {}),
55
+ ...(prompt.validate !== undefined ? { validate: prompt.validate } : {}),
56
+ }
57
+ case "password":
58
+ return { kind: "password", message: prompt.message }
59
+ case "select":
60
+ return {
61
+ kind: "select",
62
+ message: prompt.message,
63
+ options: prompt.options ?? [],
64
+ ...(typeof prompt.initialValue === "string" ? { initialValue: prompt.initialValue } : {}),
65
+ }
66
+ default:
67
+ return { kind: "confirm", message: prompt.message, initialValue: false }
68
+ }
69
+ }
70
+
71
+ function scrollHint(scrollFromBottom: number, total: number, visible: number): string {
72
+ if (total <= visible) return "all lines visible"
73
+ if (scrollFromBottom === 0) return "latest"
74
+ if (scrollFromBottom >= total - visible) return "oldest"
75
+ return `${scrollFromBottom} lines up from latest`
76
+ }
77
+
78
+ interface DevDashboardProps {
79
+ bus: DevLogBus
80
+ }
81
+
82
+ export function DevDashboard({ bus }: DevDashboardProps): React.ReactElement {
83
+ const { stdout } = useStdout()
84
+ const cols = Math.max(MIN_WIDTH, stdout.columns ?? 80)
85
+ const rows = Math.max(MIN_HEIGHT, stdout.rows ?? 24)
86
+ const [, tick] = useState(0)
87
+ const [scrollFromBottom, setScrollFromBottom] = useState(0)
88
+ const [activePrompt, setActivePrompt] = useState(getActiveDevPrompt())
89
+
90
+ useEffect(() => {
91
+ const offBus = bus.onUpdate(() => tick((n) => n + 1))
92
+ const offPrompt = subscribeDevPrompts(() => setActivePrompt(getActiveDevPrompt()))
93
+ return () => {
94
+ offBus()
95
+ offPrompt()
96
+ }
97
+ }, [bus])
98
+
99
+ const promptOpen = activePrompt !== null
100
+ const focusedId = bus.getFocusedTaskId()
101
+ const taskIds = bus.getTaskOrder()
102
+ const readyPanel = bus.getReadyPanel()
103
+ const logoRows = logoRowCount() + 1 // figlet + tagline
104
+ const promptRows = promptOpen ? 6 : 0
105
+ const fullReadyRows = readyPanel ? bus.readyPanelRowCount(false) : 0
106
+ const availableForPanel =
107
+ rows - logoRows - CHROME_ABOVE_LOG - CHROME_BELOW_LOG - MIN_LOG_ROWS - promptRows
108
+ const useCompactPanel = readyPanel !== null && fullReadyRows > availableForPanel
109
+ const readyRows = readyPanel ? bus.readyPanelRowCount(useCompactPanel) : 0
110
+ const chromeRows = logoRows + readyRows + CHROME_ABOVE_LOG + CHROME_BELOW_LOG + promptRows
111
+ const logHeight = Math.max(0, rows - chromeRows)
112
+ const logWidth = cols - TASK_COL_WIDTH - 1
113
+
114
+ useInput((input, key) => {
115
+ if (promptOpen) return
116
+
117
+ if ((key.ctrl && input === "c") || input === "\x03") {
118
+ requestDevShutdown()
119
+ return
120
+ }
121
+
122
+ if (key.downArrow || input === "j") {
123
+ bus.focusNext()
124
+ setScrollFromBottom(0)
125
+ return
126
+ }
127
+ if (key.upArrow || input === "k") {
128
+ bus.focusPrevious()
129
+ setScrollFromBottom(0)
130
+ return
131
+ }
132
+ if (input === "u") {
133
+ setScrollFromBottom((s) =>
134
+ Math.min(s + 3, Math.max(0, (bus.getTask(focusedId)?.lines.length ?? 0) - logHeight)),
135
+ )
136
+ return
137
+ }
138
+ if (input === "d") {
139
+ setScrollFromBottom((s) => Math.max(s - 3, 0))
140
+ return
141
+ }
142
+ if (input === "g") {
143
+ setScrollFromBottom(Math.max(0, (bus.getTask(focusedId)?.lines.length ?? 0) - logHeight))
144
+ return
145
+ }
146
+ if (input === "G") {
147
+ setScrollFromBottom(0)
148
+ }
149
+ })
150
+
151
+ const taskLines = bus.getTask(focusedId)?.lines ?? []
152
+ const totalLines = taskLines.length
153
+ const end = totalLines - scrollFromBottom
154
+ const start = Math.max(0, end - logHeight)
155
+
156
+ return (
157
+ <Box flexDirection="column" width={cols} height={rows}>
158
+ <LogoWordmark maxWidth={cols} tagline={TAGLINE} />
159
+ {readyPanel ? (
160
+ <DevReadyPanelView panel={readyPanel} width={cols} compact={useCompactPanel} />
161
+ ) : null}
162
+ <Text dimColor>{"─".repeat(cols)}</Text>
163
+ <Text dimColor>
164
+ {truncate(" ↑/k ↓/j task u/d scroll g/G top/bottom Ctrl+C quit", cols)}
165
+ </Text>
166
+
167
+ {Array.from({ length: logHeight }, (_, row) => {
168
+ const taskId = taskIds[row]
169
+ const task = taskId ? bus.getTask(taskId) : undefined
170
+ let left = " ".repeat(TASK_COL_WIDTH)
171
+ if (task && taskId) {
172
+ const marker = taskId === focusedId ? "▶" : " "
173
+ const unread = task.unread ? " •" : " "
174
+ const label = truncate(`${marker} ${task.title}${unread}`, TASK_COL_WIDTH - 1)
175
+ left = padPlain(label, TASK_COL_WIDTH)
176
+ }
177
+
178
+ const index = start + row
179
+ const rawFull = index >= end || index >= totalLines ? "" : (taskLines[index] ?? "")
180
+ const raw = stripAnsi(rawFull)
181
+ const logLine = padPlain(truncate(raw, logWidth), logWidth)
182
+
183
+ return (
184
+ <Box key={`row-${row}`}>
185
+ <Text
186
+ {...(taskId === focusedId ? { bold: true } : {})}
187
+ color={taskId ? taskInkColor(taskId) : theme.dim}
188
+ >
189
+ {left}
190
+ </Text>
191
+ <Text dimColor>│</Text>
192
+ <Text
193
+ {...(raw
194
+ ? { color: logInkColor(focusedId, raw) }
195
+ : { dimColor: true })}
196
+ >
197
+ {logLine}
198
+ </Text>
199
+ </Box>
200
+ )
201
+ })}
202
+
203
+ <Text dimColor>{"─".repeat(cols)}</Text>
204
+ <Text>
205
+ <Text dimColor> focused </Text>
206
+ <Text color={taskInkColor(focusedId)} bold>
207
+ {bus.getTask(focusedId)?.title ?? focusedId}
208
+ </Text>
209
+ <Text dimColor>{` · ${scrollHint(scrollFromBottom, totalLines, logHeight)}`}</Text>
210
+ </Text>
211
+
212
+ {promptOpen && activePrompt ? (
213
+ <PromptPanel>
214
+ <ActiveFlowPromptView
215
+ prompt={devPromptToFlowPrompt(activePrompt)}
216
+ onSubmit={(value) => resolveDevPrompt(value)}
217
+ onCancel={() => resolveDevPrompt(CLACK_CANCEL)}
218
+ />
219
+ </PromptPanel>
220
+ ) : null}
221
+ </Box>
222
+ )
223
+ }