@aprovan/patchwork-vscode 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/.turbo/turbo-build.log +17 -0
- package/LICENSE +373 -0
- package/README.md +31 -0
- package/dist/extension.d.ts +6 -0
- package/dist/extension.js +1405 -0
- package/dist/extension.js.map +1 -0
- package/media/outline.png +0 -0
- package/media/outline.svg +70 -0
- package/media/patchwork.png +0 -0
- package/media/patchwork.svg +72 -0
- package/package.json +144 -0
- package/src/extension.ts +612 -0
- package/src/providers/PatchworkFileSystemProvider.ts +205 -0
- package/src/providers/PatchworkTreeProvider.ts +177 -0
- package/src/providers/PreviewPanelProvider.ts +536 -0
- package/src/services/EditService.ts +24 -0
- package/src/services/EmbeddedStitchery.ts +82 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +11 -0
|
@@ -0,0 +1,1405 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/extension.ts
|
|
31
|
+
var extension_exports = {};
|
|
32
|
+
__export(extension_exports, {
|
|
33
|
+
activate: () => activate,
|
|
34
|
+
deactivate: () => deactivate
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(extension_exports);
|
|
37
|
+
var path2 = __toESM(require("path"));
|
|
38
|
+
var vscode4 = __toESM(require("vscode"));
|
|
39
|
+
var import_patchwork_compiler = require("@aprovan/patchwork-compiler");
|
|
40
|
+
|
|
41
|
+
// src/providers/PatchworkFileSystemProvider.ts
|
|
42
|
+
var vscode = __toESM(require("vscode"));
|
|
43
|
+
var PatchworkFileSystemProvider = class {
|
|
44
|
+
onDidChangeFileEmitter = new vscode.EventEmitter();
|
|
45
|
+
onDidChangeFile = this.onDidChangeFileEmitter.event;
|
|
46
|
+
projects = /* @__PURE__ */ new Map();
|
|
47
|
+
setProject(id, project) {
|
|
48
|
+
this.projects.set(id, project);
|
|
49
|
+
this.onDidChangeFileEmitter.fire([]);
|
|
50
|
+
}
|
|
51
|
+
clearProjects() {
|
|
52
|
+
this.projects.clear();
|
|
53
|
+
this.onDidChangeFileEmitter.fire([]);
|
|
54
|
+
}
|
|
55
|
+
readFile(uri) {
|
|
56
|
+
const { projectId, path: path3 } = this.parseUri(uri);
|
|
57
|
+
const project = this.getProject(projectId);
|
|
58
|
+
const file = project.files.get(path3);
|
|
59
|
+
if (!file) throw vscode.FileSystemError.FileNotFound(uri);
|
|
60
|
+
return this.encodeFileContent(file);
|
|
61
|
+
}
|
|
62
|
+
writeFile(uri, content, options) {
|
|
63
|
+
const { projectId, path: path3 } = this.parseUri(uri);
|
|
64
|
+
const project = this.getProject(projectId);
|
|
65
|
+
const exists = project.files.has(path3);
|
|
66
|
+
if (!exists && !options.create) {
|
|
67
|
+
throw vscode.FileSystemError.FileNotFound(uri);
|
|
68
|
+
}
|
|
69
|
+
if (exists && !options.overwrite) {
|
|
70
|
+
throw vscode.FileSystemError.FileExists(uri);
|
|
71
|
+
}
|
|
72
|
+
const file = this.decodeFileContent(path3, content);
|
|
73
|
+
project.files.set(path3, file);
|
|
74
|
+
this.onDidChangeFileEmitter.fire([
|
|
75
|
+
{
|
|
76
|
+
type: exists ? vscode.FileChangeType.Changed : vscode.FileChangeType.Created,
|
|
77
|
+
uri
|
|
78
|
+
}
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
stat(uri) {
|
|
82
|
+
const { projectId, path: path3 } = this.parseUri(uri);
|
|
83
|
+
const project = this.getProject(projectId);
|
|
84
|
+
if (!path3) {
|
|
85
|
+
return {
|
|
86
|
+
type: vscode.FileType.Directory,
|
|
87
|
+
ctime: 0,
|
|
88
|
+
mtime: 0,
|
|
89
|
+
size: 0
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const file = project.files.get(path3);
|
|
93
|
+
if (file) {
|
|
94
|
+
return {
|
|
95
|
+
type: vscode.FileType.File,
|
|
96
|
+
ctime: 0,
|
|
97
|
+
mtime: 0,
|
|
98
|
+
size: this.encodeFileContent(file).byteLength
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (this.hasDirectory(project, path3)) {
|
|
102
|
+
return {
|
|
103
|
+
type: vscode.FileType.Directory,
|
|
104
|
+
ctime: 0,
|
|
105
|
+
mtime: 0,
|
|
106
|
+
size: 0
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
throw vscode.FileSystemError.FileNotFound(uri);
|
|
110
|
+
}
|
|
111
|
+
readDirectory(uri) {
|
|
112
|
+
const { projectId, path: path3 } = this.parseUri(uri);
|
|
113
|
+
const project = this.getProject(projectId);
|
|
114
|
+
return this.listDirectoryEntries(project, path3);
|
|
115
|
+
}
|
|
116
|
+
createDirectory() {
|
|
117
|
+
throw vscode.FileSystemError.NoPermissions(
|
|
118
|
+
"Patchwork file system is read/write via file edits only."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
delete() {
|
|
122
|
+
throw vscode.FileSystemError.NoPermissions(
|
|
123
|
+
"Patchwork file deletion is not implemented yet."
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
rename() {
|
|
127
|
+
throw vscode.FileSystemError.NoPermissions(
|
|
128
|
+
"Patchwork file rename is not implemented yet."
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
watch() {
|
|
132
|
+
return new vscode.Disposable(() => void 0);
|
|
133
|
+
}
|
|
134
|
+
parseUri(uri) {
|
|
135
|
+
if (uri.scheme !== "patchwork") {
|
|
136
|
+
throw vscode.FileSystemError.Unavailable(
|
|
137
|
+
"Unsupported URI scheme for Patchwork provider."
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const projectId = uri.authority;
|
|
141
|
+
if (!projectId) {
|
|
142
|
+
throw vscode.FileSystemError.FileNotFound(uri);
|
|
143
|
+
}
|
|
144
|
+
const path3 = uri.path.replace(/^\/+/, "");
|
|
145
|
+
return { projectId, path: path3 };
|
|
146
|
+
}
|
|
147
|
+
getProject(projectId) {
|
|
148
|
+
const project = this.projects.get(projectId);
|
|
149
|
+
if (!project) {
|
|
150
|
+
throw vscode.FileSystemError.FileNotFound(
|
|
151
|
+
vscode.Uri.parse(`patchwork://${projectId}`)
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return project;
|
|
155
|
+
}
|
|
156
|
+
encodeFileContent(file) {
|
|
157
|
+
if (file.encoding === "base64") {
|
|
158
|
+
return Buffer.from(file.content, "base64");
|
|
159
|
+
}
|
|
160
|
+
return Buffer.from(file.content, "utf8");
|
|
161
|
+
}
|
|
162
|
+
decodeFileContent(path3, content) {
|
|
163
|
+
const hasNull = content.some((byte) => byte === 0);
|
|
164
|
+
if (hasNull) {
|
|
165
|
+
return {
|
|
166
|
+
path: path3,
|
|
167
|
+
content: Buffer.from(content).toString("base64"),
|
|
168
|
+
encoding: "base64"
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
path: path3,
|
|
173
|
+
content: Buffer.from(content).toString("utf8"),
|
|
174
|
+
encoding: "utf8"
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
hasDirectory(project, path3) {
|
|
178
|
+
const prefix = path3.endsWith("/") ? path3 : `${path3}/`;
|
|
179
|
+
for (const filePath of project.files.keys()) {
|
|
180
|
+
if (filePath.startsWith(prefix)) return true;
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
listDirectoryEntries(project, path3) {
|
|
185
|
+
const prefix = path3 ? `${path3.replace(/\/+$/, "")}/` : "";
|
|
186
|
+
const entries = /* @__PURE__ */ new Map();
|
|
187
|
+
for (const filePath of project.files.keys()) {
|
|
188
|
+
if (!filePath.startsWith(prefix)) continue;
|
|
189
|
+
const remainder = filePath.slice(prefix.length);
|
|
190
|
+
if (!remainder) continue;
|
|
191
|
+
const [segment, ...rest] = remainder.split("/");
|
|
192
|
+
if (!segment) continue;
|
|
193
|
+
if (rest.length === 0) {
|
|
194
|
+
entries.set(segment, vscode.FileType.File);
|
|
195
|
+
} else if (!entries.has(segment)) {
|
|
196
|
+
entries.set(segment, vscode.FileType.Directory);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return Array.from(entries.entries()).sort(
|
|
200
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/providers/PatchworkTreeProvider.ts
|
|
206
|
+
var vscode2 = __toESM(require("vscode"));
|
|
207
|
+
var PatchworkTreeItem = class _PatchworkTreeItem extends vscode2.TreeItem {
|
|
208
|
+
kind;
|
|
209
|
+
projectId;
|
|
210
|
+
path;
|
|
211
|
+
children;
|
|
212
|
+
constructor(node, collapsibleState) {
|
|
213
|
+
super(node.label, collapsibleState);
|
|
214
|
+
this.kind = node.kind;
|
|
215
|
+
this.projectId = node.projectId;
|
|
216
|
+
this.path = node.path;
|
|
217
|
+
this.children = node.children?.map(
|
|
218
|
+
(child) => new _PatchworkTreeItem(
|
|
219
|
+
child,
|
|
220
|
+
child.children && child.children.length > 0 ? vscode2.TreeItemCollapsibleState.Collapsed : vscode2.TreeItemCollapsibleState.None
|
|
221
|
+
)
|
|
222
|
+
);
|
|
223
|
+
if (this.kind === "folder" || this.kind === "project") {
|
|
224
|
+
this.iconPath = vscode2.ThemeIcon.Folder;
|
|
225
|
+
} else if (this.kind === "file") {
|
|
226
|
+
this.iconPath = vscode2.ThemeIcon.File;
|
|
227
|
+
if (this.projectId && this.path) {
|
|
228
|
+
this.command = {
|
|
229
|
+
command: "patchwork.openFile",
|
|
230
|
+
title: "Open Patchwork File",
|
|
231
|
+
arguments: [this.projectId, this.path]
|
|
232
|
+
};
|
|
233
|
+
this.tooltip = this.path;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
var PatchworkTreeProvider = class {
|
|
239
|
+
onDidChangeTreeDataEmitter = new vscode2.EventEmitter();
|
|
240
|
+
onDidChangeTreeData = this.onDidChangeTreeDataEmitter.event;
|
|
241
|
+
projects = /* @__PURE__ */ new Map();
|
|
242
|
+
setProject(id, project) {
|
|
243
|
+
this.projects.set(id, project);
|
|
244
|
+
this.onDidChangeTreeDataEmitter.fire(void 0);
|
|
245
|
+
}
|
|
246
|
+
clearProjects() {
|
|
247
|
+
this.projects.clear();
|
|
248
|
+
this.onDidChangeTreeDataEmitter.fire(void 0);
|
|
249
|
+
}
|
|
250
|
+
getTreeItem(element) {
|
|
251
|
+
return element;
|
|
252
|
+
}
|
|
253
|
+
getChildren(element) {
|
|
254
|
+
if (!element) {
|
|
255
|
+
if (this.projects.size === 0) {
|
|
256
|
+
return Promise.resolve([
|
|
257
|
+
new PatchworkTreeItem(
|
|
258
|
+
{ label: "No Patchwork projects", kind: "empty" },
|
|
259
|
+
vscode2.TreeItemCollapsibleState.None
|
|
260
|
+
)
|
|
261
|
+
]);
|
|
262
|
+
}
|
|
263
|
+
const projectItems = Array.from(this.projects.values()).sort((a, b) => a.id.localeCompare(b.id)).map(
|
|
264
|
+
(project) => new PatchworkTreeItem(
|
|
265
|
+
{
|
|
266
|
+
label: project.id,
|
|
267
|
+
kind: "project",
|
|
268
|
+
projectId: project.id,
|
|
269
|
+
children: this.buildProjectNodes(project)
|
|
270
|
+
},
|
|
271
|
+
vscode2.TreeItemCollapsibleState.Collapsed
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
return Promise.resolve(projectItems);
|
|
275
|
+
}
|
|
276
|
+
if (element.children) {
|
|
277
|
+
return Promise.resolve(element.children);
|
|
278
|
+
}
|
|
279
|
+
return Promise.resolve([]);
|
|
280
|
+
}
|
|
281
|
+
buildProjectNodes(project) {
|
|
282
|
+
const root = {
|
|
283
|
+
label: project.id,
|
|
284
|
+
kind: "project",
|
|
285
|
+
projectId: project.id,
|
|
286
|
+
path: "",
|
|
287
|
+
children: /* @__PURE__ */ new Map()
|
|
288
|
+
};
|
|
289
|
+
for (const [path3] of project.files) {
|
|
290
|
+
const parts = path3.split("/").filter(Boolean);
|
|
291
|
+
let current = root;
|
|
292
|
+
let currentPath = "";
|
|
293
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
294
|
+
const part = parts[index];
|
|
295
|
+
const isFile = index === parts.length - 1;
|
|
296
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
297
|
+
if (!current.children.has(part)) {
|
|
298
|
+
current.children.set(part, {
|
|
299
|
+
label: part,
|
|
300
|
+
kind: isFile ? "file" : "folder",
|
|
301
|
+
projectId: project.id,
|
|
302
|
+
path: currentPath,
|
|
303
|
+
children: /* @__PURE__ */ new Map()
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const next = current.children.get(part);
|
|
307
|
+
if (next) current = next;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const collectChildren = (nodeMap) => {
|
|
311
|
+
const nodes = Array.from(nodeMap.values());
|
|
312
|
+
return nodes.sort((a, b) => {
|
|
313
|
+
if (a.kind !== b.kind) return a.kind === "folder" ? -1 : 1;
|
|
314
|
+
return a.label.localeCompare(b.label);
|
|
315
|
+
}).map((node) => ({
|
|
316
|
+
label: node.label,
|
|
317
|
+
kind: node.kind,
|
|
318
|
+
projectId: node.projectId,
|
|
319
|
+
path: node.path,
|
|
320
|
+
children: node.kind === "file" ? void 0 : collectChildren(node.children)
|
|
321
|
+
}));
|
|
322
|
+
};
|
|
323
|
+
return collectChildren(root.children);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/providers/PreviewPanelProvider.ts
|
|
328
|
+
var path = __toESM(require("path"));
|
|
329
|
+
var vscode3 = __toESM(require("vscode"));
|
|
330
|
+
var PreviewPanelProvider = class {
|
|
331
|
+
constructor(context, handlers = {}) {
|
|
332
|
+
this.context = context;
|
|
333
|
+
this.handlers = handlers;
|
|
334
|
+
}
|
|
335
|
+
panel;
|
|
336
|
+
activeDocument;
|
|
337
|
+
showPreview(document) {
|
|
338
|
+
if (!this.isPreviewableDocument(document)) {
|
|
339
|
+
vscode3.window.showInformationMessage(
|
|
340
|
+
"Patchwork: preview supports .tsx/.jsx files only."
|
|
341
|
+
);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.activeDocument = document;
|
|
345
|
+
if (this.panel) {
|
|
346
|
+
this.panel.reveal(vscode3.ViewColumn.Beside);
|
|
347
|
+
this.postActiveDocument();
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
this.panel = vscode3.window.createWebviewPanel(
|
|
351
|
+
"patchworkPreview",
|
|
352
|
+
"Patchwork Preview",
|
|
353
|
+
vscode3.ViewColumn.Beside,
|
|
354
|
+
{
|
|
355
|
+
enableScripts: true,
|
|
356
|
+
retainContextWhenHidden: true
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
this.panel.webview.html = this.getWebviewHtml(this.panel.webview);
|
|
360
|
+
this.panel.webview.onDidReceiveMessage(
|
|
361
|
+
(message) => {
|
|
362
|
+
if (message?.type === "ready") {
|
|
363
|
+
this.postActiveDocument();
|
|
364
|
+
this.handlers.onWebviewReady?.();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (message?.type === "compileError") {
|
|
368
|
+
this.handlers.onCompileError?.(message.payload, this.activeDocument);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (message?.type === "compileSuccess") {
|
|
372
|
+
this.handlers.onCompileSuccess?.(this.activeDocument);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (message?.type === "editRequest") {
|
|
376
|
+
this.handlers.onEditRequest?.(message.payload, this.activeDocument);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (message?.type === "serviceCall") {
|
|
380
|
+
this.handlers.onServiceCall?.(message.payload);
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
void 0,
|
|
384
|
+
this.context.subscriptions
|
|
385
|
+
);
|
|
386
|
+
this.panel.onDidDispose(() => {
|
|
387
|
+
this.panel = void 0;
|
|
388
|
+
this.activeDocument = void 0;
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
updateDocument(document) {
|
|
392
|
+
if (!this.panel) return;
|
|
393
|
+
if (!this.isPreviewableDocument(document)) return;
|
|
394
|
+
this.activeDocument = document;
|
|
395
|
+
this.postActiveDocument();
|
|
396
|
+
}
|
|
397
|
+
postActiveDocument() {
|
|
398
|
+
if (!this.panel || !this.activeDocument) return;
|
|
399
|
+
const payload = {
|
|
400
|
+
uri: this.activeDocument.uri.toString(),
|
|
401
|
+
languageId: this.activeDocument.languageId,
|
|
402
|
+
text: this.activeDocument.getText()
|
|
403
|
+
};
|
|
404
|
+
this.panel.webview.postMessage({ type: "updateFile", payload });
|
|
405
|
+
}
|
|
406
|
+
postMessage(message) {
|
|
407
|
+
if (!this.panel) return;
|
|
408
|
+
this.panel.webview.postMessage(message);
|
|
409
|
+
}
|
|
410
|
+
getWebviewHtml(webview) {
|
|
411
|
+
const nonce = this.getNonce();
|
|
412
|
+
const csp = `default-src 'none'; img-src ${webview.cspSource} https:; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}' https:; connect-src https:;`;
|
|
413
|
+
const compilerUri = webview.asWebviewUri(
|
|
414
|
+
vscode3.Uri.joinPath(
|
|
415
|
+
this.context.extensionUri,
|
|
416
|
+
"node_modules",
|
|
417
|
+
"@aprovan",
|
|
418
|
+
"patchwork-compiler",
|
|
419
|
+
"dist",
|
|
420
|
+
"index.js"
|
|
421
|
+
)
|
|
422
|
+
);
|
|
423
|
+
return `<!DOCTYPE html>
|
|
424
|
+
<html lang="en">
|
|
425
|
+
<head>
|
|
426
|
+
<meta charset="UTF-8" />
|
|
427
|
+
<meta http-equiv="Content-Security-Policy" content="${csp}" />
|
|
428
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
429
|
+
<title>Patchwork Preview</title>
|
|
430
|
+
<style>
|
|
431
|
+
body {
|
|
432
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
433
|
+
margin: 0;
|
|
434
|
+
padding: 16px;
|
|
435
|
+
color: var(--vscode-editor-foreground);
|
|
436
|
+
background: var(--vscode-editor-background);
|
|
437
|
+
}
|
|
438
|
+
.header {
|
|
439
|
+
font-size: 12px;
|
|
440
|
+
letter-spacing: 0.08em;
|
|
441
|
+
text-transform: uppercase;
|
|
442
|
+
opacity: 0.7;
|
|
443
|
+
margin-bottom: 12px;
|
|
444
|
+
}
|
|
445
|
+
pre {
|
|
446
|
+
white-space: pre-wrap;
|
|
447
|
+
word-break: break-word;
|
|
448
|
+
border-radius: 8px;
|
|
449
|
+
padding: 12px;
|
|
450
|
+
background: var(--vscode-editorWidget-background);
|
|
451
|
+
}
|
|
452
|
+
#preview {
|
|
453
|
+
min-height: 120px;
|
|
454
|
+
border-radius: 8px;
|
|
455
|
+
padding: 12px;
|
|
456
|
+
background: var(--vscode-editorWidget-background);
|
|
457
|
+
margin-bottom: 12px;
|
|
458
|
+
}
|
|
459
|
+
.error {
|
|
460
|
+
background: var(--vscode-inputValidation-errorBackground);
|
|
461
|
+
color: var(--vscode-inputValidation-errorForeground);
|
|
462
|
+
border: 1px solid var(--vscode-inputValidation-errorBorder);
|
|
463
|
+
border-radius: 6px;
|
|
464
|
+
padding: 8px 10px;
|
|
465
|
+
margin-bottom: 12px;
|
|
466
|
+
font-size: 12px;
|
|
467
|
+
white-space: pre-wrap;
|
|
468
|
+
}
|
|
469
|
+
.edit-bar {
|
|
470
|
+
display: flex;
|
|
471
|
+
gap: 8px;
|
|
472
|
+
align-items: center;
|
|
473
|
+
margin-bottom: 12px;
|
|
474
|
+
}
|
|
475
|
+
.edit-input {
|
|
476
|
+
flex: 1;
|
|
477
|
+
background: var(--vscode-input-background);
|
|
478
|
+
color: var(--vscode-input-foreground);
|
|
479
|
+
border: 1px solid var(--vscode-input-border);
|
|
480
|
+
border-radius: 6px;
|
|
481
|
+
padding: 6px 8px;
|
|
482
|
+
}
|
|
483
|
+
.edit-submit {
|
|
484
|
+
background: var(--vscode-button-background);
|
|
485
|
+
color: var(--vscode-button-foreground);
|
|
486
|
+
border: none;
|
|
487
|
+
border-radius: 6px;
|
|
488
|
+
padding: 6px 10px;
|
|
489
|
+
cursor: pointer;
|
|
490
|
+
}
|
|
491
|
+
.edit-submit:hover {
|
|
492
|
+
background: var(--vscode-button-hoverBackground);
|
|
493
|
+
}
|
|
494
|
+
.edit-secondary {
|
|
495
|
+
background: var(--vscode-button-secondaryBackground);
|
|
496
|
+
color: var(--vscode-button-secondaryForeground);
|
|
497
|
+
border: none;
|
|
498
|
+
border-radius: 6px;
|
|
499
|
+
padding: 6px 10px;
|
|
500
|
+
cursor: pointer;
|
|
501
|
+
}
|
|
502
|
+
.edit-secondary:hover {
|
|
503
|
+
background: var(--vscode-button-secondaryHoverBackground);
|
|
504
|
+
}
|
|
505
|
+
.edit-status {
|
|
506
|
+
font-size: 11px;
|
|
507
|
+
opacity: 0.7;
|
|
508
|
+
}
|
|
509
|
+
.history-panel {
|
|
510
|
+
border-radius: 8px;
|
|
511
|
+
border: 1px solid var(--vscode-panel-border);
|
|
512
|
+
background: var(--vscode-sideBar-background);
|
|
513
|
+
margin-bottom: 12px;
|
|
514
|
+
}
|
|
515
|
+
.history-header {
|
|
516
|
+
padding: 8px 10px;
|
|
517
|
+
font-size: 12px;
|
|
518
|
+
text-transform: uppercase;
|
|
519
|
+
letter-spacing: 0.08em;
|
|
520
|
+
opacity: 0.7;
|
|
521
|
+
border-bottom: 1px solid var(--vscode-panel-border);
|
|
522
|
+
}
|
|
523
|
+
.history-list {
|
|
524
|
+
max-height: 200px;
|
|
525
|
+
overflow-y: auto;
|
|
526
|
+
padding: 8px 10px;
|
|
527
|
+
display: grid;
|
|
528
|
+
gap: 10px;
|
|
529
|
+
}
|
|
530
|
+
.history-item {
|
|
531
|
+
background: var(--vscode-editorWidget-background);
|
|
532
|
+
border-radius: 6px;
|
|
533
|
+
padding: 8px 10px;
|
|
534
|
+
border: 1px solid var(--vscode-panel-border);
|
|
535
|
+
}
|
|
536
|
+
.history-prompt {
|
|
537
|
+
font-size: 12px;
|
|
538
|
+
font-weight: 600;
|
|
539
|
+
margin-bottom: 6px;
|
|
540
|
+
}
|
|
541
|
+
.history-summary {
|
|
542
|
+
font-size: 12px;
|
|
543
|
+
opacity: 0.8;
|
|
544
|
+
}
|
|
545
|
+
</style>
|
|
546
|
+
</head>
|
|
547
|
+
<body>
|
|
548
|
+
<div class="header">
|
|
549
|
+
<span>Patchwork Preview</span>
|
|
550
|
+
<span id="services-label" class="services-label"></span>
|
|
551
|
+
</div>
|
|
552
|
+
<div id="error" class="error" hidden></div>
|
|
553
|
+
<form id="edit-form" class="edit-bar">
|
|
554
|
+
<input id="edit-input" class="edit-input" placeholder="Ask Patchwork to edit" />
|
|
555
|
+
<button id="edit-submit" class="edit-submit" type="submit">Edit</button>
|
|
556
|
+
<button id="history-toggle" class="edit-secondary" type="button">History</button>
|
|
557
|
+
<span id="edit-status" class="edit-status"></span>
|
|
558
|
+
</form>
|
|
559
|
+
<section id="history-panel" class="history-panel" hidden>
|
|
560
|
+
<div class="history-header">Edit History</div>
|
|
561
|
+
<div id="history-list" class="history-list"></div>
|
|
562
|
+
</section>
|
|
563
|
+
<div id="preview"></div>
|
|
564
|
+
<pre id="payload">Waiting for file...</pre>
|
|
565
|
+
<script type="module" nonce="${nonce}">
|
|
566
|
+
const vscode = acquireVsCodeApi();
|
|
567
|
+
const output = document.getElementById('payload');
|
|
568
|
+
const previewRoot = document.getElementById('preview');
|
|
569
|
+
const errorBox = document.getElementById('error');
|
|
570
|
+
const compilerUrl = "${compilerUri}";
|
|
571
|
+
const fallbackCompilerUrl = "https://esm.sh/@aprovan/patchwork-compiler@0.1.0";
|
|
572
|
+
const imagePackage = "@aprovan/patchwork-image-shadcn";
|
|
573
|
+
const proxyBase = "https://patchwork.local/api/proxy";
|
|
574
|
+
let compiler = null;
|
|
575
|
+
let mounted = null;
|
|
576
|
+
let editBuffer = '';
|
|
577
|
+
const pendingServices = new Map();
|
|
578
|
+
let serviceNamespaces = [];
|
|
579
|
+
const servicesLabel = document.getElementById('services-label');
|
|
580
|
+
|
|
581
|
+
async function loadCompiler() {
|
|
582
|
+
try {
|
|
583
|
+
return await import(compilerUrl);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
console.warn('[patchwork-vscode] Failed to load local compiler:', error);
|
|
586
|
+
return import(fallbackCompilerUrl);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async function ensureCompiler() {
|
|
591
|
+
if (compiler) return compiler;
|
|
592
|
+
const mod = await loadCompiler();
|
|
593
|
+
compiler = await mod.createCompiler({
|
|
594
|
+
image: imagePackage,
|
|
595
|
+
proxyUrl: proxyBase,
|
|
596
|
+
});
|
|
597
|
+
return compiler;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function setError(message) {
|
|
601
|
+
if (!message) {
|
|
602
|
+
errorBox.hidden = true;
|
|
603
|
+
display: flex;
|
|
604
|
+
align-items: center;
|
|
605
|
+
justify-content: space-between;
|
|
606
|
+
errorBox.textContent = '';
|
|
607
|
+
.services-label {
|
|
608
|
+
font-size: 10px;
|
|
609
|
+
text-transform: none;
|
|
610
|
+
letter-spacing: 0.02em;
|
|
611
|
+
padding: 4px 6px;
|
|
612
|
+
border-radius: 999px;
|
|
613
|
+
background: var(--vscode-badge-background);
|
|
614
|
+
color: var(--vscode-badge-foreground);
|
|
615
|
+
opacity: 0.9;
|
|
616
|
+
}
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
errorBox.hidden = false;
|
|
620
|
+
errorBox.textContent = message;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async function compileAndMount(text) {
|
|
624
|
+
if (!previewRoot) return;
|
|
625
|
+
try {
|
|
626
|
+
setError('');
|
|
627
|
+
const activeCompiler = await ensureCompiler();
|
|
628
|
+
if (mounted) {
|
|
629
|
+
activeCompiler.unmount(mounted);
|
|
630
|
+
mounted = null;
|
|
631
|
+
}
|
|
632
|
+
const manifest = {
|
|
633
|
+
name: 'preview',
|
|
634
|
+
version: '0.0.0',
|
|
635
|
+
platform: 'browser',
|
|
636
|
+
image: imagePackage,
|
|
637
|
+
};
|
|
638
|
+
const widget = await activeCompiler.compile(text, manifest, {
|
|
639
|
+
typescript: true,
|
|
640
|
+
});
|
|
641
|
+
mounted = await activeCompiler.mount(widget, {
|
|
642
|
+
target: previewRoot,
|
|
643
|
+
mode: 'embedded',
|
|
644
|
+
});
|
|
645
|
+
vscode.postMessage({ type: 'compileSuccess' });
|
|
646
|
+
} catch (error) {
|
|
647
|
+
const err = error instanceof Error ? error : new Error('Compile failed');
|
|
648
|
+
setError(err.message);
|
|
649
|
+
vscode.postMessage({
|
|
650
|
+
type: 'compileError',
|
|
651
|
+
payload: { message: err.message, line: 1, column: 1 },
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const originalFetch = window.fetch.bind(window);
|
|
657
|
+
window.fetch = async (input, init) => {
|
|
658
|
+
const url = typeof input === 'string' ? input : input.url;
|
|
659
|
+
if (url && url.startsWith(proxyBase)) {
|
|
660
|
+
const parsed = new URL(url);
|
|
661
|
+
const parts = parsed.pathname.split('/').filter(Boolean);
|
|
662
|
+
const namespace = parts[2];
|
|
663
|
+
const procedure = parts.slice(3).join('/');
|
|
664
|
+
const body = init?.body ? JSON.parse(init.body) : {};
|
|
665
|
+
const args = body.args || {};
|
|
666
|
+
try {
|
|
667
|
+
const result = await callService(namespace, procedure, args);
|
|
668
|
+
if (result && result.error) {
|
|
669
|
+
return new Response(JSON.stringify({ error: result.error }), {
|
|
670
|
+
status: 500,
|
|
671
|
+
headers: { 'Content-Type': 'application/json' },
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
return new Response(JSON.stringify(result?.result ?? result), {
|
|
675
|
+
status: 200,
|
|
676
|
+
headers: { 'Content-Type': 'application/json' },
|
|
677
|
+
});
|
|
678
|
+
} catch (error) {
|
|
679
|
+
const err = error instanceof Error ? error.message : 'Service call failed';
|
|
680
|
+
return new Response(JSON.stringify({ error: err }), {
|
|
681
|
+
status: 500,
|
|
682
|
+
headers: { 'Content-Type': 'application/json' },
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return originalFetch(input, init);
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
function callService(namespace, procedure, args) {
|
|
690
|
+
const id = Date.now() + '-' + Math.random().toString(36).slice(2, 10);
|
|
691
|
+
return new Promise((resolve) => {
|
|
692
|
+
pendingServices.set(id, resolve);
|
|
693
|
+
vscode.postMessage({
|
|
694
|
+
type: 'serviceCall',
|
|
695
|
+
payload: { id, namespace, procedure, args },
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
window.addEventListener('message', (event) => {
|
|
701
|
+
const message = event.data;
|
|
702
|
+
if (!message) return;
|
|
703
|
+
if (message.type === 'updateFile') {
|
|
704
|
+
const payload = message.payload || {};
|
|
705
|
+
output.textContent = payload.text || 'No content.';
|
|
706
|
+
const code = payload.text || '';
|
|
707
|
+
compileAndMount(code);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (message.type === 'serviceResult') {
|
|
711
|
+
const payload = message.payload || {};
|
|
712
|
+
const handler = pendingServices.get(payload.id);
|
|
713
|
+
if (handler) {
|
|
714
|
+
pendingServices.delete(payload.id);
|
|
715
|
+
handler(payload);
|
|
716
|
+
}
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (message.type === 'editHistorySet') {
|
|
720
|
+
const payload = message.payload || {};
|
|
721
|
+
renderHistory(payload.entries || []);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (message.type === 'editHistoryToggle') {
|
|
725
|
+
toggleHistory();
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (message.type === 'setServices') {
|
|
729
|
+
const payload = message.payload || {};
|
|
730
|
+
serviceNamespaces = payload.namespaces || [];
|
|
731
|
+
if (servicesLabel) {
|
|
732
|
+
const count = serviceNamespaces.length;
|
|
733
|
+
servicesLabel.textContent = count === 0
|
|
734
|
+
? 'No services'
|
|
735
|
+
: 'Services: ' + serviceNamespaces.join(', ');
|
|
736
|
+
}
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (message.type === 'editProgress') {
|
|
740
|
+
const payload = message.payload || {};
|
|
741
|
+
if (payload.chunk) {
|
|
742
|
+
editBuffer += payload.chunk;
|
|
743
|
+
}
|
|
744
|
+
if (payload.done) {
|
|
745
|
+
output.textContent = editBuffer || output.textContent;
|
|
746
|
+
editBuffer = '';
|
|
747
|
+
if (status) status.textContent = 'Applied.';
|
|
748
|
+
}
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (message.type === 'editError') {
|
|
752
|
+
const payload = message.payload || {};
|
|
753
|
+
setError(payload.message || 'Edit failed');
|
|
754
|
+
if (status) status.textContent = 'Failed.';
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
const form = document.getElementById('edit-form');
|
|
759
|
+
const input = document.getElementById('edit-input');
|
|
760
|
+
const submit = document.getElementById('edit-submit');
|
|
761
|
+
const status = document.getElementById('edit-status');
|
|
762
|
+
const historyToggle = document.getElementById('history-toggle');
|
|
763
|
+
const historyPanel = document.getElementById('history-panel');
|
|
764
|
+
const historyList = document.getElementById('history-list');
|
|
765
|
+
|
|
766
|
+
form?.addEventListener('submit', (event) => {
|
|
767
|
+
event.preventDefault();
|
|
768
|
+
const prompt = input?.value?.trim();
|
|
769
|
+
if (!prompt) return;
|
|
770
|
+
if (status) status.textContent = 'Editing...';
|
|
771
|
+
editBuffer = '';
|
|
772
|
+
vscode.postMessage({
|
|
773
|
+
type: 'editRequest',
|
|
774
|
+
payload: { prompt },
|
|
775
|
+
});
|
|
776
|
+
if (input) input.value = '';
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
historyToggle?.addEventListener('click', () => {
|
|
780
|
+
toggleHistory();
|
|
781
|
+
vscode.postMessage({ type: 'editHistoryToggle' });
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
function toggleHistory() {
|
|
785
|
+
if (!historyPanel) return;
|
|
786
|
+
historyPanel.hidden = !historyPanel.hidden;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function renderHistory(entries) {
|
|
790
|
+
if (!historyList) return;
|
|
791
|
+
historyList.innerHTML = '';
|
|
792
|
+
if (!entries || entries.length === 0) {
|
|
793
|
+
const empty = document.createElement('div');
|
|
794
|
+
empty.className = 'history-item';
|
|
795
|
+
empty.textContent = 'No edits yet.';
|
|
796
|
+
historyList.appendChild(empty);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
entries.forEach((entry) => {
|
|
800
|
+
const item = document.createElement('div');
|
|
801
|
+
item.className = 'history-item';
|
|
802
|
+
const prompt = document.createElement('div');
|
|
803
|
+
prompt.className = 'history-prompt';
|
|
804
|
+
prompt.textContent = entry.prompt || 'Edit';
|
|
805
|
+
const summary = document.createElement('div');
|
|
806
|
+
summary.className = 'history-summary';
|
|
807
|
+
summary.textContent = entry.summary || '';
|
|
808
|
+
item.appendChild(prompt);
|
|
809
|
+
item.appendChild(summary);
|
|
810
|
+
historyList.appendChild(item);
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
vscode.postMessage({ type: 'ready' });
|
|
815
|
+
</script>
|
|
816
|
+
</body>
|
|
817
|
+
</html>`;
|
|
818
|
+
}
|
|
819
|
+
getNonce() {
|
|
820
|
+
let text = "";
|
|
821
|
+
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
822
|
+
for (let i = 0; i < 32; i += 1) {
|
|
823
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
824
|
+
}
|
|
825
|
+
return text;
|
|
826
|
+
}
|
|
827
|
+
isPreviewableDocument(document) {
|
|
828
|
+
const ext = path.extname(document.uri.path).toLowerCase();
|
|
829
|
+
return ext === ".tsx" || ext === ".jsx";
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
// src/services/EditService.ts
|
|
834
|
+
var import_openai_compatible = require("@ai-sdk/openai-compatible");
|
|
835
|
+
var import_ai = require("ai");
|
|
836
|
+
var import_stitchery = require("@aprovan/stitchery");
|
|
837
|
+
var EditService = class {
|
|
838
|
+
constructor(baseUrl) {
|
|
839
|
+
this.baseUrl = baseUrl;
|
|
840
|
+
}
|
|
841
|
+
async *streamEdit(code, prompt) {
|
|
842
|
+
const provider = (0, import_openai_compatible.createOpenAICompatible)({
|
|
843
|
+
name: "copilot-proxy",
|
|
844
|
+
baseURL: this.baseUrl
|
|
845
|
+
});
|
|
846
|
+
const result = (0, import_ai.streamText)({
|
|
847
|
+
model: provider("claude-opus-4.5"),
|
|
848
|
+
system: `Current component code:
|
|
849
|
+
\`\`\`tsx
|
|
850
|
+
${code}
|
|
851
|
+
\`\`\`
|
|
852
|
+
|
|
853
|
+
${import_stitchery.EDIT_PROMPT}`,
|
|
854
|
+
messages: [{ role: "user", content: prompt }]
|
|
855
|
+
});
|
|
856
|
+
for await (const chunk of result.textStream) {
|
|
857
|
+
yield chunk;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/services/EmbeddedStitchery.ts
|
|
863
|
+
var import_mcp = require("@ai-sdk/mcp");
|
|
864
|
+
var import_mcp_stdio = require("@ai-sdk/mcp/mcp-stdio");
|
|
865
|
+
var import_patchwork_utcp = require("@aprovan/patchwork-utcp");
|
|
866
|
+
var import_stitchery2 = require("@aprovan/stitchery");
|
|
867
|
+
var EmbeddedStitchery = class {
|
|
868
|
+
registry = new import_stitchery2.ServiceRegistry();
|
|
869
|
+
async initialize(config = {}) {
|
|
870
|
+
this.registry = new import_stitchery2.ServiceRegistry();
|
|
871
|
+
const { utcp, mcpServers = [] } = config;
|
|
872
|
+
if (mcpServers.length > 0) {
|
|
873
|
+
await this.initMcpTools(mcpServers);
|
|
874
|
+
}
|
|
875
|
+
if (utcp) {
|
|
876
|
+
try {
|
|
877
|
+
const { backend, toolInfos } = await (0, import_patchwork_utcp.createUtcpBackend)(
|
|
878
|
+
utcp,
|
|
879
|
+
utcp.cwd
|
|
880
|
+
);
|
|
881
|
+
this.registry.registerBackend(backend, toolInfos);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
console.error("[patchwork-vscode] UTCP init failed:", error);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async handleServiceCall(msg) {
|
|
888
|
+
try {
|
|
889
|
+
const result = await this.registry.call(
|
|
890
|
+
msg.namespace,
|
|
891
|
+
msg.procedure,
|
|
892
|
+
msg.args
|
|
893
|
+
);
|
|
894
|
+
return { id: msg.id, result };
|
|
895
|
+
} catch (error) {
|
|
896
|
+
return {
|
|
897
|
+
id: msg.id,
|
|
898
|
+
error: error instanceof Error ? error.message : "Service call failed"
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
getNamespaces() {
|
|
903
|
+
return this.registry.getNamespaces();
|
|
904
|
+
}
|
|
905
|
+
async initMcpTools(servers) {
|
|
906
|
+
for (const server of servers) {
|
|
907
|
+
const client = await (0, import_mcp.createMCPClient)({
|
|
908
|
+
transport: new import_mcp_stdio.Experimental_StdioMCPTransport({
|
|
909
|
+
command: server.command,
|
|
910
|
+
args: server.args
|
|
911
|
+
})
|
|
912
|
+
});
|
|
913
|
+
this.registry.registerTools(await client.tools(), server.name);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
// src/extension.ts
|
|
919
|
+
function activate(context) {
|
|
920
|
+
const treeProvider = new PatchworkTreeProvider();
|
|
921
|
+
const fileSystemProvider = new PatchworkFileSystemProvider();
|
|
922
|
+
const diagnostics = vscode4.languages.createDiagnosticCollection("patchwork");
|
|
923
|
+
const projectRoots = /* @__PURE__ */ new Map();
|
|
924
|
+
const projects = /* @__PURE__ */ new Map();
|
|
925
|
+
const editHistory = /* @__PURE__ */ new Map();
|
|
926
|
+
const embeddedStitchery = new EmbeddedStitchery();
|
|
927
|
+
const statusBar = vscode4.window.createStatusBarItem(
|
|
928
|
+
vscode4.StatusBarAlignment.Right
|
|
929
|
+
);
|
|
930
|
+
statusBar.command = "patchwork.testConnection";
|
|
931
|
+
statusBar.tooltip = "Patchwork: Copilot proxy status";
|
|
932
|
+
statusBar.show();
|
|
933
|
+
const previewProvider = new PreviewPanelProvider(context, {
|
|
934
|
+
onCompileError: (payload, document) => {
|
|
935
|
+
if (!document) return;
|
|
936
|
+
diagnostics.set(document.uri, toDiagnostics(payload, document));
|
|
937
|
+
},
|
|
938
|
+
onCompileSuccess: (document) => {
|
|
939
|
+
if (!document) return;
|
|
940
|
+
diagnostics.delete(document.uri);
|
|
941
|
+
},
|
|
942
|
+
onEditRequest: async (payload, document) => {
|
|
943
|
+
if (!document) return;
|
|
944
|
+
const request = parseEditRequest(payload);
|
|
945
|
+
if (!request) return;
|
|
946
|
+
await runEditRequest(
|
|
947
|
+
request.prompt,
|
|
948
|
+
document,
|
|
949
|
+
previewProvider,
|
|
950
|
+
editHistory
|
|
951
|
+
);
|
|
952
|
+
},
|
|
953
|
+
onServiceCall: async (payload) => {
|
|
954
|
+
const call = parseServiceCall(payload);
|
|
955
|
+
if (!call) return;
|
|
956
|
+
const result = await embeddedStitchery.handleServiceCall(call);
|
|
957
|
+
previewProvider.postMessage({ type: "serviceResult", payload: result });
|
|
958
|
+
},
|
|
959
|
+
onWebviewReady: () => {
|
|
960
|
+
previewProvider.postMessage({
|
|
961
|
+
type: "setServices",
|
|
962
|
+
payload: { namespaces: embeddedStitchery.getNamespaces() }
|
|
963
|
+
});
|
|
964
|
+
const doc = vscode4.window.activeTextEditor?.document;
|
|
965
|
+
if (doc) {
|
|
966
|
+
previewProvider.postMessage({
|
|
967
|
+
type: "editHistorySet",
|
|
968
|
+
payload: { entries: getHistoryForDoc(editHistory, doc) }
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
context.subscriptions.push(
|
|
974
|
+
vscode4.window.registerTreeDataProvider("patchworkExplorer", treeProvider)
|
|
975
|
+
);
|
|
976
|
+
context.subscriptions.push(diagnostics);
|
|
977
|
+
context.subscriptions.push(statusBar);
|
|
978
|
+
context.subscriptions.push(
|
|
979
|
+
vscode4.workspace.registerFileSystemProvider(
|
|
980
|
+
"patchwork",
|
|
981
|
+
fileSystemProvider,
|
|
982
|
+
{ isCaseSensitive: true }
|
|
983
|
+
)
|
|
984
|
+
);
|
|
985
|
+
context.subscriptions.push(
|
|
986
|
+
vscode4.commands.registerCommand("patchwork.openProject", async () => {
|
|
987
|
+
const selection = await vscode4.window.showOpenDialog({
|
|
988
|
+
canSelectFolders: true,
|
|
989
|
+
canSelectFiles: false,
|
|
990
|
+
canSelectMany: false,
|
|
991
|
+
openLabel: "Open Patchwork Project"
|
|
992
|
+
});
|
|
993
|
+
const folderUri = selection?.[0];
|
|
994
|
+
if (!folderUri) return;
|
|
995
|
+
const project = await loadProjectFromFolder(folderUri);
|
|
996
|
+
if (!project) {
|
|
997
|
+
vscode4.window.showWarningMessage(
|
|
998
|
+
"Patchwork: no supported files found in the selected folder."
|
|
999
|
+
);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
treeProvider.setProject(project.id, project);
|
|
1003
|
+
fileSystemProvider.setProject(project.id, project);
|
|
1004
|
+
projectRoots.set(project.id, folderUri);
|
|
1005
|
+
projects.set(project.id, project);
|
|
1006
|
+
await vscode4.commands.executeCommand(
|
|
1007
|
+
"patchwork.openFile",
|
|
1008
|
+
project.id,
|
|
1009
|
+
project.entry
|
|
1010
|
+
);
|
|
1011
|
+
})
|
|
1012
|
+
);
|
|
1013
|
+
context.subscriptions.push(
|
|
1014
|
+
vscode4.commands.registerCommand("patchwork.showPreview", async () => {
|
|
1015
|
+
const editor = vscode4.window.activeTextEditor;
|
|
1016
|
+
if (!editor) {
|
|
1017
|
+
vscode4.window.showInformationMessage(
|
|
1018
|
+
"Patchwork: open a file to preview."
|
|
1019
|
+
);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
previewProvider.showPreview(editor.document);
|
|
1023
|
+
})
|
|
1024
|
+
);
|
|
1025
|
+
context.subscriptions.push(
|
|
1026
|
+
vscode4.commands.registerCommand(
|
|
1027
|
+
"patchwork.openFile",
|
|
1028
|
+
async (projectId, filePath) => {
|
|
1029
|
+
const uri = buildPatchworkUri(projectId, filePath);
|
|
1030
|
+
await vscode4.commands.executeCommand("vscode.open", uri);
|
|
1031
|
+
}
|
|
1032
|
+
)
|
|
1033
|
+
);
|
|
1034
|
+
context.subscriptions.push(
|
|
1035
|
+
vscode4.commands.registerCommand("patchwork.editWithAI", async () => {
|
|
1036
|
+
const editor = vscode4.window.activeTextEditor;
|
|
1037
|
+
if (!editor) {
|
|
1038
|
+
vscode4.window.showInformationMessage("Patchwork: open a file to edit.");
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const prompt = await vscode4.window.showInputBox({
|
|
1042
|
+
prompt: "Describe the edit you want"
|
|
1043
|
+
});
|
|
1044
|
+
if (!prompt) return;
|
|
1045
|
+
await runEditRequest(
|
|
1046
|
+
prompt,
|
|
1047
|
+
editor.document,
|
|
1048
|
+
previewProvider,
|
|
1049
|
+
editHistory
|
|
1050
|
+
);
|
|
1051
|
+
})
|
|
1052
|
+
);
|
|
1053
|
+
context.subscriptions.push(
|
|
1054
|
+
vscode4.commands.registerCommand("patchwork.showEditHistory", async () => {
|
|
1055
|
+
previewProvider.postMessage({ type: "editHistoryToggle" });
|
|
1056
|
+
})
|
|
1057
|
+
);
|
|
1058
|
+
context.subscriptions.push(
|
|
1059
|
+
vscode4.commands.registerCommand("patchwork.testConnection", async () => {
|
|
1060
|
+
const ok = await updateProxyStatus(statusBar);
|
|
1061
|
+
if (ok) {
|
|
1062
|
+
vscode4.window.showInformationMessage(
|
|
1063
|
+
"Patchwork: Copilot proxy is reachable."
|
|
1064
|
+
);
|
|
1065
|
+
} else {
|
|
1066
|
+
vscode4.window.showWarningMessage(
|
|
1067
|
+
"Patchwork: Copilot proxy is unreachable."
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
})
|
|
1071
|
+
);
|
|
1072
|
+
context.subscriptions.push(
|
|
1073
|
+
vscode4.commands.registerCommand("patchwork.exportProject", async () => {
|
|
1074
|
+
if (projects.size === 0) {
|
|
1075
|
+
vscode4.window.showInformationMessage(
|
|
1076
|
+
"Patchwork: open a project before exporting."
|
|
1077
|
+
);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const projectId = await pickProjectId(projects);
|
|
1081
|
+
if (!projectId) return;
|
|
1082
|
+
const project = projects.get(projectId);
|
|
1083
|
+
if (!project) return;
|
|
1084
|
+
const target = await vscode4.window.showOpenDialog({
|
|
1085
|
+
canSelectFolders: true,
|
|
1086
|
+
canSelectFiles: false,
|
|
1087
|
+
canSelectMany: false,
|
|
1088
|
+
openLabel: "Export Patchwork Project"
|
|
1089
|
+
});
|
|
1090
|
+
const targetDir = target?.[0];
|
|
1091
|
+
if (!targetDir) return;
|
|
1092
|
+
await exportProject(project, targetDir);
|
|
1093
|
+
vscode4.window.showInformationMessage(
|
|
1094
|
+
`Patchwork: exported ${project.id}.`
|
|
1095
|
+
);
|
|
1096
|
+
})
|
|
1097
|
+
);
|
|
1098
|
+
context.subscriptions.push(
|
|
1099
|
+
vscode4.window.onDidChangeActiveTextEditor((editor) => {
|
|
1100
|
+
if (!editor) return;
|
|
1101
|
+
previewProvider.updateDocument(editor.document);
|
|
1102
|
+
previewProvider.postMessage({
|
|
1103
|
+
type: "editHistorySet",
|
|
1104
|
+
payload: { entries: getHistoryForDoc(editHistory, editor.document) }
|
|
1105
|
+
});
|
|
1106
|
+
})
|
|
1107
|
+
);
|
|
1108
|
+
context.subscriptions.push(
|
|
1109
|
+
vscode4.languages.registerCodeActionsProvider(
|
|
1110
|
+
["typescriptreact", "javascriptreact"],
|
|
1111
|
+
{
|
|
1112
|
+
provideCodeActions(document, range) {
|
|
1113
|
+
const action = new vscode4.CodeAction(
|
|
1114
|
+
"Edit with Patchwork AI",
|
|
1115
|
+
vscode4.CodeActionKind.QuickFix
|
|
1116
|
+
);
|
|
1117
|
+
action.command = {
|
|
1118
|
+
command: "patchwork.editWithAI",
|
|
1119
|
+
title: "Edit with Patchwork AI",
|
|
1120
|
+
arguments: [document.uri, range]
|
|
1121
|
+
};
|
|
1122
|
+
return [action];
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
providedCodeActionKinds: [vscode4.CodeActionKind.QuickFix]
|
|
1127
|
+
}
|
|
1128
|
+
)
|
|
1129
|
+
);
|
|
1130
|
+
context.subscriptions.push(
|
|
1131
|
+
vscode4.workspace.onDidSaveTextDocument(async (document) => {
|
|
1132
|
+
if (document.uri.scheme !== "patchwork") return;
|
|
1133
|
+
const parsed = parsePatchworkUri(document.uri);
|
|
1134
|
+
if (!parsed) return;
|
|
1135
|
+
const root = projectRoots.get(parsed.projectId);
|
|
1136
|
+
if (!root) return;
|
|
1137
|
+
await writeProjectFile(root, parsed.filePath, document.getText());
|
|
1138
|
+
})
|
|
1139
|
+
);
|
|
1140
|
+
context.subscriptions.push(
|
|
1141
|
+
vscode4.workspace.onDidChangeTextDocument((event) => {
|
|
1142
|
+
const editor = vscode4.window.activeTextEditor;
|
|
1143
|
+
if (!editor) return;
|
|
1144
|
+
if (event.document !== editor.document) return;
|
|
1145
|
+
previewProvider.updateDocument(event.document);
|
|
1146
|
+
})
|
|
1147
|
+
);
|
|
1148
|
+
context.subscriptions.push(
|
|
1149
|
+
vscode4.workspace.onDidChangeConfiguration((event) => {
|
|
1150
|
+
if (event.affectsConfiguration("patchwork.copilotProxyUrl")) {
|
|
1151
|
+
void updateProxyStatus(statusBar);
|
|
1152
|
+
}
|
|
1153
|
+
if (event.affectsConfiguration("patchwork.mcpServers") || event.affectsConfiguration("patchwork.utcpConfig")) {
|
|
1154
|
+
void initializeEmbeddedStitchery(embeddedStitchery, previewProvider);
|
|
1155
|
+
}
|
|
1156
|
+
})
|
|
1157
|
+
);
|
|
1158
|
+
void updateProxyStatus(statusBar);
|
|
1159
|
+
void initializeEmbeddedStitchery(embeddedStitchery, previewProvider);
|
|
1160
|
+
}
|
|
1161
|
+
function deactivate() {
|
|
1162
|
+
}
|
|
1163
|
+
async function loadProjectFromFolder(folderUri) {
|
|
1164
|
+
const files = [];
|
|
1165
|
+
const ignoredDirs = /* @__PURE__ */ new Set([
|
|
1166
|
+
".git",
|
|
1167
|
+
"node_modules",
|
|
1168
|
+
".turbo",
|
|
1169
|
+
"dist",
|
|
1170
|
+
"build",
|
|
1171
|
+
".next",
|
|
1172
|
+
".cache"
|
|
1173
|
+
]);
|
|
1174
|
+
const ignoredFiles = /* @__PURE__ */ new Set([".DS_Store"]);
|
|
1175
|
+
const walk = async (dirUri) => {
|
|
1176
|
+
const entries = await vscode4.workspace.fs.readDirectory(dirUri);
|
|
1177
|
+
for (const [name, type] of entries) {
|
|
1178
|
+
if (type === vscode4.FileType.Directory && ignoredDirs.has(name)) {
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
if (type === vscode4.FileType.File && ignoredFiles.has(name)) {
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
const entryUri = vscode4.Uri.joinPath(dirUri, name);
|
|
1185
|
+
if (type === vscode4.FileType.Directory) {
|
|
1186
|
+
await walk(entryUri);
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
if (type !== vscode4.FileType.File) continue;
|
|
1190
|
+
const bytes = await vscode4.workspace.fs.readFile(entryUri);
|
|
1191
|
+
const hasNull = bytes.some((value) => value === 0);
|
|
1192
|
+
const content = hasNull ? Buffer.from(bytes).toString("base64") : Buffer.from(bytes).toString("utf8");
|
|
1193
|
+
const relative2 = path2.relative(folderUri.fsPath, entryUri.fsPath).split(path2.sep).join("/");
|
|
1194
|
+
files.push({
|
|
1195
|
+
path: relative2,
|
|
1196
|
+
content,
|
|
1197
|
+
encoding: hasNull ? "base64" : "utf8"
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
await walk(folderUri);
|
|
1202
|
+
if (files.length === 0) return null;
|
|
1203
|
+
const projectId = path2.basename(folderUri.fsPath);
|
|
1204
|
+
return (0, import_patchwork_compiler.createProjectFromFiles)(files, projectId);
|
|
1205
|
+
}
|
|
1206
|
+
function buildPatchworkUri(projectId, filePath) {
|
|
1207
|
+
return vscode4.Uri.parse(`patchwork://${projectId}/${filePath}`);
|
|
1208
|
+
}
|
|
1209
|
+
function parsePatchworkUri(uri) {
|
|
1210
|
+
if (uri.scheme !== "patchwork") return null;
|
|
1211
|
+
const projectId = uri.authority;
|
|
1212
|
+
const filePath = uri.path.replace(/^\/+/, "");
|
|
1213
|
+
if (!projectId || !filePath) return null;
|
|
1214
|
+
return { projectId, filePath };
|
|
1215
|
+
}
|
|
1216
|
+
async function writeProjectFile(root, filePath, content) {
|
|
1217
|
+
const segments = filePath.split("/");
|
|
1218
|
+
const target = vscode4.Uri.joinPath(root, ...segments);
|
|
1219
|
+
if (segments.length > 1) {
|
|
1220
|
+
const dir = vscode4.Uri.joinPath(root, ...segments.slice(0, -1));
|
|
1221
|
+
await vscode4.workspace.fs.createDirectory(dir);
|
|
1222
|
+
}
|
|
1223
|
+
await vscode4.workspace.fs.writeFile(target, Buffer.from(content, "utf8"));
|
|
1224
|
+
}
|
|
1225
|
+
async function exportProject(project, targetDir) {
|
|
1226
|
+
for (const [filePath, file] of project.files) {
|
|
1227
|
+
const segments = filePath.split("/");
|
|
1228
|
+
const target = vscode4.Uri.joinPath(targetDir, ...segments);
|
|
1229
|
+
if (segments.length > 1) {
|
|
1230
|
+
const dir = vscode4.Uri.joinPath(targetDir, ...segments.slice(0, -1));
|
|
1231
|
+
await vscode4.workspace.fs.createDirectory(dir);
|
|
1232
|
+
}
|
|
1233
|
+
const content = file.encoding === "base64" ? Buffer.from(file.content, "base64") : Buffer.from(file.content, "utf8");
|
|
1234
|
+
await vscode4.workspace.fs.writeFile(target, content);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
async function pickProjectId(projects) {
|
|
1238
|
+
if (projects.size === 1) {
|
|
1239
|
+
return projects.keys().next().value;
|
|
1240
|
+
}
|
|
1241
|
+
const options = Array.from(projects.keys()).sort();
|
|
1242
|
+
return vscode4.window.showQuickPick(options, {
|
|
1243
|
+
placeHolder: "Select a Patchwork project"
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
function toDiagnostics(payload, document) {
|
|
1247
|
+
const data = payload && typeof payload === "object" ? payload : {};
|
|
1248
|
+
const message = typeof data.message === "string" ? data.message : "Patchwork compile error";
|
|
1249
|
+
const line = typeof data.line === "number" ? data.line : 1;
|
|
1250
|
+
const column = typeof data.column === "number" ? data.column : 1;
|
|
1251
|
+
const position = new vscode4.Position(
|
|
1252
|
+
clampLine(line - 1, document),
|
|
1253
|
+
Math.max(0, column - 1)
|
|
1254
|
+
);
|
|
1255
|
+
const range = new vscode4.Range(position, position);
|
|
1256
|
+
const diagnostic = new vscode4.Diagnostic(
|
|
1257
|
+
range,
|
|
1258
|
+
message,
|
|
1259
|
+
vscode4.DiagnosticSeverity.Error
|
|
1260
|
+
);
|
|
1261
|
+
return [diagnostic];
|
|
1262
|
+
}
|
|
1263
|
+
async function updateProxyStatus(statusBar) {
|
|
1264
|
+
statusBar.text = "$(sync~spin) Patchwork";
|
|
1265
|
+
const config = vscode4.workspace.getConfiguration("patchwork");
|
|
1266
|
+
const baseUrl = config.get(
|
|
1267
|
+
"copilotProxyUrl",
|
|
1268
|
+
"http://localhost:3000"
|
|
1269
|
+
);
|
|
1270
|
+
try {
|
|
1271
|
+
const response = await fetch(`${baseUrl}/health`);
|
|
1272
|
+
if (response.ok) {
|
|
1273
|
+
statusBar.text = "$(plug) Patchwork";
|
|
1274
|
+
statusBar.tooltip = "Copilot Proxy: Connected";
|
|
1275
|
+
return true;
|
|
1276
|
+
}
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
statusBar.text = "$(debug-disconnect) Patchwork";
|
|
1280
|
+
statusBar.tooltip = "Copilot Proxy: Disconnected";
|
|
1281
|
+
return false;
|
|
1282
|
+
}
|
|
1283
|
+
function clampLine(line, document) {
|
|
1284
|
+
const maxLine = Math.max(0, document.lineCount - 1);
|
|
1285
|
+
return Math.min(Math.max(0, line), maxLine);
|
|
1286
|
+
}
|
|
1287
|
+
function parseEditRequest(payload) {
|
|
1288
|
+
if (!payload || typeof payload !== "object") return null;
|
|
1289
|
+
const data = payload;
|
|
1290
|
+
if (typeof data.prompt !== "string" || !data.prompt.trim()) return null;
|
|
1291
|
+
return { prompt: data.prompt.trim() };
|
|
1292
|
+
}
|
|
1293
|
+
function parseServiceCall(payload) {
|
|
1294
|
+
if (!payload || typeof payload !== "object") return null;
|
|
1295
|
+
const data = payload;
|
|
1296
|
+
const id = typeof data.id === "string" ? data.id : null;
|
|
1297
|
+
const namespace = typeof data.namespace === "string" ? data.namespace : null;
|
|
1298
|
+
const procedure = typeof data.procedure === "string" ? data.procedure : null;
|
|
1299
|
+
const args = data.args && typeof data.args === "object" ? data.args : {};
|
|
1300
|
+
if (!id || !namespace || !procedure) return null;
|
|
1301
|
+
return { id, namespace, procedure, args };
|
|
1302
|
+
}
|
|
1303
|
+
async function runEditRequest(prompt, document, previewProvider, historyStore) {
|
|
1304
|
+
const history = getHistoryForDoc(historyStore, document);
|
|
1305
|
+
const editService = createEditService();
|
|
1306
|
+
let combined = "";
|
|
1307
|
+
previewProvider.postMessage({
|
|
1308
|
+
type: "editProgress",
|
|
1309
|
+
payload: { chunk: "", done: false }
|
|
1310
|
+
});
|
|
1311
|
+
try {
|
|
1312
|
+
for await (const chunk of editService.streamEdit(
|
|
1313
|
+
document.getText(),
|
|
1314
|
+
prompt
|
|
1315
|
+
)) {
|
|
1316
|
+
combined += chunk;
|
|
1317
|
+
previewProvider.postMessage({
|
|
1318
|
+
type: "editProgress",
|
|
1319
|
+
payload: { chunk, done: false }
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
const updated = extractEditedCode(combined);
|
|
1323
|
+
await applyDocumentEdit(document, updated);
|
|
1324
|
+
history.push({ prompt, summary: summarizeEdit(combined) });
|
|
1325
|
+
setHistoryForDoc(historyStore, document, history);
|
|
1326
|
+
previewProvider.postMessage({
|
|
1327
|
+
type: "editHistorySet",
|
|
1328
|
+
payload: { entries: history }
|
|
1329
|
+
});
|
|
1330
|
+
previewProvider.postMessage({
|
|
1331
|
+
type: "editProgress",
|
|
1332
|
+
payload: { chunk: "", done: true }
|
|
1333
|
+
});
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
const message = error instanceof Error ? error.message : "Edit failed";
|
|
1336
|
+
previewProvider.postMessage({
|
|
1337
|
+
type: "editError",
|
|
1338
|
+
payload: { message }
|
|
1339
|
+
});
|
|
1340
|
+
vscode4.window.showErrorMessage(`Patchwork edit failed: ${message}`);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
function createEditService() {
|
|
1344
|
+
const config = vscode4.workspace.getConfiguration("patchwork");
|
|
1345
|
+
const baseUrl = config.get(
|
|
1346
|
+
"copilotProxyUrl",
|
|
1347
|
+
"http://localhost:3000"
|
|
1348
|
+
);
|
|
1349
|
+
return new EditService(baseUrl);
|
|
1350
|
+
}
|
|
1351
|
+
function extractEditedCode(response) {
|
|
1352
|
+
const fence = response.match(/```[a-zA-Z0-9]*\n([\s\S]*?)```/);
|
|
1353
|
+
if (fence && fence[1]) {
|
|
1354
|
+
return fence[1].trimEnd();
|
|
1355
|
+
}
|
|
1356
|
+
return response.trimEnd();
|
|
1357
|
+
}
|
|
1358
|
+
function summarizeEdit(response) {
|
|
1359
|
+
const cleaned = response.replace(/```[\s\S]*?```/g, "").trim();
|
|
1360
|
+
if (!cleaned) return "Edit applied.";
|
|
1361
|
+
const firstLine = cleaned.split("\n").find((line) => line.trim());
|
|
1362
|
+
return (firstLine ?? "Edit applied.").slice(0, 200);
|
|
1363
|
+
}
|
|
1364
|
+
function getHistoryForDoc(history, document) {
|
|
1365
|
+
return history.get(document.uri.toString()) ?? [];
|
|
1366
|
+
}
|
|
1367
|
+
function setHistoryForDoc(history, document, entries) {
|
|
1368
|
+
history.set(document.uri.toString(), entries);
|
|
1369
|
+
}
|
|
1370
|
+
async function applyDocumentEdit(document, text) {
|
|
1371
|
+
const editor = await vscode4.window.showTextDocument(document, {
|
|
1372
|
+
preserveFocus: true,
|
|
1373
|
+
preview: false
|
|
1374
|
+
});
|
|
1375
|
+
const fullRange = new vscode4.Range(
|
|
1376
|
+
document.positionAt(0),
|
|
1377
|
+
document.positionAt(document.getText().length)
|
|
1378
|
+
);
|
|
1379
|
+
await editor.edit((builder) => {
|
|
1380
|
+
builder.replace(fullRange, text);
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
async function initializeEmbeddedStitchery(embeddedStitchery, previewProvider) {
|
|
1384
|
+
const config = vscode4.workspace.getConfiguration("patchwork");
|
|
1385
|
+
const mcpServers = config.get("mcpServers");
|
|
1386
|
+
const utcp = config.get("utcpConfig");
|
|
1387
|
+
await embeddedStitchery.initialize({
|
|
1388
|
+
mcpServers: (mcpServers ?? []).map((server) => ({
|
|
1389
|
+
name: server.name,
|
|
1390
|
+
command: server.command,
|
|
1391
|
+
args: server.args ?? []
|
|
1392
|
+
})),
|
|
1393
|
+
utcp
|
|
1394
|
+
});
|
|
1395
|
+
previewProvider.postMessage({
|
|
1396
|
+
type: "setServices",
|
|
1397
|
+
payload: { namespaces: embeddedStitchery.getNamespaces() }
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1401
|
+
0 && (module.exports = {
|
|
1402
|
+
activate,
|
|
1403
|
+
deactivate
|
|
1404
|
+
});
|
|
1405
|
+
//# sourceMappingURL=extension.js.map
|