@nonamelego/dsh-catppuccin 0.1.2 → 0.2.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/lib/client.js CHANGED
@@ -119,7 +119,23 @@ window.__ModuleLoader__.load({
119
119
  "flavor.latte": "Latte",
120
120
  "flavor.frappe": "Frappé",
121
121
  "flavor.macchiato": "Macchiato",
122
- "flavor.mocha": "Mocha"
122
+ "flavor.mocha": "Mocha",
123
+ "glass.title": "玻璃质感",
124
+ "glass.description": "顶栏、侧边栏、输入框、统计行、轨迹视图的磨砂玻璃效果,一键开关。",
125
+ "glass.enable": "总开关",
126
+ "glass.on": "开启",
127
+ "glass.off": "关闭",
128
+ "glass.mode": "模式",
129
+ "glass.modeMica": "云母效果",
130
+ "glass.modeCompat": "兼容模式",
131
+ "glass.modeHint": "云母效果把界面改成悬浮磨砂卡片;兼容模式保持原版排版,只换玻璃材质",
132
+ "glass.blur": "玻璃模糊度",
133
+ "glass.frost": "磨砂度",
134
+ "glass.brightness": "背景亮度",
135
+ "glass.brightnessHintDark": "深色模式:0 压暗至纯黑,50 原样",
136
+ "glass.brightnessHintLight": "浅色模式:50 原样,100 提亮至纯白",
137
+ "glass.hue": "背景色调",
138
+ "glass.hueHint": "背景渐变与光晕的色相偏移(0–360°)"
123
139
  };
124
140
  /** English dictionary, checked complete against the zh key set. */
125
141
  const en = {
@@ -129,7 +145,23 @@ window.__ModuleLoader__.load({
129
145
  "flavor.latte": "Latte",
130
146
  "flavor.frappe": "Frappé",
131
147
  "flavor.macchiato": "Macchiato",
132
- "flavor.mocha": "Mocha"
148
+ "flavor.mocha": "Mocha",
149
+ "glass.title": "Glassmorphism",
150
+ "glass.description": "Frosted glass for the top bar, sidebar, composer, stats line and trajectory view — one click on/off.",
151
+ "glass.enable": "Master switch",
152
+ "glass.on": "On",
153
+ "glass.off": "Off",
154
+ "glass.mode": "Mode",
155
+ "glass.modeMica": "Mica",
156
+ "glass.modeCompat": "Compatibility",
157
+ "glass.modeHint": "Mica restyles the UI into floating frosted cards; Compatibility keeps the stock layout and only swaps the material to glass",
158
+ "glass.blur": "Glass blur",
159
+ "glass.frost": "Frost",
160
+ "glass.brightness": "Backdrop brightness",
161
+ "glass.brightnessHintDark": "Dark mode: 0 fades to pure black, 50 unchanged",
162
+ "glass.brightnessHintLight": "Light mode: 50 unchanged, 100 brightens to pure white",
163
+ "glass.hue": "Backdrop hue",
164
+ "glass.hueHint": "Hue shift for the backdrop gradient and glow (0–360°)"
133
165
  };
134
166
  const CATPPUCCIN_FLAVORS = [
135
167
  {
@@ -814,11 +846,581 @@ window.__ModuleLoader__.load({
814
846
  }
815
847
  ];
816
848
  //#endregion
849
+ //#region src/client/glass/glass-seams.ts
850
+ const SEAMS = [
851
+ {
852
+ attribute: "data-dsh-glass-frame",
853
+ selector: ":has(> [class*=\"sidebarCol\"])"
854
+ },
855
+ {
856
+ attribute: "data-dsh-glass-sidebar-root",
857
+ selector: "[class*=\"sidebarCol\"] [class*=\"root\"]",
858
+ first: true
859
+ },
860
+ {
861
+ attribute: "data-dsh-glass-surface",
862
+ selector: "button[class*=\"newSession\"]"
863
+ },
864
+ {
865
+ attribute: "data-dsh-glass-trajectory",
866
+ selector: "[data-conversation-composer-overlay]"
867
+ },
868
+ {
869
+ attribute: "data-dsh-glass-details",
870
+ selector: "[class*=\"detailsCol\"] [class*=\"root\"]",
871
+ first: true
872
+ },
873
+ {
874
+ attribute: "data-dsh-glass-inputbar",
875
+ selector: ":has(> [data-composer-card])"
876
+ },
877
+ {
878
+ attribute: "data-dsh-glass-add",
879
+ selector: "[data-composer-card] [class*=\"add\"]"
880
+ },
881
+ {
882
+ attribute: "data-dsh-glass-stats",
883
+ selector: "[data-slot=\"conversation.composer.dock\"] [class*=\"root\"]"
884
+ }
885
+ ];
886
+ function stamp(seam) {
887
+ if (seam.first) {
888
+ const el = document.querySelector(seam.selector);
889
+ if (el !== null && !el.hasAttribute(seam.attribute)) el.setAttribute(seam.attribute, "");
890
+ return;
891
+ }
892
+ for (const el of document.querySelectorAll(seam.selector)) if (!el.hasAttribute(seam.attribute)) el.setAttribute(seam.attribute, "");
893
+ }
894
+ function stampAll() {
895
+ for (const seam of SEAMS) stamp(seam);
896
+ }
897
+ /**
898
+ * Stamp the seams once, then keep them stamped as React remounts nodes.
899
+ * @returns a disposer that disconnects the observer.
900
+ */
901
+ function startGlassSeamStamper() {
902
+ stampAll();
903
+ const observer = new MutationObserver(() => {
904
+ stampAll();
905
+ });
906
+ observer.observe(document.documentElement, {
907
+ childList: true,
908
+ subtree: true
909
+ });
910
+ return () => {
911
+ observer.disconnect();
912
+ };
913
+ }
914
+ //#endregion
915
+ //#region src/client/glass/glass-layer.ts
916
+ /** html attribute selecting the glass layer: CSS hooks and ambient effects. */
917
+ const GLASS_ATTRIBUTE = "data-dsh-glass";
918
+ /** html attribute selecting the mica (floating-card) layout rules. */
919
+ const GLASS_FLOAT_ATTRIBUTE = "data-dsh-glass-float";
920
+ /** html attribute selecting the compatibility (stock-layout) rules. */
921
+ const GLASS_COMPAT_ATTRIBUTE = "data-dsh-glass-compat";
922
+ /** localStorage key carrying the layer enable flag. */
923
+ const GLASS_ENABLED_KEY = "dsh.catppuccin.glass.enabled";
924
+ const SETTINGS_DEFAULTS = {
925
+ mode: "mica",
926
+ blur: 14,
927
+ frost: 50,
928
+ brightness: 50,
929
+ hue: 0
930
+ };
931
+ /** Numeric knob keys and their localStorage names. */
932
+ const NUMERIC_KEYS = {
933
+ blur: "dsh.catppuccin.glass.blur",
934
+ frost: "dsh.catppuccin.glass.frost",
935
+ brightness: "dsh.catppuccin.glass.brightness",
936
+ hue: "dsh.catppuccin.glass.hue"
937
+ };
938
+ const MODE_KEY = "dsh.catppuccin.glass.mode";
939
+ /** Clamp a numeric knob into its sane range. */
940
+ function clampSetting(key, value) {
941
+ const max = key === "blur" ? 40 : key === "frost" || key === "brightness" ? 100 : 360;
942
+ return Number.isFinite(value) ? Math.min(max, Math.max(0, value)) : SETTINGS_DEFAULTS[key];
943
+ }
944
+ /** Read one numeric knob from localStorage (absent/parse failure means the default). */
945
+ function readSetting(key) {
946
+ try {
947
+ const raw = localStorage.getItem(NUMERIC_KEYS[key]);
948
+ return raw === null ? SETTINGS_DEFAULTS[key] : clampSetting(key, Number(raw));
949
+ } catch {
950
+ return SETTINGS_DEFAULTS[key];
951
+ }
952
+ }
953
+ /** Persist one numeric knob (storage failures keep the in-memory state). */
954
+ function writeSetting(key, value) {
955
+ try {
956
+ localStorage.setItem(NUMERIC_KEYS[key], String(value));
957
+ } catch {}
958
+ }
959
+ /** Read the rendering mode ('mica' or 'compat'). */
960
+ function readMode() {
961
+ try {
962
+ return localStorage.getItem(MODE_KEY) === "compat" ? "compat" : "mica";
963
+ } catch {
964
+ return "mica";
965
+ }
966
+ }
967
+ /** Persist the rendering mode. */
968
+ function writeMode(value) {
969
+ try {
970
+ localStorage.setItem(MODE_KEY, value);
971
+ } catch {}
972
+ }
973
+ /** Read the persisted enable flag (absent storage means the default). */
974
+ function readEnabled() {
975
+ try {
976
+ const raw = localStorage.getItem(GLASS_ENABLED_KEY);
977
+ return raw === null ? false : raw === "true";
978
+ } catch {
979
+ return false;
980
+ }
981
+ }
982
+ /** Persist the enable flag (storage failures keep the in-memory state). */
983
+ function writeEnabled(value) {
984
+ try {
985
+ localStorage.setItem(GLASS_ENABLED_KEY, String(value));
986
+ } catch {}
987
+ }
988
+ /** Resolved scheme from the theme service (falls back to the body attribute). */
989
+ function resolveDark(ctx) {
990
+ try {
991
+ return ctx.theme.getTheme().active.colorScheme === "dark";
992
+ } catch {
993
+ return document.body.hasAttribute("data-ds-dark-theme");
994
+ }
995
+ }
996
+ /**
997
+ * The ambient backdrop markup injected while enabled (CSS-only, no WebGL):
998
+ * the living backdrop plus the two page-edge fade bands that blur the chat
999
+ * content melting into the viewport edges (ported from
1000
+ * DSH-Transparent-UI-Plugin). All three are fixed-position, click-through,
1001
+ * and removed together on disable.
1002
+ */
1003
+ const AMBIENT_MARKUP = [
1004
+ "<div data-dsh-glass-ambient aria-hidden=\"true\">",
1005
+ " <span data-dsh-glass-blob=\"1\"></span>",
1006
+ " <span data-dsh-glass-blob=\"2\"></span>",
1007
+ " <span data-dsh-glass-blob=\"3\"></span>",
1008
+ "</div>",
1009
+ "<span data-dsh-glass-fade=\"top\" aria-hidden=\"true\"></span>",
1010
+ "<span data-dsh-glass-fade=\"bottom\" aria-hidden=\"true\"></span>"
1011
+ ].join("");
1012
+ /**
1013
+ * Owns the glass layer lifecycle: reads the durable enable flag and knobs,
1014
+ * and applies / retracts every layer on change. Cross-tab flips arrive through
1015
+ * the storage event; the seam observer and every subscription are released
1016
+ * when the plugin fiber is disposed.
1017
+ */
1018
+ var GlassLayer = class {
1019
+ enabled = false;
1020
+ settings = { ...SETTINGS_DEFAULTS };
1021
+ /** Cached snapshot for the settings row (stable reference between changes). */
1022
+ snapshot;
1023
+ listeners = /* @__PURE__ */ new Set();
1024
+ dark = false;
1025
+ seamDisposer;
1026
+ ctx;
1027
+ /**
1028
+ * @param ctx - owning client context (listeners ride this fiber).
1029
+ */
1030
+ constructor(ctx) {
1031
+ this.ctx = ctx;
1032
+ this.enabled = readEnabled();
1033
+ this.reloadSettings();
1034
+ this.dark = resolveDark(ctx);
1035
+ this.snapshot = this.buildSnapshot();
1036
+ ctx.effect(() => {
1037
+ const onStorage = (event) => {
1038
+ if (event.key === null) {
1039
+ this.reloadSettings();
1040
+ this.enabled = readEnabled();
1041
+ this.sync();
1042
+ } else if (event.key === "dsh.catppuccin.glass.enabled") {
1043
+ this.enabled = readEnabled();
1044
+ this.sync();
1045
+ } else if (event.key === MODE_KEY || event.key in NUMERIC_KEYS) {
1046
+ this.reloadSettings();
1047
+ if (this.enabled) this.applySettings();
1048
+ this.publish();
1049
+ }
1050
+ };
1051
+ window.addEventListener("storage", onStorage);
1052
+ const themeListener = ctx.on("theme/change", () => {
1053
+ this.dark = resolveDark(ctx);
1054
+ if (this.enabled) this.applySettings();
1055
+ this.publish();
1056
+ });
1057
+ return () => {
1058
+ window.removeEventListener("storage", onStorage);
1059
+ themeListener();
1060
+ this.unmount();
1061
+ };
1062
+ }, "catppuccin: glass layer lifecycle");
1063
+ this.sync();
1064
+ }
1065
+ /** Current state snapshot (stable reference until the next change). */
1066
+ getSnapshot() {
1067
+ return this.snapshot;
1068
+ }
1069
+ /** Subscribe to snapshot changes. Returns the disposer. */
1070
+ subscribe(listener) {
1071
+ this.listeners.add(listener);
1072
+ return () => {
1073
+ this.listeners.delete(listener);
1074
+ };
1075
+ }
1076
+ /** Flip the layer: persist, then apply or retract every owned effect. */
1077
+ setEnabled(value) {
1078
+ if (value === this.enabled) return;
1079
+ this.enabled = value;
1080
+ writeEnabled(value);
1081
+ this.sync();
1082
+ }
1083
+ /** Set the rendering mode ('mica' or 'compat'). */
1084
+ setMode(value) {
1085
+ if (value === this.settings.mode) return;
1086
+ this.settings.mode = value;
1087
+ writeMode(value);
1088
+ if (this.enabled) this.applySettings();
1089
+ this.publish();
1090
+ }
1091
+ /** Set the glass blur radius (px). */
1092
+ setBlur(value) {
1093
+ const next = clampSetting("blur", value);
1094
+ if (next === this.settings.blur) return;
1095
+ this.settings.blur = next;
1096
+ writeSetting("blur", next);
1097
+ if (this.enabled) this.applySettings();
1098
+ this.publish();
1099
+ }
1100
+ /** Set the glass frost amount (0-100). */
1101
+ setFrost(value) {
1102
+ const next = clampSetting("frost", value);
1103
+ if (next === this.settings.frost) return;
1104
+ this.settings.frost = next;
1105
+ writeSetting("frost", next);
1106
+ if (this.enabled) this.applySettings();
1107
+ this.publish();
1108
+ }
1109
+ /** Set the backdrop brightness (0-100: 0 = pure black, 50 = transparent, 100 = pure white). */
1110
+ setBrightness(value) {
1111
+ const next = clampSetting("brightness", value);
1112
+ if (next === this.settings.brightness) return;
1113
+ this.settings.brightness = next;
1114
+ writeSetting("brightness", next);
1115
+ if (this.enabled) this.applySettings();
1116
+ this.publish();
1117
+ }
1118
+ /** Set the ambient hue shift (degrees). */
1119
+ setHue(value) {
1120
+ const next = clampSetting("hue", value);
1121
+ if (next === this.settings.hue) return;
1122
+ this.settings.hue = next;
1123
+ writeSetting("hue", next);
1124
+ if (this.enabled) this.applySettings();
1125
+ this.publish();
1126
+ }
1127
+ /** Re-read every knob from localStorage into memory. */
1128
+ reloadSettings() {
1129
+ this.settings = {
1130
+ mode: readMode(),
1131
+ blur: readSetting("blur"),
1132
+ frost: readSetting("frost"),
1133
+ brightness: readSetting("brightness"),
1134
+ hue: readSetting("hue")
1135
+ };
1136
+ }
1137
+ buildSnapshot() {
1138
+ return {
1139
+ ...this.settings,
1140
+ enabled: this.enabled,
1141
+ dark: this.dark
1142
+ };
1143
+ }
1144
+ publish() {
1145
+ this.snapshot = this.buildSnapshot();
1146
+ for (const listener of this.listeners) listener();
1147
+ }
1148
+ sync() {
1149
+ if (this.enabled) this.mount();
1150
+ else this.unmount();
1151
+ this.publish();
1152
+ }
1153
+ /** Write the knob-driven CSS variables and mode attributes onto <html>. */
1154
+ applySettings() {
1155
+ const style = document.documentElement.style;
1156
+ style.setProperty("--dsh-glass-blur", `${this.settings.blur}px`);
1157
+ style.setProperty("--dsh-glass-frost", String(Math.min(this.settings.frost / 50, 1.4)));
1158
+ style.setProperty("--dsh-glass-hue", `${this.settings.hue}deg`);
1159
+ const dark = this.dark;
1160
+ style.setProperty("--dsh-glass-brightness-black", String(dark ? Math.max(0, (50 - this.settings.brightness) / 50) : 0));
1161
+ style.setProperty("--dsh-glass-brightness-white", String(dark ? 0 : Math.max(0, (this.settings.brightness - 50) / 50)));
1162
+ const compat = this.settings.mode === "compat";
1163
+ document.documentElement.toggleAttribute(GLASS_FLOAT_ATTRIBUTE, !compat);
1164
+ document.documentElement.toggleAttribute(GLASS_COMPAT_ATTRIBUTE, compat);
1165
+ }
1166
+ mount() {
1167
+ document.documentElement.setAttribute(GLASS_ATTRIBUTE, "");
1168
+ this.applySettings();
1169
+ this.ensureAmbient();
1170
+ if (this.seamDisposer === void 0) this.seamDisposer = startGlassSeamStamper();
1171
+ }
1172
+ unmount() {
1173
+ document.documentElement.removeAttribute(GLASS_ATTRIBUTE);
1174
+ document.documentElement.removeAttribute(GLASS_FLOAT_ATTRIBUTE);
1175
+ document.documentElement.removeAttribute(GLASS_COMPAT_ATTRIBUTE);
1176
+ for (const node of document.querySelectorAll("[data-dsh-glass-ambient], [data-dsh-glass-fade]")) node.remove();
1177
+ this.seamDisposer?.();
1178
+ this.seamDisposer = void 0;
1179
+ }
1180
+ /** Insert the ambient backdrop + edge fades (or reuse the existing ones). */
1181
+ ensureAmbient() {
1182
+ if (document.querySelector("[data-dsh-glass-ambient]") !== null) return;
1183
+ const holder = document.createElement("div");
1184
+ holder.innerHTML = AMBIENT_MARKUP;
1185
+ for (const node of Array.from(holder.children)) if (node instanceof HTMLElement) document.body.append(node);
1186
+ }
1187
+ };
1188
+ //#endregion
1189
+ //#region \0dsh-css:D:\Vibe-Coding\dsh-catppuccin\src\client\glass\GlassRow.module.css.mjs
1190
+ const css$1 = ".SYX6rq_group{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;padding:16px 0;display:flex}.SYX6rq_title{color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}.SYX6rq_description{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.SYX6rq_controls{flex-direction:column;gap:10px;margin-top:6px;display:flex}.SYX6rq_row{align-items:center;gap:10px;display:flex}.SYX6rq_rowLabel{width:92px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.SYX6rq_rowHint,.SYX6rq_knobHint{color:var(--dsw-alias-label-tertiary);margin-top:-4px;margin-left:102px;font-size:12px;line-height:18px}.SYX6rq_toggle,.SYX6rq_toggleOn{border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:14px;align-items:center;gap:6px;padding:0 10px 0 6px;font-size:12px;line-height:18px;display:inline-flex}.SYX6rq_toggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.SYX6rq_toggleOn{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-state-business-primary);border-color:#0000}.SYX6rq_check{justify-content:center;align-items:center;width:16px;height:16px;font-size:12px;display:inline-flex}.SYX6rq_knob{align-items:center;gap:10px;display:flex}.SYX6rq_knobLabel{width:92px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px;line-height:18px}.SYX6rq_slider{min-width:0;accent-color:var(--dsw-alias-state-business-primary);flex:1}.SYX6rq_numberWrap{flex:none;align-items:center;gap:4px;display:inline-flex}.SYX6rq_number{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);width:56px;height:26px;color:var(--dsw-alias-label-primary);text-align:right;border-radius:8px;padding:0 6px;font-size:12px;line-height:18px}.SYX6rq_number::-webkit-outer-spin-button,.SYX6rq_number::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.SYX6rq_unit{width:18px;color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:18px}.SYX6rq_segmented{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:inline-flex;overflow:hidden}.SYX6rq_seg,.SYX6rq_segActive{height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0 12px;font-size:12px;line-height:18px}.SYX6rq_seg+.SYX6rq_seg,.SYX6rq_segActive+.SYX6rq_seg,.SYX6rq_seg+.SYX6rq_segActive{border-left:1px solid var(--dsw-alias-border-l2)}.SYX6rq_segActive{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-state-business-primary)}";
1191
+ const tagId$1 = "@nonamelego/dsh-catppuccin/GlassRow.module.css";
1192
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1193
+ const tag = document.createElement("style");
1194
+ tag.dataset.plugin = "@nonamelego/dsh-catppuccin";
1195
+ tag.dataset.pluginCss = tagId$1;
1196
+ tag.textContent = css$1;
1197
+ document.head.appendChild(tag);
1198
+ }
1199
+ var GlassRow_module_css_default = {
1200
+ "check": "SYX6rq_check",
1201
+ "controls": "SYX6rq_controls",
1202
+ "description": "SYX6rq_description",
1203
+ "group": "SYX6rq_group",
1204
+ "knob": "SYX6rq_knob",
1205
+ "knobHint": "SYX6rq_knobHint",
1206
+ "knobLabel": "SYX6rq_knobLabel",
1207
+ "number": "SYX6rq_number",
1208
+ "numberWrap": "SYX6rq_numberWrap",
1209
+ "row": "SYX6rq_row",
1210
+ "rowHint": "SYX6rq_rowHint",
1211
+ "rowLabel": "SYX6rq_rowLabel",
1212
+ "seg": "SYX6rq_seg",
1213
+ "segActive": "SYX6rq_segActive",
1214
+ "segmented": "SYX6rq_segmented",
1215
+ "slider": "SYX6rq_slider",
1216
+ "title": "SYX6rq_title",
1217
+ "toggle": "SYX6rq_toggle",
1218
+ "toggleOn": "SYX6rq_toggleOn",
1219
+ "unit": "SYX6rq_unit"
1220
+ };
1221
+ //#endregion
1222
+ //#region src/client/glass/glass-row.tsx
1223
+ /**
1224
+ * Catppuccin glass settings row — registered into the General settings
1225
+ * section (`settings.general.item`, right under the Catppuccin flavour row).
1226
+ * Holds the master on/off switch plus every glass knob: mode (mica /
1227
+ * compatibility), blur, frost, backdrop brightness and ambient hue. Every
1228
+ * write goes straight through to the glass layer, so the skin moves live.
1229
+ */
1230
+ /** One slider + number box, wired to a single value. */
1231
+ function Knob(props) {
1232
+ const { label, value, min, max, step, unit, onChange } = props;
1233
+ const clamp = (n) => Math.min(max, Math.max(min, Number.isFinite(n) ? n : min));
1234
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1235
+ className: GlassRow_module_css_default.knob,
1236
+ children: [
1237
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1238
+ className: GlassRow_module_css_default.knobLabel,
1239
+ children: label
1240
+ }),
1241
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1242
+ type: "range",
1243
+ className: GlassRow_module_css_default.slider,
1244
+ min,
1245
+ max,
1246
+ step,
1247
+ value,
1248
+ onChange: (e) => {
1249
+ onChange(clamp(Number(e.target.value)));
1250
+ }
1251
+ }),
1252
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1253
+ className: GlassRow_module_css_default.numberWrap,
1254
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1255
+ type: "number",
1256
+ className: GlassRow_module_css_default.number,
1257
+ min,
1258
+ max,
1259
+ step,
1260
+ value,
1261
+ onChange: (e) => {
1262
+ onChange(clamp(Number(e.target.value)));
1263
+ }
1264
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1265
+ className: GlassRow_module_css_default.unit,
1266
+ children: unit
1267
+ })]
1268
+ })
1269
+ ]
1270
+ });
1271
+ }
1272
+ /** A two-option segmented picker. */
1273
+ function Segmented(props) {
1274
+ const { label, value, options, onSelect } = props;
1275
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1276
+ className: GlassRow_module_css_default.segmented,
1277
+ role: "group",
1278
+ "aria-label": label,
1279
+ children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1280
+ type: "button",
1281
+ className: option.id === value ? GlassRow_module_css_default.segActive : GlassRow_module_css_default.seg,
1282
+ "aria-pressed": option.id === value,
1283
+ onClick: () => {
1284
+ onSelect(option.id);
1285
+ },
1286
+ children: option.label
1287
+ }, option.id))
1288
+ });
1289
+ }
1290
+ /**
1291
+ * Render the glass row: title, the master switch, and — while enabled — the
1292
+ * mode picker and every knob.
1293
+ * @param props - composed slot props.
1294
+ * @returns the General section row.
1295
+ */
1296
+ function GlassRow({ t, getState, subscribe, setEnabled, setMode, setBlur, setFrost, setBrightness, setHue }) {
1297
+ const { enabled, mode, blur, frost, brightness, hue, dark } = (0, react.useSyncExternalStore)(subscribe, getState);
1298
+ const bgMin = dark ? 0 : 50;
1299
+ const bgMax = dark ? 50 : 100;
1300
+ const bgDisplay = Math.min(bgMax, Math.max(bgMin, brightness));
1301
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1302
+ className: GlassRow_module_css_default.group,
1303
+ children: [
1304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1305
+ className: GlassRow_module_css_default.title,
1306
+ children: t("glass.title")
1307
+ }),
1308
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1309
+ className: GlassRow_module_css_default.description,
1310
+ children: t("glass.description")
1311
+ }),
1312
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1313
+ className: GlassRow_module_css_default.controls,
1314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1315
+ className: GlassRow_module_css_default.row,
1316
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1317
+ className: GlassRow_module_css_default.rowLabel,
1318
+ children: t("glass.enable")
1319
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1320
+ type: "button",
1321
+ className: enabled ? GlassRow_module_css_default.toggleOn : GlassRow_module_css_default.toggle,
1322
+ "aria-pressed": enabled,
1323
+ onClick: () => {
1324
+ setEnabled(!enabled);
1325
+ },
1326
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1327
+ className: GlassRow_module_css_default.check,
1328
+ children: enabled ? "✓" : ""
1329
+ }), enabled ? t("glass.on") : t("glass.off")]
1330
+ })]
1331
+ }), enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1333
+ className: GlassRow_module_css_default.row,
1334
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1335
+ className: GlassRow_module_css_default.rowLabel,
1336
+ children: t("glass.mode")
1337
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segmented, {
1338
+ label: t("glass.mode"),
1339
+ value: mode,
1340
+ options: [{
1341
+ id: "mica",
1342
+ label: t("glass.modeMica")
1343
+ }, {
1344
+ id: "compat",
1345
+ label: t("glass.modeCompat")
1346
+ }],
1347
+ onSelect: setMode
1348
+ })]
1349
+ }),
1350
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1351
+ className: GlassRow_module_css_default.rowHint,
1352
+ children: t("glass.modeHint")
1353
+ }),
1354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Knob, {
1355
+ label: t("glass.blur"),
1356
+ value: blur,
1357
+ min: 0,
1358
+ max: 40,
1359
+ step: .5,
1360
+ unit: "px",
1361
+ onChange: setBlur
1362
+ }),
1363
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Knob, {
1364
+ label: t("glass.frost"),
1365
+ value: frost,
1366
+ min: 0,
1367
+ max: 100,
1368
+ step: 1,
1369
+ unit: "%",
1370
+ onChange: setFrost
1371
+ }),
1372
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Knob, {
1373
+ label: t("glass.brightness"),
1374
+ value: bgDisplay,
1375
+ min: bgMin,
1376
+ max: bgMax,
1377
+ step: 1,
1378
+ unit: "%",
1379
+ onChange: setBrightness
1380
+ }),
1381
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1382
+ className: GlassRow_module_css_default.knobHint,
1383
+ children: t(dark ? "glass.brightnessHintDark" : "glass.brightnessHintLight")
1384
+ }),
1385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Knob, {
1386
+ label: t("glass.hue"),
1387
+ value: hue,
1388
+ min: 0,
1389
+ max: 360,
1390
+ step: 1,
1391
+ unit: "°",
1392
+ onChange: setHue
1393
+ }),
1394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1395
+ className: GlassRow_module_css_default.knobHint,
1396
+ children: t("glass.hueHint")
1397
+ })
1398
+ ] })]
1399
+ })
1400
+ ]
1401
+ });
1402
+ }
1403
+ //#endregion
1404
+ //#region \0dsh-css:D:\Vibe-Coding\dsh-catppuccin\src\client\glass\glass.module.css.mjs
1405
+ const css = "[data-dsh-glass] body{--dsh-glass-card:linear-gradient(180deg, color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(52% * var(--dsh-glass-frost,1)), transparent), color-mix(in srgb, var(--dsw-alias-bg-layer-2) calc(40% * var(--dsh-glass-frost,1)), transparent));background:var(--dsw-alias-bg-base)}[data-dsh-glass] [data-dsh-glass-frame],[data-dsh-glass] [data-phase],[data-dsh-glass] [data-dsh-glass-details]{background:0 0}[data-dsh-glass-float] [data-phase=active] header{z-index:8;position:relative}[data-dsh-glass-float] [class*=banner]{position:static}[data-dsh-glass-float] [data-phase=active] [data-conversation-scroll]{margin-top:-95px;padding-top:107px}[data-dsh-glass] [data-phase] [class*=composerSeat][class*=composerSeat]{background:0 0}[data-dsh-glass] [data-dsh-glass-ambient]{z-index:0;pointer-events:none;filter:hue-rotate(var(--dsh-glass-hue,0deg));background:radial-gradient(760px 420px at 50% -8%, color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 18%, transparent), transparent 70%), linear-gradient(180deg, color-mix(in srgb, var(--dsw-alias-bg-layer-1) 62%, transparent) 0%, color-mix(in srgb, var(--dsw-alias-bg-layer-2) 46%, transparent) 100%), radial-gradient(900px 420px at 50% 108%, color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent), transparent 70%);position:fixed;inset:0;overflow:hidden}[data-dsh-glass] [data-dsh-glass-ambient]:after{content:\"\";background-image:linear-gradient(rgba(255, 255, 255, var(--dsh-glass-brightness-white,0)), rgba(255, 255, 255, var(--dsh-glass-brightness-white,0))), linear-gradient(rgba(0, 0, 0, var(--dsh-glass-brightness-black,0)), rgba(0, 0, 0, var(--dsh-glass-brightness-black,0)));position:absolute;inset:0}@media (prefers-reduced-motion:no-preference){[data-dsh-glass] [data-dsh-glass-ambient]{animation:bOHSKq_dsh-glass-breathe 9s var(--ds-ease-in-out) infinite alternate}}@keyframes bOHSKq_dsh-glass-breathe{0%{opacity:.88}to{opacity:1}}[data-dsh-glass] [data-dsh-glass-blob]{filter:blur(70px);opacity:.42;will-change:transform;border-radius:50%;position:absolute}[data-dsh-glass] [data-dsh-glass-blob=\"1\"]{background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 15%, transparent);width:44vw;height:44vw;animation:bOHSKq_dsh-glass-drift-a 30s var(--ds-ease-in-out) infinite alternate;top:-8vw;left:-10vw}[data-dsh-glass] [data-dsh-glass-blob=\"2\"]{background:color-mix(in srgb, var(--dsw-alias-state-success-tertiary) 45%, transparent);width:38vw;height:38vw;animation:bOHSKq_dsh-glass-drift-b 36s var(--ds-ease-in-out) infinite alternate;top:24vh;right:-12vw}[data-dsh-glass] [data-dsh-glass-blob=\"3\"]{background:color-mix(in srgb, var(--dsw-alias-state-warn-tertiary) 40%, transparent);width:30vw;height:30vw;animation:bOHSKq_dsh-glass-drift-c 42s var(--ds-ease-in-out) infinite alternate;bottom:-14vh;left:30vw}@keyframes bOHSKq_dsh-glass-drift-a{0%{transform:translate(0,0)scale(1)}to{transform:translate(6vw,4vh)scale(1.12)}}@keyframes bOHSKq_dsh-glass-drift-b{0%{transform:translate(0,0)scale(1.08)}to{transform:translate(-7vw,6vh)scale(.96)}}@keyframes bOHSKq_dsh-glass-drift-c{0%{transform:translate(0,0)scale(.96)}to{transform:translate(8vw,-5vh)scale(1.1)}}[data-dsh-glass] [data-dsh-glass-fade]{z-index:7;pointer-events:none;backdrop-filter:blur(5px);background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) 50%, transparent);height:13px;position:fixed;left:0;right:0}[data-dsh-glass] [data-dsh-glass-fade=top]{top:0;-webkit-mask-image:linear-gradient(#000 0%,#0000 100%);mask-image:linear-gradient(#000 0%,#0000 100%)}[data-dsh-glass] [data-dsh-glass-fade=bottom]{bottom:0;-webkit-mask-image:linear-gradient(#0000 0%,#000 100%);mask-image:linear-gradient(#0000 0%,#000 100%)}[data-dsh-glass-float] [role=menu],[data-dsh-glass-float] [role=dialog],[data-dsh-glass-float] [role=alert],[data-dsh-glass-float] [data-dsh-glass-surface]{border-radius:14px}[data-dsh-glass-float] [role=menuitem],[data-dsh-glass-float] [role=tooltip],[data-dsh-glass-float] [class*=pill]{border-radius:8px}[data-dsh-glass-float] button[class*=button]{border-radius:10px}[data-dsh-glass-float] [class*=iconButton],[data-dsh-glass-float] [class*=searchButton]{border-radius:8px}[data-dsh-glass-float] [data-dsh-glass-add]{background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(55% * var(--dsh-glass-frost,1)), transparent);border:1px solid var(--dsw-alias-border-l2);box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass-float] [data-dsh-glass-add]:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(72% * var(--dsh-glass-frost,1)), transparent)}[data-dsh-glass-float] [data-dsh-glass-surface]{background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(62% * var(--dsh-glass-frost,1)), transparent);border:1px solid var(--dsw-alias-border-l2);box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass-float] [data-dsh-glass-surface]:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(78% * var(--dsh-glass-frost,1)), transparent)}[data-dsh-glass-float] [class*=bubble]{border:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 80%, transparent);background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(46% * var(--dsh-glass-frost,1)), transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-radius:14px}[data-dsh-glass-float] [class*=card]{border-radius:14px}[data-dsh-glass-float] header{border:1px solid var(--dsw-alias-border-l2);background:var(--dsh-glass-card);box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent), 0 8px 28px color-mix(in srgb, var(--dsw-alias-bg-mask-2) 55%, transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-bottom-color:#0000;border-radius:20px;margin:12px 16px 0;padding:10px 16px 8px}[data-dsh-glass-float] header:after{display:none}[data-dsh-glass-float] [class*=sidebarCol]{--dsw-specific-sidebar-fill:transparent;z-index:9;border:1px solid var(--dsw-alias-border-l2);border-right-color:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 22%, transparent);background:var(--dsh-glass-card);box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent), 0 8px 28px color-mix(in srgb, var(--dsw-alias-bg-mask-2) 55%, transparent);border-radius:20px;margin:12px;padding:10px 12px 14px;position:relative;overflow:hidden}[data-dsh-glass-float] [data-dsh-glass-frame][data-sidebar-collapsed] [class*=sidebarCol]{transition:margin .15s var(--ds-ease-in-out), border-radius .15s var(--ds-ease-in-out);border-radius:16px;margin:12px -12px 12px 12px;padding:0}[data-dsh-glass-float] [data-dsh-glass-frame]:not([data-sidebar-collapsed]) [data-dsh-glass-sidebar-root]{width:100%!important}[data-dsh-glass-float] [data-dsh-glass-trajectory]{border:1px solid var(--dsw-alias-border-l2);background:var(--dsh-glass-card);width:calc(100% - 32px);height:calc(100% - 20px);box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent), 0 8px 28px color-mix(in srgb, var(--dsw-alias-bg-mask-2) 55%, transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-radius:20px;margin:8px 16px 12px;overflow:hidden}[data-dsh-glass-float] [data-dsh-glass-trajectory] [role=toolbar],[data-dsh-glass-float] [data-dsh-glass-trajectory] section[aria-label=Trajectory\\ timeline]{background:0 0}[data-dsh-glass-float] [data-composer-card],[data-dsh-glass-float] [data-composer-card]:after{border-radius:24px}[data-dsh-glass-float] [data-composer-card]{z-index:8;background:var(--dsh-glass-card);border:1px solid var(--dsw-alias-border-l2);box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent), 0 8px 32px color-mix(in srgb, var(--dsw-alias-bg-mask-2) 60%, transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px));position:relative}[data-dsh-glass-float] [data-dsh-glass-inputbar]:not([class*=hero]){padding-bottom:12px}[data-dsh-glass][data-dsh-glass-float] [data-dsh-glass-inputbar] [data-slot]{border:1px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-border-l1);background:linear-gradient(180deg, color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(30% * var(--dsh-glass-frost,1)), transparent), color-mix(in srgb, var(--dsw-alias-bg-layer-2) calc(18% * var(--dsh-glass-frost,1)), transparent));box-shadow:0 8px 32px color-mix(in srgb, var(--dsw-alias-bg-mask-2) 60%, transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-radius:0 0 24px 24px;margin:0 auto;width:var(--dsh-composer-card-max-width)!important;max-width:var(--dsh-composer-card-max-width)!important}[data-dsh-glass][data-dsh-glass-float] [data-dsh-glass-inputbar] [data-slot] [data-dsh-glass-stats]{min-width:0;box-shadow:none;backdrop-filter:none;background:0 0;border:none;border-radius:0;margin:0;width:auto!important;max-width:none!important;padding:4px 16px 0!important}[data-dsh-glass][data-dsh-glass-float] [data-dsh-glass-inputbar] [data-slot] [data-dsh-live-tps]{padding-right:16px}[data-dsh-glass-float] [data-dsh-glass-inputbar]:has([data-dsh-glass-stats]) [data-composer-card]{box-shadow:inset 0 1px 0 color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);border-bottom:none;border-radius:24px 24px 0 0}[data-dsh-glass-float] [role=treeitem][aria-selected=true]{box-shadow:inset 2px 0 0 var(--dsw-specific-sidebar-nav-item-active-accent), 0 0 16px color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent)}[data-dsh-glass-float] button[class*=button]:hover:not(:disabled),[data-dsh-glass-float] [role=menuitem]:hover:not(:disabled){box-shadow:0 0 12px color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent), inset 0 0 0 1px color-mix(in srgb, var(--dsw-alias-border-l3) 60%, transparent)}[data-dsh-glass-float] [role=menu]{background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(72% * var(--dsh-glass-frost,1)), transparent);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass-float] [data-phase=hero]{animation:bOHSKq_dsh-glass-in .32s var(--ds-ease-in-out)}[data-dsh-glass-float] [data-phase=active]{animation:bOHSKq_dsh-glass-in .3s var(--ds-ease-in-out)}[data-dsh-glass-float] [data-testid^=view-]{animation:bOHSKq_dsh-glass-in .26s var(--ds-ease-in-out)}[data-dsh-glass-float] [data-tool]{animation:bOHSKq_dsh-glass-rise .3s var(--ds-ease-in-out) both}[data-dsh-glass-float] [role=dialog]{animation:bOHSKq_dsh-glass-dialog-in .24s var(--ds-ease-in-out)}@keyframes bOHSKq_dsh-glass-in{0%{opacity:0}}@keyframes bOHSKq_dsh-glass-rise{0%{opacity:0;transform:translateY(6px)}}@keyframes bOHSKq_dsh-glass-dialog-in{0%{opacity:0;transform:translateY(8px)scale(.985)}}[data-dsh-glass] :focus-visible{outline:2px solid color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 85%, transparent);outline-offset:1px}[data-dsh-glass] ::selection{background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 35%, transparent)}[data-dsh-glass-compat] [role=menu],[data-dsh-glass-compat] [role=tooltip],[data-dsh-glass-compat] [class*=card],[data-dsh-glass-compat] [class*=bubble],[data-dsh-glass-compat] [class*=panel],[data-dsh-glass-compat] [class*=popover],[data-dsh-glass-compat] [class*=dropdown]{backdrop-filter:blur(12px)}@media (prefers-reduced-motion:reduce){[data-dsh-glass-float] [data-phase=hero],[data-dsh-glass-float] [data-phase=active],[data-dsh-glass-float] [data-testid^=view-],[data-dsh-glass-float] [data-tool],[data-dsh-glass-float] [role=dialog],[data-dsh-glass] [data-dsh-glass-ambient],[data-dsh-glass] [data-dsh-glass-blob]{animation:none}}";
1406
+ const tagId = "@nonamelego/dsh-catppuccin/glass.module.css";
1407
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1408
+ const tag = document.createElement("style");
1409
+ tag.dataset.plugin = "@nonamelego/dsh-catppuccin";
1410
+ tag.dataset.pluginCss = tagId;
1411
+ tag.textContent = css;
1412
+ document.head.appendChild(tag);
1413
+ }
1414
+ //#endregion
817
1415
  //#region src/client/index.ts
818
1416
  /** Locale namespace owned by this plugin. */
819
1417
  const NS = "catppuccin";
820
- /** The settings namespace string (must match the host half's settingsNamespace). */
821
- const SETTINGS_NAMESPACE = "dsh-catppuccin";
1418
+ /** localStorage key carrying the persisted flavour choice. */
1419
+ const FLAVOR_STORAGE_KEY = "dsh.catppuccin.flavor";
1420
+ /** Accepted flavour values — the four registered theme ids plus `off`. These
1421
+ * MUST stay in sync with the registered themes (guarded by
1422
+ * tests/palettes.spec.ts) because the persisted value is the theme id. */
1423
+ const CATPPUCCIN_FLAVOR_VALUES = [...CATPPUCCIN_FLAVORS.map((f) => f.themeId), "off"];
822
1424
  /** The flavour whose themeId this is, or `off`. */
823
1425
  function flavorFromThemeId(themeId) {
824
1426
  return CATPPUCCIN_FLAVORS.some((f) => f.themeId === themeId) ? themeId : "off";
@@ -827,14 +1429,26 @@ window.__ModuleLoader__.load({
827
1429
  function flavorInfo(themeId) {
828
1430
  return CATPPUCCIN_FLAVORS.find((f) => f.themeId === themeId);
829
1431
  }
830
- /** Required services: slots + locale (settings row) and theme (register + switch). */
1432
+ /** Read the persisted flavour (absent / unknown values mean the default `off`). */
1433
+ function readFlavor() {
1434
+ try {
1435
+ const raw = localStorage.getItem(FLAVOR_STORAGE_KEY);
1436
+ return raw !== null && CATPPUCCIN_FLAVOR_VALUES.includes(raw) ? raw : "off";
1437
+ } catch {
1438
+ return "off";
1439
+ }
1440
+ }
1441
+ /** Persist the flavour choice (storage failures keep the in-memory state). */
1442
+ function writeFlavor(choice) {
1443
+ try {
1444
+ localStorage.setItem(FLAVOR_STORAGE_KEY, choice);
1445
+ } catch {}
1446
+ }
1447
+ /** Required services: slots + locale (settings rows) and theme (register + switch). */
831
1448
  const inject = [
832
1449
  "slots",
833
1450
  "locale",
834
- "theme",
835
- "settingsScope",
836
- "connection",
837
- "remote"
1451
+ "theme"
838
1452
  ];
839
1453
  /**
840
1454
  * Register the Catppuccin dictionaries, the four flavour themes, and the
@@ -857,23 +1471,38 @@ window.__ModuleLoader__.load({
857
1471
  for (const dispose of disposers) dispose();
858
1472
  };
859
1473
  }, "catppuccin: flavour themes");
860
- const scope = ctx.get("settingsScope").bind({ namespace: SETTINGS_NAMESPACE });
861
1474
  ctx.effect(() => {
862
- let restored = false;
863
- const restoreOnce = () => {
864
- if (restored) return;
865
- const snapshot = scope.getSnapshot();
866
- if (snapshot.status !== "ready") return;
867
- restored = true;
868
- const saved = snapshot.value?.flavor;
869
- if (saved !== void 0 && saved !== "off") try {
870
- theme.setTheme(saved);
1475
+ const saved = readFlavor();
1476
+ if (saved === "off") return () => {};
1477
+ let settled = false;
1478
+ const started = Date.now();
1479
+ const applySaved = () => {
1480
+ if (settled) return;
1481
+ if (Date.now() - started > 5e3) {
1482
+ settled = true;
1483
+ return;
1484
+ }
1485
+ try {
1486
+ if (theme.getTheme().preference !== saved) theme.setTheme(saved);
1487
+ } catch {}
1488
+ };
1489
+ applySaved();
1490
+ const disposer = ctx.on("theme/change", applySaved);
1491
+ const timer = window.setTimeout(() => {
1492
+ settled = true;
1493
+ }, 5e3);
1494
+ const onStorage = (event) => {
1495
+ if (event.key !== "dsh.catppuccin.flavor") return;
1496
+ const next = readFlavor();
1497
+ if (next !== "off") try {
1498
+ theme.setTheme(next);
871
1499
  } catch {}
872
1500
  };
873
- restoreOnce();
874
- const unsubscribe = scope.subscribe(restoreOnce);
1501
+ window.addEventListener("storage", onStorage);
875
1502
  return () => {
876
- unsubscribe();
1503
+ disposer();
1504
+ window.clearTimeout(timer);
1505
+ window.removeEventListener("storage", onStorage);
877
1506
  };
878
1507
  }, "catppuccin: boot restore");
879
1508
  const injected = () => ({
@@ -887,20 +1516,16 @@ window.__ModuleLoader__.load({
887
1516
  return flavorFromThemeId(pref);
888
1517
  },
889
1518
  subscribe: (listener) => ctx.on("theme/change", listener),
890
- select: async (choice) => {
1519
+ select: (choice) => {
891
1520
  if (choice === "off") {
892
1521
  theme.setTheme("system");
893
- await scope.set("flavor", "off").catch((err) => {
894
- console.error("[dsh-catppuccin] persist off failed:", err);
895
- });
1522
+ writeFlavor("off");
896
1523
  return;
897
1524
  }
898
1525
  const flavor = flavorInfo(choice);
899
1526
  if (!flavor) return;
900
1527
  theme.setTheme(flavor.themeId);
901
- await scope.set("flavor", flavor.themeId).catch((err) => {
902
- console.error("[dsh-catppuccin] persist flavor failed:", err);
903
- });
1528
+ writeFlavor(flavor.themeId);
904
1529
  }
905
1530
  });
906
1531
  ctx.slots.inject("settings.general.item", () => ctx.slots.register({
@@ -910,14 +1535,47 @@ window.__ModuleLoader__.load({
910
1535
  locale: NS,
911
1536
  inject: injected
912
1537
  }, CatppuccinRow));
1538
+ const glass = new GlassLayer(ctx);
1539
+ const glassInjected = () => ({
1540
+ getState: () => glass.getSnapshot(),
1541
+ subscribe: (listener) => glass.subscribe(listener),
1542
+ setEnabled: (enabled) => {
1543
+ glass.setEnabled(enabled);
1544
+ },
1545
+ setMode: (mode) => {
1546
+ glass.setMode(mode);
1547
+ },
1548
+ setBlur: (blur) => {
1549
+ glass.setBlur(blur);
1550
+ },
1551
+ setFrost: (frost) => {
1552
+ glass.setFrost(frost);
1553
+ },
1554
+ setBrightness: (brightness) => {
1555
+ glass.setBrightness(brightness);
1556
+ },
1557
+ setHue: (hue) => {
1558
+ glass.setHue(hue);
1559
+ }
1560
+ });
1561
+ ctx.slots.inject("settings.general.item", () => ctx.slots.register({
1562
+ name: "settings.general.item",
1563
+ id: "catppuccin-glass",
1564
+ order: 21,
1565
+ locale: NS,
1566
+ inject: glassInjected
1567
+ }, GlassRow));
913
1568
  }
914
1569
  //#endregion
1570
+ exports.CATPPUCCIN_FLAVOR_VALUES = CATPPUCCIN_FLAVOR_VALUES;
1571
+ exports.FLAVOR_STORAGE_KEY = FLAVOR_STORAGE_KEY;
915
1572
  exports.NS = NS;
916
- exports.SETTINGS_NAMESPACE = SETTINGS_NAMESPACE;
917
1573
  exports.apply = apply;
918
1574
  exports.flavorFromThemeId = flavorFromThemeId;
919
1575
  exports.flavorInfo = flavorInfo;
920
1576
  exports.inject = inject;
1577
+ exports.readFlavor = readFlavor;
1578
+ exports.writeFlavor = writeFlavor;
921
1579
  return module.exports;
922
1580
  }
923
1581
  });