From 21aac83de1a0e0a6e97cbca1e233fa35026720fe Mon Sep 17 00:00:00 2001 From: spoukyii <61633921+spoukyii@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:08:31 -0800 Subject: [PATCH 1/2] test(web): theme import sizes a file just under 1 MB Uploads a 1,048,100-byte theme file and reads the oversized notice. The dialog's own formatter picks KB before rounding, so the notice says "1024 KB" here and the test fails. --- .../ThemeImportDialog.browser.test.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/web/src/components/settings/ThemeImportDialog.browser.test.tsx b/apps/web/src/components/settings/ThemeImportDialog.browser.test.tsx index 81f3e499d7a30..5e10f6592ae2a 100644 --- a/apps/web/src/components/settings/ThemeImportDialog.browser.test.tsx +++ b/apps/web/src/components/settings/ThemeImportDialog.browser.test.tsx @@ -127,6 +127,29 @@ function isolateThemes() { }; } +it("sizes an oversized theme file just under 1 MB as 1.00 MB", async () => { + using _themes = isolateThemes(); + await using _screen = await render( + {}} + onImported={() => true} + onImportedMany={() => {}} + />, + ); + const input = document.querySelector('input[type="file"]')!; + await userEvent.upload( + input, + new File(["x".repeat(1_048_100)], "huge.json", { type: "application/json" }), + ); + + await expect + .element(page.getByText(/^That file is/)) + .toHaveTextContent( + "That file is 1.00 MB. Theme files are only a few KB, so this one was not read (limit 256 KB).", + ); +}); + function themeJsonc(name: string, background: string) { return `{ // VS Code themes use JSONC even with a .json extension. From c34c3ec10852b3be11b68969c5f8b239a57fdae6 Mon Sep 17 00:00:00 2001 From: spoukyii <61633921+spoukyii@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:10:51 -0800 Subject: [PATCH 2/2] fix(web): theme import no longer says a file just under 1 MB is "1024 KB" The theme import dialog formatted the size in its "That file is ..." notice with its own helper, which picked KB before rounding. A file from 1,048,064 to 1,048,575 bytes rounded to 1024 and read "1024 KB". That is the defect #26067 fixed in the shared formatBytes, and #28300 and #29165 removed from the Diagnostics and attachment sizes. The dialog now uses formatBytes from lib, so it picks the unit after rounding like the rest of the app. The 256 KB limit still reads "256 KB". Bug: theme import says a file just under 1 MB is "1024 KB" --- apps/web/src/components/settings/ThemeImportDialog.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/web/src/components/settings/ThemeImportDialog.tsx b/apps/web/src/components/settings/ThemeImportDialog.tsx index 5c8911623dfed..dc093d55b65c9 100644 --- a/apps/web/src/components/settings/ThemeImportDialog.tsx +++ b/apps/web/src/components/settings/ThemeImportDialog.tsx @@ -2,6 +2,7 @@ import { DownloadIcon, PlusIcon } from "lucide-react"; import type { ChangeEvent, DragEvent, UIEvent } from "react"; import { settlePromise } from "@t3tools/client-runtime/state/runtime"; import { useRef, useState } from "react"; +import { formatBytes } from "../../lib/formatBytes"; import { cn } from "../../lib/utils"; import { getCustomThemes, @@ -33,15 +34,9 @@ const MAX_THEME_FILE_BYTES = 256 * 1024; /** Highlighting rebuilds markup per keystroke; fall back to plain text for oversized pastes. */ const MAX_HIGHLIGHTED_JSON_LENGTH = 20_000; -function formatByteSize(bytes: number): string { - if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; - if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`; - return `${bytes} bytes`; -} - function describeOversizedThemeFile(bytes: number): string | null { if (bytes <= MAX_THEME_FILE_BYTES) return null; - return `That file is ${formatByteSize(bytes)}. Theme files are only a few KB, so this one was not read (limit ${formatByteSize(MAX_THEME_FILE_BYTES)}).`; + return `That file is ${formatBytes(bytes)}. Theme files are only a few KB, so this one was not read (limit ${formatBytes(MAX_THEME_FILE_BYTES)}).`; } function escapeJsonHtml(value: string): string {