From f681f9dbb3fa794d9d4a861ad2dee0fedca313d9 Mon Sep 17 00:00:00 2001 From: "t3-code[bot]" <269035359+t3-code[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:06:05 +0000 Subject: [PATCH] feat(web): add Open VSX theme search (#5654) Co-authored-by: Julius Marminge Co-authored-by: codex (cherry picked from commit f0b57ca2313bd7971cefb366d3b3808152b014fc) --- apps/web/package.json | 3 + .../components/settings/ThemeEditorPanel.tsx | 37 +- .../components/settings/ThemeImportDialog.tsx | 28 +- .../settings/ThemeSearchSection.tsx | 389 +++++++++ .../src/components/settings/ThemeSettings.tsx | 535 +++++++++--- apps/web/src/openVsxThemes.test.ts | 526 ++++++++++++ apps/web/src/openVsxThemes.ts | 776 ++++++++++++++++++ apps/web/src/themePalette.test.ts | 247 ++++++ apps/web/src/themePalette.ts | 146 +++- apps/web/src/vscodeThemeImport.ts | 4 + pnpm-lock.yaml | 9 + 11 files changed, 2566 insertions(+), 134 deletions(-) create mode 100644 apps/web/src/components/settings/ThemeSearchSection.tsx create mode 100644 apps/web/src/openVsxThemes.test.ts create mode 100644 apps/web/src/openVsxThemes.ts diff --git a/apps/web/package.json b/apps/web/package.json index 0d61d8593..d7026a725 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -25,6 +25,7 @@ "@formkit/auto-animate": "^0.9.0", "@legendapp/list": "catalog:", "@lexical/react": "^0.41.0", + "@noble/hashes": "catalog:", "@pierre/diffs": "catalog:", "@pierre/trees": "1.0.0-beta.4", "@t3tools/client-runtime": "workspace:*", @@ -36,6 +37,8 @@ "culori": "^4.0.2", "effect": "catalog:", "jose": "catalog:", + "jsonc-parser": "3.3.1", + "jszip": "3.10.1", "lexical": "^0.41.0", "lucide-react": "^0.564.0", "react": "19.2.6", diff --git a/apps/web/src/components/settings/ThemeEditorPanel.tsx b/apps/web/src/components/settings/ThemeEditorPanel.tsx index 92f041cfc..56cb0ff63 100644 --- a/apps/web/src/components/settings/ThemeEditorPanel.tsx +++ b/apps/web/src/components/settings/ThemeEditorPanel.tsx @@ -610,7 +610,7 @@ export function ThemeEditorPanel({ [activeAppearance, editingTheme, selectedRole], ); - const handleSubmit = useCallback(() => { + const handleSubmit = () => { if (!name.trim()) { setError("Name your theme first."); return; @@ -645,8 +645,8 @@ export function ThemeEditorPanel({ return; } mergedAppearance = editedModes[0] ?? null; - savedTheme = updateCustomTheme( - parseThemeFile({ + savedTheme = updateCustomTheme({ + ...parseThemeFile({ version: THEME_FILE_VERSION, id: mergeTarget.id, name: mergeTarget.label, @@ -658,7 +658,8 @@ export function ThemeEditorPanel({ }, ...(mergeTarget.managed === true && !isAdvanced ? { managed: true } : {}), }), - ); + ...(mergeTarget.collection ? { collection: mergeTarget.collection } : {}), + }); retiredTheme = editingTheme; try { removeCustomTheme(editingTheme.id); @@ -676,8 +677,8 @@ export function ThemeEditorPanel({ } else if (editingTheme) { const baseAppearance = editingTheme.appearance; const variantAppearance = baseAppearance === "light" ? "dark" : "light"; - savedTheme = updateCustomTheme( - parseThemeFile({ + savedTheme = updateCustomTheme({ + ...parseThemeFile({ version: THEME_FILE_VERSION, id: editingTheme.id, name, @@ -688,7 +689,8 @@ export function ThemeEditorPanel({ : {}), ...(isAdvanced ? {} : { managed: true }), }), - ); + ...(editingTheme.collection ? { collection: editingTheme.collection } : {}), + }); } else if (mergeTarget) { if (takenAppearances.includes(activeAppearance)) { setError( @@ -701,8 +703,8 @@ export function ThemeEditorPanel({ // survives when every palette in the theme came from the guided // editor. mergedAppearance = activeAppearance; - savedTheme = updateCustomTheme( - parseThemeFile({ + savedTheme = updateCustomTheme({ + ...parseThemeFile({ version: THEME_FILE_VERSION, id: mergeTarget.id, name: mergeTarget.label, @@ -714,7 +716,8 @@ export function ThemeEditorPanel({ }, ...(mergeTarget.managed === true && !isAdvanced ? { managed: true } : {}), }), - ); + ...(mergeTarget.collection ? { collection: mergeTarget.collection } : {}), + }); } else { savedTheme = installCustomTheme( parseThemeFile({ @@ -762,19 +765,7 @@ export function ThemeEditorPanel({ : "Could not create the theme.", ); } - }, [ - activeAppearance, - colorsByAppearance, - editingTheme, - isAdvanced, - isEditing, - mergeTarget, - name, - onOpenChange, - onSaved, - simpleColorsDirtyByAppearance, - takenAppearances, - ]); + }; const renderNameField = () => (