citadel_cli 1.0.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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +337 -0
  3. package/dist/.vite/manifest.json +12 -0
  4. package/dist/citadel.css +1 -0
  5. package/dist/citadel.es.js +3039 -0
  6. package/dist/citadel.umd.js +1015 -0
  7. package/dist/command_examples/basic-commands.d.ts +83 -0
  8. package/dist/dist/styles.css +789 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/src/App.d.ts +4 -0
  11. package/dist/src/__test-utils__/factories.d.ts +21 -0
  12. package/dist/src/components/Citadel/Citadel.d.ts +11 -0
  13. package/dist/src/components/Citadel/Cursor.d.ts +9 -0
  14. package/dist/src/components/Citadel/__tests__/Citadel.test.d.ts +1 -0
  15. package/dist/src/components/Citadel/commands/history-commands.d.ts +2 -0
  16. package/dist/src/components/Citadel/components/AvailableCommands.d.ts +9 -0
  17. package/dist/src/components/Citadel/components/CommandInput.d.ts +10 -0
  18. package/dist/src/components/Citadel/components/CommandOutput.d.ts +8 -0
  19. package/dist/src/components/Citadel/components/CommandOutputLine.d.ts +9 -0
  20. package/dist/src/components/Citadel/components/Spinner.d.ts +2 -0
  21. package/dist/src/components/Citadel/components/__tests__/AvailableCommands.test.d.ts +1 -0
  22. package/dist/src/components/Citadel/components/__tests__/CommandInput.test.d.ts +1 -0
  23. package/dist/src/components/Citadel/components/__tests__/CommandOutput.test.d.ts +1 -0
  24. package/dist/src/components/Citadel/components/__tests__/CommandOutputLine.test.d.ts +1 -0
  25. package/dist/src/components/Citadel/components/__tests__/Spinner.test.d.ts +1 -0
  26. package/dist/src/components/Citadel/config/CitadelConfigContext.d.ts +11 -0
  27. package/dist/src/components/Citadel/config/__tests__/CitadelConfigContext.test.d.ts +1 -0
  28. package/dist/src/components/Citadel/config/defaults.d.ts +26 -0
  29. package/dist/src/components/Citadel/config/types.d.ts +56 -0
  30. package/dist/src/components/Citadel/hooks/__tests__/useCitadelState.test.d.ts +1 -0
  31. package/dist/src/components/Citadel/hooks/__tests__/useCommandHistory.test.d.ts +1 -0
  32. package/dist/src/components/Citadel/hooks/__tests__/useCommandParser.test.d.ts +1 -0
  33. package/dist/src/components/Citadel/hooks/__tests__/useCommandTrie.test.d.ts +1 -0
  34. package/dist/src/components/Citadel/hooks/useCitadelState.d.ts +6 -0
  35. package/dist/src/components/Citadel/hooks/useCommandHistory.d.ts +17 -0
  36. package/dist/src/components/Citadel/hooks/useCommandParser.d.ts +19 -0
  37. package/dist/src/components/Citadel/hooks/useCommandTrie.d.ts +2 -0
  38. package/dist/src/components/Citadel/hooks/useGlobalShortcut.d.ts +8 -0
  39. package/dist/src/components/Citadel/hooks/useSlideAnimation.d.ts +14 -0
  40. package/dist/src/components/Citadel/index.d.ts +2 -0
  41. package/dist/src/components/Citadel/services/HistoryService.d.ts +15 -0
  42. package/dist/src/components/Citadel/storage/BaseStorage.d.ts +25 -0
  43. package/dist/src/components/Citadel/storage/LocalStorage.d.ts +12 -0
  44. package/dist/src/components/Citadel/storage/MemoryStorage.d.ts +12 -0
  45. package/dist/src/components/Citadel/storage/StorageFactory.d.ts +9 -0
  46. package/dist/src/components/Citadel/storage/__tests__/LocalStorage.test.d.ts +1 -0
  47. package/dist/src/components/Citadel/storage/__tests__/MemoryStorage.test.d.ts +1 -0
  48. package/dist/src/components/Citadel/types/__tests__/command-trie.test.d.ts +1 -0
  49. package/dist/src/components/Citadel/types/command-context.d.ts +4 -0
  50. package/dist/src/components/Citadel/types/command-results.d.ts +41 -0
  51. package/dist/src/components/Citadel/types/command-trie.d.ts +238 -0
  52. package/dist/src/components/Citadel/types/cursor.d.ts +26 -0
  53. package/dist/src/components/Citadel/types/help-command.d.ts +3 -0
  54. package/dist/src/components/Citadel/types/index.d.ts +3 -0
  55. package/dist/src/components/Citadel/types/state.d.ts +40 -0
  56. package/dist/src/components/Citadel/types/storage.d.ts +44 -0
  57. package/dist/src/components/Citadel/utils/keySimulation.d.ts +2 -0
  58. package/dist/src/index.d.ts +4 -0
  59. package/dist/src/main.d.ts +1 -0
  60. package/dist/src/test/setup.d.ts +1 -0
  61. package/package.json +73 -0
@@ -0,0 +1,238 @@
1
+ import { CommandResult } from './command-results';
2
+ /** Function type for handling command execution */
3
+ export type CommandHandler = (args: string[]) => Promise<CommandResult>;
4
+ /**
5
+ * Represents an argument that can be passed to a command
6
+ */
7
+ export interface CommandArgument {
8
+ name: string;
9
+ description: string;
10
+ }
11
+ export interface CommandNodeParams {
12
+ fullPath: string[];
13
+ description: string;
14
+ parent?: CommandNode;
15
+ argument?: CommandArgument;
16
+ handler?: CommandHandler;
17
+ }
18
+ export interface CommandSignature {
19
+ signature: string[];
20
+ }
21
+ /**
22
+ * A no-op handler that returns an empty string. Used as the default handler
23
+ * for CommandNodes that don't specify a handler.
24
+ */
25
+ export declare const NoopHandler: CommandHandler;
26
+ export declare class CommandNode {
27
+ private _fullPath;
28
+ private _description;
29
+ private _children;
30
+ private _argument?;
31
+ private _handler;
32
+ private _parent?;
33
+ private _signature?;
34
+ /**
35
+ * Creates a new CommandNode representing a command the user can enter. From a
36
+ * high level, a command is one or more words followed by an optional
37
+ * argument, and with an optional handler.
38
+ *
39
+ * @param params Configuration parameters for the node
40
+ * @param params.fullPath Complete path from root to this node (e.g., ['service', 'deploy'])
41
+ * @param params.description Human-readable description of the command
42
+ * @param params.parent Optional parent node in the command hierarchy
43
+ * @param params.handler Optional async function to execute when command is invoked
44
+ * @param params.argument Optional argument definition for the command
45
+ * @throws {Error} If fullPath is empty or undefined
46
+ *
47
+ */
48
+ constructor(params: CommandNodeParams);
49
+ /**
50
+ * Gets the name of this command (last segment of the path)
51
+ */
52
+ get name(): string;
53
+ /**
54
+ * Whether this is a leaf node (has no children)
55
+ */
56
+ get isLeaf(): boolean;
57
+ /**
58
+ * Whether this command has a handler
59
+ */
60
+ get hasHandler(): boolean;
61
+ /**
62
+ * Whether this command requires an argument
63
+ */
64
+ get requiresArgument(): boolean;
65
+ /**
66
+ * Gets the parent node if it exists
67
+ */
68
+ get parent(): CommandNode | undefined;
69
+ /**
70
+ * Gets the command's signature
71
+ */
72
+ get signature(): string | undefined;
73
+ /**
74
+ * Sets the signature for this command based on the current command trie state
75
+ * @param trie The command trie to use for signature generation
76
+ */
77
+ setSignature(trie: CommandTrie): void;
78
+ /**
79
+ * Gets the map of child commands
80
+ */
81
+ get children(): ReadonlyMap<string, CommandNode>;
82
+ /**
83
+ * Whether this command has any children
84
+ */
85
+ get hasChildren(): boolean;
86
+ /**
87
+ * Adds a child command
88
+ */
89
+ addChild(segment: string, node: CommandNode): void;
90
+ /**
91
+ * Gets a child command by name
92
+ */
93
+ getChild(name: string): CommandNode | undefined;
94
+ /**
95
+ * Gets the full path from root to this command
96
+ */
97
+ get fullPath(): string[];
98
+ /**
99
+ * Gets the command's description
100
+ */
101
+ get description(): string;
102
+ /**
103
+ * Gets the command's argument definition if it exists
104
+ */
105
+ get argument(): CommandArgument | undefined;
106
+ /**
107
+ * Sets the command's argument definition
108
+ */
109
+ set argument(value: CommandArgument | undefined);
110
+ /**
111
+ * Gets the command's handler
112
+ */
113
+ get handler(): CommandHandler;
114
+ /**
115
+ * Sets the command's handler
116
+ */
117
+ set handler(value: CommandHandler);
118
+ }
119
+ /**
120
+ * A trie data structure for managing hierarchical commands.
121
+ * Provides functionality for adding commands, retrieving commands,
122
+ * and getting command completions.
123
+ */
124
+ export declare class CommandTrie {
125
+ private readonly _root;
126
+ /**
127
+ * Creates a new CommandTrie instance.
128
+ */
129
+ constructor();
130
+ /**
131
+ * Adds a new command to the trie.
132
+ *
133
+ * @param params Parameters for the command.
134
+ * @param params.path The path segments for the command (e.g., ['service', 'deploy'])
135
+ * @param params.description Description of what the command does
136
+ * @param params.handler Optional function to execute when command is invoked
137
+ * @param params.argument Optional argument definition for the command
138
+ * @throws {Error} If attempting to add a duplicate leaf command or a subcommand to a leaf
139
+ *
140
+ */
141
+ addCommand(params: Omit<ConstructorParameters<typeof CommandNode>[0], 'fullPath' | 'parent'> & {
142
+ path: string[];
143
+ }): void;
144
+ /**
145
+ * Retrieves a command from the trie.
146
+ *
147
+ * @param path The path of the command.
148
+ * @returns The command node or undefined if not found.
149
+ */
150
+ getCommand(path: string[]): CommandNode | undefined;
151
+ /**
152
+ * Gets command completions for a given path.
153
+ *
154
+ * @param path The path to get completions for.
155
+ * @returns An array of completion strings.
156
+ */
157
+ getCompletions(path: string[]): string[];
158
+ /**
159
+ * Executes a command with the given path and arguments.
160
+ * @param path The command path
161
+ * @param args Arguments to pass to the command handler
162
+ * @returns The command result or undefined if command not found
163
+ * @throws Error if command validation fails
164
+ */
165
+ executeCommand(path: string[], args?: string[]): Promise<CommandResult | undefined>;
166
+ /**
167
+ * Gets the root commands in the trie.
168
+ *
169
+ * @returns An array of root command nodes.
170
+ */
171
+ getRootCommands(): CommandNode[];
172
+ /**
173
+ * Gets the leaf commands in the trie.
174
+ *
175
+ * @returns An array of leaf command nodes.
176
+ */
177
+ getLeafCommands(): CommandNode[];
178
+ /**
179
+ * Retrieves a command using its unique signature.
180
+ * A signature is the minimal sequence of prefixes that uniquely identifies a command.
181
+ *
182
+ * @param signature Array of minimal prefixes that uniquely identify the command
183
+ * @returns The matching command node or undefined if not found
184
+ *
185
+ * @example
186
+ * // Will match 'image random cat' command
187
+ * getCommandBySignature(['i', 'r', 'c'])
188
+ * // Will match 'user show' command (not ambiguous with 'user status')
189
+ * getCommandBySignature(['u', 'sh'])
190
+ */
191
+ getCommandBySignature(signature: CommandSignature): CommandNode | undefined;
192
+ /**
193
+ * Generates a minimal unique signature for a command node.
194
+ * The signature consists of the shortest prefixes that uniquely identify each segment
195
+ * in the path from root to this node.
196
+ *
197
+ * @param command The command node to generate a signature for
198
+ * @returns Array of minimal prefixes that uniquely identify the command
199
+ *
200
+ * @example
201
+ * // For commands: ['image', 'random', 'cat'] and ['image', 'random', 'dog']
202
+ * buildSignatureForCommand(catCommand) // returns ['i', 'r', 'c']
203
+ * // For commands: ['user', 'show'] and ['user', 'status']
204
+ * buildSignatureForCommand(showCommand) // returns ['u', 'sh']
205
+ */
206
+ buildSignatureForCommand(command: CommandNode): CommandSignature;
207
+ /**
208
+ * Validates the command trie structure for common errors.
209
+ *
210
+ * Performs the following validations:
211
+ * 1. Checks for duplicate command paths
212
+ * 2. Ensures non-leaf nodes (nodes with children) use NoopHandler
213
+ * 3. Ensures non-leaf nodes don't have arguments
214
+ * 4. Verifies all nodes have a handler (either custom or NoopHandler)
215
+ * 5. Validates command path uniqueness
216
+ *
217
+ * @returns An object containing:
218
+ * - isValid: boolean indicating if the trie is valid
219
+ * - errors: array of error messages describing any validation failures
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const result = commandTrie.validate();
224
+ * if (!result.isValid) {
225
+ * console.error('Command trie validation failed:');
226
+ * result.errors.forEach(error => console.error(error));
227
+ * }
228
+ * ```
229
+ */
230
+ /**
231
+ * Updates signatures for all nodes in the trie
232
+ */
233
+ setSignatures(): void;
234
+ validate(): {
235
+ isValid: boolean;
236
+ errors: string[];
237
+ };
238
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Defines the available cursor animation types.
3
+ * - 'blink': Traditional blinking cursor
4
+ * - 'spin': Spinning animation cursor
5
+ * - 'solid': Static cursor
6
+ * - 'bbs': BBS-style cursor
7
+ */
8
+ export type CursorType = 'blink' | 'spin' | 'solid' | 'bbs';
9
+ /**
10
+ * Configuration interface for cursor appearance and behavior
11
+ */
12
+ export interface CursorStyle {
13
+ /** The type of cursor animation */
14
+ type: CursorType;
15
+ /** The character to display as the cursor */
16
+ character?: string;
17
+ /** Animation speed in milliseconds */
18
+ speed?: number;
19
+ /** CSS color value for the cursor */
20
+ color?: string;
21
+ }
22
+ /**
23
+ * Default configurations for each cursor type
24
+ * Provides complete configurations with all optional properties defined
25
+ */
26
+ export declare const DEFAULT_CURSOR_CONFIGS: Record<CursorType, Required<Omit<CursorStyle, 'type'>>>;
@@ -0,0 +1,3 @@
1
+ import { CommandNode, CommandTrie } from './command-trie';
2
+ import { CitadelConfig } from '../config/types';
3
+ export declare const createHelpCommand: (trie: CommandTrie, config: CitadelConfig) => [string, CommandNode];
@@ -0,0 +1,3 @@
1
+ export * from './state';
2
+ export * from './cursor';
3
+ export * from './command-results';
@@ -0,0 +1,40 @@
1
+ import { CommandNode } from './command-trie';
2
+ import { CommandResult } from './command-results';
3
+ import { CommandStorage, StoredCommand } from './storage';
4
+ export declare class OutputItem {
5
+ readonly timestamp: number;
6
+ readonly command: string[];
7
+ result: CommandResult;
8
+ constructor(command: string[], result?: CommandResult);
9
+ }
10
+ export interface CitadelState {
11
+ commandStack: string[];
12
+ currentInput: string;
13
+ isEnteringArg: boolean;
14
+ currentNode?: CommandNode;
15
+ output: OutputItem[];
16
+ validation: {
17
+ isValid: boolean;
18
+ message?: string;
19
+ };
20
+ history: {
21
+ commands: StoredCommand[];
22
+ position: number | null;
23
+ savedInput: string | null;
24
+ storage?: CommandStorage;
25
+ };
26
+ }
27
+ export interface CitadelActions {
28
+ setCommandStack: (stack: string[]) => void;
29
+ setCurrentInput: (input: string) => void;
30
+ setIsEnteringArg: (isEntering: boolean) => void;
31
+ setCurrentNode: (node: CommandNode | undefined) => void;
32
+ addOutput: (output: OutputItem) => void;
33
+ setValidation: (validation: {
34
+ isValid: boolean;
35
+ message?: string;
36
+ }) => void;
37
+ executeCommand: (path: string[], args?: string[]) => Promise<void>;
38
+ executeHistoryCommand: (index: number) => Promise<void>;
39
+ clearHistory: () => Promise<void>;
40
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Supported storage mechanisms for command history
3
+ */
4
+ export type StorageType = 'localStorage' | 'memory';
5
+ /**
6
+ * Configuration options for command history storage
7
+ */
8
+ export interface StorageConfig {
9
+ /**
10
+ * The storage mechanism to use for command history.
11
+ * Defaults to 'localStorage' with fallback to 'memory' if unavailable.
12
+ */
13
+ type?: StorageType;
14
+ /**
15
+ * Maximum number of commands to store in history.
16
+ * When exceeded, oldest commands will be removed.
17
+ * Default is 100.
18
+ */
19
+ maxCommands?: number;
20
+ }
21
+ /**
22
+ * Represents a command entry to be stored in history
23
+ */
24
+ export interface StoredCommand {
25
+ inputs: string[];
26
+ timestamp: number;
27
+ }
28
+ /**
29
+ * Interface for command history storage implementations
30
+ */
31
+ export interface CommandStorage {
32
+ /**
33
+ * Add a command to storage
34
+ */
35
+ addCommand: (command: StoredCommand) => Promise<void>;
36
+ /**
37
+ * Get all stored commands
38
+ */
39
+ getCommands: () => Promise<StoredCommand[]>;
40
+ /**
41
+ * Clear all stored commands
42
+ */
43
+ clear: () => Promise<void>;
44
+ }
@@ -0,0 +1,2 @@
1
+ import { CitadelState, CitadelActions } from '../types/state';
2
+ export declare const simulateKeyPress: (key: string, state: CitadelState, actions: CitadelActions) => void;
@@ -0,0 +1,4 @@
1
+ export { Citadel } from './components/Citadel/Citadel';
2
+ export type { CitadelProps } from './components/Citadel/Citadel';
3
+ export type { CitadelConfig } from './components/Citadel/config/types';
4
+ export * from './components/Citadel/types';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "citadel_cli",
3
+ "description": "A hierarchical command line and console for your webapp",
4
+ "keywords": [
5
+ "react",
6
+ "command",
7
+ "cli",
8
+ "interface",
9
+ "console"
10
+ ],
11
+ "author": "James Childers <james.childers@gmail.com>",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/jchilders/citadel_react.git"
16
+ },
17
+ "version": "1.0.0",
18
+ "type": "module",
19
+ "scripts": {
20
+ "build": "tsc && vite build",
21
+ "dev": "vite",
22
+ "lint": "eslint .",
23
+ "preview": "vite preview",
24
+ "test": "vitest --run",
25
+ "coverage": "vitest run --coverage"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "style": "./dist/citadel.css",
31
+ "import": "./dist/citadel.es.js",
32
+ "require": "./dist/citadel.umd.cjs"
33
+ },
34
+ "./styles.css": "./dist/styles.css",
35
+ "./citadel.css": "./dist/citadel.css"
36
+ },
37
+ "types": "./dist/index.d.ts",
38
+ "main": "./dist/citadel.umd.cjs",
39
+ "module": "./dist/citadel.es.js",
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "dependencies": {
44
+ "lucide-react": "^0.460.0",
45
+ "react": "^18.2.0",
46
+ "react-dom": "^18.2.0"
47
+ },
48
+ "devDependencies": {
49
+ "@eslint/js": "^9.13.0",
50
+ "@testing-library/jest-dom": "^6.6.3",
51
+ "@testing-library/react": "^16.1.0",
52
+ "@testing-library/user-event": "^14.5.2",
53
+ "@types/react": "^18.2.64",
54
+ "@types/react-dom": "^18.2.21",
55
+ "@vitejs/plugin-react": "^4.3.3",
56
+ "@vitest/coverage-v8": "^2.1.6",
57
+ "autoprefixer": "^10.4.20",
58
+ "eslint": "^9.13.0",
59
+ "eslint-plugin-react-hooks": "^5.0.0",
60
+ "eslint-plugin-react-refresh": "^0.4.14",
61
+ "globals": "^15.11.0",
62
+ "jsdom": "^25.0.1",
63
+ "playwright": "^1.49.0",
64
+ "postcss": "^8.4.49",
65
+ "tailwindcss": "^3.4.17",
66
+ "typescript": "^5.4.2",
67
+ "typescript-eslint": "^8.11.0",
68
+ "vite": "^5.1.5",
69
+ "vite-plugin-dts": "^4.5.0",
70
+ "vitest": "^2.1.6",
71
+ "webpack-cli": "^6.0.1"
72
+ }
73
+ }