@nonamelego/dsh-catppuccin 0.5.6-beta.1 → 0.5.6
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/README.en.md +5 -4
- package/README.md +9 -6
- package/lib/client.js +166 -39
- package/lib/client.js.map +1 -1
- package/lib/index.js +223 -65
- package/lib/index.js.map +1 -1
- package/package.json +14 -14
package/README.en.md
CHANGED
|
@@ -318,11 +318,12 @@ Then add `@nonamelego/dsh-catppuccin` to the profile's `package.json`
|
|
|
318
318
|
- **Q: "How is my theme choice remembered?"**
|
|
319
319
|
A: The choice persists in DSH's official settings document (the `catppuccin` namespace,
|
|
320
320
|
stored under the DSH home, shared across every DSH instance on the machine), with
|
|
321
|
-
localStorage as an in-browser cache and cross-tab sync. So
|
|
322
|
-
`dsh web --port <custom
|
|
323
|
-
|
|
321
|
+
localStorage as an in-browser cache and cross-tab sync. So switching browser,
|
|
322
|
+
clearing site data, a custom port (`dsh web --port <custom>`) or a second desktop
|
|
323
|
+
instance never loses the preference; **DSH Desktop** (the official shell and
|
|
324
|
+
`anywhere-labs/dsh-desktop`) restores it across restarts too. The glass switch and every
|
|
324
325
|
knob persist the same way. Since 0.5.0, a legacy `catppuccin-state.json` is migrated
|
|
325
|
-
into the settings
|
|
326
|
+
into the official settings once on first launch (the file is kept as a rollback copy).
|
|
326
327
|
- **Q: "How do I know if this plugin has a new version?"**
|
|
327
328
|
A: Settings → General → **Check Catppuccin plugin updates** compares against npm in one
|
|
328
329
|
click and gives a copyable upgrade command; or run
|
package/README.md
CHANGED
|
@@ -292,12 +292,15 @@ pnpm --dir C:\Users\LeGo\.dsh\profiles\web add link:D:\Vibe-Coding\dsh-catppucci
|
|
|
292
292
|
A: 官方外观行只列出内置的浅色/深色/跟随系统偏好。四个主题在它正下方的
|
|
293
293
|
**Catppuccin** 行里。
|
|
294
294
|
- Q: **_"我的主题选择是怎么记住的?"_**
|
|
295
|
-
A: 选择持久保存在 DSH
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
295
|
+
A: 选择持久保存在 DSH 的官方设置里(同一台机器的 DSH 共享这份偏好),浏览器
|
|
296
|
+
localStorage 作为即时缓存与多标签页同步。落点随 DSH 版本而异:**0.1.6 及更早**
|
|
297
|
+
存于 DSH home 下的官方 settings 文档(`catppuccin` 命名空间);**0.1.7 起**上游
|
|
298
|
+
改为「插件配置表单」,落点是当前 profile 的 patch 文件里本插件的 `config:` 段
|
|
299
|
+
(本插件两套都支持,按宿主实际提供的服务自动选择)。因此换浏览器、清站点数据、
|
|
300
|
+
自定义端口(`dsh web --port`)或再开一个桌面实例,偏好都不会丢;
|
|
301
|
+
**DSH Desktop**(官方壳与 `anywhere-labs/dsh-desktop`)同样跨重启自动恢复。
|
|
302
|
+
玻璃质感开关与各旋钮同样持久保存。0.5.0 起旧版 `catppuccin-state.json`
|
|
303
|
+
会在首次启动时一次性迁移进官方设置(文件保留作回退)。
|
|
301
304
|
- Q: **_"怎么知道这个插件有没有新版本?"_**
|
|
302
305
|
A: 设置 → 常规 → **检查 Catppuccin 插件更新** 一键检测本插件在 npm 上的最新版本,
|
|
303
306
|
发现新版会给出可复制的升级命令;也可以随时手动执行
|
package/lib/client.js
CHANGED
|
@@ -1954,9 +1954,39 @@ window.__ModuleLoader__.load({
|
|
|
1954
1954
|
* glass.module.css and rebuilding.
|
|
1955
1955
|
*/
|
|
1956
1956
|
const GLASS_CSS_TEXT = "[data-dsh-glass] body{--dsh-glass-card:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(52% * var(--dsh-glass-frost,1)), transparent);--dsh-glass-card-raised:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(64% * var(--dsh-glass-frost,1)), transparent);--dsh-glass-card-hover:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(76% * var(--dsh-glass-frost,1)), transparent);--dsh-glass-rim:color-mix(in srgb, var(--dsw-alias-label-primary) 18%, transparent);--dsh-glass-rim-soft:color-mix(in srgb, var(--dsw-alias-label-primary) 12%, transparent);--dsh-glass-edge:inset 0 1px 0 #ffffff6b;--dsh-glass-drop:0 10px 32px color-mix(in srgb, var(--dsw-alias-bg-mask-2) 38%, transparent)}[data-dsh-glass] body[data-ds-dark-theme]{--dsh-glass-rim:color-mix(in srgb, var(--dsw-alias-label-primary) 24%, transparent);--dsh-glass-rim-soft:color-mix(in srgb, var(--dsw-alias-label-primary) 16%, transparent);--dsh-glass-edge:inset 0 1px 0 #ffffff14;--dsh-glass-drop:0 10px 32px #02060e73}[data-dsh-glass] body{background:color-mix(in srgb, color-mix(in srgb, var(--dsw-alias-bg-base), #fff calc(var(--dsh-glass-brightness-white,0) * 100%)), #000 calc(var(--dsh-glass-brightness-black,0) * 100%))}[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-fade]{z-index:7;pointer-events:none;backdrop-filter:blur(5px);background:#ffffff2e;height:13px;position:fixed;left:0;right:0}[data-dsh-glass] body[data-ds-dark-theme] [data-dsh-glass-fade]{background:#00000024}[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] [class*=block]{--dsl-code-block-border-radius:14px;--dsl-diff-radius:14px;--dsl-read-radius:14px;--dsl-terminal-radius:14px;--dsl-web-radius:14px;--dsl-search-radius:14px;border-radius:14px}[data-dsh-glass-float] [data-dsh-glass-add]{background:var(--dsh-glass-card-raised);border:1px solid var(--dsh-glass-rim);box-shadow:var(--dsh-glass-edge);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass-float] [data-dsh-glass-add]:hover:not(:disabled){background:var(--dsh-glass-card-hover)}[data-dsh-glass-float] [data-dsh-glass-surface]{background:var(--dsh-glass-card-raised);border:1px solid var(--dsh-glass-rim);box-shadow:var(--dsh-glass-edge);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass-float] [data-dsh-glass-surface]:hover:not(:disabled){background:var(--dsh-glass-card-hover)}[data-dsh-glass-float] [data-conversation-scroll] [class*=bubble]{border:1px solid var(--dsh-glass-rim-soft);background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) calc(46% * var(--dsh-glass-frost,1)), transparent);border-radius:14px}[data-dsh-glass-float] [class*=card]{border-radius:14px}[data-dsh-glass] [data-dsh-glass-settings]{--dsh-glass-settings-raised:calc(38% + 38% * var(--dsh-glass-frost,1));--dsh-glass-settings-soft:calc(30% + 30% * var(--dsh-glass-frost,1));--dsw-alias-bg-module-platform:color-mix(in srgb, var(--dsw-static-neutral-bluish-800) var(--dsh-glass-settings-raised), transparent);--dsw-alias-bg-layer-3:color-mix(in srgb, var(--dsw-static-neutral-bluish-800) var(--dsh-glass-settings-soft), transparent);--dsw-alias-bg-layer-2:color-mix(in srgb, var(--dsw-static-neutral-bluish-850) var(--dsh-glass-settings-raised), transparent);--dsw-alias-bg-layer-1:color-mix(in srgb, var(--dsw-static-neutral-bluish-850) var(--dsh-glass-settings-soft), transparent);--dsw-specific-sidebar-nav-item-active:color-mix(in srgb, var(--dsw-static-neutral-bluish-750) var(--dsh-glass-settings-raised), transparent);--dsw-specific-sidebar-nav-item-hover:color-mix(in srgb, var(--dsw-static-neutral-bluish-850) var(--dsh-glass-settings-soft), transparent)}[data-dsh-glass] body:not([data-ds-dark-theme]) [data-dsh-glass-settings]{--dsw-alias-bg-module-platform:color-mix(in srgb, var(--dsw-static-neutral-bluish-60) var(--dsh-glass-settings-raised), transparent);--dsw-alias-bg-layer-3:color-mix(in srgb, var(--dsw-static-neutral-bluish-00) var(--dsh-glass-settings-soft), transparent);--dsw-alias-bg-layer-2:color-mix(in srgb, var(--dsw-static-neutral-bluish-00) var(--dsh-glass-settings-raised), transparent);--dsw-alias-bg-layer-1:color-mix(in srgb, var(--dsw-static-neutral-bluish-00) var(--dsh-glass-settings-soft), transparent);--dsw-specific-sidebar-nav-item-active:color-mix(in srgb, var(--dsw-static-neutral-bluish-100) var(--dsh-glass-settings-raised), transparent);--dsw-specific-sidebar-nav-item-hover:color-mix(in srgb, var(--dsw-static-neutral-bluish-75) var(--dsh-glass-settings-soft), transparent)}[data-dsh-glass-float] header{border:1px solid var(--dsh-glass-rim);background:var(--dsh-glass-card);box-shadow:var(--dsh-glass-edge), var(--dsh-glass-drop);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-radius:20px;margin:12px 16px 0;padding:10px 16px 8px}[data-dsh-glass-float] header:after{display:none}[data-dsh-glass-float] [data-dsh-glass-frame][data-sidebar-collapsed] header{margin-left:28px}[data-dsh-glass-float] [class*=sidebarCol]{--dsw-specific-sidebar-fill:transparent;z-index:9;border:1px solid var(--dsh-glass-rim);border-right-color:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 40%, transparent);box-shadow:var(--dsh-glass-drop);border-radius:20px;margin:12px;padding:10px 12px 14px;position:relative;overflow:hidden}[data-dsh-glass-float] [class*=sidebarCol]:before{content:\"\";border-radius:inherit;background:var(--dsh-glass-card);box-shadow:var(--dsh-glass-edge);z-index:-1;pointer-events:none;transition:border-radius .15s var(--ds-ease-in-out);position:absolute;inset:0}[data-dsh-glass-float] body[data-ds-dark-theme] [class*=sidebarCol]{border-right-color:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 22%, transparent)}[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(--dsh-glass-rim);background:var(--dsh-glass-card);width:calc(100% - 32px);height:calc(100% - 20px);box-shadow:var(--dsh-glass-edge), var(--dsh-glass-drop);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(--dsh-glass-rim);box-shadow:var(--dsh-glass-edge), var(--dsh-glass-drop);backdrop-filter:blur(var(--dsh-glass-blur,14px));position:relative}[data-dsh-glass-float] [data-dsh-glass-inputbar]:not([class*=hero]):not(:has([data-dsh-glass-stats])){padding-bottom:12px}[data-dsh-glass][data-dsh-glass-float] [data-dsh-glass-inputbar]:has([data-dsh-glass-stats]){border:1px solid var(--dsh-glass-rim);background:var(--dsh-glass-card);box-shadow:var(--dsh-glass-edge), var(--dsh-glass-drop);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-radius:24px;margin:0 auto 12px;padding:0;width:var(--dsh-composer-card-max-width)!important;max-width:var(--dsh-composer-card-max-width)!important}[data-dsh-glass-float] [data-dsh-glass-inputbar]:has([data-dsh-glass-stats]) [data-composer-card]{box-shadow:none;backdrop-filter:none;background:0 0;border:none;border-radius:0}[data-dsh-glass-float] [data-dsh-glass-inputbar]:has([data-dsh-glass-stats]) [data-composer-card]:after{display:none}[data-dsh-glass][data-dsh-glass-float] [data-dsh-glass-inputbar]:has([data-dsh-glass-stats]) [data-slot]{border:none;border-top:1px solid var(--dsh-glass-rim-soft);box-shadow:none;backdrop-filter:none;background:0 0;border-radius:0;margin:0;padding:2px 16px 6px;width:100%!important;max-width:none!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:100%!important;padding:4px 0 0!important}[data-dsh-glass][data-dsh-glass-float] [data-dsh-glass-inputbar] [data-slot] [data-dsh-live-tps]{padding-right:0}[data-dsh-glass-float] [role=treeitem][aria-selected=true]{background:color-mix(in srgb, var(--dsw-specific-sidebar-nav-item-active) 40%, transparent);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:var(--dsh-glass-card-raised);border:1px solid var(--dsh-glass-rim-soft);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass-float] [role=menu]:has([role=group]>[role=menuitemradio]){background:color-mix(in srgb, var(--dsw-alias-bg-layer-2) 70%, transparent)}[data-dsh-glass-float] [role=menu] [role=group]:has(>[role=menuitemradio])>:first-child{border:1px solid var(--dsh-glass-rim);background:var(--dsh-glass-card-raised);backdrop-filter:blur(var(--dsh-glass-blur,14px));border-radius:999px;align-items:center;width:fit-content;margin:3px 0 5px 6px;padding:3px 10px;display:inline-flex}[data-dsh-glass-float] [role=dialog][aria-modal=true]{background:var(--dsh-glass-card-raised);border:1px solid var(--dsh-glass-rim);box-shadow:var(--dsh-glass-edge);backdrop-filter:blur(var(--dsh-glass-blur,14px))}[data-dsh-glass] [data-conversation-scroll]{text-shadow:0 0 1px #ffffff80}[data-dsh-glass] body[data-ds-dark-theme] [data-conversation-scroll]{text-shadow:0 0 1px #0006}[data-dsh-glass-float] [data-dsh-glass-inputbar] [data-slot]{color:var(--dsw-alias-label-secondary)}[data-dsh-glass-float] [data-phase=hero]{animation:dsh-glass-in .32s var(--ds-ease-in-out)}[data-dsh-glass-float] [data-phase=active]{animation:dsh-glass-in .3s var(--ds-ease-in-out)}[data-dsh-glass-float] [data-testid^=view-]{animation:dsh-glass-in .26s var(--ds-ease-in-out)}[data-dsh-glass-float] [data-tool]{animation:dsh-glass-rise .3s var(--ds-ease-in-out) both}[data-dsh-glass-float] [role=dialog]{animation:dsh-glass-dialog-in .24s var(--ds-ease-in-out)}@keyframes dsh-glass-in{0%{opacity:0}}@keyframes dsh-glass-rise{0%{opacity:0;transform:translateY(6px)}}@keyframes 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*=panel],[data-dsh-glass-compat] [class*=popover],[data-dsh-glass-compat] [class*=dropdown]{backdrop-filter:blur(12px)}[data-dsh-glass-compat] [role=menu],[data-dsh-glass-compat] [role=tooltip],[data-dsh-glass-compat] [class*=card],[data-dsh-glass-compat] [class*=popover],[data-dsh-glass-compat] [class*=dropdown]{background:var(--dsh-glass-card-raised)}[data-dsh-glass-compat] [role=menu]:not(:focus-visible),[data-dsh-glass-compat] [role=tooltip]:not(:focus-visible),[data-dsh-glass-compat] [class*=card]:not(:focus-visible),[data-dsh-glass-compat] [class*=popover]:not(:focus-visible),[data-dsh-glass-compat] [class*=dropdown]:not(:focus-visible){outline:1px solid var(--dsh-glass-rim);outline-offset:-1px}[data-dsh-glass-compat] [role=dialog][aria-modal=true]{background:var(--dsh-glass-card-raised);border:1px solid var(--dsh-glass-rim);backdrop-filter:blur(var(--dsh-glass-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]{animation:none}}";
|
|
1957
|
-
/**
|
|
1958
|
-
*
|
|
1957
|
+
/** Legacy settings namespace (DSH <= 0.1.6-alpha.2): the Host registers it
|
|
1958
|
+
* through `settings.installSection`, the Client binds it through
|
|
1959
|
+
* `ctx.settingsScope` (see `src/settings-catppuccin.ts`). */
|
|
1959
1960
|
const CATPPUCCIN_SETTINGS_NS = "catppuccin";
|
|
1961
|
+
/**
|
|
1962
|
+
* Profile entry id carrying the plugin (DSH >= 0.1.7-alpha.1): the settings
|
|
1963
|
+
* service projects our volatile `Config` into a form named by the ACTIVE
|
|
1964
|
+
* PROFILE ENTRY's id, so this constant is the new seam's namespace.
|
|
1965
|
+
*
|
|
1966
|
+
* It is a CONTRACT with `cordis.patch.yml`: the `insert` row whose `name` is
|
|
1967
|
+
* `@nonamelego/dsh-catppuccin` must keep `id: dsh-catppuccin`, because the
|
|
1968
|
+
* Client can only address the form by that id (asserted by
|
|
1969
|
+
* `tests/settings-seam.spec.ts`). A user who renames the entry loses the new
|
|
1970
|
+
* seam and degrades to localStorage-only — the legacy seam is unaffected,
|
|
1971
|
+
* since that namespace is registered by the plugin itself.
|
|
1972
|
+
*/
|
|
1973
|
+
const CATPPUCCIN_ENTRY_ID = "dsh-catppuccin";
|
|
1974
|
+
/**
|
|
1975
|
+
* Whether a raw settings view carries a user layer at all.
|
|
1976
|
+
*
|
|
1977
|
+
* The two seams answer this differently and the difference is load-bearing
|
|
1978
|
+
* (issue #15): the NEW form's `user` is the profile patch's `override`, which
|
|
1979
|
+
* `configEditor.configuration()` defaults to `{}` — so "absent" arrives as an
|
|
1980
|
+
* EMPTY OBJECT, and `!== undefined` would be true forever. The LEGACY
|
|
1981
|
+
* document omits the field entirely when the document holds no such section,
|
|
1982
|
+
* so the old `!== undefined` test stays correct there. Callers must use the
|
|
1983
|
+
* predicate their channel defines (see `src/client/state-sync.ts`).
|
|
1984
|
+
* @param user - a snapshot's raw user layer.
|
|
1985
|
+
* @returns whether it holds at least one entry.
|
|
1986
|
+
*/
|
|
1987
|
+
function hasUserLayerSection(user) {
|
|
1988
|
+
return typeof user === "object" && user !== null && !Array.isArray(user) && Object.keys(user).length > 0;
|
|
1989
|
+
}
|
|
1960
1990
|
/** The four registered Catppuccin theme ids. MUST stay in sync with the
|
|
1961
1991
|
* `themeId` of `CATPPUCCIN_FLAVORS` in `src/client/palettes.ts` — guarded by
|
|
1962
1992
|
* `tests/state.spec.ts` — because the persisted flavour value IS the theme id. */
|
|
@@ -3283,14 +3313,17 @@ window.__ModuleLoader__.load({
|
|
|
3283
3313
|
const UPDATE_ROUTE_PATH = "/catppuccin/check-update";
|
|
3284
3314
|
//#endregion
|
|
3285
3315
|
//#region src/client/state-sync.ts
|
|
3286
|
-
/**
|
|
3287
|
-
*
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3316
|
+
/** The snapshot an unbound scope reports: the pre-0.5.0 "transport absent"
|
|
3317
|
+
* degradation, where localStorage is the only store. */
|
|
3318
|
+
const UNBOUND_SNAPSHOT = {
|
|
3319
|
+
status: "unavailable",
|
|
3320
|
+
value: void 0,
|
|
3321
|
+
base: void 0,
|
|
3322
|
+
user: void 0,
|
|
3323
|
+
revision: void 0,
|
|
3324
|
+
writable: false,
|
|
3325
|
+
mode: "memory"
|
|
3326
|
+
};
|
|
3294
3327
|
/** Whether the scope is usable for durable persistence: the document is
|
|
3295
3328
|
* resolved (`ready`) and the Host persistence is active (`host`). A `memory`
|
|
3296
3329
|
* scope (non-loopback pages / absent transport) or a not-yet-ready one is
|
|
@@ -3304,6 +3337,94 @@ window.__ModuleLoader__.load({
|
|
|
3304
3337
|
if (!isScopeUsable(snapshot) || snapshot.value === void 0) return null;
|
|
3305
3338
|
return stateFromSettingsSection(snapshot.value);
|
|
3306
3339
|
}
|
|
3340
|
+
/** The NEW seam's channel (DSH >= 0.1.7-alpha.1). */
|
|
3341
|
+
function configFormsScope(form) {
|
|
3342
|
+
return {
|
|
3343
|
+
kind: "configForms",
|
|
3344
|
+
getSnapshot: () => form.getSnapshot(),
|
|
3345
|
+
subscribe: (listener) => form.subscribe(listener),
|
|
3346
|
+
hasUserLayer: (snapshot) => hasUserLayerSection(snapshot.user),
|
|
3347
|
+
write: async (ops, baseRevision) => {
|
|
3348
|
+
try {
|
|
3349
|
+
return await form.mutate(ops, baseRevision) ? "accepted" : "refused";
|
|
3350
|
+
} catch {
|
|
3351
|
+
return "failed";
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
};
|
|
3355
|
+
}
|
|
3356
|
+
/** The LEGACY seam's channel (DSH <= 0.1.6-alpha.2). */
|
|
3357
|
+
function legacyScope(scope) {
|
|
3358
|
+
return {
|
|
3359
|
+
kind: "settingsScope",
|
|
3360
|
+
getSnapshot: () => scope.getSnapshot(),
|
|
3361
|
+
subscribe: (listener) => scope.subscribe(listener),
|
|
3362
|
+
hasUserLayer: (snapshot) => snapshot.user !== void 0,
|
|
3363
|
+
write: async (ops) => {
|
|
3364
|
+
try {
|
|
3365
|
+
await scope.mutate(ops);
|
|
3366
|
+
return "accepted";
|
|
3367
|
+
} catch {
|
|
3368
|
+
return "failed";
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
/**
|
|
3374
|
+
* Bind whichever settings channel this host serves, late and without blocking.
|
|
3375
|
+
*
|
|
3376
|
+
* Both bindings ride OPTIONAL injections: a service the deployment never
|
|
3377
|
+
* provides simply never fires its callback (it does not hold the plugin
|
|
3378
|
+
* back), and the two names cannot both be live in one host — 0.1.7-alpha.1
|
|
3379
|
+
* removed `settingsScope` from the service set, and 0.1.6 and earlier know
|
|
3380
|
+
* nothing of `configForms`. Until one binds, the scope reports the unbound
|
|
3381
|
+
* snapshot and every write short-circuits, which is exactly the
|
|
3382
|
+
* "settings transport unavailable" degradation the rows already handle.
|
|
3383
|
+
* @param ctx - client root context.
|
|
3384
|
+
* @returns the scope facade, bound for the lifetime of the caller's fiber.
|
|
3385
|
+
*/
|
|
3386
|
+
function createDurableScope(ctx) {
|
|
3387
|
+
let current;
|
|
3388
|
+
let offBound;
|
|
3389
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3390
|
+
const notify = () => {
|
|
3391
|
+
for (const listener of [...listeners]) listener();
|
|
3392
|
+
};
|
|
3393
|
+
const attach = (next) => {
|
|
3394
|
+
if (next === current) return;
|
|
3395
|
+
offBound?.();
|
|
3396
|
+
current = next;
|
|
3397
|
+
offBound = next?.subscribe(notify);
|
|
3398
|
+
notify();
|
|
3399
|
+
};
|
|
3400
|
+
ctx.inject(["configForms"], (formsCtx) => {
|
|
3401
|
+
attach(configFormsScope(formsCtx.configForms.get(CATPPUCCIN_ENTRY_ID)));
|
|
3402
|
+
return () => {
|
|
3403
|
+
attach(void 0);
|
|
3404
|
+
};
|
|
3405
|
+
});
|
|
3406
|
+
ctx.inject(["settingsScope"], (legacyCtx) => {
|
|
3407
|
+
const binder = legacyCtx.settingsScope;
|
|
3408
|
+
if (binder !== void 0) attach(legacyScope(binder.bind({ namespace: CATPPUCCIN_SETTINGS_NS })));
|
|
3409
|
+
return () => {
|
|
3410
|
+
attach(void 0);
|
|
3411
|
+
};
|
|
3412
|
+
});
|
|
3413
|
+
return {
|
|
3414
|
+
get kind() {
|
|
3415
|
+
return current?.kind ?? "none";
|
|
3416
|
+
},
|
|
3417
|
+
getSnapshot: () => current?.getSnapshot() ?? UNBOUND_SNAPSHOT,
|
|
3418
|
+
subscribe: (listener) => {
|
|
3419
|
+
listeners.add(listener);
|
|
3420
|
+
return () => {
|
|
3421
|
+
listeners.delete(listener);
|
|
3422
|
+
};
|
|
3423
|
+
},
|
|
3424
|
+
hasUserLayer: (snapshot) => current?.hasUserLayer(snapshot) ?? false,
|
|
3425
|
+
write: async (ops, baseRevision) => current?.write(ops, baseRevision) ?? "failed"
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3307
3428
|
/** One atomic mutation covering the whole local state (revision-fenced).
|
|
3308
3429
|
* Writing every field keeps the section self-contained — a partial write
|
|
3309
3430
|
* could otherwise leave a knob stuck on a stale override after a document
|
|
@@ -3366,11 +3487,13 @@ window.__ModuleLoader__.load({
|
|
|
3366
3487
|
/**
|
|
3367
3488
|
* Read-side consistency guard for the durable persist (items C/X).
|
|
3368
3489
|
*
|
|
3369
|
-
* The WRITE side is
|
|
3370
|
-
*
|
|
3371
|
-
*
|
|
3372
|
-
* localStorage value, the user changes flavour, and the debounced flush
|
|
3490
|
+
* The WRITE side is revision-fenced by the channel, so no optimistic locking
|
|
3491
|
+
* is re-built here. What a fence cannot see is a STALE LOCAL VIEW: tab A reads
|
|
3492
|
+
* an old localStorage value, the user changes flavour, and the debounced flush
|
|
3373
3493
|
* writes that old-based state over tab B's already-committed newer choice.
|
|
3494
|
+
* This guard is also the ONLY conflict detector the legacy seam can offer —
|
|
3495
|
+
* that seam resolves a refused write silently, so without it a concurrent edit
|
|
3496
|
+
* would be lost without a trace.
|
|
3374
3497
|
*
|
|
3375
3498
|
* The guard compares the revision the local state was derived from
|
|
3376
3499
|
* (`baseRevision`, captured at schedule time) with the snapshot revision at
|
|
@@ -3386,21 +3509,19 @@ window.__ModuleLoader__.load({
|
|
|
3386
3509
|
* a redundant identical mutation).
|
|
3387
3510
|
*/
|
|
3388
3511
|
async function persistStateToScope(scope, state, options = {}) {
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
if (
|
|
3397
|
-
if (!(lastWrittenSection !== void 0 && remote !== null && settingsSectionsEqual(settingsSectionFromState(remote), lastWrittenSection))) return "stale";
|
|
3398
|
-
}
|
|
3399
|
-
await scope.mutate(stateToMutateOps(state));
|
|
3400
|
-
return "written";
|
|
3401
|
-
} catch {
|
|
3402
|
-
return "error";
|
|
3512
|
+
const snapshot = scope.getSnapshot();
|
|
3513
|
+
if (!isScopeUsable(snapshot)) return "error";
|
|
3514
|
+
const remote = durableStateFromSnapshot(snapshot);
|
|
3515
|
+
const localSection = settingsSectionFromState(state);
|
|
3516
|
+
if (remote !== null && settingsSectionsEqual(settingsSectionFromState(remote), localSection)) return "noop";
|
|
3517
|
+
const { baseRevision, lastWrittenSection } = options;
|
|
3518
|
+
if (baseRevision !== void 0 && snapshot.revision !== void 0 && snapshot.revision !== baseRevision) {
|
|
3519
|
+
if (!(lastWrittenSection !== void 0 && remote !== null && settingsSectionsEqual(settingsSectionFromState(remote), lastWrittenSection))) return "stale";
|
|
3403
3520
|
}
|
|
3521
|
+
const outcome = await scope.write(stateToMutateOps(state), baseRevision);
|
|
3522
|
+
if (outcome === "accepted") return "written";
|
|
3523
|
+
if (outcome === "refused") return "stale";
|
|
3524
|
+
return "error";
|
|
3404
3525
|
}
|
|
3405
3526
|
let pendingTimer;
|
|
3406
3527
|
let pendingWrite;
|
|
@@ -3499,11 +3620,13 @@ window.__ModuleLoader__.load({
|
|
|
3499
3620
|
/** Whether localStorage carries an EXPLICIT `off` flavour choice.
|
|
3500
3621
|
*
|
|
3501
3622
|
* `readFlavor()` maps both "no value" and "off" to `off`, and the two are not
|
|
3502
|
-
* the same thing:
|
|
3503
|
-
*
|
|
3504
|
-
*
|
|
3505
|
-
*
|
|
3506
|
-
*
|
|
3623
|
+
* the same thing: an absent value only means THIS browser/origin never chose
|
|
3624
|
+
* (a first visit, cleared site data, or a second Desktop instance that landed
|
|
3625
|
+
* on the next free loopback port), while the durable store — which may
|
|
3626
|
+
* legitimately hold a flavour picked elsewhere — is machine-wide. Only a
|
|
3627
|
+
* literal `off` in storage proves the user (or another window) actually turned
|
|
3628
|
+
* the flavour off, which is the case the cross-tab restore must act on
|
|
3629
|
+
* (audit F4). */
|
|
3507
3630
|
function readExplicitFlavorOff() {
|
|
3508
3631
|
try {
|
|
3509
3632
|
return localStorage.getItem(FLAVOR_STORAGE_KEY) === "off";
|
|
@@ -3678,13 +3801,17 @@ window.__ModuleLoader__.load({
|
|
|
3678
3801
|
if (preference !== "light" && preference !== "dark") return false;
|
|
3679
3802
|
return preference === livePick;
|
|
3680
3803
|
}
|
|
3681
|
-
/** Required services: slots + locale (settings rows)
|
|
3682
|
-
* switch)
|
|
3804
|
+
/** Required services: slots + locale (settings rows) and theme (register +
|
|
3805
|
+
* switch). The durable settings transport is NOT a hard dependency: which
|
|
3806
|
+
* service provides it depends on the host version (issue #15 — 0.1.7-alpha.1
|
|
3807
|
+
* replaced `settingsScope` with `configForms`), so `createDurableScope`
|
|
3808
|
+
* binds whichever one appears through its own optional injections. Listing
|
|
3809
|
+
* either name here would leave the plugin permanently pending on the other
|
|
3810
|
+
* half of the installed base. */
|
|
3683
3811
|
const inject = [
|
|
3684
3812
|
"slots",
|
|
3685
3813
|
"locale",
|
|
3686
|
-
"theme"
|
|
3687
|
-
"settingsScope"
|
|
3814
|
+
"theme"
|
|
3688
3815
|
];
|
|
3689
3816
|
/**
|
|
3690
3817
|
* Register the Catppuccin dictionaries, the flavour themes (lazy — item JJ:
|
|
@@ -3762,7 +3889,7 @@ window.__ModuleLoader__.load({
|
|
|
3762
3889
|
};
|
|
3763
3890
|
ctx.effect(() => disposeThemeRegistrations, "catppuccin: flavour theme disposers");
|
|
3764
3891
|
const glass = new GlassLayer(ctx);
|
|
3765
|
-
const scope =
|
|
3892
|
+
const scope = createDurableScope(ctx);
|
|
3766
3893
|
const buildLocalState = () => ({
|
|
3767
3894
|
version: 1,
|
|
3768
3895
|
flavor: readFlavor(),
|
|
@@ -3876,7 +4003,7 @@ window.__ModuleLoader__.load({
|
|
|
3876
4003
|
const state = durableStateFromSnapshot(snapshot);
|
|
3877
4004
|
if (state === null) return;
|
|
3878
4005
|
const local = buildLocalState();
|
|
3879
|
-
if (snapshot
|
|
4006
|
+
if (!scope.hasUserLayer(snapshot)) {
|
|
3880
4007
|
if (!isDefaultState(local)) queuePersist();
|
|
3881
4008
|
return;
|
|
3882
4009
|
}
|