@danielng23/dsh-client-ui-theme-store 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -0
- package/catalog/edex-themes.json +634 -0
- package/lib/client.js +1591 -0
- package/lib/index.js +1093 -0
- package/lib/invariant.js +19 -0
- package/lib/types/client/ThemeStoreSection.d.ts +60 -0
- package/lib/types/client/catalog.d.ts +84 -0
- package/lib/types/client/index.d.ts +40 -0
- package/lib/types/client/locales.d.ts +60 -0
- package/lib/types/client/settings-store.d.ts +35 -0
- package/lib/types/client/theme-store.d.ts +95 -0
- package/lib/types/host/installer.d.ts +82 -0
- package/lib/types/index.d.ts +16 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/theme-store-settings.d.ts +14 -0
- package/package.json +104 -0
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion.
|
|
4
|
+
* @module @danielng23/dsh-client-ui-theme-store/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@danielng23/dsh-client-ui-theme-store";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "client-ui-theme-store-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
const install = () => {};
|
|
12
|
+
/**
|
|
13
|
+
* Register this package's invariant companion.
|
|
14
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
15
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
16
|
+
*/
|
|
17
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
18
|
+
//#endregion
|
|
19
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme store section: a Settings page that lists repository themes as image
|
|
3
|
+
* cards. Color themes apply inline (`register` + `setTheme`); shell themes
|
|
4
|
+
* (eDEX variants) also preview their palette but need a pnpm install for the
|
|
5
|
+
* full UI, so their card surfaces the install command and copies it on demand.
|
|
6
|
+
*
|
|
7
|
+
* The component receives the store mirror (catalog status/themes/applied/
|
|
8
|
+
* installedShells) and injected callbacks. It is a pure props consumer — no
|
|
9
|
+
* React context, no ctx, no subscription machinery.
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactNode } from 'react';
|
|
12
|
+
import type { PropsLocale, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
|
|
13
|
+
import type { createThemeStoreStore } from './settings-store.ts';
|
|
14
|
+
/** Install progress stages surfaced to the card's status bar. */
|
|
15
|
+
export type ThemeInstallStage = 'installing' | 'restarting' | 'reloading';
|
|
16
|
+
/**
|
|
17
|
+
* sessionStorage key the install flow writes right before `location.reload()`
|
|
18
|
+
* so the section can show a visible "installed and restarted" confirmation
|
|
19
|
+
* after the page comes back (the reload wipes the DevTools console).
|
|
20
|
+
*/
|
|
21
|
+
export declare const LAST_INSTALL_KEY = "theme-store.last-install";
|
|
22
|
+
/** Marker payload read back by the section after the reload. */
|
|
23
|
+
export interface LastInstallMarker {
|
|
24
|
+
/** Catalog theme id that was installed. */
|
|
25
|
+
id: string;
|
|
26
|
+
/** Display name of the installed theme. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** ISO timestamp of the install. */
|
|
29
|
+
ts: string;
|
|
30
|
+
}
|
|
31
|
+
/** Injected business face: load/retry, apply, install-command copy, and shell-theme install. */
|
|
32
|
+
export interface ThemeStoreSectionInjected {
|
|
33
|
+
/** Load (or re-load) the theme catalog. */
|
|
34
|
+
load: () => void;
|
|
35
|
+
/** Apply a catalog theme by id (color preview for shell themes). */
|
|
36
|
+
apply: (id: string) => void;
|
|
37
|
+
/** Copy a shell theme's install command to the clipboard. */
|
|
38
|
+
copyInstall: (command: string) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Install a shell theme's bundle into the active profile (pnpm add +
|
|
41
|
+
* profile patch rewrite), respawn the GUI, and reload once it is back.
|
|
42
|
+
* Reports progress through the optional stage callback so the card can
|
|
43
|
+
* render a status bar. Writes a last-install marker before the reload so
|
|
44
|
+
* the section can confirm the install after the page returns. Throws on
|
|
45
|
+
* error.
|
|
46
|
+
* @param installPackage - npm bundle package to install.
|
|
47
|
+
* @param onStage - optional progress callback.
|
|
48
|
+
* @param marker - catalog theme identity to confirm after the reload.
|
|
49
|
+
*/
|
|
50
|
+
installShell: (installPackage: string, onStage?: (stage: ThemeInstallStage) => void, marker?: LastInstallMarker) => Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
/** Full component props: runtime share + store share + locale seat + injected face. */
|
|
53
|
+
export type ThemeStoreSectionComponentProps = PropsRuntime<'settings.section'> & PropsStore<ReturnType<typeof createThemeStoreStore>> & PropsLocale<'settings.themeStore'> & ThemeStoreSectionInjected;
|
|
54
|
+
/**
|
|
55
|
+
* Render the theme store section.
|
|
56
|
+
* @param props - composed slot props.
|
|
57
|
+
* @returns the section element tree.
|
|
58
|
+
*/
|
|
59
|
+
export declare function ThemeStoreSection({ t, useStore, apply, load, installShell }: ThemeStoreSectionComponentProps): ReactNode;
|
|
60
|
+
//# sourceMappingURL=ThemeStoreSection.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme store catalog: types, runtime validation, and remote loading.
|
|
3
|
+
*
|
|
4
|
+
* The catalog is a plain JSON document hosted in this repository (pushed to
|
|
5
|
+
* GitHub) and fetched at runtime by the browser half. It is deliberately
|
|
6
|
+
* category-free: one flat `themes` array whose entries carry the display
|
|
7
|
+
* metadata the store renders — `name`, `author`, `screenshot` — plus the
|
|
8
|
+
* functional fields needed to apply a theme through the harness theme
|
|
9
|
+
* service (`id`, `colorScheme`, `tokens`).
|
|
10
|
+
*/
|
|
11
|
+
import type { ThemeDefinition } from '@deepseek-ai/dsh-client-ui-theme/client';
|
|
12
|
+
/**
|
|
13
|
+
* Default catalog URL. Served locally by this plugin's node half at the
|
|
14
|
+
* webserver route `/catalog/edex-themes.json` (same origin as the GUI), so
|
|
15
|
+
* themes load without any GitHub push. Overridable at build time via
|
|
16
|
+
* DSH_CLIENT_THEME_STORE_CATALOG_URL.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_CATALOG_URL = "/catalog/edex-themes.json";
|
|
19
|
+
/** Resolve the effective catalog URL (build-time override, else default). */
|
|
20
|
+
export declare function catalogUrl(): string;
|
|
21
|
+
/**
|
|
22
|
+
* What a theme changes when applied.
|
|
23
|
+
* - `color`: a pure token override layer — applying it recolors the base UI
|
|
24
|
+
* in-process (the theme store's `register` + `setTheme` path).
|
|
25
|
+
* - `shell`: a full UI shell (eDEX variants) — the color tokens are only the
|
|
26
|
+
* palette preview; the actual shell must be installed into a profile with
|
|
27
|
+
* pnpm (`dsh plugin --profile <name> add <bundle>`).
|
|
28
|
+
*/
|
|
29
|
+
export type CatalogThemeKind = 'color' | 'shell';
|
|
30
|
+
/**
|
|
31
|
+
* One catalog theme: display metadata plus the functional {@link ThemeDefinition}.
|
|
32
|
+
* The user-visible card renders `name`, `author`, and `screenshot`; applying
|
|
33
|
+
* the theme hands `id`/`colorScheme`/`tokens` to the theme service.
|
|
34
|
+
*/
|
|
35
|
+
export interface CatalogTheme extends ThemeDefinition {
|
|
36
|
+
/** Display name (product copy). */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Author attribution. */
|
|
39
|
+
author: string;
|
|
40
|
+
/**
|
|
41
|
+
* Preview image: an absolute URL, or a path relative to the catalog
|
|
42
|
+
* document's directory (resolved against the catalog URL).
|
|
43
|
+
*/
|
|
44
|
+
screenshot: string;
|
|
45
|
+
/** What the theme changes; defaults to `color` for backward compatibility. */
|
|
46
|
+
type?: CatalogThemeKind;
|
|
47
|
+
/** npm bundle package to install for a `shell` theme (e.g. `@danielng23/dsh-edex-armory-ui`). */
|
|
48
|
+
installPackage?: string;
|
|
49
|
+
/** The shell plugin's client package name, used to detect an installed shell (e.g. `@danielng23/dsh-armory-client-ui-edex`). */
|
|
50
|
+
shellPluginId?: string;
|
|
51
|
+
/** Copyable `dsh plugin` command that installs this shell theme into a profile. */
|
|
52
|
+
installHint?: string;
|
|
53
|
+
}
|
|
54
|
+
/** Parsed catalog document (flat, category-free). */
|
|
55
|
+
export interface ThemeCatalog {
|
|
56
|
+
/** Catalog themes in display order. */
|
|
57
|
+
themes: readonly CatalogTheme[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a screenshot reference against the catalog document URL.
|
|
61
|
+
* @param screenshot - absolute URL or catalog-relative path.
|
|
62
|
+
* @param catalogHref - the catalog document's absolute URL (or an origin-relative path).
|
|
63
|
+
* @returns an absolute, fetchable image URL.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveScreenshot(screenshot: string, catalogHref: string): string;
|
|
66
|
+
/** Error thrown when the fetched catalog is not a valid theme catalog. */
|
|
67
|
+
export declare class CatalogParseError extends Error {
|
|
68
|
+
readonly name = "CatalogParseError";
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Runtime-validate an unknown parsed JSON value as a {@link ThemeCatalog}.
|
|
72
|
+
* Rejects malformed shapes with a teaching error; returns defensive copies so
|
|
73
|
+
* later caller mutation cannot reach the stored catalog.
|
|
74
|
+
* @param value - the parsed JSON document.
|
|
75
|
+
* @returns the validated catalog.
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseCatalog(value: unknown): ThemeCatalog;
|
|
78
|
+
/**
|
|
79
|
+
* Fetch and parse the theme catalog from a URL.
|
|
80
|
+
* @param href - catalog document URL (defaults to {@link catalogUrl}).
|
|
81
|
+
* @returns the validated catalog.
|
|
82
|
+
*/
|
|
83
|
+
export declare function fetchCatalog(href?: string): Promise<ThemeCatalog>;
|
|
84
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
+
import { ThemeStoreRuntime } from './theme-store.ts';
|
|
3
|
+
import { type ThemeStoreLocaleKey } from './locales.ts';
|
|
4
|
+
export type { ThemeStoreSectionComponentProps, ThemeStoreSectionInjected, } from './ThemeStoreSection.tsx';
|
|
5
|
+
export type { ThemeStoreState } from './settings-store.ts';
|
|
6
|
+
export { createThemeStoreStore } from './settings-store.ts';
|
|
7
|
+
export type { ThemeStoreSnapshot, ThemeStoreStatus } from './theme-store.ts';
|
|
8
|
+
export { ThemeStoreRuntime } from './theme-store.ts';
|
|
9
|
+
export type { CatalogTheme, ThemeCatalog } from './catalog.ts';
|
|
10
|
+
export { parseCatalog, resolveScreenshot } from './catalog.ts';
|
|
11
|
+
export type { ThemeStoreLocaleKey } from './locales.ts';
|
|
12
|
+
export type { ThemeStoreSettings } from '../theme-store-settings.ts';
|
|
13
|
+
/** Namespace owning this feature's settings-row copy. */
|
|
14
|
+
export declare const SETTINGS_NS = "settings.themeStore";
|
|
15
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
16
|
+
interface LocaleNamespaceMap {
|
|
17
|
+
/** The Theme Store settings section's copy. */
|
|
18
|
+
'settings.themeStore': ThemeStoreLocaleKey;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
declare module '@deepseek-ai/cordis' {
|
|
22
|
+
interface Context {
|
|
23
|
+
/** Theme catalog store service: load, apply, and durable applied id. */
|
|
24
|
+
themeStore: ThemeStoreRuntime;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Required services: slots/locale for the section, the settings scope for the
|
|
29
|
+
* durable applied id, the harness theme service, the remote transport, and
|
|
30
|
+
* the plugin inventory Remote to detect installed shell themes.
|
|
31
|
+
*/
|
|
32
|
+
export declare const inject: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Client plugin body: provide the theme store service and register the
|
|
35
|
+
* feature-owned Theme Store settings section. On boot, also queries the
|
|
36
|
+
* plugin inventory to detect which shell themes are already installed.
|
|
37
|
+
* @param ctx - client cordis context.
|
|
38
|
+
*/
|
|
39
|
+
export declare function apply(ctx: ClientContext): void;
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Copy dictionaries for the theme store Settings section. */
|
|
2
|
+
/** Simplified Chinese dictionary and key source of truth. */
|
|
3
|
+
export declare const zh: {
|
|
4
|
+
nav: string;
|
|
5
|
+
title: string;
|
|
6
|
+
subtitle: string;
|
|
7
|
+
loading: string;
|
|
8
|
+
error: string;
|
|
9
|
+
retry: string;
|
|
10
|
+
empty: string;
|
|
11
|
+
author: string;
|
|
12
|
+
light: string;
|
|
13
|
+
dark: string;
|
|
14
|
+
apply: string;
|
|
15
|
+
applied: string;
|
|
16
|
+
install: string;
|
|
17
|
+
installing: string;
|
|
18
|
+
restarting: string;
|
|
19
|
+
reloading: string;
|
|
20
|
+
installFailed: string;
|
|
21
|
+
installHint: string;
|
|
22
|
+
copyCommand: string;
|
|
23
|
+
copied: string;
|
|
24
|
+
colorTheme: string;
|
|
25
|
+
shellTheme: string;
|
|
26
|
+
shellInstallNote: string;
|
|
27
|
+
installedBanner: string;
|
|
28
|
+
dismiss: string;
|
|
29
|
+
};
|
|
30
|
+
/** Theme store locale key union. */
|
|
31
|
+
export type ThemeStoreLocaleKey = keyof typeof zh;
|
|
32
|
+
/** English dictionary checked against the Chinese key set. */
|
|
33
|
+
export declare const en: {
|
|
34
|
+
nav: string;
|
|
35
|
+
title: string;
|
|
36
|
+
subtitle: string;
|
|
37
|
+
loading: string;
|
|
38
|
+
error: string;
|
|
39
|
+
retry: string;
|
|
40
|
+
empty: string;
|
|
41
|
+
author: string;
|
|
42
|
+
light: string;
|
|
43
|
+
dark: string;
|
|
44
|
+
apply: string;
|
|
45
|
+
applied: string;
|
|
46
|
+
install: string;
|
|
47
|
+
installing: string;
|
|
48
|
+
restarting: string;
|
|
49
|
+
reloading: string;
|
|
50
|
+
installFailed: string;
|
|
51
|
+
installHint: string;
|
|
52
|
+
copyCommand: string;
|
|
53
|
+
copied: string;
|
|
54
|
+
colorTheme: string;
|
|
55
|
+
shellTheme: string;
|
|
56
|
+
shellInstallNote: string;
|
|
57
|
+
installedBanner: string;
|
|
58
|
+
dismiss: string;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme store section slot store: mirrors the theme store runtime snapshot.
|
|
3
|
+
* The plugin's apply-world change listener is the only writer; the section
|
|
4
|
+
* component reads via props.useStore.
|
|
5
|
+
*/
|
|
6
|
+
import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client';
|
|
7
|
+
import type { ThemeStoreStatus } from './theme-store.ts';
|
|
8
|
+
import type { ThemeStoreSnapshot } from './theme-store.ts';
|
|
9
|
+
import type { CatalogTheme } from './catalog.ts';
|
|
10
|
+
/** Store state mirrored from the theme store runtime. */
|
|
11
|
+
export interface ThemeStoreState {
|
|
12
|
+
/** Catalog loading phase. */
|
|
13
|
+
status: ThemeStoreStatus;
|
|
14
|
+
/** Loaded catalog themes (empty until ready). */
|
|
15
|
+
themes: readonly CatalogTheme[];
|
|
16
|
+
/** Applied catalog theme id (undefined when built-in light/dark/system is active). */
|
|
17
|
+
applied: string | undefined;
|
|
18
|
+
/** Shell plugin package ids currently installed in the active profile. */
|
|
19
|
+
installedShells: readonly string[];
|
|
20
|
+
/** Human-readable load error. */
|
|
21
|
+
error: string | undefined;
|
|
22
|
+
/** Runtime revision; -1 until first sync so revision 0 lands as a change. */
|
|
23
|
+
revision: number;
|
|
24
|
+
}
|
|
25
|
+
/** Declared action shape giving the exported factory a stable return type. */
|
|
26
|
+
type ThemeStoreActions = {
|
|
27
|
+
sync: (draft: ThemeStoreState, snapshot: ThemeStoreSnapshot) => void;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Declares the theme store section state and write surface.
|
|
31
|
+
* @returns the store handle.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createThemeStoreStore(): EngineStoreHandle<ThemeStoreState, ThemeStoreActions>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=settings-store.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme store runtime: owns catalog loading, theme registration/application
|
|
3
|
+
* through the harness theme service, and durable persistence of the applied
|
|
4
|
+
* third-party theme id in the `ui-theme-store` settings namespace.
|
|
5
|
+
*
|
|
6
|
+
* The runtime is a business service (never touches DOM/React); the settings
|
|
7
|
+
* section mirrors its state through a declared store. It publishes an
|
|
8
|
+
* immutable {@link ThemeStoreSnapshot} on every change through the snapshot
|
|
9
|
+
* store it exposes (`getState`/`subscribe`).
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
|
|
13
|
+
import type { ThemeRuntime } from '@deepseek-ai/dsh-client-ui-theme/client';
|
|
14
|
+
import { type CatalogTheme } from './catalog.ts';
|
|
15
|
+
import type { ThemeStoreSettings } from '../theme-store-settings.ts';
|
|
16
|
+
/** Catalog loading phase. */
|
|
17
|
+
export type ThemeStoreStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
18
|
+
/** Immutable theme store state published on every change. */
|
|
19
|
+
export interface ThemeStoreSnapshot {
|
|
20
|
+
/** Catalog loading phase. */
|
|
21
|
+
status: ThemeStoreStatus;
|
|
22
|
+
/** Loaded catalog themes in display order (empty until ready). */
|
|
23
|
+
themes: readonly CatalogTheme[];
|
|
24
|
+
/** Id of the currently applied catalog theme (undefined when a built-in preference is active). */
|
|
25
|
+
applied: string | undefined;
|
|
26
|
+
/** Shell plugin package ids currently installed in the active profile (inventory detection). */
|
|
27
|
+
installedShells: readonly string[];
|
|
28
|
+
/** Human-readable load error (undefined unless status is `error`). */
|
|
29
|
+
error: string | undefined;
|
|
30
|
+
/** Monotonic change counter (status, themes, or applied changes). */
|
|
31
|
+
revision: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Theme catalog store: loads the repository catalog, registers catalog themes
|
|
35
|
+
* with the harness theme service on apply (lazily, so a duplicate id never
|
|
36
|
+
* throws at load), tracks which shell plugins the active profile has
|
|
37
|
+
* installed, and persists the applied theme id across reloads.
|
|
38
|
+
*/
|
|
39
|
+
export declare class ThemeStoreRuntime {
|
|
40
|
+
private readonly host;
|
|
41
|
+
private readonly theme;
|
|
42
|
+
private readonly href;
|
|
43
|
+
private readonly store;
|
|
44
|
+
/** Theme ids this runtime registered with the theme service (id → disposer). */
|
|
45
|
+
private readonly registered;
|
|
46
|
+
/** The active applied theme id. */
|
|
47
|
+
private applied;
|
|
48
|
+
private disposed;
|
|
49
|
+
private readonly offTheme;
|
|
50
|
+
private readonly offHost;
|
|
51
|
+
/**
|
|
52
|
+
* @param ctx - owning context (change and theme listeners are released on dispose).
|
|
53
|
+
* @param host - durable `ui-theme-store` scope owned by the same plugin.
|
|
54
|
+
* @param theme - harness theme service (register/setTheme).
|
|
55
|
+
* @param href - catalog document URL (defaults to the build-time catalog URL).
|
|
56
|
+
*/
|
|
57
|
+
constructor(ctx: Context, host: SettingsScope<ThemeStoreSettings>, theme: ThemeRuntime, href?: string);
|
|
58
|
+
/** @returns the current immutable snapshot (stable reference until the next change). */
|
|
59
|
+
getState(): ThemeStoreSnapshot;
|
|
60
|
+
/** Observe snapshot replacements. @returns the disposer. */
|
|
61
|
+
subscribe(listener: () => void): () => void;
|
|
62
|
+
/**
|
|
63
|
+
* Load the catalog from the configured URL and reconcile the persisted
|
|
64
|
+
* applied theme. Safe to call repeatedly (retry); a failed fetch leaves
|
|
65
|
+
* the previous ready state intact and reports an error snapshot.
|
|
66
|
+
* @returns completion of the load.
|
|
67
|
+
*/
|
|
68
|
+
load(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Apply a catalog theme: register it with the harness theme service if
|
|
71
|
+
* needed, switch the active preference to it, and persist the id.
|
|
72
|
+
* @param id - a catalog theme id.
|
|
73
|
+
*/
|
|
74
|
+
apply(id: string): void;
|
|
75
|
+
/**
|
|
76
|
+
* Adopt the currently installed shell plugin ids from the profile's plugin
|
|
77
|
+
* inventory. The store reports shell themes as "installed" when their
|
|
78
|
+
* `shellPluginId` appears here; color themes are unaffected.
|
|
79
|
+
* @param shellPluginIds - module names of installed client shell plugins.
|
|
80
|
+
*/
|
|
81
|
+
syncInstalledShells(shellPluginIds: readonly string[]): void;
|
|
82
|
+
/**
|
|
83
|
+
* Release the plugin's registrations and listeners. Disposing the active
|
|
84
|
+
* theme resets the harness preference to its default, exactly as the theme
|
|
85
|
+
* service's own disposer contract specifies.
|
|
86
|
+
*/
|
|
87
|
+
dispose(): void;
|
|
88
|
+
private ensureRegistered;
|
|
89
|
+
/** Keep `applied` in sync with the theme service's active preference. */
|
|
90
|
+
private adoptApplied;
|
|
91
|
+
/** Restore a persisted applied theme id once the catalog is ready. */
|
|
92
|
+
private adoptPersisted;
|
|
93
|
+
private publish;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=theme-store.d.ts.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side shell-theme installer: installs an eDEX variant's packages into
|
|
3
|
+
* the active profile with pnpm and rewrites the profile's `cordis.patch.yml`
|
|
4
|
+
* to mount the variant's rows with VARIANT-SPECIFIC row ids
|
|
5
|
+
* (`<variant>-host-system-metrics`, `<variant>-ui-edex`,
|
|
6
|
+
* `<variant>-ui-theme-terminal`).
|
|
7
|
+
*
|
|
8
|
+
* The profile patch is hot-reloaded by the harness, and a page reload then
|
|
9
|
+
* boots against the new client graph — so "Apply" on a shell theme actually
|
|
10
|
+
* installs and mounts the full UI, not just the color. Switching variants
|
|
11
|
+
* works because each variant owns distinct row ids: the Loader disposes the
|
|
12
|
+
* previous variant's fibers (rows removed) and creates the new one's (rows
|
|
13
|
+
* inserted) — never reusing an id with a different module, which the config
|
|
14
|
+
* hot-reload would not re-fiber.
|
|
15
|
+
*
|
|
16
|
+
* The active profile is discovered by scanning `$DSH_HOME/profiles/*` for the
|
|
17
|
+
* directory whose `node_modules/@danielng23/dsh-client-ui-theme-store`
|
|
18
|
+
* resolves to this package.
|
|
19
|
+
*/
|
|
20
|
+
/** The variant id derived from a bundle package name (`@danielng23/dsh-edex-armory-ui` → `armory`). */
|
|
21
|
+
export declare function variantIdOf(bundlePackage: string): string;
|
|
22
|
+
/** Resolve $DSH_HOME (mirrors dsh-home-paths): $DSH_HOME, else ~/.dsh. */
|
|
23
|
+
declare function dshHome(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Find the active profile directory: the one whose node_modules contains
|
|
26
|
+
* this package (a `file:` link into the harness profile).
|
|
27
|
+
* @returns the profile directory, or undefined when none matches.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveActiveProfileDir(): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The variant's three rows, derived from the bundle's own cordis.patch rows
|
|
32
|
+
* but re-keyed with variant-specific ids. The bundle patch is the
|
|
33
|
+
* authoritative source of the row NAMES, so an install stays correct even
|
|
34
|
+
* when a variant changes its patch; the ids are prefixed so switching
|
|
35
|
+
* variants replaces fibers instead of reusing an id with a new module.
|
|
36
|
+
* @param bundlePackage - the variant bundle package name.
|
|
37
|
+
* @param profileDir - the active profile directory.
|
|
38
|
+
* @returns the {id, name} rows to mount (id prefixed with the variant id).
|
|
39
|
+
*/
|
|
40
|
+
declare function variantRowsOf(bundlePackage: string, profileDir: string): {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
}[];
|
|
44
|
+
/**
|
|
45
|
+
* Rewrite the profile's cordis.patch.yml so the variant rows point at the
|
|
46
|
+
* given packages. Removes every previous variant's rows (any id ending in the
|
|
47
|
+
* variant suffixes), preserves all other rows (the theme-store row, user
|
|
48
|
+
* rows), and appends the new variant's rows. Returns whether the document
|
|
49
|
+
* actually changed (false when the variant was already mounted verbatim).
|
|
50
|
+
* @param profileDir - the active profile directory.
|
|
51
|
+
* @param rows - the variant rows to mount (variant-prefixed id + package name).
|
|
52
|
+
* @returns true when the patch file changed.
|
|
53
|
+
*/
|
|
54
|
+
export declare function writeVariantPatch(profileDir: string, rows: {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
}[]): boolean;
|
|
58
|
+
/** Result of a shell-theme install. */
|
|
59
|
+
export interface InstallResult {
|
|
60
|
+
ok: boolean;
|
|
61
|
+
/** Whether the profile patch actually changed (false when the variant was already mounted). */
|
|
62
|
+
changed: boolean;
|
|
63
|
+
error?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Install a shell theme's variant into the active profile: pnpm-add the
|
|
67
|
+
* bundle's three packages, then point the profile patch's variant rows at
|
|
68
|
+
* them. Returns the install outcome; the browser reloads on success.
|
|
69
|
+
* @param bundlePackage - the variant bundle package (e.g. `@danielng23/dsh-edex-armory-ui`).
|
|
70
|
+
* @returns the install result.
|
|
71
|
+
*/
|
|
72
|
+
export declare function installShellTheme(bundlePackage: string): InstallResult;
|
|
73
|
+
/** Exposed for tests. */
|
|
74
|
+
export declare const internals: {
|
|
75
|
+
dshHome: typeof dshHome;
|
|
76
|
+
resolveActiveProfileDir: typeof resolveActiveProfileDir;
|
|
77
|
+
variantRowsOf: typeof variantRowsOf;
|
|
78
|
+
writeVariantPatch: typeof writeVariantPatch;
|
|
79
|
+
variantIdOf: typeof variantIdOf;
|
|
80
|
+
};
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=installer.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host registration for the theme store settings namespace (persists the
|
|
3
|
+
* applied third-party theme id across reloads), serves the eDEX theme catalog
|
|
4
|
+
* JSON locally, and provides a POST route to install shell themes (pnpm add
|
|
5
|
+
* + profile patch rewrite) — so "Apply" on a shell theme actually installs
|
|
6
|
+
* and mounts the full UI.
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
export { THEME_STORE_APPLIED_FIELD, THEME_STORE_NAMESPACE, ThemeStoreSettingsSchema, type ThemeStoreSettings, } from './theme-store-settings.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Register the durable theme-store section, serve the catalog, and mount the
|
|
12
|
+
* shell-theme installer route.
|
|
13
|
+
* @param ctx - Host context.
|
|
14
|
+
*/
|
|
15
|
+
export declare function apply(ctx: Context): void;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion.
|
|
3
|
+
* @module @danielng23/dsh-client-ui-theme-store/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "client-ui-theme-store-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Theme store settings stored in the Host user-settings document. */
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
|
+
/** Settings namespace owned by the theme store plugin. */
|
|
4
|
+
export declare const THEME_STORE_NAMESPACE = "ui-theme-store";
|
|
5
|
+
/** Field carrying the currently applied theme id. */
|
|
6
|
+
export declare const THEME_STORE_APPLIED_FIELD = "applied";
|
|
7
|
+
/** Durable settings section shared by the Host schema and the browser scope. */
|
|
8
|
+
export interface ThemeStoreSettings {
|
|
9
|
+
/** Applied theme id (empty string = none / built-in preference active). */
|
|
10
|
+
applied: string;
|
|
11
|
+
}
|
|
12
|
+
/** Durable schema; also the wire envelope the browser scope validates against. */
|
|
13
|
+
export declare const ThemeStoreSettingsSchema: z<ThemeStoreSettings>;
|
|
14
|
+
//# sourceMappingURL=theme-store-settings.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@danielng23/dsh-client-ui-theme-store",
|
|
3
|
+
"description": "Theme store plugin for the DeepSeek Harness web GUI: a settings Theme Store page that reads a JSON theme catalog from this repository (GitHub), shows name/author/screenshot cards, and applies selected --dsw-* token themes through the harness theme service",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ph4310822/dsh-edex-themes.git",
|
|
11
|
+
"directory": "."
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./client": {
|
|
26
|
+
"types": "./lib/types/client/index.d.ts",
|
|
27
|
+
"default": "./lib/client.js"
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"dsh": {
|
|
33
|
+
"client": {
|
|
34
|
+
"inject": [
|
|
35
|
+
"@deepseek-ai/dsh-client-connection",
|
|
36
|
+
"@deepseek-ai/dsh-client-locale",
|
|
37
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
39
|
+
"@deepseek-ai/dsh-client-ui-theme",
|
|
40
|
+
"@deepseek-ai/dsh-api-remotes"
|
|
41
|
+
],
|
|
42
|
+
"platform": "web"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
48
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-theme": "^0.1.1-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
60
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.1-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-theme": "^0.1.1-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
71
|
+
"@deepseek-ai/schemastery": "^3.18.1-rc.4",
|
|
72
|
+
"@testing-library/dom": "^10.4.1",
|
|
73
|
+
"@testing-library/react": "^16.1.0",
|
|
74
|
+
"@types/js-yaml": "^4.0.9",
|
|
75
|
+
"@types/node": "^26.4.0",
|
|
76
|
+
"@types/react": "~18.3.1",
|
|
77
|
+
"clsx": "^2.0.0",
|
|
78
|
+
"jsdom": "^26.0.0",
|
|
79
|
+
"lightningcss": "^1.32.0",
|
|
80
|
+
"react": "^18.2.0",
|
|
81
|
+
"react-dom": "^18.2.0",
|
|
82
|
+
"tsdown": "^0.22.2",
|
|
83
|
+
"typescript": "^6.0.3",
|
|
84
|
+
"vitest": "^4.1.8"
|
|
85
|
+
},
|
|
86
|
+
"files": [
|
|
87
|
+
"lib/index.js",
|
|
88
|
+
"lib/invariant.js",
|
|
89
|
+
"lib/client.js",
|
|
90
|
+
"lib/types/**/*.d.ts",
|
|
91
|
+
"catalog/edex-themes.json"
|
|
92
|
+
],
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"js-yaml": "^5.4.1"
|
|
95
|
+
},
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "tsc -b tsconfig.json && tsdown",
|
|
98
|
+
"bundle": "tsdown",
|
|
99
|
+
"watch": "tsdown --watch",
|
|
100
|
+
"test": "vitest run",
|
|
101
|
+
"test:watch": "vitest",
|
|
102
|
+
"typecheck": "tsc -b tsconfig.json --pretty false"
|
|
103
|
+
}
|
|
104
|
+
}
|