@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
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
readdir,
|
|
6
6
|
stat,
|
|
7
7
|
mkdir,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import
|
|
8
|
+
rm,
|
|
9
|
+
} from "node:fs/promises";
|
|
10
|
+
import { watch } from "node:fs";
|
|
11
|
+
import { dirname, resolve, sep } from "node:path";
|
|
12
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
11
13
|
|
|
12
14
|
export interface VFSContext {
|
|
13
15
|
rootDir: string;
|
|
@@ -15,27 +17,57 @@ export interface VFSContext {
|
|
|
15
17
|
log: (...args: unknown[]) => void;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
function normalizeRelPath(path: string): string {
|
|
21
|
+
const decoded = decodeURIComponent(path).replace(/\\/g, "/");
|
|
22
|
+
return decoded.replace(/^\/+|\/+$/g, "");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolvePath(rootDir: string, relPath: string): string {
|
|
26
|
+
const root = resolve(rootDir);
|
|
27
|
+
const full = resolve(root, relPath);
|
|
28
|
+
if (full !== root && !full.startsWith(`${root}${sep}`)) {
|
|
29
|
+
throw new Error("Invalid path");
|
|
30
|
+
}
|
|
31
|
+
return full;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function joinRelPath(base: string, name: string): string {
|
|
35
|
+
return base ? `${base}/${name}` : name;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function listAllFiles(
|
|
39
|
+
rootDir: string,
|
|
40
|
+
relPath: string,
|
|
41
|
+
): Promise<string[]> {
|
|
42
|
+
const targetPath = resolvePath(rootDir, relPath);
|
|
43
|
+
let entries: Awaited<ReturnType<typeof readdir>> = [];
|
|
19
44
|
try {
|
|
20
|
-
|
|
21
|
-
const files: string[] = [];
|
|
22
|
-
for (const entry of entries) {
|
|
23
|
-
const full = join(dir, entry.name);
|
|
24
|
-
if (entry.isDirectory()) {
|
|
25
|
-
files.push(...(await listFilesRecursive(full)));
|
|
26
|
-
} else {
|
|
27
|
-
files.push(full);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return files;
|
|
45
|
+
entries = await readdir(targetPath, { withFileTypes: true });
|
|
31
46
|
} catch {
|
|
32
47
|
return [];
|
|
33
48
|
}
|
|
49
|
+
|
|
50
|
+
const results: string[] = [];
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const entryRelPath = joinRelPath(relPath, entry.name);
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
results.push(...(await listAllFiles(rootDir, entryRelPath)));
|
|
55
|
+
} else {
|
|
56
|
+
results.push(entryRelPath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return results.sort((a, b) => a.localeCompare(b));
|
|
34
61
|
}
|
|
35
62
|
|
|
36
63
|
async function ensureDir(filePath: string): Promise<void> {
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sendJson(res: ServerResponse, status: number, body: unknown): void {
|
|
68
|
+
res.setHeader("Content-Type", "application/json");
|
|
69
|
+
res.writeHead(status);
|
|
70
|
+
res.end(JSON.stringify(body));
|
|
39
71
|
}
|
|
40
72
|
|
|
41
73
|
export function handleVFS(
|
|
@@ -43,81 +75,239 @@ export function handleVFS(
|
|
|
43
75
|
res: ServerResponse,
|
|
44
76
|
ctx: VFSContext,
|
|
45
77
|
): boolean {
|
|
46
|
-
const url = req.url ||
|
|
47
|
-
const method = req.method ||
|
|
78
|
+
const url = req.url || "/";
|
|
79
|
+
const method = req.method || "GET";
|
|
48
80
|
|
|
49
|
-
if (!url.startsWith(
|
|
81
|
+
if (!url.startsWith("/vfs")) return false;
|
|
50
82
|
|
|
51
83
|
// Handle config endpoint
|
|
52
|
-
if (url ===
|
|
53
|
-
res.setHeader(
|
|
84
|
+
if (url === "/vfs/config" && method === "GET") {
|
|
85
|
+
res.setHeader("Content-Type", "application/json");
|
|
54
86
|
res.writeHead(200);
|
|
55
87
|
res.end(JSON.stringify({ usePaths: ctx.usePaths }));
|
|
56
88
|
return true;
|
|
57
89
|
}
|
|
58
90
|
|
|
59
91
|
const handleRequest = async () => {
|
|
60
|
-
const
|
|
61
|
-
const query =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
res.
|
|
69
|
-
res.writeHead(200);
|
|
70
|
-
res.end(JSON.stringify(relativePaths));
|
|
92
|
+
const urlObj = new URL(url, "http://localhost");
|
|
93
|
+
const query = urlObj.searchParams;
|
|
94
|
+
const rawPath = urlObj.pathname.slice(4);
|
|
95
|
+
const relPath = normalizeRelPath(rawPath);
|
|
96
|
+
|
|
97
|
+
if (query.has("watch")) {
|
|
98
|
+
if (method !== "GET") {
|
|
99
|
+
res.writeHead(405);
|
|
100
|
+
res.end("Method not allowed");
|
|
71
101
|
return;
|
|
72
102
|
}
|
|
103
|
+
|
|
104
|
+
const watchPath = normalizeRelPath(query.get("watch") || "");
|
|
105
|
+
const fullWatchPath = resolvePath(ctx.rootDir, watchPath);
|
|
106
|
+
let watchStats;
|
|
107
|
+
try {
|
|
108
|
+
watchStats = await stat(fullWatchPath);
|
|
109
|
+
} catch {
|
|
110
|
+
res.writeHead(404);
|
|
111
|
+
res.end("Not found");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
116
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
117
|
+
res.setHeader("Connection", "keep-alive");
|
|
118
|
+
res.writeHead(200);
|
|
119
|
+
|
|
120
|
+
const watcher = watch(
|
|
121
|
+
fullWatchPath,
|
|
122
|
+
{ recursive: watchStats.isDirectory() },
|
|
123
|
+
async (eventType, filename) => {
|
|
124
|
+
const eventPath = normalizeRelPath(
|
|
125
|
+
[watchPath, filename ? filename.toString() : ""]
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
.join("/"),
|
|
128
|
+
);
|
|
129
|
+
const fullEventPath = resolvePath(ctx.rootDir, eventPath);
|
|
130
|
+
|
|
131
|
+
let type: "create" | "update" | "delete" = "update";
|
|
132
|
+
if (eventType === "rename") {
|
|
133
|
+
try {
|
|
134
|
+
await stat(fullEventPath);
|
|
135
|
+
type = "create";
|
|
136
|
+
} catch {
|
|
137
|
+
type = "delete";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
res.write("event: change\n");
|
|
142
|
+
res.write(
|
|
143
|
+
`data: ${JSON.stringify({
|
|
144
|
+
type,
|
|
145
|
+
path: eventPath,
|
|
146
|
+
mtime: new Date().toISOString(),
|
|
147
|
+
})}\n\n`,
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
req.on("close", () => watcher.close());
|
|
153
|
+
return;
|
|
73
154
|
}
|
|
74
155
|
|
|
75
|
-
|
|
156
|
+
if (method === "HEAD" && !relPath) {
|
|
157
|
+
res.writeHead(200);
|
|
158
|
+
res.end();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (method === "GET" && !relPath && !query.toString()) {
|
|
163
|
+
const files = await listAllFiles(ctx.rootDir, "");
|
|
164
|
+
sendJson(res, 200, files);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!relPath && method !== "GET" && method !== "HEAD") {
|
|
169
|
+
res.writeHead(400);
|
|
170
|
+
res.end("Invalid path");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const targetPath = resolvePath(ctx.rootDir, relPath);
|
|
175
|
+
|
|
176
|
+
if (method === "GET" && query.get("stat") === "true") {
|
|
177
|
+
try {
|
|
178
|
+
const stats = await stat(targetPath);
|
|
179
|
+
sendJson(res, 200, {
|
|
180
|
+
size: stats.size,
|
|
181
|
+
mtime: stats.mtime.toISOString(),
|
|
182
|
+
isFile: stats.isFile(),
|
|
183
|
+
isDirectory: stats.isDirectory(),
|
|
184
|
+
});
|
|
185
|
+
} catch {
|
|
186
|
+
res.writeHead(404);
|
|
187
|
+
res.end("Not found");
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (method === "GET" && query.get("readdir") === "true") {
|
|
193
|
+
try {
|
|
194
|
+
const entries = await readdir(targetPath, { withFileTypes: true });
|
|
195
|
+
const mapped = entries
|
|
196
|
+
.map((entry) => ({
|
|
197
|
+
name: entry.name,
|
|
198
|
+
isDirectory: entry.isDirectory(),
|
|
199
|
+
}))
|
|
200
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
201
|
+
sendJson(res, 200, mapped);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
204
|
+
if (code === "ENOTDIR") {
|
|
205
|
+
res.writeHead(409);
|
|
206
|
+
res.end("Not a directory");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
res.writeHead(404);
|
|
210
|
+
res.end("Not found");
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (method === "POST" && query.get("mkdir") === "true") {
|
|
216
|
+
const recursive = query.get("recursive") === "true";
|
|
217
|
+
try {
|
|
218
|
+
await mkdir(targetPath, { recursive });
|
|
219
|
+
res.writeHead(200);
|
|
220
|
+
res.end("ok");
|
|
221
|
+
} catch {
|
|
222
|
+
res.writeHead(500);
|
|
223
|
+
res.end("Mkdir failed");
|
|
224
|
+
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
76
227
|
|
|
77
228
|
switch (method) {
|
|
78
|
-
case
|
|
229
|
+
case "GET": {
|
|
79
230
|
try {
|
|
80
|
-
const content = await readFile(
|
|
81
|
-
res.setHeader(
|
|
231
|
+
const content = await readFile(targetPath, "utf-8");
|
|
232
|
+
res.setHeader("Content-Type", "text/plain");
|
|
82
233
|
res.writeHead(200);
|
|
83
234
|
res.end(content);
|
|
84
235
|
} catch {
|
|
85
236
|
res.writeHead(404);
|
|
86
|
-
res.end(
|
|
237
|
+
res.end("Not found");
|
|
87
238
|
}
|
|
88
239
|
return;
|
|
89
240
|
}
|
|
90
|
-
case
|
|
91
|
-
let body =
|
|
92
|
-
req.on(
|
|
93
|
-
req.on(
|
|
241
|
+
case "PUT": {
|
|
242
|
+
let body = "";
|
|
243
|
+
req.on("data", (chunk) => (body += chunk));
|
|
244
|
+
req.on("end", async () => {
|
|
94
245
|
try {
|
|
95
|
-
await ensureDir(
|
|
96
|
-
await writeFile(
|
|
246
|
+
await ensureDir(targetPath);
|
|
247
|
+
await writeFile(targetPath, body, "utf-8");
|
|
97
248
|
res.writeHead(200);
|
|
98
|
-
res.end(
|
|
249
|
+
res.end("ok");
|
|
99
250
|
} catch (err) {
|
|
100
|
-
ctx.log(
|
|
251
|
+
ctx.log("VFS PUT error:", err);
|
|
101
252
|
res.writeHead(500);
|
|
102
|
-
res.end(
|
|
253
|
+
res.end("Write failed");
|
|
103
254
|
}
|
|
104
255
|
});
|
|
105
256
|
return;
|
|
106
257
|
}
|
|
107
|
-
case
|
|
258
|
+
case "DELETE": {
|
|
259
|
+
const recursive = query.get("recursive") === "true";
|
|
260
|
+
let stats;
|
|
261
|
+
try {
|
|
262
|
+
stats = await stat(targetPath);
|
|
263
|
+
} catch {
|
|
264
|
+
res.writeHead(404);
|
|
265
|
+
res.end("Not found");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (stats.isDirectory()) {
|
|
270
|
+
if (!recursive) {
|
|
271
|
+
try {
|
|
272
|
+
const entries = await readdir(targetPath);
|
|
273
|
+
if (entries.length > 0) {
|
|
274
|
+
res.writeHead(409);
|
|
275
|
+
res.end("Directory not empty");
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
await rm(targetPath, { recursive: false });
|
|
279
|
+
res.writeHead(200);
|
|
280
|
+
res.end("ok");
|
|
281
|
+
} catch {
|
|
282
|
+
res.writeHead(500);
|
|
283
|
+
res.end("Delete failed");
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
await rm(targetPath, { recursive: true });
|
|
289
|
+
res.writeHead(200);
|
|
290
|
+
res.end("ok");
|
|
291
|
+
} catch {
|
|
292
|
+
res.writeHead(500);
|
|
293
|
+
res.end("Delete failed");
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
108
298
|
try {
|
|
109
|
-
await unlink(
|
|
299
|
+
await unlink(targetPath);
|
|
110
300
|
res.writeHead(200);
|
|
111
|
-
res.end(
|
|
301
|
+
res.end("ok");
|
|
112
302
|
} catch {
|
|
113
303
|
res.writeHead(404);
|
|
114
|
-
res.end(
|
|
304
|
+
res.end("Not found");
|
|
115
305
|
}
|
|
116
306
|
return;
|
|
117
307
|
}
|
|
118
|
-
case
|
|
308
|
+
case "HEAD": {
|
|
119
309
|
try {
|
|
120
|
-
await stat(
|
|
310
|
+
await stat(targetPath);
|
|
121
311
|
res.writeHead(200);
|
|
122
312
|
res.end();
|
|
123
313
|
} catch {
|
|
@@ -128,14 +318,14 @@ export function handleVFS(
|
|
|
128
318
|
}
|
|
129
319
|
default:
|
|
130
320
|
res.writeHead(405);
|
|
131
|
-
res.end(
|
|
321
|
+
res.end("Method not allowed");
|
|
132
322
|
}
|
|
133
323
|
};
|
|
134
324
|
|
|
135
325
|
handleRequest().catch((err) => {
|
|
136
|
-
ctx.log(
|
|
326
|
+
ctx.log("VFS error:", err);
|
|
137
327
|
res.writeHead(500);
|
|
138
|
-
res.end(
|
|
328
|
+
res.end("Internal error");
|
|
139
329
|
});
|
|
140
330
|
|
|
141
331
|
return true;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
entry: {
|
|
5
|
-
index:
|
|
6
|
-
cli:
|
|
7
|
-
|
|
5
|
+
index: "src/index.ts",
|
|
6
|
+
cli: "src/cli.ts",
|
|
7
|
+
"server/index": "src/server/index.ts",
|
|
8
8
|
},
|
|
9
|
-
format: [
|
|
9
|
+
format: ["esm", "cjs"],
|
|
10
10
|
dts: true,
|
|
11
11
|
clean: true,
|
|
12
12
|
sourcemap: true,
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"description": "UTCP backend integration for Patchwork",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
14
15
|
"scripts": {
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
"extends": "../../tsconfig.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"outDir": "./dist",
|
|
5
|
-
"rootDir": "
|
|
5
|
+
"rootDir": "..",
|
|
6
|
+
"baseUrl": ".",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@aprovan/patchwork": ["../patchwork/src/index.ts"]
|
|
9
|
+
}
|
|
6
10
|
},
|
|
7
|
-
"include": ["src/**/*"]
|
|
11
|
+
"include": ["src/**/*", "../patchwork/src/**/*"]
|
|
8
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
1
|
+
import { defineConfig } from "tsup";
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
|
-
entry: [
|
|
5
|
-
format: [
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
format: ["esm", "cjs"],
|
|
6
6
|
dts: true,
|
|
7
7
|
clean: true,
|
|
8
8
|
sourcemap: true,
|
|
9
|
-
target:
|
|
10
|
-
outDir:
|
|
11
|
-
external: [
|
|
9
|
+
target: "node20",
|
|
10
|
+
outDir: "dist",
|
|
11
|
+
external: ["@aprovan/patchwork"],
|
|
12
12
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Patchwork VS Code Extension
|
|
2
|
+
|
|
3
|
+
Patchwork Viewer is a VS Code extension for opening Patchwork projects, previewing widgets, and syncing edits back to disk.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `Patchwork: Open Project` (`patchwork.openProject`)
|
|
8
|
+
- `Patchwork: Export Project` (`patchwork.exportProject`)
|
|
9
|
+
- `Patchwork: Show Preview` (`patchwork.showPreview`)
|
|
10
|
+
- `Patchwork: Edit with AI` (`patchwork.editWithAI`)
|
|
11
|
+
- `Patchwork: Show Edit History` (`patchwork.showEditHistory`)
|
|
12
|
+
- `Patchwork: Test Copilot Proxy` (`patchwork.testConnection`)
|
|
13
|
+
|
|
14
|
+
## Settings
|
|
15
|
+
|
|
16
|
+
- `patchwork.copilotProxyUrl`: Base URL for the Copilot proxy server.
|
|
17
|
+
- `patchwork.mcpServers`: MCP server configs (name, command, args).
|
|
18
|
+
- `patchwork.utcpConfig`: UTCP configuration for service registration.
|
|
19
|
+
|
|
20
|
+
## Keybindings
|
|
21
|
+
|
|
22
|
+
- `Cmd+Shift+Alt+P`: Show Patchwork Preview
|
|
23
|
+
- `Cmd+Shift+Alt+E`: Edit with AI (selected code)
|
|
24
|
+
- `Cmd+Shift+Alt+H`: Show Edit History
|
|
25
|
+
|
|
26
|
+
## Development
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm --filter @aprovan/patchwork-vscode build
|
|
30
|
+
pnpm --filter @aprovan/patchwork-vscode dev
|
|
31
|
+
```
|
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
2
|
+
width="100%" viewBox="0 0 336 336" enable-background="new 0 0 336 336" xml:space="preserve">
|
|
3
|
+
<path fill="#000000" opacity="1.000000" stroke="none"
|
|
4
|
+
d="
|
|
5
|
+
M152.000000,337.000000
|
|
6
|
+
C101.333344,337.000000 51.166683,337.000000 1.000020,337.000000
|
|
7
|
+
C1.000013,225.000031 1.000013,113.000061 1.000007,1.000065
|
|
8
|
+
C112.999947,1.000044 224.999893,1.000044 336.999878,1.000022
|
|
9
|
+
C336.999939,112.999931 336.999939,224.999863 336.999969,336.999878
|
|
10
|
+
C275.500000,337.000000 214.000000,337.000000 152.000000,337.000000
|
|
11
|
+
M93.196281,166.261337
|
|
12
|
+
C100.770424,174.654449 109.507950,180.155777 121.377304,181.074783
|
|
13
|
+
C144.430206,182.859695 164.816849,162.887146 162.995041,138.852432
|
|
14
|
+
C161.425385,118.144455 141.941452,98.219574 115.832298,103.695320
|
|
15
|
+
C86.953171,109.752014 75.672241,143.447800 93.196281,166.261337
|
|
16
|
+
M213.624863,163.442642
|
|
17
|
+
C222.153275,163.231750 231.186264,157.583130 233.468781,150.947678
|
|
18
|
+
C237.582123,138.989868 233.959045,124.391647 217.375061,121.223755
|
|
19
|
+
C207.997711,119.432487 195.747040,126.108696 193.148285,134.999390
|
|
20
|
+
C189.314651,148.114777 198.639481,161.612564 213.624863,163.442642
|
|
21
|
+
M156.886856,213.755325
|
|
22
|
+
C153.925049,213.940735 150.963226,214.126129 147.855179,214.320694
|
|
23
|
+
C147.855179,220.513016 147.855179,226.567719 147.855179,232.605728
|
|
24
|
+
C179.550980,233.856476 210.824387,208.428116 216.870972,188.608521
|
|
25
|
+
C212.298660,187.360626 207.653397,186.333801 203.190536,184.782257
|
|
26
|
+
C200.610748,183.885361 199.528000,184.444382 198.027328,186.681854
|
|
27
|
+
C188.309372,201.171295 175.006470,210.375549 156.886856,213.755325
|
|
28
|
+
z"/>
|
|
29
|
+
<path fill="#F5F7FD" opacity="1.000000" stroke="none"
|
|
30
|
+
d="
|
|
31
|
+
M92.949310,166.000244
|
|
32
|
+
C75.672241,143.447800 86.953171,109.752014 115.832298,103.695320
|
|
33
|
+
C141.941452,98.219574 161.425385,118.144455 162.995041,138.852432
|
|
34
|
+
C164.816849,162.887146 144.430206,182.859695 121.377304,181.074783
|
|
35
|
+
C109.507950,180.155777 100.770424,174.654449 92.949310,166.000244
|
|
36
|
+
M121.725945,121.729195
|
|
37
|
+
C119.649338,122.159027 117.372192,122.179886 115.528221,123.084129
|
|
38
|
+
C102.119904,129.659180 97.660942,148.377686 112.702919,159.308014
|
|
39
|
+
C119.427475,164.194412 130.724854,163.348480 137.073151,157.873138
|
|
40
|
+
C145.445633,150.651962 146.667221,139.959183 142.131287,132.396027
|
|
41
|
+
C137.722137,125.044228 131.650635,120.708099 121.725945,121.729195
|
|
42
|
+
z"/>
|
|
43
|
+
<path fill="#F8F9FD" opacity="1.000000" stroke="none"
|
|
44
|
+
d="
|
|
45
|
+
M213.210052,163.440598
|
|
46
|
+
C198.639481,161.612564 189.314651,148.114777 193.148285,134.999390
|
|
47
|
+
C195.747040,126.108696 207.997711,119.432487 217.375061,121.223755
|
|
48
|
+
C233.959045,124.391647 237.582123,138.989868 233.468781,150.947678
|
|
49
|
+
C231.186264,157.583130 222.153275,163.231750 213.210052,163.440598
|
|
50
|
+
z"/>
|
|
51
|
+
<path fill="#F5F7FD" opacity="1.000000" stroke="none"
|
|
52
|
+
d="
|
|
53
|
+
M157.244431,213.504639
|
|
54
|
+
C175.006470,210.375549 188.309372,201.171295 198.027328,186.681854
|
|
55
|
+
C199.528000,184.444382 200.610748,183.885361 203.190536,184.782257
|
|
56
|
+
C207.653397,186.333801 212.298660,187.360626 216.870972,188.608521
|
|
57
|
+
C210.824387,208.428116 179.550980,233.856476 147.855179,232.605728
|
|
58
|
+
C147.855179,226.567719 147.855179,220.513016 147.855179,214.320694
|
|
59
|
+
C150.963226,214.126129 153.925049,213.940735 157.244431,213.504639
|
|
60
|
+
z"/>
|
|
61
|
+
<path fill="#000000" opacity="1.000000" stroke="none"
|
|
62
|
+
d="
|
|
63
|
+
M122.181000,121.777527
|
|
64
|
+
C131.650635,120.708099 137.722137,125.044228 142.131287,132.396027
|
|
65
|
+
C146.667221,139.959183 145.445633,150.651962 137.073151,157.873138
|
|
66
|
+
C130.724854,163.348480 119.427475,164.194412 112.702919,159.308014
|
|
67
|
+
C97.660942,148.377686 102.119904,129.659180 115.528221,123.084129
|
|
68
|
+
C117.372192,122.179886 119.649338,122.159027 122.181000,121.777527
|
|
69
|
+
z"/>
|
|
70
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
2
|
+
width="100%" viewBox="0 0 336 336" enable-background="new 0 0 336 336" xml:space="preserve">
|
|
3
|
+
<path fill="#3059D4" opacity="1.000000" stroke="none"
|
|
4
|
+
d="
|
|
5
|
+
M152.000000,337.000000
|
|
6
|
+
C101.333344,337.000000 51.166683,337.000000 1.000020,337.000000
|
|
7
|
+
C1.000013,225.000031 1.000013,113.000061 1.000007,1.000065
|
|
8
|
+
C112.999947,1.000044 224.999893,1.000044 336.999878,1.000022
|
|
9
|
+
C336.999939,112.999931 336.999939,224.999863 336.999969,336.999878
|
|
10
|
+
C275.500000,337.000000 214.000000,337.000000 152.000000,337.000000
|
|
11
|
+
M93.980904,117.485725
|
|
12
|
+
C80.263412,134.303772 82.603081,158.166016 99.314697,171.884918
|
|
13
|
+
C116.440109,185.943497 140.148102,183.292328 154.846756,165.675034
|
|
14
|
+
C167.992661,149.918793 164.827667,125.333229 147.840790,111.252892
|
|
15
|
+
C132.319046,98.386986 107.835983,100.991982 93.980904,117.485725
|
|
16
|
+
M216.172760,162.929596
|
|
17
|
+
C225.867188,161.315903 232.070618,155.787811 234.469101,146.253860
|
|
18
|
+
C236.407928,138.547150 232.923737,129.540771 225.935318,124.815331
|
|
19
|
+
C218.682510,119.911118 210.934479,119.673912 203.324478,124.015686
|
|
20
|
+
C195.283951,128.603104 191.404617,138.386856 193.794647,147.201721
|
|
21
|
+
C196.453827,157.009277 204.468170,162.884003 216.172760,162.929596
|
|
22
|
+
M170.393631,210.076569
|
|
23
|
+
C163.046021,211.875854 155.698410,213.675140 148.243820,215.500626
|
|
24
|
+
C148.243820,220.751282 148.243820,226.350037 148.243820,231.927643
|
|
25
|
+
C175.580719,233.511520 207.048187,212.813141 216.859344,188.926392
|
|
26
|
+
C212.378143,187.653473 207.904816,186.470901 203.492065,185.094116
|
|
27
|
+
C201.091522,184.345123 199.652283,184.835846 198.223221,187.081223
|
|
28
|
+
C191.601929,197.484787 182.403381,204.927856 170.393631,210.076569
|
|
29
|
+
z"/>
|
|
30
|
+
<path fill="#FDFDFE" opacity="1.000000" stroke="none"
|
|
31
|
+
d="
|
|
32
|
+
M94.207321,117.210541
|
|
33
|
+
C107.835983,100.991982 132.319046,98.386986 147.840790,111.252892
|
|
34
|
+
C164.827667,125.333229 167.992661,149.918793 154.846756,165.675034
|
|
35
|
+
C140.148102,183.292328 116.440109,185.943497 99.314697,171.884918
|
|
36
|
+
C82.603081,158.166016 80.263412,134.303772 94.207321,117.210541
|
|
37
|
+
M138.505569,157.023026
|
|
38
|
+
C144.863892,150.363983 146.846893,142.669434 143.255798,134.082870
|
|
39
|
+
C139.871994,125.991959 133.549652,121.403931 124.724533,121.127022
|
|
40
|
+
C115.942032,120.851448 109.399979,124.904480 105.311424,132.739258
|
|
41
|
+
C100.931969,141.131470 103.009468,151.494095 110.277122,157.689835
|
|
42
|
+
C118.796677,164.952850 128.281952,164.887268 138.505569,157.023026
|
|
43
|
+
z"/>
|
|
44
|
+
<path fill="#FCFDFE" opacity="1.000000" stroke="none"
|
|
45
|
+
d="
|
|
46
|
+
M215.737030,162.948135
|
|
47
|
+
C204.468170,162.884003 196.453827,157.009277 193.794647,147.201721
|
|
48
|
+
C191.404617,138.386856 195.283951,128.603104 203.324478,124.015686
|
|
49
|
+
C210.934479,119.673912 218.682510,119.911118 225.935318,124.815331
|
|
50
|
+
C232.923737,129.540771 236.407928,138.547150 234.469101,146.253860
|
|
51
|
+
C232.070618,155.787811 225.867188,161.315903 215.737030,162.948135
|
|
52
|
+
z"/>
|
|
53
|
+
<path fill="#FCFDFE" opacity="1.000000" stroke="none"
|
|
54
|
+
d="
|
|
55
|
+
M170.767456,209.972427
|
|
56
|
+
C182.403381,204.927856 191.601929,197.484787 198.223221,187.081223
|
|
57
|
+
C199.652283,184.835846 201.091522,184.345123 203.492065,185.094116
|
|
58
|
+
C207.904816,186.470901 212.378143,187.653473 216.859344,188.926392
|
|
59
|
+
C207.048187,212.813141 175.580719,233.511520 148.243820,231.927643
|
|
60
|
+
C148.243820,226.350037 148.243820,220.751282 148.243820,215.500626
|
|
61
|
+
C155.698410,213.675140 163.046021,211.875854 170.767456,209.972427
|
|
62
|
+
z"/>
|
|
63
|
+
<path fill="#335BD5" opacity="1.000000" stroke="none"
|
|
64
|
+
d="
|
|
65
|
+
M138.231918,157.260757
|
|
66
|
+
C128.281952,164.887268 118.796677,164.952850 110.277122,157.689835
|
|
67
|
+
C103.009468,151.494095 100.931969,141.131470 105.311424,132.739258
|
|
68
|
+
C109.399979,124.904480 115.942032,120.851448 124.724533,121.127022
|
|
69
|
+
C133.549652,121.403931 139.871994,125.991959 143.255798,134.082870
|
|
70
|
+
C146.846893,142.669434 144.863892,150.363983 138.231918,157.260757
|
|
71
|
+
z"/>
|
|
72
|
+
</svg>
|
|
@@ -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
|