Skip to content

Commit d27e594

Browse files
Keep Athas themes built in
Retire the installable Athas theme pack and reserve Athas Light/Dark identities for the built-in theme source.
1 parent 3693f7e commit d27e594

14 files changed

Lines changed: 204 additions & 171 deletions

extensions/official/theme-market/extension.json

Lines changed: 0 additions & 158 deletions
This file was deleted.

extensions/official/theme-market/icon.svg

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/extensions/marketplace/marketplace-extensions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ExtensionCategory, ExtensionManifest } from "../types/extension-manifest";
2+
import { filterRetiredExtensions } from "../registry/retired-extensions";
23
import {
34
getManifestAIProviderContributions,
45
getManifestDatabaseContributions,
@@ -89,7 +90,7 @@ export async function loadMarketplaceContributionExtensions(): Promise<Extension
8990

9091
try {
9192
const manifests = await fetchMarketplaceManifests();
92-
cachedMarketplaceExtensions = Object.values(manifests)
93+
cachedMarketplaceExtensions = filterRetiredExtensions(Object.values(manifests))
9394
.map((manifest) => ({
9495
...manifest,
9596
displayName: manifest.displayName || manifest.name,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { describe, expect, it } from "vite-plus/test";
2+
import type { ExtensionManifest } from "../types/extension-manifest";
3+
import { buildInstalledExtensionsMap } from "./extension-store-bootstrap";
4+
import type { AvailableExtension } from "./extension-store-types";
5+
6+
function createAvailableExtension(manifest: ExtensionManifest): AvailableExtension {
7+
return {
8+
manifest,
9+
isInstalled: false,
10+
isEnabled: false,
11+
isInstalling: false,
12+
runtimeIssues: [],
13+
};
14+
}
15+
16+
describe("extension-store bootstrap", () => {
17+
it("drops retired installed extensions before activation state is built", () => {
18+
const availableExtensions = new Map<string, AvailableExtension>([
19+
[
20+
"athas.theme.market",
21+
createAvailableExtension({
22+
id: "athas.theme.market",
23+
name: "Athas Themes",
24+
displayName: "Athas Theme Pack",
25+
description: "Retired theme pack",
26+
version: "1.0.0",
27+
publisher: "Athas",
28+
categories: ["Theme"],
29+
themes: [
30+
{
31+
id: "market-light",
32+
name: "Athas Light",
33+
appearance: "light",
34+
colors: {},
35+
syntax: {},
36+
},
37+
],
38+
}),
39+
],
40+
[
41+
"athas.theme.vercel",
42+
createAvailableExtension({
43+
id: "athas.theme.vercel",
44+
name: "vercel",
45+
displayName: "Vercel Theme",
46+
description: "Vercel theme",
47+
version: "1.0.0",
48+
publisher: "Athas",
49+
categories: ["Theme"],
50+
installation: { type: "bundled" },
51+
themes: [
52+
{
53+
id: "vercel-light",
54+
name: "Vercel Light",
55+
appearance: "light",
56+
colors: {},
57+
syntax: {},
58+
},
59+
],
60+
}),
61+
],
62+
]);
63+
64+
const installedExtensions = buildInstalledExtensionsMap({
65+
backendInstalled: [
66+
{
67+
id: "athas.theme.market",
68+
name: "Athas Theme Pack",
69+
version: "1.0.0",
70+
installed_at: "2026-07-08T00:00:00.000Z",
71+
enabled: true,
72+
},
73+
],
74+
indexedDBInstalled: [{ languageId: "athas.theme.market", version: "1.0.0" }],
75+
bundledContributionInstalled: ["athas.theme.market", "athas.theme.vercel"],
76+
availableExtensions,
77+
});
78+
79+
expect(installedExtensions.has("athas.theme.market")).toBe(false);
80+
expect(installedExtensions.has("athas.theme.vercel")).toBe(true);
81+
});
82+
});

src/extensions/registry/extension-store-bootstrap.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { readInstalledBundledContributionExtensionIds } from "./bundled-contribu
77
import { readDisabledExtensionIds } from "./extension-enabled-state";
88
import { initializeLanguagePackager } from "../languages/language-packager";
99
import { extensionRegistry } from "./extension-registry";
10+
import { isRetiredExtensionId } from "./retired-extensions";
1011
import {
1112
buildRuntimeManifest,
1213
getExtensionManifestForLanguage,
@@ -116,16 +117,22 @@ export function buildInstalledExtensionsMap(params: {
116117
} = params;
117118
const disabledExtensionIds = readDisabledExtensionIds();
118119
const installedExtensions = new Map(
119-
backendInstalled.map((extension) => [
120-
extension.id,
121-
{
122-
...extension,
123-
enabled: extension.enabled !== false && !disabledExtensionIds.has(extension.id),
124-
},
125-
]),
120+
backendInstalled
121+
.filter((extension) => !isRetiredExtensionId(extension.id))
122+
.map((extension) => [
123+
extension.id,
124+
{
125+
...extension,
126+
enabled: extension.enabled !== false && !disabledExtensionIds.has(extension.id),
127+
},
128+
]),
126129
);
127130

128131
for (const extensionId of bundledContributionInstalled) {
132+
if (isRetiredExtensionId(extensionId)) {
133+
continue;
134+
}
135+
129136
const extension = availableExtensions.get(extensionId);
130137
if (!extension || !isBundledContributionExtension(extension.manifest)) {
131138
continue;
@@ -142,6 +149,9 @@ export function buildInstalledExtensionsMap(params: {
142149

143150
for (const installed of indexedDBInstalled) {
144151
const extensionId = resolveInstalledExtensionId(installed, availableExtensions);
152+
if (isRetiredExtensionId(extensionId)) {
153+
continue;
154+
}
145155

146156
if (!installedExtensions.has(extensionId)) {
147157
const extension =

src/extensions/registry/extension-store.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
updateExtensionLifecycle,
3232
} from "./extension-store-lifecycle";
3333
import { markExtensionDisabled, markExtensionEnabled } from "./extension-enabled-state";
34+
import { isRetiredExtensionId } from "./retired-extensions";
3435
import { resolveInstalledExtensionId } from "./extension-store-runtime";
3536
import type { AvailableExtension, ExtensionInstallationMetadata } from "./extension-store-types";
3637
import type { ExtensionManifest } from "../types/extension-manifest";
@@ -100,6 +101,7 @@ const useExtensionStoreBase = create<ExtensionStoreState>()(
100101
...bundledContributionExtensions,
101102
...marketplaceExtensions,
102103
]) {
104+
if (isRetiredExtensionId(manifest.id)) continue;
103105
extensionById.set(manifest.id, manifest);
104106
}
105107

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { describe, expect, it } from "vite-plus/test";
2+
import { filterRetiredExtensions, isRetiredExtensionId } from "./retired-extensions";
3+
4+
describe("retired extensions", () => {
5+
it("retires the marketplace Athas theme pack", () => {
6+
expect(isRetiredExtensionId("athas.theme.market")).toBe(true);
7+
expect(isRetiredExtensionId("athas.theme.vercel")).toBe(false);
8+
});
9+
10+
it("filters retired extensions from marketplace and installed extension lists", () => {
11+
expect(
12+
filterRetiredExtensions([
13+
{ id: "athas.theme.market", name: "Athas Theme Pack" },
14+
{ id: "athas.theme.vercel", name: "Vercel Theme" },
15+
]),
16+
).toEqual([{ id: "athas.theme.vercel", name: "Vercel Theme" }]);
17+
});
18+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const RETIRED_EXTENSION_IDS = new Set(["athas.theme.market"]);
2+
3+
export function isRetiredExtensionId(extensionId: string): boolean {
4+
return RETIRED_EXTENSION_IDS.has(extensionId);
5+
}
6+
7+
export function filterRetiredExtensions<T extends { id: string }>(extensions: T[]): T[] {
8+
return extensions.filter((extension) => !isRetiredExtensionId(extension.id));
9+
}

src/extensions/runtime/extension-contribution-runtime.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { toSyntaxTokenVariables } from "../themes/syntax-token-colors";
88
import type { ThemeDefinition } from "../themes/types";
99
import type { ExtensionManifest } from "../types/extension-manifest";
1010
import { getManifestIconContributions } from "../types/extension-contributions";
11+
import { isRetiredExtensionId } from "../registry/retired-extensions";
1112
import {
1213
activateBundledContributionModule,
1314
deactivateBundledContributionModule,
@@ -209,6 +210,10 @@ export async function activateExtensionContributions(
209210
manifest: ExtensionManifest,
210211
extensionPath?: string,
211212
): Promise<void> {
213+
if (isRetiredExtensionId(extensionId)) {
214+
return;
215+
}
216+
212217
const iconThemes = getIconThemeContributions(manifest);
213218
const resolvedExtensionPath = await resolveContributionExtensionPath(
214219
extensionId,

src/features/extensions/scripts/build-extensions-index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
getContributionArray,
66
getExtensionCdnPath,
77
getExtensionSourceDir,
8+
getReservedBuiltInThemeContribution,
89
listExtensionFolders,
910
} from "./extension-workspace";
1011

@@ -119,6 +120,13 @@ async function buildCatalog() {
119120
const themes = getContributionArray(manifest, "themes");
120121
const icons = getContributionArray(manifest, "icons");
121122

123+
const reservedTheme = themes.find(getReservedBuiltInThemeContribution);
124+
if (reservedTheme) {
125+
throw new Error(
126+
`Extension ${manifest.id} contributes reserved built-in Athas theme "${String(reservedTheme.name || reservedTheme.id)}"`,
127+
);
128+
}
129+
122130
if (
123131
languages.length === 0 &&
124132
databases.length === 0 &&

0 commit comments

Comments
 (0)