@khorsheed/dsh-context-guard 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.en.md +2 -1
- package/README.i18n.yaml +2 -2
- package/README.md +2 -1
- package/lib/client.js +320 -135
- package/lib/index.js +47 -18
- package/lib/types/client/CompactGuardButton.js +2 -2
- package/lib/types/client/SettingsCard.d.ts +11 -6
- package/lib/types/client/SettingsCard.js +50 -22
- package/lib/types/client/config.js +14 -1
- package/lib/types/client/icons.d.ts +19 -0
- package/lib/types/client/icons.js +3 -0
- package/lib/types/client/index.d.ts +22 -7
- package/lib/types/client/index.js +52 -10
- package/lib/types/client/scope.d.ts +62 -0
- package/lib/types/client/scope.js +59 -0
- package/lib/types/client/slots.d.ts +22 -11
- package/lib/types/index.d.ts +22 -8
- package/lib/types/index.js +32 -9
- package/lib/types/namespace.d.ts +2 -2
- package/lib/types/namespace.js +2 -2
- package/lib/types/settings.d.ts +16 -10
- package/lib/types/settings.js +19 -6
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +7 -21
- package/lib/client.js.map +0 -1
- package/lib/tsconfig.tsbuildinfo +0 -1
- package/lib/types/client/CompactGuardButton.d.ts.map +0 -1
- package/lib/types/client/SettingsCard.d.ts.map +0 -1
- package/lib/types/client/config.d.ts.map +0 -1
- package/lib/types/client/guard.d.ts.map +0 -1
- package/lib/types/client/index.d.ts.map +0 -1
- package/lib/types/client/locales.d.ts.map +0 -1
- package/lib/types/client/slots.d.ts.map +0 -1
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/invariant.d.ts.map +0 -1
- package/lib/types/namespace.d.ts.map +0 -1
- package/lib/types/settings.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.2.2(2026-09-26)
|
|
4
|
+
|
|
5
|
+
适配宿主 rc.1 线并实证 0.1.5/0.1.7 双线可用(0.1.5-rc.1 全量 boot 实证,2026-09-25)。
|
|
6
|
+
|
|
7
|
+
- **家族 bundle 形态下设置卡迁址**:随 `@khorsheed/dsh-bundle-conversation-toolbox` 安装时成员不再是 profile 直依,插件页不给成员开详情页——设置卡(阈值编辑)改注册到行级槽位 `plugins.row.config`(键 `…#context-guard`),从 bundle 详情页该行的「配置」入口打开;独立安装与 0.1.5 legacy 卡两臂不变
|
|
8
|
+
- 设置面双线:rc.1 走 entry Config(volatile 探测)+ `configForms`,0.1.5 留 `settings.register` + `settingsScope`;`SessionStatus` 新面迁移,卡注册改双臂(`settings.plugin.item` / `plugins.bundle.config`)
|
|
9
|
+
- 图标自持化:插件用到的 rc.1 图标图样由 `sync-icon-artwork` 生成器摊平进包内 `src/client/icons.tsx`(0.1.5 与 rc.1 的图标导出名零交集,外部化引用在 0.1.5 上是 undefined 会炸槽位);两条宿主线渲染同一份图样
|
|
10
|
+
- 插件清单展示元数据(`locale/*.json`):rc.1 宿主插件页的卡面标题/描述中文化
|
|
11
|
+
|
|
12
|
+
## 0.2.1(2026-09-11)
|
|
13
|
+
|
|
14
|
+
- 无功能变更:开发基线随仓内 pin 对齐官方 `0.1.5-rc.1` / cordis `4.0.2`(消除 4.0.1/4.0.2 双实例图分裂);peer 依赖范围与 minHost(`0.1.2-rc.1`)不变
|
|
15
|
+
|
|
3
16
|
## 0.2.0(2026-09-10)
|
|
4
17
|
|
|
5
18
|
适配宿主 0.1.2 线。
|
package/README.en.md
CHANGED
|
@@ -31,7 +31,7 @@ dsh plugin --profile web remove @khorsheed/dsh-context-guard
|
|
|
31
31
|
|
|
32
32
|
## Config
|
|
33
33
|
|
|
34
|
-
One tunable, editable in the GUI
|
|
34
|
+
One tunable, editable in the GUI and live with no restart — on 0.1.5 under Settings → Plugins ("压缩提醒时机 / Compaction reminder timing"); on 0.1.6-alpha.2+ in the bundle's own configuration section on its Plugins-page detail view.
|
|
35
35
|
|
|
36
36
|
| Key | Default | Meaning |
|
|
37
37
|
|---|---|---|
|
|
@@ -48,6 +48,7 @@ plugins:
|
|
|
48
48
|
## Compatibility
|
|
49
49
|
|
|
50
50
|
- npm release line (`@deepseek-ai/dsh@0.1.2-rc.1`): ✅ full — baseline moved to the 0.1.2-rc.1 API surface (single-arm 0.1.2 API consumption; the 0.1.1-rc.2 runtime arm is retired), full build+test green; minHost moves up to 0.1.2-rc.1 — older hosts stay on the previous release line.
|
|
51
|
+
- 0.1.6-alpha.2 pre-release line (`@deepseek-ai/dsh@0.1.6-alpha.2`): ✅ full — the `settings.plugin.item` slot is removed in the ui-settings-plugins refactor; the settings card moves to `plugins.bundle.config` (keyed by package name, the Plugins page drawing the title chrome); the dual-inject probe keeps the 0.1.5 card working, minHost unchanged.
|
|
51
52
|
- source line (deepseek-harness master): ✅ (verifiedHost: 0.1.2-rc.1)
|
|
52
53
|
|
|
53
54
|
**Version line mapping**: 0.2.0 and up support host `0.1.2-rc.1` and later; hosts on `0.1.0-rc.6` ~ `0.1.1-rc.2` stay on the 0.1.x release line (last release `0.1.0`).
|
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# last confirmed-consistent state. Both languages carry equal authority; after
|
|
3
3
|
# editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/context-guard/README.en.md
|
|
5
|
-
packages/context-guard/README.en.md:
|
|
6
|
-
packages/context-guard/README.md:
|
|
5
|
+
packages/context-guard/README.en.md: 2c1f5f4465d6f658a2f5ba18bf9707d7a1082443
|
|
6
|
+
packages/context-guard/README.md: 12a9ddd4fb4ee7d2b7466a85315c783e113b6fa7
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ dsh plugin --profile web remove @khorsheed/dsh-context-guard
|
|
|
31
31
|
|
|
32
32
|
## 配置
|
|
33
33
|
|
|
34
|
-
一个可调项,在 GUI
|
|
34
|
+
一个可调项,在 GUI 里改且实时生效,无需重启——0.1.5:设置 → 插件配置 →「压缩提醒时机 / Compaction reminder timing」;0.1.6-alpha.2 起:插件管理页 → 本包详情页的配置区。
|
|
35
35
|
|
|
36
36
|
| 键 | 默认值 | 含义 |
|
|
37
37
|
|---|---|---|
|
|
@@ -48,6 +48,7 @@ plugins:
|
|
|
48
48
|
## Compatibility
|
|
49
49
|
|
|
50
50
|
- npm 发布线(`@deepseek-ai/dsh@0.1.2-rc.1`):✅ 完整——基线迁移至 0.1.2-rc.1 API 面(单臂消费 0.1.2 API,0.1.1-rc.2 运行臂已退役),全量构建测试通过;minHost 前移至 0.1.2-rc.1,旧宿主请停留在旧发布线。
|
|
51
|
+
- 0.1.6-alpha.2 预发布线(`@deepseek-ai/dsh@0.1.6-alpha.2`):✅ 完整——`settings.plugin.item` 槽随 ui-settings-plugins 重构删除,设置卡改投 `plugins.bundle.config`(按包名 keyed,Plugins 页绘制标题后渲染本表单);双注入探测保持 0.1.5 线旧卡可用,minHost 不动。
|
|
51
52
|
- 源码线(deepseek-harness master):✅(verifiedHost: 0.1.2-rc.1)
|
|
52
53
|
|
|
53
54
|
**版本线对照**:0.2.0 起支持宿主 `0.1.2-rc.1` 及以后;宿主 `0.1.0-rc.6` ~ `0.1.1-rc.2` 的用户请停留在 0.1.x 发布线(末版 `0.1.0`)。
|
package/lib/client.js
CHANGED
|
@@ -5,14 +5,13 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
-
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
9
|
//#region src/namespace.ts
|
|
11
10
|
/**
|
|
12
11
|
* The settings namespace owned by the context-guard plugin. Plain string so
|
|
13
12
|
* both the host half (settings registration) and the browser half
|
|
14
|
-
* (settingsScope binding and the settings.plugin.item card key) import
|
|
15
|
-
* without dragging host-only dependencies into the client bundle.
|
|
13
|
+
* (settingsScope binding and the 0.1.5 settings.plugin.item card key) import
|
|
14
|
+
* it without dragging host-only dependencies into the client bundle.
|
|
16
15
|
*/
|
|
17
16
|
/** Settings namespace owned by the context-guard plugin. */
|
|
18
17
|
const CONTEXT_GUARD_NS = "context-guard";
|
|
@@ -25,6 +24,16 @@ window.__ModuleLoader__.load({
|
|
|
25
24
|
return Math.min(max, Math.max(min, value));
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
27
|
+
* Unwrap one maybe-Volatile config field. rc.1 resolves volatile-marked
|
|
28
|
+
* Config fields into live references (`{ get() }`); 0.1.5 passes the plain
|
|
29
|
+
* value. The schema's volatile marker is probed per line (see
|
|
30
|
+
* ../settings.ts), so BOTH shapes arrive depending on the host line.
|
|
31
|
+
*/
|
|
32
|
+
function unwrapVolatile(value) {
|
|
33
|
+
if (value !== null && typeof value === "object" && typeof value.get === "function") return value.get();
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
28
37
|
* Normalize the entry config into the full {@link ContextGuardConfig}: every
|
|
29
38
|
* omitted field takes its documented default and every numeric field is
|
|
30
39
|
* clamped into its legal range.
|
|
@@ -32,7 +41,8 @@ window.__ModuleLoader__.load({
|
|
|
32
41
|
* @returns the effective guard behavior.
|
|
33
42
|
*/
|
|
34
43
|
function resolveConfig(config) {
|
|
35
|
-
|
|
44
|
+
const ratio = unwrapVolatile(config?.thresholdRatio);
|
|
45
|
+
return { thresholdRatio: clamp(typeof ratio === "number" ? ratio : .8, THRESHOLD_RATIO_MIN, THRESHOLD_RATIO_MAX) };
|
|
36
46
|
}
|
|
37
47
|
//#endregion
|
|
38
48
|
//#region src/client/locales.ts
|
|
@@ -76,6 +86,106 @@ window.__ModuleLoader__.load({
|
|
|
76
86
|
"settings.error": "Save failed, please retry"
|
|
77
87
|
};
|
|
78
88
|
//#endregion
|
|
89
|
+
//#region src/client/scope.ts
|
|
90
|
+
/** Published while no settings provider serves the section (stable identity). */
|
|
91
|
+
const UNAVAILABLE = {
|
|
92
|
+
status: "unavailable",
|
|
93
|
+
value: void 0,
|
|
94
|
+
base: void 0,
|
|
95
|
+
user: void 0,
|
|
96
|
+
revision: void 0,
|
|
97
|
+
writable: false
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Observable mirror of the bound scope. The slot entries' `hooks.config` seat
|
|
101
|
+
* binds to this at registration time — before (and whether or not) a settings
|
|
102
|
+
* provider appears — and arming swaps the source in and republishes.
|
|
103
|
+
*/
|
|
104
|
+
var GuardScopeChannel = class {
|
|
105
|
+
scope;
|
|
106
|
+
listeners = /* @__PURE__ */ new Set();
|
|
107
|
+
getSnapshot = () => this.scope?.getSnapshot() ?? UNAVAILABLE;
|
|
108
|
+
subscribe = (listener) => {
|
|
109
|
+
this.listeners.add(listener);
|
|
110
|
+
return () => {
|
|
111
|
+
this.listeners.delete(listener);
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
set = (field, value) => this.scope?.set(field, value) ?? Promise.resolve(false);
|
|
115
|
+
unset = (field) => this.scope?.unset(field) ?? Promise.resolve(false);
|
|
116
|
+
/** Bind the line-served scope; the first arm wins (the two lines never coexist). */
|
|
117
|
+
arm(scope) {
|
|
118
|
+
if (this.scope !== void 0) return;
|
|
119
|
+
this.scope = scope;
|
|
120
|
+
scope.subscribe(() => {
|
|
121
|
+
this.emit();
|
|
122
|
+
});
|
|
123
|
+
this.emit();
|
|
124
|
+
}
|
|
125
|
+
emit() {
|
|
126
|
+
for (const listener of this.listeners) listener();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Arm the channel from whichever settings face the host line serves: rc.1's
|
|
131
|
+
* `configForms.get(entryId)` — the entry id IS the settings namespace, both
|
|
132
|
+
* being the row id — first, 0.1.5's `settingsScope.bind({ namespace })`
|
|
133
|
+
* second. Both probes ride deferred injects so a later-mounting provider
|
|
134
|
+
* still lands; a composition with neither leaves the channel unavailable.
|
|
135
|
+
* @param ctx - the client plugin context.
|
|
136
|
+
* @param channel - the observable the slot entries read.
|
|
137
|
+
*/
|
|
138
|
+
function bindGuardScope(ctx, channel) {
|
|
139
|
+
ctx.inject(["configForms"], (formsCtx) => {
|
|
140
|
+
const forms = formsCtx.get("configForms");
|
|
141
|
+
if (typeof forms?.get === "function") channel.arm(forms.get(CONTEXT_GUARD_NS));
|
|
142
|
+
});
|
|
143
|
+
ctx.inject(["settingsScope"], (legacyCtx) => {
|
|
144
|
+
const legacy = legacyCtx.get("settingsScope");
|
|
145
|
+
if (typeof legacy?.bind === "function") channel.arm(legacy.bind({ namespace: CONTEXT_GUARD_NS }));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/client/icons.tsx
|
|
150
|
+
const IconChevronDownOutlineMedium = ({ size = 14, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
151
|
+
width: size,
|
|
152
|
+
height: size,
|
|
153
|
+
className,
|
|
154
|
+
viewBox: "0 0 16 16",
|
|
155
|
+
fill: "none",
|
|
156
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
157
|
+
"aria-hidden": "true",
|
|
158
|
+
strokeWidth: 1.3,
|
|
159
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
160
|
+
d: "M4 6L7.29289 9.29289C7.68342 9.68342 8.31658 9.68342 8.70711 9.29289L12 6",
|
|
161
|
+
stroke: "currentColor"
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
const IconWarningOutlineMedium = ({ size = 16, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
165
|
+
width: size,
|
|
166
|
+
height: size,
|
|
167
|
+
className,
|
|
168
|
+
viewBox: "0 0 16 16",
|
|
169
|
+
fill: "none",
|
|
170
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
171
|
+
"aria-hidden": "true",
|
|
172
|
+
strokeWidth: 1.3,
|
|
173
|
+
children: [
|
|
174
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
175
|
+
d: "M8 14.5C11.5899 14.5 14.5 11.5899 14.5 8C14.5 4.41015 11.5899 1.5 8 1.5C4.41015 1.5 1.5 4.41015 1.5 8C1.5 11.5899 4.41015 14.5 8 14.5Z",
|
|
176
|
+
stroke: "currentColor"
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
179
|
+
d: "M8 4.29199V9.79199",
|
|
180
|
+
stroke: "currentColor"
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
183
|
+
d: "M8 10.708V11.708",
|
|
184
|
+
stroke: "currentColor"
|
|
185
|
+
})
|
|
186
|
+
]
|
|
187
|
+
});
|
|
188
|
+
//#endregion
|
|
79
189
|
//#region src/client/guard.ts
|
|
80
190
|
/**
|
|
81
191
|
* Decide the guard level for one request budget.
|
|
@@ -100,7 +210,7 @@ window.__ModuleLoader__.load({
|
|
|
100
210
|
}
|
|
101
211
|
//#endregion
|
|
102
212
|
//#region \0dsh-css:src/client/CompactGuardButton.module.css.mjs
|
|
103
|
-
const css$1 = ".
|
|
213
|
+
const css$1 = ".X5Dx0q_wrap{align-items:center;gap:6px;display:inline-flex}.X5Dx0q_button{cursor:pointer;border:none;border-radius:999px;align-items:center;gap:4px;padding:2px 8px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.X5Dx0q_button:focus-visible{outline-offset:2px;outline:2px solid}.X5Dx0q_button:disabled{opacity:.6;cursor:default}.X5Dx0q_icon{color:currentColor;align-items:center;display:inline-flex}.X5Dx0q_warning{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label)}.X5Dx0q_warning:hover:not(:disabled){color:var(--dsw-alias-state-warn-primary)}.X5Dx0q_error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}";
|
|
104
214
|
const tagId$1 = "@khorsheed/dsh-context-guard/CompactGuardButton.module.css";
|
|
105
215
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
106
216
|
const tag = document.createElement("style");
|
|
@@ -110,11 +220,11 @@ window.__ModuleLoader__.load({
|
|
|
110
220
|
document.head.appendChild(tag);
|
|
111
221
|
}
|
|
112
222
|
var CompactGuardButton_module_css_default = {
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
223
|
+
"icon": "X5Dx0q_icon",
|
|
224
|
+
"button": "X5Dx0q_button",
|
|
225
|
+
"wrap": "X5Dx0q_wrap",
|
|
226
|
+
"error": "X5Dx0q_error",
|
|
227
|
+
"warning": "X5Dx0q_warning"
|
|
118
228
|
};
|
|
119
229
|
//#endregion
|
|
120
230
|
//#region src/client/CompactGuardButton.tsx
|
|
@@ -170,7 +280,7 @@ window.__ModuleLoader__.load({
|
|
|
170
280
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
171
281
|
className: CompactGuardButton_module_css_default.icon,
|
|
172
282
|
"aria-hidden": true,
|
|
173
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
283
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutlineMedium, { size: 14 })
|
|
174
284
|
}), t("button.label")]
|
|
175
285
|
}), error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
176
286
|
className: CompactGuardButton_module_css_default.error,
|
|
@@ -182,7 +292,7 @@ window.__ModuleLoader__.load({
|
|
|
182
292
|
}
|
|
183
293
|
//#endregion
|
|
184
294
|
//#region \0dsh-css:src/client/SettingsCard.module.css.mjs
|
|
185
|
-
const css = ".
|
|
295
|
+
const css = ".MRoRmW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.MRoRmW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.MRoRmW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.MRoRmW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.MRoRmW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.MRoRmW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.MRoRmW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.MRoRmW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.MRoRmW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.MRoRmW_chevronOpen{transform:rotate(180deg)}.MRoRmW_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:12px;margin:0 16px;padding:12px 0 8px;display:flex}.MRoRmW_field{flex-direction:column;gap:6px;display:flex}.MRoRmW_fieldHead{align-items:center;gap:8px;display:flex}.MRoRmW_fieldName{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.4}.MRoRmW_badge{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);border-radius:999px;padding:2px 6px;font-size:11px;line-height:1}.MRoRmW_inputRow{align-items:center;gap:8px;display:flex}.MRoRmW_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:140px;color:var(--dsw-alias-label-primary);border-radius:8px;padding:4px 8px;font-size:13px;line-height:1.4}.MRoRmW_input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.MRoRmW_inputInvalid{border-color:var(--dsw-alias-state-error-primary)}.MRoRmW_reset{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;padding:2px 6px;font-size:12px;line-height:1.4}.MRoRmW_reset:hover{color:var(--dsw-alias-label-primary)}.MRoRmW_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.MRoRmW_footer{justify-content:flex-end;align-items:center;gap:8px;margin-top:4px;display:flex}.MRoRmW_status{flex:1;min-width:0}.MRoRmW_saved{color:var(--dsw-alias-state-success-primary);font-size:12px}.MRoRmW_errorText{color:var(--dsw-alias-state-error-primary);font-size:12px}.MRoRmW_secondary,.MRoRmW_save{appearance:none;border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:4px 12px;font-size:13px;line-height:1.4}.MRoRmW_secondary:disabled,.MRoRmW_save:disabled{opacity:.5;cursor:default}.MRoRmW_save{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.MRoRmW_save:hover:not(:disabled){filter:brightness(.95)}";
|
|
186
296
|
const tagId = "@khorsheed/dsh-context-guard/SettingsCard.module.css";
|
|
187
297
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
188
298
|
const tag = document.createElement("style");
|
|
@@ -192,30 +302,30 @@ window.__ModuleLoader__.load({
|
|
|
192
302
|
document.head.appendChild(tag);
|
|
193
303
|
}
|
|
194
304
|
var SettingsCard_module_css_default = {
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
"
|
|
210
|
-
"
|
|
211
|
-
"
|
|
212
|
-
"
|
|
213
|
-
"
|
|
214
|
-
"
|
|
215
|
-
"
|
|
216
|
-
"
|
|
217
|
-
"
|
|
218
|
-
"
|
|
305
|
+
"fieldName": "MRoRmW_fieldName",
|
|
306
|
+
"headText": "MRoRmW_headText",
|
|
307
|
+
"description": "MRoRmW_description",
|
|
308
|
+
"fieldHead": "MRoRmW_fieldHead",
|
|
309
|
+
"name": "MRoRmW_name",
|
|
310
|
+
"badge": "MRoRmW_badge",
|
|
311
|
+
"inputInvalid": "MRoRmW_inputInvalid",
|
|
312
|
+
"saved": "MRoRmW_saved",
|
|
313
|
+
"input": "MRoRmW_input",
|
|
314
|
+
"body": "MRoRmW_body",
|
|
315
|
+
"hint": "MRoRmW_hint",
|
|
316
|
+
"status": "MRoRmW_status",
|
|
317
|
+
"field": "MRoRmW_field",
|
|
318
|
+
"inputRow": "MRoRmW_inputRow",
|
|
319
|
+
"footer": "MRoRmW_footer",
|
|
320
|
+
"errorText": "MRoRmW_errorText",
|
|
321
|
+
"card": "MRoRmW_card",
|
|
322
|
+
"reset": "MRoRmW_reset",
|
|
323
|
+
"save": "MRoRmW_save",
|
|
324
|
+
"header": "MRoRmW_header",
|
|
325
|
+
"secondary": "MRoRmW_secondary",
|
|
326
|
+
"chevron": "MRoRmW_chevron",
|
|
327
|
+
"cardOpen": "MRoRmW_cardOpen",
|
|
328
|
+
"chevronOpen": "MRoRmW_chevronOpen"
|
|
219
329
|
};
|
|
220
330
|
//#endregion
|
|
221
331
|
//#region src/client/SettingsCard.tsx
|
|
@@ -229,18 +339,11 @@ window.__ModuleLoader__.load({
|
|
|
229
339
|
return snapshot.user !== void 0 && typeof snapshot.user === "object" && snapshot.user !== null && field in snapshot.user;
|
|
230
340
|
}
|
|
231
341
|
/**
|
|
232
|
-
* The
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
* re-implemented here because the client bundle-purity gate forbids
|
|
236
|
-
* value-importing the official card chrome. Two staged number fields write
|
|
237
|
-
* through the shared settingsScope on save; per-field reset reverts a field
|
|
238
|
-
* to the composition layer.
|
|
342
|
+
* The staged threshold edit both settings surfaces share. Two staged number
|
|
343
|
+
* fields write through the shared settings scope on save; per-field reset
|
|
344
|
+
* reverts a field to the composition layer.
|
|
239
345
|
*/
|
|
240
|
-
function
|
|
241
|
-
const snapshot = useConfig((value) => value);
|
|
242
|
-
const value = snapshot.status === "ready" ? snapshot.value : void 0;
|
|
243
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
346
|
+
function useThresholdDraft(scope, snapshot, value) {
|
|
244
347
|
const [threshold, setThreshold] = (0, react.useState)({
|
|
245
348
|
text: "",
|
|
246
349
|
invalid: false
|
|
@@ -265,7 +368,6 @@ window.__ModuleLoader__.load({
|
|
|
265
368
|
aliveRef.current = false;
|
|
266
369
|
};
|
|
267
370
|
}, [value, snapshot.revision]);
|
|
268
|
-
if (snapshot.status === "unavailable") return null;
|
|
269
371
|
const write = (draft, min, max) => {
|
|
270
372
|
const parsed = parseNumber(draft.text);
|
|
271
373
|
setThreshold({
|
|
@@ -294,8 +396,116 @@ window.__ModuleLoader__.load({
|
|
|
294
396
|
if (aliveRef.current) setError(true);
|
|
295
397
|
});
|
|
296
398
|
};
|
|
399
|
+
const discard = () => {
|
|
400
|
+
if (value === void 0) return;
|
|
401
|
+
setThreshold({
|
|
402
|
+
text: String(value.thresholdRatio),
|
|
403
|
+
invalid: false
|
|
404
|
+
});
|
|
405
|
+
setSaved(false);
|
|
406
|
+
setError(false);
|
|
407
|
+
};
|
|
408
|
+
return {
|
|
409
|
+
threshold,
|
|
410
|
+
saved,
|
|
411
|
+
error,
|
|
412
|
+
dirty: threshold.text !== String(value?.thresholdRatio ?? ""),
|
|
413
|
+
write,
|
|
414
|
+
save,
|
|
415
|
+
reset,
|
|
416
|
+
discard
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/** The threshold field row and the save/discard footer both settings surfaces render. */
|
|
420
|
+
function SettingsBody({ snapshot, draft, t }) {
|
|
421
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
422
|
+
className: SettingsCard_module_css_default.body,
|
|
423
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
424
|
+
className: SettingsCard_module_css_default.field,
|
|
425
|
+
children: [
|
|
426
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
427
|
+
className: SettingsCard_module_css_default.fieldHead,
|
|
428
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
429
|
+
className: SettingsCard_module_css_default.fieldName,
|
|
430
|
+
children: t("settings.field.threshold")
|
|
431
|
+
}), overridden(snapshot, "thresholdRatio") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
432
|
+
className: SettingsCard_module_css_default.badge,
|
|
433
|
+
children: t("settings.overridden")
|
|
434
|
+
})]
|
|
435
|
+
}),
|
|
436
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
437
|
+
className: SettingsCard_module_css_default.inputRow,
|
|
438
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
439
|
+
className: draft.threshold.invalid ? `${SettingsCard_module_css_default.input} ${SettingsCard_module_css_default.inputInvalid}` : SettingsCard_module_css_default.input,
|
|
440
|
+
type: "number",
|
|
441
|
+
step: "0.01",
|
|
442
|
+
min: "0.01",
|
|
443
|
+
max: "1",
|
|
444
|
+
value: draft.threshold.text,
|
|
445
|
+
"aria-label": t("settings.field.threshold"),
|
|
446
|
+
onChange: (event) => {
|
|
447
|
+
draft.write({
|
|
448
|
+
text: event.target.value,
|
|
449
|
+
invalid: false
|
|
450
|
+
}, .01, 1);
|
|
451
|
+
}
|
|
452
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
453
|
+
type: "button",
|
|
454
|
+
className: SettingsCard_module_css_default.reset,
|
|
455
|
+
onClick: draft.reset,
|
|
456
|
+
children: t("settings.reset")
|
|
457
|
+
})]
|
|
458
|
+
}),
|
|
459
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
460
|
+
className: SettingsCard_module_css_default.hint,
|
|
461
|
+
children: t("settings.field.threshold.hint")
|
|
462
|
+
})
|
|
463
|
+
]
|
|
464
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
465
|
+
className: SettingsCard_module_css_default.footer,
|
|
466
|
+
children: [
|
|
467
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
468
|
+
className: SettingsCard_module_css_default.status,
|
|
469
|
+
children: [draft.saved ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
470
|
+
className: SettingsCard_module_css_default.saved,
|
|
471
|
+
children: t("settings.saved")
|
|
472
|
+
}) : null, draft.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
473
|
+
className: SettingsCard_module_css_default.errorText,
|
|
474
|
+
children: t("settings.error")
|
|
475
|
+
}) : null]
|
|
476
|
+
}),
|
|
477
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
478
|
+
type: "button",
|
|
479
|
+
className: SettingsCard_module_css_default.secondary,
|
|
480
|
+
disabled: !draft.dirty,
|
|
481
|
+
onClick: draft.discard,
|
|
482
|
+
children: t("settings.discard")
|
|
483
|
+
}),
|
|
484
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
485
|
+
type: "button",
|
|
486
|
+
className: SettingsCard_module_css_default.save,
|
|
487
|
+
disabled: !draft.dirty || draft.threshold.invalid || snapshot.writable !== true,
|
|
488
|
+
onClick: draft.save,
|
|
489
|
+
children: t("settings.save")
|
|
490
|
+
})
|
|
491
|
+
]
|
|
492
|
+
})]
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* The context-guard settings card in the 0.1.5 plugin configuration tab: the
|
|
497
|
+
* same collapsible chrome the official plugin cards use (a header naming the
|
|
498
|
+
* plugin and what its settings govern, disclosing the controls in place),
|
|
499
|
+
* re-implemented here because the client bundle-purity gate forbids
|
|
500
|
+
* value-importing the official card chrome.
|
|
501
|
+
*/
|
|
502
|
+
function ContextGuardSettingsCard({ useConfig, scope, t }) {
|
|
503
|
+
const snapshot = useConfig((value) => value);
|
|
504
|
+
const value = snapshot.status === "ready" ? snapshot.value : void 0;
|
|
505
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
506
|
+
const draft = useThresholdDraft(scope, snapshot, value);
|
|
507
|
+
if (snapshot.status === "unavailable") return null;
|
|
297
508
|
const title = t("settings.title");
|
|
298
|
-
const dirty = threshold.text !== String(value?.thresholdRatio ?? "");
|
|
299
509
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
300
510
|
className: open ? `${SettingsCard_module_css_default.card} ${SettingsCard_module_css_default.cardOpen}` : SettingsCard_module_css_default.card,
|
|
301
511
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -315,99 +525,54 @@ window.__ModuleLoader__.load({
|
|
|
315
525
|
className: SettingsCard_module_css_default.description,
|
|
316
526
|
children: t("settings.description")
|
|
317
527
|
})]
|
|
318
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
319
|
-
}), open && value !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
children: [
|
|
324
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
325
|
-
className: SettingsCard_module_css_default.fieldHead,
|
|
326
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
327
|
-
className: SettingsCard_module_css_default.fieldName,
|
|
328
|
-
children: t("settings.field.threshold")
|
|
329
|
-
}), overridden(snapshot, "thresholdRatio") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
330
|
-
className: SettingsCard_module_css_default.badge,
|
|
331
|
-
children: t("settings.overridden")
|
|
332
|
-
})]
|
|
333
|
-
}),
|
|
334
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
335
|
-
className: SettingsCard_module_css_default.inputRow,
|
|
336
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
337
|
-
className: threshold.invalid ? `${SettingsCard_module_css_default.input} ${SettingsCard_module_css_default.inputInvalid}` : SettingsCard_module_css_default.input,
|
|
338
|
-
type: "number",
|
|
339
|
-
step: "0.01",
|
|
340
|
-
min: "0.01",
|
|
341
|
-
max: "1",
|
|
342
|
-
value: threshold.text,
|
|
343
|
-
"aria-label": t("settings.field.threshold"),
|
|
344
|
-
onChange: (event) => {
|
|
345
|
-
write({
|
|
346
|
-
text: event.target.value,
|
|
347
|
-
invalid: false
|
|
348
|
-
}, .01, 1);
|
|
349
|
-
}
|
|
350
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
351
|
-
type: "button",
|
|
352
|
-
className: SettingsCard_module_css_default.reset,
|
|
353
|
-
onClick: reset,
|
|
354
|
-
children: t("settings.reset")
|
|
355
|
-
})]
|
|
356
|
-
}),
|
|
357
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
358
|
-
className: SettingsCard_module_css_default.hint,
|
|
359
|
-
children: t("settings.field.threshold.hint")
|
|
360
|
-
})
|
|
361
|
-
]
|
|
362
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
363
|
-
className: SettingsCard_module_css_default.footer,
|
|
364
|
-
children: [
|
|
365
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
366
|
-
className: SettingsCard_module_css_default.status,
|
|
367
|
-
children: [saved ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
368
|
-
className: SettingsCard_module_css_default.saved,
|
|
369
|
-
children: t("settings.saved")
|
|
370
|
-
}) : null, error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
371
|
-
className: SettingsCard_module_css_default.errorText,
|
|
372
|
-
children: t("settings.error")
|
|
373
|
-
}) : null]
|
|
374
|
-
}),
|
|
375
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
376
|
-
type: "button",
|
|
377
|
-
className: SettingsCard_module_css_default.secondary,
|
|
378
|
-
disabled: !dirty,
|
|
379
|
-
onClick: () => {
|
|
380
|
-
if (value === void 0) return;
|
|
381
|
-
setThreshold({
|
|
382
|
-
text: String(value.thresholdRatio),
|
|
383
|
-
invalid: false
|
|
384
|
-
});
|
|
385
|
-
setSaved(false);
|
|
386
|
-
setError(false);
|
|
387
|
-
},
|
|
388
|
-
children: t("settings.discard")
|
|
389
|
-
}),
|
|
390
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
391
|
-
type: "button",
|
|
392
|
-
className: SettingsCard_module_css_default.save,
|
|
393
|
-
disabled: !dirty || threshold.invalid || snapshot.writable !== true,
|
|
394
|
-
onClick: save,
|
|
395
|
-
children: t("settings.save")
|
|
396
|
-
})
|
|
397
|
-
]
|
|
398
|
-
})]
|
|
528
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutlineMedium, { className: open ? `${SettingsCard_module_css_default.chevron} ${SettingsCard_module_css_default.chevronOpen}` : SettingsCard_module_css_default.chevron })]
|
|
529
|
+
}), open && value !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsBody, {
|
|
530
|
+
snapshot,
|
|
531
|
+
draft,
|
|
532
|
+
t
|
|
399
533
|
})]
|
|
400
534
|
});
|
|
401
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* The bundle's configuration on the alpha.2 Plugins page
|
|
538
|
+
* (`plugins.bundle.config`, keyed by package name): the page draws the title,
|
|
539
|
+
* the icon, and the crumb itself, so the entry renders the one-liner for the
|
|
540
|
+
* `summary` view and the bare form — with its own save control — for `page`.
|
|
541
|
+
*/
|
|
542
|
+
function ContextGuardBundleConfig({ view, useConfig, scope, t }) {
|
|
543
|
+
const snapshot = useConfig((value) => value);
|
|
544
|
+
const value = snapshot.status === "ready" ? snapshot.value : void 0;
|
|
545
|
+
const draft = useThresholdDraft(scope, snapshot, value);
|
|
546
|
+
if (view === "summary") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
547
|
+
className: SettingsCard_module_css_default.description,
|
|
548
|
+
children: t("settings.description")
|
|
549
|
+
});
|
|
550
|
+
if (snapshot.status === "unavailable" || value === void 0) return null;
|
|
551
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsBody, {
|
|
552
|
+
snapshot,
|
|
553
|
+
draft,
|
|
554
|
+
t
|
|
555
|
+
});
|
|
556
|
+
}
|
|
402
557
|
//#endregion
|
|
403
558
|
//#region src/client/index.ts
|
|
404
|
-
/**
|
|
559
|
+
/**
|
|
560
|
+
* This bundle's package name — the key the Plugins page dispatches
|
|
561
|
+
* `plugins.bundle.config` entries on (identity triangle: cordis.patch.yml,
|
|
562
|
+
* tsdown.config.ts, invariant.ts).
|
|
563
|
+
*/
|
|
564
|
+
const PACKAGE_NAME = "@khorsheed/dsh-context-guard";
|
|
565
|
+
/**
|
|
566
|
+
* Required services: the slot ledger, the command Remote, and the copy. The
|
|
567
|
+
* settings scope is deliberately NOT injected — rc.1 and 0.1.5 name different
|
|
568
|
+
* services (`configForms` vs `settingsScope`), and a composition without
|
|
569
|
+
* either must not pend the bundle (see ./scope.ts).
|
|
570
|
+
*/
|
|
405
571
|
const inject = [
|
|
406
572
|
"slots",
|
|
407
573
|
"remote",
|
|
408
574
|
"remote.commands",
|
|
409
|
-
"locale"
|
|
410
|
-
"settingsScope"
|
|
575
|
+
"locale"
|
|
411
576
|
];
|
|
412
577
|
/**
|
|
413
578
|
* Client plugin body: register the composer-tool-row compact button and the
|
|
@@ -421,7 +586,8 @@ window.__ModuleLoader__.load({
|
|
|
421
586
|
zh,
|
|
422
587
|
en
|
|
423
588
|
}), "context-guard: dictionaries");
|
|
424
|
-
const scope =
|
|
589
|
+
const scope = new GuardScopeChannel();
|
|
590
|
+
bindGuardScope(ctx, scope);
|
|
425
591
|
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
426
592
|
name: "conversation.input.right",
|
|
427
593
|
id: "context-guard",
|
|
@@ -438,7 +604,26 @@ window.__ModuleLoader__.load({
|
|
|
438
604
|
hooks: { config: scope }
|
|
439
605
|
})
|
|
440
606
|
}, CompactGuardButton));
|
|
441
|
-
ctx.slots.inject("
|
|
607
|
+
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
608
|
+
name: "plugins.bundle.config",
|
|
609
|
+
key: PACKAGE_NAME,
|
|
610
|
+
locale: NS,
|
|
611
|
+
inject: () => ({
|
|
612
|
+
scope,
|
|
613
|
+
hooks: { config: scope }
|
|
614
|
+
})
|
|
615
|
+
}, ContextGuardBundleConfig));
|
|
616
|
+
ctx.slots.inject("plugins.row.config", () => ctx.slots.register({
|
|
617
|
+
name: "plugins.row.config",
|
|
618
|
+
key: "@khorsheed/dsh-bundle-conversation-toolbox#context-guard",
|
|
619
|
+
locale: NS,
|
|
620
|
+
inject: () => ({
|
|
621
|
+
scope,
|
|
622
|
+
hooks: { config: scope }
|
|
623
|
+
})
|
|
624
|
+
}, ContextGuardBundleConfig));
|
|
625
|
+
const legacy = ctx.slots;
|
|
626
|
+
legacy.inject("settings.plugin.item", () => legacy.register({
|
|
442
627
|
name: "settings.plugin.item",
|
|
443
628
|
key: CONTEXT_GUARD_NS,
|
|
444
629
|
locale: NS,
|