@aprovan/patchwork 0.1.0 → 0.1.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.
- package/.github/workflows/publish.yml +1 -1
- package/.vscode/launch.json +19 -0
- package/README.md +24 -0
- package/apps/chat/package.json +4 -4
- package/apps/chat/vite.config.ts +8 -8
- package/docs/specs/directory-sync.md +822 -0
- package/docs/specs/patchwork-vscode.md +625 -0
- package/package.json +2 -2
- package/packages/compiler/package.json +3 -2
- package/packages/compiler/src/index.ts +13 -14
- package/packages/compiler/src/vfs/backends/http.ts +139 -0
- package/packages/compiler/src/vfs/backends/indexeddb.ts +185 -24
- package/packages/compiler/src/vfs/backends/memory.ts +166 -0
- package/packages/compiler/src/vfs/core/index.ts +26 -0
- package/packages/compiler/src/vfs/core/types.ts +93 -0
- package/packages/compiler/src/vfs/core/utils.ts +42 -0
- package/packages/compiler/src/vfs/core/virtual-fs.ts +120 -0
- package/packages/compiler/src/vfs/index.ts +37 -5
- package/packages/compiler/src/vfs/project.ts +16 -16
- package/packages/compiler/src/vfs/store.ts +183 -19
- package/packages/compiler/src/vfs/sync/differ.ts +47 -0
- package/packages/compiler/src/vfs/sync/engine.ts +398 -0
- package/packages/compiler/src/vfs/sync/index.ts +3 -0
- package/packages/compiler/src/vfs/sync/resolver.ts +46 -0
- package/packages/compiler/src/vfs/types.ts +1 -8
- package/packages/compiler/tsup.config.ts +5 -5
- package/packages/editor/package.json +1 -1
- package/packages/editor/src/components/CodeBlockExtension.tsx +1 -1
- package/packages/editor/src/components/CodePreview.tsx +59 -1
- package/packages/editor/src/components/edit/CodeBlockView.tsx +72 -0
- package/packages/editor/src/components/edit/EditModal.tsx +169 -28
- package/packages/editor/src/components/edit/FileTree.tsx +67 -13
- package/packages/editor/src/components/edit/MediaPreview.tsx +106 -0
- package/packages/editor/src/components/edit/SaveConfirmDialog.tsx +60 -0
- package/packages/editor/src/components/edit/fileTypes.ts +125 -0
- package/packages/editor/src/components/edit/index.ts +4 -0
- package/packages/editor/src/components/edit/types.ts +3 -0
- package/packages/editor/src/components/edit/useEditSession.ts +22 -4
- package/packages/editor/src/index.ts +17 -0
- package/packages/editor/src/lib/diff.ts +2 -1
- package/packages/editor/src/lib/vfs.ts +28 -10
- package/packages/editor/tsup.config.ts +10 -5
- package/packages/stitchery/package.json +5 -3
- package/packages/stitchery/src/server/index.ts +57 -57
- package/packages/stitchery/src/server/vfs-routes.ts +246 -56
- package/packages/stitchery/tsup.config.ts +5 -5
- package/packages/utcp/package.json +3 -2
- package/packages/utcp/tsconfig.json +6 -2
- package/packages/utcp/tsup.config.ts +6 -6
- package/packages/vscode/README.md +31 -0
- package/packages/vscode/media/outline.png +0 -0
- package/packages/vscode/media/outline.svg +70 -0
- package/packages/vscode/media/patchwork.png +0 -0
- package/packages/vscode/media/patchwork.svg +72 -0
- package/packages/vscode/node_modules/.bin/jiti +17 -0
- package/packages/vscode/node_modules/.bin/tsc +17 -0
- package/packages/vscode/node_modules/.bin/tsserver +17 -0
- package/packages/vscode/node_modules/.bin/tsup +17 -0
- package/packages/vscode/node_modules/.bin/tsup-node +17 -0
- package/packages/vscode/node_modules/.bin/tsx +17 -0
- package/packages/vscode/package.json +136 -0
- package/packages/vscode/src/extension.ts +612 -0
- package/packages/vscode/src/providers/PatchworkFileSystemProvider.ts +205 -0
- package/packages/vscode/src/providers/PatchworkTreeProvider.ts +177 -0
- package/packages/vscode/src/providers/PreviewPanelProvider.ts +536 -0
- package/packages/vscode/src/services/EditService.ts +24 -0
- package/packages/vscode/src/services/EmbeddedStitchery.ts +82 -0
- package/packages/vscode/tsconfig.json +13 -0
- package/packages/vscode/tsup.config.ts +11 -0
- package/packages/compiler/src/vfs/backends/local-fs.ts +0 -41
- package/packages/compiler/src/vfs/backends/s3.ts +0 -60
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
# Patchwork Viewer VS Code
|
|
2
|
+
|
|
3
|
+
Implement a VS Code extension for viewing Patchwork projects. This should be a fairly simple wrapper around the existing Patchwork file viewer/editor/render, with a few differences:
|
|
4
|
+
|
|
5
|
+
1. The file selector widget shouldn't be shown for Patchwork. Instead, the extension should render a file tree using VS Code's native components. When a file is selected (opened), it should communicate this to Patchwork
|
|
6
|
+
2. When a file stored in a Patchwork project is opened, it should operate like a normal VS Code file, opening as an open file rather than in the Patchwork tree.
|
|
7
|
+
3. The preview renderer should show the Patchwork compilation/renderer, if supported. Defer to VS Code's in-built editor/preview otherwise (e.g. let VS Code's Markdown preview show)
|
|
8
|
+
|
|
9
|
+
Keep the same LLM-driven editing functionality and editor widget that Patchwork provides for browser-based widgets. The chat-based method and history editing should be available for all files, too.
|
|
10
|
+
|
|
11
|
+
We'll need a way to connect to an LLM provider. Provide a method for connecting to the GitHub Code Copilot proxy that we already use (i.e. let me run the Copilot proxy and give the extension the option to point to it. )
|
|
12
|
+
|
|
13
|
+
Where needed, prefer to add functionality to the other Patchwork packages/components rather than bundling into the VS Code extension (e.g. disabling the file tree viewer should be a Patchwork component property).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Technical Overview
|
|
18
|
+
|
|
19
|
+
### Existing Patchwork Architecture
|
|
20
|
+
|
|
21
|
+
The Patchwork monorepo contains these relevant packages:
|
|
22
|
+
|
|
23
|
+
| Package | Purpose |
|
|
24
|
+
|---------|---------|
|
|
25
|
+
| `@aprovan/patchwork` | Core services/types, service proxy system, context providers |
|
|
26
|
+
| `@aprovan/patchwork-editor` | React components: `MarkdownEditor`, `CodePreview`, `FileTree`, `EditModal`, `EditHistory`, `useEditSession` |
|
|
27
|
+
| `@aprovan/bobbin` | Visual element selection/editing with design tokens and change tracking |
|
|
28
|
+
| `@aprovan/patchwork-compiler` | JSX→ESM compilation, image loading, DOM mounting (`createCompiler`, `mount`, `unmount`) |
|
|
29
|
+
| `@aprovan/patchwork-stitchery` | LLM server (Vercel AI SDK), MCP client integration, service registry, edit/chat prompts |
|
|
30
|
+
| `@aprovan/patchwork-utcp` | Backend service integration through UTCP protocol |
|
|
31
|
+
| `@aprovan/patchwork-vscode` | **Placeholder** - only contains package.json stub |
|
|
32
|
+
|
|
33
|
+
### Key Existing Components
|
|
34
|
+
|
|
35
|
+
**Editor Package (`@aprovan/patchwork-editor`)**:
|
|
36
|
+
- `FileTree.tsx` - React file tree component with `VirtualFile[]` input, supports file selection and media upload
|
|
37
|
+
- `EditModal.tsx` - Full editing UI with code view, preview, edit history, and LLM edit submission
|
|
38
|
+
- `useEditSession.ts` - React hook managing edit state, history, API calls to `/api/edit`
|
|
39
|
+
- `CodePreview.tsx` - Live compilation preview with save-to-VFS capability
|
|
40
|
+
- `api.ts` - `sendEditRequest()` for LLM-powered code edits
|
|
41
|
+
|
|
42
|
+
**Compiler Package (`@aprovan/patchwork-compiler`)**:
|
|
43
|
+
- `createCompiler(options)` - Returns a `Compiler` instance
|
|
44
|
+
- `compiler.compile(source, manifest, options)` - Compiles JSX/TSX to widget
|
|
45
|
+
- `compiler.mount(widget, { target, mode })` - Mounts to DOM element
|
|
46
|
+
- `compiler.unmount(mounted)` - Cleanup
|
|
47
|
+
- Supports browser platform with image packages (`@aprovan/patchwork-image-shadcn`)
|
|
48
|
+
|
|
49
|
+
**Stitchery Package (`@aprovan/patchwork-stitchery`)**:
|
|
50
|
+
- `createStitcheryServer(config)` - HTTP server with `/api/chat`, `/api/edit`, `/api/services` routes
|
|
51
|
+
- Uses `@ai-sdk/openai-compatible` provider pointed at Copilot proxy
|
|
52
|
+
- `ServiceRegistry` for tool discovery and registration
|
|
53
|
+
- `PATCHWORK_PROMPT`, `EDIT_PROMPT` - System prompts for LLM
|
|
54
|
+
|
|
55
|
+
**Copilot Proxy (`@aprovan/copilot-proxy`)**:
|
|
56
|
+
- Standalone package providing OpenAI-compatible API using GitHub Copilot
|
|
57
|
+
- `copilot-proxy serve --port 3000` starts the proxy server
|
|
58
|
+
- Device flow authentication via `copilot-proxy connect`
|
|
59
|
+
- Already used by stitchery server for LLM access
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Implementation Plan
|
|
64
|
+
|
|
65
|
+
### Phase 1: Extension Foundation
|
|
66
|
+
|
|
67
|
+
#### 1.1 Project Setup (`packages/vscode/`)
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
packages/vscode/
|
|
71
|
+
├── package.json # VS Code extension manifest
|
|
72
|
+
├── tsconfig.json
|
|
73
|
+
├── tsup.config.ts # Bundle for VS Code
|
|
74
|
+
├── src/
|
|
75
|
+
│ ├── extension.ts # activate/deactivate
|
|
76
|
+
│ ├── commands/ # Command handlers
|
|
77
|
+
│ ├── providers/ # Tree, Editor, Preview providers
|
|
78
|
+
│ ├── webview/ # Webview panel logic
|
|
79
|
+
│ └── config.ts # Settings management
|
|
80
|
+
└── media/
|
|
81
|
+
└── webview/ # Bundled React app for webviews
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**package.json contributions**:
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"activationEvents": ["onView:patchworkExplorer", "onCommand:patchwork.*"],
|
|
88
|
+
"contributes": {
|
|
89
|
+
"viewsContainers": {
|
|
90
|
+
"activitybar": [{
|
|
91
|
+
"id": "patchwork",
|
|
92
|
+
"title": "Patchwork",
|
|
93
|
+
"icon": "media/icon.svg"
|
|
94
|
+
}]
|
|
95
|
+
},
|
|
96
|
+
"views": {
|
|
97
|
+
"patchwork": [{
|
|
98
|
+
"id": "patchworkExplorer",
|
|
99
|
+
"name": "Projects"
|
|
100
|
+
}]
|
|
101
|
+
},
|
|
102
|
+
"commands": [
|
|
103
|
+
{ "command": "patchwork.openProject", "title": "Open Patchwork Project" },
|
|
104
|
+
{ "command": "patchwork.newProject", "title": "New Patchwork Project" },
|
|
105
|
+
{ "command": "patchwork.showPreview", "title": "Show Patchwork Preview" },
|
|
106
|
+
{ "command": "patchwork.editWithAI", "title": "Edit with AI" }
|
|
107
|
+
],
|
|
108
|
+
"configuration": {
|
|
109
|
+
"title": "Patchwork",
|
|
110
|
+
"properties": {
|
|
111
|
+
"patchwork.copilotProxyUrl": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"default": "http://localhost:3000",
|
|
114
|
+
"description": "URL of the GitHub Copilot proxy server"
|
|
115
|
+
},
|
|
116
|
+
"patchwork.imagePackage": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"default": "@aprovan/patchwork-image-shadcn",
|
|
119
|
+
"description": "Default image package for widget compilation"
|
|
120
|
+
},
|
|
121
|
+
"patchwork.vfsDir": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"default": "",
|
|
124
|
+
"description": "Directory for virtual file system storage (defaults to workspace)"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"menus": {
|
|
129
|
+
"editor/title": [{
|
|
130
|
+
"command": "patchwork.showPreview",
|
|
131
|
+
"when": "resourceExtname =~ /\\.(tsx|jsx)$/",
|
|
132
|
+
"group": "navigation"
|
|
133
|
+
}]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### 1.2 Patchwork Project Tree Provider
|
|
140
|
+
|
|
141
|
+
Replace the React `FileTree` with VS Code's native `TreeDataProvider`:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// src/providers/PatchworkTreeProvider.ts
|
|
145
|
+
import * as vscode from 'vscode';
|
|
146
|
+
import type { VirtualProject, VirtualFile } from '@aprovan/patchwork-compiler';
|
|
147
|
+
|
|
148
|
+
export class PatchworkTreeProvider implements vscode.TreeDataProvider<PatchworkTreeItem> {
|
|
149
|
+
private _onDidChangeTreeData = new vscode.EventEmitter<PatchworkTreeItem | undefined>();
|
|
150
|
+
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
|
|
151
|
+
|
|
152
|
+
private projects: Map<string, VirtualProject> = new Map();
|
|
153
|
+
|
|
154
|
+
setProject(id: string, project: VirtualProject): void {
|
|
155
|
+
this.projects.set(id, project);
|
|
156
|
+
this._onDidChangeTreeData.fire(undefined);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getTreeItem(element: PatchworkTreeItem): vscode.TreeItem { /* ... */ }
|
|
160
|
+
getChildren(element?: PatchworkTreeItem): Thenable<PatchworkTreeItem[]> { /* ... */ }
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**File selection behavior**:
|
|
165
|
+
- Clicking a file in the tree opens it in VS Code's standard editor
|
|
166
|
+
- The extension listens for `vscode.window.onDidChangeActiveTextEditor` to sync state
|
|
167
|
+
- Preview panel updates when a compilable file is selected
|
|
168
|
+
|
|
169
|
+
#### 1.3 Webview Preview Panel
|
|
170
|
+
|
|
171
|
+
Create a webview that hosts the compiled widget preview:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// src/providers/PreviewPanelProvider.ts
|
|
175
|
+
export class PreviewPanelProvider {
|
|
176
|
+
private panel: vscode.WebviewPanel | undefined;
|
|
177
|
+
|
|
178
|
+
async showPreview(document: vscode.TextDocument): Promise<void> {
|
|
179
|
+
// Create or reveal panel
|
|
180
|
+
// Send code to webview for compilation via postMessage
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private getWebviewContent(): string {
|
|
184
|
+
// Returns HTML that loads the bundled React preview app
|
|
185
|
+
// Includes @aprovan/patchwork-compiler and image package
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Webview React App** (`media/webview/`):
|
|
191
|
+
- Minimal React app using `createCompiler()` and `mount()`
|
|
192
|
+
- Listens for `window.addEventListener('message', ...)` to receive code updates
|
|
193
|
+
- Reports compilation errors back to extension
|
|
194
|
+
|
|
195
|
+
### Phase 2: Package Modifications
|
|
196
|
+
|
|
197
|
+
#### 2.1 Editor Package Changes
|
|
198
|
+
|
|
199
|
+
**Add `hideFileTree` prop to `EditModal`**:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// @aprovan/patchwork-editor - EditModal.tsx
|
|
203
|
+
export interface EditModalProps extends UseEditSessionOptions {
|
|
204
|
+
// ... existing props
|
|
205
|
+
hideFileTree?: boolean; // NEW: Hide built-in file tree for VS Code integration
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Export standalone edit components**:
|
|
210
|
+
- `EditHistoryPanel` - Just the history list
|
|
211
|
+
- `EditInputBar` - Just the prompt input with submit
|
|
212
|
+
- `BobbinOverlay` - Visual selection without modal wrapper
|
|
213
|
+
|
|
214
|
+
#### 2.2 Compiler Package Changes
|
|
215
|
+
|
|
216
|
+
**Headless compilation mode**:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// @aprovan/patchwork-compiler
|
|
220
|
+
export interface CompileOptions {
|
|
221
|
+
// ... existing
|
|
222
|
+
headless?: boolean; // Return module without DOM dependencies for Node.js
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface HeadlessCompileResult {
|
|
226
|
+
code: string; // Compiled ESM
|
|
227
|
+
css?: string; // Extracted styles
|
|
228
|
+
imports: string[]; // External dependencies
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
This enables the extension to:
|
|
233
|
+
1. Pre-compile code in the extension host
|
|
234
|
+
2. Send compiled bundle to webview (faster reload)
|
|
235
|
+
3. Surface compile errors in VS Code's Problems panel
|
|
236
|
+
|
|
237
|
+
#### 2.3 Create `@aprovan/patchwork-vscode-common`
|
|
238
|
+
|
|
239
|
+
Shared types and utilities for extension-webview communication:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// packages/vscode-common/src/index.ts
|
|
243
|
+
export interface ExtensionToWebviewMessage {
|
|
244
|
+
type: 'compile' | 'updateFile' | 'setServices' | 'setColorScheme' | 'serviceResult';
|
|
245
|
+
payload: unknown;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface WebviewToExtensionMessage {
|
|
249
|
+
type: 'ready' | 'compileError' | 'compileSuccess' | 'editRequest' | 'saveRequest' | 'serviceCall';
|
|
250
|
+
payload: unknown;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface PatchworkProjectState {
|
|
254
|
+
projectId: string;
|
|
255
|
+
activeFile: string;
|
|
256
|
+
files: Array<{ path: string; content: string; encoding: 'utf8' | 'base64' }>;
|
|
257
|
+
services: string[];
|
|
258
|
+
colorScheme: 'light' | 'dark';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface ServiceCallMessage {
|
|
262
|
+
id: string; // Correlation ID for response
|
|
263
|
+
namespace: string; // e.g., 'github'
|
|
264
|
+
procedure: string; // e.g., 'repos_list_for_user'
|
|
265
|
+
args: Record<string, unknown>;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface ServiceResultMessage {
|
|
269
|
+
id: string;
|
|
270
|
+
result?: unknown;
|
|
271
|
+
error?: string;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Phase 3: LLM Integration
|
|
276
|
+
|
|
277
|
+
#### 3.1 Copilot Proxy Configuration
|
|
278
|
+
|
|
279
|
+
**Settings UI**:
|
|
280
|
+
- `patchwork.copilotProxyUrl` - URL to running proxy
|
|
281
|
+
- Status bar item showing connection state
|
|
282
|
+
- Command `patchwork.testConnection` to verify proxy is reachable
|
|
283
|
+
|
|
284
|
+
**Connection flow**:
|
|
285
|
+
1. Extension checks proxy health on activation: `GET {proxyUrl}/health`
|
|
286
|
+
2. If unreachable, show notification with "Start Proxy" action
|
|
287
|
+
3. "Start Proxy" opens terminal and runs `npx @aprovan/copilot-proxy serve`
|
|
288
|
+
|
|
289
|
+
#### 3.2 Edit API Integration
|
|
290
|
+
|
|
291
|
+
**Direct API calls from extension** (no separate stitchery server needed):
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// src/services/EditService.ts
|
|
295
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
296
|
+
import { streamText } from 'ai';
|
|
297
|
+
import { EDIT_PROMPT } from '@aprovan/patchwork-stitchery';
|
|
298
|
+
|
|
299
|
+
export class EditService {
|
|
300
|
+
constructor(private copilotProxyUrl: string) {}
|
|
301
|
+
|
|
302
|
+
async *streamEdit(code: string, prompt: string): AsyncGenerator<string> {
|
|
303
|
+
const provider = createOpenAICompatible({
|
|
304
|
+
name: 'copilot-proxy',
|
|
305
|
+
baseURL: this.copilotProxyUrl,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const result = streamText({
|
|
309
|
+
model: provider('claude-sonnet-4'),
|
|
310
|
+
system: EDIT_PROMPT,
|
|
311
|
+
messages: [{ role: 'user', content: `Code:\n${code}\n\nEdit: ${prompt}` }],
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
for await (const chunk of result.textStream) {
|
|
315
|
+
yield chunk;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### 3.3 Embedded Stitchery for Services
|
|
322
|
+
|
|
323
|
+
Rather than requiring a separate stitchery server process, embed the service registry directly in the extension host:
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// src/services/EmbeddedStitchery.ts
|
|
327
|
+
import { ServiceRegistry } from '@aprovan/patchwork-stitchery';
|
|
328
|
+
import { createUtcpBackend } from '@aprovan/patchwork-utcp';
|
|
329
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
330
|
+
import type { ServiceCallMessage, ServiceResultMessage } from '@aprovan/patchwork-vscode-common';
|
|
331
|
+
|
|
332
|
+
export class EmbeddedStitchery {
|
|
333
|
+
private registry: ServiceRegistry;
|
|
334
|
+
|
|
335
|
+
async initialize(utcpConfig?: object, mcpServers?: McpServerConfig[]): Promise<void> {
|
|
336
|
+
this.registry = new ServiceRegistry();
|
|
337
|
+
|
|
338
|
+
// Register UTCP services if configured
|
|
339
|
+
if (utcpConfig) {
|
|
340
|
+
const { toolInfos } = await createUtcpBackend(utcpConfig);
|
|
341
|
+
for (const tool of toolInfos) {
|
|
342
|
+
this.registry.registerTool(tool);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Register MCP servers if configured
|
|
347
|
+
for (const server of mcpServers ?? []) {
|
|
348
|
+
const client = await createMCPClient({ /* ... */ });
|
|
349
|
+
this.registry.registerTools(await client.tools(), server.name);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async handleServiceCall(msg: ServiceCallMessage): Promise<ServiceResultMessage> {
|
|
354
|
+
try {
|
|
355
|
+
const result = await this.registry.callTool(msg.namespace, msg.procedure, msg.args);
|
|
356
|
+
return { id: msg.id, result };
|
|
357
|
+
} catch (err) {
|
|
358
|
+
return { id: msg.id, error: err instanceof Error ? err.message : 'Unknown error' };
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
getNamespaces(): string[] {
|
|
363
|
+
return this.registry.getNamespaces();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Service call flow**:
|
|
369
|
+
1. Widget calls `services.github.repos_list_for_user({ username: 'foo' })`
|
|
370
|
+
2. Webview intercepts via injected service proxy, sends `postMessage({ type: 'serviceCall', payload: {...} })`
|
|
371
|
+
3. Extension host receives, routes to `EmbeddedStitchery.handleServiceCall()`
|
|
372
|
+
4. Result sent back via `postMessage({ type: 'serviceResult', payload: {...} })`
|
|
373
|
+
5. Webview resolves the original promise
|
|
374
|
+
|
|
375
|
+
#### 3.4 Edit UI in Webview
|
|
376
|
+
|
|
377
|
+
The webview includes a minimal edit interface:
|
|
378
|
+
- Input field at bottom for edit prompts
|
|
379
|
+
- Streaming response display
|
|
380
|
+
- Diff preview before applying
|
|
381
|
+
- History panel (collapsible)
|
|
382
|
+
|
|
383
|
+
This reuses logic from `useEditSession` but adapted for webview messaging.
|
|
384
|
+
|
|
385
|
+
### Phase 4: File System Integration
|
|
386
|
+
|
|
387
|
+
#### 4.1 Virtual File System Provider
|
|
388
|
+
|
|
389
|
+
Implement `FileSystemProvider` for `patchwork://` URI scheme:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// src/providers/PatchworkFileSystemProvider.ts
|
|
393
|
+
export class PatchworkFileSystemProvider implements vscode.FileSystemProvider {
|
|
394
|
+
private projects: Map<string, VirtualProject> = new Map();
|
|
395
|
+
|
|
396
|
+
// Maps patchwork://projectId/path/to/file.tsx to VirtualFile
|
|
397
|
+
readFile(uri: vscode.Uri): Uint8Array { /* ... */ }
|
|
398
|
+
writeFile(uri: vscode.Uri, content: Uint8Array): void { /* ... */ }
|
|
399
|
+
|
|
400
|
+
// Sync changes back to in-memory project
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**URI format**: `patchwork://project-id/src/components/Button.tsx`
|
|
405
|
+
|
|
406
|
+
#### 4.2 Document Sync
|
|
407
|
+
|
|
408
|
+
Keep VS Code editor and Patchwork project in sync:
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
// On text document change
|
|
412
|
+
vscode.workspace.onDidChangeTextDocument((e) => {
|
|
413
|
+
if (e.document.uri.scheme === 'patchwork') {
|
|
414
|
+
const { projectId, filePath } = parseUri(e.document.uri);
|
|
415
|
+
this.updateProjectFile(projectId, filePath, e.document.getText());
|
|
416
|
+
this.previewPanel.refresh();
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
#### 4.3 Save to Disk
|
|
422
|
+
|
|
423
|
+
Command to export Patchwork project to real filesystem:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
// Command: patchwork.exportProject
|
|
427
|
+
async function exportProject(projectId: string, targetDir: vscode.Uri): Promise<void> {
|
|
428
|
+
const project = this.getProject(projectId);
|
|
429
|
+
for (const [path, file] of project.files) {
|
|
430
|
+
const targetPath = vscode.Uri.joinPath(targetDir, path);
|
|
431
|
+
await vscode.workspace.fs.writeFile(targetPath, Buffer.from(file.content));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Phase 5: Preview & Compilation
|
|
437
|
+
|
|
438
|
+
#### 5.1 Preview Panel Architecture
|
|
439
|
+
|
|
440
|
+
Single shared preview panel that follows the active editor:
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
444
|
+
│ VS Code Extension Host │
|
|
445
|
+
│ ┌─────────────────────────────────────────────────────────┤
|
|
446
|
+
│ │ PreviewPanelProvider (singleton) │
|
|
447
|
+
│ │ - Single WebviewPanel, follows active editor │
|
|
448
|
+
│ │ - Sends code updates via postMessage │
|
|
449
|
+
│ │ - Receives compile errors, edit requests │
|
|
450
|
+
│ ├─────────────────────────────────────────────────────────┤
|
|
451
|
+
│ │ EmbeddedStitchery │
|
|
452
|
+
│ │ - ServiceRegistry for UTCP/MCP tools │
|
|
453
|
+
│ │ - Routes webview service calls to backends │
|
|
454
|
+
│ │ - EditService for LLM-powered code edits │
|
|
455
|
+
│ └─────────────────────────────────────────────────────────┤
|
|
456
|
+
│ │ postMessage │
|
|
457
|
+
├───────────────────────────┼─────────────────────────────────┤
|
|
458
|
+
│ Webview (sandboxed) ▼ │
|
|
459
|
+
│ ┌─────────────────────────────────────────────────────────┤
|
|
460
|
+
│ │ React App (pre-bundled with esbuild) │
|
|
461
|
+
│ │ - createCompiler() from @aprovan/patchwork-compiler │
|
|
462
|
+
│ │ - Renders compiled widget │
|
|
463
|
+
│ │ - Service calls → postMessage → extension host │
|
|
464
|
+
│ │ - EditInputBar for AI edits │
|
|
465
|
+
│ │ - EditHistory panel │
|
|
466
|
+
│ │ - Respects colorScheme: 'light' | 'dark' │
|
|
467
|
+
│ └─────────────────────────────────────────────────────────┤
|
|
468
|
+
└─────────────────────────────────────────────────────────────┘
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Active editor tracking**:
|
|
472
|
+
```typescript
|
|
473
|
+
// PreviewPanelProvider.ts
|
|
474
|
+
vscode.window.onDidChangeActiveTextEditor((editor) => {
|
|
475
|
+
if (editor && this.isPreviewableFile(editor.document.uri)) {
|
|
476
|
+
this.updatePreview(editor.document);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
vscode.window.onDidChangeActiveColorTheme((theme) => {
|
|
481
|
+
const colorScheme = theme.kind === vscode.ColorThemeKind.Dark ? 'dark' : 'light';
|
|
482
|
+
this.panel?.webview.postMessage({ type: 'setColorScheme', payload: colorScheme });
|
|
483
|
+
});
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
#### 5.2 Supported File Types
|
|
487
|
+
|
|
488
|
+
| Extension | Behavior |
|
|
489
|
+
|-----------|----------|
|
|
490
|
+
| `.tsx`, `.jsx` | Compile with patchwork-compiler, show in preview panel |
|
|
491
|
+
| `.ts`, `.js` | Open in VS Code editor, no preview |
|
|
492
|
+
| `.md` | Open in VS Code editor, use VS Code's markdown preview |
|
|
493
|
+
| `.css`, `.json` | Open in VS Code editor |
|
|
494
|
+
| Images | Show in preview panel using `MediaPreview` component |
|
|
495
|
+
|
|
496
|
+
#### 5.3 Error Handling
|
|
497
|
+
|
|
498
|
+
Compilation errors surface in multiple places:
|
|
499
|
+
1. **Preview panel**: Inline error display with stack trace
|
|
500
|
+
2. **Problems panel**: `vscode.languages.createDiagnosticCollection('patchwork')`
|
|
501
|
+
3. **Editor decorations**: Squiggles on error lines
|
|
502
|
+
|
|
503
|
+
### Phase 6: Polish & UX
|
|
504
|
+
|
|
505
|
+
#### 6.1 Status Bar
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
|
|
509
|
+
statusBar.text = '$(plug) Patchwork';
|
|
510
|
+
statusBar.tooltip = 'Copilot Proxy: Connected';
|
|
511
|
+
statusBar.command = 'patchwork.showConnectionStatus';
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
States:
|
|
515
|
+
- `$(plug)` Connected (green)
|
|
516
|
+
- `$(debug-disconnect)` Disconnected (red)
|
|
517
|
+
- `$(sync~spin)` Connecting...
|
|
518
|
+
|
|
519
|
+
#### 6.2 Code Actions
|
|
520
|
+
|
|
521
|
+
Register quick fixes for common scenarios:
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
vscode.languages.registerCodeActionsProvider(['typescriptreact', 'javascriptreact'], {
|
|
525
|
+
provideCodeActions(document, range, context) {
|
|
526
|
+
return [
|
|
527
|
+
{
|
|
528
|
+
title: 'Edit with Patchwork AI',
|
|
529
|
+
command: 'patchwork.editWithAI',
|
|
530
|
+
arguments: [document.uri, range]
|
|
531
|
+
}
|
|
532
|
+
];
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### 6.3 Keyboard Shortcuts
|
|
538
|
+
|
|
539
|
+
| Shortcut | Command |
|
|
540
|
+
|----------|---------|
|
|
541
|
+
| `Cmd+Shift+Alt+P` | Show Patchwork Preview |
|
|
542
|
+
| `Cmd+Shift+Alt+E` | Edit with AI (selected code) |
|
|
543
|
+
| `Cmd+Shift+Alt+H` | Show Edit History |
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Package Dependencies
|
|
548
|
+
|
|
549
|
+
### `@aprovan/patchwork-vscode`
|
|
550
|
+
|
|
551
|
+
```json
|
|
552
|
+
{
|
|
553
|
+
"dependencies": {
|
|
554
|
+
"@aprovan/patchwork": "workspace:*",
|
|
555
|
+
"@aprovan/patchwork-stitchery": "workspace:*",
|
|
556
|
+
"@aprovan/patchwork-utcp": "workspace:*",
|
|
557
|
+
"@ai-sdk/openai-compatible": "^0.1.0",
|
|
558
|
+
"@ai-sdk/mcp": "^0.1.0",
|
|
559
|
+
"ai": "^4.0.0"
|
|
560
|
+
},
|
|
561
|
+
"devDependencies": {
|
|
562
|
+
"@types/vscode": "^1.85.0",
|
|
563
|
+
"@vscode/vsce": "^2.22.0",
|
|
564
|
+
"esbuild": "^0.20.0"
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### Webview Bundle
|
|
570
|
+
|
|
571
|
+
Pre-bundled React app (esbuild) including:
|
|
572
|
+
- `@aprovan/patchwork-compiler`
|
|
573
|
+
- `@aprovan/patchwork-editor` (EditHistory, EditInputBar components)
|
|
574
|
+
- `@aprovan/bobbin` (for visual editing)
|
|
575
|
+
- `@aprovan/patchwork-image-shadcn` (or configurable)
|
|
576
|
+
- Service proxy bridge that routes `services.*` calls through `postMessage` to extension host
|
|
577
|
+
- Light/dark mode support via CSS class toggle (`[data-theme="dark"]`)
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Development Milestones
|
|
582
|
+
|
|
583
|
+
### Milestone 1: Basic Extension (1-2 weeks)
|
|
584
|
+
- [x] Extension scaffold with activation
|
|
585
|
+
- [x] TreeDataProvider showing hardcoded project
|
|
586
|
+
- [x] Basic webview panel with "Hello World"
|
|
587
|
+
- [x] Configuration for Copilot proxy URL
|
|
588
|
+
|
|
589
|
+
### Milestone 2: Preview System (1-2 weeks)
|
|
590
|
+
- [x] Integrate patchwork-compiler in webview
|
|
591
|
+
- [x] Live preview of TSX files
|
|
592
|
+
- [x] Error display in preview and Problems panel
|
|
593
|
+
- [x] File selection syncs with preview
|
|
594
|
+
|
|
595
|
+
### Milestone 3: AI Editing (1-2 weeks)
|
|
596
|
+
- [x] Edit input in webview
|
|
597
|
+
- [x] Direct calls to Copilot proxy from extension
|
|
598
|
+
- [x] Streaming response display
|
|
599
|
+
- [x] Apply edits to document
|
|
600
|
+
- [x] Edit history panel
|
|
601
|
+
- [x] EmbeddedStitchery for service registration (UTCP/MCP)
|
|
602
|
+
|
|
603
|
+
### Milestone 4: File System (1 week)
|
|
604
|
+
- [x] PatchworkFileSystemProvider implementation
|
|
605
|
+
- [x] Persist patchwork:// saves to disk
|
|
606
|
+
- [x] Save/export to disk
|
|
607
|
+
- [x] Sync between editor and preview
|
|
608
|
+
|
|
609
|
+
### Milestone 5: Polish (1 week)
|
|
610
|
+
- [x] Status bar connection indicator
|
|
611
|
+
- [x] Code actions integration
|
|
612
|
+
- [x] Keyboard shortcuts
|
|
613
|
+
- [x] Documentation and README
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Design Decisions
|
|
618
|
+
|
|
619
|
+
1. **Webview bundling strategy**: Pre-bundle the webview with esbuild for offline capability and faster load. Fall back to CDN only if bundling proves overly complex.
|
|
620
|
+
|
|
621
|
+
2. **Multiple preview panels**: Single shared preview panel with file indicator that follows the active editor. No per-file preview panels.
|
|
622
|
+
|
|
623
|
+
3. **Service registration**: Embed Stitchery directly in the extension host to handle service calls. The webview communicates with the extension host via postMessage, which routes to the embedded Stitchery for UTCP/MCP service execution. Avoids requiring a separate background server process.
|
|
624
|
+
|
|
625
|
+
4. **Theme synchronization**: Minimal theming—only respect VS Code's dark/light mode preference. Pass `colorScheme: 'dark' | 'light'` to the webview based on `vscode.window.activeColorTheme.kind`. No CSS variable mapping.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aprovan/patchwork",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Platform for building generative UI experiences",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"devDependencies": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"node": ">=20.0.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "turbo run build
|
|
26
|
+
"build": "turbo run build",
|
|
27
27
|
"check-types": "turbo run check-types",
|
|
28
28
|
"dev": "turbo run dev --parallel",
|
|
29
29
|
"lint": "eslint \"**/*.ts?(x)\"",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"description": "Patchwork compiler - JSX→ESM compilation, image loading, DOM mounting",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist"
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
|
-
"import": "./dist/index.js"
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.cjs"
|
|
18
19
|
}
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|