@aprovan/patchwork-editor 0.1.2-dev.03aaf5b → 0.1.2-dev.f456953
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 +2 -2
- package/dist/components/CodePreview.d.ts +10 -1
- package/dist/components/MarkdownPreview.d.ts +8 -0
- package/dist/components/SaveStatusButton.d.ts +9 -0
- package/dist/components/ServicesInspector.d.ts +3 -4
- package/dist/components/WidgetPreview.d.ts +8 -0
- package/dist/components/edit/EditModal.d.ts +2 -1
- package/dist/components/edit/FileTree.d.ts +22 -3
- package/dist/components/edit/api.d.ts +1 -1
- package/dist/components/edit/fileTypes.d.ts +3 -1
- package/dist/components/edit/useEditSession.d.ts +1 -0
- package/dist/components/index.d.ts +7 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.js +927 -179
- package/package.json +3 -3
- package/src/components/CodePreview.tsx +118 -160
- package/src/components/MarkdownPreview.tsx +147 -0
- package/src/components/SaveStatusButton.tsx +55 -0
- package/src/components/ServicesInspector.tsx +102 -38
- package/src/components/WidgetPreview.tsx +102 -0
- package/src/components/edit/EditModal.tsx +83 -26
- package/src/components/edit/FileTree.tsx +523 -28
- package/src/components/edit/api.ts +14 -9
- package/src/components/edit/fileTypes.ts +82 -54
- package/src/components/edit/useEditSession.ts +13 -3
- package/src/components/index.ts +7 -5
- package/src/index.ts +10 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { applyDiffs, hasDiffBlocks, parseEditResponse } from
|
|
2
|
-
import type { EditRequest, EditResponse } from
|
|
1
|
+
import { applyDiffs, hasDiffBlocks, parseEditResponse } from "../../lib/diff";
|
|
2
|
+
import type { EditRequest, EditResponse } from "./types";
|
|
3
3
|
|
|
4
4
|
export interface EditApiOptions {
|
|
5
5
|
endpoint?: string;
|
|
@@ -12,22 +12,27 @@ export async function sendEditRequest(
|
|
|
12
12
|
request: EditRequest,
|
|
13
13
|
options: EditApiOptions = {},
|
|
14
14
|
): Promise<EditResponse> {
|
|
15
|
-
const { endpoint =
|
|
15
|
+
const { endpoint = "/api/edit", onProgress, sanitize = true } = options;
|
|
16
16
|
|
|
17
17
|
const response = await fetch(endpoint, {
|
|
18
|
-
method:
|
|
19
|
-
headers: {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "Content-Type": "application/json" },
|
|
20
20
|
body: JSON.stringify(request),
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
if (!response.ok) {
|
|
24
|
-
throw new Error(
|
|
24
|
+
throw new Error("Edit request failed");
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const text = await streamResponse(response, onProgress);
|
|
28
28
|
|
|
29
29
|
if (!hasDiffBlocks(text)) {
|
|
30
|
-
|
|
30
|
+
// No diffs — return the original code unchanged with the response as summary
|
|
31
|
+
return {
|
|
32
|
+
newCode: request.code,
|
|
33
|
+
summary: text.trim(),
|
|
34
|
+
progressNotes: [],
|
|
35
|
+
};
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
const parsed = parseEditResponse(text);
|
|
@@ -37,7 +42,7 @@ export async function sendEditRequest(
|
|
|
37
42
|
// Provide detailed context about failed diffs for better error feedback
|
|
38
43
|
const failedDetails = result.failed
|
|
39
44
|
.map((f, i) => `[${i + 1}] "${f}"`)
|
|
40
|
-
.join(
|
|
45
|
+
.join("\n");
|
|
41
46
|
throw new Error(
|
|
42
47
|
`Failed to apply ${parsed.diffs.length} diff(s). None of the SEARCH blocks matched the code.\n\nFailed searches:\n${failedDetails}\n\nThis usually means the code has changed or the SEARCH text doesn't match exactly.`,
|
|
43
48
|
);
|
|
@@ -66,7 +71,7 @@ async function streamResponse(
|
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
const decoder = new TextDecoder();
|
|
69
|
-
let fullText =
|
|
74
|
+
let fullText = "";
|
|
70
75
|
const emittedNotes = new Set<string>();
|
|
71
76
|
|
|
72
77
|
let done = false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type FileCategory =
|
|
1
|
+
export type FileCategory = "compilable" | "text" | "media" | "binary";
|
|
2
2
|
|
|
3
3
|
export interface FileTypeInfo {
|
|
4
4
|
category: FileCategory;
|
|
@@ -6,55 +6,75 @@ export interface FileTypeInfo {
|
|
|
6
6
|
mimeType: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const COMPILABLE_EXTENSIONS = [
|
|
10
|
-
const MEDIA_EXTENSIONS = [
|
|
11
|
-
|
|
9
|
+
const COMPILABLE_EXTENSIONS = [".tsx", ".jsx", ".ts", ".js"];
|
|
10
|
+
const MEDIA_EXTENSIONS = [
|
|
11
|
+
".svg",
|
|
12
|
+
".png",
|
|
13
|
+
".jpg",
|
|
14
|
+
".jpeg",
|
|
15
|
+
".gif",
|
|
16
|
+
".webp",
|
|
17
|
+
".mp4",
|
|
18
|
+
".mov",
|
|
19
|
+
".webm",
|
|
20
|
+
];
|
|
21
|
+
const TEXT_EXTENSIONS = [
|
|
22
|
+
".json",
|
|
23
|
+
".yaml",
|
|
24
|
+
".yml",
|
|
25
|
+
".md",
|
|
26
|
+
".txt",
|
|
27
|
+
".css",
|
|
28
|
+
".html",
|
|
29
|
+
".xml",
|
|
30
|
+
".toml",
|
|
31
|
+
];
|
|
12
32
|
|
|
13
33
|
const EXTENSION_TO_LANGUAGE: Record<string, string> = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
".tsx": "tsx",
|
|
35
|
+
".jsx": "jsx",
|
|
36
|
+
".ts": "typescript",
|
|
37
|
+
".js": "javascript",
|
|
38
|
+
".json": "json",
|
|
39
|
+
".yaml": "yaml",
|
|
40
|
+
".yml": "yaml",
|
|
41
|
+
".md": "markdown",
|
|
42
|
+
".txt": "text",
|
|
43
|
+
".css": "css",
|
|
44
|
+
".html": "html",
|
|
45
|
+
".xml": "xml",
|
|
46
|
+
".toml": "toml",
|
|
47
|
+
".svg": "xml",
|
|
28
48
|
};
|
|
29
49
|
|
|
30
50
|
const EXTENSION_TO_MIME: Record<string, string> = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
".tsx": "text/typescript-jsx",
|
|
52
|
+
".jsx": "text/javascript-jsx",
|
|
53
|
+
".ts": "text/typescript",
|
|
54
|
+
".js": "text/javascript",
|
|
55
|
+
".json": "application/json",
|
|
56
|
+
".yaml": "text/yaml",
|
|
57
|
+
".yml": "text/yaml",
|
|
58
|
+
".md": "text/markdown",
|
|
59
|
+
".txt": "text/plain",
|
|
60
|
+
".css": "text/css",
|
|
61
|
+
".html": "text/html",
|
|
62
|
+
".xml": "application/xml",
|
|
63
|
+
".toml": "text/toml",
|
|
64
|
+
".svg": "image/svg+xml",
|
|
65
|
+
".png": "image/png",
|
|
66
|
+
".jpg": "image/jpeg",
|
|
67
|
+
".jpeg": "image/jpeg",
|
|
68
|
+
".gif": "image/gif",
|
|
69
|
+
".webp": "image/webp",
|
|
70
|
+
".mp4": "video/mp4",
|
|
71
|
+
".mov": "video/quicktime",
|
|
72
|
+
".webm": "video/webm",
|
|
53
73
|
};
|
|
54
74
|
|
|
55
75
|
function getExtension(path: string): string {
|
|
56
|
-
const lastDot = path.lastIndexOf(
|
|
57
|
-
if (lastDot === -1) return
|
|
76
|
+
const lastDot = path.lastIndexOf(".");
|
|
77
|
+
if (lastDot === -1) return "";
|
|
58
78
|
return path.slice(lastDot).toLowerCase();
|
|
59
79
|
}
|
|
60
80
|
|
|
@@ -63,32 +83,32 @@ export function getFileType(path: string): FileTypeInfo {
|
|
|
63
83
|
|
|
64
84
|
if (COMPILABLE_EXTENSIONS.includes(ext)) {
|
|
65
85
|
return {
|
|
66
|
-
category:
|
|
86
|
+
category: "compilable",
|
|
67
87
|
language: EXTENSION_TO_LANGUAGE[ext] ?? null,
|
|
68
|
-
mimeType: EXTENSION_TO_MIME[ext] ??
|
|
88
|
+
mimeType: EXTENSION_TO_MIME[ext] ?? "text/plain",
|
|
69
89
|
};
|
|
70
90
|
}
|
|
71
91
|
|
|
72
92
|
if (TEXT_EXTENSIONS.includes(ext)) {
|
|
73
93
|
return {
|
|
74
|
-
category:
|
|
94
|
+
category: "text",
|
|
75
95
|
language: EXTENSION_TO_LANGUAGE[ext] ?? null,
|
|
76
|
-
mimeType: EXTENSION_TO_MIME[ext] ??
|
|
96
|
+
mimeType: EXTENSION_TO_MIME[ext] ?? "text/plain",
|
|
77
97
|
};
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
if (MEDIA_EXTENSIONS.includes(ext)) {
|
|
81
101
|
return {
|
|
82
|
-
category:
|
|
83
|
-
language: ext ===
|
|
84
|
-
mimeType: EXTENSION_TO_MIME[ext] ??
|
|
102
|
+
category: "media",
|
|
103
|
+
language: ext === ".svg" ? "xml" : null,
|
|
104
|
+
mimeType: EXTENSION_TO_MIME[ext] ?? "application/octet-stream",
|
|
85
105
|
};
|
|
86
106
|
}
|
|
87
107
|
|
|
88
108
|
return {
|
|
89
|
-
category:
|
|
109
|
+
category: "binary",
|
|
90
110
|
language: null,
|
|
91
|
-
mimeType:
|
|
111
|
+
mimeType: "application/octet-stream",
|
|
92
112
|
};
|
|
93
113
|
}
|
|
94
114
|
|
|
@@ -104,6 +124,14 @@ export function isTextFile(path: string): boolean {
|
|
|
104
124
|
return TEXT_EXTENSIONS.includes(getExtension(path));
|
|
105
125
|
}
|
|
106
126
|
|
|
127
|
+
export function isMarkdownFile(path: string): boolean {
|
|
128
|
+
return getExtension(path) === ".md";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function isPreviewable(path: string): boolean {
|
|
132
|
+
return isCompilable(path) || isMarkdownFile(path);
|
|
133
|
+
}
|
|
134
|
+
|
|
107
135
|
export function getLanguageFromExt(path: string): string | null {
|
|
108
136
|
const ext = getExtension(path);
|
|
109
137
|
return EXTENSION_TO_LANGUAGE[ext] ?? null;
|
|
@@ -111,15 +139,15 @@ export function getLanguageFromExt(path: string): string | null {
|
|
|
111
139
|
|
|
112
140
|
export function getMimeType(path: string): string {
|
|
113
141
|
const ext = getExtension(path);
|
|
114
|
-
return EXTENSION_TO_MIME[ext] ??
|
|
142
|
+
return EXTENSION_TO_MIME[ext] ?? "application/octet-stream";
|
|
115
143
|
}
|
|
116
144
|
|
|
117
145
|
export function isImageFile(path: string): boolean {
|
|
118
146
|
const ext = getExtension(path);
|
|
119
|
-
return [
|
|
147
|
+
return [".svg", ".png", ".jpg", ".jpeg", ".gif", ".webp"].includes(ext);
|
|
120
148
|
}
|
|
121
149
|
|
|
122
150
|
export function isVideoFile(path: string): boolean {
|
|
123
151
|
const ext = getExtension(path);
|
|
124
|
-
return [
|
|
152
|
+
return [".mp4", ".mov", ".webm"].includes(ext);
|
|
125
153
|
}
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
export interface UseEditSessionOptions {
|
|
13
13
|
originalCode?: string;
|
|
14
14
|
originalProject?: VirtualProject;
|
|
15
|
+
initialActiveFile?: string;
|
|
15
16
|
compile?: CompileFn;
|
|
16
17
|
apiEndpoint?: string;
|
|
17
18
|
}
|
|
@@ -29,6 +30,7 @@ export function useEditSession(
|
|
|
29
30
|
const {
|
|
30
31
|
originalCode,
|
|
31
32
|
originalProject: providedProject,
|
|
33
|
+
initialActiveFile,
|
|
32
34
|
compile,
|
|
33
35
|
apiEndpoint,
|
|
34
36
|
} = options;
|
|
@@ -49,7 +51,11 @@ export function useEditSession(
|
|
|
49
51
|
const lastSyncedProjectRef = useRef<VirtualProject>(originalProject);
|
|
50
52
|
|
|
51
53
|
const [project, setProject] = useState<VirtualProject>(originalProject);
|
|
52
|
-
const [activeFile, setActiveFile] = useState(
|
|
54
|
+
const [activeFile, setActiveFile] = useState(
|
|
55
|
+
initialActiveFile && originalProject.files.has(initialActiveFile)
|
|
56
|
+
? initialActiveFile
|
|
57
|
+
: originalProject.entry,
|
|
58
|
+
);
|
|
53
59
|
const [history, setHistory] = useState<EditHistoryEntry[]>([]);
|
|
54
60
|
const [isApplying, setIsApplying] = useState(false);
|
|
55
61
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -61,12 +67,16 @@ export function useEditSession(
|
|
|
61
67
|
if (originalProject !== lastSyncedProjectRef.current) {
|
|
62
68
|
lastSyncedProjectRef.current = originalProject;
|
|
63
69
|
setProject(originalProject);
|
|
64
|
-
setActiveFile(
|
|
70
|
+
setActiveFile(
|
|
71
|
+
initialActiveFile && originalProject.files.has(initialActiveFile)
|
|
72
|
+
? initialActiveFile
|
|
73
|
+
: originalProject.entry,
|
|
74
|
+
);
|
|
65
75
|
setHistory([]);
|
|
66
76
|
setError(null);
|
|
67
77
|
setStreamingNotes([]);
|
|
68
78
|
}
|
|
69
|
-
}, [originalProject]);
|
|
79
|
+
}, [originalProject, initialActiveFile]);
|
|
70
80
|
|
|
71
81
|
const performEdit = useCallback(
|
|
72
82
|
async (
|
package/src/components/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { CodeBlockExtension } from
|
|
2
|
-
export { CodePreview } from
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
1
|
+
export { CodeBlockExtension } from "./CodeBlockExtension";
|
|
2
|
+
export { CodePreview } from "./CodePreview";
|
|
3
|
+
export { WidgetPreview } from "./WidgetPreview";
|
|
4
|
+
export { MarkdownEditor } from "./MarkdownEditor";
|
|
5
|
+
export { MarkdownPreview } from "./MarkdownPreview";
|
|
6
|
+
export { ServicesInspector, type ServiceInfo } from "./ServicesInspector";
|
|
7
|
+
export * from "./edit";
|
package/src/index.ts
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
export {
|
|
3
3
|
CodeBlockExtension,
|
|
4
4
|
CodePreview,
|
|
5
|
+
WidgetPreview,
|
|
5
6
|
MarkdownEditor,
|
|
7
|
+
MarkdownPreview,
|
|
6
8
|
ServicesInspector,
|
|
7
9
|
type ServiceInfo,
|
|
8
|
-
} from
|
|
10
|
+
} from "./components";
|
|
9
11
|
|
|
10
12
|
// Edit components
|
|
11
13
|
export {
|
|
@@ -40,11 +42,13 @@ export {
|
|
|
40
42
|
isCompilable,
|
|
41
43
|
isMediaFile,
|
|
42
44
|
isTextFile,
|
|
45
|
+
isMarkdownFile,
|
|
46
|
+
isPreviewable,
|
|
43
47
|
isImageFile,
|
|
44
48
|
isVideoFile,
|
|
45
49
|
getLanguageFromExt,
|
|
46
50
|
getMimeType,
|
|
47
|
-
} from
|
|
51
|
+
} from "./components/edit";
|
|
48
52
|
|
|
49
53
|
// Lib utilities
|
|
50
54
|
export {
|
|
@@ -58,7 +62,7 @@ export {
|
|
|
58
62
|
type CodePart,
|
|
59
63
|
type ParsedPart,
|
|
60
64
|
type ExtractOptions,
|
|
61
|
-
|
|
65
|
+
|
|
62
66
|
// Diff utilities
|
|
63
67
|
parseCodeBlockAttributes,
|
|
64
68
|
parseCodeBlocks,
|
|
@@ -74,7 +78,7 @@ export {
|
|
|
74
78
|
type CodeBlock,
|
|
75
79
|
type DiffBlock,
|
|
76
80
|
type ParsedEditResponse,
|
|
77
|
-
|
|
81
|
+
|
|
78
82
|
// VFS utilities
|
|
79
83
|
getVFSConfig,
|
|
80
84
|
getVFSStore,
|
|
@@ -83,7 +87,7 @@ export {
|
|
|
83
87
|
listProjects,
|
|
84
88
|
saveFile,
|
|
85
89
|
isVFSAvailable,
|
|
86
|
-
|
|
90
|
+
|
|
87
91
|
// General utilities
|
|
88
92
|
cn,
|
|
89
|
-
} from
|
|
93
|
+
} from "./lib";
|