@pablozaiden/terminatui 0.3.0 → 0.4.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.
Files changed (111) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/adapterNoSharedUi.test.ts +34 -0
  3. package/src/__tests__/schemaToFields.test.ts +0 -4
  4. package/src/__tests__/tuiRootNoCoupling.test.ts +25 -0
  5. package/src/index.ts +2 -2
  6. package/src/tui/TuiApplication.tsx +0 -4
  7. package/src/tui/TuiRoot.tsx +58 -102
  8. package/src/tui/actions.ts +4 -0
  9. package/src/tui/adapters/ink/InkRenderer.tsx +191 -45
  10. package/src/tui/adapters/ink/SemanticInkRenderer.tsx +210 -0
  11. package/src/tui/adapters/ink/components/Button.tsx +10 -2
  12. package/src/tui/adapters/ink/components/Overlay.tsx +8 -2
  13. package/src/tui/adapters/ink/components/Panel.tsx +26 -5
  14. package/src/tui/adapters/ink/components/ScrollView.tsx +44 -3
  15. package/src/tui/adapters/ink/components/Spinner.tsx +8 -2
  16. package/src/tui/adapters/ink/keyboard.ts +0 -3
  17. package/src/tui/adapters/ink/ui/CommandSelector.tsx +56 -0
  18. package/src/tui/adapters/ink/ui/ConfigForm.tsx +77 -0
  19. package/src/tui/adapters/ink/ui/Header.tsx +25 -0
  20. package/src/tui/adapters/ink/ui/JsonHighlight.tsx +21 -0
  21. package/src/tui/adapters/ink/ui/ResultsPanel.tsx +57 -0
  22. package/src/tui/adapters/opentui/OpenTuiRenderer.tsx +190 -43
  23. package/src/tui/adapters/opentui/SemanticOpenTuiRenderer.tsx +192 -0
  24. package/src/tui/adapters/opentui/components/Label.tsx +2 -2
  25. package/src/tui/adapters/opentui/components/Overlay.tsx +12 -3
  26. package/src/tui/adapters/opentui/components/Panel.tsx +11 -1
  27. package/src/tui/adapters/opentui/components/ScrollView.tsx +1 -8
  28. package/src/tui/adapters/opentui/components/Spinner.tsx +1 -1
  29. package/src/tui/adapters/opentui/keyboard.ts +0 -3
  30. package/src/tui/adapters/opentui/ui/CommandSelector.tsx +55 -0
  31. package/src/tui/adapters/opentui/ui/ConfigForm.tsx +74 -0
  32. package/src/tui/adapters/opentui/ui/Header.tsx +24 -0
  33. package/src/tui/adapters/opentui/ui/JsonHighlight.tsx +20 -0
  34. package/src/tui/adapters/opentui/ui/LogsPanel.tsx +44 -0
  35. package/src/tui/adapters/opentui/ui/ResultsPanel.tsx +62 -0
  36. package/src/tui/adapters/shared/TerminalClipboard.ts +65 -0
  37. package/src/tui/adapters/{opentui/hooks → shared}/useSpinner.ts +5 -1
  38. package/src/tui/adapters/types.ts +25 -46
  39. package/src/tui/components/JsonHighlight.tsx +41 -111
  40. package/src/tui/context/ActionContext.tsx +51 -0
  41. package/src/tui/context/ExecutorContext.tsx +7 -1
  42. package/src/tui/context/NavigationContext.tsx +20 -4
  43. package/src/tui/controllers/CommandBrowserController.tsx +100 -0
  44. package/src/tui/controllers/ConfigController.tsx +183 -0
  45. package/src/tui/controllers/EditorController.tsx +169 -0
  46. package/src/tui/controllers/LogsController.tsx +48 -0
  47. package/src/tui/controllers/OutcomeController.tsx +110 -0
  48. package/src/tui/driver/TuiDriver.tsx +148 -0
  49. package/src/tui/driver/context/TuiDriverContext.tsx +44 -0
  50. package/src/tui/driver/types.ts +72 -0
  51. package/src/tui/semantic/AppShell.tsx +30 -0
  52. package/src/tui/semantic/CommandBrowserScreen.tsx +16 -0
  53. package/src/tui/semantic/ConfigScreen.tsx +23 -0
  54. package/src/tui/semantic/EditorScreen.tsx +20 -0
  55. package/src/tui/semantic/LogsScreen.tsx +9 -0
  56. package/src/tui/semantic/RunningScreen.tsx +17 -0
  57. package/src/tui/semantic/layoutTypes.ts +72 -0
  58. package/src/tui/semantic/render.tsx +44 -0
  59. package/src/tui/semantic/types.ts +31 -98
  60. package/src/tui/utils/jsonTokenizer.ts +98 -0
  61. package/src/tui/utils/schemaToFields.ts +1 -25
  62. package/src/tui/adapters/ink/components/Code.tsx +0 -6
  63. package/src/tui/adapters/ink/components/Container.tsx +0 -5
  64. package/src/tui/adapters/ink/components/Spacer.tsx +0 -15
  65. package/src/tui/adapters/ink/components/Value.tsx +0 -7
  66. package/src/tui/adapters/opentui/components/Code.tsx +0 -12
  67. package/src/tui/adapters/opentui/components/Container.tsx +0 -56
  68. package/src/tui/adapters/opentui/components/Spacer.tsx +0 -5
  69. package/src/tui/adapters/opentui/components/Value.tsx +0 -13
  70. package/src/tui/components/ActionButton.tsx +0 -0
  71. package/src/tui/components/CommandSelector.tsx +0 -119
  72. package/src/tui/components/ConfigForm.tsx +0 -174
  73. package/src/tui/components/FieldRow.tsx +0 -0
  74. package/src/tui/components/Header.tsx +0 -32
  75. package/src/tui/components/ModalBase.tsx +0 -38
  76. package/src/tui/components/ResultsPanel.tsx +0 -84
  77. package/src/tui/components/StatusBar.tsx +0 -44
  78. package/src/tui/components/logColors.ts +0 -12
  79. package/src/tui/components/types.ts +0 -30
  80. package/src/tui/context/ClipboardContext.tsx +0 -87
  81. package/src/tui/context/KeyboardContext.tsx +0 -132
  82. package/src/tui/hooks/useActiveKeyHandler.ts +0 -75
  83. package/src/tui/hooks/useClipboard.ts +0 -81
  84. package/src/tui/hooks/useClipboardProvider.ts +0 -42
  85. package/src/tui/hooks/useGlobalKeyHandler.ts +0 -54
  86. package/src/tui/modals/CliModal.tsx +0 -82
  87. package/src/tui/modals/EditorModal.tsx +0 -207
  88. package/src/tui/modals/LogsModal.tsx +0 -98
  89. package/src/tui/registry.ts +0 -102
  90. package/src/tui/screens/CommandSelectScreen.tsx +0 -162
  91. package/src/tui/screens/ConfigScreen.tsx +0 -165
  92. package/src/tui/screens/ErrorScreen.tsx +0 -58
  93. package/src/tui/screens/ResultsScreen.tsx +0 -68
  94. package/src/tui/screens/RunningScreen.tsx +0 -72
  95. package/src/tui/screens/ScreenBase.ts +0 -6
  96. package/src/tui/semantic/Button.tsx +0 -7
  97. package/src/tui/semantic/Code.tsx +0 -7
  98. package/src/tui/semantic/CodeHighlight.tsx +0 -7
  99. package/src/tui/semantic/Container.tsx +0 -7
  100. package/src/tui/semantic/Field.tsx +0 -7
  101. package/src/tui/semantic/Label.tsx +0 -7
  102. package/src/tui/semantic/MenuButton.tsx +0 -7
  103. package/src/tui/semantic/MenuItem.tsx +0 -7
  104. package/src/tui/semantic/Overlay.tsx +0 -7
  105. package/src/tui/semantic/Panel.tsx +0 -7
  106. package/src/tui/semantic/ScrollView.tsx +0 -9
  107. package/src/tui/semantic/Select.tsx +0 -7
  108. package/src/tui/semantic/Spacer.tsx +0 -7
  109. package/src/tui/semantic/Spinner.tsx +0 -7
  110. package/src/tui/semantic/TextInput.tsx +0 -7
  111. package/src/tui/semantic/Value.tsx +0 -7
@@ -0,0 +1,148 @@
1
+ import type { AnyCommand } from "../../core/command.ts";
2
+ import type { NavigationAPI } from "../context/NavigationContext.tsx";
3
+ import type { ExecutorContextValue } from "../context/ExecutorContext.tsx";
4
+ import type { LogEvent } from "../../core/logger.ts";
5
+ import type { AppShellProps } from "../semantic/AppShell.tsx";
6
+
7
+ import { RenderAppShell } from "../semantic/render.tsx";
8
+
9
+ import type { ConfigRouteParams, CopyPayload, EditorModalParams, TuiRoute } from "./types.ts";
10
+ import { CommandBrowserController } from "../controllers/CommandBrowserController.tsx";
11
+ import { ConfigController } from "../controllers/ConfigController.tsx";
12
+ import { EditorController } from "../controllers/EditorController.tsx";
13
+ import { LogsController } from "../controllers/LogsController.tsx";
14
+ import { OutcomeController } from "../controllers/OutcomeController.tsx";
15
+
16
+ export class TuiDriver {
17
+ private navigation: NavigationAPI;
18
+ private executor: ExecutorContextValue;
19
+ private logs: LogEvent[];
20
+
21
+ private commandBrowser: CommandBrowserController;
22
+ private config: ConfigController;
23
+ private editor: EditorController;
24
+ private logsModal: LogsController;
25
+ private outcome: OutcomeController;
26
+
27
+ public constructor({
28
+ appName,
29
+ commands,
30
+ navigation,
31
+ executor,
32
+ logs,
33
+ }: {
34
+ appName: string;
35
+ commands: AnyCommand[];
36
+ navigation: NavigationAPI;
37
+ executor: ExecutorContextValue;
38
+ logs: LogEvent[];
39
+ }) {
40
+ this.navigation = navigation;
41
+ this.executor = executor;
42
+ this.logs = logs;
43
+
44
+ this.config = new ConfigController({ appName, navigation, executor });
45
+ this.commandBrowser = new CommandBrowserController({ commands, navigation, configController: this.config });
46
+ this.editor = new EditorController({ navigation });
47
+ this.logsModal = new LogsController({ navigation });
48
+ this.outcome = new OutcomeController({ navigation });
49
+ }
50
+
51
+ public get statusMessage(): string {
52
+ return this.executor.isExecuting ? "Executing..." : "Ready";
53
+ }
54
+
55
+ public getActiveCopyPayload(): CopyPayload | null {
56
+ // Modal takes priority over screen - if a modal is open, use its copy payload
57
+ const topModal = this.navigation.modalStack[this.navigation.modalStack.length - 1];
58
+ if (topModal?.id === "logs") {
59
+ return this.logsModal.getCopyPayload(this.logs);
60
+ }
61
+
62
+ if (topModal?.id === "editor") {
63
+ return this.editor.getCopyPayload();
64
+ }
65
+
66
+ // No modal open, check current screen
67
+ const currentRoute = this.navigation.current.route as TuiRoute;
68
+ if (currentRoute === "config") {
69
+ const params = this.navigation.current.params as ConfigRouteParams | undefined;
70
+ if (params) {
71
+ return this.config.getCopyPayload(params);
72
+ }
73
+ }
74
+
75
+ if (currentRoute === "results" || currentRoute === "error") {
76
+ return this.outcome.getCopyPayload(currentRoute);
77
+ }
78
+
79
+ return null;
80
+ }
81
+
82
+ public renderAppShell({
83
+ app,
84
+ copyToast,
85
+ }: {
86
+ app: { name: string; displayName?: string; version: string; breadcrumb?: string[] };
87
+ copyToast?: string | null;
88
+ }): React.ReactNode {
89
+ const currentRoute = this.navigation.current.route as TuiRoute;
90
+
91
+ const { screen, breadcrumb } = this.renderScreen(currentRoute);
92
+ const modals = this.renderModals();
93
+
94
+ return (
95
+ <RenderAppShell
96
+ app={{
97
+ name: app.name,
98
+ displayName: app.displayName,
99
+ version: app.version,
100
+ breadcrumb: breadcrumb ?? app.breadcrumb,
101
+ }}
102
+ status={{
103
+ isExecuting: this.executor.isExecuting,
104
+ isCancelling: this.executor.isCancelling,
105
+ message: this.statusMessage,
106
+ }}
107
+ screen={screen}
108
+ modals={modals}
109
+ copyToast={copyToast}
110
+ />
111
+ );
112
+ }
113
+
114
+ private renderScreen(route: TuiRoute): { screen: React.ReactNode; breadcrumb?: string[] } {
115
+ if (route === "commandBrowser") {
116
+ const { node, breadcrumb } = this.commandBrowser.render();
117
+ return { screen: node, breadcrumb };
118
+ }
119
+
120
+ if (route === "config") {
121
+ const { node, breadcrumb } = this.config.render();
122
+ return { screen: node, breadcrumb };
123
+ }
124
+
125
+ if (route === "running" || route === "results" || route === "error") {
126
+ const { node } = this.outcome.render(route);
127
+ return { screen: node };
128
+ }
129
+
130
+ return { screen: null };
131
+ }
132
+
133
+ private renderModals(): AppShellProps["modals"] {
134
+ const modals: React.ReactNode[] = this.navigation.modalStack.map((modal) => {
135
+ if (modal.id === "logs") {
136
+ return this.logsModal.render(this.logs);
137
+ }
138
+
139
+ if (modal.id === "editor") {
140
+ return this.editor.render(modal.params as EditorModalParams | undefined);
141
+ }
142
+
143
+ return null;
144
+ });
145
+
146
+ return modals.filter(Boolean) as React.ReactNode[];
147
+ }
148
+ }
@@ -0,0 +1,44 @@
1
+ import { createContext, useContext, useMemo, type ReactNode } from "react";
2
+ import type { AnyCommand } from "../../../core/command.ts";
3
+
4
+ import { useNavigation } from "../../context/NavigationContext.tsx";
5
+ import { useExecutor } from "../../context/ExecutorContext.tsx";
6
+ import { useLogs } from "../../context/LogsContext.tsx";
7
+
8
+ import { TuiDriver } from "../TuiDriver.tsx";
9
+
10
+ const TuiDriverContext = createContext<TuiDriver | null>(null);
11
+
12
+ export function TuiDriverProvider({
13
+ appName,
14
+ commands,
15
+ children,
16
+ }: {
17
+ appName: string;
18
+ commands: AnyCommand[];
19
+ children: ReactNode;
20
+ }) {
21
+ const navigation = useNavigation();
22
+ const executor = useExecutor();
23
+ const { logs } = useLogs();
24
+
25
+ const driver = useMemo(() => {
26
+ return new TuiDriver({
27
+ appName,
28
+ commands,
29
+ navigation,
30
+ executor,
31
+ logs,
32
+ });
33
+ }, [appName, commands, executor, logs, navigation]);
34
+
35
+ return <TuiDriverContext.Provider value={driver}>{children}</TuiDriverContext.Provider>;
36
+ }
37
+
38
+ export function useTuiDriver(): TuiDriver {
39
+ const driver = useContext(TuiDriverContext);
40
+ if (!driver) {
41
+ throw new Error("useTuiDriver must be used within TuiDriverProvider");
42
+ }
43
+ return driver;
44
+ }
@@ -0,0 +1,72 @@
1
+ import type { AnyCommand } from "../../core/command.ts";
2
+ import type { OptionSchema } from "../../types/command.ts";
3
+ import type { schemaToFieldConfigs } from "../utils/schemaToFields.ts";
4
+
5
+ export type TuiRoute = "commandBrowser" | "config" | "running" | "results" | "error";
6
+
7
+ export type TuiModalId = "logs" | "editor";
8
+
9
+ export type EditorModalParams = {
10
+ fieldKey: string;
11
+ fieldDisplayName: string;
12
+ currentValue: unknown;
13
+ fieldConfigs: ReturnType<typeof schemaToFieldConfigs>;
14
+ /** Current buffer value (string representation) - used for reactive updates */
15
+ bufferValue?: string;
16
+ /** Current select index for enum/boolean fields */
17
+ selectIndex?: number;
18
+ onSubmit: (value: unknown) => void;
19
+ onCancel: () => void;
20
+ };
21
+
22
+ export type ConfigRouteParams = {
23
+ command: AnyCommand;
24
+ commandPath: string[];
25
+ values: Record<string, unknown>;
26
+ fieldConfigs: ReturnType<typeof schemaToFieldConfigs>;
27
+ selectedFieldIndex?: number;
28
+ };
29
+
30
+ export type CommandBrowserRouteParams = {
31
+ commandPath: string[];
32
+ selectedIndex?: number;
33
+ };
34
+
35
+ export type RunningRouteParams = {
36
+ command: AnyCommand;
37
+ commandPath: string[];
38
+ values: Record<string, unknown>;
39
+ };
40
+
41
+ export type ResultsRouteParams = {
42
+ command: AnyCommand;
43
+ commandPath: string[];
44
+ values: Record<string, unknown>;
45
+ result: unknown;
46
+ };
47
+
48
+ export type ErrorRouteParams = {
49
+ command: AnyCommand;
50
+ commandPath: string[];
51
+ values: Record<string, unknown>;
52
+ error: Error;
53
+ };
54
+
55
+ export type RouteParamsById = {
56
+ commandBrowser: CommandBrowserRouteParams;
57
+ config: ConfigRouteParams;
58
+ running: RunningRouteParams;
59
+ results: ResultsRouteParams;
60
+ error: ErrorRouteParams;
61
+ };
62
+
63
+ export type TuiScreenEntry = {
64
+ [K in TuiRoute]: { route: K; params: RouteParamsById[K] };
65
+ }[TuiRoute];
66
+
67
+ export type CopyPayload = {
68
+ label: string;
69
+ content: string;
70
+ };
71
+
72
+ export type CommandConfigDefaultsResolver = (schema: OptionSchema) => Record<string, unknown>;
@@ -0,0 +1,30 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export interface AppIdentity {
4
+ name: string;
5
+ displayName?: string;
6
+ version: string;
7
+ breadcrumb?: string[];
8
+ }
9
+
10
+ export interface AppStatus {
11
+ isExecuting: boolean;
12
+ isCancelling: boolean;
13
+ message: string;
14
+ }
15
+
16
+ export interface AppShellProps {
17
+ app: AppIdentity;
18
+ status: AppStatus;
19
+
20
+ screen: ReactNode;
21
+ modals: ReactNode[];
22
+
23
+ /** Optional copy toast message (adapter-owned display) */
24
+ copyToast?: string | null;
25
+ }
26
+
27
+ export function AppShell(_props: AppShellProps) {
28
+ // This is a semantic-only marker component. The adapter owns layout.
29
+ return null;
30
+ }
@@ -0,0 +1,16 @@
1
+ import type { AnyCommand } from "../../core/command.ts";
2
+
3
+ export interface CommandBrowserScreenProps {
4
+ commandId: string[];
5
+ commands: AnyCommand[];
6
+ selectedCommandIndex: number;
7
+
8
+ onOpenPath: (commandId: string[]) => void;
9
+ onSelectCommand: (index: number) => void;
10
+ onRunSelected: () => void;
11
+ }
12
+
13
+ export function CommandBrowserScreen(_props: CommandBrowserScreenProps) {
14
+ // Semantic-only marker component. Adapter renders this.
15
+ return null;
16
+ }
@@ -0,0 +1,23 @@
1
+ import type { FieldConfig } from "./types.ts";
2
+
3
+ export interface ConfigScreenProps {
4
+ title: string;
5
+
6
+ commandId: string[];
7
+ fieldConfigs: FieldConfig[];
8
+ values: Record<string, unknown>;
9
+
10
+ /** CLI command string for display and copy */
11
+ cliCommand: string;
12
+
13
+ selectedFieldIndex: number;
14
+ onSelectionChange: (index: number) => void;
15
+
16
+ onEditField: (fieldId: string) => void;
17
+ onRun: () => void;
18
+ }
19
+
20
+ export function ConfigScreen(_props: ConfigScreenProps) {
21
+ // Semantic-only marker component. Adapter renders this.
22
+ return null;
23
+ }
@@ -0,0 +1,20 @@
1
+ export interface EditorScreenProps {
2
+ fieldId: string;
3
+ label: string;
4
+ valueString: string;
5
+
6
+ editorType: "text" | "number" | "select";
7
+
8
+ selectOptions?: { label: string; value: string }[];
9
+ selectIndex?: number;
10
+
11
+ onChangeText?: (text: string) => void;
12
+ onChangeSelectIndex?: (index: number) => void;
13
+ onSubmit?: () => void;
14
+ onCancel: () => void;
15
+ }
16
+
17
+ export function EditorScreen(_props: EditorScreenProps) {
18
+ // Semantic-only marker component. Adapter renders this.
19
+ return null;
20
+ }
@@ -0,0 +1,9 @@
1
+ export interface LogsScreenProps {
2
+ items: { level: string; message: string; timestamp: number }[];
3
+ onClose: () => void;
4
+ }
5
+
6
+ export function LogsScreen(_props: LogsScreenProps) {
7
+ // Semantic-only marker component. Adapter renders this.
8
+ return null;
9
+ }
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ import type { CommandResult } from "../../core/command.ts";
3
+
4
+ export interface RunningScreenProps {
5
+ title: string;
6
+ kind: "running" | "results" | "error";
7
+ message?: string;
8
+ /** The actual result object when kind is "results" */
9
+ result?: CommandResult;
10
+ /** Custom content rendered by the command's renderResult method */
11
+ customContent?: ReactNode;
12
+ }
13
+
14
+ export function RunningScreen(_props: RunningScreenProps) {
15
+ // Semantic-only marker component. Adapter renders this.
16
+ return null;
17
+ }
@@ -0,0 +1,72 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export type Align = "flex-start" | "center" | "flex-end" | "stretch";
4
+ export type Justify = "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | "space-evenly";
5
+ export type FlexDirection = "row" | "column";
6
+
7
+ export interface Spacing {
8
+ top?: number;
9
+ right?: number;
10
+ bottom?: number;
11
+ left?: number;
12
+ }
13
+
14
+ export interface LayoutProps {
15
+ flex?: number;
16
+ width?: number | string;
17
+ height?: number | string;
18
+ maxHeight?: number | string;
19
+
20
+ /** Positioning props for absolute/relative layouts */
21
+ top?: number | string;
22
+ left?: number | string;
23
+ right?: number | string;
24
+ bottom?: number | string;
25
+
26
+ flexDirection?: FlexDirection;
27
+ alignItems?: Align;
28
+ justifyContent?: Justify;
29
+
30
+ gap?: number;
31
+ padding?: number | Spacing;
32
+
33
+ /** When set, should prevent the node from shrinking in flex layouts. */
34
+ noShrink?: boolean;
35
+ }
36
+
37
+ export type PanelSurface = "panel" | "overlay";
38
+
39
+ export interface PanelProps extends LayoutProps {
40
+ title?: string;
41
+ focused?: boolean;
42
+ border?: boolean;
43
+ surface?: PanelSurface;
44
+
45
+ /** Renderer-level compact styling (e.g. no default padding). */
46
+ dense?: boolean;
47
+
48
+ children?: ReactNode;
49
+ }
50
+
51
+ export interface ScrollViewRef {
52
+ scrollToIndex: (index: number) => void;
53
+ }
54
+
55
+ export interface ScrollViewProps extends LayoutProps {
56
+ axis?: "vertical" | "horizontal" | "both";
57
+ stickyToEnd?: boolean;
58
+ focused?: boolean;
59
+ scrollRef?: (ref: ScrollViewRef | null) => void;
60
+ children?: ReactNode;
61
+ }
62
+
63
+ export interface OverlayProps {
64
+ zIndex?: number;
65
+ top?: number | string;
66
+ left?: number | string;
67
+ right?: number | string;
68
+ bottom?: number | string;
69
+ width?: number | string;
70
+ height?: number | string;
71
+ children?: ReactNode;
72
+ }
@@ -0,0 +1,44 @@
1
+ import { AppShell, type AppShellProps } from "./AppShell.tsx";
2
+ import { CommandBrowserScreen, type CommandBrowserScreenProps } from "./CommandBrowserScreen.tsx";
3
+ import { ConfigScreen, type ConfigScreenProps } from "./ConfigScreen.tsx";
4
+ import { RunningScreen, type RunningScreenProps } from "./RunningScreen.tsx";
5
+ import { LogsScreen, type LogsScreenProps } from "./LogsScreen.tsx";
6
+ import { EditorScreen, type EditorScreenProps } from "./EditorScreen.tsx";
7
+ import { useRenderer } from "../context/RendererContext.tsx";
8
+
9
+ export function RenderAppShell(props: AppShellProps) {
10
+ const renderer = useRenderer();
11
+ // Keep marker component in tree for debugging/introspection.
12
+ void AppShell;
13
+ return renderer.renderSemanticAppShell(props);
14
+ }
15
+
16
+ export function RenderCommandBrowserScreen(props: CommandBrowserScreenProps) {
17
+ const renderer = useRenderer();
18
+ void CommandBrowserScreen;
19
+ return renderer.renderSemanticCommandBrowserScreen(props);
20
+ }
21
+
22
+ export function RenderConfigScreen(props: ConfigScreenProps) {
23
+ const renderer = useRenderer();
24
+ void ConfigScreen;
25
+ return renderer.renderSemanticConfigScreen(props);
26
+ }
27
+
28
+ export function RenderRunningScreen(props: RunningScreenProps) {
29
+ const renderer = useRenderer();
30
+ void RunningScreen;
31
+ return renderer.renderSemanticRunningScreen(props);
32
+ }
33
+
34
+ export function RenderLogsScreen(props: LogsScreenProps) {
35
+ const renderer = useRenderer();
36
+ void LogsScreen;
37
+ return renderer.renderSemanticLogsScreen(props);
38
+ }
39
+
40
+ export function RenderEditorScreen(props: EditorScreenProps) {
41
+ const renderer = useRenderer();
42
+ void EditorScreen;
43
+ return renderer.renderSemanticEditorScreen(props);
44
+ }
@@ -1,15 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
- export type Align = "flex-start" | "center" | "flex-end" | "stretch";
4
- export type Justify = "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | "space-evenly";
5
- export type FlexDirection = "row" | "column";
6
-
7
- export interface Spacing {
8
- top?: number;
9
- right?: number;
10
- bottom?: number;
11
- left?: number;
12
- }
3
+ export * from "./layoutTypes.ts";
13
4
 
14
5
  export interface ThemeConfig {
15
6
  colors: {
@@ -41,93 +32,6 @@ export interface ThemeConfig {
41
32
 
42
33
  export type SemanticColor = keyof ThemeConfig["colors"];
43
34
 
44
- export interface LayoutProps {
45
- flex?: number;
46
- width?: number | string;
47
- height?: number | string;
48
-
49
- flexDirection?: FlexDirection;
50
- alignItems?: Align;
51
- justifyContent?: Justify;
52
-
53
- gap?: number;
54
- padding?: number | Spacing;
55
-
56
- /** When set, should prevent the node from shrinking in flex layouts. */
57
- noShrink?: boolean;
58
- }
59
-
60
- export type PanelSurface = "panel" | "overlay";
61
-
62
- export interface PanelProps extends LayoutProps {
63
- title?: string;
64
- focused?: boolean;
65
- border?: boolean;
66
- surface?: PanelSurface;
67
-
68
- /** Renderer-level compact styling (e.g. no default padding). */
69
- dense?: boolean;
70
-
71
- children?: ReactNode;
72
- }
73
-
74
- export interface ContainerProps extends LayoutProps {
75
- children?: ReactNode;
76
- }
77
-
78
- export interface ScrollViewRef {
79
- scrollToTop: () => void;
80
- scrollToBottom: () => void;
81
- scrollToIndex: (index: number) => void;
82
- }
83
-
84
- export interface ScrollViewProps extends LayoutProps {
85
- axis?: "vertical" | "horizontal" | "both";
86
- stickyToEnd?: boolean;
87
- focused?: boolean;
88
- scrollRef?: (ref: ScrollViewRef | null) => void;
89
- children?: ReactNode;
90
- }
91
-
92
- export interface OverlayProps {
93
- zIndex?: number;
94
- top?: number | string;
95
- left?: number | string;
96
- right?: number | string;
97
- bottom?: number | string;
98
- width?: number | string;
99
- height?: number | string;
100
- children?: ReactNode;
101
- }
102
-
103
- export interface SpacerProps {
104
- size: number;
105
- axis?: "horizontal" | "vertical";
106
- }
107
-
108
- export interface SpinnerProps {
109
- active: boolean;
110
- }
111
-
112
- export interface LabelProps {
113
- color?: SemanticColor;
114
- bold?: boolean;
115
- italic?: boolean;
116
- wrap?: boolean;
117
- children: ReactNode;
118
- }
119
-
120
- export interface ValueProps {
121
- color?: SemanticColor;
122
- truncate?: boolean;
123
- children: ReactNode;
124
- }
125
-
126
- export interface CodeProps {
127
- color?: SemanticColor;
128
- children: string;
129
- }
130
-
131
35
  export type CodeTokenType =
132
36
  | "punctuation"
133
37
  | "string"
@@ -146,6 +50,13 @@ export interface CodeHighlightProps {
146
50
  tokens: CodeToken[];
147
51
  }
148
52
 
53
+ export interface LabelProps {
54
+ color?: SemanticColor;
55
+ bold?: boolean;
56
+ italic?: boolean;
57
+ children: ReactNode;
58
+ }
59
+
149
60
  export interface FieldProps {
150
61
  label: string;
151
62
  value: ReactNode;
@@ -192,4 +103,26 @@ export interface MenuItemProps {
192
103
  suffix?: string;
193
104
  selected?: boolean;
194
105
  onActivate?: () => void;
195
- }
106
+ }
107
+
108
+ export interface SpinnerProps {
109
+ active: boolean;
110
+ }
111
+
112
+ // ─────────────────────────────────────────────────────────────────────────────
113
+ // FieldConfig types (used by ConfigScreen, schemaToFields, adapter ConfigForm)
114
+ // ─────────────────────────────────────────────────────────────────────────────
115
+
116
+ export type FieldType = "text" | "number" | "boolean" | "enum";
117
+
118
+ export interface FieldOption {
119
+ name: string;
120
+ value: unknown;
121
+ }
122
+
123
+ export interface FieldConfig {
124
+ key: string;
125
+ label: string;
126
+ type: FieldType;
127
+ options?: FieldOption[];
128
+ }