@emotionalamo/plugin-extratheme 0.0.5

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.
Files changed (48) hide show
  1. package/README.md +153 -0
  2. package/README.zh-CN.md +137 -0
  3. package/client-v2.d.ts +2 -0
  4. package/client-v2.js +1 -0
  5. package/client.d.ts +2 -0
  6. package/client.js +1 -0
  7. package/dist/client/index.d.ts +1 -0
  8. package/dist/client/index.js +10 -0
  9. package/dist/client/plugin.d.ts +5 -0
  10. package/dist/client/selectors.d.ts +1 -0
  11. package/dist/client/settings/LivePreview.d.ts +8 -0
  12. package/dist/client/settings/SettingsPage.d.ts +3 -0
  13. package/dist/client/settings/controls.d.ts +17 -0
  14. package/dist/client/useT.d.ts +2 -0
  15. package/dist/client-v2/index.d.ts +1 -0
  16. package/dist/client-v2/index.js +10 -0
  17. package/dist/client-v2/plugin.d.ts +5 -0
  18. package/dist/client-v2/selectors.d.ts +1 -0
  19. package/dist/externalVersion.js +19 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +48 -0
  22. package/dist/locale/en-US.json +80 -0
  23. package/dist/locale/zh-CN.json +80 -0
  24. package/dist/server/collections/extraThemeSettings.d.ts +2 -0
  25. package/dist/server/collections/extraThemeSettings.js +39 -0
  26. package/dist/server/index.d.ts +2 -0
  27. package/dist/server/index.js +48 -0
  28. package/dist/server/plugin.d.ts +6 -0
  29. package/dist/server/plugin.js +64 -0
  30. package/dist/server/resources/extraTheme.d.ts +24 -0
  31. package/dist/server/resources/extraTheme.js +65 -0
  32. package/dist/shared/buildTheme.d.ts +35 -0
  33. package/dist/shared/buildTheme.js +72 -0
  34. package/dist/shared/color.d.ts +44 -0
  35. package/dist/shared/color.js +122 -0
  36. package/dist/shared/defaults.d.ts +22 -0
  37. package/dist/shared/defaults.js +109 -0
  38. package/dist/shared/generateCss.d.ts +5 -0
  39. package/dist/shared/generateCss.js +116 -0
  40. package/dist/shared/injector.d.ts +15 -0
  41. package/dist/shared/injector.js +88 -0
  42. package/dist/shared/selectors.d.ts +17 -0
  43. package/dist/shared/selectors.js +48 -0
  44. package/dist/shared/types.d.ts +97 -0
  45. package/dist/shared/types.js +24 -0
  46. package/package.json +42 -0
  47. package/server.d.ts +2 -0
  48. package/server.js +1 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var color_exports = {};
28
+ __export(color_exports, {
29
+ buildBackground: () => buildBackground,
30
+ fontFormatFromUrl: () => fontFormatFromUrl,
31
+ hexToRgba: () => hexToRgba,
32
+ safeCssColor: () => safeCssColor,
33
+ safeNum: () => safeNum,
34
+ sanitizeCssUrl: () => sanitizeCssUrl,
35
+ sanitizeFontFamily: () => sanitizeFontFamily,
36
+ toSolidRgb: () => toSolidRgb,
37
+ withAlpha: () => withAlpha
38
+ });
39
+ module.exports = __toCommonJS(color_exports);
40
+ function sanitizeFontFamily(family) {
41
+ let s = (family || "").replace(/[;{}<>\\/*]/g, "").replace(/[\r\n\t]+/g, " ").trim();
42
+ if ((s.match(/"/g) || []).length % 2) s = s.replace(/"/g, "");
43
+ if ((s.match(/'/g) || []).length % 2) s = s.replace(/'/g, "");
44
+ return s.slice(0, 200);
45
+ }
46
+ function fontFormatFromUrl(url) {
47
+ var _a;
48
+ const ext = (((_a = (url || "").split("?")[0].split("#")[0].match(/\.([a-z0-9]+)$/i)) == null ? void 0 : _a[1]) || "").toLowerCase();
49
+ return { woff2: "woff2", woff: "woff", ttf: "truetype", otf: "opentype", ttc: "truetype" }[ext] || "";
50
+ }
51
+ function sanitizeCssUrl(url) {
52
+ return (url || "").replace(/["')(;{}<>\\]/g, "").replace(/\s+/g, "").slice(0, 2e3);
53
+ }
54
+ function hexToRgba(hex, alpha) {
55
+ let h = (hex || "").replace("#", "");
56
+ if (h.length === 3) h = h.split("").map((c) => c + c).join("");
57
+ const r = parseInt(h.slice(0, 2), 16);
58
+ const g = parseInt(h.slice(2, 4), 16);
59
+ const b = parseInt(h.slice(4, 6), 16);
60
+ return `rgba(${r},${g},${b},${alpha})`;
61
+ }
62
+ function parseRgb(color) {
63
+ const c = (color || "").trim();
64
+ const m = c.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i);
65
+ if (m) return [Math.round(+m[1]), Math.round(+m[2]), Math.round(+m[3])];
66
+ let h = c.replace("#", "");
67
+ if (h.length === 3) h = h.split("").map((x) => x + x).join("");
68
+ if (/^[0-9a-f]{6}$/i.test(h)) return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
69
+ return null;
70
+ }
71
+ function safeCssColor(color) {
72
+ const c = (color || "").trim();
73
+ if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(c)) return c;
74
+ if (/^(rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/]+\)$/i.test(c)) return c;
75
+ if (/^[a-z]+$/i.test(c)) return c;
76
+ return "transparent";
77
+ }
78
+ function safeNum(v, def = 0) {
79
+ const n = Number(v);
80
+ return Number.isFinite(n) ? n : def;
81
+ }
82
+ function toSolidRgb(color) {
83
+ const rgb = parseRgb(color);
84
+ return rgb ? `rgb(${rgb[0]},${rgb[1]},${rgb[2]})` : "rgb(0,0,0)";
85
+ }
86
+ function withAlpha(color, alpha) {
87
+ const rgb = parseRgb(color);
88
+ return rgb ? `rgba(${rgb[0]},${rgb[1]},${rgb[2]},${safeNum(alpha)})` : "transparent";
89
+ }
90
+ function buildBackground(bg) {
91
+ var _a;
92
+ const none = { image: "transparent", size: "auto", repeat: "no-repeat", position: "center" };
93
+ if (!bg || bg.type === "none") return none;
94
+ if (bg.type === "color") return { image: safeCssColor(bg.color), size: "auto", repeat: "no-repeat", position: "center" };
95
+ if (bg.type === "gradient") {
96
+ const stops = (bg.gradient.colors || []).map(safeCssColor).join(",");
97
+ return {
98
+ image: `linear-gradient(${safeNum(bg.gradient.angle)}deg,${stops})`,
99
+ size: "cover",
100
+ repeat: "no-repeat",
101
+ position: "center"
102
+ };
103
+ }
104
+ const url = sanitizeCssUrl(((_a = bg.image) == null ? void 0 : _a.url) || "");
105
+ if (!url) return none;
106
+ const fit = bg.image.fit;
107
+ const repeat = fit === "repeat" ? "repeat" : "no-repeat";
108
+ const size = fit === "repeat" ? "auto" : fit === "stretch" ? "100% 100%" : fit === "contain" ? "contain" : "cover";
109
+ return { image: `url("${url}")`, size, repeat, position: "center" };
110
+ }
111
+ // Annotate the CommonJS export names for ESM import in node:
112
+ 0 && (module.exports = {
113
+ buildBackground,
114
+ fontFormatFromUrl,
115
+ hexToRgba,
116
+ safeCssColor,
117
+ safeNum,
118
+ sanitizeCssUrl,
119
+ sanitizeFontFamily,
120
+ toSolidRgb,
121
+ withAlpha
122
+ });
@@ -0,0 +1,22 @@
1
+ import type { AppConfig, LoginConfig, ExtraThemeConfig } from './types';
2
+ /** Background gradient presets (name -> color stops). Options for the background picker, not skins. */
3
+ export declare const GRADIENT_PRESETS: Record<string, string[]>;
4
+ /**
5
+ * Global font presets (label -> CSS font-family stack). All are SYSTEM-font stacks —
6
+ * no web-font is loaded (CSP-safe, offline-safe, zero-latency). value '' = system
7
+ * default (no override). CJK-first so Chinese text stays correct on macOS/Windows/Linux.
8
+ */
9
+ export declare const FONT_PRESETS: {
10
+ label: string;
11
+ value: string;
12
+ }[];
13
+ /**
14
+ * Starting values when an admin first enables the 工作区外观 group. Everything is
15
+ * OFF by default (`enabled: false`) — these numbers only take visual effect once
16
+ * the admin flips the switch on.
17
+ */
18
+ export declare const DEFAULT_APP: AppConfig;
19
+ /** Starting values for the 登录页外观 group (independent of app). */
20
+ export declare const DEFAULT_LOGIN: LoginConfig;
21
+ /** Merge a partial (from DB / API) onto the defaults, producing a complete config. */
22
+ export declare function mergeConfig(partial: any): ExtraThemeConfig;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var defaults_exports = {};
28
+ __export(defaults_exports, {
29
+ DEFAULT_APP: () => DEFAULT_APP,
30
+ DEFAULT_LOGIN: () => DEFAULT_LOGIN,
31
+ FONT_PRESETS: () => FONT_PRESETS,
32
+ GRADIENT_PRESETS: () => GRADIENT_PRESETS,
33
+ mergeConfig: () => mergeConfig
34
+ });
35
+ module.exports = __toCommonJS(defaults_exports);
36
+ const GRADIENT_PRESETS = {
37
+ \u6674\u7A7A: ["#e0f2fe", "#ede9fe"],
38
+ \u6696\u9633: ["#fff7ed", "#ffedd5", "#fed7aa"],
39
+ \u6781\u5149: ["#c7d2fe", "#a7f3d0", "#fbcfe8"],
40
+ \u6D77\u6D0B: ["#cffafe", "#bae6fd", "#a5b4fc"],
41
+ \u8584\u8377: ["#d1fae5", "#a7f3d0"],
42
+ \u6A31\u7C89: ["#fce7f3", "#fbcfe8", "#ddd6fe"],
43
+ \u6DF1\u591C: ["#1e293b", "#0f172a"]
44
+ };
45
+ const FONT_PRESETS = [
46
+ { label: "\u7CFB\u7EDF\u9ED8\u8BA4", value: "" },
47
+ { label: "\u65E0\u886C\u7EBF\uFF08\u82F9\u65B9/\u96C5\u9ED1\uFF09", value: '"PingFang SC","Microsoft YaHei","Hiragino Sans GB","Segoe UI",Roboto,system-ui,sans-serif' },
48
+ { label: "\u601D\u6E90\u9ED1\u4F53", value: '"Source Han Sans SC","Noto Sans CJK SC","PingFang SC","Microsoft YaHei",sans-serif' },
49
+ { label: "\u886C\u7EBF\uFF08\u5B8B\u4F53\uFF09", value: '"Songti SC","Noto Serif CJK SC","SimSun",Georgia,"Times New Roman",serif' },
50
+ { label: "\u6977\u4F53", value: '"Kaiti SC","STKaiti","KaiTi","Noto Serif CJK SC",serif' },
51
+ { label: "\u5706\u4F53", value: '"Yuanti SC","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif' },
52
+ { label: "\u7B49\u5BBD\uFF08\u4EE3\u7801\uFF09", value: '"SF Mono","JetBrains Mono","Fira Code",Consolas,"Courier New",monospace' }
53
+ ];
54
+ const DEFAULT_APP = {
55
+ enabled: false,
56
+ background: {
57
+ type: "gradient",
58
+ color: "#eef2f7",
59
+ gradient: { preset: "\u6674\u7A7A", angle: 135, colors: GRADIENT_PRESETS["\u6674\u7A7A"] },
60
+ image: { url: "", fit: "cover", position: "center" },
61
+ dim: 0
62
+ },
63
+ card: { glass: true, opacity: 72, blur: 12, border: true },
64
+ // header / sider / font are independent of the 工作区外观 (background+card) switch;
65
+ // all default OFF so a fresh install changes nothing. font.family defaults to the
66
+ // 无衬线 stack so flipping the switch on shows an immediate effect.
67
+ header: { enabled: false, style: "frosted", color: "#ffffff", opacity: 90, blur: 14, text: "dark" },
68
+ sider: { enabled: false, style: "frosted", color: "#ffffff", opacity: 86, blur: 16, text: "dark" },
69
+ font: { enabled: false, source: "system", family: FONT_PRESETS[1].value, upload: { url: "", name: "", format: "" } },
70
+ // scrollbar off by default (native); when enabled, 'always' shows / 'hidden' hides.
71
+ scrollbar: { enabled: false, mode: "always" }
72
+ };
73
+ const DEFAULT_LOGIN = {
74
+ enabled: false,
75
+ background: {
76
+ type: "gradient",
77
+ color: "#1e293b",
78
+ gradient: { preset: "\u6696\u9633", angle: 135, colors: GRADIENT_PRESETS["\u6696\u9633"] },
79
+ image: { url: "", fit: "cover", position: "center" },
80
+ dim: 15
81
+ },
82
+ card: { glass: true, opacity: 70, blur: 18, radius: 18, shadow: true }
83
+ };
84
+ function isObj(v) {
85
+ return v != null && typeof v === "object" && !Array.isArray(v);
86
+ }
87
+ function deepMerge(base, over) {
88
+ if (!isObj(base)) return over === void 0 ? base : over;
89
+ const out = Array.isArray(base) ? [...base] : { ...base };
90
+ for (const k of Object.keys(base)) {
91
+ if (isObj(base[k])) out[k] = deepMerge(base[k], over == null ? void 0 : over[k]);
92
+ else if (over && over[k] !== void 0) out[k] = over[k];
93
+ }
94
+ return out;
95
+ }
96
+ function mergeConfig(partial) {
97
+ return {
98
+ app: deepMerge(DEFAULT_APP, partial == null ? void 0 : partial.app),
99
+ login: deepMerge(DEFAULT_LOGIN, partial == null ? void 0 : partial.login)
100
+ };
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ DEFAULT_APP,
105
+ DEFAULT_LOGIN,
106
+ FONT_PRESETS,
107
+ GRADIENT_PRESETS,
108
+ mergeConfig
109
+ });
@@ -0,0 +1,5 @@
1
+ import type { ExtraThemeConfig, AppConfig, Selectors } from './types';
2
+ /** True when any app-scope section (bg/card, header, sider, font, or scrollbar) is on. */
3
+ export declare function isAppActive(app: AppConfig): boolean;
4
+ /** Thin stylesheet: page background + frosted blur (surface colors are antd tokens). */
5
+ export declare function generateStylesheet(config: ExtraThemeConfig, sel: Selectors): string;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var generateCss_exports = {};
28
+ __export(generateCss_exports, {
29
+ generateStylesheet: () => generateStylesheet,
30
+ isAppActive: () => isAppActive
31
+ });
32
+ module.exports = __toCommonJS(generateCss_exports);
33
+ var import_color = require("./color");
34
+ const FONT_FORMATS = ["woff2", "woff", "truetype", "opentype"];
35
+ function bgDecl(bg) {
36
+ const b = (0, import_color.buildBackground)(bg);
37
+ let image = b.image;
38
+ const dim = (0, import_color.safeNum)(bg.dim);
39
+ if (dim > 0 && bg.type !== "none") {
40
+ const d = (0, import_color.hexToRgba)("#000000", dim / 100);
41
+ image = `linear-gradient(${d},${d}),${b.image}`;
42
+ }
43
+ return `background:${image}!important;background-size:${b.size}!important;background-repeat:${b.repeat}!important;background-position:${b.position}!important;background-attachment:fixed!important;`;
44
+ }
45
+ function scopedList(scope, list) {
46
+ return list.split(",").map((s) => `${scope} ${s.trim()}`).join(",");
47
+ }
48
+ function blur(px) {
49
+ const n = (0, import_color.safeNum)(px);
50
+ return `backdrop-filter:blur(${n}px);-webkit-backdrop-filter:blur(${n}px);`;
51
+ }
52
+ function appCss(app, s) {
53
+ var _a, _b;
54
+ const scope = "body.extra-theme-app-on";
55
+ const out = [];
56
+ if (app.enabled) {
57
+ out.push(`${scope}{${bgDecl(app.background)}}`);
58
+ out.push(`${scope} .ant-layout-sider-children{top:auto!important;bottom:0!important;}`);
59
+ out.push(`${scopedList(scope, s.content)}{background:transparent!important;}`);
60
+ out.push(`${scopedList(scope, s.card + " div:not([class]):not([id])")}{background-color:transparent!important;}`);
61
+ if (app.card.glass && app.card.blur > 0) {
62
+ out.push(`${scopedList(scope, s.card)},${scope} .code-block{${blur(app.card.blur)}}`);
63
+ }
64
+ }
65
+ if (app.header.enabled) {
66
+ const headerBg = (0, import_color.withAlpha)(app.header.color, app.header.opacity / 100);
67
+ const glass = app.header.style === "frosted" && app.header.blur > 0 ? blur(app.header.blur) : "";
68
+ out.push(`${scope} .ant-layout-header,${scope} .ant-pro-layout-header{background:${headerBg}!important;${glass}}`);
69
+ }
70
+ const font = app.font;
71
+ if (font == null ? void 0 : font.enabled) {
72
+ if (font.source === "upload" && ((_a = font.upload) == null ? void 0 : _a.url)) {
73
+ const name = (0, import_color.sanitizeFontFamily)(font.upload.name || "").replace(/["']/g, "") || "ExtraTheme Font";
74
+ const url = (0, import_color.sanitizeCssUrl)(font.upload.url);
75
+ const fmt = FONT_FORMATS.includes(font.upload.format) ? font.upload.format : (0, import_color.fontFormatFromUrl)(font.upload.url);
76
+ if (url) {
77
+ out.push(`@font-face{font-family:"${name}";src:url("${url}")${fmt ? ` format("${fmt}")` : ""};font-display:swap;}`);
78
+ out.push(`${scope}{font-family:"${name}"!important;}`);
79
+ }
80
+ } else if (font.source !== "upload") {
81
+ const fam = (0, import_color.sanitizeFontFamily)(font.family || "");
82
+ if (fam) out.push(`${scope}{font-family:${fam}!important;}`);
83
+ }
84
+ }
85
+ if (app.sider.enabled) {
86
+ const siderBg = (0, import_color.withAlpha)(app.sider.color, app.sider.opacity / 100);
87
+ const glass = app.sider.style === "frosted" && app.sider.blur > 0 ? blur(app.sider.blur) : "";
88
+ out.push(
89
+ `${scope} .ant-layout-sider{background:transparent!important;}${scope} .ant-layout-sider-children{background:${siderBg}!important;border-right:none!important;${glass}}${scope} .ant-layout-sider .ant-menu{background:transparent!important;border-inline-end:none!important;}${scope} .ant-layout-sider .ant-menu::-webkit-scrollbar{width:6px;height:6px;}${scope} .ant-layout-sider .ant-menu::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.18);border-radius:4px;}${scope} .ant-layout-sider .ant-menu::-webkit-scrollbar-track{background:transparent;}`
90
+ );
91
+ }
92
+ if ((_b = app.scrollbar) == null ? void 0 : _b.enabled) {
93
+ if (app.scrollbar.mode === "hidden") {
94
+ out.push(
95
+ `${scope} ::-webkit-scrollbar,${scope}::-webkit-scrollbar{display:none;width:0;height:0;}${scope},${scope} *{scrollbar-width:none;-ms-overflow-style:none;}`
96
+ );
97
+ } else {
98
+ out.push(
99
+ `${scope} ::-webkit-scrollbar,${scope}::-webkit-scrollbar{width:10px;height:10px;}${scope} ::-webkit-scrollbar-thumb,${scope}::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.22);border-radius:8px;border:2px solid transparent;background-clip:content-box;}${scope} ::-webkit-scrollbar-thumb:hover,${scope}::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,0.4);background-clip:content-box;}${scope} ::-webkit-scrollbar-track,${scope}::-webkit-scrollbar-track{background:transparent;}`
100
+ );
101
+ }
102
+ }
103
+ return out.join("\n");
104
+ }
105
+ function isAppActive(app) {
106
+ var _a, _b;
107
+ return !!(app.enabled || app.header.enabled || app.sider.enabled || ((_a = app.font) == null ? void 0 : _a.enabled) || ((_b = app.scrollbar) == null ? void 0 : _b.enabled));
108
+ }
109
+ function generateStylesheet(config, sel) {
110
+ return appCss(config.app, sel.app).trim();
111
+ }
112
+ // Annotate the CommonJS export names for ESM import in node:
113
+ 0 && (module.exports = {
114
+ generateStylesheet,
115
+ isAppActive
116
+ });
@@ -0,0 +1,15 @@
1
+ import type { Selectors, ExtraThemeConfig } from './types';
2
+ export declare const CHANGE_EVENT = "extra-theme:changed";
3
+ type ApiRequest = (opts: any) => Promise<any>;
4
+ export declare class ThemeInjector {
5
+ private request;
6
+ private selectors;
7
+ constructor(request: ApiRequest, selectors: Selectors);
8
+ /** Paint from cache immediately, then fetch fresh config, then listen for saves. */
9
+ start(): void;
10
+ /** Fetch the public config, cache it, and apply. Silent on failure (keep cache / native). */
11
+ refresh(): Promise<void>;
12
+ /** Toggle body markers + (re)write the single injected <style>. */
13
+ apply(cfg: ExtraThemeConfig): void;
14
+ }
15
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var injector_exports = {};
28
+ __export(injector_exports, {
29
+ CHANGE_EVENT: () => CHANGE_EVENT,
30
+ ThemeInjector: () => ThemeInjector
31
+ });
32
+ module.exports = __toCommonJS(injector_exports);
33
+ var import_generateCss = require("./generateCss");
34
+ var import_defaults = require("./defaults");
35
+ const STYLE_ID = "extra-theme-style";
36
+ const CACHE_KEY = "EXTRA_THEME_CACHE";
37
+ const CHANGE_EVENT = "extra-theme:changed";
38
+ class ThemeInjector {
39
+ constructor(request, selectors) {
40
+ this.request = request;
41
+ this.selectors = selectors;
42
+ }
43
+ /** Paint from cache immediately, then fetch fresh config, then listen for saves. */
44
+ start() {
45
+ try {
46
+ const cached = localStorage.getItem(CACHE_KEY);
47
+ if (cached) this.apply((0, import_defaults.mergeConfig)(JSON.parse(cached)));
48
+ } catch {
49
+ }
50
+ this.refresh();
51
+ window.addEventListener(CHANGE_EVENT, () => this.refresh());
52
+ }
53
+ /** Fetch the public config, cache it, and apply. Silent on failure (keep cache / native). */
54
+ async refresh() {
55
+ var _a;
56
+ try {
57
+ const res = await this.request({ url: "extraTheme:getPublic", method: "get" });
58
+ const data = ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.data) ?? (res == null ? void 0 : res.data) ?? {};
59
+ const cfg = (0, import_defaults.mergeConfig)(data);
60
+ try {
61
+ localStorage.setItem(CACHE_KEY, JSON.stringify(cfg));
62
+ } catch {
63
+ }
64
+ this.apply(cfg);
65
+ } catch {
66
+ }
67
+ }
68
+ /** Toggle body markers + (re)write the single injected <style>. */
69
+ apply(cfg) {
70
+ var _a;
71
+ const body = document.body;
72
+ if (!body) return;
73
+ body.classList.toggle("extra-theme-app-on", (0, import_generateCss.isAppActive)(cfg.app));
74
+ body.classList.toggle("extra-theme-login-on", !!((_a = cfg.login) == null ? void 0 : _a.enabled));
75
+ let el = document.getElementById(STYLE_ID);
76
+ if (!el) {
77
+ el = document.createElement("style");
78
+ el.id = STYLE_ID;
79
+ document.head.appendChild(el);
80
+ }
81
+ el.textContent = (0, import_generateCss.generateStylesheet)(cfg, this.selectors);
82
+ }
83
+ }
84
+ // Annotate the CommonJS export names for ESM import in node:
85
+ 0 && (module.exports = {
86
+ CHANGE_EVENT,
87
+ ThemeInjector
88
+ });
@@ -0,0 +1,17 @@
1
+ import type { Selectors } from './types';
2
+ /**
3
+ * DOM anchors pinned against the live NocoBase 2.1.19 pro-layout DOM. Verified
4
+ * to be IDENTICAL for the legacy `/admin` client and the modern `/v` client —
5
+ * both render the same `@ant-design/pro-layout` chrome (only the content engine
6
+ * differs), so one selector set drives both lanes.
7
+ *
8
+ * .ant-layout → layout root (background)
9
+ * .ant-layout-header → top nav bar (2 exist; both frost)
10
+ * .ant-layout-sider → left sider (settings / data sub-menu / side-mode)
11
+ * .ant-card → content block containers
12
+ * pro-layout-content/-container + nb-subpages-slot* → transparent so bg shows through
13
+ *
14
+ * Login anchors are placeholders; pinned for real in Task 10 against the
15
+ * sign-in DOM (legacy `/signin` + modern `/v/signin`).
16
+ */
17
+ export declare const DEFAULT_SELECTORS: Selectors;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var selectors_exports = {};
28
+ __export(selectors_exports, {
29
+ DEFAULT_SELECTORS: () => DEFAULT_SELECTORS
30
+ });
31
+ module.exports = __toCommonJS(selectors_exports);
32
+ const DEFAULT_SELECTORS = {
33
+ app: {
34
+ appRoot: ".ant-layout",
35
+ header: ".ant-layout-header",
36
+ sider: ".ant-layout-sider",
37
+ card: ".ant-card",
38
+ content: '.ant-pro-layout-content,.ant-pro-layout-container,.ant-layout-content,.ant-page-header,[class*="nb-subpages-slot"],[class*="nb-subpages-slot"]>div'
39
+ },
40
+ login: {
41
+ loginRoot: ".ant-layout",
42
+ loginCard: ".ant-card"
43
+ }
44
+ };
45
+ // Annotate the CommonJS export names for ESM import in node:
46
+ 0 && (module.exports = {
47
+ DEFAULT_SELECTORS
48
+ });
@@ -0,0 +1,97 @@
1
+ /**
2
+ * ExtraTheme config types — shared by server (defaults/seed), legacy client and
3
+ * modern client-v2. Two logical config groups: `app` (工作区外观, applies to
4
+ * /admin + /v) and `login` (登录页外观, independent). Everything is a plain
5
+ * JSON-serialisable object so it round-trips through the `extraThemeSettings`
6
+ * collection and the public `extraTheme:getPublic` action.
7
+ */
8
+ export type BgType = 'none' | 'color' | 'gradient' | 'image';
9
+ export type NavStyle = 'solid' | 'frosted';
10
+ export type TextTone = 'dark' | 'light';
11
+ export type BgFit = 'cover' | 'contain' | 'stretch' | 'repeat';
12
+ export interface BackgroundConfig {
13
+ type: BgType;
14
+ color: string;
15
+ gradient: {
16
+ preset: string;
17
+ angle: number;
18
+ colors: string[];
19
+ };
20
+ image: {
21
+ url: string;
22
+ fit: BgFit;
23
+ position: string;
24
+ };
25
+ dim: number;
26
+ }
27
+ export interface CardConfig {
28
+ glass: boolean;
29
+ opacity: number;
30
+ blur: number;
31
+ border: boolean;
32
+ }
33
+ export interface NavConfig {
34
+ enabled: boolean;
35
+ style: NavStyle;
36
+ color: string;
37
+ opacity: number;
38
+ blur: number;
39
+ text: TextTone;
40
+ }
41
+ export interface FontUpload {
42
+ url: string;
43
+ name: string;
44
+ format: string;
45
+ }
46
+ export interface FontConfig {
47
+ enabled: boolean;
48
+ source: 'system' | 'upload';
49
+ family: string;
50
+ upload: FontUpload;
51
+ }
52
+ export type ScrollbarMode = 'always' | 'hidden';
53
+ export interface ScrollbarConfig {
54
+ enabled: boolean;
55
+ mode: ScrollbarMode;
56
+ }
57
+ export interface AppConfig {
58
+ enabled: boolean;
59
+ background: BackgroundConfig;
60
+ card: CardConfig;
61
+ header: NavConfig;
62
+ sider: NavConfig;
63
+ font: FontConfig;
64
+ scrollbar: ScrollbarConfig;
65
+ }
66
+ export interface LoginCard {
67
+ glass: boolean;
68
+ opacity: number;
69
+ blur: number;
70
+ radius: number;
71
+ shadow: boolean;
72
+ }
73
+ export interface LoginConfig {
74
+ enabled: boolean;
75
+ background: BackgroundConfig;
76
+ card: LoginCard;
77
+ }
78
+ export interface ExtraThemeConfig {
79
+ app: AppConfig;
80
+ login: LoginConfig;
81
+ }
82
+ /** DOM anchors — each client lane passes its own (pinned against the live DOM). */
83
+ export interface AppSelectors {
84
+ appRoot: string;
85
+ header: string;
86
+ sider: string;
87
+ card: string;
88
+ content: string;
89
+ }
90
+ export interface LoginSelectors {
91
+ loginRoot: string;
92
+ loginCard: string;
93
+ }
94
+ export interface Selectors {
95
+ app: AppSelectors;
96
+ login: LoginSelectors;
97
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
23
+ var types_exports = {};
24
+ module.exports = __toCommonJS(types_exports);