lumiverse-spindle-types 0.6.18 → 0.6.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumiverse-spindle-types",
3
- "version": "0.6.18",
3
+ "version": "0.6.19",
4
4
  "types": "./src/index.ts",
5
5
  "keywords": [
6
6
  "lumiverse",
package/src/dom.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { PromptBlockDTO, PromptVariableValuesDTO, RequestInitDTO } from "./api";
2
2
  import type { SpindleComponentsHelper } from "./components";
3
3
  import type { SpindleHostDescriptorV1, SpindleHostLocaleAPI } from "./host";
4
+ import type { SpindleThemeAuthoringAPI } from "./theme";
4
5
 
5
6
  /** A chat-message DOM element paired with its stable message id. */
6
7
  export interface SpindleMessageElement {
@@ -951,6 +952,8 @@ export interface SpindleFrontendContext {
951
952
  readonly host: SpindleHostDescriptorV1;
952
953
  /** Synchronous host locale access with removable live-change subscriptions. */
953
954
  readonly locale: SpindleHostLocaleAPI;
955
+ /** Native persistent theme authoring. Feature-detect each versioned host capability before use. */
956
+ readonly theme: SpindleThemeAuthoringAPI;
954
957
  dom: SpindleDOMHelper;
955
958
  events: {
956
959
  on(event: string, handler: (payload: unknown) => void): () => void;
package/src/index.ts CHANGED
@@ -18,6 +18,23 @@ export {
18
18
  SPINDLE_COMPATIBILITY_ERROR_CODE,
19
19
  } from "./host";
20
20
 
21
+ export type {
22
+ SpindleThemeAsset,
23
+ SpindleThemeAssetUploadOptions,
24
+ SpindleThemeAssetUpdate,
25
+ SpindleThemePackComponentDraft,
26
+ SpindleThemePackDraft,
27
+ SpindleThemePackImportResult,
28
+ SpindleThemePackInstallOptions,
29
+ SpindleThemePackInstallResult,
30
+ SpindleThemeComponentCatalogEntry,
31
+ SpindleThemeVariableCatalogEntry,
32
+ SpindleThemeEditorTarget,
33
+ SpindleThemeEditorOptions,
34
+ SpindleThemeAuthoringAPI,
35
+ } from "./theme";
36
+ export { SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES } from "./theme";
37
+
21
38
  export type { SpindlePermission as SpindlePermissionType } from "./permissions";
22
39
  export { ALL_PERMISSIONS, isValidPermission } from "./permissions";
23
40
 
package/src/theme.ts ADDED
@@ -0,0 +1,135 @@
1
+ import type { SpindleUploadFile } from "./dom";
2
+
3
+ /** Frontend-only host capabilities for the native theme-authoring context. */
4
+ export const SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES = Object.freeze({
5
+ "theme-assets-v1": 1,
6
+ "theme-packs-v1": 1,
7
+ "theme-catalog-v1": 1,
8
+ "theme-editor-navigation-v1": 1,
9
+ } as const);
10
+
11
+ /** A native theme asset with separate browser and pack-safe references. */
12
+ export interface SpindleThemeAsset {
13
+ id: string;
14
+ bundleId: string;
15
+ slug: string;
16
+ originalFilename: string;
17
+ mimeType: string;
18
+ sizeBytes: number;
19
+ tags?: string[];
20
+ metadata?: Record<string, unknown>;
21
+ /** Stable path to use from CSS stored in a native theme pack. */
22
+ cssPath: string;
23
+ /** Live URL suitable for browser previews and downloads. */
24
+ contentUrl: string;
25
+ }
26
+
27
+ export interface SpindleThemeAssetUploadOptions {
28
+ bundleId: string;
29
+ slug?: string;
30
+ tags?: string[];
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+
34
+ export interface SpindleThemeAssetUpdate {
35
+ slug?: string;
36
+ tags?: string[];
37
+ metadata?: Record<string, unknown>;
38
+ }
39
+
40
+ /** CSS-only component input for the canonical native theme-pack adapter. */
41
+ export interface SpindleThemePackComponentDraft {
42
+ css: string;
43
+ enabled?: boolean;
44
+ }
45
+
46
+ /** Authoring input independent of Lumiverse's internal archive representation. */
47
+ export interface SpindleThemePackDraft {
48
+ name: string;
49
+ author?: string;
50
+ description?: string;
51
+ globalCSS: string;
52
+ components?: Record<string, SpindleThemePackComponentDraft>;
53
+ assetBundleId?: string | null;
54
+ }
55
+
56
+ export interface SpindleThemePackImportResult {
57
+ draft: SpindleThemePackDraft;
58
+ assets: SpindleThemeAsset[];
59
+ warnings: string[];
60
+ }
61
+
62
+ export interface SpindleThemePackInstallOptions {
63
+ apply?: boolean;
64
+ saveToLibrary?: boolean;
65
+ }
66
+
67
+ export interface SpindleThemePackInstallResult {
68
+ bundleId: string;
69
+ applied: boolean;
70
+ savedToLibrary: boolean;
71
+ savedThemeId?: string;
72
+ assetCount: number;
73
+ componentCount: number;
74
+ }
75
+
76
+ /** Sanitized native component metadata; source and filesystem paths are omitted. */
77
+ export interface SpindleThemeComponentCatalogEntry {
78
+ id: string;
79
+ label: string;
80
+ category: string;
81
+ selector: string;
82
+ hasCss: boolean;
83
+ hasTsx: boolean;
84
+ }
85
+
86
+ export interface SpindleThemeVariableCatalogEntry {
87
+ name: string;
88
+ defaultValue?: string;
89
+ value?: string;
90
+ category?: string;
91
+ }
92
+
93
+ export type SpindleThemeEditorTarget = "global" | "assets" | "component";
94
+
95
+ export interface SpindleThemeEditorOptions {
96
+ target?: SpindleThemeEditorTarget;
97
+ componentId?: string;
98
+ }
99
+
100
+ /**
101
+ * Persistent native theme-authoring integration available to frontend extensions.
102
+ *
103
+ * Extensions must feature-detect the corresponding `ctx.host.capabilities` key
104
+ * before calling a capability group. This surface is distinct from the worker-side
105
+ * `spindle.theme` live-presentation API.
106
+ */
107
+ export interface SpindleThemeAuthoringAPI {
108
+ /** Requires host capability `theme-assets-v1`. Mutations require `app_manipulation`. */
109
+ readonly assets: {
110
+ getActiveBundleId(): string | null;
111
+ createBundle(): string;
112
+ list(bundleId: string): Promise<SpindleThemeAsset[]>;
113
+ upload(file: SpindleUploadFile, options: SpindleThemeAssetUploadOptions): Promise<SpindleThemeAsset>;
114
+ update(assetId: string, patch: SpindleThemeAssetUpdate): Promise<SpindleThemeAsset>;
115
+ delete(assetId: string): Promise<void>;
116
+ optimizeWebp(assetId: string): Promise<SpindleThemeAsset>;
117
+ getBytes(assetId: string): Promise<Uint8Array>;
118
+ };
119
+ /** Requires host capability `theme-packs-v1`. Import/install mutations require `app_manipulation`. */
120
+ readonly packs: {
121
+ exportDraft(draft: SpindleThemePackDraft): Promise<Uint8Array>;
122
+ importArchive(bytes: Uint8Array): Promise<SpindleThemePackImportResult>;
123
+ installDraft(
124
+ draft: SpindleThemePackDraft,
125
+ options?: SpindleThemePackInstallOptions,
126
+ ): Promise<SpindleThemePackInstallResult>;
127
+ };
128
+ /** Requires host capability `theme-catalog-v1`. */
129
+ readonly catalog: {
130
+ listComponents(): readonly SpindleThemeComponentCatalogEntry[];
131
+ listVariables(): readonly SpindleThemeVariableCatalogEntry[];
132
+ };
133
+ /** Requires host capability `theme-editor-navigation-v1`. */
134
+ openEditor(options?: SpindleThemeEditorOptions): boolean;
135
+ }
@@ -4,6 +4,7 @@ import {
4
4
  SPINDLE_HOST_CAPABILITIES,
5
5
  } from "../src/host";
6
6
  import { ALL_PERMISSIONS, isValidPermission } from "../src/permissions";
7
+ import { SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES } from "../src/theme";
7
8
 
8
9
  test("final-response permission is valid and publicly declared", () => {
9
10
  expect(ALL_PERMISSIONS).toContain("final_response");
@@ -30,3 +31,14 @@ test("host compatibility constants are canonical and immutable", () => {
30
31
  });
31
32
  expect(Object.isFrozen(SPINDLE_HOST_CAPABILITIES)).toBe(true);
32
33
  });
34
+
35
+ test("theme authoring capabilities remain frontend-specific and immutable", () => {
36
+ expect(SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES).toEqual({
37
+ "theme-assets-v1": 1,
38
+ "theme-packs-v1": 1,
39
+ "theme-catalog-v1": 1,
40
+ "theme-editor-navigation-v1": 1,
41
+ });
42
+ expect(Object.isFrozen(SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES)).toBe(true);
43
+ expect(SPINDLE_HOST_CAPABILITIES).not.toHaveProperty("theme-assets-v1");
44
+ });
@@ -0,0 +1,55 @@
1
+ import type {
2
+ SpindleFrontendContext,
3
+ SpindleThemeComponentCatalogEntry,
4
+ SpindleThemePackDraft,
5
+ SpindleUploadFile,
6
+ } from "lumiverse-spindle-types";
7
+ import { SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES } from "lumiverse-spindle-types";
8
+
9
+ declare const ctx: SpindleFrontendContext;
10
+ declare const upload: SpindleUploadFile;
11
+ declare const archive: Uint8Array;
12
+
13
+ const themeAssetsVersion: 1 = SPINDLE_THEME_AUTHORING_HOST_CAPABILITIES["theme-assets-v1"];
14
+ void themeAssetsVersion;
15
+
16
+ const draft: SpindleThemePackDraft = {
17
+ name: "Visual Theme",
18
+ globalCSS: ":root { --lumiverse-primary: #c084fc; }",
19
+ components: {
20
+ BubbleMessage: { css: "color: var(--lumiverse-text);", enabled: true },
21
+ },
22
+ };
23
+
24
+ ctx.theme.assets.upload(upload, { bundleId: "bundle-id" }).then((asset) => {
25
+ const preview: string = asset.contentUrl;
26
+ const stylesheetReference: string = asset.cssPath;
27
+ void preview;
28
+ void stylesheetReference;
29
+ });
30
+
31
+ const exported: Promise<Uint8Array> = ctx.theme.packs.exportDraft(draft);
32
+ ctx.theme.packs.importArchive(archive).then((result) => {
33
+ const imported: SpindleThemePackDraft = result.draft;
34
+ void imported;
35
+ });
36
+
37
+ const component: SpindleThemeComponentCatalogEntry = ctx.theme.catalog.listComponents()[0]!;
38
+ const selector: string = component.selector;
39
+ // @ts-expect-error catalog entries intentionally expose no source or filesystem path
40
+ component.cssPath;
41
+ void selector;
42
+ void exported;
43
+
44
+ const draftWithTsx: SpindleThemePackDraft = {
45
+ name: "Unsafe input",
46
+ globalCSS: "",
47
+ components: {
48
+ BubbleMessage: {
49
+ css: "",
50
+ // @ts-expect-error theme pack drafts deliberately cannot provide component TSX
51
+ tsx: "export default function Replacement() {}",
52
+ },
53
+ },
54
+ };
55
+ void draftWithTsx;
@@ -5,5 +5,5 @@
5
5
  "outDir": "./.consumer-dist",
6
6
  "declaration": false
7
7
  },
8
- "include": ["test/loom-block-editor-consumer.ts", "test/bound-generation-consumer.ts", "test/interceptor-consumer.ts", "test/host-runtime-contract-consumer.ts", "test/image-gen-stream-consumer.ts"]
8
+ "include": ["test/loom-block-editor-consumer.ts", "test/bound-generation-consumer.ts", "test/interceptor-consumer.ts", "test/host-runtime-contract-consumer.ts", "test/image-gen-stream-consumer.ts", "test/theme-authoring-consumer.ts"]
9
9
  }