From 7556f379f11d63e231d7fd21b2a4dfcbde67dbd6 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 10:14:32 +0900 Subject: [PATCH 01/33] =?UTF-8?q?fix:=20=ED=94=84=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=ED=95=9C=20=EC=A4=84=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/styles/components.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index 37360b3..701a35e 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1751,6 +1751,10 @@ button.ghost { .link-preview-body strong { font-size: 14px; color: var(--color-link-preview-strong); + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .link-preview-description { @@ -1763,7 +1767,9 @@ button.ghost { .link-preview-url { font-size: 12px; color: var(--color-link-preview-url); - word-break: break-all; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .status footer { From d11d1176d3c18ddf9b15389fcc671e786baadf44 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 10:30:41 +0900 Subject: [PATCH 02/33] =?UTF-8?q?fix:=20=ED=94=84=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=A0=95?= =?UTF-8?q?=EA=B7=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/ui/components/TimelineItem.tsx | 26 +++++++++++++++++++++++--- src/ui/styles/components.css | 3 +++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8908afa..31a135d 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite", "build": "vite build", + "dev:preview": "bun run build && bunx wrangler pages dev dist", "preview": "vite preview", "test": "bun test", "test:watch": "bun test --watch" diff --git a/src/ui/components/TimelineItem.tsx b/src/ui/components/TimelineItem.tsx index 9476df8..bdef017 100644 --- a/src/ui/components/TimelineItem.tsx +++ b/src/ui/components/TimelineItem.tsx @@ -53,6 +53,24 @@ const extractFirstUrl = (text: string): string | null => { return `https://${value}`; }; +const decodeHtmlEntities = (value: string): string => { + if (typeof document === "undefined") { + return value; + } + const textarea = document.createElement("textarea"); + textarea.innerHTML = value; + return textarea.value; +}; + +const normalizePreviewText = (value: string | null | undefined): string | null => { + if (!value) { + return null; + } + const decoded = decodeHtmlEntities(value); + const normalized = decoded.replace(/\s+/g, " ").trim(); + return normalized || null; +}; + const MediaVideo = ({ id, src, @@ -1209,14 +1227,16 @@ export const TimelineItem = ({ const data = (await response.json()) as | { url?: string; title?: string; description?: string | null; image?: string | null; error?: string } | undefined; - if (!data || data.error || !data.title || !data.url) { + const title = normalizePreviewText(data?.title); + const description = normalizePreviewText(data?.description); + if (!data || data.error || !title || !data.url) { previewCache.set(previewCandidate, null); return; } const card: LinkCard = { url: data.url, - title: data.title, - description: data.description ?? null, + title, + description, image: data.image ?? null }; previewCache.set(previewCandidate, card); diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index 701a35e..f2a430d 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1746,6 +1746,7 @@ button.ghost { gap: 6px; font-size: 13px; color: var(--color-link-preview-text); + min-width: 0; } .link-preview-body strong { @@ -1755,6 +1756,7 @@ button.ghost { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + max-width: 100%; } .link-preview-description { @@ -1770,6 +1772,7 @@ button.ghost { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + max-width: 100%; } .status footer { From fa3c393009dfd6182494922f9b01857bc92ee945 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 10:34:35 +0900 Subject: [PATCH 03/33] =?UTF-8?q?fix:=20=EC=9C=A0=ED=8A=9C=EB=B8=8C=20?= =?UTF-8?q?=ED=94=84=EB=A6=AC=EB=B7=B0=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/api/preview.ts | 44 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/functions/api/preview.ts b/functions/api/preview.ts index f385824..0d7e3e6 100644 --- a/functions/api/preview.ts +++ b/functions/api/preview.ts @@ -100,6 +100,37 @@ const isBlockedHostname = (hostname: string): boolean => { return false; }; +const isYouTubeHost = (hostname: string): boolean => { + const lower = hostname.toLowerCase(); + return lower === "youtu.be" || lower.endsWith("youtube.com"); +}; + +const fetchYouTubeOEmbed = async (targetUrl: string): Promise<{ title: string; image: string | null } | null> => { + try { + const oembedUrl = new URL("https://www.youtube.com/oembed"); + oembedUrl.searchParams.set("url", targetUrl); + oembedUrl.searchParams.set("format", "json"); + const response = await fetch(oembedUrl.toString(), { + headers: { + Accept: "application/json" + } + }); + if (!response.ok) { + return null; + } + const data = (await response.json()) as { title?: string; thumbnail_url?: string }; + if (!data.title) { + return null; + } + return { + title: data.title, + image: data.thumbnail_url ?? null + }; + } catch { + return null; + } +}; + const readResponseText = async (response: Response): Promise => { if (!response.body) { return response.text(); @@ -183,11 +214,20 @@ export const onRequestGet = async (context: { request: Request } & { env?: Env } const ogImageRaw = extractMetaTagContent(html, "property", "og:image"); const ogUrl = extractMetaTagContent(html, "property", "og:url"); const metaDescription = extractMetaTagContent(html, "name", "description"); - const title = ogTitle || extractTitle(html); + let title = ogTitle || extractTitle(html); const description = ogDescription || metaDescription; - const image = toAbsoluteUrl(ogImageRaw, targetUrl.toString()); + let image = toAbsoluteUrl(ogImageRaw, targetUrl.toString()); const canonicalUrl = toAbsoluteUrl(ogUrl, targetUrl.toString()) ?? targetUrl.toString(); + const shouldFetchYouTube = !title || title.trim() === "YouTube"; + if (shouldFetchYouTube && isYouTubeHost(targetUrl.hostname)) { + const oembed = await fetchYouTubeOEmbed(targetUrl.toString()); + if (oembed) { + title = title || oembed.title; + image = image || oembed.image; + } + } + if (!title) { return buildResponse({ error: "missing_title" }, 200, 300); } From fe85f70152f473a7529f53b87f15f1aad8e7c83b Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 10:35:15 +0900 Subject: [PATCH 04/33] =?UTF-8?q?fix:=20=ED=8A=B8=EC=9C=84=ED=84=B0=20?= =?UTF-8?q?=ED=94=84=EB=A6=AC=EB=B7=B0=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/api/preview.ts | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/functions/api/preview.ts b/functions/api/preview.ts index 0d7e3e6..0735132 100644 --- a/functions/api/preview.ts +++ b/functions/api/preview.ts @@ -105,6 +105,11 @@ const isYouTubeHost = (hostname: string): boolean => { return lower === "youtu.be" || lower.endsWith("youtube.com"); }; +const isTwitterHost = (hostname: string): boolean => { + const lower = hostname.toLowerCase(); + return lower === "twitter.com" || lower.endsWith(".twitter.com") || lower === "x.com" || lower.endsWith(".x.com"); +}; + const fetchYouTubeOEmbed = async (targetUrl: string): Promise<{ title: string; image: string | null } | null> => { try { const oembedUrl = new URL("https://www.youtube.com/oembed"); @@ -131,6 +136,45 @@ const fetchYouTubeOEmbed = async (targetUrl: string): Promise<{ title: string; i } }; +const fetchTwitterOEmbed = async ( + targetUrl: string +): Promise<{ title?: string; authorName?: string; image: string | null } | null> => { + try { + const oembedUrl = new URL("https://publish.twitter.com/oembed"); + oembedUrl.searchParams.set("url", targetUrl); + oembedUrl.searchParams.set("omit_script", "true"); + const response = await fetch(oembedUrl.toString(), { + headers: { + Accept: "application/json" + } + }); + if (!response.ok) { + return null; + } + const data = (await response.json()) as { title?: string; author_name?: string; thumbnail_url?: string }; + if (!data.title && !data.author_name) { + return null; + } + return { + title: data.title, + authorName: data.author_name, + image: data.thumbnail_url ?? null + }; + } catch { + return null; + } +}; + +const buildTwitterTitle = (title: string | undefined, authorName: string | undefined): string | null => { + if (title && title.trim()) { + return title.trim(); + } + if (authorName && authorName.trim()) { + return `${authorName.trim()}님의 게시물`; + } + return null; +}; + const readResponseText = async (response: Response): Promise => { if (!response.body) { return response.text(); @@ -228,6 +272,15 @@ export const onRequestGet = async (context: { request: Request } & { env?: Env } } } + const shouldFetchTwitter = !title || title.trim() === "Twitter" || title.trim() === "X"; + if (shouldFetchTwitter && isTwitterHost(targetUrl.hostname)) { + const oembed = await fetchTwitterOEmbed(targetUrl.toString()); + if (oembed) { + title = title || buildTwitterTitle(oembed.title, oembed.authorName); + image = image || oembed.image; + } + } + if (!title) { return buildResponse({ error: "missing_title" }, 200, 300); } From a3078b2511423efe454fd2559e732c4a07f1dc40 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 10:42:36 +0900 Subject: [PATCH 05/33] =?UTF-8?q?fix:=20=ED=8A=B8=EC=9C=84=ED=84=B0=20oEmb?= =?UTF-8?q?ed=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/api/preview.ts | 66 +++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/functions/api/preview.ts b/functions/api/preview.ts index 0735132..759b3e4 100644 --- a/functions/api/preview.ts +++ b/functions/api/preview.ts @@ -136,33 +136,57 @@ const fetchYouTubeOEmbed = async (targetUrl: string): Promise<{ title: string; i } }; +const extractTwitterOEmbedText = (html: string | undefined): string | null => { + if (!html) { + return null; + } + const match = html.match(/]*>([\s\S]*?)<\/p>/i); + if (!match) { + return null; + } + const stripped = match[1].replace(/<[^>]+>/g, "").trim(); + const decoded = decodeHtmlEntities(stripped); + return decoded || null; +}; + const fetchTwitterOEmbed = async ( targetUrl: string ): Promise<{ title?: string; authorName?: string; image: string | null } | null> => { - try { - const oembedUrl = new URL("https://publish.twitter.com/oembed"); - oembedUrl.searchParams.set("url", targetUrl); - oembedUrl.searchParams.set("omit_script", "true"); - const response = await fetch(oembedUrl.toString(), { - headers: { - Accept: "application/json" + const candidates = ["https://publish.twitter.com/oembed", "https://publish.x.com/oembed"]; + for (const baseUrl of candidates) { + try { + const oembedUrl = new URL(baseUrl); + oembedUrl.searchParams.set("url", targetUrl); + oembedUrl.searchParams.set("omit_script", "true"); + const response = await fetch(oembedUrl.toString(), { + headers: { + Accept: "application/json", + "User-Agent": "DeckLinkPreview/1.0" + } + }); + if (!response.ok) { + continue; } - }); - if (!response.ok) { - return null; - } - const data = (await response.json()) as { title?: string; author_name?: string; thumbnail_url?: string }; - if (!data.title && !data.author_name) { - return null; + const data = (await response.json()) as { + title?: string; + author_name?: string; + thumbnail_url?: string; + html?: string; + }; + const title = data.title ?? extractTwitterOEmbedText(data.html) ?? undefined; + if (!title && !data.author_name) { + continue; + } + return { + title, + authorName: data.author_name, + image: data.thumbnail_url ?? null + }; + } catch { + continue; } - return { - title: data.title, - authorName: data.author_name, - image: data.thumbnail_url ?? null - }; - } catch { - return null; } + return null; }; const buildTwitterTitle = (title: string | undefined, authorName: string | undefined): string | null => { From 2afe6fe2e298c82d975b68212c869e281a1433bc Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 10:51:51 +0900 Subject: [PATCH 06/33] =?UTF-8?q?chore:=20X=20=ED=94=84=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/api/preview.ts | 83 +++++----------------------------------- 1 file changed, 9 insertions(+), 74 deletions(-) diff --git a/functions/api/preview.ts b/functions/api/preview.ts index 759b3e4..2154bd7 100644 --- a/functions/api/preview.ts +++ b/functions/api/preview.ts @@ -13,6 +13,15 @@ const decodeHtmlEntities = (value: string): string => .replace(/"/g, '"') .replace(/'/g, "'"); +const normalizeInlineText = (value: string | null | undefined): string | null => { + if (!value) { + return null; + } + const decoded = decodeHtmlEntities(value); + const normalized = decoded.replace(/\s+/g, " ").trim(); + return normalized || null; +}; + const extractMetaTagContent = (html: string, attribute: "property" | "name", key: string): string | null => { const tagRegex = new RegExp(`]+${attribute}=["']${key}["'][^>]*>`, "i"); const match = html.match(tagRegex); @@ -105,10 +114,6 @@ const isYouTubeHost = (hostname: string): boolean => { return lower === "youtu.be" || lower.endsWith("youtube.com"); }; -const isTwitterHost = (hostname: string): boolean => { - const lower = hostname.toLowerCase(); - return lower === "twitter.com" || lower.endsWith(".twitter.com") || lower === "x.com" || lower.endsWith(".x.com"); -}; const fetchYouTubeOEmbed = async (targetUrl: string): Promise<{ title: string; image: string | null } | null> => { try { @@ -136,68 +141,6 @@ const fetchYouTubeOEmbed = async (targetUrl: string): Promise<{ title: string; i } }; -const extractTwitterOEmbedText = (html: string | undefined): string | null => { - if (!html) { - return null; - } - const match = html.match(/]*>([\s\S]*?)<\/p>/i); - if (!match) { - return null; - } - const stripped = match[1].replace(/<[^>]+>/g, "").trim(); - const decoded = decodeHtmlEntities(stripped); - return decoded || null; -}; - -const fetchTwitterOEmbed = async ( - targetUrl: string -): Promise<{ title?: string; authorName?: string; image: string | null } | null> => { - const candidates = ["https://publish.twitter.com/oembed", "https://publish.x.com/oembed"]; - for (const baseUrl of candidates) { - try { - const oembedUrl = new URL(baseUrl); - oembedUrl.searchParams.set("url", targetUrl); - oembedUrl.searchParams.set("omit_script", "true"); - const response = await fetch(oembedUrl.toString(), { - headers: { - Accept: "application/json", - "User-Agent": "DeckLinkPreview/1.0" - } - }); - if (!response.ok) { - continue; - } - const data = (await response.json()) as { - title?: string; - author_name?: string; - thumbnail_url?: string; - html?: string; - }; - const title = data.title ?? extractTwitterOEmbedText(data.html) ?? undefined; - if (!title && !data.author_name) { - continue; - } - return { - title, - authorName: data.author_name, - image: data.thumbnail_url ?? null - }; - } catch { - continue; - } - } - return null; -}; - -const buildTwitterTitle = (title: string | undefined, authorName: string | undefined): string | null => { - if (title && title.trim()) { - return title.trim(); - } - if (authorName && authorName.trim()) { - return `${authorName.trim()}님의 게시물`; - } - return null; -}; const readResponseText = async (response: Response): Promise => { if (!response.body) { @@ -296,14 +239,6 @@ export const onRequestGet = async (context: { request: Request } & { env?: Env } } } - const shouldFetchTwitter = !title || title.trim() === "Twitter" || title.trim() === "X"; - if (shouldFetchTwitter && isTwitterHost(targetUrl.hostname)) { - const oembed = await fetchTwitterOEmbed(targetUrl.toString()); - if (oembed) { - title = title || buildTwitterTitle(oembed.title, oembed.authorName); - image = image || oembed.image; - } - } if (!title) { return buildResponse({ error: "missing_title" }, 200, 300); From 6b9cd0486dcc428855a7d2fcf1ac9919d9f37c37 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 30 Jan 2026 11:04:47 +0900 Subject: [PATCH 07/33] =?UTF-8?q?fix:=20=EB=8B=B5=EA=B8=80=20=EB=A9=98?= =?UTF-8?q?=EC=85=98=20=EA=B3=B5=EB=B0=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 3427198..f90ba45 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -885,7 +885,7 @@ export const App = () => { setComposeAccountId(account.id); setReplyTarget(status); const formattedHandle = formatReplyHandle(status.accountHandle, status.accountUrl, account.instanceUrl); - setMentionSeed(`@${formattedHandle}`); + setMentionSeed(`@${formattedHandle} `); setSelectedStatus(null); }; From 53120c8309a69cbf5f3cd87ce53cfd30c40a1dee Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Mon, 2 Feb 2026 11:44:04 +0900 Subject: [PATCH 08/33] =?UTF-8?q?feat:=20=EC=84=B9=EC=85=98=EB=B3=84=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EC=84=A4=EC=A0=95=20=ED=8C=A8=EB=84=90?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 192 ++++++++++++++++---------- src/ui/components/ProfileModal.tsx | 13 +- src/ui/components/SettingsModal.tsx | 74 ---------- src/ui/components/StatusModal.tsx | 11 +- src/ui/components/TimelineSection.tsx | 168 +++++++++++++++++++--- src/ui/styles/components.css | 43 ++++++ src/ui/types/section.ts | 6 + 7 files changed, 330 insertions(+), 177 deletions(-) create mode 100644 src/ui/types/section.ts diff --git a/src/App.tsx b/src/App.tsx index f90ba45..5f09efe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,15 +19,55 @@ import { normalizeTimelineType } from "./ui/utils/timeline"; import { buildOptimisticReactionStatus, hasSameReactions } from "./ui/utils/reactions"; import { ColorScheme, ThemeMode, getStoredColorScheme, getStoredTheme, isColorScheme, isThemeMode } from "./ui/utils/theme"; import type { InfoModalType } from "./ui/types/info"; +import type { SectionDisplaySettings } from "./ui/types/section"; import logoUrl from "./ui/assets/textodon-icon-blue.png"; type Route = "home" | "terms" | "license" | "oss" | "shortcuts"; type SelectedTimelineStatus = { sectionId: string; statusId: string }; -type ProfileTarget = { status: Status; account: Account | null; zIndex: number }; +type ProfileTarget = { status: Status; account: Account | null; settings: SectionDisplaySettings; zIndex: number }; const SECTION_STORAGE_KEY = "textodon.sections"; const COMPOSE_ACCOUNT_KEY = "textodon.compose.accountId"; +const getStoredSectionSize = (): SectionDisplaySettings["sectionSize"] => { + try { + const stored = localStorage.getItem("textodon.sectionSize"); + if (stored === "medium" || stored === "large" || stored === "small") { + return stored; + } + } catch { + /* noop */ + } + return "small"; +}; + +const getDefaultSectionSettings = (): SectionDisplaySettings => { + let showProfileImages = true; + let showCustomEmojis = true; + let showReactions = true; + try { + showProfileImages = localStorage.getItem("textodon.profileImages") !== "off"; + } catch { + /* noop */ + } + try { + showCustomEmojis = localStorage.getItem("textodon.customEmojis") !== "off"; + } catch { + /* noop */ + } + try { + showReactions = localStorage.getItem("textodon.reactions") !== "off"; + } catch { + /* noop */ + } + return { + showProfileImages, + showCustomEmojis, + showReactions, + sectionSize: getStoredSectionSize() + }; +}; + const parseRoute = (): Route => { const hash = window.location.hash.replace(/^#/, ""); if (!hash || hash === "/") { @@ -44,22 +84,6 @@ const parseRoute = (): Route => { export const App = () => { const [themeMode, setThemeMode] = useState(() => getStoredTheme()); const [colorScheme, setColorScheme] = useState(() => getStoredColorScheme()); - const [sectionSize, setSectionSize] = useState<"small" | "medium" | "large">(() => { - const stored = localStorage.getItem("textodon.sectionSize"); - if (stored === "medium" || stored === "large" || stored === "small") { - return stored; - } - return "small"; - }); - const [showProfileImages, setShowProfileImages] = useState(() => { - return localStorage.getItem("textodon.profileImages") !== "off"; - }); - const [showCustomEmojis, setShowCustomEmojis] = useState(() => { - return localStorage.getItem("textodon.customEmojis") !== "off"; - }); - const [showMisskeyReactions, setShowMisskeyReactions] = useState(() => { - return localStorage.getItem("textodon.reactions") !== "off"; - }); const [showPomodoro, setShowPomodoro] = useState(() => { return localStorage.getItem("textodon.pomodoro") === "on"; }); @@ -91,6 +115,7 @@ export const App = () => { if (raw) { const parsed = JSON.parse(raw) as Array>; if (Array.isArray(parsed) && parsed.length > 0) { + const defaults = getDefaultSectionSettings(); return parsed.map((item) => ({ id: item.id || crypto.randomUUID(), accountId: item.accountId ?? null, @@ -100,13 +125,20 @@ export const App = () => { ? accountsState.accounts.find((account) => account.id === item.accountId)?.platform ?? null : null, false - ) + ), + settings: { + showProfileImages: item.settings?.showProfileImages ?? defaults.showProfileImages, + showCustomEmojis: item.settings?.showCustomEmojis ?? defaults.showCustomEmojis, + showReactions: item.settings?.showReactions ?? defaults.showReactions, + sectionSize: item.settings?.sectionSize ?? defaults.sectionSize + } })); } } } catch { /* noop */ } + const defaults = getDefaultSectionSettings(); if (accountsState.accounts.length === 0) { return []; } @@ -114,7 +146,8 @@ export const App = () => { { id: crypto.randomUUID(), accountId: accountsState.activeAccountId ?? accountsState.accounts[0]?.id ?? null, - timelineType: "home" + timelineType: "home", + settings: { ...defaults } } ]; }); @@ -135,6 +168,9 @@ export const App = () => { ); const [replyTarget, setReplyTarget] = useState(null); const [selectedStatus, setSelectedStatus] = useState(null); + const [selectedStatusSettings, setSelectedStatusSettings] = useState(() => + getDefaultSectionSettings() + ); const [selectedStatusThreadAccount, setSelectedStatusThreadAccount] = useState(null); const [selectedTimelineStatus, setSelectedTimelineStatus] = useState(null); const [profileTargets, setProfileTargets] = useState([]); @@ -380,11 +416,6 @@ export const App = () => { localStorage.setItem("textodon.colorScheme", colorScheme); }, [colorScheme]); - useEffect(() => { - document.documentElement.dataset.sectionSize = sectionSize; - localStorage.setItem("textodon.sectionSize", sectionSize); - }, [sectionSize]); - useEffect(() => { try { if (composeAccountId) { @@ -397,18 +428,6 @@ export const App = () => { } }, [composeAccountId]); - useEffect(() => { - localStorage.setItem("textodon.profileImages", showProfileImages ? "on" : "off"); - }, [showProfileImages]); - - useEffect(() => { - localStorage.setItem("textodon.customEmojis", showCustomEmojis ? "on" : "off"); - }, [showCustomEmojis]); - - useEffect(() => { - localStorage.setItem("textodon.reactions", showMisskeyReactions ? "on" : "off"); - }, [showMisskeyReactions]); - useEffect(() => { localStorage.setItem("textodon.pomodoro", showPomodoro ? "on" : "off"); }, [showPomodoro]); @@ -823,11 +842,17 @@ export const App = () => { (account) => !previousAccountIds.current.has(account.id) ); if (addedAccounts.length > 0) { + const defaults = getDefaultSectionSettings(); setSections((current) => { const next = [...current]; addedAccounts.forEach((account) => { if (!next.some((section) => section.accountId === account.id)) { - next.push({ id: crypto.randomUUID(), accountId: account.id, timelineType: "home" }); + next.push({ + id: crypto.randomUUID(), + accountId: account.id, + timelineType: "home", + settings: { ...defaults } + }); } }); return next; @@ -889,16 +914,27 @@ export const App = () => { setSelectedStatus(null); }; - const handleStatusClick = (status: Status, columnAccount: Account | null) => { + const handleStatusClick = ( + status: Status, + columnAccount: Account | null, + settings: SectionDisplaySettings + ) => { setSelectedStatus(status); setStatusModalZIndex(nextModalZIndexRef.current++); setSelectedStatusThreadAccount(columnAccount); + setSelectedStatusSettings(settings); }; - const handleProfileOpen = useCallback((target: Status, columnAccount: Account | null) => { - const zIndex = nextModalZIndexRef.current++; - setProfileTargets((current) => [...current, { status: target, account: columnAccount, zIndex }]); - }, []); + const handleProfileOpen = useCallback( + (target: Status, columnAccount: Account | null, settings: SectionDisplaySettings) => { + const zIndex = nextModalZIndexRef.current++; + setProfileTargets((current) => [ + ...current, + { status: target, account: columnAccount, settings, zIndex } + ]); + }, + [] + ); const handleCloseProfileModal = useCallback((index?: number) => { setProfileTargets((current) => { @@ -961,15 +997,17 @@ export const App = () => { /> ); - const addSectionAt = (index: number) => { + const addSectionAt = (index: number, baseSettings?: SectionDisplaySettings) => { const defaultAccountId = composeAccountId ?? accountsState.accounts[0]?.id ?? null; + const settings = baseSettings ?? getDefaultSectionSettings(); setSections((current) => { const next = [...current]; const insertIndex = Math.max(0, Math.min(index, next.length)); next.splice(insertIndex, 0, { id: crypto.randomUUID(), accountId: defaultAccountId, - timelineType: "home" + timelineType: "home", + settings: { ...settings } }); return next; }); @@ -984,7 +1022,8 @@ export const App = () => { addSectionAt(sections.length); return; } - addSectionAt(direction === "left" ? index : index + 1); + const baseSettings = sections[index]?.settings; + addSectionAt(direction === "left" ? index : index + 1, baseSettings); }; const moveSection = (sectionId: string, direction: "left" | "right") => { @@ -1041,6 +1080,19 @@ export const App = () => { ); }; + const updateSectionSettings = useCallback( + (sectionId: string, updates: Partial) => { + setSections((current) => + current.map((section) => + section.id === sectionId + ? { ...section, settings: { ...section.settings, ...updates } } + : section + ) + ); + }, + [] + ); + useEffect(() => { try { localStorage.setItem(SECTION_STORAGE_KEY, JSON.stringify(sections)); @@ -1217,17 +1269,17 @@ export const App = () => { className="timeline-board" ref={timelineBoardRef} > - {sections.map((section, index) => { - const sectionAccount = - section.accountId - ? accountsState.accounts.find((account) => account.id === section.accountId) ?? null - : null; - const shouldShowReactions = showMisskeyReactions; - const selectedStatusId = - selectedTimelineStatus?.sectionId === section.id - ? selectedTimelineStatus.statusId - : null; - return ( + {sections.map((section, index) => { + const sectionAccount = + section.accountId + ? accountsState.accounts.find((account) => account.id === section.accountId) ?? null + : null; + const shouldShowReactions = section.settings.showReactions; + const selectedStatusId = + selectedTimelineStatus?.sectionId === section.id + ? selectedTimelineStatus.statusId + : null; + return ( { onMoveSection={moveSection} onTimelineItemsChange={handleTimelineItemsChange} onSelectStatus={handleSelectStatus} + onUpdateSectionSettings={updateSectionSettings} canMoveLeft={index > 0} canMoveRight={index < sections.length - 1} canRemoveSection={sections.length > 1} timelineType={section.timelineType} - showProfileImage={showProfileImages} - showCustomEmojis={showCustomEmojis} - showReactions={shouldShowReactions} registerTimelineListener={registerTimelineListener} unregisterTimelineListener={unregisterTimelineListener} registerTimelineShortcutHandler={registerTimelineShortcutHandler} @@ -1331,14 +1381,6 @@ export const App = () => { setColorScheme(value); } }} - showProfileImages={showProfileImages} - onToggleProfileImages={setShowProfileImages} - showCustomEmojis={showCustomEmojis} - onToggleCustomEmojis={setShowCustomEmojis} - showMisskeyReactions={showMisskeyReactions} - onToggleMisskeyReactions={setShowMisskeyReactions} - sectionSize={sectionSize} - onSectionSizeChange={setSectionSize} showPomodoro={showPomodoro} onTogglePomodoro={setShowPomodoro} pomodoroFocus={pomodoroFocus} @@ -1359,11 +1401,12 @@ export const App = () => { isTopmost={index === profileTargets.length - 1} onClose={() => handleCloseProfileModal(index)} onReply={handleReply} - onStatusClick={(status) => handleStatusClick(status, target.account)} + onStatusClick={(status, account, settings) => handleStatusClick(status, account, settings)} onProfileClick={handleProfileOpen} - showProfileImage={showProfileImages} - showCustomEmojis={showCustomEmojis} - showReactions={showMisskeyReactions} + showProfileImage={target.settings.showProfileImages} + showCustomEmojis={target.settings.showCustomEmojis} + showReactions={target.settings.showReactions} + sectionSettings={target.settings} /> ))} @@ -1451,9 +1494,10 @@ export const App = () => { } activeAccountHandle={composeAccount?.handle ?? ""} activeAccountUrl={composeAccount?.url ?? null} - showProfileImage={showProfileImages} - showCustomEmojis={showCustomEmojis} - showReactions={showMisskeyReactions} + showProfileImage={selectedStatusSettings.showProfileImages} + showCustomEmojis={selectedStatusSettings.showCustomEmojis} + showReactions={selectedStatusSettings.showReactions} + sectionSettings={selectedStatusSettings} /> ) : null} diff --git a/src/ui/components/ProfileModal.tsx b/src/ui/components/ProfileModal.tsx index 54bcf16..723990e 100644 --- a/src/ui/components/ProfileModal.tsx +++ b/src/ui/components/ProfileModal.tsx @@ -15,6 +15,7 @@ import { renderMarkdown } from "../utils/markdown"; import { useClickOutside } from "../hooks/useClickOutside"; import { useToast } from "../state/ToastContext"; import { TimelineItem } from "./TimelineItem"; +import type { SectionDisplaySettings } from "../types/section"; const PAGE_SIZE = 20; @@ -146,7 +147,8 @@ export const ProfileModal = ({ onProfileClick, showProfileImage, showCustomEmojis, - showReactions + showReactions, + sectionSettings }: { status: Status; account: Account | null; @@ -155,11 +157,12 @@ export const ProfileModal = ({ isTopmost: boolean; onClose: () => void; onReply: (status: Status, account: Account | null) => void; - onStatusClick: (status: Status) => void; - onProfileClick: (status: Status, account: Account | null) => void; + onStatusClick: (status: Status, account: Account | null, settings: SectionDisplaySettings) => void; + onProfileClick: (status: Status, account: Account | null, settings: SectionDisplaySettings) => void; showProfileImage: boolean; showCustomEmojis: boolean; showReactions: boolean; + sectionSettings: SectionDisplaySettings; }) => { const [profile, setProfile] = useState(null); const [profileError, setProfileError] = useState(null); @@ -985,8 +988,8 @@ export const ProfileModal = ({ onToggleBookmark={handleToggleBookmark} onDelete={handleDeleteStatus} onReact={handleReact} - onStatusClick={onStatusClick} - onProfileClick={(target) => onProfileClick(target, account)} + onStatusClick={(target) => onStatusClick(target, account, sectionSettings)} + onProfileClick={(target) => onProfileClick(target, account, sectionSettings)} onUpdateStatus={updateItem} activeHandle={activeHandle} activeAccountHandle={account?.handle ?? ""} diff --git a/src/ui/components/SettingsModal.tsx b/src/ui/components/SettingsModal.tsx index 37f6395..ac3cc08 100644 --- a/src/ui/components/SettingsModal.tsx +++ b/src/ui/components/SettingsModal.tsx @@ -16,14 +16,6 @@ type SettingsModalProps = { onThemeChange: (value: string) => void; colorScheme: ColorScheme; onColorSchemeChange: (value: string) => void; - showProfileImages: boolean; - onToggleProfileImages: (value: boolean) => void; - showCustomEmojis: boolean; - onToggleCustomEmojis: (value: boolean) => void; - showMisskeyReactions: boolean; - onToggleMisskeyReactions: (value: boolean) => void; - sectionSize: "small" | "medium" | "large"; - onSectionSizeChange: (value: "small" | "medium" | "large") => void; showPomodoro: boolean; onTogglePomodoro: (value: boolean) => void; pomodoroFocus: number; @@ -48,14 +40,6 @@ export const SettingsModal = ({ onThemeChange, colorScheme, onColorSchemeChange, - showProfileImages, - onToggleProfileImages, - showCustomEmojis, - onToggleCustomEmojis, - showMisskeyReactions, - onToggleMisskeyReactions, - sectionSize, - onSectionSizeChange, showPomodoro, onTogglePomodoro, pomodoroFocus, @@ -153,64 +137,6 @@ export const SettingsModal = ({ -
-
- 프로필 이미지 표시 -

피드에서 사용자 프로필 이미지를 보여줍니다.

-
- -
-
-
- 커스텀 이모지 표시 -

사용자 이름과 본문에 커스텀 이모지를 표시합니다.

-
- -
-
-
- 리액션 표시 -

리액션 정보를 지원하는 서버에서 받은 리액션을 보여줍니다.

-
- -
-
-
- 섹션 폭 -

타임라인 섹션의 가로 폭을 조절합니다.

-
- -
뽀모도로 타이머 diff --git a/src/ui/components/StatusModal.tsx b/src/ui/components/StatusModal.tsx index 8e58b12..5471459 100644 --- a/src/ui/components/StatusModal.tsx +++ b/src/ui/components/StatusModal.tsx @@ -4,6 +4,7 @@ import type { MastodonApi } from "../../services/MastodonApi"; import { TimelineItem } from "./TimelineItem"; import BoostIcon from "../assets/boost-icon.svg?react"; import { useToast } from "../state/ToastContext"; +import type { SectionDisplaySettings } from "../types/section"; export const StatusModal = ({ status, @@ -24,7 +25,8 @@ export const StatusModal = ({ activeAccountUrl, showProfileImage, showCustomEmojis, - showReactions + showReactions, + sectionSettings }: { status: Status; account: Account | null; @@ -37,7 +39,7 @@ export const StatusModal = ({ onToggleReblog: (status: Status) => void; onToggleBookmark: (status: Status) => void; onDelete?: (status: Status) => void; - onProfileClick?: (status: Status, account: Account | null) => void; + onProfileClick?: (status: Status, account: Account | null, settings: SectionDisplaySettings) => void; onUpdateStatus?: (status: Status) => void; activeHandle: string; activeAccountHandle: string; @@ -45,6 +47,7 @@ export const StatusModal = ({ showProfileImage: boolean; showCustomEmojis: boolean; showReactions: boolean; + sectionSettings: SectionDisplaySettings; }) => { const displayStatus = status.reblog ?? status; const boostedBy = status.reblog ? status.boostedBy : null; @@ -116,9 +119,9 @@ export const StatusModal = ({ if (!onProfileClick) { return; } - onProfileClick(target, threadAccount ?? account ?? null); + onProfileClick(target, threadAccount ?? account ?? null, sectionSettings); }, - [account, onProfileClick, threadAccount] + [account, onProfileClick, sectionSettings, threadAccount] ); // 스레드 컨텍스트 상태 diff --git a/src/ui/components/TimelineSection.tsx b/src/ui/components/TimelineSection.tsx index 41b3a06..256d720 100644 --- a/src/ui/components/TimelineSection.tsx +++ b/src/ui/components/TimelineSection.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { Account, ReactionInput, Status, TimelineType } from "../../domain/types"; +import type { SectionDisplaySettings } from "../types/section"; import type { AccountsState, AppServices } from "../state/AppContext"; import { useTimeline } from "../hooks/useTimeline"; import { useClickOutside } from "../hooks/useClickOutside"; @@ -9,7 +10,12 @@ import { TimelineItem } from "./TimelineItem"; import { formatHandle, normalizeInstanceUrl } from "../utils/account"; import { getTimelineLabel, getTimelineOptions } from "../utils/timeline"; -export type TimelineSectionConfig = { id: string; accountId: string | null; timelineType: TimelineType }; +export type TimelineSectionConfig = { + id: string; + accountId: string | null; + timelineType: TimelineType; + settings: SectionDisplaySettings; +}; type TimelineSectionProps = { section: TimelineSectionConfig; @@ -22,22 +28,20 @@ type TimelineSectionProps = { onAddSectionRight: (sectionId: string) => void; onRemoveSection: (sectionId: string) => void; onReply: (status: Status, account: Account | null) => void; - onStatusClick: (status: Status, columnAccount: Account | null) => void; + onStatusClick: (status: Status, columnAccount: Account | null, settings: SectionDisplaySettings) => void; onReact: (account: Account | null, status: Status, reaction: ReactionInput) => void; - onProfileClick: (status: Status, account: Account | null) => void; + onProfileClick: (status: Status, account: Account | null, settings: SectionDisplaySettings) => void; onError: (message: string | null) => void; onMoveSection: (sectionId: string, direction: "left" | "right") => void; onScrollToSection: (sectionId: string) => void; onCloseStatusModal: () => void; onTimelineItemsChange: (sectionId: string, items: Status[]) => void; onSelectStatus: (sectionId: string, statusId: string) => void; + onUpdateSectionSettings: (sectionId: string, updates: Partial) => void; canMoveLeft: boolean; canMoveRight: boolean; canRemoveSection: boolean; timelineType: TimelineType; - showProfileImage: boolean; - showCustomEmojis: boolean; - showReactions: boolean; registerTimelineListener: (accountId: string, listener: (status: Status) => void) => void; unregisterTimelineListener: (accountId: string, listener: (status: Status) => void) => void; registerTimelineShortcutHandler: (sectionId: string, handler: ((event: KeyboardEvent) => boolean) | null) => void; @@ -45,6 +49,12 @@ type TimelineSectionProps = { selectedStatusId: string | null; }; +const SECTION_SIZE_MAP: Record = { + small: { width: 440, maxWidth: 520 }, + medium: { width: 550, maxWidth: 650 }, + large: { width: 660, maxWidth: 780 } +}; + const TimelineIcon = ({ timeline }: { timeline: TimelineType | string }) => { switch (timeline) { case "divider-before-bookmarks": @@ -133,12 +143,10 @@ export const TimelineSection = ({ canMoveRight, canRemoveSection, timelineType, - showProfileImage, - showCustomEmojis, - showReactions, registerTimelineListener, unregisterTimelineListener, registerTimelineShortcutHandler, + onUpdateSectionSettings, columnRef, selectedStatusId }: TimelineSectionProps) => { @@ -160,6 +168,7 @@ export const TimelineSection = ({ const menuRef = useRef(null); const timelineMenuRef = useRef(null); const notificationMenuRef = useRef(null); + const settingsPanelRef = useRef(null); const scrollRef = useRef(null); const notificationScrollRef = useRef(null); const accountSummaryRef = useRef(null); @@ -167,11 +176,20 @@ export const TimelineSection = ({ const [menuOpen, setMenuOpen] = useState(false); const [timelineMenuOpen, setTimelineMenuOpen] = useState(false); const [notificationsOpen, setNotificationsOpen] = useState(false); + const [settingsOpen, setSettingsOpen] = useState(false); const [notificationCount, setNotificationCount] = useState(0); const [isAtTop, setIsAtTop] = useState(true); const [highlightedTimelineIndex, setHighlightedTimelineIndex] = useState(null); const [highlightedSectionMenuIndex, setHighlightedSectionMenuIndex] = useState(null); const [highlightedNotificationIndex, setHighlightedNotificationIndex] = useState(null); + const { showProfileImages, showCustomEmojis, showReactions, sectionSize } = section.settings; + const columnStyle = useMemo(() => { + const sizeConfig = SECTION_SIZE_MAP[sectionSize]; + return { + "--timeline-column-width": `${sizeConfig.width}px`, + "--timeline-column-max-width": `${sizeConfig.maxWidth}px` + } as React.CSSProperties; + }, [sectionSize]); const { showToast } = useToast(); const timelineOptions = useMemo(() => getTimelineOptions(account?.platform, false), [account?.platform]); const actionableTimelineOptions = useMemo( @@ -277,6 +295,8 @@ export const TimelineSection = ({ useClickOutside(notificationMenuRef, notificationsOpen, () => setNotificationsOpen(false)); + useClickOutside(settingsPanelRef, settingsOpen, () => setSettingsOpen(false)); + useEffect(() => { if (!timelineMenuOpen) { setHighlightedTimelineIndex(null); @@ -488,6 +508,11 @@ export const TimelineSection = ({ setMenuOpen(false); return true; } + if (settingsOpen) { + event.preventDefault(); + setSettingsOpen(false); + return true; + } if (notificationsOpen) { event.preventDefault(); setNotificationsOpen(false); @@ -568,7 +593,7 @@ export const TimelineSection = ({ const status = notificationItems[highlightedNotificationIndex]; if (status) { event.preventDefault(); - onStatusClick(status, account); + onStatusClick(status, account, section.settings); return true; } return true; @@ -660,12 +685,12 @@ export const TimelineSection = ({ } if (key === "enter") { event.preventDefault(); - onStatusClick(selectedStatus, account); + onStatusClick(selectedStatus, account, section.settings); return true; } if (key === "p") { event.preventDefault(); - onProfileClick(selectedStatus, account); + onProfileClick(selectedStatus, account, section.settings); return true; } if (key === "a") { @@ -693,6 +718,7 @@ export const TimelineSection = ({ setTimelineMenuOpen(true); setMenuOpen(false); setNotificationsOpen(false); + setSettingsOpen(false); return true; } if (key === "g") { @@ -704,6 +730,7 @@ export const TimelineSection = ({ setNotificationsOpen((current) => !current); setTimelineMenuOpen(false); setMenuOpen(false); + setSettingsOpen(false); return true; } if (key === "m") { @@ -711,6 +738,7 @@ export const TimelineSection = ({ setMenuOpen(true); setTimelineMenuOpen(false); setNotificationsOpen(false); + setSettingsOpen(false); return true; } return false; @@ -732,6 +760,7 @@ export const TimelineSection = ({ onStatusClick, onTimelineChange, section.id, + settingsOpen, selectedStatusId, showReactions, timeline.items, @@ -740,7 +769,7 @@ export const TimelineSection = ({ ); useEffect(() => { - if (!timelineMenuOpen && !notificationsOpen && !menuOpen) { + if (!timelineMenuOpen && !notificationsOpen && !menuOpen && !settingsOpen) { return; } const onKeyDown = (event: KeyboardEvent) => { @@ -751,7 +780,7 @@ export const TimelineSection = ({ }; window.addEventListener("keydown", onKeyDown, { capture: true }); return () => window.removeEventListener("keydown", onKeyDown, { capture: true }); - }, [handleTimelineShortcuts, menuOpen, notificationsOpen, timelineMenuOpen]); + }, [handleTimelineShortcuts, menuOpen, notificationsOpen, settingsOpen, timelineMenuOpen]); useEffect(() => { registerTimelineShortcutHandler(section.id, handleTimelineShortcuts); @@ -763,6 +792,7 @@ export const TimelineSection = ({ className="timeline-column" ref={columnRef} data-section-id={section.id} + style={columnStyle} >
!current); setMenuOpen(false); setNotificationsOpen(false); + setSettingsOpen(false); }} disabled={!account} aria-label={timelineButtonLabel} @@ -853,6 +884,7 @@ export const TimelineSection = ({ setMenuOpen(false); setTimelineMenuOpen(false); setNotificationsOpen((current) => !current); + setSettingsOpen(false); }} disabled={!account} aria-label={notificationBadgeLabel} @@ -887,13 +919,13 @@ export const TimelineSection = ({ key={status.id} status={status} onReply={(item) => onReply(item, account)} - onStatusClick={(currentStatus) => onStatusClick(currentStatus, account)} + onStatusClick={(currentStatus) => onStatusClick(currentStatus, account, section.settings)} onToggleFavourite={handleToggleFavourite} onToggleReblog={handleToggleReblog} onToggleBookmark={handleToggleBookmark} onDelete={handleDeleteStatus} onReact={handleReact} - onProfileClick={(item) => onProfileClick(item, account)} + onProfileClick={(item) => onProfileClick(item, account, section.settings)} activeHandle={ account?.handle ? formatHandle(account.handle, account.instanceUrl) : account?.instanceUrl ?? "" } @@ -901,7 +933,7 @@ export const TimelineSection = ({ activeAccountUrl={account?.url ?? null} account={account} api={services.api} - showProfileImage={showProfileImage} + showProfileImage={showProfileImages} showCustomEmojis={showCustomEmojis} showReactions={showReactions} disableActions @@ -926,6 +958,7 @@ export const TimelineSection = ({ setMenuOpen((current) => !current); setNotificationsOpen(false); setTimelineMenuOpen(false); + setSettingsOpen(false); }} >
; + } const className = [ item.danger ? "danger" : "", highlightedSectionMenuIndex === index ? "is-highlighted" : "" @@ -1018,6 +1065,87 @@ export const TimelineSection = ({
) : null} + {settingsOpen ? ( + <> +
@@ -1033,7 +1161,7 @@ export const TimelineSection = ({ key={status.id} status={status} onReply={(item) => onReply(item, account)} - onStatusClick={(currentStatus) => onStatusClick(currentStatus, account)} + onStatusClick={(currentStatus) => onStatusClick(currentStatus, account, section.settings)} onSelect={(statusId) => onSelectStatus(section.id, statusId)} isSelected={selectedStatusId === status.id} onUpdateStatus={timeline.updateItem} @@ -1042,7 +1170,7 @@ export const TimelineSection = ({ onToggleBookmark={handleToggleBookmark} onDelete={handleDeleteStatus} onReact={handleReact} - onProfileClick={(item) => onProfileClick(item, account)} + onProfileClick={(item) => onProfileClick(item, account, section.settings)} activeHandle={ account?.handle ? formatHandle(account.handle, account.instanceUrl) : account?.instanceUrl ?? "" } @@ -1050,7 +1178,7 @@ export const TimelineSection = ({ activeAccountUrl={account?.url ?? null} account={account} api={services.api} - showProfileImage={showProfileImage} + showProfileImage={showProfileImages} showCustomEmojis={showCustomEmojis} showReactions={showReactions} disableActions={actionsDisabled} diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index f2a430d..f8eff33 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1324,6 +1324,39 @@ button.ghost { z-index: 20; } +.section-settings-panel { + min-width: 240px; + padding: 12px; + gap: 12px; +} + +.section-settings-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.section-settings-text { + display: flex; + flex-direction: column; + gap: 2px; +} + +.section-settings-text strong { + font-size: 12px; +} + +.section-settings-text p { + margin: 0; + font-size: 11px; + color: var(--color-hint-text); +} + +.section-settings-select { + min-width: 84px; +} + .section-menu-panel button { background: none; color: var(--color-section-menu-text); @@ -1342,6 +1375,13 @@ button.ghost { font-weight: 600; } +.section-menu-divider { + height: 1px; + margin: 4px 6px; + background: var(--color-section-menu-border); + opacity: 0.7; +} + .notification-menu { position: relative; } @@ -2797,6 +2837,8 @@ button.ghost { width: 44px; height: 24px; display: inline-block; + min-width: 44px; + flex: 0 0 44px; } .switch input { @@ -2811,6 +2853,7 @@ button.ghost { border-radius: 999px; background: var(--color-switch-track); transition: background 0.2s ease; + overflow: hidden; } .slider::before { diff --git a/src/ui/types/section.ts b/src/ui/types/section.ts new file mode 100644 index 0000000..4b83784 --- /dev/null +++ b/src/ui/types/section.ts @@ -0,0 +1,6 @@ +export type SectionDisplaySettings = { + showProfileImages: boolean; + showCustomEmojis: boolean; + showReactions: boolean; + sectionSize: "small" | "medium" | "large"; +}; From 016e968a8d35af4f2d673d630853e7e225d04186 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Mon, 2 Feb 2026 12:18:17 +0900 Subject: [PATCH 09/33] =?UTF-8?q?style:=20=EC=84=B9=EC=85=98=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/components/TimelineSection.tsx | 52 ++++++++++++++++++++++++++- src/ui/styles/components.css | 28 +++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/ui/components/TimelineSection.tsx b/src/ui/components/TimelineSection.tsx index 256d720..07f0154 100644 --- a/src/ui/components/TimelineSection.tsx +++ b/src/ui/components/TimelineSection.tsx @@ -1043,6 +1043,48 @@ export const TimelineSection = ({ if ("type" in item && item.type === "divider") { return
; } + const icon = (() => { + switch (item.label) { + case "새로고침": + return ( + + ); + case "원본 서버에서 보기": + return ( + + ); + case "섹션 설정": + return ( + + ); + case "왼쪽 섹션 추가": + return null; + case "왼쪽으로 이동": + return null; + case "오른쪽으로 이동": + return null; + case "오른쪽 섹션 추가": + return null; + case "섹션 삭제": + return null; + default: + return null; + } + })(); const className = [ item.danger ? "danger" : "", highlightedSectionMenuIndex === index ? "is-highlighted" : "" @@ -1058,7 +1100,15 @@ export const TimelineSection = ({ onClick={item.onClick} disabled={item.disabled} > - {item.label} + {icon ? ( + + ) : null} + {item.label} ); })} diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index f8eff33..ac272cf 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1364,6 +1364,34 @@ button.ghost { text-align: left; font-size: 12px; border-radius: 8px; + display: inline-flex; + align-items: center; + gap: 8px; + width: 100%; +} + +.section-menu-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + flex: 0 0 16px; +} + +.section-menu-icon svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 1.7; + stroke-linecap: round; + stroke-linejoin: round; +} + + +.section-menu-label { + flex: 1; } .section-menu-panel button.is-highlighted { From a024709a1added2fee400c13d0af1ae70bee9003 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Mon, 2 Feb 2026 12:44:33 +0900 Subject: [PATCH 10/33] =?UTF-8?q?fix:=20=EC=84=B9=EC=85=98=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A0=91=EA=B7=BC=EC=84=B1=EA=B3=BC=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 1 - src/ui/components/TimelineSection.tsx | 20 ++++++++++++++------ src/ui/styles/components.css | 2 -- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5f09efe..79dafc5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1274,7 +1274,6 @@ export const App = () => { section.accountId ? accountsState.accounts.find((account) => account.id === section.accountId) ?? null : null; - const shouldShowReactions = section.settings.showReactions; const selectedStatusId = selectedTimelineStatus?.sectionId === section.id ? selectedTimelineStatus.statusId diff --git a/src/ui/components/TimelineSection.tsx b/src/ui/components/TimelineSection.tsx index 07f0154..b655670 100644 --- a/src/ui/components/TimelineSection.tsx +++ b/src/ui/components/TimelineSection.tsx @@ -183,6 +183,7 @@ export const TimelineSection = ({ const [highlightedSectionMenuIndex, setHighlightedSectionMenuIndex] = useState(null); const [highlightedNotificationIndex, setHighlightedNotificationIndex] = useState(null); const { showProfileImages, showCustomEmojis, showReactions, sectionSize } = section.settings; + const settingsIdPrefix = `section-settings-${section.id}`; const columnStyle = useMemo(() => { const sizeConfig = SECTION_SIZE_MAP[sectionSize]; return { @@ -760,6 +761,7 @@ export const TimelineSection = ({ onStatusClick, onTimelineChange, section.id, + section.settings, settingsOpen, selectedStatusId, showReactions, @@ -1127,13 +1129,15 @@ export const TimelineSection = ({ >
- 프로필 이미지 표시 -

이 섹션에서만 프로필 이미지를 보여줍니다.

+ 프로필 이미지 표시 +

이 섹션에서만 프로필 이미지를 보여줍니다.

- 커스텀 이모지 표시 -

사용자 이름과 본문에 커스텀 이모지를 표시합니다.

+ 커스텀 이모지 표시 +

사용자 이름과 본문에 커스텀 이모지를 표시합니다.

- 리액션 표시 -

리액션을 지원하는 서버에서 받은 리액션을 보여줍니다.

+ 리액션 표시 +

리액션을 지원하는 서버에서 받은 리액션을 보여줍니다.

+ {hasPendingUpdates ? ( + + ) : null} {!account ?

계정을 선택하면 타임라인을 불러옵니다.

: null} {account && timeline.items.length === 0 && !timeline.loading ? (

{emptyMessage}

diff --git a/src/ui/hooks/useTimeline.ts b/src/ui/hooks/useTimeline.ts index 6fe0ef6..050d83c 100644 --- a/src/ui/hooks/useTimeline.ts +++ b/src/ui/hooks/useTimeline.ts @@ -3,6 +3,23 @@ import type { Account, Status, TimelineType } from "../../domain/types"; import type { MastodonApi } from "../../services/MastodonApi"; import type { StreamingClient } from "../../services/StreamingClient"; +const TIMELINE_POLICY: Record = { + home: { maxItems: 400, flushInterval: 300, maxPending: 200 }, + local: { maxItems: 600, flushInterval: 400, maxPending: 240 }, + federated: { maxItems: 800, flushInterval: 500, maxPending: 260 }, + social: { maxItems: 600, flushInterval: 400, maxPending: 220 }, + global: { maxItems: 800, flushInterval: 500, maxPending: 260 }, + notifications: { maxItems: 300, flushInterval: 400, maxPending: 120 }, + bookmarks: { maxItems: 500, flushInterval: 400, maxPending: 0 } +}; + +const capItems = (items: Status[], maxItems: number): Status[] => { + if (items.length <= maxItems) { + return items; + } + return items.slice(0, maxItems); +}; + const mergeStatus = (items: Status[], next: Status): Status[] => { const index = items.findIndex((item) => item.id === next.id); if (index >= 0) { @@ -42,21 +59,49 @@ export const useTimeline = (params: { timelineType: TimelineType; onNotification?: () => void; enableStreaming?: boolean; + pauseUpdates?: boolean; + maxItems?: number; + flushInterval?: number; + maxPending?: number; }) => { - const { account, api, streaming, timelineType, onNotification, enableStreaming = true } = params; + const { + account, + api, + streaming, + timelineType, + onNotification, + enableStreaming = true, + pauseUpdates = false, + maxItems, + flushInterval, + maxPending + } = params; + const policy = TIMELINE_POLICY[timelineType]; + const resolvedMaxItems = maxItems ?? policy.maxItems; + const resolvedFlushInterval = flushInterval ?? policy.flushInterval; + const resolvedMaxPending = maxPending ?? policy.maxPending; const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [error, setError] = useState(null); const [hasMore, setHasMore] = useState(true); + const [pendingCount, setPendingCount] = useState(0); const disconnectRef = useRef void)>(null); const notificationDisconnectRef = useRef void)>(null); const notificationRef = useRef<(() => void) | null>(null); + const pauseUpdatesRef = useRef(pauseUpdates); + const pendingUpdatesRef = useRef([]); + const flushTimerRef = useRef | null>(null); + const pendingCountTimerRef = useRef | null>(null); useEffect(() => { notificationRef.current = onNotification ?? null; }, [onNotification]); + useEffect(() => { + pauseUpdatesRef.current = pauseUpdates; + }, [pauseUpdates]); + const refresh = useCallback(async () => { if (!account) { return; @@ -64,6 +109,8 @@ export const useTimeline = (params: { setLoading(true); setError(null); setItems([]); + setPendingCount(0); + pendingUpdatesRef.current = []; try { let timeline: Status[]; if (timelineType === "bookmarks") { @@ -71,14 +118,14 @@ export const useTimeline = (params: { } else { timeline = await api.fetchTimeline(account, timelineType, 30); } - setItems(timeline); + setItems(capItems(timeline, resolvedMaxItems)); setHasMore(timeline.length > 0); } catch (err) { setError(err instanceof Error ? err.message : "타임라인을 불러오지 못했습니다."); } finally { setLoading(false); } - }, [account, api, timelineType]); + }, [account, api, resolvedMaxItems, timelineType]); const loadMore = useCallback(async () => { if (!account || loadingMore || loading) { @@ -96,7 +143,7 @@ export const useTimeline = (params: { } else { next = await api.fetchTimeline(account, timelineType, 20, lastId); } - setItems((current) => appendStatuses(current, next)); + setItems((current) => capItems(appendStatuses(current, next), resolvedMaxItems)); if (next.length === 0) { setHasMore(false); } @@ -105,17 +152,94 @@ export const useTimeline = (params: { } finally { setLoadingMore(false); } - }, [account, api, hasMore, items, loading, loadingMore, timelineType]); + }, [account, api, hasMore, items, loading, loadingMore, resolvedMaxItems, timelineType]); useEffect(() => { if (!account) { setItems([]); setHasMore(false); + setPendingCount(0); + pendingUpdatesRef.current = []; return; } refresh(); }, [account, refresh]); + const syncPendingCount = useCallback(() => { + setPendingCount(pendingUpdatesRef.current.length); + }, []); + + const schedulePendingCountSync = useCallback(() => { + if (pendingCountTimerRef.current !== null) { + return; + } + pendingCountTimerRef.current = setTimeout(() => { + pendingCountTimerRef.current = null; + syncPendingCount(); + }, resolvedFlushInterval); + }, [resolvedFlushInterval, syncPendingCount]); + + const flushPendingUpdates = useCallback(() => { + const batch = pendingUpdatesRef.current; + if (batch.length === 0) { + return; + } + pendingUpdatesRef.current = []; + setPendingCount(0); + setItems((current) => { + let next = current; + for (const status of batch) { + next = mergeStatus(next, status); + } + return capItems(next, resolvedMaxItems); + }); + }, [resolvedMaxItems]); + + const scheduleFlush = useCallback(() => { + if (flushTimerRef.current !== null) { + return; + } + flushTimerRef.current = setTimeout(() => { + flushTimerRef.current = null; + flushPendingUpdates(); + }, resolvedFlushInterval); + }, [flushPendingUpdates, resolvedFlushInterval]); + + const enqueuePendingUpdate = useCallback( + (status: Status) => { + if (resolvedMaxPending === 0) { + return; + } + pendingUpdatesRef.current.push(status); + if (pendingUpdatesRef.current.length > resolvedMaxPending) { + pendingUpdatesRef.current = pendingUpdatesRef.current.slice(-resolvedMaxPending); + } + if (pauseUpdatesRef.current) { + schedulePendingCountSync(); + return; + } + scheduleFlush(); + }, + [resolvedMaxPending, scheduleFlush, schedulePendingCountSync] + ); + + const dropPendingUpdate = useCallback( + (statusId: string) => { + if (pendingUpdatesRef.current.length === 0) { + return; + } + const next = pendingUpdatesRef.current.filter((item) => item.id !== statusId); + if (next.length === pendingUpdatesRef.current.length) { + return; + } + pendingUpdatesRef.current = next; + if (pauseUpdatesRef.current) { + schedulePendingCountSync(); + } + }, + [schedulePendingCountSync] + ); + useEffect(() => { disconnectRef.current?.(); disconnectRef.current = null; @@ -128,11 +252,12 @@ export const useTimeline = (params: { disconnectRef.current = streaming.connect(account, timelineType, (event) => { if (event.type === "update") { if (timelineType !== "notifications") { - setItems((current) => mergeStatus(current, event.status)); + enqueuePendingUpdate(event.status); } } else if (event.type === "delete") { if (timelineType !== "notifications") { setItems((current) => current.filter((item) => item.id !== event.id)); + dropPendingUpdate(event.id); } } else if (event.type === "notification") { notificationRef.current?.(); @@ -152,8 +277,48 @@ export const useTimeline = (params: { disconnectRef.current = null; notificationDisconnectRef.current?.(); notificationDisconnectRef.current = null; + pendingUpdatesRef.current = []; + setPendingCount(0); + if (flushTimerRef.current !== null) { + clearTimeout(flushTimerRef.current); + flushTimerRef.current = null; + } + if (pendingCountTimerRef.current !== null) { + clearTimeout(pendingCountTimerRef.current); + pendingCountTimerRef.current = null; + } }; - }, [account, enableStreaming, onNotification, streaming, timelineType]); + }, [ + account, + dropPendingUpdate, + enableStreaming, + enqueuePendingUpdate, + onNotification, + streaming, + timelineType + ]); + + useEffect(() => { + if (!pauseUpdates && pendingUpdatesRef.current.length > 0) { + if (flushTimerRef.current !== null) { + clearTimeout(flushTimerRef.current); + flushTimerRef.current = null; + } + flushPendingUpdates(); + } + if (pauseUpdates && flushTimerRef.current !== null) { + clearTimeout(flushTimerRef.current); + flushTimerRef.current = null; + } + }, [flushPendingUpdates, pauseUpdates]); + + const flushPending = useCallback(() => { + if (flushTimerRef.current !== null) { + clearTimeout(flushTimerRef.current); + flushTimerRef.current = null; + } + flushPendingUpdates(); + }, [flushPendingUpdates]); const updateItem = useCallback((status: Status) => { setItems((current) => replaceStatus(current, status)); @@ -164,8 +329,20 @@ export const useTimeline = (params: { }, []); const timeline = useMemo( - () => ({ items, loading, loadingMore, error, hasMore, refresh, loadMore, updateItem, removeItem }), - [items, loading, loadingMore, error, hasMore, refresh, loadMore, updateItem, removeItem] + () => ({ + items, + loading, + loadingMore, + error, + hasMore, + refresh, + loadMore, + updateItem, + removeItem, + pendingCount, + flushPending + }), + [items, loading, loadingMore, error, hasMore, refresh, loadMore, updateItem, removeItem, pendingCount, flushPending] ); return timeline; diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index d1d5e6e..274071a 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1460,6 +1460,47 @@ button.ghost { padding-right: 6px; } +.timeline-pending-banner { + position: sticky; + top: 0; + z-index: 2; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px 12px; + margin-bottom: 8px; + border-radius: 999px; + border: 1px solid var(--color-panel-border); + background: var(--color-panel-bg); + color: var(--color-text-strong); + font-size: 12px; + font-weight: 600; + box-shadow: var(--shadow-panel, 0 10px 18px rgba(0, 0, 0, 0.08)); + cursor: pointer; +} + +.timeline-pending-banner:hover { + filter: brightness(0.98); +} + +.timeline-pending-banner:focus-visible { + outline: 2px solid var(--color-action-active-bg); + outline-offset: 2px; +} + +.timeline-pending-action { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 2px 8px; + border-radius: 999px; + background: var(--color-action-active-bg); + color: var(--color-action-active-text); + font-size: 11px; +} + .scroll-top-fab { position: absolute; right: 16px; From 6afc5ea700d3da3e7059130dd0283e0e34e7da73 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Mon, 2 Feb 2026 13:31:04 +0900 Subject: [PATCH 12/33] =?UTF-8?q?fix:=20=ED=83=80=EC=9E=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EB=8C=80=EA=B8=B0=20=ED=81=90=20=EC=A0=95=EA=B5=90?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/components/TimelineSection.tsx | 34 ++++++----- src/ui/hooks/useTimeline.ts | 81 ++++++++++++++++----------- src/ui/styles/components.css | 12 ++++ 3 files changed, 80 insertions(+), 47 deletions(-) diff --git a/src/ui/components/TimelineSection.tsx b/src/ui/components/TimelineSection.tsx index 08c1f20..c9db7c7 100644 --- a/src/ui/components/TimelineSection.tsx +++ b/src/ui/components/TimelineSection.tsx @@ -484,15 +484,16 @@ export const TimelineSection = ({ } }; - const scrollToTop = () => { + const scrollToTop = (behavior: ScrollBehavior = "smooth") => { if (scrollRef.current) { - scrollRef.current.scrollTo({ top: 0, behavior: "smooth" }); + scrollRef.current.scrollTo({ top: 0, behavior }); } }; const handleShowPending = () => { timeline.flushPending(); - scrollToTop(); + setIsAtTop(true); + scrollToTop("auto"); }; const handleOpenInstanceOrigin = useCallback(() => { @@ -1218,16 +1219,21 @@ export const TimelineSection = ({
{hasPendingUpdates ? ( - + <> + + 새 글 {pendingCountLabel}개가 새로 도착했습니다. + + + ) : null} {!account ?

계정을 선택하면 타임라인을 불러옵니다.

: null} {account && timeline.items.length === 0 && !timeline.loading ? ( @@ -1270,7 +1276,7 @@ export const TimelineSection = ({ )} + {account ? ( + + ) : null} @@ -1474,6 +1583,34 @@ export const TimelineItem = ({ {showContent ? ( <>
{displayStatus.content ? contentParts : "(내용 없음)"}
+ {translation ? ( +
+
+ 번역 + +
+
+ {translationParts ?? "(내용 없음)"} +
+ {translationMeta ? ( +
+ {translationMeta.map((item) => ( + {item} + ))} +
+ ) : null} +
+ ) : null} {previewCard ? (
Date: Tue, 3 Feb 2026 13:04:18 +0900 Subject: [PATCH 22/33] chore: ignore .codex --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 081ff24..093b432 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ bun.lockb dist .env .wrangler +.codex From 6ea212f755821d45cd1afa975cf1cd2510ca22c3 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 3 Feb 2026 14:16:35 +0900 Subject: [PATCH 23/33] fix: ignore emoji shortcodes inside urls --- src/ui/utils/markdown.test.ts | 10 ++++++++++ src/ui/utils/markdown.ts | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ui/utils/markdown.test.ts b/src/ui/utils/markdown.test.ts index 282f653..ac7ede0 100644 --- a/src/ui/utils/markdown.test.ts +++ b/src/ui/utils/markdown.test.ts @@ -60,6 +60,16 @@ describe("renderMarkdown", () => { ); }); + it("does not render emoji shortcodes inside URLs", () => { + const input = "https://example.com/:wave:/path"; + const emojiMap = new Map([["wave", "https://example.com/wave.png"]]); + const output = renderMarkdown(input, emojiMap); + + expect(output).toBe( + '

https://example.com/:wave:/path

' + ); + }); + it("renders emojis inside markdown link labels", () => { const input = "[go :wave:](https://example.com)"; const emojiMap = new Map([["wave", "https://example.com/wave.png"]]); diff --git a/src/ui/utils/markdown.ts b/src/ui/utils/markdown.ts index f36a07c..43c8932 100644 --- a/src/ui/utils/markdown.ts +++ b/src/ui/utils/markdown.ts @@ -37,9 +37,11 @@ const renderEmojiTag = (shortcode: string, url: string): string => { return `${safeAlt}`; }; +const bareUrlPattern = /https?:\/\/[^\s<]+[^\s<\])"'.,;:!?]/g; + // Linkify plain URLs while excluding trailing punctuation. const linkifyBareUrls = (text: string): string => { - return text.replace(/https?:\/\/[^\s<]+[^\s<\])"'.,;:!?]/g, (match) => { + return text.replace(bareUrlPattern, (match) => { if (!isSafeUrl(match)) { return match; } @@ -102,6 +104,11 @@ const formatInline = ( } ); } + const bareUrls: string[] = []; + tokenized = tokenized.replace(bareUrlPattern, (match) => { + bareUrls.push(match); + return `\u0005${bareUrls.length - 1}\u0005`; + }); const emojis: string[] = []; if (emojiMap && emojiMap.size > 0) { tokenized = tokenized.replace(/:([a-zA-Z0-9_]+):/g, (_match, shortcode) => { @@ -114,6 +121,7 @@ const formatInline = ( return `\u0002${emojis.length - 1}\u0002`; }); } + tokenized = tokenized.replace(/\u0005(\d+)\u0005/g, (_match, index) => bareUrls[Number(index)] ?? ""); let out = escapeHtml(tokenized); out = out.replace(/\*\*([^*]+)\*\*/g, "$1"); out = out.replace(/\*([^*]+)\*/g, "$1"); From d1d56e486336d5ab6e61b59c08c421bce1419362 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 3 Feb 2026 16:11:35 +0900 Subject: [PATCH 24/33] feat: prevent timeline back-swipe --- src/ui/styles/components.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index c243747..22a0d14 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1207,6 +1207,7 @@ button.ghost { cursor: grab; scroll-behavior: smooth; scroll-snap-type: x mandatory; + overscroll-behavior-x: contain; } .timeline-board.is-dragging { From 52424c0179a6429ddf8e59cb5f6676c85ab090fd Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 3 Feb 2026 16:24:46 +0900 Subject: [PATCH 25/33] feat: contain overscroll in column body --- src/ui/styles/components.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index 22a0d14..dc54d21 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1459,6 +1459,7 @@ button.ghost { overflow-y: auto; max-height: var(--height-timeline-column-body); padding-right: 6px; + overscroll-behavior-x: contain; } .sr-only { From 82d24a939daf4399a093bfb049015d6b1647564a Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 3 Feb 2026 16:47:02 +0900 Subject: [PATCH 26/33] =?UTF-8?q?Revert=20"feat:=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EB=B3=B8=EB=AC=B8=20=EC=98=A4=EB=B2=84=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EC=B0=A8=EB=8B=A8"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/styles/components.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/styles/components.css b/src/ui/styles/components.css index dc54d21..22a0d14 100644 --- a/src/ui/styles/components.css +++ b/src/ui/styles/components.css @@ -1459,7 +1459,6 @@ button.ghost { overflow-y: auto; max-height: var(--height-timeline-column-body); padding-right: 6px; - overscroll-behavior-x: contain; } .sr-only { From e582cf273523171eba695866e283b77399179dae Mon Sep 17 00:00:00 2001 From: opencode Date: Sat, 7 Feb 2026 14:27:01 +0900 Subject: [PATCH 27/33] =?UTF-8?q?fix:=20=EB=8B=B5=EA=B8=80=20=EC=8B=9C=20C?= =?UTF-8?q?W=20=EC=9E=90=EB=8F=99=20=EB=8F=99=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 12 ++++++++++-- src/ui/components/ComposeBox.tsx | 17 ++++++++++++++++- src/ui/components/MobileMenus.tsx | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 79dafc5..1db6007 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1151,7 +1151,11 @@ export const App = () => { account={composeAccount} api={services.api} onSubmit={handleSubmit} - replyingTo={replyTarget ? { id: replyTarget.id, summary: replySummary ?? "" } : null} + replyingTo={ + replyTarget + ? { id: replyTarget.id, summary: replySummary ?? "", spoilerText: replyTarget.spoilerText } + : null + } onCancelReply={() => { setReplyTarget(null); setMentionSeed(null); @@ -1343,7 +1347,11 @@ export const App = () => { composeAccountSelector={composeAccountSelector} api={services.api} onSubmit={handleSubmit} - replyingTo={replyTarget ? { id: replyTarget.id, summary: replySummary ?? "" } : null} + replyingTo={ + replyTarget + ? { id: replyTarget.id, summary: replySummary ?? "", spoilerText: replyTarget.spoilerText } + : null + } onCancelReply={() => { setReplyTarget(null); setMentionSeed(null); diff --git a/src/ui/components/ComposeBox.tsx b/src/ui/components/ComposeBox.tsx index 67e947c..7dd849b 100644 --- a/src/ui/components/ComposeBox.tsx +++ b/src/ui/components/ComposeBox.tsx @@ -49,7 +49,7 @@ export const ComposeBox = ({ files: File[]; spoilerText: string; }) => Promise; - replyingTo: { id: string; summary: string } | null; + replyingTo: { id: string; summary: string; spoilerText: string } | null; onCancelReply: () => void; mentionText: string | null; accountSelector?: React.ReactNode; @@ -302,6 +302,21 @@ export const ComposeBox = ({ } }, [mentionText]); + useEffect(() => { + if (!replyingTo) { + setCwEnabled(false); + setCwText(""); + return; + } + if (!replyingTo.spoilerText) { + setCwEnabled(false); + setCwText(""); + return; + } + setCwEnabled(true); + setCwText(replyingTo.spoilerText); + }, [replyingTo?.id, replyingTo?.spoilerText]); + // 이모지 패널이 열리면 이모지 로드 useEffect(() => { if (emojiPanelOpen && account) { diff --git a/src/ui/components/MobileMenus.tsx b/src/ui/components/MobileMenus.tsx index 81b3992..1efb959 100644 --- a/src/ui/components/MobileMenus.tsx +++ b/src/ui/components/MobileMenus.tsx @@ -18,7 +18,7 @@ type MobileComposeMenuProps = { files: File[]; spoilerText: string; }) => Promise; - replyingTo: { id: string; summary: string } | null; + replyingTo: { id: string; summary: string; spoilerText: string } | null; onCancelReply: () => void; mentionText: string | null; }; From e618c9f0246aa20f0d50bd0aa1bfb2d190509267 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 10 Feb 2026 09:00:27 +0900 Subject: [PATCH 28/33] =?UTF-8?q?refactor:=20=EB=8B=B5=EA=B8=80=20?= =?UTF-8?q?=EB=8C=80=EC=83=81=20=ED=83=80=EC=9E=85=20=EA=B3=B5=EC=9A=A9?= =?UTF-8?q?=ED=99=94=EB=A1=9C=20=EB=8F=99=EA=B8=B0=ED=99=94=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 16 ++++++---------- src/ui/components/ComposeBox.tsx | 3 ++- src/ui/components/MobileMenus.tsx | 3 ++- src/ui/types/compose.ts | 5 +++++ 4 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 src/ui/types/compose.ts diff --git a/src/App.tsx b/src/App.tsx index 1db6007..c5ff754 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,6 +19,7 @@ import { normalizeTimelineType } from "./ui/utils/timeline"; import { buildOptimisticReactionStatus, hasSameReactions } from "./ui/utils/reactions"; import { ColorScheme, ThemeMode, getStoredColorScheme, getStoredTheme, isColorScheme, isThemeMode } from "./ui/utils/theme"; import type { InfoModalType } from "./ui/types/info"; +import type { ReplyingTo } from "./ui/types/compose"; import type { SectionDisplaySettings } from "./ui/types/section"; import logoUrl from "./ui/assets/textodon-icon-blue.png"; @@ -188,6 +189,9 @@ export const App = () => { const replySummary = replyTarget ? `@${formatReplyHandle(replyTarget.accountHandle, replyTarget.accountUrl, composeAccount?.instanceUrl ?? "")} · ${replyTarget.content.slice(0, 80)}` : null; + const replyingTo: ReplyingTo | null = replyTarget + ? { id: replyTarget.id, summary: replySummary ?? "", spoilerText: replyTarget.spoilerText } + : null; const [route, setRoute] = useState(() => parseRoute()); const timelineListeners = useRef void>>>(new Map()); const previousAccountIds = useRef>(new Set()); @@ -1151,11 +1155,7 @@ export const App = () => { account={composeAccount} api={services.api} onSubmit={handleSubmit} - replyingTo={ - replyTarget - ? { id: replyTarget.id, summary: replySummary ?? "", spoilerText: replyTarget.spoilerText } - : null - } + replyingTo={replyingTo} onCancelReply={() => { setReplyTarget(null); setMentionSeed(null); @@ -1347,11 +1347,7 @@ export const App = () => { composeAccountSelector={composeAccountSelector} api={services.api} onSubmit={handleSubmit} - replyingTo={ - replyTarget - ? { id: replyTarget.id, summary: replySummary ?? "", spoilerText: replyTarget.spoilerText } - : null - } + replyingTo={replyingTo} onCancelReply={() => { setReplyTarget(null); setMentionSeed(null); diff --git a/src/ui/components/ComposeBox.tsx b/src/ui/components/ComposeBox.tsx index 7dd849b..a6a21c5 100644 --- a/src/ui/components/ComposeBox.tsx +++ b/src/ui/components/ComposeBox.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { Account, Visibility } from "../../domain/types"; import type { MastodonApi } from "../../services/MastodonApi"; +import type { ReplyingTo } from "../types/compose"; import { useEmojiManager, type EmojiItem } from "../hooks/useEmojiManager"; import { useImageZoom } from "../hooks/useImageZoom"; import { useClickOutside } from "../hooks/useClickOutside"; @@ -49,7 +50,7 @@ export const ComposeBox = ({ files: File[]; spoilerText: string; }) => Promise; - replyingTo: { id: string; summary: string; spoilerText: string } | null; + replyingTo: ReplyingTo | null; onCancelReply: () => void; mentionText: string | null; accountSelector?: React.ReactNode; diff --git a/src/ui/components/MobileMenus.tsx b/src/ui/components/MobileMenus.tsx index 1efb959..4ed12be 100644 --- a/src/ui/components/MobileMenus.tsx +++ b/src/ui/components/MobileMenus.tsx @@ -2,6 +2,7 @@ import type { ReactNode } from "react"; import type { Account, Visibility } from "../../domain/types"; import type { MastodonApi } from "../../services/MastodonApi"; import type { OAuthClient } from "../../services/OAuthClient"; +import type { ReplyingTo } from "../types/compose"; import { AccountAdd } from "./AccountAdd"; import { ComposeBox } from "./ComposeBox"; @@ -18,7 +19,7 @@ type MobileComposeMenuProps = { files: File[]; spoilerText: string; }) => Promise; - replyingTo: { id: string; summary: string; spoilerText: string } | null; + replyingTo: ReplyingTo | null; onCancelReply: () => void; mentionText: string | null; }; diff --git a/src/ui/types/compose.ts b/src/ui/types/compose.ts new file mode 100644 index 0000000..8468007 --- /dev/null +++ b/src/ui/types/compose.ts @@ -0,0 +1,5 @@ +export type ReplyingTo = { + id: string; + summary: string; + spoilerText: string; +}; From 311cb6a4eff0630e8d9f21b0e0e1586cf62e4823 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 10 Feb 2026 11:23:50 +0900 Subject: [PATCH 29/33] =?UTF-8?q?fix:=20=EA=B2=8C=EC=8B=9C=20=ED=9B=84=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=ED=8F=AC=EC=BB=A4=EC=8A=A4=EC=99=80=20CW?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/components/ComposeBox.tsx | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/ui/components/ComposeBox.tsx b/src/ui/components/ComposeBox.tsx index a6a21c5..d315b72 100644 --- a/src/ui/components/ComposeBox.tsx +++ b/src/ui/components/ComposeBox.tsx @@ -90,6 +90,8 @@ export const ComposeBox = ({ const emojiToggleRef = useRef(null); const cwToggleRef = useRef(null); const emojiPanelRef = useRef(null); + const submitFocusTargetRef = useRef<"textarea" | "cw" | null>(null); + const skipReplyResetRef = useRef(false); // useImageZoom 훅 사용 const { @@ -265,6 +267,10 @@ export const ComposeBox = ({ return; } + const activeElement = document.activeElement; + submitFocusTargetRef.current = activeElement === cwInputRef.current ? "cw" : "textarea"; + const shouldKeepCwOpen = cwEnabled; + setIsSubmitting(true); try { const ok = await onSubmit({ @@ -276,8 +282,9 @@ export const ComposeBox = ({ }); if (ok) { setText(""); - setCwText(""); - setCwEnabled(false); + if (shouldKeepCwOpen) { + skipReplyResetRef.current = true; + } setAttachments((current) => { current.forEach((item) => URL.revokeObjectURL(item.previewUrl)); return []; @@ -286,6 +293,14 @@ export const ComposeBox = ({ } } finally { setIsSubmitting(false); + requestAnimationFrame(() => { + if (submitFocusTargetRef.current === "cw" && cwEnabled) { + cwInputRef.current?.focus(); + } else { + textareaRef.current?.focus(); + } + submitFocusTargetRef.current = null; + }); } }; @@ -305,10 +320,15 @@ export const ComposeBox = ({ useEffect(() => { if (!replyingTo) { + if (skipReplyResetRef.current) { + skipReplyResetRef.current = false; + return; + } setCwEnabled(false); setCwText(""); return; } + skipReplyResetRef.current = false; if (!replyingTo.spoilerText) { setCwEnabled(false); setCwText(""); From 21b6f6f570c14f641e723828afa597418f85e2a4 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Fri, 6 Mar 2026 09:48:05 +0900 Subject: [PATCH 30/33] =?UTF-8?q?feat:=20=EB=BD=80=EB=AA=A8=EB=8F=84?= =?UTF-8?q?=EB=A1=9C=20=ED=88=AC=EB=91=90=20=EC=88=9C=EC=84=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/components/PomodoroTimer.tsx | 123 +++++++++++++++++++++++++++- src/ui/styles/components.css | 14 ++++ 2 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/ui/components/PomodoroTimer.tsx b/src/ui/components/PomodoroTimer.tsx index 98ce46d..a68dcf9 100644 --- a/src/ui/components/PomodoroTimer.tsx +++ b/src/ui/components/PomodoroTimer.tsx @@ -130,6 +130,8 @@ export const PomodoroTimer = ({ } }); const [selectedTodoId, setSelectedTodoId] = useState(null); + const [draggingTodoId, setDraggingTodoId] = useState(null); + const [dragOverTodoId, setDragOverTodoId] = useState(null); const todoListRef = useRef(null); const todoInputRef = useRef(null); @@ -461,6 +463,55 @@ export const PomodoroTimer = ({ const displayedTodos = useMemo(() => todoItems, [todoItems]); + const moveTodoByIndex = useCallback((fromIndex: number, toIndex: number) => { + if (fromIndex === toIndex) { + return; + } + setTodoItems((prev) => { + if (fromIndex < 0 || toIndex < 0 || fromIndex >= prev.length || toIndex >= prev.length) { + return prev; + } + const next = [...prev]; + const [movedItem] = next.splice(fromIndex, 1); + if (!movedItem) { + return prev; + } + next.splice(toIndex, 0, movedItem); + return next; + }); + }, []); + + const moveTodoById = useCallback((draggedId: string, targetId: string) => { + if (draggedId === targetId) { + return; + } + const fromIndex = displayedTodos.findIndex((item) => item.id === draggedId); + const toIndex = displayedTodos.findIndex((item) => item.id === targetId); + if (fromIndex === -1 || toIndex === -1) { + return; + } + moveTodoByIndex(fromIndex, toIndex); + }, [displayedTodos, moveTodoByIndex]); + + const moveSelectedTodo = useCallback( + (direction: "up" | "down") => { + if (!selectedTodoId) { + return false; + } + const currentIndex = displayedTodos.findIndex((item) => item.id === selectedTodoId); + if (currentIndex === -1) { + return false; + } + const nextIndex = direction === "up" ? currentIndex - 1 : currentIndex + 1; + if (nextIndex < 0 || nextIndex >= displayedTodos.length) { + return false; + } + moveTodoByIndex(currentIndex, nextIndex); + return true; + }, + [displayedTodos, moveTodoByIndex, selectedTodoId] + ); + const selectTodo = useCallback( (id: string) => { setSelectedTodoId(id); @@ -469,6 +520,52 @@ export const PomodoroTimer = ({ [onRequestClearTimelineSelection] ); + const handleTodoDragStart = useCallback( + (event: React.DragEvent, id: string) => { + setDraggingTodoId(id); + setDragOverTodoId(id); + setSelectedTodoId(id); + onRequestClearTimelineSelection?.(); + event.dataTransfer.effectAllowed = "move"; + event.dataTransfer.setData("text/plain", id); + }, + [onRequestClearTimelineSelection] + ); + + const handleTodoDragOver = useCallback( + (event: React.DragEvent, id: string) => { + if (!draggingTodoId) { + return; + } + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + if (draggingTodoId !== id && dragOverTodoId !== id) { + setDragOverTodoId(id); + } + }, + [dragOverTodoId, draggingTodoId] + ); + + const handleTodoDrop = useCallback( + (event: React.DragEvent, targetId: string) => { + event.preventDefault(); + const draggedId = draggingTodoId; + if (!draggedId) { + return; + } + moveTodoById(draggedId, targetId); + setSelectedTodoId(draggedId); + setDraggingTodoId(null); + setDragOverTodoId(null); + }, + [draggingTodoId, moveTodoById] + ); + + const handleTodoDragEnd = useCallback(() => { + setDraggingTodoId(null); + setDragOverTodoId(null); + }, []); + const handleTodoKeyDown = useCallback( (event: React.KeyboardEvent) => { const target = event.target; @@ -508,6 +605,20 @@ export const PomodoroTimer = ({ return; } + if ( + (key === "ArrowUp" || key === "ArrowDown") && + event.altKey && + !event.ctrlKey && + !event.metaKey && + !event.shiftKey + ) { + const moved = moveSelectedTodo(key === "ArrowUp" ? "up" : "down"); + if (moved) { + event.preventDefault(); + } + return; + } + if (key === "ArrowRight") { if (!selectedTodoId || !onRequestSelectTimelineAtY) { return; @@ -545,7 +656,7 @@ export const PomodoroTimer = ({ event.preventDefault(); selectTodo(displayedTodos[nextIndex].id); }, - [displayedTodos, handleRemoveTodo, handleToggleTodo, onRequestSelectTimelineAtY, selectTodo, selectedTodoId] + [displayedTodos, handleRemoveTodo, handleToggleTodo, moveSelectedTodo, onRequestSelectTimelineAtY, selectTodo, selectedTodoId] ); const handleTodoInputKeyDown = useCallback( @@ -623,18 +734,24 @@ export const PomodoroTimer = ({ ref={todoListRef} tabIndex={displayedTodos.length > 0 ? 0 : -1} onKeyDownCapture={handleTodoKeyDown} - title="↑/↓ 이동 · Space 완료 · D 삭제 · → 타임라인 이동 · ESC 선택 해제" + title="↑/↓ 이동 · Alt+↑/↓ 순서 변경 · Space 완료 · D 삭제 · → 타임라인 이동 · ESC 선택 해제" > {displayedTodos.map((item) => (
{ selectTodo(item.id); todoListRef.current?.focus(); }} + onDragStart={(event) => handleTodoDragStart(event, item.id)} + onDragOver={(event) => handleTodoDragOver(event, item.id)} + onDrop={(event) => handleTodoDrop(event, item.id)} + onDragEnd={handleTodoDragEnd} > Date: Tue, 31 Mar 2026 17:55:25 +0900 Subject: [PATCH 31/33] =?UTF-8?q?fix:=20=EB=AF=B8=EB=A6=AC=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20API=EC=99=80=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EB=94=94=EC=BD=94=EB=94=A9=20=EB=B3=B4=EC=95=88=20=EA=B0=95?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- functions/api/preview.ts | 136 +++++++++++++++++++++-------- src/ui/components/TimelineItem.tsx | 26 ++++-- 2 files changed, 119 insertions(+), 43 deletions(-) diff --git a/functions/api/preview.ts b/functions/api/preview.ts index 2154bd7..e1d74a0 100644 --- a/functions/api/preview.ts +++ b/functions/api/preview.ts @@ -2,6 +2,7 @@ type Env = Record; const MAX_RESPONSE_BYTES = 512 * 1024; const REQUEST_TIMEOUT_MS = 5000; +const MAX_REDIRECTS = 2; const textDecoder = new TextDecoder("utf-8"); @@ -13,15 +14,6 @@ const decodeHtmlEntities = (value: string): string => .replace(/"/g, '"') .replace(/'/g, "'"); -const normalizeInlineText = (value: string | null | undefined): string | null => { - if (!value) { - return null; - } - const decoded = decodeHtmlEntities(value); - const normalized = decoded.replace(/\s+/g, " ").trim(); - return normalized || null; -}; - const extractMetaTagContent = (html: string, attribute: "property" | "name", key: string): string | null => { const tagRegex = new RegExp(`]+${attribute}=["']${key}["'][^>]*>`, "i"); const match = html.match(tagRegex); @@ -44,12 +36,16 @@ const extractTitle = (html: string): string | null => { return text || null; }; -const toAbsoluteUrl = (value: string | null, baseUrl: string): string | null => { +const toAbsoluteHttpUrl = (value: string | null, baseUrl: string): string | null => { if (!value) { return null; } try { - return new URL(value, baseUrl).toString(); + const url = new URL(value, baseUrl); + if (url.protocol !== "http:" && url.protocol !== "https:") { + return null; + } + return url.toString(); } catch { return null; } @@ -90,6 +86,13 @@ const isPrivateIpv4 = (host: string): boolean => { const isPrivateIpv6 = (host: string): boolean => { const normalized = host.toLowerCase(); + if (normalized === "::" || normalized === "0:0:0:0:0:0:0:0") { + return true; + } + if (normalized.startsWith("::ffff:")) { + const mappedIpv4 = normalized.slice(7); + return isIpAddress(mappedIpv4) && isPrivateIpv4(mappedIpv4); + } return ( normalized === "::1" || normalized.startsWith("fe80:") || @@ -111,7 +114,52 @@ const isBlockedHostname = (hostname: string): boolean => { const isYouTubeHost = (hostname: string): boolean => { const lower = hostname.toLowerCase(); - return lower === "youtu.be" || lower.endsWith("youtube.com"); + return lower === "youtu.be" || lower === "youtube.com" || lower.endsWith(".youtube.com"); +}; + + +const isAllowedContentType = (contentType: string): boolean => { + const normalized = contentType.toLowerCase().split(";")[0]?.trim(); + return normalized === "text/html" || normalized === "application/xhtml+xml"; +}; + + +const fetchWithSafeRedirects = async ( + targetUrl: URL, + signal: AbortSignal, + redirectCount = 0 +): Promise => { + const response = await fetch(targetUrl.toString(), { + signal, + redirect: "manual", + headers: { + "User-Agent": "DeckLinkPreview/1.0", + Accept: "text/html,application/xhtml+xml" + } + }); + + if (response.status >= 300 && response.status < 400) { + if (redirectCount >= MAX_REDIRECTS) { + throw new Error("too_many_redirects"); + } + const location = response.headers.get("location"); + if (!location) { + throw new Error("invalid_redirect"); + } + let redirectedUrl: URL; + try { + redirectedUrl = new URL(location, targetUrl.toString()); + } catch { + throw new Error("invalid_redirect"); + } + const validatedRedirectUrl = isValidHttpUrl(redirectedUrl.toString()); + if (!validatedRedirectUrl || isBlockedHostname(validatedRedirectUrl.hostname)) { + throw new Error("blocked_redirect"); + } + return fetchWithSafeRedirects(validatedRedirectUrl, signal, redirectCount + 1); + } + + return response; }; @@ -171,53 +219,63 @@ const readResponseText = async (response: Response): Promise => { return textDecoder.decode(combined); }; -const buildResponse = (body: Record, status = 200, cacheSeconds = 600): Response => { +const buildResponse = ( + body: Record, + status = 200, + cacheSeconds = 600, + allowedOrigin?: string +): Response => { + const headers: Record = { + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": `public, max-age=${cacheSeconds}`, + "Content-Security-Policy": "default-src 'none'", + "Referrer-Policy": "strict-origin-when-cross-origin", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + Vary: "Origin" + }; + + if (allowedOrigin) { + headers["Access-Control-Allow-Origin"] = allowedOrigin; + } + return new Response(JSON.stringify(body), { status, - headers: { - "Content-Type": "application/json; charset=utf-8", - "Cache-Control": `public, max-age=${cacheSeconds}`, - "Access-Control-Allow-Origin": "*" - } + headers }); }; export const onRequestGet = async (context: { request: Request } & { env?: Env }) => { const requestUrl = new URL(context.request.url); + const allowedOrigin = requestUrl.origin; const urlParam = requestUrl.searchParams.get("url"); if (!urlParam) { - return buildResponse({ error: "missing_url" }, 400, 60); + return buildResponse({ error: "missing_url" }, 400, 60, allowedOrigin); } const targetUrl = isValidHttpUrl(urlParam); if (!targetUrl || isBlockedHostname(targetUrl.hostname)) { - return buildResponse({ error: "invalid_url" }, 400, 60); + return buildResponse({ error: "invalid_url" }, 400, 60, allowedOrigin); } const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); try { - const response = await fetch(targetUrl.toString(), { - signal: controller.signal, - headers: { - "User-Agent": "DeckLinkPreview/1.0", - Accept: "text/html,application/xhtml+xml" - } - }); + const response = await fetchWithSafeRedirects(targetUrl, controller.signal); if (!response.ok) { - return buildResponse({ error: "fetch_failed", status: response.status }, 200, 60); + return buildResponse({ error: "fetch_failed", status: response.status }, 200, 60, allowedOrigin); } const contentType = response.headers.get("content-type") ?? ""; - if (!contentType.includes("text/html")) { - return buildResponse({ error: "unsupported_content" }, 200, 300); + if (!isAllowedContentType(contentType)) { + return buildResponse({ error: "unsupported_content" }, 200, 300, allowedOrigin); } const html = await readResponseText(response); if (!html) { - return buildResponse({ error: "empty_body" }, 200, 60); + return buildResponse({ error: "empty_body" }, 200, 60, allowedOrigin); } const ogTitle = extractMetaTagContent(html, "property", "og:title"); @@ -227,8 +285,8 @@ export const onRequestGet = async (context: { request: Request } & { env?: Env } const metaDescription = extractMetaTagContent(html, "name", "description"); let title = ogTitle || extractTitle(html); const description = ogDescription || metaDescription; - let image = toAbsoluteUrl(ogImageRaw, targetUrl.toString()); - const canonicalUrl = toAbsoluteUrl(ogUrl, targetUrl.toString()) ?? targetUrl.toString(); + let image = toAbsoluteHttpUrl(ogImageRaw, targetUrl.toString()); + const canonicalUrl = toAbsoluteHttpUrl(ogUrl, targetUrl.toString()) ?? targetUrl.toString(); const shouldFetchYouTube = !title || title.trim() === "YouTube"; if (shouldFetchYouTube && isYouTubeHost(targetUrl.hostname)) { @@ -241,7 +299,7 @@ export const onRequestGet = async (context: { request: Request } & { env?: Env } if (!title) { - return buildResponse({ error: "missing_title" }, 200, 300); + return buildResponse({ error: "missing_title" }, 200, 300, allowedOrigin); } return buildResponse( @@ -252,13 +310,17 @@ export const onRequestGet = async (context: { request: Request } & { env?: Env } image: image || null }, 200, - 600 + 600, + allowedOrigin ); } catch (error) { + if (error instanceof Error && ["invalid_redirect", "blocked_redirect", "too_many_redirects"].includes(error.message)) { + return buildResponse({ error: error.message }, 200, 60, allowedOrigin); + } if (error instanceof Error && error.name === "AbortError") { - return buildResponse({ error: "timeout" }, 200, 60); + return buildResponse({ error: "timeout" }, 200, 60, allowedOrigin); } - return buildResponse({ error: "fetch_failed" }, 200, 60); + return buildResponse({ error: "fetch_failed" }, 200, 60, allowedOrigin); } finally { clearTimeout(timeout); } diff --git a/src/ui/components/TimelineItem.tsx b/src/ui/components/TimelineItem.tsx index 27507f8..84665c1 100644 --- a/src/ui/components/TimelineItem.tsx +++ b/src/ui/components/TimelineItem.tsx @@ -54,12 +54,26 @@ const extractFirstUrl = (text: string): string | null => { }; const decodeHtmlEntities = (value: string): string => { - if (typeof document === "undefined") { - return value; - } - const textarea = document.createElement("textarea"); - textarea.innerHTML = value; - return textarea.value; + const decodeCodePoint = (match: string, input: string, radix: number): string => { + const codePoint = Number.parseInt(input, radix); + if (!Number.isFinite(codePoint) || codePoint < 0 || codePoint > 0x10ffff) { + return match; + } + try { + return String.fromCodePoint(codePoint); + } catch { + return match; + } + }; + + return value + .replace(/&#(\d+);/g, (match, decimal) => decodeCodePoint(match, decimal, 10)) + .replace(/&#x([0-9a-fA-F]+);/g, (match, hexadecimal) => decodeCodePoint(match, hexadecimal, 16)) + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/&/g, "&"); }; const normalizePreviewText = (value: string | null | undefined): string | null => { From f391b1f3ee25de3b7440356da589f3610d784420 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 31 Mar 2026 17:55:33 +0900 Subject: [PATCH 32/33] =?UTF-8?q?chore:=20CSP=20=EB=B0=8F=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=EB=B3=B4=EC=95=88=20=ED=97=A4=EB=8D=94=20=EC=A0=95?= =?UTF-8?q?=EC=B1=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- index.html | 2 +- public/_headers | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 public/_headers diff --git a/index.html b/index.html index cbfd991..9bed5b0 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ diff --git a/public/_headers b/public/_headers new file mode 100644 index 0000000..b3f337f --- /dev/null +++ b/public/_headers @@ -0,0 +1,5 @@ +/* + Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' https: data: blob:; media-src 'self' https: blob: data:; font-src 'self' https://cdn.jsdelivr.net data:; connect-src 'self' https: wss:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; worker-src 'self' blob: + X-Content-Type-Options: nosniff + X-Frame-Options: DENY + Referrer-Policy: strict-origin-when-cross-origin From 4f06a6a8bfb6fd24f4714c327de657479b844578 Mon Sep 17 00:00:00 2001 From: Euigyom Kim Date: Tue, 31 Mar 2026 17:55:41 +0900 Subject: [PATCH 33/33] =?UTF-8?q?chore:=20=EC=B7=A8=EC=95=BD=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=EA=B3=BC=20=ED=83=80=EC=9E=85=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EC=84=A4=EC=A0=95=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- bun.lock | 470 +++++++++++++++++++++---------------- package.json | 18 +- src/ui/utils/oauth.test.ts | 3 +- tsconfig.json | 3 +- 4 files changed, 288 insertions(+), 206 deletions(-) diff --git a/bun.lock b/bun.lock index 37b8b5f..f42a5b6 100644 --- a/bun.lock +++ b/bun.lock @@ -5,23 +5,29 @@ "": { "name": "textodon", "dependencies": { - "dompurify": "^3.3.1", + "dompurify": "^3.3.2", "emoji-datasource": "^16.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "wrangler": "3.90.0", + "wrangler": "^4.78.0", }, "devDependencies": { - "@types/dompurify": "^3.2.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^4.3.1", + "@vitejs/plugin-react": "^6.0.1", + "bun-types": "^1.3.11", + "esbuild": "^0.25.5", + "picomatch": "^4.0.4", + "rollup": "^4.59.0", "typescript": "^5.5.4", - "vite": "^5.4.0", - "vite-plugin-svgr": "^4.5.0", + "vite": "^8.0.3", + "vite-plugin-svgr": "^5.0.0", }, }, }, + "overrides": { + "picomatch": "^4.0.4", + }, "packages": { "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -39,8 +45,6 @@ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], @@ -51,83 +55,135 @@ "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], - "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], - - "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], - "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], - "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.3.4", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20241106.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw=="], + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg=="], - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20241106.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260317.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g=="], - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20241106.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw=="], + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260317.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg=="], - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20241106.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA=="], + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260317.1", "", { "os": "linux", "cpu": "x64" }, "sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug=="], - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20241106.1", "", { "os": "win32", "cpu": "x64" }, "sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg=="], + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260317.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw=="], - "@cloudflare/workers-shared": ["@cloudflare/workers-shared@0.8.0", "", { "dependencies": { "mime": "^3.0.0", "zod": "^3.22.3" } }, "sha512-1OvFkNtslaMZAJsaocTmbACApgmWv55uLpNj50Pn2MGcxdAjpqykXJFQw5tKc+lGV9TDZh9oO3Rsk17IEQDzIg=="], + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260317.1", "", { "os": "win32", "cpu": "x64" }, "sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ=="], "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], - "@esbuild-plugins/node-globals-polyfill": ["@esbuild-plugins/node-globals-polyfill@0.2.3", "", { "peerDependencies": { "esbuild": "*" } }, "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw=="], + "@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - "@esbuild-plugins/node-modules-polyfill": ["@esbuild-plugins/node-modules-polyfill@0.2.2", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "rollup-plugin-node-polyfills": "^0.2.1" }, "peerDependencies": { "esbuild": "*" } }, "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], - "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], @@ -137,55 +193,105 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="], + + "@oxc-project/types": ["@oxc-project/types@0.122.0", "", {}, "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA=="], + + "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], + + "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], + + "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm" }, "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.12", "", { "os": "none", "cpu": "arm64" }, "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.12", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "x64" }, "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.1", "", { "os": "android", "cpu": "arm64" }, "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.54.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.54.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.54.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.54.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.54.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.1", "", { "os": "linux", "cpu": "arm" }, "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.54.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.54.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.1", "", { "os": "linux", "cpu": "x64" }, "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.54.0", "", { "os": "none", "cpu": "arm64" }, "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.54.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.54.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="], + + "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], + + "@speed-highlight/core": ["@speed-highlight/core@1.2.15", "", {}, "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw=="], "@svgr/babel-plugin-add-jsx-attribute": ["@svgr/babel-plugin-add-jsx-attribute@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g=="], @@ -211,21 +317,11 @@ "@svgr/plugin-jsx": ["@svgr/plugin-jsx@8.1.0", "", { "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" }, "peerDependencies": { "@svgr/core": "*" } }, "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA=="], - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], - - "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], - - "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], - - "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - - "@types/dompurify": ["@types/dompurify@3.2.0", "", { "dependencies": { "dompurify": "*" } }, "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], - - "@types/node-forge": ["@types/node-forge@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw=="], + "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], @@ -235,49 +331,37 @@ "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], - - "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - - "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "as-table": ["as-table@1.0.55", "", { "dependencies": { "printable-characters": "^1.0.42" } }, "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.11", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ=="], "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], "caniuse-lite": ["caniuse-lite@1.0.30001761", "", {}, "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g=="], - "capnp-ts": ["capnp-ts@0.7.0", "", { "dependencies": { "debug": "^4.3.1", "tslib": "^2.2.0" } }, "sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g=="], - - "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - "data-uri-to-buffer": ["data-uri-to-buffer@2.0.2", "", {}, "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA=="], - - "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="], + "dompurify": ["dompurify@3.3.3", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="], "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], @@ -289,36 +373,24 @@ "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], - "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "get-source": ["get-source@2.0.12", "", { "dependencies": { "data-uri-to-buffer": "^2.0.0", "source-map": "^0.6.1" } }, "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w=="], - - "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - - "itty-time": ["itty-time@1.0.6", "", {}, "sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -329,6 +401,32 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], @@ -337,172 +435,148 @@ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - "magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], - - "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], - - "miniflare": ["miniflare@3.20241106.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "^8.8.0", "acorn-walk": "^8.2.0", "capnp-ts": "^0.7.0", "exit-hook": "^2.2.1", "glob-to-regexp": "^0.4.1", "stoppable": "^1.1.0", "undici": "^5.28.4", "workerd": "1.20241106.1", "ws": "^8.18.0", "youch": "^3.2.2", "zod": "^3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-dM3RBlJE8rUFxnqlPCaFCq0E7qQqEQvKbYX7W/APGCK+rLcyLmEBzC4GQR/niXdNM/oV6gdg9AA50ghnn2ALuw=="], + "miniflare": ["miniflare@4.20260317.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.4", "workerd": "1.20260317.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-tK78D3X4q30/SXqVwMhWrUfH+ffRou9dJLC+jkhNy5zh1I7i7T4JH6xihOvYxdCSBavJ5fQXaaxDJz6orh09BA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], - "node-forge": ["node-forge@1.3.3", "", {}, "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg=="], - "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], - "ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="], - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], - "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - "printable-characters": ["printable-characters@1.0.42", "", {}, "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ=="], + "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], - "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - - "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], - - "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - "resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="], - - "rollup": ["rollup@4.54.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.54.0", "@rollup/rollup-android-arm64": "4.54.0", "@rollup/rollup-darwin-arm64": "4.54.0", "@rollup/rollup-darwin-x64": "4.54.0", "@rollup/rollup-freebsd-arm64": "4.54.0", "@rollup/rollup-freebsd-x64": "4.54.0", "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", "@rollup/rollup-linux-arm-musleabihf": "4.54.0", "@rollup/rollup-linux-arm64-gnu": "4.54.0", "@rollup/rollup-linux-arm64-musl": "4.54.0", "@rollup/rollup-linux-loong64-gnu": "4.54.0", "@rollup/rollup-linux-ppc64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-musl": "4.54.0", "@rollup/rollup-linux-s390x-gnu": "4.54.0", "@rollup/rollup-linux-x64-gnu": "4.54.0", "@rollup/rollup-linux-x64-musl": "4.54.0", "@rollup/rollup-openharmony-arm64": "4.54.0", "@rollup/rollup-win32-arm64-msvc": "4.54.0", "@rollup/rollup-win32-ia32-msvc": "4.54.0", "@rollup/rollup-win32-x64-gnu": "4.54.0", "@rollup/rollup-win32-x64-msvc": "4.54.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw=="], + "rolldown": ["rolldown@1.0.0-rc.12", "", { "dependencies": { "@oxc-project/types": "=0.122.0", "@rolldown/pluginutils": "1.0.0-rc.12" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-x64": "1.0.0-rc.12", "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A=="], - "rollup-plugin-inject": ["rollup-plugin-inject@3.0.2", "", { "dependencies": { "estree-walker": "^0.6.1", "magic-string": "^0.25.3", "rollup-pluginutils": "^2.8.1" } }, "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w=="], - - "rollup-plugin-node-polyfills": ["rollup-plugin-node-polyfills@0.2.1", "", { "dependencies": { "rollup-plugin-inject": "^3.0.0" } }, "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA=="], - - "rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="], + "rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="], "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - "selfsigned": ["selfsigned@2.4.1", "", { "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" } }, "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q=="], - "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - "sourcemap-codec": ["sourcemap-codec@1.4.8", "", {}, "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="], - - "stacktracey": ["stacktracey@2.1.8", "", { "dependencies": { "as-table": "^1.0.36", "get-source": "^2.0.12" } }, "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw=="], - - "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], - - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], "svg-parser": ["svg-parser@2.0.4", "", {}, "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], - - "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "undici": ["undici@7.24.4", "", {}, "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], - "unenv": ["unenv-nightly@2.0.0-20241111-080453-894aa31", "", { "dependencies": { "defu": "^6.1.4", "ohash": "^1.1.4", "pathe": "^1.1.2", "ufo": "^1.5.4" } }, "sha512-0W39QQOQ9VE8kVVUpGwEG+pZcsCXk5wqNG6rDPE6Gr+fiA69LR0qERM61hW5KCOkC1/ArCFrfCGjwHyyv/bI0Q=="], + "unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], + "vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="], - "vite-plugin-svgr": ["vite-plugin-svgr@4.5.0", "", { "dependencies": { "@rollup/pluginutils": "^5.2.0", "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0" }, "peerDependencies": { "vite": ">=2.6.0" } }, "sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA=="], + "vite-plugin-svgr": ["vite-plugin-svgr@5.0.0", "", { "dependencies": { "@rollup/pluginutils": "^5.2.0", "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0" }, "peerDependencies": { "vite": ">=3.0.0" } }, "sha512-CZFWDtbWSLnF6C+uv8u7E5Ao6UVQYBpJrS6212XsEod/Lm4ErhOoFc01/po4ie5hqvMCr5KYrlMrSGQQEtMtBg=="], - "workerd": ["workerd@1.20241106.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20241106.1", "@cloudflare/workerd-darwin-arm64": "1.20241106.1", "@cloudflare/workerd-linux-64": "1.20241106.1", "@cloudflare/workerd-linux-arm64": "1.20241106.1", "@cloudflare/workerd-windows-64": "1.20241106.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw=="], + "workerd": ["workerd@1.20260317.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260317.1", "@cloudflare/workerd-darwin-arm64": "1.20260317.1", "@cloudflare/workerd-linux-64": "1.20260317.1", "@cloudflare/workerd-linux-arm64": "1.20260317.1", "@cloudflare/workerd-windows-64": "1.20260317.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ZuEq1OdrJBS+NV+L5HMYPCzVn49a2O60slQiiLpG44jqtlOo+S167fWC76kEXteXLLLydeuRrluRel7WdOUa4g=="], - "wrangler": ["wrangler@3.90.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@cloudflare/workers-shared": "0.8.0", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", "chokidar": "^4.0.1", "date-fns": "^4.1.0", "esbuild": "0.17.19", "itty-time": "^1.0.6", "miniflare": "3.20241106.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.3.0", "resolve": "^1.22.8", "resolve.exports": "^2.0.2", "selfsigned": "^2.0.1", "source-map": "^0.6.1", "unenv": "npm:unenv-nightly@2.0.0-20241111-080453-894aa31", "workerd": "1.20241106.1", "xxhash-wasm": "^1.0.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20241106.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-E/6E9ORAl987+3kP8wDiE3L1lj9r4vQ32/dl5toIxIkSMssmPRQVdxqwgMxbxJrytbFNo8Eo6swgjd4y4nUaLg=="], + "wrangler": ["wrangler@4.78.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.16.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260317.3", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260317.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260317.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-He/vUhk4ih0D0eFmtNnlbT6Od8j+BEokaSR+oYjbVsH0SWIrIch+eHqfLRSBjBQaOoh6HCNxcafcIkBm2u0Hag=="], - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - - "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], + "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], + "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], + + "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], + + "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.12", "", {}, "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw=="], + + "sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "wrangler/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], - "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], - "rollup-plugin-inject/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], + "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], - "rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], + "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], - "wrangler/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="], + "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], - "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="], + "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], - "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="], + "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], - "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="], + "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], - "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="], + "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], - "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="], + "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], - "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="], + "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], - "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="], + "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], - "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="], + "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], - "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="], + "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], - "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="], + "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], - "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="], + "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], - "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="], + "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], - "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="], + "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], - "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="], + "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], - "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="], + "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], - "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="], + "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], - "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="], + "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], - "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="], + "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], - "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="], + "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], - "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="], + "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], - "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="], + "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], - "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="], + "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], } } diff --git a/package.json b/package.json index 31a135d..7b22902 100644 --- a/package.json +++ b/package.json @@ -12,19 +12,25 @@ "test:watch": "bun test --watch" }, "dependencies": { - "dompurify": "^3.3.1", + "dompurify": "^3.3.2", "emoji-datasource": "^16.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "wrangler": "3.90.0" + "wrangler": "^4.78.0" }, "devDependencies": { - "@types/dompurify": "^3.2.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^4.3.1", + "@vitejs/plugin-react": "^6.0.1", + "bun-types": "^1.3.11", + "esbuild": "^0.25.5", + "picomatch": "^4.0.4", + "rollup": "^4.59.0", "typescript": "^5.5.4", - "vite": "^5.4.0", - "vite-plugin-svgr": "^4.5.0" + "vite": "^8.0.3", + "vite-plugin-svgr": "^5.0.0" + }, + "overrides": { + "picomatch": "^4.0.4" } } diff --git a/src/ui/utils/oauth.test.ts b/src/ui/utils/oauth.test.ts index 30543a9..9f6dc79 100644 --- a/src/ui/utils/oauth.test.ts +++ b/src/ui/utils/oauth.test.ts @@ -4,6 +4,7 @@ import { clearPendingOAuth, loadPendingOAuth, loadRegisteredApp, + type OAuthPending, saveRegisteredApp, storePendingOAuth } from "./oauth"; @@ -37,7 +38,7 @@ describe("oauth utils", () => { }); it("stores and clears pending OAuth state", () => { - const pending = { + const pending: OAuthPending = { platform: "mastodon", instanceUrl: "https://social.example", clientId: "client", diff --git a/tsconfig.json b/tsconfig.json index 8b1ed04..09df5d5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "strict": true + "strict": true, + "types": ["bun-types"] }, "include": ["src"] }