authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-12 21:03:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-12 21:03:21-07:00
log876bbd715ae6aa8e1d663455747e17c92e0a287c
tree7881f15b09c9f4e7095776b7b2e384aa62751c9b
parentac0d1f52a087c63819b2b032ac4535d18d3c23af
signature Signed by PGP key B5690EEEBB952194

Extract reusable clipboard hook and standardize media queries (#1006)

Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com> Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>

8 files changed, 189 insertions(+), 83 deletions(-)

apps/web/src/components/PlanSidebar.tsx+6-24
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { memo, useState, useCallback, useRef, useEffect } from "react";1import { memo, useState, useCallback } from "react";
2import { type TimestampFormat } from "../appSettings";2import { type TimestampFormat } from "../appSettings";
3import { Badge } from "./ui/badge";3import { Badge } from "./ui/badge";
4import { Button } from "./ui/button";4import { Button } from "./ui/button";
...@@ -26,6 +26,7 @@ import {...@@ -26,6 +26,7 @@ import {
26import { Menu, MenuItem, MenuPopup, MenuTrigger } from "./ui/menu";26import { Menu, MenuItem, MenuPopup, MenuTrigger } from "./ui/menu";
27import { readNativeApi } from "~/nativeApi";27import { readNativeApi } from "~/nativeApi";
28import { toastManager } from "./ui/toast";28import { toastManager } from "./ui/toast";
29import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
2930
30function stepStatusIcon(status: string): React.ReactNode {31function stepStatusIcon(status: string): React.ReactNode {
31 if (status === "completed") {32 if (status === "completed") {
...@@ -68,8 +69,7 @@ const PlanSidebar = memo(function PlanSidebar({...@@ -68,8 +69,7 @@ const PlanSidebar = memo(function PlanSidebar({
68}: PlanSidebarProps) {69}: PlanSidebarProps) {
69 const [proposedPlanExpanded, setProposedPlanExpanded] = useState(false);70 const [proposedPlanExpanded, setProposedPlanExpanded] = useState(false);
70 const [isSavingToWorkspace, setIsSavingToWorkspace] = useState(false);71 const [isSavingToWorkspace, setIsSavingToWorkspace] = useState(false);
71 const [copied, setCopied] = useState(false);72 const { copyToClipboard, isCopied } = useCopyToClipboard();
72 const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
7373
74 const planMarkdown = activeProposedPlan?.planMarkdown ?? null;74 const planMarkdown = activeProposedPlan?.planMarkdown ?? null;
75 const displayedPlanMarkdown = planMarkdown ? stripDisplayedPlanMarkdown(planMarkdown) : null;75 const displayedPlanMarkdown = planMarkdown ? stripDisplayedPlanMarkdown(planMarkdown) : null;
...@@ -77,26 +77,8 @@ const PlanSidebar = memo(function PlanSidebar({...@@ -77,26 +77,8 @@ const PlanSidebar = memo(function PlanSidebar({
7777
78 const handleCopyPlan = useCallback(() => {78 const handleCopyPlan = useCallback(() => {
79 if (!planMarkdown) return;79 if (!planMarkdown) return;
80 void navigator.clipboard.writeText(planMarkdown);80 copyToClipboard(planMarkdown);
81 if (copiedTimerRef.current != null) {81 }, [planMarkdown, copyToClipboard]);
82 clearTimeout(copiedTimerRef.current);
83 }
84
85 setCopied(true);
86 copiedTimerRef.current = setTimeout(() => {
87 setCopied(false);
88 copiedTimerRef.current = null;
89 }, 2000);
90 }, [planMarkdown]);
91
92 // Cleanup timeout on unmount
93 useEffect(() => {
94 return () => {
95 if (copiedTimerRef.current != null) {
96 clearTimeout(copiedTimerRef.current);
97 }
98 };
99 }, []);
10082
101 const handleDownload = useCallback(() => {83 const handleDownload = useCallback(() => {
102 if (!planMarkdown) return;84 if (!planMarkdown) return;
...@@ -169,7 +151,7 @@ const PlanSidebar = memo(function PlanSidebar({...@@ -169,7 +151,7 @@ const PlanSidebar = memo(function PlanSidebar({
169 </MenuTrigger>151 </MenuTrigger>
170 <MenuPopup align="end">152 <MenuPopup align="end">
171 <MenuItem onClick={handleCopyPlan}>153 <MenuItem onClick={handleCopyPlan}>
172 {copied ? "Copied!" : "Copy to clipboard"}154 {isCopied ? "Copied!" : "Copy to clipboard"}
173 </MenuItem>155 </MenuItem>
174 <MenuItem onClick={handleDownload}>Download as markdown</MenuItem>156 <MenuItem onClick={handleDownload}>Download as markdown</MenuItem>
175 <MenuItem157 <MenuItem
apps/web/src/components/Sidebar.tsx+19-22
...@@ -89,17 +89,11 @@ import {...@@ -89,17 +89,11 @@ import {
89 resolveThreadStatusPill,89 resolveThreadStatusPill,
90 shouldClearThreadSelectionOnMouseDown,90 shouldClearThreadSelectionOnMouseDown,
91} from "./Sidebar.logic";91} from "./Sidebar.logic";
92import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
9293
93const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = [];94const EMPTY_KEYBINDINGS: ResolvedKeybindingsConfig = [];
94const THREAD_PREVIEW_LIMIT = 6;95const THREAD_PREVIEW_LIMIT = 6;
9596
96async function copyTextToClipboard(text: string): Promise<void> {
97 if (typeof navigator === "undefined" || navigator.clipboard?.writeText === undefined) {
98 throw new Error("Clipboard API unavailable.");
99 }
100 await navigator.clipboard.writeText(text);
101}
102
103function formatRelativeTime(iso: string): string {97function formatRelativeTime(iso: string): string {
104 const diff = Date.now() - new Date(iso).getTime();98 const diff = Date.now() - new Date(iso).getTime();
105 const minutes = Math.floor(diff / 60_000);99 const minutes = Math.floor(diff / 60_000);
...@@ -671,6 +665,22 @@ export default function Sidebar() {...@@ -671,6 +665,22 @@ export default function Sidebar() {
671 ],665 ],
672 );666 );
673667
668 const { copyToClipboard } = useCopyToClipboard<{ threadId: ThreadId }>({
669 onCopy: (ctx) => {
670 toastManager.add({
671 type: "success",
672 title: "Thread ID copied",
673 description: ctx.threadId,
674 });
675 },
676 onError: (error) => {
677 toastManager.add({
678 type: "error",
679 title: "Failed to copy thread ID",
680 description: error instanceof Error ? error.message : "An error occurred.",
681 });
682 },
683 });
674 const handleThreadContextMenu = useCallback(684 const handleThreadContextMenu = useCallback(
675 async (threadId: ThreadId, position: { x: number; y: number }) => {685 async (threadId: ThreadId, position: { x: number; y: number }) => {
676 const api = readNativeApi();686 const api = readNativeApi();
...@@ -699,20 +709,7 @@ export default function Sidebar() {...@@ -699,20 +709,7 @@ export default function Sidebar() {
699 return;709 return;
700 }710 }
701 if (clicked === "copy-thread-id") {711 if (clicked === "copy-thread-id") {
702 try {712 copyToClipboard(threadId, { threadId });
703 await copyTextToClipboard(threadId);
704 toastManager.add({
705 type: "success",
706 title: "Thread ID copied",
707 description: threadId,
708 });
709 } catch (error) {
710 toastManager.add({
711 type: "error",
712 title: "Failed to copy thread ID",
713 description: error instanceof Error ? error.message : "An error occurred.",
714 });
715 }
716 return;713 return;
717 }714 }
718 if (clicked !== "delete") return;715 if (clicked !== "delete") return;
...@@ -729,7 +726,7 @@ export default function Sidebar() {...@@ -729,7 +726,7 @@ export default function Sidebar() {
729 }726 }
730 await deleteThread(threadId);727 await deleteThread(threadId);
731 },728 },
732 [appSettings.confirmThreadDelete, deleteThread, markThreadUnread, threads],729 [appSettings.confirmThreadDelete, copyToClipboard, deleteThread, markThreadUnread, threads],
733 );730 );
734731
735 const handleMultiSelectContextMenu = useCallback(732 const handleMultiSelectContextMenu = useCallback(
apps/web/src/components/chat/MessageCopyButton.tsx+11-10
...@@ -1,19 +1,20 @@...@@ -1,19 +1,20 @@
1import { memo, useCallback, useState } from "react";1import { memo } from "react";
2import { CopyIcon, CheckIcon } from "lucide-react";2import { CopyIcon, CheckIcon } from "lucide-react";
3import { Button } from "../ui/button";3import { Button } from "../ui/button";
4import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
45
5export const MessageCopyButton = memo(function MessageCopyButton({ text }: { text: string }) {6export const MessageCopyButton = memo(function MessageCopyButton({ text }: { text: string }) {
6 const [copied, setCopied] = useState(false);7 const { copyToClipboard, isCopied } = useCopyToClipboard();
7
8 const handleCopy = useCallback(() => {
9 void navigator.clipboard.writeText(text);
10 setCopied(true);
11 setTimeout(() => setCopied(false), 2000);
12 }, [text]);
138
14 return (9 return (
15 <Button type="button" size="xs" variant="outline" onClick={handleCopy} title="Copy message">10 <Button
16 {copied ? <CheckIcon className="size-3 text-success" /> : <CopyIcon className="size-3" />}11 type="button"
12 size="xs"
13 variant="outline"
14 onClick={() => copyToClipboard(text)}
15 title="Copy message"
16 >
17 {isCopied ? <CheckIcon className="size-3 text-success" /> : <CopyIcon className="size-3" />}
17 </Button>18 </Button>
18 );19 );
19});20});
apps/web/src/components/ui/sidebar.tsx+2-2
...@@ -17,7 +17,7 @@ import {...@@ -17,7 +17,7 @@ import {
17} from "~/components/ui/sheet";17} from "~/components/ui/sheet";
18import { Skeleton } from "~/components/ui/skeleton";18import { Skeleton } from "~/components/ui/skeleton";
19import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";19import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";
20import { useMediaQuery } from "~/hooks/useMediaQuery";20import { useIsMobile } from "~/hooks/useMediaQuery";
21import { getLocalStorageItem, setLocalStorageItem } from "~/hooks/useLocalStorage";21import { getLocalStorageItem, setLocalStorageItem } from "~/hooks/useLocalStorage";
22import { Schema } from "effect";22import { Schema } from "effect";
2323
...@@ -98,7 +98,7 @@ function SidebarProvider({...@@ -98,7 +98,7 @@ function SidebarProvider({
98 open?: boolean;98 open?: boolean;
99 onOpenChange?: (open: boolean) => void;99 onOpenChange?: (open: boolean) => void;
100}) {100}) {
101 const isMobile = useMediaQuery("(max-width: 767px)");101 const isMobile = useIsMobile();
102 const [openMobile, setOpenMobile] = React.useState(false);102 const [openMobile, setOpenMobile] = React.useState(false);
103103
104 // This is the internal state of the sidebar.104 // This is the internal state of the sidebar.
apps/web/src/hooks/useCopyToClipboard.ts created+66
...@@ -0,0 +1,66 @@
1import * as React from "react";
2
3export function useCopyToClipboard<TContext = void>({
4 timeout = 2000,
5 onCopy,
6 onError,
7}: {
8 timeout?: number;
9 onCopy?: (ctx: TContext) => void;
10 onError?: (error: Error, ctx: TContext) => void;
11} = {}): { copyToClipboard: (value: string, ctx: TContext) => void; isCopied: boolean } {
12 const [isCopied, setIsCopied] = React.useState(false);
13 const timeoutIdRef = React.useRef<NodeJS.Timeout | null>(null);
14 const onCopyRef = React.useRef(onCopy);
15 const onErrorRef = React.useRef(onError);
16 const timeoutRef = React.useRef(timeout);
17
18 onCopyRef.current = onCopy;
19 onErrorRef.current = onError;
20 timeoutRef.current = timeout;
21
22 const copyToClipboard = React.useCallback((value: string, ctx: TContext): void => {
23 if (typeof window === "undefined" || !navigator.clipboard?.writeText) {
24 onErrorRef.current?.(new Error("Clipboard API unavailable."), ctx);
25 return;
26 }
27
28 if (!value) return;
29
30 navigator.clipboard.writeText(value).then(
31 () => {
32 if (timeoutIdRef.current) {
33 clearTimeout(timeoutIdRef.current);
34 }
35 setIsCopied(true);
36
37 onCopyRef.current?.(ctx);
38
39 if (timeoutRef.current !== 0) {
40 timeoutIdRef.current = setTimeout(() => {
41 setIsCopied(false);
42 timeoutIdRef.current = null;
43 }, timeoutRef.current);
44 }
45 },
46 (error) => {
47 if (onErrorRef.current) {
48 onErrorRef.current(error, ctx);
49 } else {
50 console.error(error);
51 }
52 },
53 );
54 }, []);
55
56 // Cleanup timeout on unmount
57 React.useEffect(() => {
58 return (): void => {
59 if (timeoutIdRef.current) {
60 clearTimeout(timeoutIdRef.current);
61 }
62 };
63 }, []);
64
65 return { copyToClipboard, isCopied };
66}
apps/web/src/hooks/useMediaQuery.ts+79-19
...@@ -1,27 +1,87 @@...@@ -1,27 +1,87 @@
1import { useEffect, useState } from "react";1import { useCallback, useSyncExternalStore } from "react";
22
3function getMediaQueryMatch(query: string): boolean {3const BREAKPOINTS = {
4 if (typeof window === "undefined") {4 "2xl": 1536,
5 return false;5 "3xl": 1600,
6 "4xl": 2000,
7 lg: 1024,
8 md: 768,
9 sm: 640,
10 xl: 1280,
11} as const;
12
13type Breakpoint = keyof typeof BREAKPOINTS;
14
15type BreakpointQuery = Breakpoint | `max-${Breakpoint}` | `${Breakpoint}:max-${Breakpoint}`;
16
17function resolveMin(value: Breakpoint | number): string {
18 const px = typeof value === "number" ? value : BREAKPOINTS[value];
19 return `(min-width: ${px}px)`;
20}
21
22function resolveMax(value: Breakpoint | number): string {
23 const px = typeof value === "number" ? value : BREAKPOINTS[value];
24 return `(max-width: ${px - 1}px)`;
25}
26
27function parseQuery(query: BreakpointQuery | MediaQueryInput | (string & {})): string {
28 if (typeof query !== "string") {
29 const parts: string[] = [];
30 if (query.min != null) parts.push(resolveMin(query.min));
31 if (query.max != null) parts.push(resolveMax(query.max));
32 if (query.pointer === "coarse") parts.push("(pointer: coarse)");
33 if (query.pointer === "fine") parts.push("(pointer: fine)");
34 if (parts.length === 0) return "(min-width: 0px)";
35 return parts.join(" and ");
36 }
37
38 if (query.startsWith("(")) return query;
39
40 const parts: string[] = [];
41 for (const segment of query.split(":")) {
42 if (segment.startsWith("max-")) {
43 const bp = segment.slice(4);
44 if (bp in BREAKPOINTS) parts.push(resolveMax(bp as Breakpoint));
45 } else if (segment in BREAKPOINTS) {
46 parts.push(resolveMin(segment as Breakpoint));
47 }
6 }48 }
7 return window.matchMedia(query).matches;49
50 return parts.length > 0 ? parts.join(" and ") : query;
51}
52
53function getServerSnapshot(): boolean {
54 return false;
8}55}
956
10export function useMediaQuery(query: string): boolean {57export type MediaQueryInput = {
11 const [matches, setMatches] = useState(() => getMediaQueryMatch(query));58 min?: Breakpoint | number;
59 max?: Breakpoint | number;
60 /** Touch-like input (finger). Use "fine" for mouse/trackpad. */
61 pointer?: "coarse" | "fine";
62};
63
64export function useMediaQuery(query: BreakpointQuery | MediaQueryInput | (string & {})): boolean {
65 const mediaQuery = parseQuery(query);
1266
13 useEffect(() => {67 const subscribe = useCallback(
14 const mediaQueryList = window.matchMedia(query);68 (callback: () => void) => {
15 const handleChange = () => {69 if (typeof window === "undefined") return () => {};
16 setMatches(mediaQueryList.matches);70 const mql = window.matchMedia(mediaQuery);
17 };71 mql.addEventListener("change", callback);
72 return () => mql.removeEventListener("change", callback);
73 },
74 [mediaQuery],
75 );
1876
19 setMatches(mediaQueryList.matches);77 const getSnapshot = useCallback(() => {
20 mediaQueryList.addEventListener("change", handleChange);78 if (typeof window === "undefined") return false;
21 return () => {79 return window.matchMedia(mediaQuery).matches;
22 mediaQueryList.removeEventListener("change", handleChange);80 }, [mediaQuery]);
23 };81
24 }, [query]);82 return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
83}
2584
26 return matches;85export function useIsMobile(): boolean {
86 return useMediaQuery("max-md");
27}87}
apps/web/src/routes/_chat.$threadId.tsx+1-1
...@@ -221,7 +221,7 @@ function ChatThreadRouteView() {...@@ -221,7 +221,7 @@ function ChatThreadRouteView() {
221 if (!shouldUseDiffSheet) {221 if (!shouldUseDiffSheet) {
222 return (222 return (
223 <>223 <>
224 <SidebarInset className="h-dvh min-h-0 overflow-hidden overscroll-y-none bg-background text-foreground">224 <SidebarInset className="h-dvh min-h-0 overflow-hidden overscroll-y-none bg-background text-foreground">
225 <ChatView key={threadId} threadId={threadId} />225 <ChatView key={threadId} threadId={threadId} />
226 </SidebarInset>226 </SidebarInset>
227 <DiffPanelInlineSidebar227 <DiffPanelInlineSidebar
bun.lock+5-5
...@@ -262,13 +262,13 @@...@@ -262,13 +262,13 @@
262262
263 "@effect/language-service": ["@effect/language-service@0.75.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-g9xD2tAQgRFpYC2YgpZq02VeSL5fBbFJ0B/g1o+14NuNmwtaYJc7SjiLWAA9eyhJHosNrn6h1Ye+Kx6j5mN0AA=="],263 "@effect/language-service": ["@effect/language-service@0.75.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-g9xD2tAQgRFpYC2YgpZq02VeSL5fBbFJ0B/g1o+14NuNmwtaYJc7SjiLWAA9eyhJHosNrn6h1Ye+Kx6j5mN0AA=="],
264264
265 "@effect/platform-node": ["@effect/platform-node@https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@8881a9b", { "dependencies": { "@effect/platform-node-shared": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node-shared@8881a9b606d84a6f5eb6615279138322984f5368", "mime": "^4.1.0", "undici": "^7.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.25", "ioredis": "^5.7.0" } }, "sha512-lOVb9aLpFnbkG4dWxY8lSTXihldd6kbcFI936X5UMvHz0HAYGrxsu1a+kHt/rYjiw1gjCmmnU6pE2pwKfO9erg=="],265 "@effect/platform-node": ["@effect/platform-node@https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@8881a9b", { "dependencies": { "@effect/platform-node-shared": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node-shared@8881a9b606d84a6f5eb6615279138322984f5368", "mime": "^4.1.0", "undici": "^7.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.25", "ioredis": "^5.7.0" } }],
266266
267 "@effect/platform-node-shared": ["@effect/platform-node-shared@https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node-shared@8881a9b606d84a6f5eb6615279138322984f5368", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.25" } }, "sha512-73FDHSTVLh/ZHeWrHFEbnCbjFE9gKi1YdJ9LEatDj0doZ8ouuTJW2BbqLDwMEfSf4zNQrxMEg+Jj/pb612HyWg=="],267 "@effect/platform-node-shared": ["@effect/platform-node-shared@https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node-shared@8881a9b606d84a6f5eb6615279138322984f5368", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.25" } }],
268268
269 "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@8881a9b", { "peerDependencies": { "effect": "^4.0.0-beta.25" } }, "sha512-7Tcfkx/NfQILRHyO5c52I8NYrg6jBcxDAguTYx7tgbGHYU9WPQRGh/AkcRgQ+1fsUGjWdojSfd+WGb/r4ltYtQ=="],269 "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@https://pkg.pr.new/Effect-TS/effect-smol/@effect/sql-sqlite-bun@8881a9b", { "peerDependencies": { "effect": "^4.0.0-beta.25" } }],
270270
271 "@effect/vitest": ["@effect/vitest@https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@8881a9b", { "peerDependencies": { "effect": "^4.0.0-beta.25", "vitest": "^3.0.0 || ^4.0.0" } }, "sha512-QQxLK156FZVPGYqSUPnEcybDm6e45KLcXb30XcimIKbIxuJLkuyxHQJW2hUu7lxjCoCotj2B4vc96gPKH/3Deg=="],271 "@effect/vitest": ["@effect/vitest@https://pkg.pr.new/Effect-TS/effect-smol/@effect/vitest@8881a9b", { "peerDependencies": { "effect": "^4.0.0-beta.25", "vitest": "^3.0.0 || ^4.0.0" } }],
272272
273 "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="],273 "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="],
274274
...@@ -1010,7 +1010,7 @@...@@ -1010,7 +1010,7 @@
10101010
1011 "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],1011 "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
10121012
1013 "effect": ["effect@https://pkg.pr.new/Effect-TS/effect-smol/effect@8881a9b", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-tZcsKgsLpQ5p1T5/duJHebWAqyG8owTuJdASj/2V+WPMuDlnB+uKvetMMXJsrNOyElUb3lVFeF2Mq00BbCnL9g=="],1013 "effect": ["effect@https://pkg.pr.new/Effect-TS/effect-smol/effect@8881a9b", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }],
10141014
1015 "electron": ["electron@40.6.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-ett8W+yOFGDuM0vhJMamYSkrbV3LoaffzJd9GfjI96zRAxyrNqUSKqBpf/WGbQCweDxX2pkUCUfrv4wwKpsFZA=="],1015 "electron": ["electron@40.6.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-ett8W+yOFGDuM0vhJMamYSkrbV3LoaffzJd9GfjI96zRAxyrNqUSKqBpf/WGbQCweDxX2pkUCUfrv4wwKpsFZA=="],
10161016