@svgrid/enterprise 2.0.0 → 2.0.2

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,91 +1,93 @@
1
- {
2
- "name": "@svgrid/enterprise",
3
- "version": "2.0.0",
4
- "description": "Pro feature pack for sv-grid: data export (Excel, PDF, CSV, TSV, HTML), import (xlsx, CSV, TSV, JSON), pivot table + designer, AI assistant (BYO model), and printing. Requires a paid license key.",
5
- "license": "SEE LICENSE IN LICENSE",
6
- "author": "jQWidgets <sales@jqwidgets.com>",
7
- "homepage": "https://svgrid.com/pricing",
8
- "type": "module",
9
- "exports": {
10
- ".": {
11
- "types": "./src/index.ts",
12
- "svelte": "./src/index.ts",
13
- "import": "./src/index.ts"
14
- },
15
- "./export": {
16
- "types": "./src/export.ts",
17
- "svelte": "./src/export.ts",
18
- "import": "./src/export.ts"
19
- },
20
- "./print": {
21
- "types": "./src/print.ts",
22
- "svelte": "./src/print.ts",
23
- "import": "./src/print.ts"
24
- },
25
- "./ai": {
26
- "types": "./src/ai.ts",
27
- "svelte": "./src/ai.ts",
28
- "import": "./src/ai.ts"
29
- },
30
- "./import": {
31
- "types": "./src/import.ts",
32
- "svelte": "./src/import.ts",
33
- "import": "./src/import.ts"
34
- },
35
- "./pivot": {
36
- "types": "./src/pivot.ts",
37
- "svelte": "./src/pivot.ts",
38
- "import": "./src/pivot.ts"
39
- },
40
- "./studio": {
41
- "types": "./src/studio/index.ts",
42
- "node": "./dist/node/studio.js",
43
- "svelte": "./src/studio/index.ts",
44
- "import": "./src/studio/index.ts"
45
- },
46
- "./cdn/svelte-external": {
47
- "import": "./dist/cdn/svgrid-enterprise.svelte-external.js",
48
- "default": "./dist/cdn/svgrid-enterprise.svelte-external.js"
49
- },
50
- "./package.json": "./package.json"
51
- },
52
- "unpkg": "./dist/cdn/svgrid-enterprise.svelte-external.js",
53
- "jsdelivr": "./dist/cdn/svgrid-enterprise.svelte-external.js",
54
- "files": [
55
- "src",
56
- "dist",
57
- "README.md",
58
- "LICENSE"
59
- ],
60
- "scripts": {
61
- "test": "vitest run",
62
- "test:types": "tsc -p tsconfig.json --noEmit",
63
- "build": "node ./scripts/build-cdn.mjs && node ./scripts/build-node.mjs",
64
- "build:cdn": "node ./scripts/build-cdn.mjs",
65
- "build:node": "node ./scripts/build-node.mjs",
66
- "prepublishOnly": "node ./scripts/build-cdn.mjs && node ./scripts/build-node.mjs"
67
- },
68
- "peerDependencies": {
69
- "svelte": "^5.0.0",
70
- "@svgrid/grid": "workspace:*",
71
- "jszip": "^3.10.0",
72
- "pdfmake": "^0.2.0"
73
- },
74
- "peerDependenciesMeta": {
75
- "jszip": {
76
- "optional": true
77
- },
78
- "pdfmake": {
79
- "optional": true
80
- }
81
- },
82
- "devDependencies": {
83
- "@sveltejs/vite-plugin-svelte": "^7.0.0",
84
- "jszip": "^3.10.1",
85
- "pdfmake": "^0.2.10",
86
- "svelte": "^5.55.5",
87
- "@svgrid/grid": "workspace:*",
88
- "typescript": "6.0.3",
89
- "vite": "^8.0.10"
90
- }
1
+ {
2
+ "name": "@svgrid/enterprise",
3
+ "version": "2.0.2",
4
+ "description": "Pro feature pack for sv-grid: data export (Excel, PDF, CSV, TSV, HTML), import (xlsx, CSV, TSV, JSON), pivot table + designer, AI assistant (BYO model), and printing. Requires a paid license key.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "jQWidgets <sales@jqwidgets.com>",
7
+ "homepage": "https://svgrid.com/pricing",
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./src/index.ts",
12
+ "svelte": "./src/index.ts",
13
+ "import": "./src/index.ts"
14
+ },
15
+ "./export": {
16
+ "types": "./src/export.ts",
17
+ "svelte": "./src/export.ts",
18
+ "import": "./src/export.ts"
19
+ },
20
+ "./print": {
21
+ "types": "./src/print.ts",
22
+ "svelte": "./src/print.ts",
23
+ "import": "./src/print.ts"
24
+ },
25
+ "./ai": {
26
+ "types": "./src/ai.ts",
27
+ "svelte": "./src/ai.ts",
28
+ "import": "./src/ai.ts"
29
+ },
30
+ "./import": {
31
+ "types": "./src/import.ts",
32
+ "svelte": "./src/import.ts",
33
+ "import": "./src/import.ts"
34
+ },
35
+ "./pivot": {
36
+ "types": "./src/pivot.ts",
37
+ "svelte": "./src/pivot.ts",
38
+ "import": "./src/pivot.ts"
39
+ },
40
+ "./studio": {
41
+ "types": "./src/studio/index.ts",
42
+ "node": "./dist/node/studio.js",
43
+ "svelte": "./src/studio/index.ts",
44
+ "import": "./src/studio/index.ts"
45
+ },
46
+ "./cdn/svelte-external": {
47
+ "import": "./dist/cdn/svgrid-enterprise.svelte-external.js",
48
+ "default": "./dist/cdn/svgrid-enterprise.svelte-external.js"
49
+ },
50
+ "./package.json": "./package.json"
51
+ },
52
+ "unpkg": "./dist/cdn/svgrid-enterprise.svelte-external.js",
53
+ "jsdelivr": "./dist/cdn/svgrid-enterprise.svelte-external.js",
54
+ "files": [
55
+ "src",
56
+ "dist",
57
+ "README.md",
58
+ "LICENSE"
59
+ ],
60
+ "publishConfig": {
61
+ "access": "public"
62
+ },
63
+ "peerDependencies": {
64
+ "svelte": "^5.0.0",
65
+ "jszip": "^3.10.0",
66
+ "pdfmake": "^0.2.0",
67
+ "@svgrid/grid": "^2.1.0"
68
+ },
69
+ "peerDependenciesMeta": {
70
+ "jszip": {
71
+ "optional": true
72
+ },
73
+ "pdfmake": {
74
+ "optional": true
75
+ }
76
+ },
77
+ "devDependencies": {
78
+ "@sveltejs/vite-plugin-svelte": "^7.0.0",
79
+ "jszip": "^3.10.1",
80
+ "pdfmake": "^0.2.10",
81
+ "svelte": "^5.55.5",
82
+ "typescript": "6.0.3",
83
+ "vite": "^8.0.10",
84
+ "@svgrid/grid": "^2.1.0"
85
+ },
86
+ "scripts": {
87
+ "test": "vitest run",
88
+ "test:types": "tsc -p tsconfig.json --noEmit",
89
+ "build": "node ./scripts/build-cdn.mjs && node ./scripts/build-node.mjs",
90
+ "build:cdn": "node ./scripts/build-cdn.mjs",
91
+ "build:node": "node ./scripts/build-node.mjs"
92
+ }
91
93
  }
@@ -549,6 +549,7 @@
549
549
  background: var(--ep-bg);
550
550
  color: var(--ep-fg);
551
551
  font: inherit;
552
+ font-family: var(--sg-font, inherit);
552
553
  }
553
554
  .sv-ep--drawer {
554
555
  width: min(440px, 100vw);
@@ -1,172 +1,55 @@
1
- /**
2
- * The Studio look-and-feel themes: faithful reproductions of SvGrid's built-in
3
- * design-system presets (shadcn/ui, Tailwind, Material 3, Excel, Fluent 2, ...),
4
- * the same tokens that ship in `examples/src/themes/*.css` and the website Theme
5
- * Builder. Each theme carries an explicit light + dark palette; picking one (and
6
- * a light/dark mode) restyles the WHOLE generated app - shell, grids, charts,
7
- * KPIs, forms - not just the accent.
8
- *
9
- * Pure + node-safe (studio subtree): plain data + helpers, so the same tokens
10
- * drive the browser preview and the emitted `+layout.svelte` / `app.css`.
11
- */
12
- import type { ProjectTheme } from './project.js'
13
-
14
- export type ThemeMode = 'light' | 'dark'
15
-
16
- /** The core palette for one mode. Mirrors the Theme Builder's preset shape. */
17
- export type ThemePalette = {
18
- bg: string; fg: string; muted: string; border: string
19
- headerBg: string; headerFg: string; accent: string
20
- rowAlt: string; rowHover: string; selectionBg: string
21
- }
22
-
23
- export type StudioTheme = {
24
- /** Stable id, stored on `ProjectTheme.preset`. */
25
- id: string
26
- /** Display name shown on the swatch. */
27
- name: string
28
- /** Control corner rounding (px). */
29
- radius: number
30
- /** Font stack applied to the app surface. */
31
- font: string
32
- light: ThemePalette
33
- dark: ThemePalette
34
- }
35
-
36
- /** The built-in Studio themes, in gallery order (default first). */
37
- export const studioThemes: StudioTheme[] = [
38
- { id: 'shadcn', name: 'shadcn/ui', radius: 6, font: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
39
- light: { bg: '#ffffff', fg: '#09090b', muted: '#71717a', border: '#e4e4e7', headerBg: '#fafafa', headerFg: '#71717a', accent: '#18181b', rowAlt: '#ffffff', rowHover: '#f4f4f5', selectionBg: '#f4f4f5' },
40
- dark: { bg: '#0a0a0a', fg: '#fafafa', muted: '#a1a1aa', border: '#27272a', headerBg: '#18181b', headerFg: '#a1a1aa', accent: '#fafafa', rowAlt: '#0a0a0a', rowHover: '#18181b', selectionBg: '#27272a' } },
41
- { id: 'tailwind', name: 'Tailwind', radius: 6, font: 'Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',
42
- light: { bg: '#ffffff', fg: '#0f172a', muted: '#64748b', border: '#e2e8f0', headerBg: '#f8fafc', headerFg: '#0f172a', accent: '#4f46e5', rowAlt: '#ffffff', rowHover: '#f1f5f9', selectionBg: '#e0e7ff' },
43
- dark: { bg: '#0f172a', fg: '#f8fafc', muted: '#94a3b8', border: '#334155', headerBg: '#1e293b', headerFg: '#f8fafc', accent: '#818cf8', rowAlt: '#0f172a', rowHover: '#1e293b', selectionBg: '#312e81' } },
44
- { id: 'material', name: 'Material 3', radius: 8, font: '"Roboto Flex", Roboto, "Segoe UI", system-ui, sans-serif',
45
- light: { bg: '#fef7ff', fg: '#1d1b20', muted: '#49454f', border: '#cac4d0', headerBg: '#f3edf7', headerFg: '#1d1b20', accent: '#6750a4', rowAlt: '#fffbfe', rowHover: '#ece6f0', selectionBg: '#e8def8' },
46
- dark: { bg: '#141218', fg: '#e6e0e9', muted: '#cac4d0', border: '#49454f', headerBg: '#211f26', headerFg: '#e6e0e9', accent: '#d0bcff', rowAlt: '#141218', rowHover: '#2b2930', selectionBg: '#4a4458' } },
47
- { id: 'excel', name: 'Excel', radius: 0, font: '"Aptos", "Segoe UI", Calibri, Arial, sans-serif',
48
- light: { bg: '#ffffff', fg: '#323130', muted: '#605e5c', border: '#d2d0ce', headerBg: '#f3f2f1', headerFg: '#444444', accent: '#107c41', rowAlt: '#ffffff', rowHover: '#edebe9', selectionBg: 'rgba(16, 124, 65, 0.10)' },
49
- dark: { bg: '#1b1a19', fg: '#f3f2f1', muted: '#c8c6c4', border: '#3b3a39', headerBg: '#252423', headerFg: '#c8c6c4', accent: '#58c188', rowAlt: '#1b1a19', rowHover: '#323130', selectionBg: 'rgba(88, 193, 136, 0.16)' } },
50
- { id: 'fluent', name: 'Fluent 2', radius: 4, font: '"Segoe UI Variable", "Segoe UI", system-ui, sans-serif',
51
- light: { bg: '#ffffff', fg: '#242424', muted: '#616161', border: '#e0e0e0', headerBg: '#f5f5f5', headerFg: '#242424', accent: '#0f6cbd', rowAlt: '#fafafa', rowHover: '#f0f0f0', selectionBg: '#ebebeb' },
52
- dark: { bg: '#1f1f1f', fg: '#ffffff', muted: '#d6d6d6', border: '#666666', headerBg: '#141414', headerFg: '#ffffff', accent: '#479ef5', rowAlt: '#1a1a1a', rowHover: '#3d3d3d', selectionBg: '#383838' } },
53
- { id: 'carbon', name: 'Carbon', radius: 0, font: '"IBM Plex Sans", "Helvetica Neue", Arial, sans-serif',
54
- light: { bg: '#ffffff', fg: '#161616', muted: '#525252', border: '#e0e0e0', headerBg: '#e0e0e0', headerFg: '#161616', accent: '#0f62fe', rowAlt: '#f4f4f4', rowHover: '#e8e8e8', selectionBg: '#d0e2ff' },
55
- dark: { bg: '#161616', fg: '#f4f4f4', muted: '#c6c6c6', border: '#393939', headerBg: '#393939', headerFg: '#f4f4f4', accent: '#78a9ff', rowAlt: '#262626', rowHover: '#333333', selectionBg: '#393939' } },
56
- { id: 'sap', name: 'SAP Fiori', radius: 6, font: '"72", "72full", "Arial", sans-serif',
57
- light: { bg: '#f5f6f7', fg: '#131e29', muted: '#556b82', border: '#e5e5e5', headerBg: '#ffffff', headerFg: '#131e29', accent: '#0070f2', rowAlt: '#f5f6f7', rowHover: '#eaecee', selectionBg: '#ebf8ff' },
58
- dark: { bg: '#12171c', fg: '#f5f6f7', muted: '#8396a8', border: '#2e3742', headerBg: '#1d232a', headerFg: '#f5f6f7', accent: '#4db1ff', rowAlt: '#12171c', rowHover: '#222b35', selectionBg: '#1d2d3e' } },
59
- { id: 'salesforce', name: 'Salesforce', radius: 4, font: '"Salesforce Sans", "SF Pro", Helvetica, Arial, sans-serif',
60
- light: { bg: '#ffffff', fg: '#181818', muted: '#757575', border: '#c9c9c9', headerBg: '#f3f3f3', headerFg: '#181818', accent: '#0176d3', rowAlt: '#ffffff', rowHover: '#f3f3f3', selectionBg: '#ecebea' },
61
- dark: { bg: '#181818', fg: '#ffffff', muted: '#c9c9c9', border: '#3e3e3c', headerBg: '#2e2e2e', headerFg: '#ffffff', accent: '#1b96ff', rowAlt: '#181818', rowHover: '#2e2e2e', selectionBg: '#1e4d8e' } },
62
- { id: 'atlassian', name: 'Atlassian', radius: 3, font: '"Charlie Display", "Atlassian Sans", -apple-system, "Segoe UI", sans-serif',
63
- light: { bg: '#ffffff', fg: '#172b4d', muted: '#44546f', border: '#dcdfe4', headerBg: '#fafbfc', headerFg: '#44546f', accent: '#0c66e4', rowAlt: '#ffffff', rowHover: '#f1f2f4', selectionBg: '#e9f2ff' },
64
- dark: { bg: '#1d2125', fg: '#b6c2cf', muted: '#8590a2', border: '#38414a', headerBg: '#22272b', headerFg: '#8590a2', accent: '#579dff', rowAlt: '#1d2125', rowHover: '#2c333a', selectionBg: '#09326c' } },
65
- { id: 'github', name: 'GitHub', radius: 6, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif',
66
- light: { bg: '#ffffff', fg: '#1f2328', muted: '#59636e', border: '#d1d9e0', headerBg: '#f6f8fa', headerFg: '#59636e', accent: '#0969da', rowAlt: '#ffffff', rowHover: '#f6f8fa', selectionBg: '#ddf4ff' },
67
- dark: { bg: '#0d1117', fg: '#f0f6fc', muted: '#9198a1', border: '#3d444d', headerBg: '#151b23', headerFg: '#9198a1', accent: '#4493f8', rowAlt: '#0d1117', rowHover: '#151b23', selectionBg: 'rgba(56, 139, 253, 0.10)' } },
68
- { id: 'antd', name: 'Ant Design', radius: 6, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", sans-serif',
69
- light: { bg: '#ffffff', fg: 'rgba(0, 0, 0, 0.88)', muted: 'rgba(0, 0, 0, 0.65)', border: '#d9d9d9', headerBg: '#fafafa', headerFg: 'rgba(0, 0, 0, 0.88)', accent: '#1677ff', rowAlt: '#ffffff', rowHover: '#f5f5f5', selectionBg: '#e6f4ff' },
70
- dark: { bg: '#141414', fg: 'rgba(255, 255, 255, 0.85)', muted: 'rgba(255, 255, 255, 0.65)', border: '#424242', headerBg: '#1f1f1f', headerFg: 'rgba(255, 255, 255, 0.85)', accent: '#177ddc', rowAlt: '#141414', rowHover: '#262626', selectionBg: '#111a2c' } },
71
- { id: 'ag-alpine', name: 'Alpine', radius: 3, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
72
- light: { bg: '#ffffff', fg: '#181d1f', muted: 'rgba(24, 29, 31, 0.5)', border: '#babfc7', headerBg: '#f8f8f8', headerFg: '#181d1f', accent: '#2196f3', rowAlt: '#fcfcfc', rowHover: 'rgba(33, 150, 243, 0.1)', selectionBg: 'rgba(33, 150, 243, 0.3)' },
73
- dark: { bg: '#181d1f', fg: '#ffffff', muted: 'rgba(255, 255, 255, 0.5)', border: '#68686e', headerBg: '#222628', headerFg: '#ffffff', accent: '#2196f3', rowAlt: '#222628', rowHover: 'rgba(33, 150, 243, 0.1)', selectionBg: 'rgba(33, 150, 243, 0.3)' } },
74
- { id: 'bootstrap', name: 'Bootstrap 5', radius: 6, font: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
75
- light: { bg: '#ffffff', fg: '#212529', muted: '#6c757d', border: '#dee2e6', headerBg: '#f8f9fa', headerFg: '#212529', accent: '#0d6efd', rowAlt: '#fcfcfd', rowHover: '#f2f2f2', selectionBg: '#cfe2ff' },
76
- dark: { bg: '#212529', fg: '#dee2e6', muted: '#adb5bd', border: '#495057', headerBg: '#2b3035', headerFg: '#dee2e6', accent: '#0d6efd', rowAlt: '#25292e', rowHover: '#2c3034', selectionBg: '#031633' } },
77
- { id: 'vercel', name: 'Vercel', radius: 6, font: '"Geist", "Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
78
- light: { bg: '#ffffff', fg: '#000000', muted: '#666666', border: '#eaeaea', headerBg: '#fafafa', headerFg: '#666666', accent: '#0070f3', rowAlt: '#fafafa', rowHover: '#f5f5f5', selectionBg: '#e8f0fe' },
79
- dark: { bg: '#000000', fg: '#ffffff', muted: '#888888', border: '#333333', headerBg: '#111111', headerFg: '#888888', accent: '#3291ff', rowAlt: '#0a0a0a', rowHover: '#1a1a1a', selectionBg: '#0d2847' } },
80
- { id: 'linear', name: 'Linear', radius: 6, font: '"Inter Variable", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
81
- light: { bg: '#ffffff', fg: '#282a30', muted: '#6b6f76', border: '#e9e9eb', headerBg: '#f7f8f8', headerFg: '#6b6f76', accent: '#5e6ad2', rowAlt: '#fbfbfc', rowHover: '#f4f4f5', selectionBg: '#e8eafd' },
82
- dark: { bg: '#08090a', fg: '#f7f8f8', muted: '#8a8f98', border: '#23252a', headerBg: '#0f1011', headerFg: '#8a8f98', accent: '#5e6ad2', rowAlt: '#0f1011', rowHover: '#1a1b1e', selectionBg: '#2a2d4a' } },
83
- { id: 'notion', name: 'Notion', radius: 4, font: 'ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif',
84
- light: { bg: '#ffffff', fg: '#37352f', muted: '#787774', border: '#e9e9e7', headerBg: '#f7f6f3', headerFg: '#787774', accent: '#2383e2', rowAlt: '#fbfbfa', rowHover: '#f1f1ef', selectionBg: '#d9eaf7' },
85
- dark: { bg: '#191919', fg: '#d4d4d4', muted: '#9b9b9b', border: '#2f2f2f', headerBg: '#202020', headerFg: '#9b9b9b', accent: '#529cca', rowAlt: '#1c1c1c', rowHover: '#252525', selectionBg: '#20415c' } },
86
- { id: 'nord', name: 'Nord', radius: 4, font: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
87
- light: { bg: '#eceff4', fg: '#2e3440', muted: '#4c566a', border: '#d8dee9', headerBg: '#e5e9f0', headerFg: '#2e3440', accent: '#5e81ac', rowAlt: '#e5e9f0', rowHover: '#dde3ec', selectionBg: '#d8dee9' },
88
- dark: { bg: '#2e3440', fg: '#d8dee9', muted: '#4c566a', border: '#434c5e', headerBg: '#3b4252', headerFg: '#e5e9f0', accent: '#88c0d0', rowAlt: '#333b48', rowHover: '#3b4252', selectionBg: '#434c5e' } },
89
- { id: 'dracula', name: 'Dracula', radius: 6, font: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
90
- light: { bg: '#fffbeb', fg: '#1f1f1f', muted: '#6c664b', border: '#dedccf', headerBg: '#ece9df', headerFg: '#1f1f1f', accent: '#644ac9', rowAlt: '#fffdf5', rowHover: '#f2efe3', selectionBg: '#cfcfde' },
91
- dark: { bg: '#282a36', fg: '#f8f8f2', muted: '#6272a4', border: '#44475a', headerBg: '#21222c', headerFg: '#f8f8f2', accent: '#bd93f9', rowAlt: '#2d2f3b', rowHover: '#343746', selectionBg: '#44475a' } },
92
- { id: 'catppuccin', name: 'Catppuccin', radius: 8, font: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
93
- light: { bg: '#eff1f5', fg: '#4c4f69', muted: '#6c6f85', border: '#ccd0da', headerBg: '#e6e9ef', headerFg: '#4c4f69', accent: '#8839ef', rowAlt: '#e6e9ef', rowHover: '#dce0e8', selectionBg: '#dce0e8' },
94
- dark: { bg: '#1e1e2e', fg: '#cdd6f4', muted: '#a6adc8', border: '#313244', headerBg: '#181825', headerFg: '#cdd6f4', accent: '#cba6f7', rowAlt: '#1a1a28', rowHover: '#313244', selectionBg: '#45475a' } },
95
- ]
96
-
97
- /** The default theme (used when a project has no `preset`). */
98
- export const defaultStudioTheme: StudioTheme = studioThemes.find((t) => t.id === 'tailwind') ?? studioThemes[0]!
99
-
100
- /** Look up a theme by id. */
101
- export const getStudioTheme = (id?: string): StudioTheme | undefined =>
102
- id ? studioThemes.find((t) => t.id === id) : undefined
103
-
104
- /** A readable (near-black / white) foreground for text placed on the accent. */
105
- function onAccent(accent: string): string {
106
- const m = /^#?([0-9a-f]{6})$/i.exec(accent.trim())
107
- if (!m) return '#ffffff'
108
- const n = parseInt(m[1]!, 16)
109
- const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255
110
- // Perceived luminance (sRGB, quick approximation).
111
- const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255
112
- return lum > 0.6 ? '#18181b' : '#ffffff'
113
- }
114
-
115
- /** Resolve the effective `--sg-*` tokens for a project theme: the chosen preset
116
- * (default if none) in the chosen mode, with the accent override applied. */
117
- export function resolveThemeTokens(theme?: ProjectTheme): Record<string, string> {
118
- const preset = getStudioTheme(theme?.preset) ?? defaultStudioTheme
119
- const mode: ThemeMode = theme?.mode === 'dark' ? 'dark' : 'light'
120
- const p = preset[mode]
121
- const accent = theme?.accent || p.accent
122
- return {
123
- '--sg-bg': p.bg,
124
- '--sg-fg': p.fg,
125
- '--sg-muted': p.muted,
126
- '--sg-border': p.border,
127
- '--sg-header-bg': p.headerBg,
128
- '--sg-header-fg': p.headerFg,
129
- // A subtle inset surface (used by e.g. the master-detail region behind the
130
- // nested grid). Themed so it isn't a fixed light grey on dark presets.
131
- '--sg-bg-subtle': p.headerBg,
132
- '--sg-row-alt-bg': p.rowAlt,
133
- '--sg-row-hover-bg': p.rowHover,
134
- '--sg-selection-bg': p.selectionBg,
135
- // Inputs/checkboxes: pin to the theme's own surface so they never borrow the
136
- // host page's input tokens (which is how a themed grid ended up with dark
137
- // checkboxes on a light page when the host mode differed).
138
- '--sg-input-bg': p.bg,
139
- '--sg-input-border': p.border,
140
- '--sg-accent': accent,
141
- '--sg-on-accent': onAccent(accent),
142
- '--sg-radius': `${preset.radius}px`,
143
- '--sg-font': preset.font,
144
- // The grid's custom scrollbar is a shadow-DOM web component that reads its
145
- // OWN --sg-scrollbar-* tokens (with fixed light-mode fallbacks). Without
146
- // these it ignores the theme entirely - a light scrollbar left on a dark
147
- // grid. CSS custom properties pierce shadow DOM, so defining them here (on
148
- // the themed container) reaches the scrollbar. Derive them from the palette
149
- // so the scrollbar tracks the preset + light/dark mode like everything else.
150
- '--sg-scrollbar-bg': p.headerBg,
151
- '--sg-scrollbar-border': p.border,
152
- '--sg-scrollbar-thumb': p.muted,
153
- '--sg-scrollbar-thumb-hover': p.fg,
154
- '--sg-scrollbar-thumb-active': p.fg,
155
- '--sg-scrollbar-arrow': p.muted,
156
- '--sg-scrollbar-arrow-hover': p.fg,
157
- '--sg-scrollbar-arrow-hover-bg': p.rowHover,
158
- '--sg-scrollbar-arrow-active': p.fg,
159
- '--sg-scrollbar-arrow-active-bg': p.selectionBg,
160
- '--sg-scrollbar-arrow-disabled': p.border,
161
- }
162
- }
163
-
164
- /** True when the project's theme is rendering in dark mode. */
165
- export const isDarkTheme = (theme?: ProjectTheme): boolean => theme?.mode === 'dark'
166
-
167
- /** An inline `style` string (`--sg-x: y; ...; color-scheme; font-family`) for previews. */
168
- export function themeStyleString(theme?: ProjectTheme): string {
169
- const tokens = resolveThemeTokens(theme)
170
- const vars = Object.entries(tokens).map(([k, v]) => `${k}: ${v}`).join('; ')
171
- return `${vars}; color-scheme: ${isDarkTheme(theme) ? 'dark' : 'light'}; font-family: ${tokens['--sg-font']}`
172
- }
1
+ /**
2
+ * The Studio look-and-feel themes: a `ProjectTheme`-aware wrapper around
3
+ * `@svgrid/grid`'s built-in design-system presets (shadcn/ui, Tailwind,
4
+ * Material 3, Excel, Fluent 2, ...) - the single source of truth for these
5
+ * tokens, also used directly (as plain CSS) by app code that isn't going
6
+ * through Studio's project model.
7
+ *
8
+ * Picking a preset (and a light/dark mode) restyles the WHOLE generated app -
9
+ * shell, grids, charts, KPIs, forms - not just the accent.
10
+ *
11
+ * Pure + node-safe (studio subtree): plain data + helpers, so the same tokens
12
+ * drive the browser preview and the emitted `+layout.svelte` / `app.css`.
13
+ */
14
+ import {
15
+ themePresets,
16
+ defaultThemePreset,
17
+ getThemePreset,
18
+ resolveThemeTokens as resolveTokens,
19
+ type ThemeMode,
20
+ type ThemePalette,
21
+ type ThemePreset,
22
+ } from '@svgrid/grid/themes'
23
+ import type { ProjectTheme } from './project.js'
24
+
25
+ export type { ThemeMode, ThemePalette }
26
+
27
+ /** @deprecated Use `ThemePreset` from `@svgrid/grid/themes` - kept as an alias for the pre-existing public name. */
28
+ export type StudioTheme = ThemePreset
29
+
30
+ /** The built-in Studio themes, in gallery order (default first). */
31
+ export const studioThemes: StudioTheme[] = themePresets
32
+
33
+ /** The default theme (used when a project has no `preset`). */
34
+ export const defaultStudioTheme: StudioTheme = defaultThemePreset
35
+
36
+ /** Look up a theme by id. */
37
+ export const getStudioTheme = (id?: string): StudioTheme | undefined => getThemePreset(id)
38
+
39
+ /** True when the project's theme is rendering in dark mode. */
40
+ export const isDarkTheme = (theme?: ProjectTheme): boolean => theme?.mode === 'dark'
41
+
42
+ /** Resolve the effective `--sg-*` tokens for a project theme: the chosen preset
43
+ * (default if none) in the chosen mode, with the accent override applied. */
44
+ export function resolveThemeTokens(theme?: ProjectTheme): Record<string, string> {
45
+ const preset = getStudioTheme(theme?.preset) ?? defaultStudioTheme
46
+ const mode: ThemeMode = theme?.mode === 'dark' ? 'dark' : 'light'
47
+ return resolveTokens(preset, mode, theme?.accent)
48
+ }
49
+
50
+ /** An inline `style` string (`--sg-x: y; ...; color-scheme; font-family`) for previews. */
51
+ export function themeStyleString(theme?: ProjectTheme): string {
52
+ const tokens = resolveThemeTokens(theme)
53
+ const vars = Object.entries(tokens).map(([k, v]) => `${k}: ${v}`).join('; ')
54
+ return `${vars}; color-scheme: ${isDarkTheme(theme) ? 'dark' : 'light'}; font-family: ${tokens['--sg-font']}`
55
+ }