dsh-claude-style 0.11.0 → 0.11.1
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/CHANGELOG.md +14 -0
- package/README.en.md +14 -0
- package/README.md +11 -0
- package/docs/decisions/D49-visual-yield.md +30 -0
- package/docs/decisions/README.md +2 -1
- package/lib/client.js +180 -37
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `dsh-claude-style` are documented here, newest first.
|
|
4
4
|
|
|
5
|
+
## [0.11.1] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
[中文](#cn-0.11.1) | [English](#en-0.11.1)
|
|
8
|
+
|
|
9
|
+
<h3 id="cn-0.11.1">新增功能</h3>
|
|
10
|
+
|
|
11
|
+
- **与皮肤中心交接**:DSH 皮肤中心把本主题列为可选皮肤。选中它即把整页交给本插件,换选其他皮肤、官方默认或壁纸在跑时页面自动交还,无需刷新。判定读皮肤中心注入服务端 HTML 的 `html[data-dsh-skin]` 与壁纸插件的 `body[data-we-wallpaper]`,两者任一存在即让路:让路期间不挂样式表、不安装功能、不占用任何文档属性,只保留设置页与偏好。构建在 `body` 上盖 `data-dsh-claude-style-handoff` 标记自己支持让路。
|
|
12
|
+
|
|
13
|
+
<h3 id="en-0.11.1">New Features</h3>
|
|
14
|
+
|
|
15
|
+
- **Skin center handoff**: the DSH Skin Center offers this theme as a selectable skin. Selecting it hands the whole page to this plugin; selecting another skin, the official default, or a running wallpaper takes the page back without a reload. The verdict reads the `html[data-dsh-skin]` attribute the Skin Center injects into the served document and the wallpaper plugin's `body[data-we-wallpaper]`; either one means yielding, and while yielded the theme mounts no stylesheet, installs no feature and claims no document attribute, keeping only its settings page and preferences. A build stamps `data-dsh-claude-style-handoff` on `body` to say it can do this.
|
|
16
|
+
|
|
17
|
+
**Full Changelog**: [v0.11.0...v0.11.1](https://github.com/Nwflower/dsh-claude-style/compare/v0.11.0...v0.11.1)
|
|
18
|
+
|
|
5
19
|
## [0.11.0] - 2026-10-06
|
|
6
20
|
|
|
7
21
|
[中文](#cn-0.11.0) | [English](#en-0.11.0)
|
package/README.en.md
CHANGED
|
@@ -123,6 +123,20 @@ To enable the Anthropic fonts, choose one of the following:
|
|
|
123
123
|
|
|
124
124
|
> The Anthropic Sans and Serif fonts are the property of Anthropic, licensed for personal use only, and are not covered by this project's MIT license.
|
|
125
125
|
|
|
126
|
+
## Skin center handoff
|
|
127
|
+
|
|
128
|
+
This theme is also offered as a **skin** in the DSH Skin Center. Selecting it there hands the whole page to this
|
|
129
|
+
plugin; selecting anything else - another skin, the official default, or a running wallpaper - takes the page back
|
|
130
|
+
without a reload.
|
|
131
|
+
|
|
132
|
+
- The answer holds from the first frame: the Skin Center stamps `html[data-dsh-skin]` into the served document, so a
|
|
133
|
+
page that boots with a skin already painting never flashes a frame of this theme first.
|
|
134
|
+
- While another owner has the page this theme mounts no stylesheet, installs no feature and claims no document
|
|
135
|
+
attribute at all. It takes the page back on a real flip, and gives it back when an owner returns.
|
|
136
|
+
- The settings page and every stored preference survive the handoff. A skin taking the screen does not uninstall
|
|
137
|
+
anything, and closing the Skin Center does not throw away what you configured here.
|
|
138
|
+
- `data-dsh-claude-style-handoff` on `body` marks a build that can do this; the Skin Center offers the row only
|
|
139
|
+
while it is there.
|
|
126
140
|
## Installation
|
|
127
141
|
|
|
128
142
|
1. From the official plugin page, add the plugin below and it installs.
|
package/README.md
CHANGED
|
@@ -123,6 +123,17 @@ Anthropic 字体启用(二选一):
|
|
|
123
123
|
|
|
124
124
|
② 免安装——把 `.ttf` 复制到插件包的 `fonts/` 目录。完成后刷新页面生效。
|
|
125
125
|
|
|
126
|
+
## 皮肤中心交接
|
|
127
|
+
|
|
128
|
+
在 DSH 皮肤中心里,这套主题同时以**皮肤**的身份出现。选它即把整页交给本插件;换选别的皮肤、官方默认,
|
|
129
|
+
或者壁纸在渲染时,页面都会在不需要刷新的情况下交还回来。
|
|
130
|
+
|
|
131
|
+
- 判定从第一帧就成立:皮肤中心把 `html[data-dsh-skin]` 注入服务端 HTML,因此带着皮肤启动的页面不会先闪过
|
|
132
|
+
一帧本主题。
|
|
133
|
+
- 页面另有属主时,本主题不挂样式表、不安装任何功能、不占用任何文档属性;属主离开时立刻接管,属主回来
|
|
134
|
+
时再次让出。
|
|
135
|
+
- 交接期间设置页与所有偏好都还在:别的皮肤占屏不等于卸载,关掉皮肤中心也不丢这里配过的东西。
|
|
136
|
+
- `body` 上的 `data-dsh-claude-style-handoff` 标记支持交接的构建;皮肤中心只在这一属性存在时才提供该选项。
|
|
126
137
|
## 安装
|
|
127
138
|
|
|
128
139
|
1. 官方插件页,添加以下插件即可快速安装
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# D49. 让路:宿主另有属主时收掉整套视觉,留下设置页
|
|
2
|
+
|
|
3
|
+
- **状态**:已实施
|
|
4
|
+
- **关联**:D12、D29、D30、D32、D33、D40
|
|
5
|
+
- **迁移**:无
|
|
6
|
+
|
|
7
|
+
## 决定
|
|
8
|
+
|
|
9
|
+
- 两个属性回答「这一页现在归谁」:`html[data-dsh-skin]`(皮肤中心在皮肤作画时盖上,值是皮肤 id)与 `body[data-we-wallpaper]`(壁纸插件渲染壁纸期间盖上)。任一存在即让路。`src/shared/visual-owner.js` 读它们,`apply()` 开头同步读一次,之后由一个 MutationObserver 跟随真实翻转;判定每次重算,不跨翻转缓存。
|
|
10
|
+
- 让路时:保留 `setHostContext`、`adoptSettingsForm` 与 `ctx.inject(['configForms'])`、`loadModelCopy` / `loadUsername` / `loadHdsl`、`adoptPrefs`,只安装 `settings` 一个功能;不盖 `data-dsh-claude-style`、不挂样式表、不跑调度器。模块作用域的 `parkForeignSheets()` 照常运行(兄弟包的样式表不能等本皮肤)。
|
|
11
|
+
- 抢回页面时(`own()`):盖上 `data-dsh-claude-style` 与 `data-dsh-claude-style-handoff`、重新 `adoptPrefs`、挂样式表、装齐功能、装调度器;已在装的功能不重复安装。交还页面时(`release(keep)`):跑完每个功能的 teardown、撤掉本包在 body 上的全部属性与样式表,`keep` 名单里的功能留下。
|
|
12
|
+
- 宿主运行时收到 teardown 仍是唯一的一次性拆卸器:`teardown()` 先停归属观察,再 `release()`,再停对话插件观察与偏好绑定。
|
|
13
|
+
- `data-dsh-claude-style-handoff` 是能力标记:它在 `body` 上与存活标记同时出现,表示这一版能让路。皮肤中心据此才把本主题当作可选皮肤列出来;没有它的旧版本不会被选用。
|
|
14
|
+
|
|
15
|
+
## 理由
|
|
16
|
+
|
|
17
|
+
- 皮肤中心把它注入服务端 HTML,所以第一帧就有答案;而 D32 已经用同样的「两路信号 + 观察者」形状处理过另一个插件,重复发明一套形状不如沿用。
|
|
18
|
+
- 让路的粒度是整页而不是单个功能:只关掉功能表会漏掉样式表、无开关的功能、`adoptPrefs` 打在 body 上的属性,以及独立安装的调度器与座位键(D12 的隔离不等于整页交还)。
|
|
19
|
+
- 收掉视觉不是卸载:读者的设置页与偏好必须继续在,别人的皮肤不该顺手删掉本包的东西。
|
|
20
|
+
|
|
21
|
+
## 代价
|
|
22
|
+
|
|
23
|
+
- 在 D40 落地前多一个常驻观察者(D40 只豁免了 head 的 childList 那一个)。
|
|
24
|
+
- 让路期间整页惰性:设置页以宿主样式渲染,页面上的其他本包行为全部停摆。
|
|
25
|
+
- 翻转会带来一次可见的重新挂载;判定在观察者回调里重算,不缓存。
|
|
26
|
+
|
|
27
|
+
## 重审条件
|
|
28
|
+
|
|
29
|
+
- 宿主提供主题层接口时改接宿主的(D30 已带着这一条)。
|
|
30
|
+
- 皮肤中心提供可注入的主题服务时,判定改走服务,属性只留作第一帧的先验。
|
package/docs/decisions/README.md
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
| [D27](D27-context-stats.md) | 会话数字收进上下文弹层 | 已实施 |
|
|
63
63
|
| [D32](D32-chat-interactions.md) | 搬来的聊天区交互:自己的前缀、自己的偏好、对上游让位 | 已实施 |
|
|
64
64
|
| [D34](D34-turn-navigator.md) | 对话导航:皮肤画导航条,跳转按宿主自己的刻度 | 已实施 |
|
|
65
|
+
| [D49](D49-visual-yield.md) | 让路:宿主另有属主时收掉整套视觉,留下设置页 | 已实施 |
|
|
65
66
|
|
|
66
67
|
## 分发与流程
|
|
67
68
|
|
|
@@ -73,4 +74,4 @@
|
|
|
73
74
|
|
|
74
75
|
## 作废的编号
|
|
75
76
|
|
|
76
|
-
D1(由 D36 取代)、D2(由 D43 取代)、D6(由 D40 取代)、D8(由 D46 取代)、D13(由 D42 取代)、D18(由 D46 取代)。
|
|
77
|
+
D1(由 D36 取代)、D2(由 D43 取代)、D6(由 D40 取代)、D8(由 D46 取代)、D13(由 D42 取代)、D18(由 D46 取代)。
|
package/lib/client.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* - src/shared/chat-dom.js
|
|
22
22
|
* - src/shared/scroll-ease.js
|
|
23
23
|
* - src/shared/peer-plugin.js
|
|
24
|
+
* - src/shared/visual-owner.js
|
|
24
25
|
* - src/features/selection/selection.js
|
|
25
26
|
* - src/features/composer/composer.js
|
|
26
27
|
* - src/features/copy/copy.js
|
|
@@ -670,6 +671,26 @@ window.__ModuleLoader__.load({
|
|
|
670
671
|
* document and the stylesheet switches on this attribute.
|
|
671
672
|
*/
|
|
672
673
|
const WINDOW_BLUR_ATTR = 'data-dsh-window-blur'
|
|
674
|
+
/**
|
|
675
|
+
* The handoff marker: stamped on `body` while this build is live AND able
|
|
676
|
+
* to give the page back (D49). Its presence is a capability another
|
|
677
|
+
* package can read without running anything: a skin that lists this theme
|
|
678
|
+
* as one of its looks has to know the page can come back, and a build from
|
|
679
|
+
* before D49 does not stamp it.
|
|
680
|
+
*/
|
|
681
|
+
const HANDOFF_ATTR = 'data-dsh-claude-style-handoff'
|
|
682
|
+
/**
|
|
683
|
+
* The document attribute the skin center stamps while a skin is painting
|
|
684
|
+
* (D49). It is read at boot and watched afterwards: the value is the skin
|
|
685
|
+
* id, and the presence means the page belongs to a skin.
|
|
686
|
+
*/
|
|
687
|
+
const SKIN_STAMP_ATTR = 'data-dsh-skin'
|
|
688
|
+
/**
|
|
689
|
+
* The document attribute the delegated wallpaper plugin stamps while it
|
|
690
|
+
* renders (D49). Two plugins cannot own one shell, so a wallpaper outranks
|
|
691
|
+
* this theme exactly as a skin does.
|
|
692
|
+
*/
|
|
693
|
+
const WALLPAPER_ACTIVE_ATTR = 'data-we-wallpaper'
|
|
673
694
|
/**
|
|
674
695
|
* On the host's scroller around the settings page while the page is
|
|
675
696
|
* mounted (src/features/settings/settings.js): the stylesheet keeps the
|
|
@@ -8442,7 +8463,7 @@ window.__ModuleLoader__.load({
|
|
|
8442
8463
|
// ============================================================================
|
|
8443
8464
|
// 构建编号(由 scripts/build.mjs 按产物内容生成) (Build id)
|
|
8444
8465
|
// ============================================================================
|
|
8445
|
-
var BUILD_ID = '
|
|
8466
|
+
var BUILD_ID = '30e63fef2abb'
|
|
8446
8467
|
|
|
8447
8468
|
// ============================================================================
|
|
8448
8469
|
// Deepy 帧图内容戳(由 scripts/build.mjs 按帧图字节生成) (Deepy sheet stamps)
|
|
@@ -10208,6 +10229,62 @@ window.__ModuleLoader__.load({
|
|
|
10208
10229
|
}
|
|
10209
10230
|
}
|
|
10210
10231
|
|
|
10232
|
+
/**
|
|
10233
|
+
* The other owner of this page, and whether it is on it right now.
|
|
10234
|
+
*
|
|
10235
|
+
* Two documented attributes answer it, and either one is enough:
|
|
10236
|
+
*
|
|
10237
|
+
* html[data-dsh-skin] the skin center stamps it while a skin is
|
|
10238
|
+
* painting. The skin center injects it into
|
|
10239
|
+
* the served document, so the answer holds
|
|
10240
|
+
* from the first frame (D49).
|
|
10241
|
+
* body[data-we-wallpaper] the delegated wallpaper plugin stamps it while
|
|
10242
|
+
* a wallpaper renders (D49).
|
|
10243
|
+
*
|
|
10244
|
+
* The skin center does not stamp its attribute for this theme's own row:
|
|
10245
|
+
* selecting this theme means the page has no skin on it, which is what
|
|
10246
|
+
* leaves the page free for the theme to take.
|
|
10247
|
+
*/
|
|
10248
|
+
function externalOwnerActive() {
|
|
10249
|
+
const doc = document
|
|
10250
|
+
if (doc.documentElement?.hasAttribute(SKIN_STAMP_ATTR) === true) return true
|
|
10251
|
+
return doc.body?.hasAttribute(WALLPAPER_ACTIVE_ATTR) === true
|
|
10252
|
+
}
|
|
10253
|
+
|
|
10254
|
+
/**
|
|
10255
|
+
* Watch the other owner, and hear when it arrives or leaves.
|
|
10256
|
+
*
|
|
10257
|
+
* Both attributes are read; nothing is written. The callback fires once
|
|
10258
|
+
* immediately with the current answer, so a page that boots while a skin
|
|
10259
|
+
* or a wallpaper already owns it never paints a frame of this theme first,
|
|
10260
|
+
* then only on a real flip. The observer is the third exception to the
|
|
10261
|
+
* single-scheduler rule (D40): it waits on two attributes of the
|
|
10262
|
+
* document, outside this package's own subtree.
|
|
10263
|
+
*
|
|
10264
|
+
* @param {function(boolean): void} listener called with the new answer
|
|
10265
|
+
* @returns {function(): void} unsubscribe
|
|
10266
|
+
*/
|
|
10267
|
+
function subscribeExternalOwner(listener) {
|
|
10268
|
+
let last = externalOwnerActive()
|
|
10269
|
+
const observer = new MutationObserver(() => {
|
|
10270
|
+
const next = externalOwnerActive()
|
|
10271
|
+
if (next === last) return
|
|
10272
|
+
last = next
|
|
10273
|
+
listener(next)
|
|
10274
|
+
})
|
|
10275
|
+
observer.observe(document.documentElement, {
|
|
10276
|
+
attributes: true,
|
|
10277
|
+
attributeFilter: [SKIN_STAMP_ATTR],
|
|
10278
|
+
})
|
|
10279
|
+
if (document.body !== null) {
|
|
10280
|
+
observer.observe(document.body, {
|
|
10281
|
+
attributes: true,
|
|
10282
|
+
attributeFilter: [WALLPAPER_ACTIVE_ATTR],
|
|
10283
|
+
})
|
|
10284
|
+
}
|
|
10285
|
+
return () => observer.disconnect()
|
|
10286
|
+
}
|
|
10287
|
+
|
|
10211
10288
|
/**
|
|
10212
10289
|
* Text selection has two paints and CSS cannot tell them apart: gray on
|
|
10213
10290
|
* black while the browser window is unfocused, blue on white while it is
|
|
@@ -28246,35 +28323,72 @@ window.__ModuleLoader__.load({
|
|
|
28246
28323
|
let offSwitches = null
|
|
28247
28324
|
/** Keeps the other chat plugin's presence watch alive; set once the features install. */
|
|
28248
28325
|
let offPeerWatch = null
|
|
28326
|
+
/** Watches the other owner of the page (a skin, a wallpaper); D49. */
|
|
28327
|
+
let offOwnerWatch = null
|
|
28249
28328
|
/** Unmounts this generation's stylesheet; set once the sheet is mounted. */
|
|
28250
28329
|
let stopStylesheet = null
|
|
28251
28330
|
let disposed = false
|
|
28331
|
+
/** True while another plugin owns the page and this theme stands down (D49). */
|
|
28332
|
+
let yielded = externalOwnerActive()
|
|
28252
28333
|
|
|
28253
28334
|
/**
|
|
28254
|
-
*
|
|
28255
|
-
*
|
|
28256
|
-
*
|
|
28335
|
+
* Take the page: stamp, mount the sheet, install the features, schedule.
|
|
28336
|
+
*
|
|
28337
|
+
* Idempotent over what is already installed, so taking the page back
|
|
28338
|
+
* after a yield leaves the settings section alone (D49).
|
|
28339
|
+
*/
|
|
28340
|
+
function own() {
|
|
28341
|
+
body.setAttribute('data-dsh-claude-style', BUILD_ID)
|
|
28342
|
+
// The handoff marker rides the live stamp: its presence is what lets the
|
|
28343
|
+
// skin center offer this theme as a selectable look (D49).
|
|
28344
|
+
body.setAttribute(HANDOFF_ATTR, BUILD_ID)
|
|
28345
|
+
adoptPrefs(prefs)
|
|
28346
|
+
if (stopStylesheet === null) stopStylesheet = mountStylesheet()
|
|
28347
|
+
for (const feature of FEATURES) {
|
|
28348
|
+
if (installed.some(entry => entry.name === feature.name)) continue
|
|
28349
|
+
if (switched.includes(feature)) applySwitch(feature)
|
|
28350
|
+
else install(feature)
|
|
28351
|
+
}
|
|
28352
|
+
if (offSwitches === null) {
|
|
28353
|
+
offSwitches = subscribePrefs(() => {
|
|
28354
|
+
// A preference flip while yielded must not install a feature back
|
|
28355
|
+
// onto a page another owner is painting (D49).
|
|
28356
|
+
if (yielded) return
|
|
28357
|
+
for (const feature of switched) applySwitch(feature)
|
|
28358
|
+
if (typeof ui.schedule === 'function') ui.schedule()
|
|
28359
|
+
})
|
|
28360
|
+
}
|
|
28361
|
+
if (installed.some(entry => entry.name === 'scheduler')) return
|
|
28362
|
+
if (!install({ name: 'scheduler', install() { return installScheduler(ctx, ui, handleNames) } })) teardown()
|
|
28363
|
+
}
|
|
28364
|
+
|
|
28365
|
+
/**
|
|
28366
|
+
* Give the page back, keeping whatever the caller names.
|
|
28367
|
+
*
|
|
28368
|
+
* A yield keeps the settings section and the preference binding: the
|
|
28369
|
+
* reader must still reach the plugin's own page, and nothing is
|
|
28370
|
+
* uninstalled by another plugin taking the screen (D49).
|
|
28371
|
+
*
|
|
28372
|
+
* @param {Array<string>} keep install names that survive this call
|
|
28257
28373
|
*/
|
|
28258
|
-
function
|
|
28259
|
-
|
|
28260
|
-
disposed = true
|
|
28261
|
-
// First, so no preference flip installs a feature mid-teardown.
|
|
28374
|
+
function release(keep) {
|
|
28375
|
+
// First, so no preference flip installs a feature mid-release.
|
|
28262
28376
|
if (offSwitches !== null) {
|
|
28263
28377
|
offSwitches()
|
|
28264
28378
|
offSwitches = null
|
|
28265
28379
|
}
|
|
28266
|
-
|
|
28267
|
-
offPeerWatch()
|
|
28268
|
-
offPeerWatch = null
|
|
28269
|
-
}
|
|
28380
|
+
const kept = []
|
|
28270
28381
|
for (let i = installed.length - 1; i >= 0; i--) {
|
|
28382
|
+
if (keep !== undefined && keep.includes(installed[i].name)) {
|
|
28383
|
+
kept.push(installed[i])
|
|
28384
|
+
continue
|
|
28385
|
+
}
|
|
28271
28386
|
// One teardown must not block the rest (D12); a failing one is reported.
|
|
28272
28387
|
try { installed[i].stop() } catch (error) { reportError(error) }
|
|
28273
28388
|
}
|
|
28274
|
-
installed =
|
|
28275
|
-
setHostContext(null)
|
|
28276
|
-
disposePrefsBinding()
|
|
28389
|
+
installed = kept.reverse()
|
|
28277
28390
|
body.removeAttribute('data-dsh-claude-style')
|
|
28391
|
+
body.removeAttribute(HANDOFF_ATTR)
|
|
28278
28392
|
body.removeAttribute(BRAND_ATTR)
|
|
28279
28393
|
body.removeAttribute(PALETTE_ATTR)
|
|
28280
28394
|
body.removeAttribute(TYPEFACE_ATTR)
|
|
@@ -28287,7 +28401,31 @@ window.__ModuleLoader__.load({
|
|
|
28287
28401
|
body.removeAttribute(WINDOW_BLUR_ATTR)
|
|
28288
28402
|
// This generation's own sheet, handed over rather than taken away when a
|
|
28289
28403
|
// newer generation has mounted after it (mountStylesheet).
|
|
28290
|
-
if (stopStylesheet !== null)
|
|
28404
|
+
if (stopStylesheet !== null) {
|
|
28405
|
+
stopStylesheet()
|
|
28406
|
+
stopStylesheet = null
|
|
28407
|
+
}
|
|
28408
|
+
}
|
|
28409
|
+
|
|
28410
|
+
/**
|
|
28411
|
+
* Undo everything this generation installed. Idempotent: the host runs it
|
|
28412
|
+
* on dispose (the effect below), and own() runs it itself when the
|
|
28413
|
+
* scheduler cannot be installed.
|
|
28414
|
+
*/
|
|
28415
|
+
function teardown() {
|
|
28416
|
+
if (disposed) return
|
|
28417
|
+
disposed = true
|
|
28418
|
+
if (offOwnerWatch !== null) {
|
|
28419
|
+
offOwnerWatch()
|
|
28420
|
+
offOwnerWatch = null
|
|
28421
|
+
}
|
|
28422
|
+
release()
|
|
28423
|
+
if (offPeerWatch !== null) {
|
|
28424
|
+
offPeerWatch()
|
|
28425
|
+
offPeerWatch = null
|
|
28426
|
+
}
|
|
28427
|
+
setHostContext(null)
|
|
28428
|
+
disposePrefsBinding()
|
|
28291
28429
|
}
|
|
28292
28430
|
|
|
28293
28431
|
// Registered before anything is installed: registered last, a feature that
|
|
@@ -28373,9 +28511,6 @@ window.__ModuleLoader__.load({
|
|
|
28373
28511
|
}
|
|
28374
28512
|
}
|
|
28375
28513
|
|
|
28376
|
-
// The value is the build id (scripts/build.mjs): the stylesheet keys on
|
|
28377
|
-
// the attribute alone, and a live page reads which lib/client.js it runs.
|
|
28378
|
-
body.setAttribute('data-dsh-claude-style', BUILD_ID)
|
|
28379
28514
|
setHostContext(ctx)
|
|
28380
28515
|
// Bind the official settings form before anything reads a preference:
|
|
28381
28516
|
// the host serves namespaces through `ctx.configForms`. Bound once here,
|
|
@@ -28390,13 +28525,10 @@ window.__ModuleLoader__.load({
|
|
|
28390
28525
|
// Preferences are read asynchronously from the host settings namespace;
|
|
28391
28526
|
// applying the defaults first keeps every gated rule in a defined state
|
|
28392
28527
|
// for the frames before that read settles, and is exactly the shipped
|
|
28393
|
-
// behaviour when it never does.
|
|
28528
|
+
// behaviour when it never does. This runs while yielded too: the reader
|
|
28529
|
+
// must still reach the settings page and see their own values (D49).
|
|
28394
28530
|
adoptPrefs(prefs)
|
|
28395
28531
|
|
|
28396
|
-
// The sheet carries this package's own tags and is unmounted by the
|
|
28397
|
-
// teardown below; the features install onto a page that already wears it.
|
|
28398
|
-
stopStylesheet = mountStylesheet()
|
|
28399
|
-
|
|
28400
28532
|
/**
|
|
28401
28533
|
* Every feature the skin installs, in install order. `name` is the label
|
|
28402
28534
|
* the failure report and the teardown use; `handle` is the name it
|
|
@@ -28441,25 +28573,36 @@ window.__ModuleLoader__.load({
|
|
|
28441
28573
|
]
|
|
28442
28574
|
|
|
28443
28575
|
const switched = FEATURES.filter(feature => Object.hasOwn(FEATURE_PREF_DEFAULTS, feature.pref ?? ''))
|
|
28444
|
-
|
|
28445
|
-
|
|
28446
|
-
|
|
28447
|
-
}
|
|
28448
|
-
offSwitches = subscribePrefs(() => {
|
|
28449
|
-
for (const feature of switched) applySwitch(feature)
|
|
28450
|
-
if (typeof ui.schedule === 'function') ui.schedule()
|
|
28451
|
-
})
|
|
28576
|
+
// The scheduler's passes read the `ui` handles lazily, so its pass order is
|
|
28577
|
+
// the feature order, skipping handles that do not exist or have no `sync`.
|
|
28578
|
+
const handleNames = FEATURES.map(feature => feature.handle || feature.name)
|
|
28452
28579
|
// Keep the presence watch alive for the page's lifetime, whether or not a
|
|
28453
28580
|
// feature subscribes on its own: the ported chat features decide on it, and
|
|
28454
28581
|
// another plugin arriving or leaving re-runs the preference stream
|
|
28455
28582
|
// (src/shared/peer-plugin.js). The subscription itself carries no logic.
|
|
28456
28583
|
offPeerWatch = subscribePeerPresence(() => {})
|
|
28457
28584
|
|
|
28458
|
-
|
|
28459
|
-
|
|
28460
|
-
|
|
28461
|
-
|
|
28462
|
-
|
|
28585
|
+
if (yielded) {
|
|
28586
|
+
// Another owner already has this page (D49). The settings section is the
|
|
28587
|
+
// one feature that stays: the reader still has to reach this theme's own
|
|
28588
|
+
// page, and a skin taking the screen must not uninstall anything.
|
|
28589
|
+
const settings = FEATURES.find(feature => feature.name === 'settings')
|
|
28590
|
+
install(settings)
|
|
28591
|
+
// adoptPrefs above stamped this theme's own body attributes; a yielded
|
|
28592
|
+
// page carries none of them, so the release strips exactly what it
|
|
28593
|
+
// stamped and leaves the section alone.
|
|
28594
|
+
release(['settings'])
|
|
28595
|
+
// The owner can arrive later or leave; a flip re-runs the whole pass, and
|
|
28596
|
+
// the verdict is read again rather than remembered across the flip.
|
|
28597
|
+
offOwnerWatch = subscribeExternalOwner(active => {
|
|
28598
|
+
if (active === yielded) return
|
|
28599
|
+
yielded = active
|
|
28600
|
+
if (active) release(['settings'])
|
|
28601
|
+
else own()
|
|
28602
|
+
})
|
|
28603
|
+
return
|
|
28604
|
+
}
|
|
28605
|
+
own()
|
|
28463
28606
|
}
|
|
28464
28607
|
|
|
28465
28608
|
exports.apply = apply
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-claude-style",
|
|
3
3
|
"description": "Claude Code Desktop theme for DeepSeek Harness (dsh) web GUI — visual & interaction overhaul",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "host/index.js",
|
|
7
7
|
"icon": "lib/claude-mark.svg",
|