authorgravatar for hiram@hiramchirino.comHiram Chirino <hiram@hiramchirino.com> 2026-03-09 07:30:00-04:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-09 22:50:23-07:00
log9d2c2cae375b6dfc7469c1ada07a109779364846
tree0abb18611710ad437a3615912e5e9c1bd866ca30
parentdcccca3f7b5d089f9946b866c9a149e94fd4c616

fix(web): polyfill crypto.randomUUID for non-secure HTTP contexts

Replace direct crypto.randomUUID() calls with a randomUUID() helper in utils.ts that falls back to crypto.getRandomValues() when the browser is not in a secure context (e.g. plain HTTP on a non-localhost address). Signed-off-by: Hiram Chirino <hiram@hiramchirino.com>

2 files changed, 23 insertions(+), 10 deletions(-)

apps/web/src/components/ChatView.tsx+6-6
...@@ -176,7 +176,7 @@ import {...@@ -176,7 +176,7 @@ import {
176 VisualStudioCode,176 VisualStudioCode,
177 Zed,177 Zed,
178} from "./Icons";178} from "./Icons";
179import { cn, isMacPlatform, isWindowsPlatform } from "~/lib/utils";179import { cn, isMacPlatform, isWindowsPlatform, randomUUID } from "~/lib/utils";
180import { Badge } from "./ui/badge";180import { Badge } from "./ui/badge";
181import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";181import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
182import { Command, CommandItem, CommandList } from "./ui/command";182import { Command, CommandItem, CommandList } from "./ui/command";
...@@ -426,7 +426,7 @@ function readFileAsDataUrl(file: File): Promise<string> {...@@ -426,7 +426,7 @@ function readFileAsDataUrl(file: File): Promise<string> {
426426
427function buildTemporaryWorktreeBranchName(): string {427function buildTemporaryWorktreeBranchName(): string {
428 // Keep the 8-hex suffix shape for backend temporary-branch detection.428 // Keep the 8-hex suffix shape for backend temporary-branch detection.
429 const token = crypto.randomUUID().slice(0, 8).toLowerCase();429 const token = randomUUID().slice(0, 8).toLowerCase();
430 return `${WORKTREE_BRANCH_PREFIX}/${token}`;430 return `${WORKTREE_BRANCH_PREFIX}/${token}`;
431}431}
432432
...@@ -1363,13 +1363,13 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -1363,13 +1363,13 @@ export default function ChatView({ threadId }: ChatViewProps) {
1363 }, [activeThreadId, setTerminalOpen, terminalState.terminalOpen]);1363 }, [activeThreadId, setTerminalOpen, terminalState.terminalOpen]);
1364 const splitTerminal = useCallback(() => {1364 const splitTerminal = useCallback(() => {
1365 if (!activeThreadId || hasReachedTerminalLimit) return;1365 if (!activeThreadId || hasReachedTerminalLimit) return;
1366 const terminalId = `terminal-${crypto.randomUUID()}`;1366 const terminalId = `terminal-${randomUUID()}`;
1367 storeSplitTerminal(activeThreadId, terminalId);1367 storeSplitTerminal(activeThreadId, terminalId);
1368 setTerminalFocusRequestId((value) => value + 1);1368 setTerminalFocusRequestId((value) => value + 1);
1369 }, [activeThreadId, storeSplitTerminal, hasReachedTerminalLimit]);1369 }, [activeThreadId, storeSplitTerminal, hasReachedTerminalLimit]);
1370 const createNewTerminal = useCallback(() => {1370 const createNewTerminal = useCallback(() => {
1371 if (!activeThreadId || hasReachedTerminalLimit) return;1371 if (!activeThreadId || hasReachedTerminalLimit) return;
1372 const terminalId = `terminal-${crypto.randomUUID()}`;1372 const terminalId = `terminal-${randomUUID()}`;
1373 storeNewTerminal(activeThreadId, terminalId);1373 storeNewTerminal(activeThreadId, terminalId);
1374 setTerminalFocusRequestId((value) => value + 1);1374 setTerminalFocusRequestId((value) => value + 1);
1375 }, [activeThreadId, storeNewTerminal, hasReachedTerminalLimit]);1375 }, [activeThreadId, storeNewTerminal, hasReachedTerminalLimit]);
...@@ -1438,7 +1438,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -1438,7 +1438,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
1438 const shouldCreateNewTerminal =1438 const shouldCreateNewTerminal =
1439 wantsNewTerminal && terminalState.terminalIds.length < MAX_THREAD_TERMINAL_COUNT;1439 wantsNewTerminal && terminalState.terminalIds.length < MAX_THREAD_TERMINAL_COUNT;
1440 const targetTerminalId = shouldCreateNewTerminal1440 const targetTerminalId = shouldCreateNewTerminal
1441 ? `terminal-${crypto.randomUUID()}`1441 ? `terminal-${randomUUID()}`
1442 : baseTerminalId;1442 : baseTerminalId;
14431443
1444 setTerminalOpen(true);1444 setTerminalOpen(true);
...@@ -2331,7 +2331,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -2331,7 +2331,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
2331 const previewUrl = URL.createObjectURL(file);2331 const previewUrl = URL.createObjectURL(file);
2332 nextImages.push({2332 nextImages.push({
2333 type: "image",2333 type: "image",
2334 id: crypto.randomUUID(),2334 id: randomUUID(),
2335 name: file.name || "image",2335 name: file.name || "image",
2336 mimeType: file.type,2336 mimeType: file.type,
2337 sizeBytes: file.size,2337 sizeBytes: file.size,
apps/web/src/lib/utils.ts+17-4
...@@ -14,10 +14,23 @@ export function isWindowsPlatform(platform: string): boolean {...@@ -14,10 +14,23 @@ export function isWindowsPlatform(platform: string): boolean {
14 return /^win(dows)?/i.test(platform);14 return /^win(dows)?/i.test(platform);
15}15}
1616
17export const newCommandId = (): CommandId => CommandId.makeUnsafe(crypto.randomUUID());17export function randomUUID(): string {
18 if (typeof crypto.randomUUID === "function") {
19 return crypto.randomUUID();
20 }
21 // Fallback for non-secure contexts (plain HTTP on non-localhost)
22 const bytes = new Uint8Array(16);
23 crypto.getRandomValues(bytes);
24 bytes[6] = (bytes[6] & 0x0f) | 0x40;
25 bytes[8] = (bytes[8] & 0x3f) | 0x80;
26 const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
27 return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
28}
29
30export const newCommandId = (): CommandId => CommandId.makeUnsafe(randomUUID());
1831
19export const newProjectId = (): ProjectId => ProjectId.makeUnsafe(crypto.randomUUID());32export const newProjectId = (): ProjectId => ProjectId.makeUnsafe(randomUUID());
2033
21export const newThreadId = (): ThreadId => ThreadId.makeUnsafe(crypto.randomUUID());34export const newThreadId = (): ThreadId => ThreadId.makeUnsafe(randomUUID());
2235
23export const newMessageId = (): MessageId => MessageId.makeUnsafe(crypto.randomUUID());36export const newMessageId = (): MessageId => MessageId.makeUnsafe(randomUUID());