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 {
176176 VisualStudioCode,
177177 Zed,
178178} from "./Icons";
179import { cn, isMacPlatform, isWindowsPlatform } from "~/lib/utils";
179import { cn, isMacPlatform, isWindowsPlatform, randomUUID } from "~/lib/utils";
180180import { Badge } from "./ui/badge";
181181import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
182182import { Command, CommandItem, CommandList } from "./ui/command";
......@@ -426,7 +426,7 @@ function readFileAsDataUrl(file: File): Promise<string> {
426426
427427function buildTemporaryWorktreeBranchName(): string {
428428 // 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();
430430 return `${WORKTREE_BRANCH_PREFIX}/${token}`;
431431}
432432
......@@ -1363,13 +1363,13 @@ export default function ChatView({ threadId }: ChatViewProps) {
13631363 }, [activeThreadId, setTerminalOpen, terminalState.terminalOpen]);
13641364 const splitTerminal = useCallback(() => {
13651365 if (!activeThreadId || hasReachedTerminalLimit) return;
1366 const terminalId = `terminal-${crypto.randomUUID()}`;
1366 const terminalId = `terminal-${randomUUID()}`;
13671367 storeSplitTerminal(activeThreadId, terminalId);
13681368 setTerminalFocusRequestId((value) => value + 1);
13691369 }, [activeThreadId, storeSplitTerminal, hasReachedTerminalLimit]);
13701370 const createNewTerminal = useCallback(() => {
13711371 if (!activeThreadId || hasReachedTerminalLimit) return;
1372 const terminalId = `terminal-${crypto.randomUUID()}`;
1372 const terminalId = `terminal-${randomUUID()}`;
13731373 storeNewTerminal(activeThreadId, terminalId);
13741374 setTerminalFocusRequestId((value) => value + 1);
13751375 }, [activeThreadId, storeNewTerminal, hasReachedTerminalLimit]);
......@@ -1438,7 +1438,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
14381438 const shouldCreateNewTerminal =
14391439 wantsNewTerminal && terminalState.terminalIds.length < MAX_THREAD_TERMINAL_COUNT;
14401440 const targetTerminalId = shouldCreateNewTerminal
1441 ? `terminal-${crypto.randomUUID()}`
1441 ? `terminal-${randomUUID()}`
14421442 : baseTerminalId;
14431443
14441444 setTerminalOpen(true);
......@@ -2331,7 +2331,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
23312331 const previewUrl = URL.createObjectURL(file);
23322332 nextImages.push({
23332333 type: "image",
2334 id: crypto.randomUUID(),
2334 id: randomUUID(),
23352335 name: file.name || "image",
23362336 mimeType: file.type,
23372337 sizeBytes: file.size,
apps/web/src/lib/utils.ts+17-4
......@@ -14,10 +14,23 @@ export function isWindowsPlatform(platform: string): boolean {
1414 return /^win(dows)?/i.test(platform);
1515}
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());