@anionex/dsh-eye-care 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +86 -0
  4. package/README.zh.md +86 -0
  5. package/assets/eye-care-dark.png +0 -0
  6. package/assets/eye-care-light.png +0 -0
  7. package/cordis.patch.yml +3 -0
  8. package/lib/client.js +1106 -0
  9. package/lib/client.js.map +1 -0
  10. package/lib/index.js +14 -0
  11. package/lib/index.js.map +1 -0
  12. package/lib/rpc.js +123 -0
  13. package/lib/rpc.js.map +1 -0
  14. package/lib/settings.js +12 -0
  15. package/lib/settings.js.map +1 -0
  16. package/lib/shared.js +70 -0
  17. package/lib/shared.js.map +1 -0
  18. package/lib/types/client/EyeCareRow.d.ts +20 -0
  19. package/lib/types/client/EyeCareRow.d.ts.map +1 -0
  20. package/lib/types/client/controller.d.ts +96 -0
  21. package/lib/types/client/controller.d.ts.map +1 -0
  22. package/lib/types/client/index.d.ts +23 -0
  23. package/lib/types/client/index.d.ts.map +1 -0
  24. package/lib/types/client/locales.d.ts +54 -0
  25. package/lib/types/client/locales.d.ts.map +1 -0
  26. package/lib/types/client/themes.d.ts +14 -0
  27. package/lib/types/client/themes.d.ts.map +1 -0
  28. package/lib/types/index.d.ts +10 -0
  29. package/lib/types/index.d.ts.map +1 -0
  30. package/lib/types/rpc.d.ts +17 -0
  31. package/lib/types/rpc.d.ts.map +1 -0
  32. package/lib/types/settings.d.ts +8 -0
  33. package/lib/types/settings.d.ts.map +1 -0
  34. package/lib/types/shared.d.ts +59 -0
  35. package/lib/types/shared.d.ts.map +1 -0
  36. package/package.json +99 -0
  37. package/src/client/EyeCareRow.module.css +137 -0
  38. package/src/client/EyeCareRow.tsx +122 -0
  39. package/src/client/controller.ts +435 -0
  40. package/src/client/css-modules.d.ts +5 -0
  41. package/src/client/index.tsx +72 -0
  42. package/src/client/locales.ts +56 -0
  43. package/src/client/themes.ts +254 -0
  44. package/src/index.ts +30 -0
  45. package/src/rpc.ts +138 -0
  46. package/src/settings.ts +22 -0
  47. package/src/shared.ts +112 -0
package/lib/client.js ADDED
@@ -0,0 +1,1106 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@anionex/dsh-eye-care",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
9
+ //#region \0dsh-eye-care-css:/Users/davidyang/.codex/worktrees/dsh-eye-care-anionex/src/client/EyeCareRow.module.css.mjs
10
+ const css = ".akRDxW_row{border-bottom:1px solid var(--dsw-alias-border-l2);grid-template-columns:minmax(170px,.8fr) minmax(360px,1.2fr);gap:20px;padding:16px 0;display:grid}.akRDxW_copy{min-width:0}.akRDxW_title{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;font-weight:500;line-height:22px}.akRDxW_description{color:var(--dsw-alias-label-tertiary);margin:4px 0 0;font-size:12px;line-height:18px}.akRDxW_controls{align-content:start;gap:8px;display:grid}.akRDxW_controlGroup{flex-wrap:wrap;gap:6px;display:flex}.akRDxW_option,.akRDxW_smallOption{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer}.akRDxW_option:focus-visible,.akRDxW_smallOption:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill);outline-offset:2px}.akRDxW_option{border-radius:9px;min-width:68px;padding:7px 11px;font-size:12px;line-height:18px}.akRDxW_smallOption{border-radius:7px;padding:4px 9px;font-size:11px;line-height:16px}.akRDxW_option:hover:not(:disabled),.akRDxW_smallOption:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.akRDxW_selected{border-color:var(--dsw-alias-button-info-fill);background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary)}.akRDxW_option:disabled,.akRDxW_smallOption:disabled{cursor:wait;opacity:.65}.akRDxW_primaryLine,.akRDxW_settingLine{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.akRDxW_settingLabel,.akRDxW_status{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.akRDxW_settingLabel{min-width:56px}.akRDxW_advanced{border:1px solid var(--dsw-alias-border-l2);border-radius:9px;overflow:hidden}.akRDxW_advanced>summary{color:var(--dsw-alias-label-secondary);cursor:pointer;padding:8px 10px;font-size:11px;line-height:16px}.akRDxW_advancedBody{gap:8px;padding:0 10px 10px;display:grid}.akRDxW_status{min-height:16px}@media (width<=640px){.akRDxW_row{grid-template-columns:1fr;gap:10px}}";
11
+ const tagId = "@anionex/dsh-eye-care/EyeCareRow.module.css";
12
+ if (typeof document !== "undefined" && document.querySelector(`style[data-plugin-css=${JSON.stringify(tagId)}]`) === null) {
13
+ const tag = document.createElement("style");
14
+ tag.dataset.plugin = "@anionex/dsh-eye-care";
15
+ tag.dataset.pluginCss = tagId;
16
+ tag.textContent = css;
17
+ document.head.appendChild(tag);
18
+ }
19
+ var EyeCareRow_module_css_default = {
20
+ "advancedBody": "akRDxW_advancedBody",
21
+ "controls": "akRDxW_controls",
22
+ "controlGroup": "akRDxW_controlGroup",
23
+ "copy": "akRDxW_copy",
24
+ "description": "akRDxW_description",
25
+ "smallOption": "akRDxW_smallOption",
26
+ "settingLine": "akRDxW_settingLine",
27
+ "option": "akRDxW_option",
28
+ "advanced": "akRDxW_advanced",
29
+ "selected": "akRDxW_selected",
30
+ "row": "akRDxW_row",
31
+ "title": "akRDxW_title",
32
+ "primaryLine": "akRDxW_primaryLine",
33
+ "status": "akRDxW_status",
34
+ "settingLabel": "akRDxW_settingLabel"
35
+ };
36
+ //#endregion
37
+ //#region src/client/EyeCareRow.tsx
38
+ const MODES = [
39
+ {
40
+ id: "off",
41
+ label: "mode.off"
42
+ },
43
+ {
44
+ id: "auto",
45
+ label: "mode.auto"
46
+ },
47
+ {
48
+ id: "light",
49
+ label: "mode.light"
50
+ },
51
+ {
52
+ id: "dark",
53
+ label: "mode.dark"
54
+ }
55
+ ];
56
+ const INTENSITIES = [
57
+ {
58
+ id: "soft",
59
+ label: "intensity.soft"
60
+ },
61
+ {
62
+ id: "balanced",
63
+ label: "intensity.balanced"
64
+ },
65
+ {
66
+ id: "warm",
67
+ label: "intensity.warm"
68
+ }
69
+ ];
70
+ function statusKey(mode) {
71
+ return `status.${mode}`;
72
+ }
73
+ /** Render the feature-owned General settings row. */
74
+ function EyeCareRow({ t, useEyeCare, setMode, setIntensity }) {
75
+ const state = useEyeCare((value) => value);
76
+ const busy = state.status === "loading" || state.status === "saving";
77
+ const enabled = state.settings.mode !== "off";
78
+ const status = state.status === "error" && state.error !== null ? state.error : t(statusKey(state.settings.mode));
79
+ const statusText = state.status === "unavailable" ? `${status} · ${t("status.local")}` : status;
80
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
81
+ className: EyeCareRow_module_css_default.row,
82
+ "aria-labelledby": "dsh-eye-care-title",
83
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
84
+ className: EyeCareRow_module_css_default.copy,
85
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
86
+ id: "dsh-eye-care-title",
87
+ className: EyeCareRow_module_css_default.title,
88
+ children: t("title")
89
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
90
+ className: EyeCareRow_module_css_default.description,
91
+ children: t("description")
92
+ })]
93
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
94
+ className: EyeCareRow_module_css_default.controls,
95
+ children: [
96
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
97
+ className: EyeCareRow_module_css_default.primaryLine,
98
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
99
+ className: EyeCareRow_module_css_default.controlGroup,
100
+ role: "group",
101
+ "aria-label": t("enabled.title"),
102
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
103
+ type: "button",
104
+ className: `${EyeCareRow_module_css_default.option}${!enabled ? ` ${EyeCareRow_module_css_default.selected}` : ""}`,
105
+ "aria-pressed": !enabled,
106
+ disabled: busy,
107
+ onClick: () => {
108
+ setMode("off");
109
+ },
110
+ children: t("enabled.off")
111
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
112
+ type: "button",
113
+ className: `${EyeCareRow_module_css_default.option}${enabled ? ` ${EyeCareRow_module_css_default.selected}` : ""}`,
114
+ "aria-pressed": enabled,
115
+ disabled: busy,
116
+ onClick: () => {
117
+ setMode(state.settings.mode === "off" ? "auto" : state.settings.mode);
118
+ },
119
+ children: t("enabled.on")
120
+ })]
121
+ })
122
+ }),
123
+ enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
124
+ className: EyeCareRow_module_css_default.advanced,
125
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advanced.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
126
+ className: EyeCareRow_module_css_default.advancedBody,
127
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
128
+ className: EyeCareRow_module_css_default.settingLine,
129
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
130
+ className: EyeCareRow_module_css_default.settingLabel,
131
+ children: t("mode.title")
132
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
133
+ className: EyeCareRow_module_css_default.controlGroup,
134
+ role: "group",
135
+ "aria-label": t("mode.title"),
136
+ children: MODES.filter(({ id }) => id !== "off").map(({ id, label }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
137
+ type: "button",
138
+ className: `${EyeCareRow_module_css_default.smallOption}${state.settings.mode === id ? ` ${EyeCareRow_module_css_default.selected}` : ""}`,
139
+ "aria-pressed": state.settings.mode === id,
140
+ disabled: busy,
141
+ onClick: () => {
142
+ setMode(id);
143
+ },
144
+ children: t(label)
145
+ }, id))
146
+ })]
147
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
148
+ className: EyeCareRow_module_css_default.settingLine,
149
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
150
+ className: EyeCareRow_module_css_default.settingLabel,
151
+ children: t("intensity.title")
152
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
153
+ className: EyeCareRow_module_css_default.controlGroup,
154
+ role: "group",
155
+ "aria-label": t("intensity.title"),
156
+ children: INTENSITIES.map(({ id, label }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
157
+ type: "button",
158
+ className: `${EyeCareRow_module_css_default.smallOption}${state.settings.intensity === id ? ` ${EyeCareRow_module_css_default.selected}` : ""}`,
159
+ "aria-pressed": state.settings.intensity === id,
160
+ disabled: busy,
161
+ onClick: () => {
162
+ setIntensity(id);
163
+ },
164
+ children: t(label)
165
+ }, id))
166
+ })]
167
+ })]
168
+ })]
169
+ }) : null,
170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
171
+ className: EyeCareRow_module_css_default.status,
172
+ "aria-live": "polite",
173
+ children: state.status === "saving" ? t("status.saving") : statusText
174
+ })
175
+ ]
176
+ })]
177
+ });
178
+ }
179
+ //#endregion
180
+ //#region src/shared.ts
181
+ /** Browser-safe eye-care settings and RPC wire values. */
182
+ /** User-selectable eye-care operating modes. */
183
+ const EYE_CARE_MODES = [
184
+ "off",
185
+ "auto",
186
+ "light",
187
+ "dark"
188
+ ];
189
+ /** Warmth levels shared by light and dark palettes. */
190
+ const EYE_CARE_INTENSITIES = [
191
+ "soft",
192
+ "balanced",
193
+ "warm"
194
+ ];
195
+ /** Loopback-only Connection RPC channel owned by this package. */
196
+ const EYE_CARE_RPC_CHANNEL = "/eye-care";
197
+ /** RPC endpoint that reads the current settings snapshot. */
198
+ const EYE_CARE_RPC_READ = "settings.read";
199
+ /** RPC endpoint that replaces the settings section. */
200
+ const EYE_CARE_RPC_SAVE = "settings.save";
201
+ /** Settings field carrying the selected mode. */
202
+ const MODE_FIELD = "mode";
203
+ /** Settings field carrying the selected warmth. */
204
+ const INTENSITY_FIELD = "intensity";
205
+ /** Defaults used when no user section exists. */
206
+ const DEFAULT_EYE_CARE_SETTINGS = Object.freeze({
207
+ mode: "off",
208
+ intensity: "balanced"
209
+ });
210
+ /** Narrow an unknown value to one supported mode. */
211
+ function isEyeCareMode(value) {
212
+ return EYE_CARE_MODES.some((mode) => mode === value);
213
+ }
214
+ /** Narrow an unknown value to one supported warmth. */
215
+ function isEyeCareIntensity(value) {
216
+ return EYE_CARE_INTENSITIES.some((intensity) => intensity === value);
217
+ }
218
+ /** Decode a complete settings value from the RPC boundary. */
219
+ function decodeEyeCareSettings(value) {
220
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
221
+ const record = value;
222
+ if (!isEyeCareMode(record["mode"]) || !isEyeCareIntensity(record["intensity"])) return void 0;
223
+ return {
224
+ mode: record[MODE_FIELD],
225
+ intensity: record[INTENSITY_FIELD]
226
+ };
227
+ }
228
+ /** Decode one complete Host snapshot from the RPC boundary. */
229
+ function decodeEyeCareSnapshot(value) {
230
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
231
+ const record = value;
232
+ const settingsEnvelope = record["settings"];
233
+ if (record["schemaVersion"] !== 1 || typeof record["writable"] !== "boolean" || typeof settingsEnvelope !== "object" || settingsEnvelope === null || Array.isArray(settingsEnvelope)) return void 0;
234
+ const settingsRecord = settingsEnvelope;
235
+ const settings = decodeEyeCareSettings(settingsRecord["value"]);
236
+ const revision = settingsRecord["revision"];
237
+ if (settings === void 0 || !Number.isSafeInteger(revision) || revision < 0 || settingsRecord["applies"] !== "live") return void 0;
238
+ const baseThemePreference = record["baseThemePreference"];
239
+ if (baseThemePreference !== void 0 && baseThemePreference !== "light" && baseThemePreference !== "dark" && baseThemePreference !== "system") return void 0;
240
+ return {
241
+ schemaVersion: 1,
242
+ writable: record["writable"],
243
+ settings: {
244
+ value: settings,
245
+ revision,
246
+ applies: "live"
247
+ },
248
+ ...baseThemePreference === void 0 ? {} : { baseThemePreference }
249
+ };
250
+ }
251
+ //#endregion
252
+ //#region src/client/themes.ts
253
+ /** Theme id prefix reserved by this package. */
254
+ const EYE_CARE_THEME_PREFIX = "eye-care-";
255
+ const LIGHT = {
256
+ soft: {
257
+ bgBase: "#fbf8ef",
258
+ bgLayer1: "#fffdf6",
259
+ bgLayer2: "#f8f3e8",
260
+ bgLayer3: "#f2ecde",
261
+ bgModule: "#f7f2e7",
262
+ bgOverlay: "#ece4d5",
263
+ textPrimary: "#29271f",
264
+ textSecondary: "#575247",
265
+ textTertiary: "#716a5d",
266
+ textCaption: "#958b7a",
267
+ border1: "rgba(85, 72, 46, 0.08)",
268
+ border2: "rgba(85, 72, 46, 0.15)",
269
+ border3: "rgba(85, 72, 46, 0.21)",
270
+ hover: "rgba(126, 101, 53, 0.08)",
271
+ hoverSolid: "#f1eadc",
272
+ active: "rgba(126, 101, 53, 0.14)",
273
+ primaryFill: "#44583d",
274
+ primaryHover: "#536a49",
275
+ primaryDimmed: "#dfe6d9",
276
+ bubble: "#eef3df",
277
+ bubbleHighlight: "#e0e9cb",
278
+ input: "#fffdf6",
279
+ selector: "#f2ecde",
280
+ sidebar: "#f4efe3",
281
+ sidebarHover: "#ebe4d5",
282
+ sidebarActive: "#e2dac9",
283
+ sidebarAccent: "#d6e0c1",
284
+ code: "#f2eee4",
285
+ codeBanner: "#ebe5d8",
286
+ inlineCode: "#eee8db",
287
+ scrollbar: "#d1c6b4",
288
+ scrollbarHover: "#baad98",
289
+ shiki: {
290
+ constant: "#176f88",
291
+ string: "#34783c",
292
+ comment: "#817768",
293
+ keyword: "#a03e58",
294
+ parameter: "#a15b1f",
295
+ function: "#6950a6",
296
+ stringExpression: "#2f7040",
297
+ punctuation: "#5a554d",
298
+ link: "#246d9a"
299
+ }
300
+ },
301
+ balanced: {
302
+ bgBase: "#faf5e8",
303
+ bgLayer1: "#fffaf0",
304
+ bgLayer2: "#f5eddd",
305
+ bgLayer3: "#eee3d0",
306
+ bgModule: "#f3ead9",
307
+ bgOverlay: "#e7dbc7",
308
+ textPrimary: "#2b281f",
309
+ textSecondary: "#5b5345",
310
+ textTertiary: "#766d5b",
311
+ textCaption: "#9b8e77",
312
+ border1: "rgba(93, 72, 36, 0.09)",
313
+ border2: "rgba(93, 72, 36, 0.17)",
314
+ border3: "rgba(93, 72, 36, 0.24)",
315
+ hover: "rgba(139, 100, 37, 0.09)",
316
+ hoverSolid: "#eee3d1",
317
+ active: "rgba(139, 100, 37, 0.16)",
318
+ primaryFill: "#425b3c",
319
+ primaryHover: "#52704a",
320
+ primaryDimmed: "#dce7d3",
321
+ bubble: "#eaf1d5",
322
+ bubbleHighlight: "#dce7bc",
323
+ input: "#fffaf0",
324
+ selector: "#eee3d0",
325
+ sidebar: "#f1e8d7",
326
+ sidebarHover: "#e7dcc8",
327
+ sidebarActive: "#ddd0b9",
328
+ sidebarAccent: "#d2dfb3",
329
+ code: "#efe9dc",
330
+ codeBanner: "#e6dece",
331
+ inlineCode: "#ebe2d1",
332
+ scrollbar: "#ccbda6",
333
+ scrollbarHover: "#b3a188",
334
+ shiki: {
335
+ constant: "#146d87",
336
+ string: "#39753b",
337
+ comment: "#837664",
338
+ keyword: "#a13e57",
339
+ parameter: "#a1581c",
340
+ function: "#684ba3",
341
+ stringExpression: "#34703d",
342
+ punctuation: "#5d5549",
343
+ link: "#206c98"
344
+ }
345
+ },
346
+ warm: {
347
+ bgBase: "#f8efda",
348
+ bgLayer1: "#fff7e7",
349
+ bgLayer2: "#f2e6d0",
350
+ bgLayer3: "#eadcc4",
351
+ bgModule: "#f0e3cc",
352
+ bgOverlay: "#e1d0b5",
353
+ textPrimary: "#30291f",
354
+ textSecondary: "#615343",
355
+ textTertiary: "#7c6a56",
356
+ textCaption: "#a08b70",
357
+ border1: "rgba(105, 73, 30, 0.10)",
358
+ border2: "rgba(105, 73, 30, 0.19)",
359
+ border3: "rgba(105, 73, 30, 0.27)",
360
+ hover: "rgba(151, 97, 27, 0.10)",
361
+ hoverSolid: "#eadbc2",
362
+ active: "rgba(151, 97, 27, 0.18)",
363
+ primaryFill: "#3f5c39",
364
+ primaryHover: "#506f46",
365
+ primaryDimmed: "#d8e5ce",
366
+ bubble: "#e6efc9",
367
+ bubbleHighlight: "#d6e3ac",
368
+ input: "#fff7e7",
369
+ selector: "#eadcc4",
370
+ sidebar: "#eee1ca",
371
+ sidebarHover: "#e3d3b9",
372
+ sidebarActive: "#d7c4a7",
373
+ sidebarAccent: "#cedeaa",
374
+ code: "#ece2d1",
375
+ codeBanner: "#e2d5c0",
376
+ inlineCode: "#e7dac4",
377
+ scrollbar: "#c7b398",
378
+ scrollbarHover: "#ad9779",
379
+ shiki: {
380
+ constant: "#116b83",
381
+ string: "#3e7439",
382
+ comment: "#85735e",
383
+ keyword: "#a33b54",
384
+ parameter: "#a25418",
385
+ function: "#67459e",
386
+ stringExpression: "#386d39",
387
+ punctuation: "#625344",
388
+ link: "#1c6b94"
389
+ }
390
+ }
391
+ };
392
+ const DARK = {
393
+ soft: {
394
+ bgBase: "#20211c",
395
+ bgLayer1: "#25261f",
396
+ bgLayer2: "#2a2b23",
397
+ bgLayer3: "#313128",
398
+ bgModule: "#2d2e26",
399
+ bgOverlay: "#3a392e",
400
+ textPrimary: "#ece9dc",
401
+ textSecondary: "#c7c1b0",
402
+ textTertiary: "#aaa28f",
403
+ textCaption: "#7f786a",
404
+ border1: "rgba(245, 233, 201, 0.07)",
405
+ border2: "rgba(245, 233, 201, 0.13)",
406
+ border3: "rgba(245, 233, 201, 0.19)",
407
+ hover: "rgba(244, 227, 182, 0.08)",
408
+ hoverSolid: "#36362c",
409
+ active: "rgba(244, 227, 182, 0.14)",
410
+ primaryFill: "#dfe9cf",
411
+ primaryHover: "#f1f5e7",
412
+ primaryDimmed: "#4d5143",
413
+ bubble: "#30372a",
414
+ bubbleHighlight: "#3d4933",
415
+ input: "#2b2d25",
416
+ selector: "#37382e",
417
+ sidebar: "#252820",
418
+ sidebarHover: "#30342a",
419
+ sidebarActive: "#3a3e32",
420
+ sidebarAccent: "#3f4b35",
421
+ code: "#1c1e1b",
422
+ codeBanner: "#252821",
423
+ inlineCode: "#292c24",
424
+ scrollbar: "#57584d",
425
+ scrollbarHover: "#707064",
426
+ shiki: {
427
+ constant: "#68b7d2",
428
+ string: "#91c88b",
429
+ comment: "#9d988d",
430
+ keyword: "#e39aaf",
431
+ parameter: "#e6ad72",
432
+ function: "#b9a5df",
433
+ stringExpression: "#9bcf91",
434
+ punctuation: "#c7c2b7",
435
+ link: "#77b8d9"
436
+ }
437
+ },
438
+ balanced: {
439
+ bgBase: "#211f19",
440
+ bgLayer1: "#27231c",
441
+ bgLayer2: "#2d2921",
442
+ bgLayer3: "#353027",
443
+ bgModule: "#302b22",
444
+ bgOverlay: "#40382b",
445
+ textPrimary: "#efe7d6",
446
+ textSecondary: "#cbc0aa",
447
+ textTertiary: "#aea087",
448
+ textCaption: "#837662",
449
+ border1: "rgba(250, 230, 190, 0.08)",
450
+ border2: "rgba(250, 230, 190, 0.15)",
451
+ border3: "rgba(250, 230, 190, 0.21)",
452
+ hover: "rgba(247, 216, 157, 0.09)",
453
+ hoverSolid: "#3b352b",
454
+ active: "rgba(247, 216, 157, 0.16)",
455
+ primaryFill: "#e0ebcf",
456
+ primaryHover: "#f2f7e6",
457
+ primaryDimmed: "#505243",
458
+ bubble: "#32392a",
459
+ bubbleHighlight: "#404c32",
460
+ input: "#302b23",
461
+ selector: "#3b352a",
462
+ sidebar: "#282820",
463
+ sidebarHover: "#33342a",
464
+ sidebarActive: "#3d3e32",
465
+ sidebarAccent: "#435036",
466
+ code: "#1d1d19",
467
+ codeBanner: "#28251f",
468
+ inlineCode: "#2d2922",
469
+ scrollbar: "#5b584b",
470
+ scrollbarHover: "#757062",
471
+ shiki: {
472
+ constant: "#69b8d0",
473
+ string: "#94c985",
474
+ comment: "#a19a8d",
475
+ keyword: "#e49aad",
476
+ parameter: "#e7aa6c",
477
+ function: "#bca2df",
478
+ stringExpression: "#9dcc89",
479
+ punctuation: "#cbc2b2",
480
+ link: "#79b9d5"
481
+ }
482
+ },
483
+ warm: {
484
+ bgBase: "#231e17",
485
+ bgLayer1: "#2a231b",
486
+ bgLayer2: "#31291f",
487
+ bgLayer3: "#3a3025",
488
+ bgModule: "#342b21",
489
+ bgOverlay: "#46392b",
490
+ textPrimary: "#f1e4cf",
491
+ textSecondary: "#cdbb9f",
492
+ textTertiary: "#b09a7e",
493
+ textCaption: "#857058",
494
+ border1: "rgba(255, 225, 177, 0.09)",
495
+ border2: "rgba(255, 225, 177, 0.17)",
496
+ border3: "rgba(255, 225, 177, 0.24)",
497
+ hover: "rgba(252, 207, 136, 0.10)",
498
+ hoverSolid: "#403529",
499
+ active: "rgba(252, 207, 136, 0.18)",
500
+ primaryFill: "#e1ebcc",
501
+ primaryHover: "#f3f7e4",
502
+ primaryDimmed: "#535143",
503
+ bubble: "#353a28",
504
+ bubbleHighlight: "#444d30",
505
+ input: "#342a20",
506
+ selector: "#403428",
507
+ sidebar: "#2b271e",
508
+ sidebarHover: "#363127",
509
+ sidebarActive: "#413a2e",
510
+ sidebarAccent: "#485235",
511
+ code: "#1f1b17",
512
+ codeBanner: "#2b241d",
513
+ inlineCode: "#31281f",
514
+ scrollbar: "#605548",
515
+ scrollbarHover: "#7a6b5b",
516
+ shiki: {
517
+ constant: "#6ab9ce",
518
+ string: "#98c87e",
519
+ comment: "#a59a89",
520
+ keyword: "#e696a8",
521
+ parameter: "#e8a765",
522
+ function: "#bea0dc",
523
+ stringExpression: "#a0cb82",
524
+ punctuation: "#cec0ad",
525
+ link: "#7bb9d1"
526
+ }
527
+ }
528
+ };
529
+ /** Build the stable id for one concrete eye-care theme. */
530
+ function eyeCareThemeId(scheme, intensity) {
531
+ return `${EYE_CARE_THEME_PREFIX}${scheme}-${intensity}`;
532
+ }
533
+ function tokensOf(palette) {
534
+ return Object.freeze({
535
+ "--dsw-alias-bg-base": palette.bgBase,
536
+ "--dsw-alias-bg-layer-1": palette.bgLayer1,
537
+ "--dsw-alias-bg-layer-2": palette.bgLayer2,
538
+ "--dsw-alias-bg-layer-3": palette.bgLayer3,
539
+ "--dsw-alias-bg-module-platform": palette.bgModule,
540
+ "--dsw-alias-bg-multi-select": palette.bgModule,
541
+ "--dsw-alias-bg-overlay": palette.bgOverlay,
542
+ "--dsw-alias-bg-skeleton": palette.hover,
543
+ "--dsw-alias-border-l1": palette.border1,
544
+ "--dsw-alias-border-l2-darkmode-thin": palette.border2,
545
+ "--dsw-alias-border-l2": palette.border2,
546
+ "--dsw-alias-border-l3": palette.border3,
547
+ "--dsw-alias-border-l4": palette.border3,
548
+ "--dsw-alias-brand-primary": palette.textPrimary,
549
+ "--dsw-alias-brand-text": palette.textPrimary,
550
+ "--dsw-alias-button-contrast-fill": palette.textSecondary,
551
+ "--dsw-alias-button-elevated-fill": palette.bgLayer3,
552
+ "--dsw-alias-button-floating-fill": palette.bgLayer2,
553
+ "--dsw-alias-button-floating-hover": palette.hoverSolid,
554
+ "--dsw-alias-button-ghost-active-border": palette.textCaption,
555
+ "--dsw-alias-button-ghost-active-fill": palette.active,
556
+ "--dsw-alias-button-ghost-active-hover": palette.hoverSolid,
557
+ "--dsw-alias-button-primary-fill": palette.primaryFill,
558
+ "--dsw-alias-button-primary-hover": palette.primaryHover,
559
+ "--dsw-alias-button-primary-dimmed": palette.primaryDimmed,
560
+ "--dsw-alias-interactive-bg-active": palette.active,
561
+ "--dsw-alias-interactive-bg-hover-accent": palette.active,
562
+ "--dsw-alias-interactive-bg-hover": palette.hover,
563
+ "--dsw-alias-interactive-bg-hover-solid": palette.hoverSolid,
564
+ "--dsw-alias-label-caption": palette.textCaption,
565
+ "--dsw-alias-label-dimmed": palette.border3,
566
+ "--dsw-alias-label-primary-bluish": palette.textPrimary,
567
+ "--dsw-alias-label-primary-dimmed": palette.textPrimary,
568
+ "--dsw-alias-label-primary": palette.textPrimary,
569
+ "--dsw-alias-label-secondary": palette.textSecondary,
570
+ "--dsw-alias-label-tertiary": palette.textTertiary,
571
+ "--dsw-alias-markdown-citation": palette.hoverSolid,
572
+ "--dsw-alias-markdown-code-block-banner": palette.codeBanner,
573
+ "--dsw-alias-markdown-code-block": palette.code,
574
+ "--dsw-alias-markdown-code-segment-selected": palette.bgLayer3,
575
+ "--dsw-alias-markdown-code-segment-unselected": palette.code,
576
+ "--dsw-alias-markdown-inline-code": palette.inlineCode,
577
+ "--dsw-alias-markdown-placeholder": palette.bgModule,
578
+ "--dsw-alias-markdown-tag": palette.hoverSolid,
579
+ "--dsw-alias-scrollbar-bg-l1": palette.scrollbar,
580
+ "--dsw-alias-scrollbar-bg-l2": palette.scrollbar,
581
+ "--dsw-alias-scrollbar-hover-l1": palette.scrollbarHover,
582
+ "--dsw-alias-scrollbar-hover-l2": palette.scrollbarHover,
583
+ "--dsw-specific-bubble-highlight": palette.bubbleHighlight,
584
+ "--dsw-specific-bubble": palette.bubble,
585
+ "--dsw-specific-input-major": palette.input,
586
+ "--dsw-specific-login-input": palette.bgLayer2,
587
+ "--dsw-specific-menu": palette.bgLayer3,
588
+ "--dsw-specific-selector": palette.selector,
589
+ "--dsw-specific-sidebar-fill": palette.sidebar,
590
+ "--dsw-specific-sidebar-nav-item-active-accent": palette.sidebarAccent,
591
+ "--dsw-specific-sidebar-nav-item-active": palette.sidebarActive,
592
+ "--dsw-specific-sidebar-nav-item-hover": palette.sidebarHover,
593
+ "--dsw-specific-tip": palette.bgModule,
594
+ "--shiki-foreground": palette.textPrimary,
595
+ "--shiki-background": palette.code,
596
+ "--shiki-token-constant": palette.shiki.constant,
597
+ "--shiki-token-string": palette.shiki.string,
598
+ "--shiki-token-comment": palette.shiki.comment,
599
+ "--shiki-token-keyword": palette.shiki.keyword,
600
+ "--shiki-token-parameter": palette.shiki.parameter,
601
+ "--shiki-token-function": palette.shiki.function,
602
+ "--shiki-token-string-expression": palette.shiki.stringExpression,
603
+ "--shiki-token-punctuation": palette.shiki.punctuation,
604
+ "--shiki-token-link": palette.shiki.link
605
+ });
606
+ }
607
+ /** Six concrete themes registered for the two schemes and three warmth levels. */
608
+ const EYE_CARE_THEMES = Object.freeze(["light", "dark"].flatMap((scheme) => [
609
+ "soft",
610
+ "balanced",
611
+ "warm"
612
+ ].map((intensity) => Object.freeze({
613
+ id: eyeCareThemeId(scheme, intensity),
614
+ colorScheme: scheme,
615
+ tokens: tokensOf((scheme === "light" ? LIGHT : DARK)[intensity])
616
+ }))));
617
+ const EYE_CARE_THEME_IDS = new Set(EYE_CARE_THEMES.map((theme) => theme.id));
618
+ /** Whether a theme id is one of this package's registered concrete themes. */
619
+ function isEyeCareThemeId(id) {
620
+ return EYE_CARE_THEME_IDS.has(id);
621
+ }
622
+ //#endregion
623
+ //#region src/client/controller.ts
624
+ const INITIAL_STATE = {
625
+ settings: { ...DEFAULT_EYE_CARE_SETTINGS },
626
+ status: "loading",
627
+ writable: false,
628
+ revision: void 0,
629
+ error: null
630
+ };
631
+ /** Maximum delay accepted for ui-theme's already-started Host adoption. */
632
+ const STARTUP_BASE_THEME_GRACE_MS = 1e3;
633
+ function sameSettings(left, right) {
634
+ return left.mode === right.mode && left.intensity === right.intensity;
635
+ }
636
+ function messageOf(error) {
637
+ return error instanceof Error ? error.message : String(error);
638
+ }
639
+ /** Stateful controller shared by theme listeners and the Settings row. */
640
+ var EyeCareController = class {
641
+ /** Reactive row source. */
642
+ store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(INITIAL_STATE);
643
+ theme;
644
+ rpc;
645
+ media;
646
+ now;
647
+ unregisterThemes = [];
648
+ unsubscribeTheme;
649
+ unsubscribeMedia;
650
+ tail = Promise.resolve();
651
+ disposeTask;
652
+ pendingSettings;
653
+ disposed = false;
654
+ applyingTheme = 0;
655
+ restoreTheme = "system";
656
+ hasRestoreTheme = false;
657
+ acceptedRemoteSnapshot = false;
658
+ startupBaseTheme;
659
+ startupBaseThemeDeadline = 0;
660
+ /**
661
+ * Register the concrete themes and lifecycle listeners.
662
+ * @param options - theme, transport, and system-scheme collaborators.
663
+ */
664
+ constructor(options) {
665
+ this.theme = options.theme;
666
+ this.rpc = options.rpc;
667
+ this.now = options.now ?? Date.now;
668
+ this.media = options.media ?? (typeof matchMedia === "function" ? matchMedia("(prefers-color-scheme: dark)") : void 0);
669
+ const currentTheme = this.theme.getTheme().preference;
670
+ if (!isEyeCareThemeId(currentTheme)) {
671
+ this.restoreTheme = currentTheme;
672
+ this.hasRestoreTheme = true;
673
+ }
674
+ try {
675
+ for (const definition of EYE_CARE_THEMES) this.unregisterThemes.push(this.theme.register(definition));
676
+ this.unsubscribeTheme = options.subscribeTheme((snapshot) => {
677
+ this.onThemeChanged(snapshot);
678
+ });
679
+ if (this.media !== void 0) {
680
+ const media = this.media;
681
+ const listener = () => {
682
+ if (!this.disposed && this.store.getSnapshot().settings.mode === "auto") this.applyCurrentTheme();
683
+ };
684
+ media.addEventListener("change", listener);
685
+ this.unsubscribeMedia = () => {
686
+ media.removeEventListener("change", listener);
687
+ };
688
+ }
689
+ } catch (error) {
690
+ for (const unregister of this.unregisterThemes.splice(0).reverse()) unregister();
691
+ throw error;
692
+ }
693
+ }
694
+ /**
695
+ * Load durable settings without blocking plugin activation.
696
+ * @returns settlement after this read reaches the serialized transport queue.
697
+ */
698
+ load() {
699
+ if (this.rpc === void 0) {
700
+ this.setUnavailable();
701
+ return Promise.resolve();
702
+ }
703
+ if (this.store.getSnapshot().revision === void 0) this.store.update((state) => {
704
+ state.status = "loading";
705
+ state.error = null;
706
+ });
707
+ return this.enqueue(async () => {
708
+ await this.readRemote();
709
+ });
710
+ }
711
+ /**
712
+ * Select a mode and persist the latest selection when Host settings are writable.
713
+ * @param mode - next operating mode.
714
+ * @returns settlement after the serialized persistence attempt.
715
+ */
716
+ setMode(mode) {
717
+ return this.select({
718
+ ...this.store.getSnapshot().settings,
719
+ mode
720
+ });
721
+ }
722
+ /**
723
+ * Select a warmth and persist the latest selection when Host settings are writable.
724
+ * @param intensity - next warmth level.
725
+ * @returns settlement after the serialized persistence attempt.
726
+ */
727
+ setIntensity(intensity) {
728
+ return this.select({
729
+ ...this.store.getSnapshot().settings,
730
+ intensity
731
+ });
732
+ }
733
+ /** Queue a refresh after a Host settings invalidation. */
734
+ refresh() {
735
+ this.load();
736
+ }
737
+ /**
738
+ * Reach transport quiescence, restore the user's live non-eye-care theme, and unregister all resources.
739
+ * @returns settlement after cleanup completes.
740
+ */
741
+ dispose() {
742
+ return this.disposeTask ??= this.disposeOnce();
743
+ }
744
+ async disposeOnce() {
745
+ this.disposed = true;
746
+ this.pendingSettings = void 0;
747
+ const failures = [];
748
+ for (const unsubscribe of [this.unsubscribeMedia, this.unsubscribeTheme]) {
749
+ if (unsubscribe === void 0) continue;
750
+ try {
751
+ unsubscribe();
752
+ } catch (error) {
753
+ failures.push(error);
754
+ }
755
+ }
756
+ this.unsubscribeMedia = void 0;
757
+ this.unsubscribeTheme = void 0;
758
+ await this.tail;
759
+ if (isEyeCareThemeId(this.theme.getTheme().preference)) try {
760
+ this.setTheme(this.resolveRestoreTheme());
761
+ } catch (error) {
762
+ failures.push(error);
763
+ try {
764
+ this.setTheme("system");
765
+ } catch (fallbackError) {
766
+ failures.push(fallbackError);
767
+ }
768
+ }
769
+ for (const unregister of this.unregisterThemes.splice(0).reverse()) try {
770
+ unregister();
771
+ } catch (error) {
772
+ failures.push(error);
773
+ }
774
+ if (failures.length > 0) throw new AggregateError(failures, "eye-care cleanup failed");
775
+ }
776
+ enqueue(operation) {
777
+ if (this.disposed) return Promise.resolve();
778
+ const task = this.tail.then(async () => {
779
+ if (!this.disposed) await operation();
780
+ });
781
+ this.tail = task.catch(() => {});
782
+ return task;
783
+ }
784
+ select(settings) {
785
+ if (this.disposed) return Promise.resolve();
786
+ if (this.store.getSnapshot().settings.mode === "off" && settings.mode !== "off") this.captureRestoreTheme();
787
+ this.store.update((state) => {
788
+ state.settings = settings;
789
+ state.error = null;
790
+ if (state.status === "error") state.status = state.revision === void 0 ? "unavailable" : "ready";
791
+ });
792
+ this.applyCurrentTheme();
793
+ return this.persist(settings);
794
+ }
795
+ persist(settings) {
796
+ const state = this.store.getSnapshot();
797
+ if (this.rpc === void 0) {
798
+ this.setUnavailable(state.error);
799
+ return Promise.resolve();
800
+ }
801
+ if (state.revision !== void 0 && !state.writable) {
802
+ this.setUnavailable(state.error);
803
+ return Promise.resolve();
804
+ }
805
+ this.pendingSettings = { ...settings };
806
+ return this.enqueue(async () => {
807
+ await this.flushPending();
808
+ });
809
+ }
810
+ async flushPending() {
811
+ while (!this.disposed && this.pendingSettings !== void 0) {
812
+ const desired = this.pendingSettings;
813
+ this.pendingSettings = void 0;
814
+ let state = this.store.getSnapshot();
815
+ if (state.revision === void 0) {
816
+ if (this.pendingSettings === void 0) this.pendingSettings = desired;
817
+ if (!await this.readRemote()) return;
818
+ continue;
819
+ }
820
+ if (!state.writable || state.revision === void 0) {
821
+ this.setUnavailable(state.error);
822
+ return;
823
+ }
824
+ this.store.update((next) => {
825
+ next.status = "saving";
826
+ next.error = null;
827
+ });
828
+ let response;
829
+ try {
830
+ const rpc = this.rpc;
831
+ if (rpc === void 0) return;
832
+ response = await rpc.call(EYE_CARE_RPC_CHANNEL, EYE_CARE_RPC_SAVE, {
833
+ expectedRevision: state.revision,
834
+ value: desired
835
+ });
836
+ } catch (error) {
837
+ if (!this.disposed) this.setUnavailable(messageOf(error));
838
+ return;
839
+ }
840
+ if (this.disposed) return;
841
+ if (!response.ok) {
842
+ if (response.error.code === "settings-conflict") {
843
+ const latest = this.pendingSettings ?? desired;
844
+ this.pendingSettings = latest;
845
+ if (!await this.readRemote()) return;
846
+ if (this.disposed) return;
847
+ continue;
848
+ }
849
+ this.setUnavailable(response.error.message);
850
+ return;
851
+ }
852
+ const snapshot = decodeEyeCareSnapshot(response.value);
853
+ if (snapshot === void 0) {
854
+ this.store.update((next) => {
855
+ next.status = "error";
856
+ next.error = "eye-care Settings returned an invalid save response";
857
+ });
858
+ return;
859
+ }
860
+ const preserveSettings = this.pendingSettings !== void 0 || !sameSettings(this.store.getSnapshot().settings, desired);
861
+ this.accept(snapshot, preserveSettings);
862
+ }
863
+ }
864
+ async readRemote() {
865
+ let response;
866
+ try {
867
+ const rpc = this.rpc;
868
+ if (rpc === void 0) return false;
869
+ response = await rpc.call(EYE_CARE_RPC_CHANNEL, EYE_CARE_RPC_READ, {});
870
+ } catch (error) {
871
+ if (!this.disposed) this.setUnavailable(messageOf(error), true);
872
+ return false;
873
+ }
874
+ if (this.disposed) return false;
875
+ if (!response.ok) {
876
+ this.setUnavailable(response.error.message, true);
877
+ return false;
878
+ }
879
+ const snapshot = decodeEyeCareSnapshot(response.value);
880
+ if (snapshot === void 0) {
881
+ this.store.update((state) => {
882
+ state.status = "error";
883
+ state.error = "eye-care Settings returned an invalid read response";
884
+ });
885
+ return false;
886
+ }
887
+ this.accept(snapshot, this.pendingSettings !== void 0);
888
+ return true;
889
+ }
890
+ accept(snapshot, preserveSettings) {
891
+ const previous = this.store.getSnapshot().settings;
892
+ const next = preserveSettings ? previous : snapshot.settings.value;
893
+ const firstRemote = !this.acceptedRemoteSnapshot;
894
+ this.acceptedRemoteSnapshot = true;
895
+ if (next.mode === "off") {
896
+ this.startupBaseTheme = void 0;
897
+ this.startupBaseThemeDeadline = 0;
898
+ if (!this.hasRestoreTheme && snapshot.baseThemePreference !== void 0) {
899
+ this.restoreTheme = snapshot.baseThemePreference;
900
+ this.hasRestoreTheme = true;
901
+ }
902
+ } else if (firstRemote) {
903
+ const base = snapshot.baseThemePreference;
904
+ if (base === void 0) {
905
+ if (!this.hasRestoreTheme) this.captureRestoreTheme();
906
+ } else {
907
+ this.restoreTheme = base;
908
+ this.hasRestoreTheme = true;
909
+ if (this.theme.getTheme().preference !== base) {
910
+ this.startupBaseTheme = base;
911
+ this.startupBaseThemeDeadline = this.now() + STARTUP_BASE_THEME_GRACE_MS;
912
+ }
913
+ }
914
+ } else if (!preserveSettings && previous.mode === "off") this.captureRestoreTheme(snapshot.baseThemePreference);
915
+ this.store.set({
916
+ settings: next,
917
+ status: snapshot.writable ? "ready" : "unavailable",
918
+ writable: snapshot.writable,
919
+ revision: snapshot.settings.revision,
920
+ error: null
921
+ });
922
+ this.applyCurrentTheme();
923
+ }
924
+ setUnavailable(error = null, clearRevision = false) {
925
+ this.store.update((state) => {
926
+ state.status = "unavailable";
927
+ state.writable = false;
928
+ if (clearRevision) state.revision = void 0;
929
+ state.error = error;
930
+ });
931
+ }
932
+ captureRestoreTheme(fallback) {
933
+ const preference = this.theme.getTheme().preference;
934
+ const candidate = isEyeCareThemeId(preference) ? fallback ?? "system" : preference;
935
+ this.restoreTheme = candidate;
936
+ this.hasRestoreTheme = true;
937
+ }
938
+ resolveRestoreTheme() {
939
+ if (!this.hasRestoreTheme || this.restoreTheme === "system") return "system";
940
+ return this.theme.getTheme().themes.some((theme) => theme.id === this.restoreTheme) ? this.restoreTheme : "system";
941
+ }
942
+ applyCurrentTheme() {
943
+ const { mode, intensity } = this.store.getSnapshot().settings;
944
+ const current = this.theme.getTheme().preference;
945
+ if (mode === "off") {
946
+ if (isEyeCareThemeId(current)) this.setTheme(this.resolveRestoreTheme());
947
+ this.hasRestoreTheme = false;
948
+ return;
949
+ }
950
+ if (!this.hasRestoreTheme) this.captureRestoreTheme();
951
+ const scheme = mode === "auto" ? this.media?.matches === true ? "dark" : "light" : mode;
952
+ this.setTheme(eyeCareThemeId(scheme, intensity));
953
+ }
954
+ setTheme(id) {
955
+ if (this.theme.getTheme().preference === id) return;
956
+ this.applyingTheme += 1;
957
+ try {
958
+ this.theme.setTheme(id);
959
+ } finally {
960
+ this.applyingTheme -= 1;
961
+ }
962
+ }
963
+ onThemeChanged(snapshot) {
964
+ if (this.disposed || this.applyingTheme > 0 || isEyeCareThemeId(snapshot.preference)) return;
965
+ if (this.startupBaseTheme !== void 0) {
966
+ const expected = this.startupBaseTheme;
967
+ const withinStartupWindow = this.now() <= this.startupBaseThemeDeadline;
968
+ this.startupBaseTheme = void 0;
969
+ this.startupBaseThemeDeadline = 0;
970
+ if (withinStartupWindow && snapshot.preference === expected) {
971
+ this.restoreTheme = expected;
972
+ this.hasRestoreTheme = true;
973
+ this.applyCurrentTheme();
974
+ return;
975
+ }
976
+ }
977
+ const state = this.store.getSnapshot();
978
+ if (state.settings.mode === "off") return;
979
+ this.restoreTheme = snapshot.preference;
980
+ this.hasRestoreTheme = true;
981
+ const settings = {
982
+ ...state.settings,
983
+ mode: "off"
984
+ };
985
+ this.store.update((next) => {
986
+ next.settings = settings;
987
+ next.error = null;
988
+ });
989
+ this.persist(settings);
990
+ }
991
+ };
992
+ //#endregion
993
+ //#region src/client/locales.ts
994
+ /** `settings.eyeCare` dictionaries. */
995
+ /** Simplified Chinese dictionary. */
996
+ const zh = {
997
+ "title": "护眼模式",
998
+ "description": "降低页面的冷白光感,长时间阅读更柔和。",
999
+ "enabled.title": "护眼模式",
1000
+ "enabled.off": "关闭",
1001
+ "enabled.on": "开启",
1002
+ "advanced.title": "显示方式与暖色强度",
1003
+ "mode.title": "显示方式",
1004
+ "mode.off": "关闭",
1005
+ "mode.auto": "自动",
1006
+ "mode.light": "日间",
1007
+ "mode.dark": "夜间",
1008
+ "intensity.title": "暖色强度",
1009
+ "intensity.soft": "柔和",
1010
+ "intensity.balanced": "均衡",
1011
+ "intensity.warm": "温暖",
1012
+ "status.off": "护眼模式已关闭",
1013
+ "status.auto": "自动跟随系统明暗外观",
1014
+ "status.light": "正在使用暖色日间主题",
1015
+ "status.dark": "正在使用暖暗夜间主题",
1016
+ "status.local": "仅当前浏览器有效",
1017
+ "status.saving": "正在保存",
1018
+ "status.error": "设置暂未保存"
1019
+ };
1020
+ /** English dictionary. */
1021
+ const en = {
1022
+ "title": "Eye care",
1023
+ "description": "Use warm semantic themes to reduce cool-white glare during long reading sessions",
1024
+ "enabled.title": "Eye care",
1025
+ "enabled.off": "Off",
1026
+ "enabled.on": "On",
1027
+ "advanced.title": "Appearance and warmth",
1028
+ "mode.title": "Appearance",
1029
+ "mode.off": "Off",
1030
+ "mode.auto": "Auto",
1031
+ "mode.light": "Day",
1032
+ "mode.dark": "Night",
1033
+ "intensity.title": "Warmth",
1034
+ "intensity.soft": "Soft",
1035
+ "intensity.balanced": "Balanced",
1036
+ "intensity.warm": "Warm",
1037
+ "status.off": "Eye care is off",
1038
+ "status.auto": "Following the system light or dark appearance",
1039
+ "status.light": "Warm day theme is active",
1040
+ "status.dark": "Warm night theme is active",
1041
+ "status.local": "Current browser only",
1042
+ "status.saving": "Saving",
1043
+ "status.error": "Setting is not saved yet"
1044
+ };
1045
+ //#endregion
1046
+ //#region src/client/index.tsx
1047
+ /** Required browser services. */
1048
+ const inject = [
1049
+ "slots",
1050
+ "locale",
1051
+ "theme",
1052
+ "connection",
1053
+ "remote"
1054
+ ];
1055
+ /** Register settings copy, controller lifecycle, and General row. */
1056
+ function apply(ctx) {
1057
+ const theme = ctx.get("theme");
1058
+ const connection = ctx.get("connection");
1059
+ const controller = new EyeCareController({
1060
+ theme,
1061
+ ...connection.isLoopback ? { rpc: connection.rpc } : {},
1062
+ subscribeTheme: (listener) => ctx.on("theme/change", listener)
1063
+ });
1064
+ ctx.effect(() => ctx.locale.register("settings.eyeCare", {
1065
+ zh,
1066
+ en
1067
+ }), "dsh-eye-care: dictionaries");
1068
+ ctx.effect(() => {
1069
+ const refresh = (namespace) => {
1070
+ if (namespace !== void 0 && namespace !== "eye-care") return;
1071
+ controller.refresh();
1072
+ };
1073
+ const disposers = [ctx.remote.$on("settings/document-updated", refresh), ctx.on("connection/reset", () => {
1074
+ if (connection.isLoopback) controller.refresh();
1075
+ })];
1076
+ controller.load();
1077
+ return async () => {
1078
+ for (const dispose of disposers) dispose();
1079
+ await controller.dispose();
1080
+ };
1081
+ }, "dsh-eye-care: controller lifecycle");
1082
+ const injected = () => ({
1083
+ hooks: { eyeCare: controller.store },
1084
+ setMode: (mode) => controller.setMode(mode),
1085
+ setIntensity: (intensity) => controller.setIntensity(intensity)
1086
+ });
1087
+ ctx.slots.inject("settings.general.item", () => ctx.slots.register({
1088
+ name: "settings.general.item",
1089
+ id: "eye-care",
1090
+ order: 15,
1091
+ locale: "settings.eyeCare",
1092
+ inject: injected
1093
+ }, EyeCareRow));
1094
+ }
1095
+ //#endregion
1096
+ exports.EYE_CARE_THEMES = EYE_CARE_THEMES;
1097
+ exports.EyeCareController = EyeCareController;
1098
+ exports.apply = apply;
1099
+ exports.eyeCareThemeId = eyeCareThemeId;
1100
+ exports.inject = inject;
1101
+ exports.isEyeCareThemeId = isEyeCareThemeId;
1102
+ return module.exports;
1103
+ }
1104
+ });
1105
+
1106
+ //# sourceMappingURL=client.js.map