@aprovan/patchwork 0.1.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.
- package/.eslintrc.json +22 -0
- package/.github/workflows/publish.yml +41 -0
- package/.prettierignore +17 -0
- package/LICENSE +373 -0
- package/README.md +15 -0
- package/apps/chat/.utcp_config.json +14 -0
- package/apps/chat/.working/widgets/27060b91-a2a5-4272-b243-6eb904bd4070/main.tsx +107 -0
- package/apps/chat/index.html +17 -0
- package/apps/chat/node_modules/.bin/autoprefixer +17 -0
- package/apps/chat/node_modules/.bin/browserslist +17 -0
- package/apps/chat/node_modules/.bin/conc +17 -0
- package/apps/chat/node_modules/.bin/concurrently +17 -0
- package/apps/chat/node_modules/.bin/copilot-proxy +17 -0
- package/apps/chat/node_modules/.bin/jiti +17 -0
- package/apps/chat/node_modules/.bin/tailwind +17 -0
- package/apps/chat/node_modules/.bin/tailwindcss +17 -0
- package/apps/chat/node_modules/.bin/tsc +17 -0
- package/apps/chat/node_modules/.bin/tsserver +17 -0
- package/apps/chat/node_modules/.bin/tsx +17 -0
- package/apps/chat/node_modules/.bin/vite +17 -0
- package/apps/chat/package.json +55 -0
- package/apps/chat/postcss.config.js +6 -0
- package/apps/chat/src/App.tsx +7 -0
- package/apps/chat/src/components/ui/avatar.tsx +48 -0
- package/apps/chat/src/components/ui/badge.tsx +36 -0
- package/apps/chat/src/components/ui/button.tsx +56 -0
- package/apps/chat/src/components/ui/card.tsx +86 -0
- package/apps/chat/src/components/ui/collapsible.tsx +9 -0
- package/apps/chat/src/components/ui/dialog.tsx +60 -0
- package/apps/chat/src/components/ui/input.tsx +25 -0
- package/apps/chat/src/components/ui/scroll-area.tsx +46 -0
- package/apps/chat/src/index.css +190 -0
- package/apps/chat/src/lib/utils.ts +6 -0
- package/apps/chat/src/main.tsx +10 -0
- package/apps/chat/src/pages/ChatPage.tsx +460 -0
- package/apps/chat/tailwind.config.js +71 -0
- package/apps/chat/tsconfig.json +25 -0
- package/apps/chat/vite.config.ts +26 -0
- package/package.json +35 -0
- package/packages/bobbin/node_modules/.bin/esbuild +14 -0
- package/packages/bobbin/node_modules/.bin/jiti +17 -0
- package/packages/bobbin/node_modules/.bin/tsc +17 -0
- package/packages/bobbin/node_modules/.bin/tsserver +17 -0
- package/packages/bobbin/node_modules/.bin/tsup +17 -0
- package/packages/bobbin/node_modules/.bin/tsup-node +17 -0
- package/packages/bobbin/node_modules/.bin/tsx +17 -0
- package/packages/bobbin/package.json +30 -0
- package/packages/bobbin/src/Bobbin.tsx +89 -0
- package/packages/bobbin/src/components/EditPanel/EditPanel.tsx +376 -0
- package/packages/bobbin/src/components/EditPanel/controls/ColorPicker.tsx +138 -0
- package/packages/bobbin/src/components/EditPanel/controls/QuickSelectDropdown.tsx +142 -0
- package/packages/bobbin/src/components/EditPanel/controls/SliderInput.tsx +94 -0
- package/packages/bobbin/src/components/EditPanel/controls/SpacingControl.tsx +285 -0
- package/packages/bobbin/src/components/EditPanel/controls/ToggleGroup.tsx +37 -0
- package/packages/bobbin/src/components/EditPanel/controls/TokenDropdown.tsx +33 -0
- package/packages/bobbin/src/components/EditPanel/sections/AnnotationSection.tsx +136 -0
- package/packages/bobbin/src/components/EditPanel/sections/BackgroundSection.tsx +79 -0
- package/packages/bobbin/src/components/EditPanel/sections/EffectsSection.tsx +85 -0
- package/packages/bobbin/src/components/EditPanel/sections/LayoutSection.tsx +224 -0
- package/packages/bobbin/src/components/EditPanel/sections/SectionWrapper.tsx +57 -0
- package/packages/bobbin/src/components/EditPanel/sections/SizeSection.tsx +166 -0
- package/packages/bobbin/src/components/EditPanel/sections/SpacingSection.tsx +69 -0
- package/packages/bobbin/src/components/EditPanel/sections/TypographySection.tsx +148 -0
- package/packages/bobbin/src/components/Inspector/Inspector.tsx +221 -0
- package/packages/bobbin/src/components/Overlay/ControlHandles.tsx +572 -0
- package/packages/bobbin/src/components/Overlay/MarginPaddingOverlay.tsx +229 -0
- package/packages/bobbin/src/components/Overlay/SelectionOverlay.tsx +73 -0
- package/packages/bobbin/src/components/Pill/Pill.tsx +155 -0
- package/packages/bobbin/src/components/ThemeToggle/ThemeToggle.tsx +72 -0
- package/packages/bobbin/src/core/changeSerializer.ts +139 -0
- package/packages/bobbin/src/core/useBobbin.ts +399 -0
- package/packages/bobbin/src/core/useChangeTracker.ts +186 -0
- package/packages/bobbin/src/core/useClipboard.ts +21 -0
- package/packages/bobbin/src/core/useElementSelection.ts +146 -0
- package/packages/bobbin/src/index.ts +46 -0
- package/packages/bobbin/src/tokens/borders.ts +19 -0
- package/packages/bobbin/src/tokens/colors.ts +150 -0
- package/packages/bobbin/src/tokens/index.ts +37 -0
- package/packages/bobbin/src/tokens/shadows.ts +10 -0
- package/packages/bobbin/src/tokens/spacing.ts +37 -0
- package/packages/bobbin/src/tokens/typography.ts +51 -0
- package/packages/bobbin/src/types.ts +157 -0
- package/packages/bobbin/src/utils/animation.ts +40 -0
- package/packages/bobbin/src/utils/dom.ts +36 -0
- package/packages/bobbin/src/utils/selectors.ts +76 -0
- package/packages/bobbin/tsconfig.json +10 -0
- package/packages/bobbin/tsup.config.ts +10 -0
- package/packages/compiler/node_modules/.bin/esbuild +17 -0
- package/packages/compiler/node_modules/.bin/jiti +17 -0
- package/packages/compiler/node_modules/.bin/tsc +17 -0
- package/packages/compiler/node_modules/.bin/tsserver +17 -0
- package/packages/compiler/node_modules/.bin/tsup +17 -0
- package/packages/compiler/node_modules/.bin/tsup-node +17 -0
- package/packages/compiler/node_modules/.bin/tsx +17 -0
- package/packages/compiler/package.json +38 -0
- package/packages/compiler/src/compiler.ts +258 -0
- package/packages/compiler/src/images/index.ts +13 -0
- package/packages/compiler/src/images/loader.ts +234 -0
- package/packages/compiler/src/images/registry.ts +112 -0
- package/packages/compiler/src/index.ts +141 -0
- package/packages/compiler/src/mount/bridge.ts +399 -0
- package/packages/compiler/src/mount/embedded.ts +306 -0
- package/packages/compiler/src/mount/iframe.ts +433 -0
- package/packages/compiler/src/mount/index.ts +18 -0
- package/packages/compiler/src/schemas.ts +169 -0
- package/packages/compiler/src/transforms/cdn.ts +411 -0
- package/packages/compiler/src/transforms/index.ts +4 -0
- package/packages/compiler/src/transforms/vfs.ts +138 -0
- package/packages/compiler/src/types.ts +233 -0
- package/packages/compiler/src/vfs/backends/indexeddb.ts +66 -0
- package/packages/compiler/src/vfs/backends/local-fs.ts +41 -0
- package/packages/compiler/src/vfs/backends/s3.ts +60 -0
- package/packages/compiler/src/vfs/index.ts +11 -0
- package/packages/compiler/src/vfs/project.ts +56 -0
- package/packages/compiler/src/vfs/store.ts +53 -0
- package/packages/compiler/src/vfs/types.ts +20 -0
- package/packages/compiler/tsconfig.json +8 -0
- package/packages/compiler/tsup.config.ts +14 -0
- package/packages/editor/node_modules/.bin/jiti +17 -0
- package/packages/editor/node_modules/.bin/tsc +17 -0
- package/packages/editor/node_modules/.bin/tsserver +17 -0
- package/packages/editor/node_modules/.bin/tsup +17 -0
- package/packages/editor/node_modules/.bin/tsup-node +17 -0
- package/packages/editor/node_modules/.bin/tsx +17 -0
- package/packages/editor/package.json +45 -0
- package/packages/editor/src/components/CodeBlockExtension.tsx +190 -0
- package/packages/editor/src/components/CodePreview.tsx +344 -0
- package/packages/editor/src/components/MarkdownEditor.tsx +270 -0
- package/packages/editor/src/components/ServicesInspector.tsx +118 -0
- package/packages/editor/src/components/edit/EditHistory.tsx +89 -0
- package/packages/editor/src/components/edit/EditModal.tsx +236 -0
- package/packages/editor/src/components/edit/FileTree.tsx +144 -0
- package/packages/editor/src/components/edit/api.ts +100 -0
- package/packages/editor/src/components/edit/index.ts +6 -0
- package/packages/editor/src/components/edit/types.ts +53 -0
- package/packages/editor/src/components/edit/useEditSession.ts +164 -0
- package/packages/editor/src/components/index.ts +5 -0
- package/packages/editor/src/index.ts +72 -0
- package/packages/editor/src/lib/code-extractor.ts +210 -0
- package/packages/editor/src/lib/diff.ts +308 -0
- package/packages/editor/src/lib/index.ts +4 -0
- package/packages/editor/src/lib/utils.ts +6 -0
- package/packages/editor/src/lib/vfs.ts +106 -0
- package/packages/editor/tsconfig.json +10 -0
- package/packages/editor/tsup.config.ts +10 -0
- package/packages/images/ink/node_modules/.bin/jiti +17 -0
- package/packages/images/ink/node_modules/.bin/tsc +17 -0
- package/packages/images/ink/node_modules/.bin/tsserver +17 -0
- package/packages/images/ink/node_modules/.bin/tsup +17 -0
- package/packages/images/ink/node_modules/.bin/tsup-node +17 -0
- package/packages/images/ink/node_modules/.bin/tsx +17 -0
- package/packages/images/ink/package.json +53 -0
- package/packages/images/ink/src/index.ts +48 -0
- package/packages/images/ink/src/runner.ts +331 -0
- package/packages/images/ink/src/setup.ts +123 -0
- package/packages/images/ink/tsconfig.json +10 -0
- package/packages/images/ink/tsup.config.ts +11 -0
- package/packages/images/shadcn/node_modules/.bin/jiti +17 -0
- package/packages/images/shadcn/node_modules/.bin/tsc +17 -0
- package/packages/images/shadcn/node_modules/.bin/tsserver +17 -0
- package/packages/images/shadcn/node_modules/.bin/tsup +17 -0
- package/packages/images/shadcn/node_modules/.bin/tsup-node +17 -0
- package/packages/images/shadcn/node_modules/.bin/tsx +17 -0
- package/packages/images/shadcn/package.json +82 -0
- package/packages/images/shadcn/src/html.ts +341 -0
- package/packages/images/shadcn/src/index.ts +37 -0
- package/packages/images/shadcn/src/setup.ts +287 -0
- package/packages/images/shadcn/tsconfig.json +9 -0
- package/packages/images/shadcn/tsup.config.ts +13 -0
- package/packages/images/vanilla/node_modules/.bin/jiti +17 -0
- package/packages/images/vanilla/node_modules/.bin/tsc +17 -0
- package/packages/images/vanilla/node_modules/.bin/tsserver +17 -0
- package/packages/images/vanilla/node_modules/.bin/tsup +17 -0
- package/packages/images/vanilla/node_modules/.bin/tsup-node +17 -0
- package/packages/images/vanilla/node_modules/.bin/tsx +17 -0
- package/packages/images/vanilla/package.json +35 -0
- package/packages/images/vanilla/src/index.ts +7 -0
- package/packages/images/vanilla/src/setup.ts +6 -0
- package/packages/images/vanilla/tsconfig.json +9 -0
- package/packages/images/vanilla/tsup.config.ts +10 -0
- package/packages/patchwork/node_modules/.bin/jiti +17 -0
- package/packages/patchwork/node_modules/.bin/tsc +17 -0
- package/packages/patchwork/node_modules/.bin/tsserver +17 -0
- package/packages/patchwork/node_modules/.bin/tsup +17 -0
- package/packages/patchwork/node_modules/.bin/tsup-node +17 -0
- package/packages/patchwork/node_modules/.bin/tsx +17 -0
- package/packages/patchwork/package.json +27 -0
- package/packages/patchwork/src/index.ts +15 -0
- package/packages/patchwork/src/services/index.ts +11 -0
- package/packages/patchwork/src/services/proxy.ts +213 -0
- package/packages/patchwork/src/services/types.ts +28 -0
- package/packages/patchwork/src/types.ts +116 -0
- package/packages/patchwork/tsconfig.json +8 -0
- package/packages/patchwork/tsup.config.ts +14 -0
- package/packages/stitchery/node_modules/.bin/jiti +17 -0
- package/packages/stitchery/node_modules/.bin/tsc +17 -0
- package/packages/stitchery/node_modules/.bin/tsserver +17 -0
- package/packages/stitchery/node_modules/.bin/tsup +17 -0
- package/packages/stitchery/node_modules/.bin/tsup-node +17 -0
- package/packages/stitchery/node_modules/.bin/tsx +17 -0
- package/packages/stitchery/package.json +40 -0
- package/packages/stitchery/src/cli.ts +116 -0
- package/packages/stitchery/src/index.ts +16 -0
- package/packages/stitchery/src/prompts.ts +326 -0
- package/packages/stitchery/src/server/index.ts +365 -0
- package/packages/stitchery/src/server/local-packages.ts +91 -0
- package/packages/stitchery/src/server/routes.ts +122 -0
- package/packages/stitchery/src/server/services.ts +382 -0
- package/packages/stitchery/src/server/vfs-routes.ts +142 -0
- package/packages/stitchery/src/types.ts +59 -0
- package/packages/stitchery/tsconfig.json +13 -0
- package/packages/stitchery/tsup.config.ts +15 -0
- package/packages/utcp/node_modules/.bin/jiti +17 -0
- package/packages/utcp/node_modules/.bin/tsc +17 -0
- package/packages/utcp/node_modules/.bin/tsserver +17 -0
- package/packages/utcp/node_modules/.bin/tsup +17 -0
- package/packages/utcp/node_modules/.bin/tsup-node +17 -0
- package/packages/utcp/node_modules/.bin/tsx +17 -0
- package/packages/utcp/package.json +38 -0
- package/packages/utcp/src/index.ts +153 -0
- package/packages/utcp/tsconfig.json +8 -0
- package/packages/utcp/tsup.config.ts +12 -0
- package/pnpm-workspace.yaml +3 -0
- package/tsconfig.json +18 -0
- package/turbo.json +23 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VFSStore,
|
|
3
|
+
LocalFSBackend,
|
|
4
|
+
type VirtualProject,
|
|
5
|
+
type VirtualFile
|
|
6
|
+
} from '@aprovan/patchwork-compiler';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* VFS client for persisting virtual projects to the stitchery server.
|
|
10
|
+
* Uses LocalFSBackend which makes HTTP requests to /vfs routes.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// VFS base URL - points to stitchery server's VFS routes
|
|
14
|
+
const VFS_BASE_URL = '/vfs';
|
|
15
|
+
|
|
16
|
+
// Cached VFS config
|
|
17
|
+
let vfsConfigCache: { usePaths: boolean } | null = null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get VFS configuration from the server.
|
|
21
|
+
* Caches the result for subsequent calls.
|
|
22
|
+
*/
|
|
23
|
+
export async function getVFSConfig(): Promise<{ usePaths: boolean }> {
|
|
24
|
+
if (vfsConfigCache) return vfsConfigCache;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch(`${VFS_BASE_URL}/config`);
|
|
28
|
+
if (res.ok) {
|
|
29
|
+
vfsConfigCache = await res.json();
|
|
30
|
+
return vfsConfigCache!;
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// Server not available, use default
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { usePaths: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create a singleton store instance for dev mode
|
|
40
|
+
let storeInstance: VFSStore | null = null;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the VFS store instance (creates one if needed).
|
|
44
|
+
* Store uses LocalFSBackend to persist to the stitchery server.
|
|
45
|
+
*/
|
|
46
|
+
export function getVFSStore(): VFSStore {
|
|
47
|
+
if (!storeInstance) {
|
|
48
|
+
const backend = new LocalFSBackend({ baseUrl: VFS_BASE_URL });
|
|
49
|
+
storeInstance = new VFSStore(backend);
|
|
50
|
+
}
|
|
51
|
+
return storeInstance;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Save a virtual project to disk via the stitchery server.
|
|
56
|
+
* Projects are saved under their ID in the VFS directory.
|
|
57
|
+
*/
|
|
58
|
+
export async function saveProject(project: VirtualProject): Promise<void> {
|
|
59
|
+
const store = getVFSStore();
|
|
60
|
+
await store.saveProject(project);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load a project from disk by ID.
|
|
65
|
+
*/
|
|
66
|
+
export async function loadProject(id: string): Promise<VirtualProject | null> {
|
|
67
|
+
const store = getVFSStore();
|
|
68
|
+
return store.loadProject(id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* List all stored project IDs.
|
|
73
|
+
*/
|
|
74
|
+
export async function listProjects(): Promise<string[]> {
|
|
75
|
+
const store = getVFSStore();
|
|
76
|
+
const files = await store.listFiles();
|
|
77
|
+
|
|
78
|
+
// Extract unique project IDs (first path segment)
|
|
79
|
+
const projectIds = new Set<string>();
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
const id = file.split('/')[0];
|
|
82
|
+
if (id) projectIds.add(id);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return Array.from(projectIds);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Save a single file to the VFS.
|
|
90
|
+
*/
|
|
91
|
+
export async function saveFile(file: VirtualFile): Promise<void> {
|
|
92
|
+
const store = getVFSStore();
|
|
93
|
+
await store.putFile(file);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if VFS is available (stitchery server is running with vfs-dir enabled).
|
|
98
|
+
*/
|
|
99
|
+
export async function isVFSAvailable(): Promise<boolean> {
|
|
100
|
+
try {
|
|
101
|
+
const res = await fetch(VFS_BASE_URL, { method: 'HEAD' });
|
|
102
|
+
return res.ok || res.status === 404; // 404 on root is expected if empty
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/jiti@1.21.7/node_modules/jiti/bin/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/jiti@1.21.7/node_modules/jiti/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/jiti@1.21.7/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/jiti@1.21.7/node_modules/jiti/bin/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/jiti@1.21.7/node_modules/jiti/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/jiti@1.21.7/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/jiti@1.21.7/node_modules/jiti/bin/jiti.js" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../../../../../node_modules/.pnpm/jiti@1.21.7/node_modules/jiti/bin/jiti.js" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../typescript/bin/tsc" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../tsup/dist/cli-default.js" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../tsup/dist/cli-default.js" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../tsup/dist/cli-node.js" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../tsup/dist/cli-node.js" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
6
|
+
esac
|
|
7
|
+
|
|
8
|
+
if [ -z "$NODE_PATH" ]; then
|
|
9
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsx@4.21.0/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules"
|
|
10
|
+
else
|
|
11
|
+
export NODE_PATH="/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/tsx@4.21.0/node_modules:/home/runner/work/patchwork/patchwork/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
|
+
fi
|
|
13
|
+
if [ -x "$basedir/node" ]; then
|
|
14
|
+
exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/cli.mjs" "$@"
|
|
15
|
+
else
|
|
16
|
+
exec node "$basedir/../../../../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/cli.mjs" "$@"
|
|
17
|
+
fi
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aprovan/patchwork-ink",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Patchwork image: Ink terminal UI for CLI widgets",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./runner": {
|
|
14
|
+
"types": "./dist/runner.d.ts",
|
|
15
|
+
"import": "./dist/runner.js"
|
|
16
|
+
},
|
|
17
|
+
"./setup": {
|
|
18
|
+
"types": "./dist/setup.d.ts",
|
|
19
|
+
"import": "./dist/setup.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"patchwork": {
|
|
29
|
+
"platform": "cli",
|
|
30
|
+
"esbuild": {
|
|
31
|
+
"target": "node20",
|
|
32
|
+
"format": "esm",
|
|
33
|
+
"jsx": "automatic"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"react": "^18.0.0",
|
|
38
|
+
"ink": "^5.0.0",
|
|
39
|
+
"ink-spinner": "^5.0.0",
|
|
40
|
+
"ink-link": "^4.0.0",
|
|
41
|
+
"ink-box": "^2.0.0",
|
|
42
|
+
"chalk": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.10.5",
|
|
46
|
+
"@types/react": "^18.0.0",
|
|
47
|
+
"tsup": "^8.3.5",
|
|
48
|
+
"typescript": "^5.7.3"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aprovan/patchwork-image-ink
|
|
3
|
+
*
|
|
4
|
+
* Ink terminal UI image for CLI widgets.
|
|
5
|
+
* Provides React components for building terminal interfaces.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { setup, cleanup } from './setup.js';
|
|
9
|
+
export type { SetupOptions, InkEnvironment } from './setup.js';
|
|
10
|
+
|
|
11
|
+
// Runner - mounting/execution for terminal widgets
|
|
12
|
+
export {
|
|
13
|
+
run,
|
|
14
|
+
runOnce,
|
|
15
|
+
evaluateWidget,
|
|
16
|
+
renderComponent,
|
|
17
|
+
getGlobals,
|
|
18
|
+
} from './runner.js';
|
|
19
|
+
export type {
|
|
20
|
+
RunnerOptions,
|
|
21
|
+
RunnerInstance,
|
|
22
|
+
CompiledWidget,
|
|
23
|
+
GlobalInjection,
|
|
24
|
+
} from './runner.js';
|
|
25
|
+
|
|
26
|
+
// Re-export Ink components for convenience
|
|
27
|
+
export {
|
|
28
|
+
render,
|
|
29
|
+
Box,
|
|
30
|
+
Text,
|
|
31
|
+
Static,
|
|
32
|
+
Transform,
|
|
33
|
+
Newline,
|
|
34
|
+
Spacer,
|
|
35
|
+
useInput,
|
|
36
|
+
useApp,
|
|
37
|
+
useFocus,
|
|
38
|
+
useFocusManager,
|
|
39
|
+
useStdin,
|
|
40
|
+
useStdout,
|
|
41
|
+
useStderr,
|
|
42
|
+
} from 'ink';
|
|
43
|
+
|
|
44
|
+
// Re-export React for widget authors
|
|
45
|
+
export { default as React } from 'react';
|
|
46
|
+
|
|
47
|
+
// Re-export chalk for styling
|
|
48
|
+
export { default as chalk } from 'chalk';
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aprovan/patchwork-ink - Runner
|
|
3
|
+
*
|
|
4
|
+
* Provides the runtime mounting logic for Ink terminal widgets.
|
|
5
|
+
* The image owns all ink/react dependencies and mounting code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { WriteStream } from 'node:tty';
|
|
9
|
+
import { render } from 'ink';
|
|
10
|
+
import React from 'react';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Global injections for compiling widgets with this image
|
|
14
|
+
*
|
|
15
|
+
* These tell the compiler which imports to transform into global variable references.
|
|
16
|
+
* The evaluateWidget function will provide these globals at runtime.
|
|
17
|
+
*/
|
|
18
|
+
export interface GlobalInjection {
|
|
19
|
+
module: string;
|
|
20
|
+
globalName: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getGlobals(): GlobalInjection[] {
|
|
24
|
+
return [
|
|
25
|
+
{ module: 'react', globalName: '__REACT__' },
|
|
26
|
+
{ module: 'ink', globalName: '__INK__' },
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RunnerOptions {
|
|
31
|
+
/** Service proxy for UTCP calls */
|
|
32
|
+
proxy?: {
|
|
33
|
+
call(
|
|
34
|
+
namespace: string,
|
|
35
|
+
procedure: string,
|
|
36
|
+
args: unknown[],
|
|
37
|
+
): Promise<unknown>;
|
|
38
|
+
};
|
|
39
|
+
/** Initial props/inputs to pass to widget */
|
|
40
|
+
inputs?: Record<string, unknown>;
|
|
41
|
+
/** Output stream (default: process.stdout) */
|
|
42
|
+
stdout?: WriteStream;
|
|
43
|
+
/** Input stream (default: process.stdin) */
|
|
44
|
+
stdin?: NodeJS.ReadStream;
|
|
45
|
+
/** Exit on Ctrl+C (default: true) */
|
|
46
|
+
exitOnCtrlC?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RunnerInstance {
|
|
50
|
+
/** Unique mount ID */
|
|
51
|
+
id: string;
|
|
52
|
+
/** Unmount the widget */
|
|
53
|
+
unmount: () => void;
|
|
54
|
+
/** Wait until the widget exits */
|
|
55
|
+
waitUntilExit: () => Promise<void>;
|
|
56
|
+
/** Rerender with new props */
|
|
57
|
+
rerender: (props: Record<string, unknown>) => void;
|
|
58
|
+
/** Clear the terminal output */
|
|
59
|
+
clear: () => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let mountCounter = 0;
|
|
63
|
+
|
|
64
|
+
function generateMountId(): string {
|
|
65
|
+
return `patchwork-ink-${Date.now()}-${++mountCounter}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Generate namespace globals that proxy calls to a service proxy
|
|
70
|
+
*
|
|
71
|
+
* Given services like ["git.branch", "git.status", "github.repos.get"],
|
|
72
|
+
* generates globals with appropriate methods.
|
|
73
|
+
*/
|
|
74
|
+
function generateNamespaceGlobals(
|
|
75
|
+
services: string[],
|
|
76
|
+
proxy: RunnerOptions['proxy'],
|
|
77
|
+
): Record<string, unknown> {
|
|
78
|
+
if (!proxy) return {};
|
|
79
|
+
|
|
80
|
+
const namespaces: Record<string, Record<string, unknown>> = {};
|
|
81
|
+
|
|
82
|
+
for (const service of services) {
|
|
83
|
+
const parts = service.split('.');
|
|
84
|
+
if (parts.length < 2) continue;
|
|
85
|
+
|
|
86
|
+
const namespace = parts[0] as string;
|
|
87
|
+
const procedurePath = parts.slice(1);
|
|
88
|
+
|
|
89
|
+
if (!namespaces[namespace]) {
|
|
90
|
+
namespaces[namespace] = {};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let current = namespaces[namespace] as Record<string, unknown>;
|
|
94
|
+
for (let i = 0; i < procedurePath.length - 1; i++) {
|
|
95
|
+
const key = procedurePath[i] as string;
|
|
96
|
+
if (!current[key]) {
|
|
97
|
+
current[key] = {};
|
|
98
|
+
}
|
|
99
|
+
current = current[key] as Record<string, unknown>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const finalKey = procedurePath[procedurePath.length - 1] as string;
|
|
103
|
+
const fullProcedure = procedurePath.join('.');
|
|
104
|
+
current[finalKey] = (...args: unknown[]) =>
|
|
105
|
+
proxy.call(namespace, fullProcedure, args);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return namespaces;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Extract unique namespace names from services array
|
|
113
|
+
*/
|
|
114
|
+
function extractNamespaces(services: string[]): string[] {
|
|
115
|
+
const namespaces = new Set<string>();
|
|
116
|
+
for (const service of services) {
|
|
117
|
+
const parts = service.split('.');
|
|
118
|
+
if (parts[0]) {
|
|
119
|
+
namespaces.add(parts[0]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return Array.from(namespaces);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Inject namespace globals into globalThis
|
|
127
|
+
*/
|
|
128
|
+
function injectNamespaceGlobals(namespaces: Record<string, unknown>): void {
|
|
129
|
+
for (const [name, value] of Object.entries(namespaces)) {
|
|
130
|
+
(globalThis as Record<string, unknown>)[name] = value;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Remove namespace globals from globalThis
|
|
136
|
+
*/
|
|
137
|
+
function removeNamespaceGlobals(namespaceNames: string[]): void {
|
|
138
|
+
for (const name of namespaceNames) {
|
|
139
|
+
delete (globalThis as Record<string, unknown>)[name];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface CompiledWidget {
|
|
144
|
+
/** Compiled ESM code */
|
|
145
|
+
code: string;
|
|
146
|
+
/** Content hash for caching */
|
|
147
|
+
hash: string;
|
|
148
|
+
/** Original manifest */
|
|
149
|
+
manifest: {
|
|
150
|
+
name: string;
|
|
151
|
+
services?: string[];
|
|
152
|
+
[key: string]: unknown;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Run a compiled widget using Ink
|
|
158
|
+
*
|
|
159
|
+
* This is the main entry point for running terminal widgets.
|
|
160
|
+
* The image owns all React/Ink dependencies.
|
|
161
|
+
*/
|
|
162
|
+
export async function run(
|
|
163
|
+
widget: CompiledWidget,
|
|
164
|
+
options: RunnerOptions = {},
|
|
165
|
+
): Promise<RunnerInstance> {
|
|
166
|
+
const {
|
|
167
|
+
proxy,
|
|
168
|
+
inputs = {},
|
|
169
|
+
stdout = process.stdout as WriteStream,
|
|
170
|
+
stdin = process.stdin,
|
|
171
|
+
exitOnCtrlC = true,
|
|
172
|
+
} = options;
|
|
173
|
+
const mountId = generateMountId();
|
|
174
|
+
|
|
175
|
+
// Inject namespace globals for services
|
|
176
|
+
const services = widget.manifest.services || [];
|
|
177
|
+
const namespaceNames = extractNamespaces(services);
|
|
178
|
+
const namespaces = generateNamespaceGlobals(services, proxy);
|
|
179
|
+
injectNamespaceGlobals(namespaces);
|
|
180
|
+
|
|
181
|
+
// Import the widget module from code
|
|
182
|
+
const dataUri = `data:text/javascript;base64,${Buffer.from(
|
|
183
|
+
widget.code,
|
|
184
|
+
).toString('base64')}`;
|
|
185
|
+
|
|
186
|
+
let module: { default?: unknown };
|
|
187
|
+
try {
|
|
188
|
+
module = await import(/* webpackIgnore: true */ /* @vite-ignore */ dataUri);
|
|
189
|
+
} catch {
|
|
190
|
+
// Fallback: use Function-based loading
|
|
191
|
+
const AsyncFunction = Object.getPrototypeOf(async function () {})
|
|
192
|
+
.constructor as new (argName: string, code: string) => (
|
|
193
|
+
exports: Record<string, unknown>,
|
|
194
|
+
) => Promise<Record<string, unknown>>;
|
|
195
|
+
const exports: Record<string, unknown> = {};
|
|
196
|
+
const fn = new AsyncFunction('exports', widget.code + '\nreturn exports;');
|
|
197
|
+
module = await fn(exports);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const Component = module.default;
|
|
201
|
+
if (!Component) {
|
|
202
|
+
removeNamespaceGlobals(namespaceNames);
|
|
203
|
+
throw new Error('Widget must export a default component');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (typeof Component !== 'function') {
|
|
207
|
+
removeNamespaceGlobals(namespaceNames);
|
|
208
|
+
throw new Error('Widget default export must be a function/component');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Render using Ink
|
|
212
|
+
let currentInputs = { ...inputs };
|
|
213
|
+
const element = React.createElement(
|
|
214
|
+
Component as React.ComponentType,
|
|
215
|
+
currentInputs,
|
|
216
|
+
);
|
|
217
|
+
const instance = render(element, {
|
|
218
|
+
stdout,
|
|
219
|
+
stdin,
|
|
220
|
+
exitOnCtrlC,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
id: mountId,
|
|
225
|
+
unmount() {
|
|
226
|
+
instance.unmount();
|
|
227
|
+
removeNamespaceGlobals(namespaceNames);
|
|
228
|
+
},
|
|
229
|
+
waitUntilExit() {
|
|
230
|
+
return instance.waitUntilExit();
|
|
231
|
+
},
|
|
232
|
+
rerender(newInputs: Record<string, unknown>) {
|
|
233
|
+
currentInputs = { ...currentInputs, ...newInputs };
|
|
234
|
+
const newElement = React.createElement(
|
|
235
|
+
Component as React.ComponentType,
|
|
236
|
+
currentInputs,
|
|
237
|
+
);
|
|
238
|
+
instance.rerender(newElement);
|
|
239
|
+
},
|
|
240
|
+
clear() {
|
|
241
|
+
instance.clear();
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Run a widget once and wait for exit
|
|
248
|
+
*/
|
|
249
|
+
export async function runOnce(
|
|
250
|
+
widget: CompiledWidget,
|
|
251
|
+
options: RunnerOptions = {},
|
|
252
|
+
): Promise<void> {
|
|
253
|
+
const instance = await run(widget, options);
|
|
254
|
+
await instance.waitUntilExit();
|
|
255
|
+
instance.unmount();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Evaluate widget code and return the component
|
|
260
|
+
*
|
|
261
|
+
* This is used for more advanced scenarios where you need
|
|
262
|
+
* direct access to the component.
|
|
263
|
+
*/
|
|
264
|
+
export async function evaluateWidget(
|
|
265
|
+
code: string,
|
|
266
|
+
services: Record<string, unknown> = {},
|
|
267
|
+
): Promise<React.ComponentType<{ services?: Record<string, unknown> }>> {
|
|
268
|
+
// Store services for widget access
|
|
269
|
+
(globalThis as Record<string, unknown>).__PATCHWORK_SERVICES__ = services;
|
|
270
|
+
|
|
271
|
+
// Inject globals that the compiled code expects
|
|
272
|
+
const __EXPORTS__: Record<string, unknown> = {};
|
|
273
|
+
const __REACT__ = React;
|
|
274
|
+
const __INK__ = await import('ink');
|
|
275
|
+
|
|
276
|
+
// Execute the transformed code with injected globals
|
|
277
|
+
const fn = new Function('__EXPORTS__', '__REACT__', '__INK__', code);
|
|
278
|
+
fn(__EXPORTS__, __REACT__, __INK__);
|
|
279
|
+
|
|
280
|
+
const Component =
|
|
281
|
+
__EXPORTS__.default ||
|
|
282
|
+
__EXPORTS__.Widget ||
|
|
283
|
+
Object.values(__EXPORTS__).find(
|
|
284
|
+
(v): v is React.ComponentType => typeof v === 'function',
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
if (!Component) {
|
|
288
|
+
throw new Error('No default export or Widget component found');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return Component as React.ComponentType<{
|
|
292
|
+
services?: Record<string, unknown>;
|
|
293
|
+
}>;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Render a component directly with Ink
|
|
298
|
+
*
|
|
299
|
+
* For cases where you already have an evaluated component.
|
|
300
|
+
*/
|
|
301
|
+
export function renderComponent(
|
|
302
|
+
Component: React.ComponentType<Record<string, unknown>>,
|
|
303
|
+
props: Record<string, unknown> = {},
|
|
304
|
+
options: Omit<RunnerOptions, 'proxy' | 'inputs'> = {},
|
|
305
|
+
): RunnerInstance {
|
|
306
|
+
const {
|
|
307
|
+
stdout = process.stdout as WriteStream,
|
|
308
|
+
stdin = process.stdin,
|
|
309
|
+
exitOnCtrlC = true,
|
|
310
|
+
} = options;
|
|
311
|
+
const mountId = generateMountId();
|
|
312
|
+
|
|
313
|
+
let currentProps = { ...props };
|
|
314
|
+
const element = React.createElement(Component, currentProps);
|
|
315
|
+
const instance = render(element, {
|
|
316
|
+
stdout,
|
|
317
|
+
stdin,
|
|
318
|
+
exitOnCtrlC,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
id: mountId,
|
|
323
|
+
unmount: () => instance.unmount(),
|
|
324
|
+
waitUntilExit: () => instance.waitUntilExit(),
|
|
325
|
+
rerender(newProps: Record<string, unknown>) {
|
|
326
|
+
currentProps = { ...currentProps, ...newProps };
|
|
327
|
+
instance.rerender(React.createElement(Component, currentProps));
|
|
328
|
+
},
|
|
329
|
+
clear: () => instance.clear(),
|
|
330
|
+
};
|
|
331
|
+
}
|