@deepseek-ai/dsh-client-ui-settings 0.0.1-rc.1 → 0.0.1-rc.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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-settings/README.md
5
- README.md: 785f0417f00ec8eb1f8c9273b4d81f8ca5ca1810
6
- README.zh.md: 239987e379aaabd2edbfc9e09cd2fc8cef7a685e
5
+ README.md: 8bf085fd02e3c76148674065bb4f5708e9a6e8d8
6
+ README.zh.md: f7b6f18809c64be6830ea23c3968e9af70b70c41
package/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- Settings shell plugin: a pure composition face. It occupies `sidebar.settings` with the trigger chrome and modal settings panel, and declares the slots registrants fill: `settings.trigger` / `settings.header` / `settings.close` (chrome content), `settings.action` (ordered content-header actions), `settings.section` (one page per feature), and `settings.onboarding` (ordered feature-owned pages in a full-viewport stage). The shell ships no copy of its own — all text arrives from registrants (ui-settings-general owns chrome, General, and the product notice; features own their actions, sections, rows, and conditional onboarding pages). Nav labels may be locale-following thunks, so the nav projection resolves them through `resolveSlotLabel` and re-renders on the section ledger bump or the locale revision (an optional `ctx.get('locale')` read; no hard locale dependency).
5
+ The settings domain's base layer, with two roles and no presentation of its own. It provides `ctx.settingsScope`, the Host transport every preference row binds its durable namespace section through, and it declares the settings slot types registrants fill: `settings.trigger` / `settings.header` / `settings.close` (chrome content), `settings.action` (ordered content-header actions), `settings.section` (one page per feature), and `settings.onboarding` (ordered feature-owned pages). It depends on no `ui-*` presentation package, so any feature that owns a preference can reach it; the settings SHELL — the `sidebar.settings` occupant, its navigation, and the chrome — lives in ui-settings-general, because a shell dependency on ui-sidebar would close a reference graph cycle through ui-layout and ui-theme. The shell's own contract types live beside the shell for the same reason.
6
6
 
7
- The shell projects the onboarding ledger into ascending order and mounts exactly one page at a time; the takeover chrome (body-level stage, mask, app-root `inert`) belongs to the step itself through ui-primitives' `OnboardingSurface`, so a mounted step still resolving its private facts renders null and neither paints nor blocks anything — the shell shows no empty stage while a step decides. The active registrant receives its id, `complete()`, and an `openSection(id)` callback; completing or skipping transfers ownership to the next entry. Registrants own durable completion, capability readiness, copy, mutations, and the surface wrap, so independently registered flows cannot stack and the shell does not become a second configuration fact source.
7
+ The plugin injects nothing and waits for nothing: `ctx.settingsScope.bind(spec)` resolves the wire face through the CALLER's context at call time, so the bound scope's disposer belongs to the calling fiber, and the caller injects `connection` for the transport and `remote` for the invalidation. Listeners exist before the first background read starts, so a row's activation never blocks on the settings transport. A bound scope reloads on the forwarded `settings/document-updated` event for its own namespace and on `connection/reset`. Writes carry one field path and the last known namespace revision as `expectedRevision`; a rejected or failed write re-reads unless a newer write already superseded it, and a stale read never publishes over a newer one. Without a `decode` in the spec, a section that is not a plain object, fails its rehydrated schema, or carries a schema envelope this client cannot rehydrate publishes no value at all, so a row renders its own absent state instead of a half-decoded one.
8
8
 
9
9
  ## Model Experience
10
10
 
11
- None, as the settings shell serves browser UI composition; nothing here reaches a model request.
11
+ None, as the settings domain base serves browser preference storage and slot declarations; nothing here reaches a model request.
12
12
 
13
13
  #### KV Cache effect
14
14
 
@@ -16,4 +16,5 @@ None; this package neither assembles nor sends a provider request.
16
16
 
17
17
  ## Known Limitations and Deferred Work
18
18
 
19
- - **Panel is browser-preference scope only** — host-side settings surfaces (permission mode, tool-call mode) have no RPC backing yet; their skeletons live in ui-settings-general.
19
+ - **Remote browsers get no durable settings** — the settings RPCs are loopback-only, so a scope bound in a non-loopback browser starts `unavailable` and never crosses the wire; every row it backs is inert there.
20
+ - **One field per write** — `set` sends a single `set` op, so a row that must move two fields together has no transaction and publishes two revisions.
package/README.zh.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 设置外壳插件:一个纯组合表层。它以触发控件和模态设置面板占用 `sidebar.settings`,并声明由注册方填充的 slot:`settings.trigger`/`settings.header`/`settings.close`(界面框架内容)、`settings.action`(内容标题栏中的有序操作)、`settings.section`(每项功能一页)和 `settings.onboarding`(由各功能持有、显示在全视口展示层中的有序页面)。外壳不自带文案:所有文本都来自注册方(ui-settings-general 拥有界面框架、「通用」分区和产品声明;各功能拥有各自的操作、分区、行和条件式首次使用引导页面)。导航 label 可以是跟随语言的 thunk,因此导航投影经 `resolveSlotLabel` 解析,并在分区账本更新或 locale revision 变化时重新渲染(`ctx.get('locale')` 可选读取,无硬 locale 依赖)。
5
+ 设置领域的底座,承担两项职责,本身不含任何呈现内容。它提供 `ctx.settingsScope`——每个偏好设置行绑定自己那份持久化命名空间分区所用的宿主传输层;并声明由注册方填充的设置 slot 类型:`settings.trigger`/`settings.header`/`settings.close`(界面框架内容)、`settings.action`(内容标题栏中的有序操作)、`settings.section`(每项功能一页)和 `settings.onboarding`(由各功能持有的有序页面)。它不依赖任何 `ui-*` 呈现包,因此任何持有偏好设置的功能都能够到它;设置**外壳**——`sidebar.settings` 占位方、它的导航与界面框架——位于 ui-settings-general,因为外壳一旦依赖 ui-sidebar,就会经 ui-layout 与 ui-theme 闭合出一条引用图环路。外壳自身的契约类型出于同一原因与外壳放在一起。
6
6
 
7
- 外壳将首次使用引导记录按升序投影,每次只挂载一个页面;接管界面框架(body 层级的展示层、遮罩、应用根节点 `inert`)经 ui-primitives 的 `OnboardingSurface` 由步骤自身持有,因此已挂载但仍在判定私有事实的步骤渲染 null 时不绘制也不阻塞任何内容——步骤判定期间外壳不会露出空白展示层。当前注册方会收到该条目的 id、`complete()` 和 `openSection(id)` 回调;完成或跳过当前页面后,所有权转交给下一项。持久化完成状态、能力就绪状态、文案、变更操作以及展示层包装均由注册方持有,因此独立注册的流程无法堆叠,外壳也不会成为第二个配置事实来源。
7
+ 该插件不注入任何服务、也不等待任何服务:`ctx.settingsScope.bind(spec)` 在调用时经**调用方**的 context 解析线路面,因此绑定所得 scope 的 disposer 归调用方 fiber 所有,而由调用方注入 `connection` 取得传输层、注入 `remote` 取得失效通知。监听器在首次后台读取启动之前就已存在,因此某一行的激活绝不会阻塞在设置传输层上。已绑定的 scope 会在收到属于自己命名空间的转发 `settings/document-updated` 事件时、以及在 `connection/reset` 时重新读取。写入携带单一字段路径以及最近已知的命名空间 revision 作为 `expectedRevision`;被拒绝或失败的写入会重新读取,除非已有更新的写入取代了它,而过期的读取绝不会覆盖发布更新的结果。若 spec 未提供 `decode`,则分区不是普通对象、未通过其重建后的 schema 校验、或携带本客户端无法重建的 schema 信封时,一律不发布任何值,于是行渲染自己的缺失状态,而不是一份半解码的值。
8
8
 
9
9
  ## 模型体验
10
10
 
11
- 无。设置外壳为浏览器 UI 提供组合能力;这里没有任何内容进入模型请求。
11
+ 无。设置领域底座为浏览器提供偏好设置存储与 slot 声明;这里没有任何内容进入模型请求。
12
12
 
13
13
  #### KV Cache 影响
14
14
 
@@ -16,4 +16,5 @@
16
16
 
17
17
  ## 已知限制与暂缓事项
18
18
 
19
- - **面板仅涵盖浏览器偏好设置**:宿主侧设置表层(权限模式、工具调用模式)尚无 RPC 支撑;其骨架位于 ui-settings-general。
19
+ - **远程浏览器没有持久化设置**:设置 RPC 仅限 loopback,因此在非 loopback 浏览器中绑定的 scope 以 `unavailable` 起步且从不跨线路,它支撑的每一行在那里都是无效的。
20
+ - **每次写入仅一个字段**:`set` 只发送单个 `set` op,因此需要同时改动两个字段的行没有事务可用,会发布两个 revision。
package/lib/client.js CHANGED
@@ -4,319 +4,247 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
8
- let react_jsx_runtime = require("react/jsx-runtime");
9
- let react = require("react");
10
- let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
11
- //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
12
- function r(e) {
13
- var t, f, n = "";
14
- if ("string" == typeof e || "number" == typeof e) n += e;
15
- else if ("object" == typeof e) if (Array.isArray(e)) {
16
- var o = e.length;
17
- for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
18
- } else for (f in e) e[f] && (n && (n += " "), n += f);
19
- return n;
20
- }
21
- function clsx() {
22
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
23
- return n;
24
- }
25
- //#endregion
26
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings/src/client/SettingsRoot.module.css.mjs
27
- const css = ".Gph0iW_trigger{cursor:pointer;width:100%;height:49px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:12px;flex:none;align-items:center;gap:8px;margin:8px 0 0;padding:0 2px 0 6px;font-family:inherit;font-size:14px;display:flex;overflow:hidden}.Gph0iW_trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.Gph0iW_trigger.Gph0iW_rail{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;margin:18px 0 10px;padding:0}.Gph0iW_triggerLabel{white-space:nowrap;overflow:hidden}.Gph0iW_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.Gph0iW_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.Gph0iW_panel{z-index:1;background:var(--dsw-alias-bg-layer-2);width:800px;max-width:calc(100vw - 48px);height:min(800px,100vh - 48px);box-shadow:var(--dsw-shadow-lv3);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:24px;display:flex;position:relative;overflow:hidden}.Gph0iW_nav{box-sizing:border-box;flex-direction:column;flex:none;gap:18px;width:188px;padding:22px 12px 0;display:flex}.Gph0iW_navTitle{color:var(--dsw-alias-label-primary);padding:0 12px;font-size:16px;font-weight:500;line-height:24px}.Gph0iW_navList{flex-direction:column;gap:4px;display:flex}.Gph0iW_navCell{box-sizing:border-box;cursor:pointer;height:40px;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:9px 16px 9px 12px;font-family:inherit;font-size:14px;font-weight:400;line-height:22px;display:flex}.Gph0iW_navCell:hover{background:var(--dsw-specific-sidebar-nav-item-hover)}.Gph0iW_navCell.Gph0iW_active{background:var(--dsw-specific-sidebar-nav-item-active)}.Gph0iW_navIcon{flex:none}.Gph0iW_navLabel{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.Gph0iW_content{flex-direction:column;flex:1;min-width:0;display:flex}.Gph0iW_header{box-sizing:border-box;flex:none;justify-content:space-between;align-items:flex-start;gap:8px;height:54px;padding:20px 14px 8px 10px;display:flex}.Gph0iW_actions{justify-content:flex-end;align-items:center;gap:8px;min-width:0;margin-left:auto;display:flex}.Gph0iW_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.Gph0iW_close:hover{background:var(--dsw-alias-interactive-bg-hover)}.Gph0iW_options{flex:1;min-height:0;padding:0 24px 8px;overflow-y:auto}.Gph0iW_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
28
- const tagId = "@deepseek-ai/dsh-client-ui-settings/SettingsRoot.module.css";
29
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
30
- const tag = document.createElement("style");
31
- tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings";
32
- tag.dataset.pluginCss = tagId;
33
- tag.textContent = css;
34
- document.head.appendChild(tag);
35
- }
36
- var SettingsRoot_module_css_default = {
37
- "hiddenLabel": "Gph0iW_hiddenLabel",
38
- "active": "Gph0iW_active",
39
- "panel": "Gph0iW_panel",
40
- "navList": "Gph0iW_navList",
41
- "header": "Gph0iW_header",
42
- "nav": "Gph0iW_nav",
43
- "trigger": "Gph0iW_trigger",
44
- "navCell": "Gph0iW_navCell",
45
- "content": "Gph0iW_content",
46
- "rail": "Gph0iW_rail",
47
- "overlay": "Gph0iW_overlay",
48
- "navTitle": "Gph0iW_navTitle",
49
- "triggerLabel": "Gph0iW_triggerLabel",
50
- "mask": "Gph0iW_mask",
51
- "navIcon": "Gph0iW_navIcon",
52
- "actions": "Gph0iW_actions",
53
- "close": "Gph0iW_close",
54
- "navLabel": "Gph0iW_navLabel",
55
- "options": "Gph0iW_options"
56
- };
57
- //#endregion
58
- //#region lib/types/client/SettingsRoot.js
7
+ let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
8
+ let _deepseek_ai_dsh_client_schema_form = require("@deepseek-ai/dsh-client-schema-form");
9
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
10
+ //#region lib/types/client/settings-scope.js
59
11
  /**
60
- * Settings shell root: the sidebar-foot trigger row plus the centered modal
61
- * panel (figma 501:29947, 1080x700) with the section nav rail. The shell is
62
- * a pure composition face — every piece of text (trigger label, panel title,
63
- * close label, sections) arrives from registrants through slots; accessible
64
- * names resolve to that content (trigger: its own text; dialog:
65
- * aria-labelledby the title node; close: visually-hidden slot text). Modal
66
- * open state and the active section id are component-local viewing state;
67
- * the onboarding coordinator mounts exactly one ordered registrant while the
68
- * sessions-derived empty-Hero fact is active — the takeover chrome
69
- * (OnboardingSurface) belongs to the step, so a mounted-but-deciding step
70
- * paints nothing here.
12
+ * Host transport for the settings-namespace scope contract. The contract types
13
+ * live in `dsh-client-runtime` (the common dependency of every feature that
14
+ * owns a preference); this file owns the wire behavior and the invalidation
15
+ * subscription, both of which are Settings-surface concerns.
71
16
  */
72
- /** Nav glyph by section id; unknown ids fall back to the settings gear. */
73
- function navIcon(id) {
74
- if (id === "models") return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDataOutline16, {
75
- className: SettingsRoot_module_css_default.navIcon,
76
- size: 16
77
- });
78
- if (id === "agent-presets") return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutline16, {
79
- className: SettingsRoot_module_css_default.navIcon,
80
- size: 16
81
- });
82
- return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16, {
83
- className: SettingsRoot_module_css_default.navIcon,
84
- size: 16
85
- });
86
- }
87
17
  /**
88
- * The modal layer: full-viewport mask + centered panel. Close paths: the
89
- * header button, a mask click, and document-level Escape (mounted only while
90
- * open, so the listener lifetime is the panel's).
18
+ * Serializes one namespace's Host reads and writes behind a snapshot store.
19
+ * Reads never block plugin activation; writes carry the latest known
20
+ * namespace revision and teardown waits for the operation already crossing
21
+ * the wire.
91
22
  */
92
- function SettingsPanel({ rows, renderSlot, activeId, onSelect, onClose }) {
93
- const active = rows.find((r) => r.id === activeId)?.id ?? rows[0]?.id;
94
- const titleId = (0, react.useId)();
95
- (0, react.useEffect)(() => {
96
- const onKeyDown = (e) => {
97
- if (e.key === "Escape") onClose();
98
- };
99
- document.addEventListener("keydown", onKeyDown);
100
- return () => {
101
- document.removeEventListener("keydown", onKeyDown);
102
- };
103
- }, [onClose]);
104
- const closeButton = (0, react.useRef)(null);
105
- (0, react.useEffect)(() => {
106
- closeButton.current?.focus();
107
- }, []);
108
- return (0, react_jsx_runtime.jsxs)("div", {
109
- className: SettingsRoot_module_css_default.overlay,
110
- role: "presentation",
111
- children: [(0, react_jsx_runtime.jsx)("div", {
112
- className: SettingsRoot_module_css_default.mask,
113
- "aria-hidden": "true",
114
- onClick: onClose
115
- }), (0, react_jsx_runtime.jsxs)("div", {
116
- className: SettingsRoot_module_css_default.panel,
117
- role: "dialog",
118
- "aria-modal": "true",
119
- "aria-labelledby": titleId,
120
- children: [(0, react_jsx_runtime.jsxs)("nav", {
121
- className: SettingsRoot_module_css_default.nav,
122
- children: [(0, react_jsx_runtime.jsx)("div", {
123
- className: SettingsRoot_module_css_default.navTitle,
124
- id: titleId,
125
- children: renderSlot("settings.header", {})
126
- }), (0, react_jsx_runtime.jsx)("div", {
127
- className: SettingsRoot_module_css_default.navList,
128
- children: rows.map((row) => (0, react_jsx_runtime.jsxs)("button", {
129
- type: "button",
130
- className: clsx(SettingsRoot_module_css_default.navCell, row.id === active && SettingsRoot_module_css_default.active),
131
- "aria-current": row.id === active ? "true" : void 0,
132
- onClick: () => {
133
- onSelect(row.id);
134
- },
135
- children: [navIcon(row.id), (0, react_jsx_runtime.jsx)("span", {
136
- className: SettingsRoot_module_css_default.navLabel,
137
- children: row.label
138
- })]
139
- }, row.id))
140
- })]
141
- }), (0, react_jsx_runtime.jsxs)("div", {
142
- className: SettingsRoot_module_css_default.content,
143
- children: [(0, react_jsx_runtime.jsxs)("div", {
144
- className: SettingsRoot_module_css_default.header,
145
- children: [(0, react_jsx_runtime.jsx)("div", {
146
- className: SettingsRoot_module_css_default.actions,
147
- children: renderSlot("settings.action", {})
148
- }), (0, react_jsx_runtime.jsxs)("button", {
149
- ref: closeButton,
150
- type: "button",
151
- className: SettingsRoot_module_css_default.close,
152
- onClick: onClose,
153
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 }), (0, react_jsx_runtime.jsx)("span", {
154
- className: SettingsRoot_module_css_default.hiddenLabel,
155
- children: renderSlot("settings.close", {})
156
- })]
157
- })]
158
- }), (0, react_jsx_runtime.jsx)("div", {
159
- className: SettingsRoot_module_css_default.options,
160
- children: active !== void 0 && renderSlot("settings.section", { close: onClose }, { only: active })
161
- })]
162
- })]
163
- })]
164
- });
165
- }
23
+ var SettingsScopeController = class {
24
+ api;
25
+ spec;
26
+ persistence;
27
+ store;
28
+ tail = Promise.resolve();
29
+ readGeneration = 0;
30
+ writeGeneration = 0;
31
+ disposed = false;
32
+ /**
33
+ * @param api - settings wire face.
34
+ * @param spec - namespace identity and optional narrowing decoder.
35
+ * @param persistence - remote browsers remain process-local because settings RPCs are loopback-only.
36
+ */
37
+ constructor(api, spec, persistence = "host") {
38
+ this.api = api;
39
+ this.spec = spec;
40
+ this.persistence = persistence;
41
+ this.store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
42
+ status: persistence === "host" ? "loading" : "unavailable",
43
+ value: void 0,
44
+ base: void 0,
45
+ user: void 0,
46
+ revision: void 0,
47
+ writable: false,
48
+ mode: persistence
49
+ });
50
+ }
51
+ /** @returns the current sync snapshot (stable reference until the next change). */
52
+ getSnapshot() {
53
+ return this.store.getSnapshot();
54
+ }
55
+ /**
56
+ * Observe snapshot replacements.
57
+ * @param listener - invoked after each snapshot change.
58
+ * @returns the disposer removing this listener.
59
+ */
60
+ subscribe(listener) {
61
+ return this.store.subscribe(listener);
62
+ }
63
+ /**
64
+ * Queue a Host refresh; a newer read or user write suppresses stale publication.
65
+ * @returns settlement after the queued read completes or is skipped.
66
+ */
67
+ load() {
68
+ const generation = ++this.readGeneration;
69
+ return this.enqueue(() => this.read(generation));
70
+ }
71
+ /**
72
+ * Queue one field write; see {@link SettingsScope.set} for the ordering,
73
+ * revision, and recovery contract.
74
+ * @param field - scalar field inside the namespace section.
75
+ * @param value - JSON-shaped value selected by the user.
76
+ * @returns settlement after the write and any latest-write recovery read.
77
+ */
78
+ set(field, value) {
79
+ return this.write({
80
+ op: "set",
81
+ path: [field],
82
+ value
83
+ });
84
+ }
85
+ /**
86
+ * Queue one field clear; see {@link SettingsScope.unset} for the ordering,
87
+ * revision, and recovery contract.
88
+ * @param field - scalar field inside the namespace section.
89
+ * @returns settlement after the clear and any latest-write recovery read.
90
+ */
91
+ unset(field) {
92
+ return this.write({
93
+ op: "unset",
94
+ path: [field]
95
+ });
96
+ }
97
+ write(op) {
98
+ this.readGeneration += 1;
99
+ const generation = ++this.writeGeneration;
100
+ return this.enqueue(async () => {
101
+ const revision = this.getSnapshot().revision;
102
+ let response;
103
+ try {
104
+ response = await this.api.settings.mutate({
105
+ ns: this.spec.namespace,
106
+ ops: [op],
107
+ ...revision === void 0 ? {} : { expectedRevision: revision }
108
+ });
109
+ } catch (_settingsWriteFailure) {
110
+ if (!this.disposed && generation === this.writeGeneration) await this.read(++this.readGeneration);
111
+ return;
112
+ }
113
+ if (!response.result.ok) {
114
+ if (!this.disposed && generation === this.writeGeneration) await this.read(++this.readGeneration);
115
+ return;
116
+ }
117
+ this.accept(response.result.value, generation === this.writeGeneration);
118
+ });
119
+ }
120
+ /**
121
+ * Stop queued operations and wait for the current wire call to settle.
122
+ * @returns settlement after the controller reaches quiescence.
123
+ */
124
+ async dispose() {
125
+ this.disposed = true;
126
+ this.readGeneration += 1;
127
+ this.writeGeneration += 1;
128
+ await this.tail;
129
+ }
130
+ enqueue(operation) {
131
+ if (this.persistence === "memory" || this.disposed) return Promise.resolve();
132
+ const task = this.tail.then(async () => {
133
+ if (this.disposed) return;
134
+ await operation();
135
+ });
136
+ this.tail = task.catch(() => {});
137
+ return task;
138
+ }
139
+ async read(generation) {
140
+ let response;
141
+ try {
142
+ response = await this.api.settings.describe({});
143
+ } catch (_settingsReadFailure) {
144
+ return;
145
+ }
146
+ if (!response.result.ok || this.disposed) return;
147
+ const { namespaces, writable } = response.result.value;
148
+ const view = namespaces.find((candidate) => candidate.ns === this.spec.namespace);
149
+ const publish = generation === this.readGeneration;
150
+ if (view === void 0) {
151
+ if (publish) this.store.update((draft) => {
152
+ draft.status = "unavailable";
153
+ draft.writable = writable;
154
+ });
155
+ return;
156
+ }
157
+ this.accept(view, publish, writable);
158
+ }
159
+ accept(view, publish, writable) {
160
+ const decoded = publish ? this.decode(view) : void 0;
161
+ this.store.update((draft) => {
162
+ draft.revision = view.revision;
163
+ draft.base = view.base;
164
+ draft.user = view.user;
165
+ if (writable !== void 0) draft.writable = writable;
166
+ if (decoded === void 0) return;
167
+ draft.status = "ready";
168
+ draft.value = decoded;
169
+ });
170
+ }
171
+ decode(view) {
172
+ if (this.spec.decode !== void 0) return this.spec.decode(view.value);
173
+ if (typeof view.value !== "object" || view.value === null || Array.isArray(view.value)) return void 0;
174
+ let failure;
175
+ try {
176
+ failure = (0, _deepseek_ai_dsh_client_schema_form.validateDraft)((0, _deepseek_ai_dsh_client_schema_form.rehydrateSchema)(view.schema), view.value);
177
+ } catch (_malformedSchemaEnvelope) {
178
+ return;
179
+ }
180
+ return failure === void 0 ? view.value : void 0;
181
+ }
182
+ };
166
183
  /**
167
- * Render the settings trigger and panel.
168
- * @param props - composed slot props (contract/slots.ts).
169
- * @returns the settings shell element tree.
184
+ * The settings domain's base service. Features that own a preference reach the
185
+ * settings transport through this service rather than a shared function: the
186
+ * client bundle purity gate forbids cross-plugin value imports and directs
187
+ * cross-plugin collaboration through cordis services
188
+ * (`packages/client/tsdown.client.ts`).
170
189
  */
171
- function SettingsRoot(props) {
172
- const { wide, useSections, useOnboardingSteps, useSessions, renderSlot } = props;
173
- const [open, setOpen] = (0, react.useState)(false);
174
- const [activeId, setActiveId] = (0, react.useState)(void 0);
175
- const [completedOnboarding, setCompletedOnboarding] = (0, react.useState)(() => /* @__PURE__ */ new Set());
176
- const close = (0, react.useCallback)(() => {
177
- setOpen(false);
178
- setActiveId(void 0);
179
- }, []);
180
- const openSection = (0, react.useCallback)((id) => {
181
- setActiveId(id);
182
- setOpen(true);
183
- }, []);
184
- const rows = useSections((s) => s);
185
- const onboardingSteps = useOnboardingSteps((s) => s);
186
- const onboardingActive = useSessions((state) => state.phase === "ready" && (state.current === void 0 || state.byId[state.current]?.blank === true));
187
- const onboardingStep = onboardingActive ? onboardingSteps.find((step) => !completedOnboarding.has(step.id)) : void 0;
188
- (0, react.useEffect)(() => {
189
- if (onboardingActive) return;
190
- setCompletedOnboarding(/* @__PURE__ */ new Set());
191
- }, [onboardingActive]);
192
- const completeOnboardingStep = (0, react.useCallback)((id) => {
193
- setCompletedOnboarding((previous) => {
194
- if (previous.has(id)) return previous;
195
- return new Set([...previous, id]);
196
- });
197
- }, []);
198
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
199
- (0, react_jsx_runtime.jsx)("button", {
200
- type: "button",
201
- className: clsx(SettingsRoot_module_css_default.trigger, !wide && SettingsRoot_module_css_default.rail),
202
- "aria-haspopup": "dialog",
203
- "aria-expanded": open,
204
- onClick: () => {
205
- setOpen(true);
206
- },
207
- children: renderSlot("settings.trigger", { wide })
208
- }),
209
- open && (0, react_jsx_runtime.jsx)(SettingsPanel, {
210
- rows,
211
- renderSlot,
212
- activeId,
213
- onSelect: setActiveId,
214
- onClose: close
215
- }),
216
- onboardingStep !== void 0 && renderSlot("settings.onboarding", {
217
- stepId: onboardingStep.id,
218
- complete: () => {
219
- completeOnboardingStep(onboardingStep.id);
220
- },
221
- openSection
222
- }, { only: onboardingStep.id })
223
- ] });
224
- }
190
+ var SettingsScopeService = class extends _deepseek_ai_cordis.Service {
191
+ /**
192
+ * @param ctx - the providing plugin's context.
193
+ */
194
+ constructor(ctx) {
195
+ super(ctx, "settingsScope");
196
+ }
197
+ /**
198
+ * Bind one namespace scope to settings and connection invalidations on the
199
+ * CALLER's plugin lifecycle — the service proxy binds `this.ctx` to the
200
+ * caller at call time, so the scope's disposer belongs to the calling fiber.
201
+ * Listeners exist before the initial background read starts, so activation
202
+ * never blocks on the settings transport. The caller injects `connection`
203
+ * for the transport and `remote` for the forwarded settings invalidation.
204
+ * @param spec - domain-owned namespace contract.
205
+ * @returns the bound scope consumed by the domain's services and rows.
206
+ */
207
+ bind(spec) {
208
+ const ctx = this.ctx;
209
+ const connection = ctx.get("connection");
210
+ const controller = new SettingsScopeController(connection.api, spec, connection.isLoopback ? "host" : "memory");
211
+ ctx.effect(() => {
212
+ const refresh = (namespace) => {
213
+ if (namespace !== void 0 && namespace !== spec.namespace) return;
214
+ controller.load();
215
+ };
216
+ const disposers = [ctx.get("remote").$on("settings/document-updated", refresh), ctx.on("connection/reset", () => {
217
+ refresh();
218
+ })];
219
+ controller.load();
220
+ return async () => {
221
+ for (const dispose of disposers) dispose();
222
+ await controller.dispose();
223
+ };
224
+ }, `ui-settings: ${spec.namespace} settings scope`);
225
+ return controller;
226
+ }
227
+ };
225
228
  //#endregion
226
229
  //#region lib/types/client/index.js
227
230
  /**
228
- * Required services (cordis fiber inject). The target slot is declared by
229
- * ui-sidebar's apply, whose activation order relative to this one is NOT
230
- * constrained (dsh.client.inject edges are informational); registration
231
- * depends on the slot through `slots.inject()`.
231
+ * Required services: none. The transport is resolved per caller through
232
+ * `this.ctx` at `bind` time, so this plugin waits for nothing.
232
233
  */
233
- const inject = ["slots"];
234
+ const inject = [];
234
235
  /**
235
- * Register the settings shell into `sidebar.settings` once the declaration is
236
- * on the ledger.
236
+ * Provide the settings-namespace scope service.
237
+ *
238
+ * Constructing the service in this plugin's fiber keeps its traced methods
239
+ * bound to each consuming plugin's context.
237
240
  * @param ctx - client root context.
238
241
  */
239
242
  function apply(ctx) {
240
- let rowsVersion = -1;
241
- let rowsRevision = -1;
242
- let rows = [];
243
- let onboardingVersion = -1;
244
- let onboardingSteps = [];
245
- const localeRevision = () => ctx.get("locale")?.getSnapshot().revision ?? 0;
246
- const injected = () => ({ hooks: {
247
- sections: {
248
- getSnapshot: () => {
249
- const version = ctx.slots.getVersion("settings.section");
250
- const revision = localeRevision();
251
- if (version !== rowsVersion || revision !== rowsRevision) {
252
- rowsVersion = version;
253
- rowsRevision = revision;
254
- rows = ctx.slots.entries("settings.section").map((e) => ({
255
- /* v8 ignore next -- list-slot registration requires id (SlotCore rejects an entry without one) */
256
- id: e.options.id ?? "",
257
- order: e.options.order ?? 0,
258
- label: (0, _deepseek_ai_dsh_client_ui_slots.resolveSlotLabel)(e.options.label) ?? ""
259
- })).sort((a, b) => a.order - b.order);
260
- }
261
- return rows;
262
- },
263
- subscribe: (listener) => {
264
- const offLedger = ctx.slots.subscribe("settings.section", listener);
265
- const offLocale = ctx.get("locale")?.subscribe(listener);
266
- return () => {
267
- offLedger();
268
- offLocale?.();
269
- };
270
- }
271
- },
272
- onboardingSteps: {
273
- getSnapshot: () => {
274
- const version = ctx.slots.getVersion("settings.onboarding");
275
- if (version !== onboardingVersion) {
276
- onboardingVersion = version;
277
- onboardingSteps = ctx.slots.entries("settings.onboarding").map((e) => ({
278
- /* v8 ignore next -- list-slot registration requires id */
279
- id: e.options.id ?? "",
280
- order: e.options.order ?? 0
281
- })).sort((a, b) => a.order - b.order);
282
- }
283
- return onboardingSteps;
284
- },
285
- subscribe: (listener) => ctx.slots.subscribe("settings.onboarding", listener)
286
- }
287
- } });
288
- ctx.slots.inject("sidebar.settings", () => ctx.slots.register({
289
- name: "sidebar.settings",
290
- children: {
291
- "settings.trigger": {
292
- kind: "single",
293
- scope: "root"
294
- },
295
- "settings.header": {
296
- kind: "single",
297
- scope: "root"
298
- },
299
- "settings.action": {
300
- kind: "list",
301
- scope: "root"
302
- },
303
- "settings.close": {
304
- kind: "single",
305
- scope: "root"
306
- },
307
- "settings.section": {
308
- kind: "list",
309
- scope: "root"
310
- },
311
- "settings.onboarding": {
312
- kind: "list",
313
- scope: "root"
314
- }
315
- },
316
- inject: injected
317
- }, SettingsRoot));
243
+ new SettingsScopeService(ctx);
318
244
  }
319
245
  //#endregion
246
+ exports.SettingsScopeController = SettingsScopeController;
247
+ exports.SettingsScopeService = SettingsScopeService;
320
248
  exports.apply = apply;
321
249
  exports.inject = inject;
322
250
  return module.exports;
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region lib/types/index.js
2
2
  /** Host loader entry for the browser implementation exported from `./client`. */
3
- /** Host plugin body — no host-side behavior for the settings shell plugin. */
3
+ /** Host plugin body — no host-side behavior for the settings domain base plugin. */
4
4
  function apply() {}
5
5
  //#endregion
6
6
  export { apply };
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Settings shell slot contract — the canonical home of every settings slot
3
- * type. The shell is a pure composition face with zero copy of its own: it
4
- * occupies the sidebar-owned `sidebar.settings` hole and declares the slots
5
- * below; ALL text (trigger label, panel title, header actions, close aria,
6
- * section content) arrives from registrants. A feature owns its settings surface — adding a
7
- * setting never means editing the shell; copy that belongs to no single
8
- * feature (chrome, the General section) is owned by ui-settings-general.
2
+ * Settings slot contract — the canonical home of every settings slot type,
3
+ * owned by the settings domain base rather than by the shell that renders
4
+ * them (ui-settings-general, which occupies `sidebar.settings`). The shell has
5
+ * zero copy of its own: ALL text (trigger label, panel title, header actions,
6
+ * close aria, section content) arrives from registrants. A feature owns its
7
+ * own settings pages — adding a setting never means editing the shell; copy
8
+ * that belongs to no single feature (chrome, the General section) is owned by
9
+ * ui-settings-general too.
9
10
  */
10
- import type { HostObservable, InjectFace, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
11
  declare module '@deepseek-ai/dsh-client-ui-slots' {
12
12
  interface SlotMap {
13
13
  /**
@@ -86,8 +86,28 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
86
86
  scope: 'root';
87
87
  owner: SettingsOnboardingOwnerProps;
88
88
  };
89
+ /**
90
+ * One preference row inside the General section, contributed by the
91
+ * feature plugin that owns the preference (locale → Language, ui-theme →
92
+ * Appearance, ui-conversation → Composer Enter). Options: `id` (row key),
93
+ * `order` (row position). Rows draw their own internals; the section
94
+ * column only stacks them. Declared at runtime by ui-settings-general's
95
+ * General entry — the type lives here with every other settings slot type,
96
+ * because this package is the settings domain's base layer and every
97
+ * registrant already depends on it for `ctx.settingsScope`.
98
+ */
99
+ 'settings.general.item': {
100
+ kind: 'list';
101
+ scope: 'root';
102
+ owner: SettingsGeneralItemOwnerProps;
103
+ };
89
104
  }
90
105
  }
106
+ /** Owner share of a General preference row (the section supplies nothing). */
107
+ export interface SettingsGeneralItemOwnerProps {
108
+ /** Marker field: item owner props are intentionally empty. */
109
+ children?: never;
110
+ }
91
111
  /** Owner share of the trigger content seat: the sidebar column state. */
92
112
  export interface SettingsTriggerOwnerProps {
93
113
  /** Whether the sidebar renders wide content (false = 56px rail, icon only). */
@@ -118,35 +138,4 @@ export interface SettingsOnboardingOwnerProps {
118
138
  /** Open the settings panel directly on one registered section. */
119
139
  openSection: (id: string) => void;
120
140
  }
121
- /** One nav row projected from a settings.section registration's options. */
122
- export interface SettingsSectionRow {
123
- id: string;
124
- order: number;
125
- label: string;
126
- }
127
- /** One ordered onboarding step projected from a slot registration. */
128
- export interface SettingsOnboardingStep {
129
- id: string;
130
- order: number;
131
- }
132
- /**
133
- * Registrant-private injected share of the settings shell (assembled in
134
- * apply): the ledger's nav-row projection as a hooks-compartment source —
135
- * the shell reads no locale state and subscribes through the bound hook.
136
- */
137
- export type SettingsRootInjected = {
138
- hooks: {
139
- /** settings.section ledger projected into ordered nav rows. */
140
- sections: HostObservable<readonly SettingsSectionRow[]>;
141
- /** settings.onboarding ledger projected into coordinator order. */
142
- onboardingSteps: HostObservable<readonly SettingsOnboardingStep[]>;
143
- };
144
- };
145
- /**
146
- * Full component props of the settings shell root: the sidebar owner share
147
- * (wide/rail state) plus the declared render shares and the injected face
148
- * (hooks compartment bound to useSections). No store is registered — modal
149
- * open state and active section id are component-local viewing state.
150
- */
151
- export type SettingsRootComponentProps = PropsRuntime<'sidebar.settings'> & PropsRenderSlots<'settings.trigger' | 'settings.header' | 'settings.action' | 'settings.close' | 'settings.section' | 'settings.onboarding'> & InjectFace<SettingsRootInjected>;
152
141
  //# sourceMappingURL=slots.d.ts.map
@@ -1,24 +1,26 @@
1
1
  /**
2
- * Settings shell plugin, browser half. A pure composition face: occupies the
3
- * sidebar-owned `sidebar.settings` hole with the trigger chrome + modal
4
- * panel, declares its chrome, section, and onboarding slots, and projects the
5
- * section ledger into panel navigation. The shell ships no copy; it reads the
6
- * optional locale revision only to resolve registrant-owned nav-label thunks.
7
- * ui-settings-general owns the chrome and General content; features own their
8
- * rows, sections, and onboarding pages. Export discipline: packages/client/AGENTS.md.
2
+ * Settings domain base plugin, browser half. Provides `ctx.settingsScope`, the
3
+ * settings-namespace Host transport every preference row binds its durable
4
+ * section through, and owns the canonical slot-type contract for the settings
5
+ * surface. It depends on no `ui-*` presentation package, so any feature that
6
+ * owns a preference can reach it: the settings SHELL — the `sidebar.settings`
7
+ * occupant, its navigation, and the chrome — lives in ui-settings-general,
8
+ * because a shell dependency on ui-sidebar would close a reference cycle
9
+ * through ui-layout and ui-theme. Export discipline: packages/client/AGENTS.md.
9
10
  */
10
11
  import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
11
- export type { SettingsHeaderOwnerProps, SettingsRootComponentProps, SettingsRootInjected, SettingsOnboardingOwnerProps, SettingsOnboardingStep, SettingsSectionOwnerProps, SettingsSectionRow, SettingsTriggerOwnerProps, } from './contract/slots.ts';
12
+ export type { SettingsGeneralItemOwnerProps, SettingsHeaderOwnerProps, SettingsOnboardingOwnerProps, SettingsSectionOwnerProps, SettingsTriggerOwnerProps, } from './contract/slots.ts';
13
+ export { SettingsScopeController, SettingsScopeService } from './settings-scope.ts';
12
14
  /**
13
- * Required services (cordis fiber inject). The target slot is declared by
14
- * ui-sidebar's apply, whose activation order relative to this one is NOT
15
- * constrained (dsh.client.inject edges are informational); registration
16
- * depends on the slot through `slots.inject()`.
15
+ * Required services: none. The transport is resolved per caller through
16
+ * `this.ctx` at `bind` time, so this plugin waits for nothing.
17
17
  */
18
- export declare const inject: string[];
18
+ export declare const inject: never[];
19
19
  /**
20
- * Register the settings shell into `sidebar.settings` once the declaration is
21
- * on the ledger.
20
+ * Provide the settings-namespace scope service.
21
+ *
22
+ * Constructing the service in this plugin's fiber keeps its traced methods
23
+ * bound to each consuming plugin's context.
22
24
  * @param ctx - client root context.
23
25
  */
24
26
  export declare function apply(ctx: ClientContext): void;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Host transport for the settings-namespace scope contract. The contract types
3
+ * live in `dsh-client-runtime` (the common dependency of every feature that
4
+ * owns a preference); this file owns the wire behavior and the invalidation
5
+ * subscription, both of which are Settings-surface concerns.
6
+ */
7
+ import { Service } from '@deepseek-ai/cordis';
8
+ import type { Context } from '@deepseek-ai/cordis';
9
+ import type { IApiClient } from '@deepseek-ai/dsh-client-connection/client';
10
+ import { type SettingsScope, type SettingsScopeSnapshot, type SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client';
11
+ type SettingsFace = Pick<IApiClient, 'settings'>;
12
+ /**
13
+ * Serializes one namespace's Host reads and writes behind a snapshot store.
14
+ * Reads never block plugin activation; writes carry the latest known
15
+ * namespace revision and teardown waits for the operation already crossing
16
+ * the wire.
17
+ */
18
+ export declare class SettingsScopeController<T> implements SettingsScope<T> {
19
+ private readonly api;
20
+ private readonly spec;
21
+ private readonly persistence;
22
+ private readonly store;
23
+ private tail;
24
+ private readGeneration;
25
+ private writeGeneration;
26
+ private disposed;
27
+ /**
28
+ * @param api - settings wire face.
29
+ * @param spec - namespace identity and optional narrowing decoder.
30
+ * @param persistence - remote browsers remain process-local because settings RPCs are loopback-only.
31
+ */
32
+ constructor(api: SettingsFace, spec: SettingsScopeSpec<T>, persistence?: 'host' | 'memory');
33
+ /** @returns the current sync snapshot (stable reference until the next change). */
34
+ getSnapshot(): SettingsScopeSnapshot<T>;
35
+ /**
36
+ * Observe snapshot replacements.
37
+ * @param listener - invoked after each snapshot change.
38
+ * @returns the disposer removing this listener.
39
+ */
40
+ subscribe(listener: () => void): () => void;
41
+ /**
42
+ * Queue a Host refresh; a newer read or user write suppresses stale publication.
43
+ * @returns settlement after the queued read completes or is skipped.
44
+ */
45
+ load(): Promise<void>;
46
+ /**
47
+ * Queue one field write; see {@link SettingsScope.set} for the ordering,
48
+ * revision, and recovery contract.
49
+ * @param field - scalar field inside the namespace section.
50
+ * @param value - JSON-shaped value selected by the user.
51
+ * @returns settlement after the write and any latest-write recovery read.
52
+ */
53
+ set(field: string, value: unknown): Promise<void>;
54
+ /**
55
+ * Queue one field clear; see {@link SettingsScope.unset} for the ordering,
56
+ * revision, and recovery contract.
57
+ * @param field - scalar field inside the namespace section.
58
+ * @returns settlement after the clear and any latest-write recovery read.
59
+ */
60
+ unset(field: string): Promise<void>;
61
+ private write;
62
+ /**
63
+ * Stop queued operations and wait for the current wire call to settle.
64
+ * @returns settlement after the controller reaches quiescence.
65
+ */
66
+ dispose(): Promise<void>;
67
+ private enqueue;
68
+ private read;
69
+ private accept;
70
+ private decode;
71
+ }
72
+ declare module '@deepseek-ai/cordis' {
73
+ interface Context {
74
+ settingsScope: SettingsScopeService;
75
+ }
76
+ }
77
+ /**
78
+ * The settings domain's base service. Features that own a preference reach the
79
+ * settings transport through this service rather than a shared function: the
80
+ * client bundle purity gate forbids cross-plugin value imports and directs
81
+ * cross-plugin collaboration through cordis services
82
+ * (`packages/client/tsdown.client.ts`).
83
+ */
84
+ export declare class SettingsScopeService extends Service {
85
+ /**
86
+ * @param ctx - the providing plugin's context.
87
+ */
88
+ constructor(ctx: Context);
89
+ /**
90
+ * Bind one namespace scope to settings and connection invalidations on the
91
+ * CALLER's plugin lifecycle — the service proxy binds `this.ctx` to the
92
+ * caller at call time, so the scope's disposer belongs to the calling fiber.
93
+ * Listeners exist before the initial background read starts, so activation
94
+ * never blocks on the settings transport. The caller injects `connection`
95
+ * for the transport and `remote` for the forwarded settings invalidation.
96
+ * @param spec - domain-owned namespace contract.
97
+ * @returns the bound scope consumed by the domain's services and rows.
98
+ */
99
+ bind<T>(spec: SettingsScopeSpec<T>): SettingsScope<T>;
100
+ }
101
+ export {};
102
+ //# sourceMappingURL=settings-scope.d.ts.map
@@ -1,4 +1,4 @@
1
1
  /** Host loader entry for the browser implementation exported from `./client`. */
2
- /** Host plugin body — no host-side behavior for the settings shell plugin. */
2
+ /** Host plugin body — no host-side behavior for the settings domain base plugin. */
3
3
  export declare function apply(): void;
4
4
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-settings",
3
- "description": "Settings shell plugin: sidebar trigger, modal panel, feature sections, and an ordered full-page onboarding stage",
4
- "version": "0.0.1-rc.1",
3
+ "description": "Settings domain base plugin: the settings-namespace scope service and the canonical settings slot-type contract",
4
+ "version": "0.0.1-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -32,37 +32,39 @@
32
32
  "dsh": {
33
33
  "client": {
34
34
  "inject": [
35
+ "@deepseek-ai/dsh-client-connection",
35
36
  "@deepseek-ai/dsh-client-runtime",
36
- "@deepseek-ai/dsh-client-ui-sidebar"
37
+ "@deepseek-ai/dsh-api-remotes"
37
38
  ],
38
39
  "platform": "web"
39
40
  }
40
41
  },
41
42
  "license": "BSD-3-Clause",
42
- "dependencies": {
43
- "clsx": "^2.0.0"
44
- },
45
43
  "peerDependencies": {
46
44
  "react": "^18.2.0",
47
- "react-dom": "^18.2.0",
48
- "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
49
- "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
50
- "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
51
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
52
- "@deepseek-ai/cordis": "^4.0.1-rc.1"
45
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
46
+ "@deepseek-ai/dsh-api-gateway": "^0.0.1-rc.2",
47
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.2",
48
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.2",
49
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.2",
50
+ "@deepseek-ai/dsh-settings": "^0.0.1-rc.2",
51
+ "@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.2",
52
+ "@deepseek-ai/dsh-client-connection": "^0.0.1-rc.2",
53
+ "@deepseek-ai/dsh-client-schema-form": "^0.0.1-rc.2"
53
54
  },
54
55
  "devDependencies": {
55
- "@types/react-dom": "~18.3.0",
56
56
  "@types/react": "~18.3.1",
57
57
  "react": "^18.2.0",
58
- "react-dom": "^18.2.0",
59
- "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
60
- "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
61
- "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
62
- "@deepseek-ai/dsh-client-ui-sidebar": "^0.0.1-rc.1",
63
- "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
64
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
65
- "@deepseek-ai/cordis": "^4.0.1-rc.1"
58
+ "@deepseek-ai/dsh-api-gateway": "^0.0.1-rc.2",
59
+ "@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.2",
60
+ "@deepseek-ai/dsh-client-connection": "^0.0.1-rc.2",
61
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.2",
62
+ "@deepseek-ai/dsh-client-schema-form": "^0.0.1-rc.2",
63
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
64
+ "@deepseek-ai/dsh-client-test-runtime": "^0.0.1-rc.2",
65
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.2",
66
+ "@deepseek-ai/dsh-settings": "^0.0.1-rc.2",
67
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.2"
66
68
  },
67
69
  "files": [
68
70
  "lib/index.js",
@@ -1,8 +0,0 @@
1
- import type { SettingsRootComponentProps } from './contract/slots.ts';
2
- /**
3
- * Render the settings trigger and panel.
4
- * @param props - composed slot props (contract/slots.ts).
5
- * @returns the settings shell element tree.
6
- */
7
- export declare function SettingsRoot(props: SettingsRootComponentProps): import("react").JSX.Element;
8
- //# sourceMappingURL=SettingsRoot.d.ts.map