uifork 0.0.1 → 0.0.2

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 (80) hide show
  1. package/README.md +128 -2
  2. package/cli.js +450 -0
  3. package/dist/components/BranchedComponent.d.ts +10 -0
  4. package/dist/components/BranchedComponent.d.ts.map +1 -0
  5. package/dist/components/ComponentSelector.d.ts +21 -0
  6. package/dist/components/ComponentSelector.d.ts.map +1 -0
  7. package/dist/components/MenuItem.d.ts +13 -0
  8. package/dist/components/MenuItem.d.ts.map +1 -0
  9. package/dist/components/SettingsView.d.ts +12 -0
  10. package/dist/components/SettingsView.d.ts.map +1 -0
  11. package/dist/components/Tooltip.d.ts +9 -0
  12. package/dist/components/Tooltip.d.ts.map +1 -0
  13. package/dist/components/UIFork.d.ts +21 -0
  14. package/dist/components/UIFork.d.ts.map +1 -0
  15. package/dist/components/VersionActionMenu.d.ts +17 -0
  16. package/dist/components/VersionActionMenu.d.ts.map +1 -0
  17. package/dist/components/VersionItem.d.ts +23 -0
  18. package/dist/components/VersionItem.d.ts.map +1 -0
  19. package/dist/components/VersionNameEditor.d.ts +10 -0
  20. package/dist/components/VersionNameEditor.d.ts.map +1 -0
  21. package/dist/components/VersionsList.d.ts +28 -0
  22. package/dist/components/VersionsList.d.ts.map +1 -0
  23. package/dist/components/icons/BranchIcon.d.ts +6 -0
  24. package/dist/components/icons/BranchIcon.d.ts.map +1 -0
  25. package/dist/components/icons/CancelIcon.d.ts +6 -0
  26. package/dist/components/icons/CancelIcon.d.ts.map +1 -0
  27. package/dist/components/icons/CheckmarkIcon.d.ts +6 -0
  28. package/dist/components/icons/CheckmarkIcon.d.ts.map +1 -0
  29. package/dist/components/icons/ChevronDownIcon.d.ts +6 -0
  30. package/dist/components/icons/ChevronDownIcon.d.ts.map +1 -0
  31. package/dist/components/icons/ChevronRightIcon.d.ts +6 -0
  32. package/dist/components/icons/ChevronRightIcon.d.ts.map +1 -0
  33. package/dist/components/icons/CopyIcon.d.ts +6 -0
  34. package/dist/components/icons/CopyIcon.d.ts.map +1 -0
  35. package/dist/components/icons/DeleteIcon.d.ts +6 -0
  36. package/dist/components/icons/DeleteIcon.d.ts.map +1 -0
  37. package/dist/components/icons/GearIcon.d.ts +6 -0
  38. package/dist/components/icons/GearIcon.d.ts.map +1 -0
  39. package/dist/components/icons/GitForkIcon.d.ts +6 -0
  40. package/dist/components/icons/GitForkIcon.d.ts.map +1 -0
  41. package/dist/components/icons/MoreOptionsIcon.d.ts +6 -0
  42. package/dist/components/icons/MoreOptionsIcon.d.ts.map +1 -0
  43. package/dist/components/icons/OpenInEditorIcon.d.ts +6 -0
  44. package/dist/components/icons/OpenInEditorIcon.d.ts.map +1 -0
  45. package/dist/components/icons/PlusIcon.d.ts +6 -0
  46. package/dist/components/icons/PlusIcon.d.ts.map +1 -0
  47. package/dist/components/icons/PromoteIcon.d.ts +6 -0
  48. package/dist/components/icons/PromoteIcon.d.ts.map +1 -0
  49. package/dist/components/icons/RenameIcon.d.ts +6 -0
  50. package/dist/components/icons/RenameIcon.d.ts.map +1 -0
  51. package/dist/hooks/useClickOutside.d.ts +17 -0
  52. package/dist/hooks/useClickOutside.d.ts.map +1 -0
  53. package/dist/hooks/useComponentDiscovery.d.ts +18 -0
  54. package/dist/hooks/useComponentDiscovery.d.ts.map +1 -0
  55. package/dist/hooks/useKeyboardShortcuts.d.ts +28 -0
  56. package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  57. package/dist/hooks/useLocalStorage.d.ts +5 -0
  58. package/dist/hooks/useLocalStorage.d.ts.map +1 -0
  59. package/dist/hooks/usePopoverPosition.d.ts +13 -0
  60. package/dist/hooks/usePopoverPosition.d.ts.map +1 -0
  61. package/dist/hooks/useVersionManagement.d.ts +18 -0
  62. package/dist/hooks/useVersionManagement.d.ts.map +1 -0
  63. package/dist/hooks/useWebSocketConnection.d.ts +25 -0
  64. package/dist/hooks/useWebSocketConnection.d.ts.map +1 -0
  65. package/dist/index.d.ts +5 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +6 -0
  68. package/dist/index.mjs +1689 -0
  69. package/dist/style.css +1 -0
  70. package/dist/types.d.ts +28 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/utils/componentRegistry.d.ts +26 -0
  73. package/dist/utils/componentRegistry.d.ts.map +1 -0
  74. package/dist/utils/tooltipManager.d.ts +4 -0
  75. package/dist/utils/tooltipManager.d.ts.map +1 -0
  76. package/lib/cli-helpers.js +77 -0
  77. package/lib/init.js +132 -0
  78. package/lib/promote.js +285 -0
  79. package/lib/watch.js +1313 -0
  80. package/package.json +73 -4
package/README.md CHANGED
@@ -1,5 +1,131 @@
1
1
  # uifork
2
2
 
3
- ⚠️ Placeholder package.
3
+ A CLI tool for managing UI component versions and switching between them.
4
4
 
5
- This name is reserved. Initial release coming soon.
5
+ ## Installation
6
+
7
+ ### Global Installation
8
+
9
+ ```bash
10
+ npm install -g uifork
11
+ ```
12
+
13
+ ### Local Development Setup
14
+
15
+ 1. Clone this repository
16
+ 2. Install dependencies:
17
+ ```bash
18
+ npm install
19
+ ```
20
+ 3. Make the CLI script executable:
21
+ ```bash
22
+ chmod +x cli.js
23
+ ```
24
+ 4. Link the package globally for development:
25
+ ```bash
26
+ npm link
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Initialize a UI Switcher
32
+
33
+ Convert a single component file into a versioned UI switcher:
34
+
35
+ ```bash
36
+ uifork init frontend/src/SomeDropdownComponent.tsx
37
+ ```
38
+
39
+ This will:
40
+
41
+ - Rename the original file to `ComponentName.v1.tsx` (or `.ts`)
42
+ - Generate a `ComponentName.versions.ts` file
43
+ - Create a `ComponentName.UISwitcher.tsx` component
44
+ - Create a `ComponentName.tsx` wrapper file that exports the component
45
+
46
+ ### Watch for Changes
47
+
48
+ Watch a component directory for version file changes:
49
+
50
+ ```bash
51
+ uifork watch SomeDropdownComponent
52
+ ```
53
+
54
+ The watch command will:
55
+
56
+ - Automatically find files matching `*.versions.ts` pattern
57
+ - Watch for new version files (ComponentName.v*.tsx or ComponentName.v*.ts)
58
+ - Auto-update imports in `versions.ts`
59
+ - Handle file renames bidirectionally
60
+
61
+ ### Promote a Version
62
+
63
+ Promote a specific version to be the main component and remove all versioning scaffolding:
64
+
65
+ ```bash
66
+ uifork promote SomeDropdownComponent v2
67
+ ```
68
+
69
+ This will:
70
+
71
+ - Replace `ComponentName.tsx` with the content from `ComponentName.v2.tsx`
72
+ - Delete all version files (`ComponentName.v*.tsx`)
73
+ - Delete `ComponentName.versions.ts`
74
+ - Delete `ComponentName.UISwitcher.tsx`
75
+ - Effectively "undo" the versioning system, leaving just the promoted version as the main component
76
+
77
+ ## Component Structure
78
+
79
+ After running `uifork init`, your component files will be created in the same directory:
80
+
81
+ ```
82
+ src/components/
83
+ ├── ComponentName.tsx # Main wrapper export (use this for imports)
84
+ ├── ComponentName.UISwitcher.tsx # Version switcher component
85
+ ├── ComponentName.versions.ts # Version configuration
86
+ ├── ComponentName.v1.tsx # Your original component (version 1)
87
+ ├── ComponentName.v2.tsx # Additional versions as needed
88
+ └── ComponentName.v1_1.tsx # Sub-versions (e.g., v1_1, v2_1)
89
+ ```
90
+
91
+ All files are created in the same directory as your original component, using a flat naming convention.
92
+
93
+ ## Adding New Versions
94
+
95
+ 1. Create new version files: `ComponentName.v2.tsx`, `ComponentName.v1_1.tsx`, etc.
96
+ 2. Run `uifork watch ComponentName.versions.ts` (or just `uifork watch ComponentName`) to automatically update `versions.ts`
97
+ 3. Or manually add imports and version entries to `ComponentName.versions.ts`
98
+
99
+ ## UI Switcher Controls
100
+
101
+ - **Cmd + Arrow Up/Down**: Cycle through versions
102
+ - **Bottom-right selector**: Click to choose specific version
103
+ - Selections are saved to localStorage per component ID
104
+
105
+ ## Commands
106
+
107
+ - `uifork init <component-path>` - Initialize a UI switcher from a component file
108
+ - `uifork watch <component-name>` - Watch for version changes in a component
109
+ - `uifork promote <component-path> <version-id>` - Promote a version to be the main component and remove versioning scaffolding
110
+ - `uifork --help` - Show help information
111
+ - `uifork --version` - Show version number
112
+
113
+ ## Examples
114
+
115
+ ```bash
116
+ # Initialize a new UI switcher
117
+ uifork init src/components/Button.tsx
118
+ # Creates: Button.tsx, Button.v1.tsx, Button.versions.ts, Button.UISwitcher.tsx
119
+
120
+ # Watch for changes (by component name or path)
121
+ uifork watch Button
122
+ # or
123
+ uifork watch src/components/Button.versions.ts
124
+
125
+ # Promote a version to be the main component (removes all versioning)
126
+ uifork promote Button v2
127
+ # This replaces Button.tsx with Button.v2.tsx and deletes all version files
128
+
129
+ # Import and use the component
130
+ import Button from './Button' # Uses Button.tsx wrapper
131
+ ```
package/cli.js ADDED
@@ -0,0 +1,450 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { UISwitcherScaffold } = require("./lib/init");
6
+ const { VersionSync } = require("./lib/watch");
7
+ const { VersionPromoter } = require("./lib/promote");
8
+ const { findComponentManager } = require("./lib/cli-helpers");
9
+
10
+ function showHelp() {
11
+ console.log(`
12
+ uifork - A CLI tool for managing UI component versions
13
+
14
+ Usage:
15
+ uifork init <component-path> Initialize a new branched component
16
+ uifork watch [directory] Watch for version changes (defaults to current directory)
17
+ uifork new <component-path> [version-id] Create a new version
18
+ uifork duplicate <component-path> <version-id> [target-version] Duplicate/fork a version
19
+ uifork rename <component-path> <version-id> <new-version-id> Rename a version
20
+ uifork delete <component-path> <version-id> Delete a version
21
+ uifork promote <component-path> <version-id> Promote a version to be the main component
22
+
23
+ Examples:
24
+ uifork init frontend/src/SomeDropdownComponent.tsx
25
+ uifork watch
26
+ uifork watch ./src
27
+ uifork new SomeDropdownComponent
28
+ uifork new SomeDropdownComponent v3
29
+ uifork duplicate SomeDropdownComponent v1 v2
30
+ uifork rename SomeDropdownComponent v1 v2
31
+ uifork delete SomeDropdownComponent v2
32
+ uifork promote SomeDropdownComponent v2
33
+
34
+ Commands:
35
+ init Convert a single component file into a versioned branched component
36
+ watch Start the watch server and discover all versioned components
37
+ new Create a new version (auto-increments if version-id not provided)
38
+ duplicate Duplicate/fork a version (auto-increments target if not provided)
39
+ rename Rename a version
40
+ delete Delete a version
41
+ promote Promote a version to be the main component and remove versioning scaffolding
42
+
43
+ Options:
44
+ -h, --help Show this help message
45
+ -v, --version Show version number
46
+ --W Don't start watching after init (init command only)
47
+ `);
48
+ }
49
+
50
+ function showVersion() {
51
+ const packageJson = require("./package.json");
52
+ console.log(`uifork v${packageJson.version}`);
53
+ }
54
+
55
+ // Parse command line arguments
56
+ const args = process.argv.slice(2);
57
+ const command = args[0];
58
+ const argument = args[1];
59
+
60
+ // Handle help and version flags
61
+ if (args.includes("-h") || args.includes("--help")) {
62
+ showHelp();
63
+ process.exit(0);
64
+ }
65
+
66
+ if (args.includes("-v") || args.includes("--version")) {
67
+ showVersion();
68
+ process.exit(0);
69
+ }
70
+
71
+ // Handle commands
72
+ switch (command) {
73
+ case "init":
74
+ if (!argument) {
75
+ console.error("Error: Component path is required for init command");
76
+ console.error("Usage: uifork init <component-path>");
77
+ console.error(
78
+ "Example: uifork init frontend/src/modules/chart-builder/ZoomDatePickerDropdown.tsx",
79
+ );
80
+ process.exit(1);
81
+ }
82
+
83
+ try {
84
+ const shouldWatch = !args.includes("--W");
85
+ const scaffolder = new UISwitcherScaffold(argument, shouldWatch);
86
+ scaffolder.scaffold();
87
+ } catch (error) {
88
+ console.error(`Error during scaffolding: ${error.message}`);
89
+ process.exit(1);
90
+ }
91
+ break;
92
+
93
+ case "watch":
94
+ // Watch can be called without arguments - defaults to current directory
95
+ try {
96
+ new VersionSync(argument || process.cwd());
97
+ } catch (error) {
98
+ console.error(`Error during watching: ${error.message}`);
99
+ process.exit(1);
100
+ }
101
+ break;
102
+
103
+ case "promote":
104
+ if (!argument) {
105
+ console.error(
106
+ "Error: Component path and version ID are required for promote command",
107
+ );
108
+ console.error("Usage: uifork promote <component-path> <version-id>");
109
+ console.error("Example: uifork promote SomeDropdownComponent v2");
110
+ console.error(
111
+ "Example: uifork promote frontend/src/SomeDropdownComponent.tsx v1_2",
112
+ );
113
+ process.exit(1);
114
+ }
115
+
116
+ const versionId = args[2];
117
+ if (!versionId) {
118
+ console.error("Error: Version ID is required for promote command");
119
+ console.error("Usage: uifork promote <component-path> <version-id>");
120
+ console.error("Example: uifork promote SomeDropdownComponent v2");
121
+ console.error(
122
+ "Example: uifork promote frontend/src/SomeDropdownComponent.tsx v1_2",
123
+ );
124
+ process.exit(1);
125
+ }
126
+
127
+ try {
128
+ const promoter = new VersionPromoter(argument, versionId);
129
+ promoter.promote();
130
+ } catch (error) {
131
+ console.error(`Error during promotion: ${error.message}`);
132
+ process.exit(1);
133
+ }
134
+ break;
135
+
136
+ case "new":
137
+ case "create":
138
+ if (!argument) {
139
+ console.error("Error: Component path is required");
140
+ console.error("Usage: uifork new <component-path> [version-id]");
141
+ console.error("Example: uifork new SomeDropdownComponent");
142
+ console.error("Example: uifork new SomeDropdownComponent v3");
143
+ process.exit(1);
144
+ }
145
+
146
+ try {
147
+ const manager = findComponentManager(argument);
148
+ const versionId = args[2]; // Optional version ID
149
+
150
+ let targetVersion;
151
+ if (versionId) {
152
+ if (!manager.validateVersionKey(versionId)) {
153
+ throw new Error(`Invalid version format: ${versionId}`);
154
+ }
155
+ targetVersion = versionId;
156
+ } else {
157
+ const nextVersionNum = manager.getNextVersionNumber();
158
+ targetVersion = manager.versionNumberToKey(nextVersionNum);
159
+ }
160
+
161
+ const targetFilePath = manager.getVersionFilePath(targetVersion);
162
+ if (fs.existsSync(targetFilePath)) {
163
+ throw new Error(`Version already exists: ${targetVersion}`);
164
+ }
165
+
166
+ const extension = manager.getMostCommonExtension();
167
+ const fileVersion = manager.versionKeyToFileVersion(targetVersion);
168
+ const finalFilePath = path.join(
169
+ manager.watchDir,
170
+ `${manager.componentName}.v${fileVersion}${extension}`,
171
+ );
172
+
173
+ const displayVersion = targetVersion
174
+ .replace(/^v/, "")
175
+ .replace(/_/g, ".")
176
+ .toUpperCase();
177
+
178
+ const importSuffix = manager.versionToImportSuffix(fileVersion);
179
+ const componentName = `${manager.componentName}${importSuffix}`;
180
+
181
+ let templateContent;
182
+ if (extension === ".tsx" || extension === ".jsx") {
183
+ templateContent = `import React from 'react';
184
+
185
+ export default function ${componentName}() {
186
+ return (
187
+ <div>
188
+ ${displayVersion}
189
+ </div>
190
+ );
191
+ }
192
+ `;
193
+ } else {
194
+ templateContent = `import React from 'react';
195
+
196
+ export default function ${componentName}() {
197
+ return React.createElement('div', null, '${displayVersion}');
198
+ }
199
+ `;
200
+ }
201
+
202
+ fs.writeFileSync(finalFilePath, templateContent, "utf8");
203
+ manager.generateVersionsFile();
204
+
205
+ console.log(`✅ Created new version: ${targetVersion}`);
206
+ console.log(` Component: ${manager.componentName}`);
207
+ console.log(` File: ${path.basename(finalFilePath)}`);
208
+ } catch (error) {
209
+ console.error(`Error creating version: ${error.message}`);
210
+ process.exit(1);
211
+ }
212
+ break;
213
+
214
+ case "duplicate":
215
+ case "fork":
216
+ if (!argument) {
217
+ console.error("Error: Component path and version ID are required");
218
+ console.error(
219
+ "Usage: uifork duplicate <component-path> <version-id> [target-version]",
220
+ );
221
+ console.error("Example: uifork duplicate SomeDropdownComponent v1");
222
+ console.error("Example: uifork duplicate SomeDropdownComponent v1 v2");
223
+ process.exit(1);
224
+ }
225
+
226
+ const sourceVersion = args[2];
227
+ if (!sourceVersion) {
228
+ console.error("Error: Source version ID is required");
229
+ console.error(
230
+ "Usage: uifork duplicate <component-path> <version-id> [target-version]",
231
+ );
232
+ process.exit(1);
233
+ }
234
+
235
+ try {
236
+ const manager = findComponentManager(argument);
237
+
238
+ if (!manager.validateVersionKey(sourceVersion)) {
239
+ throw new Error(`Invalid source version format: ${sourceVersion}`);
240
+ }
241
+
242
+ const sourceFilePath = manager.getVersionFilePath(sourceVersion);
243
+ if (!fs.existsSync(sourceFilePath)) {
244
+ throw new Error(`Source version file not found: ${sourceVersion}`);
245
+ }
246
+
247
+ let targetVersion;
248
+ const newVersion = args[3]; // Optional target version
249
+ if (newVersion) {
250
+ if (!manager.validateVersionKey(newVersion)) {
251
+ throw new Error(`Invalid target version format: ${newVersion}`);
252
+ }
253
+ targetVersion = newVersion;
254
+ } else {
255
+ const nextVersionNum = manager.getNextVersionNumber();
256
+ targetVersion = manager.versionNumberToKey(nextVersionNum);
257
+ }
258
+
259
+ const targetFilePath = manager.getVersionFilePath(targetVersion);
260
+ if (fs.existsSync(targetFilePath)) {
261
+ throw new Error(`Target version already exists: ${targetVersion}`);
262
+ }
263
+
264
+ const sourceContent = fs.readFileSync(sourceFilePath, "utf8");
265
+ const extension = path.extname(sourceFilePath);
266
+ const fileVersion = manager.versionKeyToFileVersion(targetVersion);
267
+ const finalTargetPath = path.join(
268
+ manager.watchDir,
269
+ `${manager.componentName}.v${fileVersion}${extension}`,
270
+ );
271
+
272
+ fs.writeFileSync(finalTargetPath, sourceContent, "utf8");
273
+ manager.generateVersionsFile();
274
+
275
+ console.log(`✅ Duplicated version: ${sourceVersion} → ${targetVersion}`);
276
+ console.log(` Component: ${manager.componentName}`);
277
+ console.log(` Source: ${path.basename(sourceFilePath)}`);
278
+ console.log(` Target: ${path.basename(finalTargetPath)}`);
279
+ } catch (error) {
280
+ console.error(`Error duplicating version: ${error.message}`);
281
+ process.exit(1);
282
+ }
283
+ break;
284
+
285
+ case "rename":
286
+ if (!argument) {
287
+ console.error(
288
+ "Error: Component path, version ID, and new version ID are required",
289
+ );
290
+ console.error(
291
+ "Usage: uifork rename <component-path> <version-id> <new-version-id>",
292
+ );
293
+ console.error("Example: uifork rename SomeDropdownComponent v1 v2");
294
+ process.exit(1);
295
+ }
296
+
297
+ const oldVersion = args[2];
298
+ const newVersionId = args[3];
299
+
300
+ if (!oldVersion || !newVersionId) {
301
+ console.error("Error: Both version ID and new version ID are required");
302
+ console.error(
303
+ "Usage: uifork rename <component-path> <version-id> <new-version-id>",
304
+ );
305
+ process.exit(1);
306
+ }
307
+
308
+ try {
309
+ const manager = findComponentManager(argument);
310
+
311
+ if (!manager.validateVersionKey(oldVersion)) {
312
+ throw new Error(`Invalid source version format: ${oldVersion}`);
313
+ }
314
+
315
+ if (!manager.validateVersionKey(newVersionId)) {
316
+ throw new Error(`Invalid target version format: ${newVersionId}`);
317
+ }
318
+
319
+ if (oldVersion === newVersionId) {
320
+ throw new Error("Source and target versions are the same");
321
+ }
322
+
323
+ const fileVersion = manager.versionKeyToFileVersion(oldVersion);
324
+ const extensions = [".tsx", ".ts", ".jsx", ".js"];
325
+ let sourceFilePath = null;
326
+
327
+ for (const ext of extensions) {
328
+ const candidatePath = path.join(
329
+ manager.watchDir,
330
+ `${manager.componentName}.v${fileVersion}${ext}`,
331
+ );
332
+ if (fs.existsSync(candidatePath)) {
333
+ sourceFilePath = candidatePath;
334
+ break;
335
+ }
336
+ }
337
+
338
+ if (!sourceFilePath) {
339
+ throw new Error(
340
+ `Source version file not found: ${oldVersion}. Checked: ${extensions
341
+ .map((ext) => `${manager.componentName}.v${fileVersion}${ext}`)
342
+ .join(", ")}`,
343
+ );
344
+ }
345
+
346
+ const targetFileVersion = manager.versionKeyToFileVersion(newVersionId);
347
+ let targetFilePath = null;
348
+ for (const ext of extensions) {
349
+ const candidatePath = path.join(
350
+ manager.watchDir,
351
+ `${manager.componentName}.v${targetFileVersion}${ext}`,
352
+ );
353
+ if (fs.existsSync(candidatePath)) {
354
+ targetFilePath = candidatePath;
355
+ break;
356
+ }
357
+ }
358
+
359
+ if (targetFilePath) {
360
+ throw new Error(`Target version already exists: ${newVersionId}`);
361
+ }
362
+
363
+ const extension = path.extname(sourceFilePath);
364
+ const finalTargetPath = path.join(
365
+ manager.watchDir,
366
+ `${manager.componentName}.v${targetFileVersion}${extension}`,
367
+ );
368
+
369
+ const sourceContent = fs.readFileSync(sourceFilePath, "utf8");
370
+
371
+ const importSuffix = manager.versionToImportSuffix(fileVersion);
372
+ const newImportSuffix = manager.versionToImportSuffix(targetFileVersion);
373
+ const oldComponentName = `${manager.componentName}${importSuffix}`;
374
+ const newComponentName = `${manager.componentName}${newImportSuffix}`;
375
+
376
+ const updatedContent = sourceContent.replace(
377
+ new RegExp(oldComponentName, "g"),
378
+ newComponentName,
379
+ );
380
+
381
+ fs.writeFileSync(finalTargetPath, updatedContent, "utf8");
382
+ fs.unlinkSync(sourceFilePath);
383
+ manager.generateVersionsFile();
384
+
385
+ console.log(`✅ Renamed version: ${oldVersion} → ${newVersionId}`);
386
+ console.log(` Component: ${manager.componentName}`);
387
+ console.log(` Source: ${path.basename(sourceFilePath)}`);
388
+ console.log(` Target: ${path.basename(finalTargetPath)}`);
389
+ } catch (error) {
390
+ console.error(`Error renaming version: ${error.message}`);
391
+ process.exit(1);
392
+ }
393
+ break;
394
+
395
+ case "delete":
396
+ if (!argument) {
397
+ console.error("Error: Component path and version ID are required");
398
+ console.error("Usage: uifork delete <component-path> <version-id>");
399
+ console.error("Example: uifork delete SomeDropdownComponent v2");
400
+ process.exit(1);
401
+ }
402
+
403
+ const deleteVersionId = args[2];
404
+ if (!deleteVersionId) {
405
+ console.error("Error: Version ID is required");
406
+ console.error("Usage: uifork delete <component-path> <version-id>");
407
+ process.exit(1);
408
+ }
409
+
410
+ try {
411
+ const manager = findComponentManager(argument);
412
+
413
+ if (!manager.validateVersionKey(deleteVersionId)) {
414
+ throw new Error(`Invalid version format: ${deleteVersionId}`);
415
+ }
416
+
417
+ const filePath = manager.getVersionFilePath(deleteVersionId);
418
+ if (!fs.existsSync(filePath)) {
419
+ throw new Error(`Version file not found: ${deleteVersionId}`);
420
+ }
421
+
422
+ const versionFiles = manager.getVersionFiles();
423
+ if (versionFiles.length === 1) {
424
+ throw new Error(
425
+ "Cannot delete the last remaining version. At least one version must exist.",
426
+ );
427
+ }
428
+
429
+ fs.unlinkSync(filePath);
430
+ manager.generateVersionsFile();
431
+
432
+ console.log(`✅ Deleted version: ${deleteVersionId}`);
433
+ console.log(` Component: ${manager.componentName}`);
434
+ console.log(` File: ${path.basename(filePath)}`);
435
+ } catch (error) {
436
+ console.error(`Error deleting version: ${error.message}`);
437
+ process.exit(1);
438
+ }
439
+ break;
440
+
441
+ default:
442
+ if (!command) {
443
+ showHelp();
444
+ process.exit(0);
445
+ } else {
446
+ console.error(`Unknown command: ${command}`);
447
+ console.error('Run "uifork --help" for available commands');
448
+ process.exit(1);
449
+ }
450
+ }
@@ -0,0 +1,10 @@
1
+ import { BranchedComponentProps } from '../types';
2
+ /**
3
+ * A component that renders a specific version based on localStorage state.
4
+ * Used to wrap components that have multiple versions managed by uifork.
5
+ *
6
+ * The UIFork component controls which version is active by writing to localStorage.
7
+ * BranchedComponent reads from localStorage and renders the appropriate version.
8
+ */
9
+ export declare function BranchedComponent<T extends Record<string, unknown>>({ id, versions, props, defaultVersion, }: BranchedComponentProps<T>): import("react/jsx-runtime").JSX.Element | null;
10
+ //# sourceMappingURL=BranchedComponent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BranchedComponent.d.ts","sourceRoot":"","sources":["../../src/components/BranchedComponent.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACnE,EAAE,EACF,QAAQ,EACR,KAAK,EACL,cAAc,GACf,EAAE,sBAAsB,CAAC,CAAC,CAAC,kDAoF3B"}
@@ -0,0 +1,21 @@
1
+ import { default as React } from 'react';
2
+ import { ComponentInfo } from '../types';
3
+ interface ComponentSelectorProps {
4
+ selectedComponent: string;
5
+ onToggle: () => void;
6
+ onSettingsClick: (e: React.MouseEvent) => void;
7
+ }
8
+ export declare function ComponentSelector({ selectedComponent, onToggle, onSettingsClick, }: ComponentSelectorProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function ComponentSelectorDropdown({ mountedComponents, selectedComponent, isOpen, position, onSelect, componentSelectorRef, }: {
10
+ mountedComponents: ComponentInfo[];
11
+ selectedComponent: string;
12
+ isOpen: boolean;
13
+ position: {
14
+ x: number;
15
+ y: number;
16
+ };
17
+ onSelect: (componentName: string) => void;
18
+ componentSelectorRef: React.RefObject<HTMLDivElement>;
19
+ }): import("react/jsx-runtime").JSX.Element | null;
20
+ export {};
21
+ //# sourceMappingURL=ComponentSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentSelector.d.ts","sourceRoot":"","sources":["../../src/components/ComponentSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,UAAU,sBAAsB;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CAChD;AAKD,wBAAgB,iBAAiB,CAAC,EAChC,iBAAiB,EACjB,QAAQ,EACR,eAAe,GAChB,EAAE,sBAAsB,2CAiCxB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,iBAAiB,EACjB,iBAAiB,EACjB,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,oBAAoB,GACrB,EAAE;IACD,iBAAiB,EAAE,aAAa,EAAE,CAAC;IACnC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,oBAAoB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;CACvD,kDA6CA"}
@@ -0,0 +1,13 @@
1
+ import { default as React } from 'react';
2
+ interface MenuItemProps {
3
+ icon: React.ComponentType<{
4
+ className?: string;
5
+ }>;
6
+ label: string;
7
+ onClick: (e: React.MouseEvent) => void;
8
+ variant?: "default" | "delete";
9
+ stopPropagation?: boolean;
10
+ }
11
+ export declare function MenuItem({ icon: Icon, label, onClick, variant, stopPropagation, }: MenuItemProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=MenuItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MenuItem.d.ts","sourceRoot":"","sources":["../../src/components/MenuItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,aAAa;IACrB,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EAAE,IAAI,EACV,KAAK,EACL,OAAO,EACP,OAAmB,EACnB,eAAuB,GACxB,EAAE,aAAa,2CAmBf"}
@@ -0,0 +1,12 @@
1
+ interface SettingsViewProps {
2
+ onBack: () => void;
3
+ theme: "light" | "dark" | "system";
4
+ setTheme: (theme: "light" | "dark" | "system") => void;
5
+ position: "top-left" | "top-right" | "bottom-left" | "bottom-right";
6
+ setPosition: (position: "top-left" | "top-right" | "bottom-left" | "bottom-right") => void;
7
+ codeEditor: "vscode" | "cursor";
8
+ setCodeEditor: (editor: "vscode" | "cursor") => void;
9
+ }
10
+ export declare function SettingsView({ onBack, theme, setTheme, position, setPosition, codeEditor, setCodeEditor, }: SettingsViewProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=SettingsView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsView.d.ts","sourceRoot":"","sources":["../../src/components/SettingsView.tsx"],"names":[],"mappings":"AAIA,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,KAAK,IAAI,CAAC;IACvD,QAAQ,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;IACpE,WAAW,EAAE,CACX,QAAQ,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,KAChE,IAAI,CAAC;IACV,UAAU,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,aAAa,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC;CACtD;AAED,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,UAAU,EACV,aAAa,GACd,EAAE,iBAAiB,2CA+DnB"}
@@ -0,0 +1,9 @@
1
+ import { default as React } from 'react';
2
+ interface TooltipProps {
3
+ label: string;
4
+ children: React.ReactElement;
5
+ placement?: "top" | "bottom" | "left" | "right";
6
+ }
7
+ export declare function Tooltip({ label, children, placement }: TooltipProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=Tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../src/components/Tooltip.tsx"],"names":[],"mappings":"AACA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAS3D,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC;IAC7B,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;CACjD;AAED,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAiB,EAAE,EAAE,YAAY,2CAuL3E"}