Skip to content

Commit 766401b

Browse files
committed
review fixes
1 parent 46adf64 commit 766401b

File tree

7 files changed

+43
-67
lines changed

7 files changed

+43
-67
lines changed

apps/twig/src/main/trpc/routers/os.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ import { publicProcedure, router } from "../trpc.js";
99

1010
const fsPromises = fs.promises;
1111

12+
const IMAGE_MIME_MAP: Record<string, string> = {
13+
png: "image/png",
14+
jpg: "image/jpeg",
15+
jpeg: "image/jpeg",
16+
gif: "image/gif",
17+
webp: "image/webp",
18+
bmp: "image/bmp",
19+
ico: "image/x-icon",
20+
svg: "image/svg+xml",
21+
tiff: "image/tiff",
22+
tif: "image/tiff",
23+
};
24+
1225
const messageBoxOptionsSchema = z.object({
1326
type: z.enum(["none", "info", "error", "question", "warning"]).optional(),
1427
title: z.string().optional(),
@@ -170,19 +183,7 @@ export const osRouter = router({
170183
if (stat.size > input.maxSizeBytes) return null;
171184

172185
const ext = path.extname(input.filePath).toLowerCase().slice(1);
173-
const mimeMap: Record<string, string> = {
174-
png: "image/png",
175-
jpg: "image/jpeg",
176-
jpeg: "image/jpeg",
177-
gif: "image/gif",
178-
webp: "image/webp",
179-
bmp: "image/bmp",
180-
ico: "image/x-icon",
181-
svg: "image/svg+xml",
182-
tiff: "image/tiff",
183-
tif: "image/tiff",
184-
};
185-
const mime = mimeMap[ext] ?? "application/octet-stream";
186+
const mime = IMAGE_MIME_MAP[ext] ?? "application/octet-stream";
186187

187188
const buffer = await fsPromises.readFile(input.filePath);
188189
return `data:${mime};base64,${buffer.toString("base64")}`;

apps/twig/src/renderer/features/code-editor/components/CodeEditorPanel.tsx

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,12 @@
11
import { PanelMessage } from "@components/ui/PanelMessage";
22
import { CodeMirrorEditor } from "@features/code-editor/components/CodeMirrorEditor";
33
import { getRelativePath } from "@features/code-editor/utils/pathUtils";
4+
import { isImageFile } from "@features/message-editor/utils/imageUtils";
45
import { useCwd } from "@features/sidebar/hooks/useCwd";
56
import { Box, Flex } from "@radix-ui/themes";
67
import { trpcReact } from "@renderer/trpc/client";
78
import type { Task } from "@shared/types";
89

9-
const IMAGE_EXTENSIONS = new Set([
10-
"png",
11-
"jpg",
12-
"jpeg",
13-
"gif",
14-
"webp",
15-
"svg",
16-
"bmp",
17-
"ico",
18-
]);
19-
20-
function isImageFile(filePath: string): boolean {
21-
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
22-
return IMAGE_EXTENSIONS.has(ext);
23-
}
24-
2510
interface CodeEditorPanelProps {
2611
taskId: string;
2712
task: Task;
@@ -72,10 +57,6 @@ export function CodeEditorPanel({
7257
);
7358
}
7459

75-
if (!repoPath) {
76-
return <PanelMessage>No repository path available</PanelMessage>;
77-
}
78-
7960
if (isLoading) {
8061
return <PanelMessage>Loading file...</PanelMessage>;
8162
}

apps/twig/src/renderer/features/message-editor/components/ImageAttachmentsBar.tsx renamed to apps/twig/src/renderer/features/message-editor/components/AttachmentsBar.tsx

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,20 @@
11
import { File, X } from "@phosphor-icons/react";
22
import { Dialog, Flex, IconButton, Text } from "@radix-ui/themes";
3-
import { trpcVanilla } from "@renderer/trpc/client";
4-
import { useEffect, useState } from "react";
3+
import { trpcReact } from "@renderer/trpc/client";
54
import type { FileAttachment } from "../utils/content";
65
import { isImageFile } from "../utils/imageUtils";
76

8-
function useDataUrl(filePath: string) {
9-
const [dataUrl, setDataUrl] = useState<string | null>(null);
10-
11-
useEffect(() => {
12-
let cancelled = false;
13-
trpcVanilla.os.readFileAsDataUrl
14-
.query({ filePath })
15-
.then((url) => {
16-
if (!cancelled) setDataUrl(url);
17-
})
18-
.catch(() => {});
19-
return () => {
20-
cancelled = true;
21-
};
22-
}, [filePath]);
23-
24-
return dataUrl;
25-
}
26-
277
function ImageThumbnail({
288
attachment,
299
onRemove,
3010
}: {
3111
attachment: FileAttachment;
3212
onRemove: () => void;
3313
}) {
34-
const dataUrl = useDataUrl(attachment.id);
14+
const { data: dataUrl } = trpcReact.os.readFileAsDataUrl.useQuery(
15+
{ filePath: attachment.id },
16+
{ staleTime: Infinity },
17+
);
3518

3619
return (
3720
<Dialog.Root>

apps/twig/src/renderer/features/message-editor/components/MessageEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import { useTiptapEditor } from "../tiptap/useTiptapEditor";
1111
import type { EditorHandle } from "../types";
1212
import type { EditorContent as EditorContentType } from "../utils/content";
1313
import { AdapterIndicator } from "./AdapterIndicator";
14+
import { AttachmentsBar } from "./AttachmentsBar";
1415
import { DiffStatsIndicator } from "./DiffStatsIndicator";
1516
import { EditorToolbar } from "./EditorToolbar";
16-
import { AttachmentsBar } from "./ImageAttachmentsBar";
1717
import { ModeIndicatorInput } from "./ModeIndicatorInput";
1818

1919
export type { EditorHandle as MessageEditorHandle };

apps/twig/src/renderer/features/message-editor/tiptap/useDraftSync.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Editor, JSONContent } from "@tiptap/core";
2-
import { useCallback, useLayoutEffect, useRef } from "react";
2+
import { useCallback, useLayoutEffect, useRef, useState } from "react";
33
import { useDraftStore } from "../stores/draftStore";
44
import {
55
type EditorContent,
@@ -166,11 +166,13 @@ export function useDraftSync(
166166
}, [editor, pendingContent, sessionId, draftActions]);
167167

168168
// Extract restored attachments from draft on first restore
169-
const restoredAttachmentsRef = useRef<FileAttachment[]>([]);
169+
const [restoredAttachments, setRestoredAttachments] = useState<
170+
FileAttachment[]
171+
>([]);
170172
useLayoutEffect(() => {
171173
if (!draft || typeof draft === "string") return;
172174
if (draft.attachments && draft.attachments.length > 0) {
173-
restoredAttachmentsRef.current = draft.attachments;
175+
setRestoredAttachments(draft.attachments);
174176
}
175177
}, [draft]);
176178

@@ -219,6 +221,6 @@ export function useDraftSync(
219221
saveDraft,
220222
clearDraft,
221223
getContent,
222-
restoredAttachments: restoredAttachmentsRef.current,
224+
restoredAttachments,
223225
};
224226
}

apps/twig/src/renderer/features/message-editor/utils/content.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,34 @@ export function contentToPlainText(content: EditorContent): string {
3434
.join("");
3535
}
3636

37+
function escapeXmlAttr(value: string): string {
38+
return value
39+
.replace(/&/g, "&amp;")
40+
.replace(/"/g, "&quot;")
41+
.replace(/</g, "&lt;")
42+
.replace(/>/g, "&gt;");
43+
}
44+
3745
export function contentToXml(content: EditorContent): string {
3846
const inlineFilePaths = new Set<string>();
3947
const parts = content.segments.map((seg) => {
4048
if (seg.type === "text") return seg.text;
4149
const chip = seg.chip;
50+
const escapedId = escapeXmlAttr(chip.id);
4251
switch (chip.type) {
4352
case "file":
4453
inlineFilePaths.add(chip.id);
45-
return `<file path="${chip.id}" />`;
54+
return `<file path="${escapedId}" />`;
4655
case "command":
4756
return `/${chip.label}`;
4857
case "error":
49-
return `<error id="${chip.id}" />`;
58+
return `<error id="${escapedId}" />`;
5059
case "experiment":
51-
return `<experiment id="${chip.id}" />`;
60+
return `<experiment id="${escapedId}" />`;
5261
case "insight":
53-
return `<insight id="${chip.id}" />`;
62+
return `<insight id="${escapedId}" />`;
5463
case "feature_flag":
55-
return `<feature_flag id="${chip.id}" />`;
64+
return `<feature_flag id="${escapedId}" />`;
5665
default:
5766
return `@${chip.label}`;
5867
}
@@ -62,7 +71,7 @@ export function contentToXml(content: EditorContent): string {
6271
if (content.attachments) {
6372
for (const att of content.attachments) {
6473
if (!inlineFilePaths.has(att.id)) {
65-
parts.push(`<file path="${att.id}" />`);
74+
parts.push(`<file path="${escapeXmlAttr(att.id)}" />`);
6675
}
6776
}
6877
}

apps/twig/src/renderer/features/task-detail/components/TaskInputEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import "@features/message-editor/components/message-editor.css";
2+
import { AttachmentsBar } from "@features/message-editor/components/AttachmentsBar";
23
import { EditorToolbar } from "@features/message-editor/components/EditorToolbar";
3-
import { AttachmentsBar } from "@features/message-editor/components/ImageAttachmentsBar";
44
import type { MessageEditorHandle } from "@features/message-editor/components/MessageEditor";
55
import { useTiptapEditor } from "@features/message-editor/tiptap/useTiptapEditor";
66
import { ModelSelector } from "@features/sessions/components/ModelSelector";

0 commit comments

Comments
 (0)