@aprovan/patchwork-editor 0.1.2-dev.9d1cd22 → 0.1.2-dev.c1ac1dc
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 +1 -1
- package/dist/components/CodePreview.d.ts +2 -3
- package/dist/components/MarkdownEditor.d.ts +1 -1
- package/dist/components/MarkdownPreview.d.ts +1 -1
- package/dist/components/SaveStatusButton.d.ts +1 -1
- package/dist/components/ServicesInspector.d.ts +1 -1
- package/dist/components/WidgetPreview.d.ts +1 -1
- package/dist/components/edit/CodeBlockView.d.ts +1 -1
- package/dist/components/edit/EditHistory.d.ts +1 -1
- package/dist/components/edit/EditModal.d.ts +1 -1
- package/dist/components/edit/FileTree.d.ts +1 -1
- package/dist/components/edit/MediaPreview.d.ts +1 -1
- package/dist/components/edit/SaveConfirmDialog.d.ts +1 -1
- package/dist/components/edit/useEditSession.d.ts +1 -1
- package/dist/index.js +264 -264
- package/package.json +3 -3
- package/src/components/CodePreview.tsx +5 -6
- package/src/components/MarkdownEditor.tsx +3 -3
- package/src/components/MarkdownPreview.tsx +2 -2
- package/src/components/ServicesInspector.tsx +1 -1
- package/src/components/WidgetPreview.tsx +1 -1
- package/src/components/edit/EditHistory.tsx +1 -1
- package/src/components/edit/EditModal.tsx +6 -6
- package/src/components/edit/FileTree.tsx +2 -3
- package/src/components/edit/MediaPreview.tsx +1 -1
- package/src/components/edit/useEditSession.ts +2 -2
- package/src/lib/code-extractor.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Compiler } from '@aprovan/patchwork-compiler';
|
|
2
|
-
import type { VirtualProject } from '@aprovan/patchwork-compiler';
|
|
1
|
+
import type { Compiler, VirtualProject } from '@aprovan/patchwork-compiler';
|
|
3
2
|
interface CodePreviewProps {
|
|
4
3
|
code: string;
|
|
5
4
|
compiler: Compiler | null;
|
|
@@ -18,5 +17,5 @@ interface CodePreviewProps {
|
|
|
18
17
|
initialProject: VirtualProject;
|
|
19
18
|
}) => void;
|
|
20
19
|
}
|
|
21
|
-
export declare function CodePreview({ code: originalCode, compiler, services, filePath, entrypoint, onOpenEditSession, }: CodePreviewProps): import("react
|
|
20
|
+
export declare function CodePreview({ code: originalCode, compiler, services, filePath, entrypoint, onOpenEditSession, }: CodePreviewProps): import("react").JSX.Element;
|
|
22
21
|
export {};
|
|
@@ -6,5 +6,5 @@ interface MarkdownEditorProps {
|
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
className?: string;
|
|
8
8
|
}
|
|
9
|
-
export declare function MarkdownEditor({ value, onChange, onSubmit, placeholder, disabled, className, }: MarkdownEditorProps): import("react
|
|
9
|
+
export declare function MarkdownEditor({ value, onChange, onSubmit, placeholder, disabled, className, }: MarkdownEditorProps): import("react").JSX.Element;
|
|
10
10
|
export {};
|
|
@@ -4,5 +4,5 @@ interface MarkdownPreviewProps {
|
|
|
4
4
|
editable?: boolean;
|
|
5
5
|
className?: string;
|
|
6
6
|
}
|
|
7
|
-
export declare function MarkdownPreview({ value, onChange, editable, className, }: MarkdownPreviewProps): import("react
|
|
7
|
+
export declare function MarkdownPreview({ value, onChange, editable, className, }: MarkdownPreviewProps): import("react").JSX.Element;
|
|
8
8
|
export {};
|
|
@@ -5,5 +5,5 @@ interface SaveStatusButtonProps {
|
|
|
5
5
|
disabled?: boolean;
|
|
6
6
|
tone: 'muted' | 'primary';
|
|
7
7
|
}
|
|
8
|
-
export declare function SaveStatusButton({ status, onClick, disabled, tone, }: SaveStatusButtonProps): import("react
|
|
8
|
+
export declare function SaveStatusButton({ status, onClick, disabled, tone, }: SaveStatusButtonProps): import("react").JSX.Element;
|
|
9
9
|
export {};
|
|
@@ -44,5 +44,5 @@ interface ServicesInspectorProps {
|
|
|
44
44
|
onClose?: () => void;
|
|
45
45
|
}>;
|
|
46
46
|
}
|
|
47
|
-
export declare function ServicesInspector({ namespaces, services, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogContentComponent, DialogCloseComponent, }: ServicesInspectorProps): import("react
|
|
47
|
+
export declare function ServicesInspector({ namespaces, services, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogContentComponent, DialogCloseComponent, }: ServicesInspectorProps): import("react").JSX.Element;
|
|
48
48
|
export {};
|
|
@@ -5,4 +5,4 @@ export interface WidgetPreviewProps {
|
|
|
5
5
|
services?: string[];
|
|
6
6
|
enabled?: boolean;
|
|
7
7
|
}
|
|
8
|
-
export declare function WidgetPreview({ code, compiler, services, enabled, }: WidgetPreviewProps): import("react
|
|
8
|
+
export declare function WidgetPreview({ code, compiler, services, enabled, }: WidgetPreviewProps): import("react").JSX.Element;
|
|
@@ -4,4 +4,4 @@ export interface CodeBlockViewProps {
|
|
|
4
4
|
editable?: boolean;
|
|
5
5
|
onChange?: (content: string) => void;
|
|
6
6
|
}
|
|
7
|
-
export declare function CodeBlockView({ content, language, editable, onChange }: CodeBlockViewProps): import("react
|
|
7
|
+
export declare function CodeBlockView({ content, language, editable, onChange }: CodeBlockViewProps): import("react").JSX.Element;
|
|
@@ -6,5 +6,5 @@ interface EditHistoryProps {
|
|
|
6
6
|
pendingPrompt?: string | null;
|
|
7
7
|
className?: string;
|
|
8
8
|
}
|
|
9
|
-
export declare function EditHistory({ entries, streamingNotes, isStreaming, pendingPrompt, className, }: EditHistoryProps): import("react
|
|
9
|
+
export declare function EditHistory({ entries, streamingNotes, isStreaming, pendingPrompt, className, }: EditHistoryProps): import("react").JSX.Element;
|
|
10
10
|
export {};
|
|
@@ -18,4 +18,4 @@ export interface EditModalProps extends UseEditSessionOptions {
|
|
|
18
18
|
previewError?: string | null;
|
|
19
19
|
previewLoading?: boolean;
|
|
20
20
|
}
|
|
21
|
-
export declare function EditModal({ isOpen, onClose, onSave, onSaveProject, renderPreview, renderLoading, renderError, previewError, previewLoading, initialTreePath, initialState, hideFileTree, ...sessionOptions }: EditModalProps): import("react
|
|
21
|
+
export declare function EditModal({ isOpen, onClose, onSave, onSaveProject, renderPreview, renderLoading, renderError, previewError, previewLoading, initialTreePath, initialState, hideFileTree, ...sessionOptions }: EditModalProps): import("react").JSX.Element;
|
|
@@ -24,4 +24,4 @@ export interface FileTreeProps {
|
|
|
24
24
|
pageSize?: number;
|
|
25
25
|
reloadToken?: number;
|
|
26
26
|
}
|
|
27
|
-
export declare function FileTree({ files, activeFile, activePath, title, onSelectFile, onSelectDirectory, onReplaceFile, onOpenInEditor, openInEditorMode, openInEditorIcon, openInEditorTitle, pinnedPaths, onTogglePin, directoryLoader, pageSize, reloadToken, }: FileTreeProps): import("react
|
|
27
|
+
export declare function FileTree({ files, activeFile, activePath, title, onSelectFile, onSelectDirectory, onReplaceFile, onOpenInEditor, openInEditorMode, openInEditorIcon, openInEditorTitle, pinnedPaths, onTogglePin, directoryLoader, pageSize, reloadToken, }: FileTreeProps): import("react").JSX.Element;
|
|
@@ -3,4 +3,4 @@ export interface MediaPreviewProps {
|
|
|
3
3
|
mimeType: string;
|
|
4
4
|
fileName: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function MediaPreview({ content, mimeType, fileName }: MediaPreviewProps): import("react
|
|
6
|
+
export declare function MediaPreview({ content, mimeType, fileName }: MediaPreviewProps): import("react").JSX.Element;
|
|
@@ -6,4 +6,4 @@ export interface SaveConfirmDialogProps {
|
|
|
6
6
|
onDiscard: () => void;
|
|
7
7
|
onCancel: () => void;
|
|
8
8
|
}
|
|
9
|
-
export declare function SaveConfirmDialog({ isOpen, isSaving, error, onSave, onDiscard, onCancel, }: SaveConfirmDialogProps): import("react
|
|
9
|
+
export declare function SaveConfirmDialog({ isOpen, isSaving, error, onSave, onDiscard, onCancel, }: SaveConfirmDialogProps): import("react").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { VirtualProject } from "@aprovan/patchwork-compiler";
|
|
2
1
|
import type { EditSessionState, EditSessionActions, CompileFn } from "./types";
|
|
2
|
+
import type { VirtualProject } from "@aprovan/patchwork-compiler";
|
|
3
3
|
export interface UseEditSessionOptions {
|
|
4
4
|
originalCode?: string;
|
|
5
5
|
originalProject?: VirtualProject;
|
package/dist/index.js
CHANGED
|
@@ -2,17 +2,17 @@ import { Node, textblockTypeInputRule } from '@tiptap/core';
|
|
|
2
2
|
import { ReactNodeViewRenderer, NodeViewWrapper, NodeViewContent, useEditor, EditorContent } from '@tiptap/react';
|
|
3
3
|
import { useRef, useCallback, useMemo, useState, useEffect } from 'react';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import { HttpBackend, VFSStore, createSingleFileProject, detectMainFile, createProjectFromFiles } from '@aprovan/patchwork-compiler';
|
|
5
6
|
import { Folder, Pin, X, Loader2, AlertCircle, FileImage, FileVideo, Pencil, RotateCcw, FileCode, FolderTree, Eye, Code, Send, MessageSquare, Server, ChevronDown, File, PinOff, ChevronRight, ChevronsDown, Upload, AlertTriangle, Save } from 'lucide-react';
|
|
6
|
-
import { createSingleFileProject, HttpBackend, VFSStore, detectMainFile, createProjectFromFiles } from '@aprovan/patchwork-compiler';
|
|
7
7
|
import Markdown from 'react-markdown';
|
|
8
8
|
import remarkGfm from 'remark-gfm';
|
|
9
|
-
import
|
|
9
|
+
import { Bobbin, serializeChangesToYAML } from '@aprovan/bobbin';
|
|
10
10
|
import Placeholder from '@tiptap/extension-placeholder';
|
|
11
11
|
import Typography from '@tiptap/extension-typography';
|
|
12
|
-
import { Markdown as Markdown$1 } from 'tiptap-markdown';
|
|
13
12
|
import { TextSelection } from '@tiptap/pm/state';
|
|
13
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
14
|
+
import { Markdown as Markdown$1 } from 'tiptap-markdown';
|
|
14
15
|
import { createHighlighter } from 'shiki';
|
|
15
|
-
import { Bobbin, serializeChangesToYAML } from '@aprovan/bobbin';
|
|
16
16
|
import { clsx } from 'clsx';
|
|
17
17
|
import { twMerge } from 'tailwind-merge';
|
|
18
18
|
|
|
@@ -180,6 +180,69 @@ var CodeBlockExtension = Node.create({
|
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
});
|
|
183
|
+
var VFS_BASE_URL = "/vfs";
|
|
184
|
+
var vfsConfigCache = null;
|
|
185
|
+
async function getVFSConfig() {
|
|
186
|
+
if (vfsConfigCache) return vfsConfigCache;
|
|
187
|
+
try {
|
|
188
|
+
const res = await fetch(`${VFS_BASE_URL}/config`);
|
|
189
|
+
if (res.ok) {
|
|
190
|
+
vfsConfigCache = await res.json();
|
|
191
|
+
return vfsConfigCache;
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
return { usePaths: false };
|
|
196
|
+
}
|
|
197
|
+
var storeInstance = null;
|
|
198
|
+
function getVFSStore() {
|
|
199
|
+
if (!storeInstance) {
|
|
200
|
+
const provider = new HttpBackend({ baseUrl: VFS_BASE_URL });
|
|
201
|
+
storeInstance = new VFSStore(provider, {
|
|
202
|
+
sync: true,
|
|
203
|
+
conflictStrategy: "local-wins"
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return storeInstance;
|
|
207
|
+
}
|
|
208
|
+
async function saveProject(project) {
|
|
209
|
+
const store = getVFSStore();
|
|
210
|
+
await store.saveProject(project);
|
|
211
|
+
}
|
|
212
|
+
async function loadProject(id) {
|
|
213
|
+
const store = getVFSStore();
|
|
214
|
+
return store.loadProject(id);
|
|
215
|
+
}
|
|
216
|
+
async function listProjects() {
|
|
217
|
+
const store = getVFSStore();
|
|
218
|
+
const files = await store.listFiles();
|
|
219
|
+
const projectIds = /* @__PURE__ */ new Set();
|
|
220
|
+
for (const file of files) {
|
|
221
|
+
const id = file.split("/")[0];
|
|
222
|
+
if (id) projectIds.add(id);
|
|
223
|
+
}
|
|
224
|
+
return Array.from(projectIds);
|
|
225
|
+
}
|
|
226
|
+
async function saveFile(path, content) {
|
|
227
|
+
const store = getVFSStore();
|
|
228
|
+
await store.writeFile(path, content);
|
|
229
|
+
}
|
|
230
|
+
async function loadFile(path, encoding) {
|
|
231
|
+
const store = getVFSStore();
|
|
232
|
+
return store.readFile(path, encoding);
|
|
233
|
+
}
|
|
234
|
+
function subscribeToChanges(callback) {
|
|
235
|
+
const store = getVFSStore();
|
|
236
|
+
return store.on("change", callback);
|
|
237
|
+
}
|
|
238
|
+
async function isVFSAvailable() {
|
|
239
|
+
try {
|
|
240
|
+
const res = await fetch(VFS_BASE_URL, { method: "HEAD" });
|
|
241
|
+
return res.ok || res.status === 404;
|
|
242
|
+
} catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
183
246
|
|
|
184
247
|
// src/components/edit/types.ts
|
|
185
248
|
function getActiveContent(state) {
|
|
@@ -1010,6 +1073,154 @@ function SaveStatusButton({
|
|
|
1010
1073
|
}
|
|
1011
1074
|
);
|
|
1012
1075
|
}
|
|
1076
|
+
var highlighterPromise = null;
|
|
1077
|
+
var COMMON_LANGUAGES = [
|
|
1078
|
+
"typescript",
|
|
1079
|
+
"javascript",
|
|
1080
|
+
"tsx",
|
|
1081
|
+
"jsx",
|
|
1082
|
+
"json",
|
|
1083
|
+
"html",
|
|
1084
|
+
"css",
|
|
1085
|
+
"markdown",
|
|
1086
|
+
"yaml",
|
|
1087
|
+
"python",
|
|
1088
|
+
"bash",
|
|
1089
|
+
"sql"
|
|
1090
|
+
];
|
|
1091
|
+
function getHighlighter() {
|
|
1092
|
+
if (!highlighterPromise) {
|
|
1093
|
+
highlighterPromise = createHighlighter({
|
|
1094
|
+
themes: ["github-light"],
|
|
1095
|
+
langs: COMMON_LANGUAGES
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
return highlighterPromise;
|
|
1099
|
+
}
|
|
1100
|
+
function normalizeLanguage(lang) {
|
|
1101
|
+
if (!lang) return "typescript";
|
|
1102
|
+
const normalized = lang.toLowerCase();
|
|
1103
|
+
const mapping = {
|
|
1104
|
+
ts: "typescript",
|
|
1105
|
+
tsx: "tsx",
|
|
1106
|
+
js: "javascript",
|
|
1107
|
+
jsx: "jsx",
|
|
1108
|
+
json: "json",
|
|
1109
|
+
html: "html",
|
|
1110
|
+
css: "css",
|
|
1111
|
+
md: "markdown",
|
|
1112
|
+
markdown: "markdown",
|
|
1113
|
+
yml: "yaml",
|
|
1114
|
+
yaml: "yaml",
|
|
1115
|
+
py: "python",
|
|
1116
|
+
python: "python",
|
|
1117
|
+
sh: "bash",
|
|
1118
|
+
bash: "bash",
|
|
1119
|
+
sql: "sql",
|
|
1120
|
+
typescript: "typescript",
|
|
1121
|
+
javascript: "javascript"
|
|
1122
|
+
};
|
|
1123
|
+
return mapping[normalized] || "typescript";
|
|
1124
|
+
}
|
|
1125
|
+
function CodeBlockView({ content, language, editable = false, onChange }) {
|
|
1126
|
+
const textareaRef = useRef(null);
|
|
1127
|
+
const containerRef = useRef(null);
|
|
1128
|
+
const [highlighter, setHighlighter] = useState(null);
|
|
1129
|
+
useEffect(() => {
|
|
1130
|
+
let mounted = true;
|
|
1131
|
+
getHighlighter().then((h) => {
|
|
1132
|
+
if (mounted) setHighlighter(h);
|
|
1133
|
+
});
|
|
1134
|
+
return () => {
|
|
1135
|
+
mounted = false;
|
|
1136
|
+
};
|
|
1137
|
+
}, []);
|
|
1138
|
+
useEffect(() => {
|
|
1139
|
+
if (textareaRef.current) {
|
|
1140
|
+
textareaRef.current.style.height = "auto";
|
|
1141
|
+
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
|
|
1142
|
+
}
|
|
1143
|
+
}, [content]);
|
|
1144
|
+
const handleChange = useCallback(
|
|
1145
|
+
(e) => {
|
|
1146
|
+
onChange?.(e.target.value);
|
|
1147
|
+
},
|
|
1148
|
+
[onChange]
|
|
1149
|
+
);
|
|
1150
|
+
const handleKeyDown = useCallback(
|
|
1151
|
+
(e) => {
|
|
1152
|
+
if (e.key === "Tab") {
|
|
1153
|
+
e.preventDefault();
|
|
1154
|
+
const target = e.target;
|
|
1155
|
+
const start = target.selectionStart;
|
|
1156
|
+
const end = target.selectionEnd;
|
|
1157
|
+
const value = target.value;
|
|
1158
|
+
const newValue = value.substring(0, start) + " " + value.substring(end);
|
|
1159
|
+
onChange?.(newValue);
|
|
1160
|
+
requestAnimationFrame(() => {
|
|
1161
|
+
target.selectionStart = target.selectionEnd = start + 2;
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
[onChange]
|
|
1166
|
+
);
|
|
1167
|
+
const langLabel = language || "text";
|
|
1168
|
+
const shikiLang = useMemo(() => normalizeLanguage(language), [language]);
|
|
1169
|
+
const highlightedHtml = useMemo(() => {
|
|
1170
|
+
if (!highlighter) return null;
|
|
1171
|
+
try {
|
|
1172
|
+
return highlighter.codeToHtml(content, {
|
|
1173
|
+
lang: shikiLang,
|
|
1174
|
+
theme: "github-light"
|
|
1175
|
+
});
|
|
1176
|
+
} catch {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
}, [highlighter, content, shikiLang]);
|
|
1180
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col bg-[#ffffff]", children: [
|
|
1181
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-4 py-2 bg-[#f6f8fa] border-b border-[#d0d7de] text-xs", children: /* @__PURE__ */ jsx("span", { className: "font-mono text-[#57606a]", children: langLabel }) }),
|
|
1182
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overflow-x-hidden", children: editable ? /* @__PURE__ */ jsxs("div", { className: "relative min-h-full", children: [
|
|
1183
|
+
/* @__PURE__ */ jsx(
|
|
1184
|
+
"div",
|
|
1185
|
+
{
|
|
1186
|
+
ref: containerRef,
|
|
1187
|
+
className: "absolute top-0 left-0 right-0 pointer-events-none p-4",
|
|
1188
|
+
"aria-hidden": "true",
|
|
1189
|
+
children: highlightedHtml ? /* @__PURE__ */ jsx(
|
|
1190
|
+
"div",
|
|
1191
|
+
{
|
|
1192
|
+
className: "highlighted-code font-mono text-xs leading-relaxed whitespace-pre-wrap break-words [&_pre]:!bg-transparent [&_pre]:!m-0 [&_pre]:!p-0 [&_pre]:whitespace-pre-wrap [&_code]:!bg-transparent [&_code]:whitespace-pre-wrap [&_code]:break-words",
|
|
1193
|
+
dangerouslySetInnerHTML: { __html: highlightedHtml }
|
|
1194
|
+
}
|
|
1195
|
+
) : /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap break-words text-[#24292f] m-0 leading-relaxed", children: /* @__PURE__ */ jsx("code", { children: content }) })
|
|
1196
|
+
}
|
|
1197
|
+
),
|
|
1198
|
+
/* @__PURE__ */ jsx(
|
|
1199
|
+
"textarea",
|
|
1200
|
+
{
|
|
1201
|
+
ref: textareaRef,
|
|
1202
|
+
value: content,
|
|
1203
|
+
onChange: handleChange,
|
|
1204
|
+
onKeyDown: handleKeyDown,
|
|
1205
|
+
className: "relative w-full min-h-full font-mono text-xs leading-relaxed bg-transparent border-none outline-none resize-none p-4 text-transparent whitespace-pre-wrap break-words",
|
|
1206
|
+
spellCheck: false,
|
|
1207
|
+
style: {
|
|
1208
|
+
tabSize: 2,
|
|
1209
|
+
caretColor: "#24292f",
|
|
1210
|
+
wordBreak: "break-word",
|
|
1211
|
+
overflowWrap: "break-word"
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
)
|
|
1215
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "p-4", children: highlightedHtml ? /* @__PURE__ */ jsx(
|
|
1216
|
+
"div",
|
|
1217
|
+
{
|
|
1218
|
+
className: "highlighted-code font-mono text-xs leading-relaxed whitespace-pre-wrap break-words [&_pre]:!bg-transparent [&_pre]:!m-0 [&_pre]:!p-0 [&_pre]:whitespace-pre-wrap [&_code]:!bg-transparent [&_code]:whitespace-pre-wrap [&_code]:break-words",
|
|
1219
|
+
dangerouslySetInnerHTML: { __html: highlightedHtml }
|
|
1220
|
+
}
|
|
1221
|
+
) : /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap break-words m-0 leading-relaxed text-[#24292f]", children: /* @__PURE__ */ jsx("code", { children: content }) }) }) })
|
|
1222
|
+
] });
|
|
1223
|
+
}
|
|
1013
1224
|
|
|
1014
1225
|
// src/components/edit/fileTypes.ts
|
|
1015
1226
|
var COMPILABLE_EXTENSIONS = [".tsx", ".jsx", ".ts", ".js"];
|
|
@@ -1693,203 +1904,6 @@ function FileTree({
|
|
|
1693
1904
|
) })
|
|
1694
1905
|
] });
|
|
1695
1906
|
}
|
|
1696
|
-
function SaveConfirmDialog({
|
|
1697
|
-
isOpen,
|
|
1698
|
-
isSaving,
|
|
1699
|
-
error,
|
|
1700
|
-
onSave,
|
|
1701
|
-
onDiscard,
|
|
1702
|
-
onCancel
|
|
1703
|
-
}) {
|
|
1704
|
-
if (!isOpen) return null;
|
|
1705
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/80", children: /* @__PURE__ */ jsxs("div", { className: "bg-background border rounded-lg shadow-lg w-full max-w-md", children: [
|
|
1706
|
-
/* @__PURE__ */ jsxs("div", { className: "p-6", children: [
|
|
1707
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: "Unsaved Changes" }),
|
|
1708
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-2", children: "You have unsaved changes. Would you like to save them before closing?" }),
|
|
1709
|
-
error && /* @__PURE__ */ jsxs("p", { className: "text-sm text-destructive mt-3", children: [
|
|
1710
|
-
"Save failed: ",
|
|
1711
|
-
error
|
|
1712
|
-
] })
|
|
1713
|
-
] }),
|
|
1714
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 p-6 pt-0", children: [
|
|
1715
|
-
/* @__PURE__ */ jsx(
|
|
1716
|
-
"button",
|
|
1717
|
-
{
|
|
1718
|
-
onClick: onCancel,
|
|
1719
|
-
disabled: isSaving,
|
|
1720
|
-
className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50",
|
|
1721
|
-
children: "Cancel"
|
|
1722
|
-
}
|
|
1723
|
-
),
|
|
1724
|
-
/* @__PURE__ */ jsx(
|
|
1725
|
-
"button",
|
|
1726
|
-
{
|
|
1727
|
-
onClick: onDiscard,
|
|
1728
|
-
disabled: isSaving,
|
|
1729
|
-
className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 py-2 border border-input bg-background text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
1730
|
-
children: "Discard"
|
|
1731
|
-
}
|
|
1732
|
-
),
|
|
1733
|
-
/* @__PURE__ */ jsx(
|
|
1734
|
-
"button",
|
|
1735
|
-
{
|
|
1736
|
-
onClick: onSave,
|
|
1737
|
-
disabled: isSaving,
|
|
1738
|
-
className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
|
|
1739
|
-
children: isSaving ? "Saving..." : "Save"
|
|
1740
|
-
}
|
|
1741
|
-
)
|
|
1742
|
-
] })
|
|
1743
|
-
] }) });
|
|
1744
|
-
}
|
|
1745
|
-
var highlighterPromise = null;
|
|
1746
|
-
var COMMON_LANGUAGES = [
|
|
1747
|
-
"typescript",
|
|
1748
|
-
"javascript",
|
|
1749
|
-
"tsx",
|
|
1750
|
-
"jsx",
|
|
1751
|
-
"json",
|
|
1752
|
-
"html",
|
|
1753
|
-
"css",
|
|
1754
|
-
"markdown",
|
|
1755
|
-
"yaml",
|
|
1756
|
-
"python",
|
|
1757
|
-
"bash",
|
|
1758
|
-
"sql"
|
|
1759
|
-
];
|
|
1760
|
-
function getHighlighter() {
|
|
1761
|
-
if (!highlighterPromise) {
|
|
1762
|
-
highlighterPromise = createHighlighter({
|
|
1763
|
-
themes: ["github-light"],
|
|
1764
|
-
langs: COMMON_LANGUAGES
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1767
|
-
return highlighterPromise;
|
|
1768
|
-
}
|
|
1769
|
-
function normalizeLanguage(lang) {
|
|
1770
|
-
if (!lang) return "typescript";
|
|
1771
|
-
const normalized = lang.toLowerCase();
|
|
1772
|
-
const mapping = {
|
|
1773
|
-
ts: "typescript",
|
|
1774
|
-
tsx: "tsx",
|
|
1775
|
-
js: "javascript",
|
|
1776
|
-
jsx: "jsx",
|
|
1777
|
-
json: "json",
|
|
1778
|
-
html: "html",
|
|
1779
|
-
css: "css",
|
|
1780
|
-
md: "markdown",
|
|
1781
|
-
markdown: "markdown",
|
|
1782
|
-
yml: "yaml",
|
|
1783
|
-
yaml: "yaml",
|
|
1784
|
-
py: "python",
|
|
1785
|
-
python: "python",
|
|
1786
|
-
sh: "bash",
|
|
1787
|
-
bash: "bash",
|
|
1788
|
-
sql: "sql",
|
|
1789
|
-
typescript: "typescript",
|
|
1790
|
-
javascript: "javascript"
|
|
1791
|
-
};
|
|
1792
|
-
return mapping[normalized] || "typescript";
|
|
1793
|
-
}
|
|
1794
|
-
function CodeBlockView({ content, language, editable = false, onChange }) {
|
|
1795
|
-
const textareaRef = useRef(null);
|
|
1796
|
-
const containerRef = useRef(null);
|
|
1797
|
-
const [highlighter, setHighlighter] = useState(null);
|
|
1798
|
-
useEffect(() => {
|
|
1799
|
-
let mounted = true;
|
|
1800
|
-
getHighlighter().then((h) => {
|
|
1801
|
-
if (mounted) setHighlighter(h);
|
|
1802
|
-
});
|
|
1803
|
-
return () => {
|
|
1804
|
-
mounted = false;
|
|
1805
|
-
};
|
|
1806
|
-
}, []);
|
|
1807
|
-
useEffect(() => {
|
|
1808
|
-
if (textareaRef.current) {
|
|
1809
|
-
textareaRef.current.style.height = "auto";
|
|
1810
|
-
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
|
|
1811
|
-
}
|
|
1812
|
-
}, [content]);
|
|
1813
|
-
const handleChange = useCallback(
|
|
1814
|
-
(e) => {
|
|
1815
|
-
onChange?.(e.target.value);
|
|
1816
|
-
},
|
|
1817
|
-
[onChange]
|
|
1818
|
-
);
|
|
1819
|
-
const handleKeyDown = useCallback(
|
|
1820
|
-
(e) => {
|
|
1821
|
-
if (e.key === "Tab") {
|
|
1822
|
-
e.preventDefault();
|
|
1823
|
-
const target = e.target;
|
|
1824
|
-
const start = target.selectionStart;
|
|
1825
|
-
const end = target.selectionEnd;
|
|
1826
|
-
const value = target.value;
|
|
1827
|
-
const newValue = value.substring(0, start) + " " + value.substring(end);
|
|
1828
|
-
onChange?.(newValue);
|
|
1829
|
-
requestAnimationFrame(() => {
|
|
1830
|
-
target.selectionStart = target.selectionEnd = start + 2;
|
|
1831
|
-
});
|
|
1832
|
-
}
|
|
1833
|
-
},
|
|
1834
|
-
[onChange]
|
|
1835
|
-
);
|
|
1836
|
-
const langLabel = language || "text";
|
|
1837
|
-
const shikiLang = useMemo(() => normalizeLanguage(language), [language]);
|
|
1838
|
-
const highlightedHtml = useMemo(() => {
|
|
1839
|
-
if (!highlighter) return null;
|
|
1840
|
-
try {
|
|
1841
|
-
return highlighter.codeToHtml(content, {
|
|
1842
|
-
lang: shikiLang,
|
|
1843
|
-
theme: "github-light"
|
|
1844
|
-
});
|
|
1845
|
-
} catch {
|
|
1846
|
-
return null;
|
|
1847
|
-
}
|
|
1848
|
-
}, [highlighter, content, shikiLang]);
|
|
1849
|
-
return /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col bg-[#ffffff]", children: [
|
|
1850
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-4 py-2 bg-[#f6f8fa] border-b border-[#d0d7de] text-xs", children: /* @__PURE__ */ jsx("span", { className: "font-mono text-[#57606a]", children: langLabel }) }),
|
|
1851
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overflow-x-hidden", children: editable ? /* @__PURE__ */ jsxs("div", { className: "relative min-h-full", children: [
|
|
1852
|
-
/* @__PURE__ */ jsx(
|
|
1853
|
-
"div",
|
|
1854
|
-
{
|
|
1855
|
-
ref: containerRef,
|
|
1856
|
-
className: "absolute top-0 left-0 right-0 pointer-events-none p-4",
|
|
1857
|
-
"aria-hidden": "true",
|
|
1858
|
-
children: highlightedHtml ? /* @__PURE__ */ jsx(
|
|
1859
|
-
"div",
|
|
1860
|
-
{
|
|
1861
|
-
className: "highlighted-code font-mono text-xs leading-relaxed whitespace-pre-wrap break-words [&_pre]:!bg-transparent [&_pre]:!m-0 [&_pre]:!p-0 [&_pre]:whitespace-pre-wrap [&_code]:!bg-transparent [&_code]:whitespace-pre-wrap [&_code]:break-words",
|
|
1862
|
-
dangerouslySetInnerHTML: { __html: highlightedHtml }
|
|
1863
|
-
}
|
|
1864
|
-
) : /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap break-words text-[#24292f] m-0 leading-relaxed", children: /* @__PURE__ */ jsx("code", { children: content }) })
|
|
1865
|
-
}
|
|
1866
|
-
),
|
|
1867
|
-
/* @__PURE__ */ jsx(
|
|
1868
|
-
"textarea",
|
|
1869
|
-
{
|
|
1870
|
-
ref: textareaRef,
|
|
1871
|
-
value: content,
|
|
1872
|
-
onChange: handleChange,
|
|
1873
|
-
onKeyDown: handleKeyDown,
|
|
1874
|
-
className: "relative w-full min-h-full font-mono text-xs leading-relaxed bg-transparent border-none outline-none resize-none p-4 text-transparent whitespace-pre-wrap break-words",
|
|
1875
|
-
spellCheck: false,
|
|
1876
|
-
style: {
|
|
1877
|
-
tabSize: 2,
|
|
1878
|
-
caretColor: "#24292f",
|
|
1879
|
-
wordBreak: "break-word",
|
|
1880
|
-
overflowWrap: "break-word"
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
)
|
|
1884
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "p-4", children: highlightedHtml ? /* @__PURE__ */ jsx(
|
|
1885
|
-
"div",
|
|
1886
|
-
{
|
|
1887
|
-
className: "highlighted-code font-mono text-xs leading-relaxed whitespace-pre-wrap break-words [&_pre]:!bg-transparent [&_pre]:!m-0 [&_pre]:!p-0 [&_pre]:whitespace-pre-wrap [&_code]:!bg-transparent [&_code]:whitespace-pre-wrap [&_code]:break-words",
|
|
1888
|
-
dangerouslySetInnerHTML: { __html: highlightedHtml }
|
|
1889
|
-
}
|
|
1890
|
-
) : /* @__PURE__ */ jsx("pre", { className: "text-xs font-mono whitespace-pre-wrap break-words m-0 leading-relaxed text-[#24292f]", children: /* @__PURE__ */ jsx("code", { children: content }) }) }) })
|
|
1891
|
-
] });
|
|
1892
|
-
}
|
|
1893
1907
|
function formatFileSize(bytes) {
|
|
1894
1908
|
if (bytes < 1024) return `${bytes} B`;
|
|
1895
1909
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -1980,6 +1994,55 @@ function MediaPreview({ content, mimeType, fileName }) {
|
|
|
1980
1994
|
] })
|
|
1981
1995
|
] });
|
|
1982
1996
|
}
|
|
1997
|
+
function SaveConfirmDialog({
|
|
1998
|
+
isOpen,
|
|
1999
|
+
isSaving,
|
|
2000
|
+
error,
|
|
2001
|
+
onSave,
|
|
2002
|
+
onDiscard,
|
|
2003
|
+
onCancel
|
|
2004
|
+
}) {
|
|
2005
|
+
if (!isOpen) return null;
|
|
2006
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/80", children: /* @__PURE__ */ jsxs("div", { className: "bg-background border rounded-lg shadow-lg w-full max-w-md", children: [
|
|
2007
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6", children: [
|
|
2008
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: "Unsaved Changes" }),
|
|
2009
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-2", children: "You have unsaved changes. Would you like to save them before closing?" }),
|
|
2010
|
+
error && /* @__PURE__ */ jsxs("p", { className: "text-sm text-destructive mt-3", children: [
|
|
2011
|
+
"Save failed: ",
|
|
2012
|
+
error
|
|
2013
|
+
] })
|
|
2014
|
+
] }),
|
|
2015
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 p-6 pt-0", children: [
|
|
2016
|
+
/* @__PURE__ */ jsx(
|
|
2017
|
+
"button",
|
|
2018
|
+
{
|
|
2019
|
+
onClick: onCancel,
|
|
2020
|
+
disabled: isSaving,
|
|
2021
|
+
className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50",
|
|
2022
|
+
children: "Cancel"
|
|
2023
|
+
}
|
|
2024
|
+
),
|
|
2025
|
+
/* @__PURE__ */ jsx(
|
|
2026
|
+
"button",
|
|
2027
|
+
{
|
|
2028
|
+
onClick: onDiscard,
|
|
2029
|
+
disabled: isSaving,
|
|
2030
|
+
className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 py-2 border border-input bg-background text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
2031
|
+
children: "Discard"
|
|
2032
|
+
}
|
|
2033
|
+
),
|
|
2034
|
+
/* @__PURE__ */ jsx(
|
|
2035
|
+
"button",
|
|
2036
|
+
{
|
|
2037
|
+
onClick: onSave,
|
|
2038
|
+
disabled: isSaving,
|
|
2039
|
+
className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
|
|
2040
|
+
children: isSaving ? "Saving..." : "Save"
|
|
2041
|
+
}
|
|
2042
|
+
)
|
|
2043
|
+
] })
|
|
2044
|
+
] }) });
|
|
2045
|
+
}
|
|
1983
2046
|
function hashCode(str) {
|
|
1984
2047
|
let hash = 0;
|
|
1985
2048
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -2416,69 +2479,6 @@ function WidgetPreview({
|
|
|
2416
2479
|
/* @__PURE__ */ jsx("div", { ref: containerRef, className: "w-full" })
|
|
2417
2480
|
] });
|
|
2418
2481
|
}
|
|
2419
|
-
var VFS_BASE_URL = "/vfs";
|
|
2420
|
-
var vfsConfigCache = null;
|
|
2421
|
-
async function getVFSConfig() {
|
|
2422
|
-
if (vfsConfigCache) return vfsConfigCache;
|
|
2423
|
-
try {
|
|
2424
|
-
const res = await fetch(`${VFS_BASE_URL}/config`);
|
|
2425
|
-
if (res.ok) {
|
|
2426
|
-
vfsConfigCache = await res.json();
|
|
2427
|
-
return vfsConfigCache;
|
|
2428
|
-
}
|
|
2429
|
-
} catch {
|
|
2430
|
-
}
|
|
2431
|
-
return { usePaths: false };
|
|
2432
|
-
}
|
|
2433
|
-
var storeInstance = null;
|
|
2434
|
-
function getVFSStore() {
|
|
2435
|
-
if (!storeInstance) {
|
|
2436
|
-
const provider = new HttpBackend({ baseUrl: VFS_BASE_URL });
|
|
2437
|
-
storeInstance = new VFSStore(provider, {
|
|
2438
|
-
sync: true,
|
|
2439
|
-
conflictStrategy: "local-wins"
|
|
2440
|
-
});
|
|
2441
|
-
}
|
|
2442
|
-
return storeInstance;
|
|
2443
|
-
}
|
|
2444
|
-
async function saveProject(project) {
|
|
2445
|
-
const store = getVFSStore();
|
|
2446
|
-
await store.saveProject(project);
|
|
2447
|
-
}
|
|
2448
|
-
async function loadProject(id) {
|
|
2449
|
-
const store = getVFSStore();
|
|
2450
|
-
return store.loadProject(id);
|
|
2451
|
-
}
|
|
2452
|
-
async function listProjects() {
|
|
2453
|
-
const store = getVFSStore();
|
|
2454
|
-
const files = await store.listFiles();
|
|
2455
|
-
const projectIds = /* @__PURE__ */ new Set();
|
|
2456
|
-
for (const file of files) {
|
|
2457
|
-
const id = file.split("/")[0];
|
|
2458
|
-
if (id) projectIds.add(id);
|
|
2459
|
-
}
|
|
2460
|
-
return Array.from(projectIds);
|
|
2461
|
-
}
|
|
2462
|
-
async function saveFile(path, content) {
|
|
2463
|
-
const store = getVFSStore();
|
|
2464
|
-
await store.writeFile(path, content);
|
|
2465
|
-
}
|
|
2466
|
-
async function loadFile(path, encoding) {
|
|
2467
|
-
const store = getVFSStore();
|
|
2468
|
-
return store.readFile(path, encoding);
|
|
2469
|
-
}
|
|
2470
|
-
function subscribeToChanges(callback) {
|
|
2471
|
-
const store = getVFSStore();
|
|
2472
|
-
return store.on("change", callback);
|
|
2473
|
-
}
|
|
2474
|
-
async function isVFSAvailable() {
|
|
2475
|
-
try {
|
|
2476
|
-
const res = await fetch(VFS_BASE_URL, { method: "HEAD" });
|
|
2477
|
-
return res.ok || res.status === 404;
|
|
2478
|
-
} catch {
|
|
2479
|
-
return false;
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
2482
|
function createManifest2(services) {
|
|
2483
2483
|
return {
|
|
2484
2484
|
name: "preview",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aprovan/patchwork-editor",
|
|
3
|
-
"version": "0.1.2-dev.
|
|
3
|
+
"version": "0.1.2-dev.c1ac1dc",
|
|
4
4
|
"description": "Components for facilitating widget generation and editing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"shiki": "^3.22.0",
|
|
26
26
|
"tailwind-merge": "^3.4.0",
|
|
27
27
|
"tiptap-markdown": "^0.9.0",
|
|
28
|
-
"@aprovan/bobbin": "0.1.0-dev.
|
|
29
|
-
"@aprovan/patchwork-compiler": "0.1.2-dev.
|
|
28
|
+
"@aprovan/bobbin": "0.1.0-dev.c1ac1dc",
|
|
29
|
+
"@aprovan/patchwork-compiler": "0.1.2-dev.c1ac1dc"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
2
|
-
import { Code, Eye, Pencil, RotateCcw, MessageSquare } from 'lucide-react';
|
|
3
|
-
import type { Compiler, Manifest } from '@aprovan/patchwork-compiler';
|
|
4
1
|
import { createSingleFileProject } from '@aprovan/patchwork-compiler';
|
|
2
|
+
import { Code, Eye, Pencil, RotateCcw, MessageSquare } from 'lucide-react';
|
|
3
|
+
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
4
|
+
import { saveProject, getVFSConfig, loadFile, subscribeToChanges } from '../lib/vfs';
|
|
5
5
|
import { EditModal, type CompileFn, CodeBlockView, MediaPreview, getFileType } from './edit';
|
|
6
|
+
import { MarkdownPreview } from './MarkdownPreview';
|
|
6
7
|
import { SaveStatusButton, type SaveStatus } from './SaveStatusButton';
|
|
7
8
|
import { WidgetPreview } from './WidgetPreview';
|
|
8
|
-
import {
|
|
9
|
-
import { saveProject, getVFSConfig, loadFile, subscribeToChanges } from '../lib/vfs';
|
|
10
|
-
import type { VirtualProject } from '@aprovan/patchwork-compiler';
|
|
9
|
+
import type { Compiler, Manifest , VirtualProject } from '@aprovan/patchwork-compiler';
|
|
11
10
|
|
|
12
11
|
interface CodePreviewProps {
|
|
13
12
|
code: string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useEditor, EditorContent } from '@tiptap/react';
|
|
2
|
-
import StarterKit from '@tiptap/starter-kit';
|
|
3
1
|
import Placeholder from '@tiptap/extension-placeholder';
|
|
4
2
|
import Typography from '@tiptap/extension-typography';
|
|
5
|
-
import { Markdown } from 'tiptap-markdown';
|
|
6
3
|
import { TextSelection } from '@tiptap/pm/state';
|
|
4
|
+
import { useEditor, EditorContent } from '@tiptap/react';
|
|
5
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
7
6
|
import { useEffect, useCallback, useRef } from 'react';
|
|
7
|
+
import { Markdown } from 'tiptap-markdown';
|
|
8
8
|
import { CodeBlockExtension } from './CodeBlockExtension';
|
|
9
9
|
|
|
10
10
|
interface MarkdownEditorProps {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import Typography from '@tiptap/extension-typography';
|
|
1
2
|
import { useEditor, EditorContent } from '@tiptap/react';
|
|
2
3
|
import StarterKit from '@tiptap/starter-kit';
|
|
3
|
-
import Typography from '@tiptap/extension-typography';
|
|
4
|
-
import { Markdown } from 'tiptap-markdown';
|
|
5
4
|
import { useEffect, useCallback, useRef, useState } from 'react';
|
|
5
|
+
import { Markdown } from 'tiptap-markdown';
|
|
6
6
|
import { CodeBlockExtension } from './CodeBlockExtension';
|
|
7
7
|
|
|
8
8
|
function parseFrontmatter(content: string): { frontmatter: string; body: string } {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
1
|
import { AlertCircle, Loader2 } from 'lucide-react';
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import type { Compiler, Manifest, MountedWidget } from '@aprovan/patchwork-compiler';
|
|
4
4
|
|
|
5
5
|
export interface WidgetPreviewProps {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Bobbin, serializeChangesToYAML, type Change } from '@aprovan/bobbin';
|
|
2
2
|
import {
|
|
3
3
|
Code,
|
|
4
4
|
Eye,
|
|
@@ -11,18 +11,18 @@ import {
|
|
|
11
11
|
FolderTree,
|
|
12
12
|
FileCode,
|
|
13
13
|
} from 'lucide-react';
|
|
14
|
+
import { useState, useCallback, useMemo, useRef, useEffect, type ReactNode } from 'react';
|
|
14
15
|
import { MarkdownEditor } from '../MarkdownEditor';
|
|
15
16
|
import { MarkdownPreview } from '../MarkdownPreview';
|
|
16
17
|
import { SaveStatusButton, type SaveStatus } from '../SaveStatusButton';
|
|
18
|
+
import { CodeBlockView } from './CodeBlockView';
|
|
17
19
|
import { EditHistory } from './EditHistory';
|
|
18
20
|
import { FileTree } from './FileTree';
|
|
19
|
-
import {
|
|
20
|
-
import { CodeBlockView } from './CodeBlockView';
|
|
21
|
+
import { getFileType, isCompilable, isMarkdownFile, getMimeType } from './fileTypes';
|
|
21
22
|
import { MediaPreview } from './MediaPreview';
|
|
22
|
-
import {
|
|
23
|
+
import { SaveConfirmDialog } from './SaveConfirmDialog';
|
|
23
24
|
import { getActiveContent, getFiles } from './types';
|
|
24
|
-
import {
|
|
25
|
-
import { Bobbin, serializeChangesToYAML, type Change } from '@aprovan/bobbin';
|
|
25
|
+
import { useEditSession, type UseEditSessionOptions } from './useEditSession';
|
|
26
26
|
import type { VirtualProject } from '@aprovan/patchwork-compiler';
|
|
27
27
|
|
|
28
28
|
// Simple hash for React key to force re-render on code changes
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useMemo, useState, useRef, useCallback, useEffect, type ReactNode } from 'react';
|
|
2
1
|
import {
|
|
3
2
|
ChevronRight,
|
|
4
3
|
ChevronDown,
|
|
@@ -12,8 +11,9 @@ import {
|
|
|
12
11
|
PinOff,
|
|
13
12
|
X,
|
|
14
13
|
} from 'lucide-react';
|
|
15
|
-
import type
|
|
14
|
+
import { useMemo, useState, useRef, useCallback, useEffect, type ReactNode } from 'react';
|
|
16
15
|
import { isMediaFile } from './fileTypes';
|
|
16
|
+
import type { VirtualFile } from '@aprovan/patchwork-compiler';
|
|
17
17
|
|
|
18
18
|
interface TreeNode {
|
|
19
19
|
name: string;
|
|
@@ -384,7 +384,6 @@ function LazyTreeNode({
|
|
|
384
384
|
.catch((err) => setLoadError(err instanceof Error ? err.message : 'Failed to load directory'))
|
|
385
385
|
.finally(() => setLoading(false));
|
|
386
386
|
}
|
|
387
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
388
387
|
}, [reloadToken]);
|
|
389
388
|
|
|
390
389
|
const isActive = entry.path === activePath;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
|
2
|
-
import type { VirtualProject } from "@aprovan/patchwork-compiler";
|
|
3
1
|
import { createSingleFileProject } from "@aprovan/patchwork-compiler";
|
|
2
|
+
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
|
4
3
|
import { sendEditRequest } from "./api";
|
|
5
4
|
import type {
|
|
6
5
|
EditHistoryEntry,
|
|
@@ -8,6 +7,7 @@ import type {
|
|
|
8
7
|
EditSessionActions,
|
|
9
8
|
CompileFn,
|
|
10
9
|
} from "./types";
|
|
10
|
+
import type { VirtualProject } from "@aprovan/patchwork-compiler";
|
|
11
11
|
|
|
12
12
|
export interface UseEditSessionOptions {
|
|
13
13
|
originalCode?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { VirtualFile, VirtualProject } from '@aprovan/patchwork-compiler';
|
|
2
1
|
import { createProjectFromFiles, detectMainFile } from '@aprovan/patchwork-compiler';
|
|
2
|
+
import type { VirtualFile, VirtualProject } from '@aprovan/patchwork-compiler';
|
|
3
3
|
|
|
4
4
|
// Matches fenced code blocks with optional attributes: ```language attr="value"\n...content...```
|
|
5
5
|
// Captures: [1] = language (optional), [2] = attributes (optional), [3] = content
|