@pablozaiden/terminatui 0.2.0 → 0.3.0-beta-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 (175) hide show
  1. package/AGENTS.md +14 -2
  2. package/CLAUDE.md +1 -0
  3. package/README.md +64 -43
  4. package/bun.lock +85 -0
  5. package/examples/tui-app/commands/config/app/get.ts +6 -10
  6. package/examples/tui-app/commands/config/app/index.ts +2 -6
  7. package/examples/tui-app/commands/config/app/set.ts +23 -13
  8. package/examples/tui-app/commands/config/index.ts +2 -6
  9. package/examples/tui-app/commands/config/user/get.ts +6 -10
  10. package/examples/tui-app/commands/config/user/index.ts +2 -6
  11. package/examples/tui-app/commands/config/user/set.ts +6 -10
  12. package/examples/tui-app/commands/greet.ts +13 -11
  13. package/examples/tui-app/commands/math.ts +5 -9
  14. package/examples/tui-app/commands/status.ts +21 -12
  15. package/examples/tui-app/index.ts +6 -3
  16. package/guides/01-hello-world.md +7 -2
  17. package/guides/02-adding-options.md +2 -2
  18. package/guides/03-multiple-commands.md +6 -8
  19. package/guides/04-subcommands.md +8 -8
  20. package/guides/05-interactive-tui.md +45 -30
  21. package/guides/06-config-validation.md +4 -12
  22. package/guides/07-async-cancellation.md +14 -16
  23. package/guides/08-complete-application.md +12 -42
  24. package/guides/README.md +7 -3
  25. package/package.json +4 -8
  26. package/src/__tests__/application.test.ts +87 -68
  27. package/src/__tests__/buildCliCommand.test.ts +99 -119
  28. package/src/__tests__/builtins.test.ts +27 -75
  29. package/src/__tests__/command.test.ts +100 -131
  30. package/src/__tests__/context.test.ts +1 -26
  31. package/src/__tests__/helpCore.test.ts +227 -0
  32. package/src/__tests__/parser.test.ts +98 -244
  33. package/src/__tests__/registry.test.ts +33 -160
  34. package/src/__tests__/schemaToFields.test.ts +75 -158
  35. package/src/builtins/help.ts +12 -4
  36. package/src/builtins/settings.ts +18 -32
  37. package/src/builtins/version.ts +4 -4
  38. package/src/cli/output/colors.ts +1 -1
  39. package/src/cli/parser.ts +26 -95
  40. package/src/core/application.ts +192 -110
  41. package/src/core/command.ts +26 -9
  42. package/src/core/context.ts +31 -20
  43. package/src/core/help.ts +24 -18
  44. package/src/core/knownCommands.ts +13 -0
  45. package/src/core/logger.ts +39 -42
  46. package/src/core/registry.ts +5 -12
  47. package/src/tui/TuiApplication.tsx +63 -120
  48. package/src/tui/TuiRoot.tsx +135 -0
  49. package/src/tui/adapters/factory.ts +19 -0
  50. package/src/tui/adapters/ink/InkRenderer.tsx +135 -0
  51. package/src/tui/adapters/ink/components/Button.tsx +12 -0
  52. package/src/tui/adapters/ink/components/Code.tsx +6 -0
  53. package/src/tui/adapters/ink/components/CodeHighlight.tsx +6 -0
  54. package/src/tui/adapters/ink/components/Container.tsx +5 -0
  55. package/src/tui/adapters/ink/components/Field.tsx +12 -0
  56. package/src/tui/adapters/ink/components/Label.tsx +24 -0
  57. package/src/tui/adapters/ink/components/MenuButton.tsx +12 -0
  58. package/src/tui/adapters/ink/components/MenuItem.tsx +17 -0
  59. package/src/tui/adapters/ink/components/Overlay.tsx +5 -0
  60. package/src/tui/adapters/ink/components/Panel.tsx +15 -0
  61. package/src/tui/adapters/ink/components/ScrollView.tsx +5 -0
  62. package/src/tui/adapters/ink/components/Select.tsx +44 -0
  63. package/src/tui/adapters/ink/components/Spacer.tsx +15 -0
  64. package/src/tui/adapters/ink/components/Spinner.tsx +5 -0
  65. package/src/tui/adapters/ink/components/TextInput.tsx +22 -0
  66. package/src/tui/adapters/ink/components/Value.tsx +7 -0
  67. package/src/tui/adapters/ink/keyboard.ts +97 -0
  68. package/src/tui/adapters/ink/utils.ts +16 -0
  69. package/src/tui/adapters/opentui/OpenTuiRenderer.tsx +115 -0
  70. package/src/tui/adapters/opentui/components/Button.tsx +13 -0
  71. package/src/tui/adapters/opentui/components/Code.tsx +12 -0
  72. package/src/tui/adapters/opentui/components/CodeHighlight.tsx +24 -0
  73. package/src/tui/adapters/opentui/components/Container.tsx +56 -0
  74. package/src/tui/adapters/opentui/components/Field.tsx +18 -0
  75. package/src/tui/adapters/opentui/components/Label.tsx +15 -0
  76. package/src/tui/adapters/opentui/components/MenuButton.tsx +14 -0
  77. package/src/tui/adapters/opentui/components/MenuItem.tsx +29 -0
  78. package/src/tui/adapters/opentui/components/Overlay.tsx +21 -0
  79. package/src/tui/adapters/opentui/components/Panel.tsx +78 -0
  80. package/src/tui/adapters/opentui/components/ScrollView.tsx +85 -0
  81. package/src/tui/adapters/opentui/components/Select.tsx +59 -0
  82. package/src/tui/adapters/opentui/components/Spacer.tsx +5 -0
  83. package/src/tui/adapters/opentui/components/Spinner.tsx +12 -0
  84. package/src/tui/adapters/opentui/components/TextInput.tsx +13 -0
  85. package/src/tui/adapters/opentui/components/Value.tsx +13 -0
  86. package/src/tui/{hooks → adapters/opentui/hooks}/useSpinner.ts +2 -11
  87. package/src/tui/adapters/opentui/keyboard.ts +61 -0
  88. package/src/tui/adapters/types.ts +70 -0
  89. package/src/tui/components/ActionButton.tsx +0 -36
  90. package/src/tui/components/CommandSelector.tsx +45 -92
  91. package/src/tui/components/ConfigForm.tsx +68 -42
  92. package/src/tui/components/FieldRow.tsx +0 -30
  93. package/src/tui/components/Header.tsx +14 -13
  94. package/src/tui/components/JsonHighlight.tsx +10 -17
  95. package/src/tui/components/ModalBase.tsx +38 -0
  96. package/src/tui/components/ResultsPanel.tsx +27 -36
  97. package/src/tui/components/StatusBar.tsx +24 -39
  98. package/src/tui/components/logColors.ts +12 -0
  99. package/src/tui/context/ClipboardContext.tsx +87 -0
  100. package/src/tui/context/ExecutorContext.tsx +139 -0
  101. package/src/tui/context/KeyboardContext.tsx +85 -71
  102. package/src/tui/context/LogsContext.tsx +35 -0
  103. package/src/tui/context/NavigationContext.tsx +194 -0
  104. package/src/tui/context/RendererContext.tsx +20 -0
  105. package/src/tui/context/TuiAppContext.tsx +58 -0
  106. package/src/tui/hooks/useActiveKeyHandler.ts +75 -0
  107. package/src/tui/hooks/useBackHandler.ts +34 -0
  108. package/src/tui/hooks/useClipboard.ts +40 -25
  109. package/src/tui/hooks/useClipboardProvider.ts +42 -0
  110. package/src/tui/hooks/useGlobalKeyHandler.ts +54 -0
  111. package/src/tui/modals/CliModal.tsx +82 -0
  112. package/src/tui/modals/EditorModal.tsx +207 -0
  113. package/src/tui/modals/LogsModal.tsx +98 -0
  114. package/src/tui/registry.ts +102 -0
  115. package/src/tui/screens/CommandSelectScreen.tsx +162 -0
  116. package/src/tui/screens/ConfigScreen.tsx +160 -0
  117. package/src/tui/screens/ErrorScreen.tsx +58 -0
  118. package/src/tui/screens/ResultsScreen.tsx +60 -0
  119. package/src/tui/screens/RunningScreen.tsx +72 -0
  120. package/src/tui/screens/ScreenBase.ts +6 -0
  121. package/src/tui/semantic/Button.tsx +7 -0
  122. package/src/tui/semantic/Code.tsx +7 -0
  123. package/src/tui/semantic/CodeHighlight.tsx +7 -0
  124. package/src/tui/semantic/Container.tsx +7 -0
  125. package/src/tui/semantic/Field.tsx +7 -0
  126. package/src/tui/semantic/Label.tsx +7 -0
  127. package/src/tui/semantic/MenuButton.tsx +7 -0
  128. package/src/tui/semantic/MenuItem.tsx +7 -0
  129. package/src/tui/semantic/Overlay.tsx +7 -0
  130. package/src/tui/semantic/Panel.tsx +7 -0
  131. package/src/tui/semantic/ScrollView.tsx +9 -0
  132. package/src/tui/semantic/Select.tsx +7 -0
  133. package/src/tui/semantic/Spacer.tsx +7 -0
  134. package/src/tui/semantic/Spinner.tsx +7 -0
  135. package/src/tui/semantic/TextInput.tsx +7 -0
  136. package/src/tui/semantic/Value.tsx +7 -0
  137. package/src/tui/semantic/types.ts +195 -0
  138. package/src/tui/theme.ts +25 -14
  139. package/src/tui/utils/buildCliCommand.ts +1 -0
  140. package/src/tui/utils/getEnumKeys.ts +3 -0
  141. package/src/tui/utils/parameterPersistence.ts +1 -0
  142. package/src/types/command.ts +0 -60
  143. package/examples/tui-app/commands/index.ts +0 -4
  144. package/src/__tests__/colors.test.ts +0 -127
  145. package/src/__tests__/commandClass.test.ts +0 -130
  146. package/src/__tests__/help.test.ts +0 -412
  147. package/src/__tests__/registryNew.test.ts +0 -160
  148. package/src/__tests__/table.test.ts +0 -146
  149. package/src/__tests__/tui.test.ts +0 -26
  150. package/src/builtins/index.ts +0 -4
  151. package/src/cli/help.ts +0 -174
  152. package/src/cli/index.ts +0 -3
  153. package/src/cli/output/index.ts +0 -2
  154. package/src/cli/output/table.ts +0 -141
  155. package/src/commands/help.ts +0 -50
  156. package/src/commands/index.ts +0 -1
  157. package/src/components/index.ts +0 -147
  158. package/src/core/index.ts +0 -15
  159. package/src/hooks/index.ts +0 -131
  160. package/src/index.ts +0 -137
  161. package/src/registry/commandRegistry.ts +0 -77
  162. package/src/registry/index.ts +0 -1
  163. package/src/tui/TuiApp.tsx +0 -619
  164. package/src/tui/app.ts +0 -29
  165. package/src/tui/components/CliModal.tsx +0 -81
  166. package/src/tui/components/EditorModal.tsx +0 -177
  167. package/src/tui/components/LogsPanel.tsx +0 -86
  168. package/src/tui/components/index.ts +0 -13
  169. package/src/tui/context/index.ts +0 -7
  170. package/src/tui/hooks/index.ts +0 -35
  171. package/src/tui/hooks/useKeyboardHandler.ts +0 -91
  172. package/src/tui/hooks/useLogStream.ts +0 -96
  173. package/src/tui/index.ts +0 -65
  174. package/src/tui/utils/index.ts +0 -13
  175. package/src/types/index.ts +0 -1
@@ -1,26 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
- import {
3
- KeyboardPriority,
4
- } from "../tui/index.ts";
5
-
6
- describe("TUI", () => {
7
- describe("KeyboardPriority", () => {
8
- test("Modal has highest priority", () => {
9
- expect(KeyboardPriority.Modal).toBe(100);
10
- });
11
-
12
- test("Focused has medium priority", () => {
13
- expect(KeyboardPriority.Focused).toBe(50);
14
- });
15
-
16
- test("Global has lowest priority", () => {
17
- expect(KeyboardPriority.Global).toBe(0);
18
- });
19
-
20
- test("priorities are correctly ordered", () => {
21
- expect(KeyboardPriority.Modal).toBeGreaterThan(KeyboardPriority.Focused);
22
- expect(KeyboardPriority.Focused).toBeGreaterThan(KeyboardPriority.Global);
23
- });
24
- });
25
- });
26
-
@@ -1,4 +0,0 @@
1
- export { HelpCommand, createHelpCommandForParent, createRootHelpCommand } from "./help.ts";
2
- export { VersionCommand, createVersionCommand, formatVersion } from "./version.ts";
3
- export { SettingsCommand, createSettingsCommand } from "./settings.ts";
4
- export type { VersionConfig } from "./version.ts";
package/src/cli/help.ts DELETED
@@ -1,174 +0,0 @@
1
- import type { Command, OptionDef } from "../types/command.ts";
2
- import { colors } from "./output/colors.ts";
3
-
4
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
- type AnyCommand = Command<any, any>;
6
-
7
- /**
8
- * Format usage line for a command
9
- */
10
- export function formatUsage(
11
- command: AnyCommand,
12
- appName = "cli"
13
- ): string {
14
- const parts = [appName, command.name];
15
-
16
- if (command.subcommands && Object.keys(command.subcommands).length > 0) {
17
- parts.push("[command]");
18
- }
19
-
20
- if (command.options && Object.keys(command.options).length > 0) {
21
- parts.push("[options]");
22
- }
23
-
24
- return parts.join(" ");
25
- }
26
-
27
- /**
28
- * Format subcommands list
29
- */
30
- export function formatCommands(command: AnyCommand): string {
31
- if (!command.subcommands) return "";
32
-
33
- const entries = Object.entries(command.subcommands)
34
- .filter(([, cmd]) => !cmd.hidden)
35
- .map(([name, cmd]) => {
36
- const aliases = cmd.aliases?.length ? ` (${cmd.aliases.join(", ")})` : "";
37
- return ` ${colors.cyan(name)}${aliases} ${cmd.description}`;
38
- });
39
-
40
- if (entries.length === 0) return "";
41
-
42
- return ["Commands:", ...entries].join("\n");
43
- }
44
-
45
- /**
46
- * Format options list
47
- */
48
- export function formatOptions(command: AnyCommand): string {
49
- if (!command.options) return "";
50
-
51
- const entries = Object.entries(command.options).map(([name, defUntyped]) => {
52
- const def = defUntyped as OptionDef;
53
- const alias = def.alias ? `-${def.alias}, ` : " ";
54
- const flag = `${alias}--${name}`;
55
- const required = def.required ? colors.red("*") : "";
56
- const defaultVal = def.default !== undefined ? ` (default: ${def.default})` : "";
57
- const enumVals = def.enum ? ` [${def.enum.join("|")}]` : "";
58
-
59
- return ` ${colors.yellow(flag)}${required} ${def.description}${enumVals}${defaultVal}`;
60
- });
61
-
62
- if (entries.length === 0) return "";
63
-
64
- return ["Options:", ...entries].join("\n");
65
- }
66
-
67
- /**
68
- * Format examples list
69
- */
70
- export function formatExamples(command: AnyCommand): string {
71
- if (!command.examples?.length) return "";
72
-
73
- const entries = command.examples.map(
74
- (ex) => ` ${colors.dim("$")} ${ex.command}\n ${colors.dim(ex.description)}`
75
- );
76
-
77
- return ["Examples:", ...entries].join("\n");
78
- }
79
-
80
- /**
81
- * Format global options (log-level, detailed-logs)
82
- */
83
- export function formatGlobalOptions(): string {
84
- const entries = [
85
- ` ${colors.yellow("--log-level")} <level> Set log level [silly|trace|debug|info|warn|error|fatal]`,
86
- ` ${colors.yellow("--detailed-logs")} Enable detailed log output`,
87
- ` ${colors.yellow("--no-detailed-logs")} Disable detailed log output`,
88
- ];
89
-
90
- return ["Global Options:", ...entries].join("\n");
91
- }
92
-
93
- /**
94
- * Get command summary line
95
- */
96
- export function getCommandSummary(command: AnyCommand): string {
97
- const aliases = command.aliases?.length ? ` (${command.aliases.join(", ")})` : "";
98
- return `${command.name}${aliases}: ${command.description}`;
99
- }
100
-
101
- /**
102
- * Generate full help text for a command
103
- */
104
- export function generateHelp(
105
- command: AnyCommand,
106
- options: { appName?: string; version?: string } = {}
107
- ): string {
108
- const { appName = "cli", version } = options;
109
- const sections: string[] = [];
110
-
111
- // Header
112
- if (version) {
113
- sections.push(`${colors.bold(appName)} ${colors.dim(`v${version}`)}`);
114
- }
115
-
116
- // Description
117
- sections.push(command.description);
118
-
119
- // Usage
120
- sections.push(`\n${colors.bold("Usage:")}\n ${formatUsage(command, appName)}`);
121
-
122
- // Commands
123
- const commandsSection = formatCommands(command);
124
- if (commandsSection) {
125
- sections.push(`\n${commandsSection}`);
126
- }
127
-
128
- // Options
129
- const optionsSection = formatOptions(command);
130
- if (optionsSection) {
131
- sections.push(`\n${optionsSection}`);
132
- }
133
-
134
- // Examples
135
- const examplesSection = formatExamples(command);
136
- if (examplesSection) {
137
- sections.push(`\n${examplesSection}`);
138
- }
139
-
140
- return sections.join("\n");
141
- }
142
-
143
- /**
144
- * Generate help text for a specific command (includes global options)
145
- */
146
- export function generateCommandHelp(
147
- command: AnyCommand,
148
- appName = "cli"
149
- ): string {
150
- const sections: string[] = [];
151
-
152
- // Description
153
- sections.push(command.description);
154
-
155
- // Usage
156
- sections.push(`\n${colors.bold("Usage:")}\n ${formatUsage(command, appName)}`);
157
-
158
- // Options
159
- const optionsSection = formatOptions(command);
160
- if (optionsSection) {
161
- sections.push(`\n${optionsSection}`);
162
- }
163
-
164
- // Global Options
165
- sections.push(`\n${formatGlobalOptions()}`);
166
-
167
- // Examples
168
- const examplesSection = formatExamples(command);
169
- if (examplesSection) {
170
- sections.push(`\n${examplesSection}`);
171
- }
172
-
173
- return sections.join("\n");
174
- }
package/src/cli/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from "./parser.ts";
2
- export * from "./help.ts";
3
- export * from "./output/index.ts";
@@ -1,2 +0,0 @@
1
- export * from "./colors.ts";
2
- export * from "./table.ts";
@@ -1,141 +0,0 @@
1
- /**
2
- * Column configuration for tables
3
- */
4
- export interface ColumnConfig {
5
- key: string;
6
- header?: string;
7
- width?: number;
8
- align?: "left" | "right" | "center";
9
- formatter?: (value: unknown) => string;
10
- }
11
-
12
- export interface TableOptions {
13
- columns?: (string | ColumnConfig)[];
14
- showHeaders?: boolean;
15
- border?: boolean;
16
- }
17
-
18
- /**
19
- * Create a formatted table string
20
- */
21
- export function table<T extends Record<string, unknown>>(
22
- data: T[],
23
- options: TableOptions = {}
24
- ): string {
25
- if (data.length === 0) return "";
26
-
27
- const { showHeaders = true } = options;
28
-
29
- // Determine columns
30
- const columns: ColumnConfig[] = options.columns
31
- ? options.columns.map((col) =>
32
- typeof col === "string" ? { key: col, header: col } : col
33
- )
34
- : Object.keys(data[0] ?? {}).map((key) => ({ key, header: key }));
35
-
36
- // Calculate column widths
37
- const widths = columns.map((col) => {
38
- const headerWidth = (col.header ?? col.key).length;
39
- const maxDataWidth = Math.max(
40
- ...data.map((row) => {
41
- const value = row[col.key];
42
- const formatted = col.formatter ? col.formatter(value) : String(value ?? "");
43
- return formatted.length;
44
- })
45
- );
46
- return col.width ?? Math.max(headerWidth, maxDataWidth);
47
- });
48
-
49
- // Format a row
50
- const formatRow = (values: string[]): string => {
51
- return values
52
- .map((val, i) => {
53
- const width = widths[i] ?? 10;
54
- const col = columns[i];
55
- const align = col?.align ?? "left";
56
-
57
- if (align === "right") {
58
- return val.padStart(width);
59
- } else if (align === "center") {
60
- const leftPad = Math.floor((width - val.length) / 2);
61
- return val.padStart(leftPad + val.length).padEnd(width);
62
- }
63
- return val.padEnd(width);
64
- })
65
- .join(" ");
66
- };
67
-
68
- const rows: string[] = [];
69
-
70
- // Add header
71
- if (showHeaders) {
72
- const headers = columns.map((col) => col.header ?? col.key);
73
- rows.push(formatRow(headers));
74
- rows.push(widths.map((w) => "-".repeat(w)).join(" "));
75
- }
76
-
77
- // Add data rows
78
- for (const row of data) {
79
- const values = columns.map((col) => {
80
- const value = row[col.key];
81
- return col.formatter ? col.formatter(value) : String(value ?? "");
82
- });
83
- rows.push(formatRow(values));
84
- }
85
-
86
- return rows.join("\n");
87
- }
88
-
89
- /**
90
- * Create a key-value list
91
- */
92
- export function keyValueList(
93
- data: Record<string, unknown>,
94
- options: { separator?: string } = {}
95
- ): string {
96
- const { separator = ":" } = options;
97
-
98
- const entries = Object.entries(data);
99
- if (entries.length === 0) return "";
100
-
101
- const maxKeyLength = Math.max(...entries.map(([key]) => key.length));
102
-
103
- return entries
104
- .map(([key, value]) => `${key.padEnd(maxKeyLength)}${separator} ${value}`)
105
- .join("\n");
106
- }
107
-
108
- /**
109
- * Create a bullet list
110
- */
111
- export function bulletList(
112
- items: string[],
113
- options: { bullet?: string; indent?: number } = {}
114
- ): string {
115
- const { bullet = "•", indent = 0 } = options;
116
-
117
- if (items.length === 0) return "";
118
-
119
- const prefix = " ".repeat(indent);
120
- return items.map((item) => `${prefix}${bullet} ${item}`).join("\n");
121
- }
122
-
123
- /**
124
- * Create a numbered list
125
- */
126
- export function numberedList(
127
- items: string[],
128
- options: { start?: number; indent?: number } = {}
129
- ): string {
130
- const { start = 1, indent = 0 } = options;
131
-
132
- if (items.length === 0) return "";
133
-
134
- const prefix = " ".repeat(indent);
135
- const maxNum = start + items.length - 1;
136
- const numWidth = String(maxNum).length;
137
-
138
- return items
139
- .map((item, i) => `${prefix}${String(start + i).padStart(numWidth)}. ${item}`)
140
- .join("\n");
141
- }
@@ -1,50 +0,0 @@
1
- import { defineCommand, type Command } from "../types/command.ts";
2
- import { generateHelp } from "../cli/help.ts";
3
-
4
- interface HelpCommandOptions {
5
- getCommands: () => Command[];
6
- appName?: string;
7
- version?: string;
8
- }
9
-
10
- /**
11
- * Create a help command
12
- */
13
- export function createHelpCommand(options: HelpCommandOptions) {
14
- const { getCommands, appName = "cli", version } = options;
15
-
16
- return defineCommand({
17
- name: "help",
18
- description: "Show help information",
19
- aliases: ["--help", "-h"],
20
- hidden: true,
21
- options: {
22
- command: {
23
- type: "string" as const,
24
- description: "Command to show help for",
25
- },
26
- },
27
- execute: (ctx) => {
28
- const commands = getCommands();
29
- const commandName = ctx.options["command"];
30
-
31
- if (commandName && typeof commandName === "string") {
32
- const cmd = commands.find((c) => c.name === commandName);
33
- if (cmd) {
34
- console.log(generateHelp(cmd, { appName, version }));
35
- return;
36
- }
37
- }
38
-
39
- // Show root help
40
- const rootCommand = defineCommand({
41
- name: appName,
42
- description: `${appName} CLI`,
43
- subcommands: Object.fromEntries(commands.map((c) => [c.name, c])),
44
- execute: () => {},
45
- });
46
-
47
- console.log(generateHelp(rootCommand, { appName, version }));
48
- },
49
- });
50
- }
@@ -1 +0,0 @@
1
- export * from "./help.ts";
@@ -1,147 +0,0 @@
1
- import React from "react";
2
-
3
- /**
4
- * Box component props
5
- */
6
- export interface BoxProps {
7
- children?: React.ReactNode;
8
- flexDirection?: "row" | "column";
9
- padding?: number;
10
- margin?: number;
11
- borderStyle?: "single" | "double" | "round" | "none";
12
- }
13
-
14
- /**
15
- * Box component for layout
16
- */
17
- export function Box({ children }: BoxProps) {
18
- return React.createElement("div", null, children);
19
- }
20
-
21
- /**
22
- * Text component props
23
- */
24
- export interface TextProps {
25
- children?: React.ReactNode;
26
- color?: string;
27
- bold?: boolean;
28
- dim?: boolean;
29
- }
30
-
31
- /**
32
- * Text component for styled text
33
- */
34
- export function Text({ children }: TextProps) {
35
- return React.createElement("span", null, children);
36
- }
37
-
38
- /**
39
- * Input component props
40
- */
41
- export interface InputProps {
42
- value: string;
43
- onChange: (value: string) => void;
44
- placeholder?: string;
45
- disabled?: boolean;
46
- }
47
-
48
- /**
49
- * Input component
50
- */
51
- export function Input({ value, onChange, placeholder }: InputProps) {
52
- return React.createElement("input", {
53
- value,
54
- onChange: (e: { target: { value: string } }) => onChange(e.target.value),
55
- placeholder,
56
- });
57
- }
58
-
59
- /**
60
- * Select option
61
- */
62
- export interface SelectOption {
63
- label: string;
64
- value: string;
65
- }
66
-
67
- /**
68
- * Select component props
69
- */
70
- export interface SelectProps {
71
- options: SelectOption[];
72
- value?: string;
73
- onChange: (value: string) => void;
74
- }
75
-
76
- /**
77
- * Select component
78
- */
79
- export function Select({ options, value, onChange }: SelectProps) {
80
- return React.createElement(
81
- "select",
82
- { value, onChange: (e: { target: { value: string } }) => onChange(e.target.value) },
83
- options.map((opt) =>
84
- React.createElement("option", { key: opt.value, value: opt.value }, opt.label)
85
- )
86
- );
87
- }
88
-
89
- /**
90
- * Button component props
91
- */
92
- export interface ButtonProps {
93
- label: string;
94
- onPress: () => void;
95
- disabled?: boolean;
96
- variant?: "primary" | "secondary" | "danger";
97
- }
98
-
99
- /**
100
- * Button component
101
- */
102
- export function Button({ label, onPress, disabled }: ButtonProps) {
103
- return React.createElement("button", { onClick: onPress, disabled }, label);
104
- }
105
-
106
- /**
107
- * Modal component props
108
- */
109
- export interface ModalProps {
110
- isOpen: boolean;
111
- onClose: () => void;
112
- title?: string;
113
- children: React.ReactNode;
114
- }
115
-
116
- /**
117
- * Modal component
118
- */
119
- export function Modal({ isOpen, onClose, title, children }: ModalProps) {
120
- if (!isOpen) return null;
121
-
122
- return React.createElement(
123
- "div",
124
- { className: "modal" },
125
- React.createElement(
126
- "div",
127
- { className: "modal-content" },
128
- title && React.createElement("h2", null, title),
129
- children,
130
- React.createElement("button", { onClick: onClose }, "Close")
131
- )
132
- );
133
- }
134
-
135
- /**
136
- * Spinner component props
137
- */
138
- export interface SpinnerProps {
139
- label?: string;
140
- }
141
-
142
- /**
143
- * Spinner component
144
- */
145
- export function Spinner({ label }: SpinnerProps) {
146
- return React.createElement("span", null, label ?? "Loading...");
147
- }
package/src/core/index.ts DELETED
@@ -1,15 +0,0 @@
1
- // Core exports
2
- export { Application, type ApplicationConfig, type ApplicationHooks, type GlobalOptions } from "./application.ts";
3
- export { AppContext, type AppConfig } from "./context.ts";
4
- export { Command, ConfigValidationError, AbortError, type AnyCommand, type CommandExample, type CommandResult, type CommandExecutionContext } from "./command.ts";
5
- export { CommandRegistry, type ResolveResult } from "./registry.ts";
6
- export { Logger, createLogger, LogLevel, type LoggerConfig, type LogEvent } from "./logger.ts";
7
- export {
8
- generateCommandHelp,
9
- generateAppHelp,
10
- formatUsage,
11
- formatSubCommands,
12
- formatOptions,
13
- formatExamples,
14
- type HelpOptions,
15
- } from "./help.ts";
@@ -1,131 +0,0 @@
1
- import { useState, useCallback } from "react";
2
- import type { Command, OptionSchema, OptionValues } from "../types/command.ts";
3
-
4
- /**
5
- * Hook for command execution
6
- */
7
- export function useCommand<T extends OptionSchema>(command: Command<T>) {
8
- const [isExecuting, setIsExecuting] = useState(false);
9
- const [error, setError] = useState<Error | null>(null);
10
-
11
- const execute = useCallback(
12
- async (options: OptionValues<T>) => {
13
- setIsExecuting(true);
14
- setError(null);
15
-
16
- try {
17
- await command.execute({
18
- options,
19
- args: [],
20
- commandPath: [command.name],
21
- });
22
- } catch (err) {
23
- setError(err instanceof Error ? err : new Error(String(err)));
24
- } finally {
25
- setIsExecuting(false);
26
- }
27
- },
28
- [command]
29
- );
30
-
31
- return { execute, isExecuting, error };
32
- }
33
-
34
- /**
35
- * Hook for managing options state
36
- */
37
- export function useOptions<T extends OptionSchema>(
38
- schema: T,
39
- initialValues?: Partial<OptionValues<T>>
40
- ) {
41
- const [values, setValues] = useState<OptionValues<T>>(() => {
42
- const defaults: Record<string, unknown> = {};
43
- for (const [key, def] of Object.entries(schema)) {
44
- defaults[key] = initialValues?.[key as keyof T] ?? def.default;
45
- }
46
- return defaults as OptionValues<T>;
47
- });
48
-
49
- const setValue = useCallback(
50
- <K extends keyof T>(key: K, value: OptionValues<T>[K]) => {
51
- setValues((prev) => ({ ...prev, [key]: value }));
52
- },
53
- []
54
- );
55
-
56
- const reset = useCallback(() => {
57
- const defaults: Record<string, unknown> = {};
58
- for (const [key, def] of Object.entries(schema)) {
59
- defaults[key] = def.default;
60
- }
61
- setValues(defaults as OptionValues<T>);
62
- }, [schema]);
63
-
64
- return { values, setValue, setValues, reset };
65
- }
66
-
67
- /**
68
- * Hook for navigation between views
69
- */
70
- export function useNavigation(views: string[], initialView?: string) {
71
- const [currentView, setCurrentView] = useState(initialView ?? views[0] ?? "");
72
- const [history, setHistory] = useState<string[]>([]);
73
-
74
- const navigate = useCallback((view: string) => {
75
- setHistory((prev) => [...prev, currentView]);
76
- setCurrentView(view);
77
- }, [currentView]);
78
-
79
- const goBack = useCallback(() => {
80
- const prev = history[history.length - 1];
81
- if (prev) {
82
- setHistory((h) => h.slice(0, -1));
83
- setCurrentView(prev);
84
- }
85
- }, [history]);
86
-
87
- const canGoBack = history.length > 0;
88
-
89
- return { currentView, navigate, goBack, canGoBack };
90
- }
91
-
92
- /**
93
- * Hook for modal state
94
- */
95
- export function useModal(initialOpen = false) {
96
- const [isOpen, setIsOpen] = useState(initialOpen);
97
-
98
- const open = useCallback(() => setIsOpen(true), []);
99
- const close = useCallback(() => setIsOpen(false), []);
100
- const toggle = useCallback(() => setIsOpen((prev) => !prev), []);
101
-
102
- return { isOpen, open, close, toggle };
103
- }
104
-
105
- /**
106
- * Hook for async operations
107
- */
108
- export function useAsync<T>(asyncFn: () => Promise<T>) {
109
- const [data, setData] = useState<T | null>(null);
110
- const [isLoading, setIsLoading] = useState(false);
111
- const [error, setError] = useState<Error | null>(null);
112
-
113
- const execute = useCallback(async () => {
114
- setIsLoading(true);
115
- setError(null);
116
-
117
- try {
118
- const result = await asyncFn();
119
- setData(result);
120
- return result;
121
- } catch (err) {
122
- const error = err instanceof Error ? err : new Error(String(err));
123
- setError(error);
124
- throw error;
125
- } finally {
126
- setIsLoading(false);
127
- }
128
- }, [asyncFn]);
129
-
130
- return { data, isLoading, error, execute };
131
- }