@x-otto/tui 0.0.1-alpha.0

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.
@@ -0,0 +1,304 @@
1
+ import { D as WizardRequest, L as Question, Q as InfoDialogProps, X as InfoDialog, Z as InfoDialogAction, a as TabableContextValue, c as TabableRegistry, d as useTabableContext, f as ConfirmDialog, i as Tabable, l as UseTabableOptions, n as RouteContext, o as TabableEntry, p as ConfirmDialogProps, r as RouteContextValue, s as TabableProps, t as FocusStyle, u as useTabable } from "./Tabable-B7PtwING.js";
2
+ import React from "react";
3
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
4
+ import { Key } from "ink";
5
+
6
+ //#region src/router/Router.d.ts
7
+ interface HistoryEntry {
8
+ path: string;
9
+ params: Record<string, string>;
10
+ state?: unknown;
11
+ /** goBack 恢复用 */
12
+ lastFocusId?: string;
13
+ }
14
+ interface NavigateOptions {
15
+ /** Replace the current history entry, matching Web Router/History replace semantics. */
16
+ replace?: boolean;
17
+ /** Opaque per-location state, matching Web Router location state semantics. */
18
+ state?: unknown;
19
+ /**
20
+ * TUI MemoryRouter extension: reset the in-memory stack to exactly the target location.
21
+ * Use only for workflow completion / returning focus to PromptInput, not for local nested-route close.
22
+ */
23
+ reset?: boolean;
24
+ }
25
+ interface NavigateFunction {
26
+ (to: string, opts?: NavigateOptions): void;
27
+ (delta: number): void;
28
+ }
29
+ interface RouterContextValue {
30
+ /** 路由栈 */
31
+ stack: readonly HistoryEntry[];
32
+ /** 栈顶路由路径 */
33
+ currentPath: string;
34
+ /** 当前聚焦 Tabable ID */
35
+ activeId: string | null;
36
+ /** Web Router 风格导航:navigate(to, options) / navigate(delta)。 */
37
+ navigate: NavigateFunction;
38
+ /** pop 栈顶;兼容别名,等价 navigate(-1)。 */
39
+ goBack: () => void;
40
+ /**
41
+ * 按路径确保栈中存在一个条目(不重复 push)——瞬态 overlay 显示用(RFC-280 D1)。
42
+ */
43
+ ensure: (path: string) => void;
44
+ /**
45
+ * 按路径移除栈中条目(不要求它在栈顶)——瞬态 overlay 自动消失/程序化关闭用(RFC-280 D1)。
46
+ *
47
+ * 为何不能用 goBack:瞬态浮层(如 toast 的 2s 自动消失定时器)到点时,用户可能已经打开了
48
+ * 别的面板 → 该浮层已不在栈顶,`goBack()` 会错误地弹掉**用户正在看的那个**。
49
+ *
50
+ * 语义:
51
+ * - 移除**最靠栈顶的** path 匹配条目
52
+ * - path 不在栈中 → no-op(幂等,容忍重复/竞态调用)
53
+ * - 移除的是栈顶 → 等价 goBack(含焦点恢复)
54
+ * - 移除的是中间条目 → 只抽走该条目,currentPath 与栈顶焦点不变
55
+ * - 栈长度 ≤1 时不移除(根路由 '/' 永不被弹出)
56
+ */
57
+ dismiss: (path: string) => void;
58
+ /** 编程式聚焦 */
59
+ focus: (id: string) => void;
60
+ /** 键盘调度(外层 useInput 调用,返回 true=已消费) */
61
+ handleKey: (input: string, key: Record<string, unknown>) => boolean;
62
+ }
63
+ declare function useRouter(): RouterContextValue;
64
+ declare function useRoute<S = unknown>(): {
65
+ path: string;
66
+ params: Record<string, string>;
67
+ state: S;
68
+ };
69
+ declare function matchPath(pattern: string, path: string): Record<string, string> | null;
70
+ interface RouterProps {
71
+ children: React.ReactNode;
72
+ initialPath?: string;
73
+ }
74
+ declare function Router({
75
+ children,
76
+ initialPath
77
+ }: RouterProps): _$react_jsx_runtime0.JSX.Element;
78
+ interface RouteProps {
79
+ /** 路径模式(支持 :param 和 ?query 匹配)。'*' = 始终渲染(layout)。 */
80
+ path: string;
81
+ /** 前缀匹配模式:path 是 currentPath 前缀时就渲染(嵌套 layout 用, RFC-262)。 */
82
+ prefix?: boolean;
83
+ /**
84
+ * 在栈中即渲染模式(RFC-280 D4)——path 出现在路由栈任意位置就渲染,不要求是栈顶。
85
+ *
86
+ * 用于**可并存的瞬态 overlay**:Band B overlay 彼此非互斥(agent 处理中 pause 浮层在场,
87
+ * 此时后台任务完成又弹 toast),若用精确匹配则被压在下面的 overlay 会被卸载,破坏其
88
+ * 可见性语义(RFC-280 G6)。
89
+ *
90
+ * 与 `prefix` 的区别:`prefix` 只看 `currentPath`(栈顶),无法表达「在栈中但非栈顶」。
91
+ *
92
+ * 语义分层:可见性 = 在栈中(本模式);焦点归属 = 在栈顶(Router 既有语义)。两者正交。
93
+ */
94
+ inStack?: boolean;
95
+ /** 匹配时渲染的元素。 */
96
+ element: React.ReactNode;
97
+ /** 子路由(嵌套时 layout 保持渲染)。 */
98
+ children?: React.ReactNode;
99
+ }
100
+ /**
101
+ * 声明式路由组件。
102
+ * - path='*': 始终渲染(layout route)
103
+ * - inStack=true: path 在路由栈任意位置即渲染(可并存瞬态 overlay, RFC-280 D4)
104
+ * - prefix=true: 前缀匹配(currentPath 以 path 开头时渲染)
105
+ * - 默认: 精确匹配
106
+ */
107
+ declare function Route({
108
+ path,
109
+ prefix,
110
+ inStack,
111
+ element,
112
+ children
113
+ }: RouteProps): React.ReactElement | null;
114
+ //#endregion
115
+ //#region src/components/List.d.ts
116
+ interface ListOption {
117
+ value: string;
118
+ label: string;
119
+ detail?: string;
120
+ disabled?: boolean;
121
+ }
122
+ interface ListProps {
123
+ /** -1=不可Tab 0+=Tab 顺序(传给 useTabable) */
124
+ tabIndex?: number;
125
+ /** 路由激活时自动聚焦 */
126
+ autoFocus?: boolean;
127
+ /** 选项列表 */
128
+ options: ListOption[];
129
+ /** 选中条目回调(Enter 触发) */
130
+ onSelect: (value: string) => void;
131
+ /** 可选:初始选中 index */
132
+ initialIndex?: number;
133
+ /** 可见行数上限(超出滚动),缺省 8 */
134
+ maxVisible?: number;
135
+ /** 手动指定 ID */
136
+ id?: string;
137
+ /** 紧凑模式:每项只占 1 行,不在条目之间插入空行。 */
138
+ compact?: boolean;
139
+ }
140
+ declare function List({
141
+ tabIndex,
142
+ autoFocus,
143
+ options,
144
+ onSelect,
145
+ initialIndex,
146
+ maxVisible,
147
+ id,
148
+ compact
149
+ }: ListProps): React.ReactElement;
150
+ interface ListItemProps {
151
+ label: string;
152
+ detail?: string;
153
+ disabled?: boolean;
154
+ children?: React.ReactNode;
155
+ }
156
+ /**
157
+ * <ListItem> — 在 <List> 外单独使用时的条目渲染(纯展示,不含焦点逻辑)。
158
+ */
159
+ declare function ListItem({
160
+ label,
161
+ detail,
162
+ disabled,
163
+ children
164
+ }: ListItemProps): React.ReactElement;
165
+ //#endregion
166
+ //#region src/router/FocusBus.d.ts
167
+ type KeyHandler = (input: string, key: Key) => boolean | void;
168
+ declare class FocusBus {
169
+ private entries;
170
+ /** 注册 handler,返回注销函数 */
171
+ register(id: string, handler: KeyHandler, priority: number): () => void;
172
+ /** 按优先级派发。handler 返回 true = 已消费;false = 冒泡。 */
173
+ dispatch(input: string, key: Key): boolean;
174
+ /** 调试用:列出当前注册的 handler */
175
+ debug(): string[];
176
+ }
177
+ declare const PRIORITY_FOCUSED = 100;
178
+ /** @deprecated 语义已拆分(RFC-281 D1)。保留为 PRIORITY_ROUTER_INTERCEPT 别名以兼容既有 import。 */
179
+ declare const PRIORITY_ROUTER = 50;
180
+ declare const PRIORITY_FALLBACK = 10;
181
+ interface FocusBusContextValue {
182
+ focusBus: FocusBus;
183
+ }
184
+ declare function FocusBusProvider({
185
+ children
186
+ }: {
187
+ children: React.ReactNode;
188
+ }): React.ReactElement;
189
+ declare function useFocusBus(): FocusBus;
190
+ /**
191
+ * 组件注册键盘 handler——只有 focused=true 时向 FocusBus 注册。
192
+ *
193
+ * 对照浏览器:只有 activeElement 才收到 keydown。
194
+ *
195
+ * handler 返回 true = 已消费(stopPropagation)。
196
+ * handler 返回 false = 不消费(冒泡到下一优先级)。
197
+ *
198
+ * 用法:
199
+ * const { focused } = useTabable({ tabIndex: 0 })
200
+ * useTabableInput(focused, (input, key) => {
201
+ * if (key.return) { submit(); return true }
202
+ * if (key.upArrow) return true // 吞掉
203
+ * return false // 冒泡
204
+ * })
205
+ */
206
+ declare function useTabableInput(focused: boolean, handler: KeyHandler, id?: string): void;
207
+ declare function useFallbackInput(id: string, handler: KeyHandler, opts?: {
208
+ isActive?: boolean;
209
+ ignoreRoute?: boolean;
210
+ }): void;
211
+ //#endregion
212
+ //#region src/dialogs/WizardDialog.d.ts
213
+ interface WizardDialogProps {
214
+ request: WizardRequest;
215
+ }
216
+ declare function WizardDialog({
217
+ request
218
+ }: WizardDialogProps): React.ReactElement;
219
+ //#endregion
220
+ //#region src/dialogs/ProgressDialog.d.ts
221
+ interface ProgressDialogProps {
222
+ title: string;
223
+ phases: string[];
224
+ phaseIndex: number;
225
+ phaseStatuses: Array<'pending' | 'active' | 'done' | 'error'>;
226
+ percent: number;
227
+ detail: string;
228
+ subProgress?: {
229
+ current: number;
230
+ total: number;
231
+ };
232
+ /** RFC-286 D2:四态。`partial` 语义见 `ProgressUpdate.status` 的 JSDoc。 */
233
+ status: 'running' | 'success' | 'partial' | 'error';
234
+ errorMessage?: string;
235
+ /** RFC-290 D2:终态「下一步」建议,由调用方结构化下发;组件零推断(见 ProgressUpdate JSDoc)。 */
236
+ nextStep?: {
237
+ kind: 'force' | 'reload';
238
+ label: string;
239
+ };
240
+ onDismiss: () => void;
241
+ }
242
+ declare function ProgressDialog({
243
+ title,
244
+ phases,
245
+ phaseStatuses,
246
+ percent,
247
+ detail,
248
+ subProgress,
249
+ status,
250
+ errorMessage,
251
+ nextStep,
252
+ onDismiss
253
+ }: ProgressDialogProps): React.ReactElement;
254
+ //#endregion
255
+ //#region src/dialogs/QuestionsDialog.d.ts
256
+ interface QuestionsDialogProps {
257
+ title?: string;
258
+ questions: Question[];
259
+ /** 提交回调(答案:{ [header]: 选中项 label 数组 } + freeformText 补充文字)。 */
260
+ onSubmit: (answers: Record<string, string[]>, freeformText?: string) => void;
261
+ /** 取消回调(Esc)。 */
262
+ onCancel?: () => void;
263
+ /** 补充文本输入框可用列宽(缺省 60,对齐 InputDialog 默认值;调用方按终端宽度动态传入)。 */
264
+ columns?: number;
265
+ }
266
+ declare function QuestionsDialog({
267
+ title,
268
+ questions,
269
+ onSubmit,
270
+ onCancel,
271
+ columns
272
+ }: QuestionsDialogProps): React.ReactElement;
273
+ //#endregion
274
+ //#region src/standalone.d.ts
275
+ interface StandaloneTuiOptions {
276
+ /** 挂载的路由树(`<Route>` 声明,对齐主 App 用法)。 */
277
+ routes: React.ReactNode;
278
+ /** 初始路径(缺省 `/`)。 */
279
+ initialPath?: string;
280
+ /** 语言(zh/en/auto),缺省 detectLanguage()。 */
281
+ language?: string;
282
+ }
283
+ interface StandaloneTuiContext<R> {
284
+ /**
285
+ * 树外导航(Web Router 风格:navigate(to, opts) / navigate(delta))。
286
+ * getter 动态读取实时 routerRef——跨 await 复用不陈旧。
287
+ */
288
+ readonly navigate: NavigateFunction;
289
+ /** pop 栈顶(等价 navigate(-1))。 */
290
+ readonly goBack: () => void;
291
+ /** 结束 standalone 会话:clear+unmount+resetStdinEncoding,resolve 调用方 Promise。 */
292
+ resolve: (result: R) => void;
293
+ }
294
+ /**
295
+ * 一次性挂载 Router+FocusBus+routes 的独立渲染会话,resolve 后清理退出。
296
+ *
297
+ * @param options 路由树/初始路径/语言
298
+ * @param run 挂载完成后调用一次,把树外可用的 navigate/goBack/resolve 交给调用方
299
+ * @returns Promise<result>——调用方调用 ctx.resolve(result) 时结算
300
+ */
301
+ declare function runStandaloneTui<R>(options: StandaloneTuiOptions, run: (ctx: StandaloneTuiContext<R>) => void): Promise<R>;
302
+ //#endregion
303
+ export { ConfirmDialog, type ConfirmDialogProps, FocusBus, type FocusBusContextValue, FocusBusProvider, type FocusStyle, InfoDialog, type InfoDialogAction, type InfoDialogProps, type KeyHandler, List, ListItem, type ListItemProps, type ListOption, type ListProps, PRIORITY_FALLBACK, PRIORITY_FOCUSED, PRIORITY_ROUTER, ProgressDialog, type ProgressDialogProps, type Question, QuestionsDialog, type QuestionsDialogProps, Route, RouteContext, type RouteContextValue, type RouteProps, Router, type RouterContextValue, StandaloneTuiContext, StandaloneTuiOptions, Tabable, type TabableContextValue, type TabableEntry, type TabableProps, TabableRegistry, type UseTabableOptions, WizardDialog, type WizardRequest, matchPath, runStandaloneTui, useFallbackInput, useFocusBus, useRoute, useRouter, useTabable, useTabableContext, useTabableInput };
304
+ //# sourceMappingURL=standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.d.ts","names":[],"sources":["../src/router/Router.tsx","../src/components/List.tsx","../src/router/FocusBus.tsx","../src/dialogs/WizardDialog.tsx","../src/dialogs/ProgressDialog.tsx","../src/dialogs/QuestionsDialog.tsx","../src/standalone.ts"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{At as e,Ct as t,Dt as n,H as r,Ht as i,I as a,L as o,Mt as s,Ot as c,R as l,St as u,U as d,V as f,_t as p,a as m,bt as h,c as g,gt as _,jt as v,kt as y,l as b,n as x,r as S,s as C,t as w,vt as T,yt as E}from"./WizardDialog-BGWYjvqR.js";import{r as D,t as O}from"./i18n-CznO8p1Y.js";import k,{useEffect as A}from"react";import{render as j,useInput as M}from"ink";const N=(()=>{}),P=()=>{};function F(e){return{get navigate(){return d()?.navigate??N},get goBack(){return d()?.goBack??P},resolve:e}}function I(e){return e===`zh`||e===`en`?e:O()}function L(e,n){return D(I(e.language)),new Promise(r=>{let a=!1,s=null,c=F(e=>{a||(a=!0,s?.clear(),s?.unmount(),i(),r(e))}),l={current:n};function u(){let e=t();return M((t,n)=>{e.dispatch(t,n)},{isActive:process.stdin.isTTY===!0}),A(()=>{let e=setTimeout(()=>l.current(c),0);return()=>clearTimeout(e)},[]),null}s=j(k.createElement(p,null,k.createElement(o,{initialPath:e.initialPath??`/`,children:k.createElement(k.Fragment,null,k.createElement(u,null),e.routes)})),{exitOnCtrlC:!1})})}export{b as ConfirmDialog,_ as FocusBus,p as FocusBusProvider,m as InfoDialog,C as List,g as ListItem,T as PRIORITY_FALLBACK,E as PRIORITY_FOCUSED,h as PRIORITY_ROUTER,S as ProgressDialog,x as QuestionsDialog,a as Route,c as RouteContext,o as Router,y as Tabable,e as TabableRegistry,w as WizardDialog,l as matchPath,L as runStandaloneTui,u as useFallbackInput,t as useFocusBus,f as useRoute,r as useRouter,v as useTabable,s as useTabableContext,n as useTabableInput};
2
+ //# sourceMappingURL=standalone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.js","names":[],"sources":["../src/standalone.ts"],"sourcesContent":["/**\n * standalone.ts — `@x-otto/tui/standalone` 子路径入口(RFC-304 D1/D2)。\n *\n * 把 Router+FocusBus+一次性 render/unmount 生命周期封装成可被 CLI 子命令调用的独立渲染骨架,\n * 泛化 WorkspaceTrustPrompt.tsx 已验证的「单弹窗」模式为「可挂载任意 Route 树」(支持多步\n * 向导/多态渲染),供 `otto <subcommand>` 场景产出 TUI 渲染效果。\n *\n * 与主 `index.ts` 的关系:独立子路径,不污染主 barrel——子命令场景只需要「路由+焦点」薄层,\n * 不需要整套交互式组件库类型面(对齐既有 `./compat` 子路径先例:消费方可直接 import,\n * 不被迫拖入主入口的整棵组件树)。\n *\n * 生命周期契约(逐字对齐 WorkspaceTrustPrompt.tsx 清理三步):\n * render → run(ctx) 被调用(挂载后一帧)→ 调用方决定何时 ctx.resolve(result)\n * → instance.clear()(擦除已渲染帧)+ instance.unmount() + resetStdinEncoding()\n * (复位 Ink 留下的 stdin utf8 解码态——不还原会让后续消费方收到 string 而非 Buffer)。\n *\n * 键盘:内部自带 StandaloneBridge(树内唯一 useInput 入口 → focusBus.dispatch),对齐主\n * App.tsx 的派发模式;Provider 存在时既有 dialog 组件的 useFallbackInput/useTabableInput\n * 走 FocusBus 注册(isActive 门控关掉 Ink 直连),不会与 bridge 重复收键。\n *\n * 树外导航:ctx.navigate/goBack 每次读取实时 routerRef(getter 动态求值,不捕获快照——\n * 对齐 router-ref.ts 的 stackRef 教训:捕获期闭包绑定渲染快照,跨 await 读到陈旧值)。\n */\nimport React, { useEffect } from 'react'\nimport { render, useInput } from 'ink'\n\nimport { FocusBusProvider, useFocusBus } from './router/FocusBus'\nimport { Router, type NavigateFunction } from './router/Router'\nimport { getRouterRef } from './router/router-ref'\nimport { initI18n, detectLanguage, type Lang } from './i18n'\nimport { resetStdinEncoding } from './term/stdin-encoding'\n\n// ═══════════════════ 公共 API 转发(router/index.ts 全量一致) ═══════════════════\n\nexport { Tabable, useTabable, useTabableContext, TabableRegistry, RouteContext } from './router/Tabable'\nexport type {\n TabableProps,\n UseTabableOptions,\n TabableContextValue,\n TabableEntry,\n RouteContextValue,\n FocusStyle,\n} from './router/Tabable'\nexport { Router, useRouter, useRoute, matchPath, Route } from './router/Router'\nexport type { RouterContextValue, RouteProps } from './router/Router'\nexport { List, ListItem } from './components/List'\nexport type { ListOption, ListProps, ListItemProps } from './components/List'\nexport {\n FocusBus,\n FocusBusProvider,\n useFocusBus,\n useTabableInput,\n useFallbackInput,\n PRIORITY_FOCUSED,\n PRIORITY_ROUTER,\n PRIORITY_FALLBACK,\n} from './router/FocusBus'\nexport type { KeyHandler, FocusBusContextValue } from './router/FocusBus'\n\n// ═══════════════════ dialog 复用面(RFC-304 D2「dialog 复用面补齐」) ═══════════════════\n// 机械性 re-export(组件零改动、类型取自既有定义处),让 standalone 消费方可以复用宿主\n// 既有多步向导/进度/信息/问卷弹窗——此前这些组件只被主 App 的 modal 路由消费,未走公共 API。\n\nexport { ConfirmDialog } from './dialogs/ConfirmDialog'\nexport type { ConfirmDialogProps } from './dialogs/ConfirmDialog'\nexport { WizardDialog } from './dialogs/WizardDialog'\nexport type { WizardRequest } from './bootstrap/types'\nexport { ProgressDialog } from './dialogs/ProgressDialog'\nexport type { ProgressDialogProps } from './dialogs/ProgressDialog'\nexport { InfoDialog } from './dialogs/InfoDialog'\nexport type { InfoDialogProps, InfoDialogAction } from './dialogs/InfoDialog'\nexport { QuestionsDialog } from './dialogs/QuestionsDialog'\nexport type { QuestionsDialogProps } from './dialogs/QuestionsDialog'\nexport type { Question } from './dialogs/questions-dialog-logic'\n\n// ═══════════════════ runStandaloneTui ═══════════════════\n\nexport interface StandaloneTuiOptions {\n /** 挂载的路由树(`<Route>` 声明,对齐主 App 用法)。 */\n routes: React.ReactNode\n /** 初始路径(缺省 `/`)。 */\n initialPath?: string\n /** 语言(zh/en/auto),缺省 detectLanguage()。 */\n language?: string\n}\n\nexport interface StandaloneTuiContext<R> {\n /**\n * 树外导航(Web Router 风格:navigate(to, opts) / navigate(delta))。\n * getter 动态读取实时 routerRef——跨 await 复用不陈旧。\n */\n readonly navigate: NavigateFunction\n /** pop 栈顶(等价 navigate(-1))。 */\n readonly goBack: () => void\n /** 结束 standalone 会话:clear+unmount+resetStdinEncoding,resolve 调用方 Promise。 */\n resolve: (result: R) => void\n}\n\ntype RenderInstance = ReturnType<typeof render>\n\nconst noopNavigate = (() => {}) as unknown as NavigateFunction\nconst noop = (): void => {}\n\nfunction createStandaloneContext<R>(finish: (result: R) => void): StandaloneTuiContext<R> {\n return {\n get navigate(): NavigateFunction {\n return getRouterRef()?.navigate ?? noopNavigate\n },\n get goBack(): () => void {\n return getRouterRef()?.goBack ?? noop\n },\n resolve: finish,\n }\n}\n\nfunction resolveLang(language: string | undefined): Lang {\n if (language === 'zh' || language === 'en') return language\n return detectLanguage()\n}\n\n/**\n * 一次性挂载 Router+FocusBus+routes 的独立渲染会话,resolve 后清理退出。\n *\n * @param options 路由树/初始路径/语言\n * @param run 挂载完成后调用一次,把树外可用的 navigate/goBack/resolve 交给调用方\n * @returns Promise<result>——调用方调用 ctx.resolve(result) 时结算\n */\nexport function runStandaloneTui<R>(\n options: StandaloneTuiOptions,\n run: (ctx: StandaloneTuiContext<R>) => void,\n): Promise<R> {\n initI18n(resolveLang(options.language))\n\n return new Promise<R>((resolvePromise) => {\n let settled = false\n let instance: RenderInstance | null = null\n\n const finish = (result: R): void => {\n if (settled) return\n settled = true\n // 逐字对齐 WorkspaceTrustPrompt.tsx 清理三步。\n instance?.clear()\n instance?.unmount()\n resetStdinEncoding()\n resolvePromise(result)\n }\n\n const ctx = createStandaloneContext<R>(finish)\n const runRef = { current: run }\n\n /** 树内桥:唯一 Ink useInput 入口 → focusBus.dispatch;挂载后一帧通知 run。 */\n function StandaloneBridge(): React.ReactElement | null {\n const focusBus = useFocusBus()\n useInput(\n (input, key) => {\n focusBus.dispatch(input, key)\n },\n // 仅在 TTY 时激活:Ink 的 handleSetRawMode 在 stdin 非 TTY 时抛错(App.js\n // isRawModeSupported=false 分支),而该 effect 抛错会中断整个 passive effects\n // flush 链(Router 的 setRouterRef 与下方 run 通知都排在它之后,永不执行)。\n // 非 TTY(脚本/管道)场景本就不应有交互输入——由调用方负责非 TTY 回退。\n { isActive: process.stdin.isTTY === true },\n )\n useEffect(() => {\n // Router 的 setRouterRef 在其自身 effect 中执行(父 effect 晚于子 effect),\n // 故延迟一帧再调 run,保证 ctx.navigate/goBack 已可用。\n const timer = setTimeout(() => runRef.current(ctx), 0)\n return () => clearTimeout(timer)\n }, [])\n return null\n }\n\n const element = React.createElement(\n FocusBusProvider,\n null,\n React.createElement(\n Router,\n {\n initialPath: options.initialPath ?? '/',\n children: React.createElement(React.Fragment, null,\n React.createElement(StandaloneBridge, null),\n options.routes,\n ),\n },\n ),\n )\n\n instance = render(element, { exitOnCtrlC: false })\n })\n}\n"],"mappings":"iXAoGA,MAAM,OAAsB,IACtB,MAAmB,GAEzB,SAAS,EAA2B,EAAsD,CACxF,MAAO,CACL,IAAI,UAA6B,CAC/B,OAAO,GAAc,EAAE,UAAY,GAErC,IAAI,QAAqB,CACvB,OAAO,GAAc,EAAE,QAAU,GAEnC,QAAS,EACV,CAGH,SAAS,EAAY,EAAoC,CAEvD,OADI,IAAa,MAAQ,IAAa,KAAa,EAC5C,GAAgB,CAUzB,SAAgB,EACd,EACA,EACY,CAGZ,OAFA,EAAS,EAAY,EAAQ,SAAS,CAAC,CAEhC,IAAI,QAAY,GAAmB,CACxC,IAAI,EAAU,GACV,EAAkC,KAYhC,EAAM,EAVI,GAAoB,CAC9B,IACJ,EAAU,GAEV,GAAU,OAAO,CACjB,GAAU,SAAS,CACnB,GAAoB,CACpB,EAAe,EAAO,GAGsB,CACxC,EAAS,CAAE,QAAS,EAAK,CAG/B,SAAS,GAA8C,CACrD,IAAM,EAAW,GAAa,CAiB9B,OAhBA,GACG,EAAO,IAAQ,CACd,EAAS,SAAS,EAAO,EAAI,EAM/B,CAAE,SAAU,QAAQ,MAAM,QAAU,GAAM,CAC3C,CACD,MAAgB,CAGd,IAAM,EAAQ,eAAiB,EAAO,QAAQ,EAAI,CAAE,EAAE,CACtD,UAAa,aAAa,EAAM,EAC/B,EAAE,CAAC,CACC,KAkBT,EAAW,EAfK,EAAM,cACpB,EACA,KACA,EAAM,cACJ,EACA,CACE,YAAa,EAAQ,aAAe,IACpC,SAAU,EAAM,cAAc,EAAM,SAAU,KAC5C,EAAM,cAAc,EAAkB,KAAK,CAC3C,EAAQ,OACT,CACF,CACF,CACF,CAE0B,CAAE,YAAa,GAAO,CAAC,EAClD"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@x-otto/tui",
3
+ "version": "0.0.1-alpha.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./compat": {
15
+ "types": "./dist/compat.d.ts",
16
+ "import": "./dist/compat.js",
17
+ "default": "./dist/compat.js"
18
+ },
19
+ "./standalone": {
20
+ "types": "./dist/standalone.d.ts",
21
+ "import": "./dist/standalone.js",
22
+ "default": "./dist/standalone.js"
23
+ }
24
+ },
25
+ "dependencies": {
26
+ "@x-otto/env": "0.1.0-alpha.1",
27
+ "@x-otto/interchange": "0.1.0-alpha.1",
28
+ "@x-otto/session-contract": "0.0.1-alpha.0",
29
+ "@x-otto/shared": "0.1.0-alpha.1",
30
+ "chalk": "^5.6.2",
31
+ "cli-highlight": "^2.1.11",
32
+ "i18next": "^26.3.6",
33
+ "ink": "^7.0.5",
34
+ "marked": "^16.4.1",
35
+ "react": "^19.2.4",
36
+ "react-i18next": "^17.0.11",
37
+ "string-width": "^8.2.0",
38
+ "wrap-ansi": "^10.0.0",
39
+ "zustand": "^5.0.8"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^19.2.2",
43
+ "ink-testing-library": "4"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "registry": "https://registry.npmjs.org",
48
+ "tag": "alpha"
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "README.md"
53
+ ],
54
+ "scripts": {
55
+ "build": "tsdown",
56
+ "test": "vitest run",
57
+ "typecheck": "tsc --noEmit",
58
+ "clean": "rm -rf dist"
59
+ }
60
+ }