@deepseek-ai/dsh-client-ui-layout 0.0.1-rc.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/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, DeepSeek
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/ui-layout/README.md
5
+ README.md: fa60520a20ac8a7f25d494879c68efb06a28998f
6
+ README.zh.md: 2ce3d1972d39013d84216596dc57d48ab2d245d9
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @deepseek-ai/dsh-client-ui-layout
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Shell plugin: three-column AppFrame (drag handles and concession chain) plus the `ctx.layout` panel-geometry service; it registers into the runtime-owned `root` slot and declares `sidebar`, `conversation`, `details`, and `conversation.empty`. The sidebar resize boundary is an invisible hit strip, while the details boundary retains its floating pill; only details shrinks during concession and then auto-closes. A closed sidebar retains a 56px control rail while details closes to zero width. The package also seats the theme presenter: it consumes resolved `ctx.theme` snapshots and projects them onto the document (`html { color-scheme }` for native UA chrome, `body[data-ds-dark-theme]` from the active color scheme, the theme's alias tokens as inline variables on body, and one owned `<meta name="theme-color">` whose content follows the computed body background). Measuring after palette and token application keeps the rendered background as the single color authority; disposing the presenter removes its metadata node with its other global writes.
6
+
7
+ AppFrame always mounts the conversation and details columns; a connected Session renders through `SessionProvider`. The transient layout store starts the sidebar at its default width and details closed, and it never reads or writes `localStorage`. Hero and other unselected states also derive a zero rendered details width without changing that stored preference. AppFrame retains the last non-blank Session id across those states: the first Session remains closed, an explicit details action opens the contract default width, returning to the same Session restores its unchanged width, and selecting a different Session closes details before paint. The conversation owner share is empty, while the sidebar owner share contains only `collapsed` and `width`; registrants obtain business data from standard hooks and actions from their own inject faces.
8
+
9
+ The `/client` export surface is the plugin body (`apply`/`inject`), `LayoutService`, and the four owner-share interfaces. AppFrame, the panel store, and the concession solver remain package-internal.
10
+
11
+ ## Model Experience
12
+
13
+ None, as the layout shell manages browser viewing state; nothing here reaches a model request.
14
+
15
+ #### KV Cache effect
16
+
17
+ None; this package neither assembles nor sends a provider request.
18
+
19
+ ## Known Limitations and Deferred Work
20
+
21
+ - **Panel geometry is transient** — reload restores the sidebar default and details closed; switching between distinct Session ids also closes details and forgets its dragged width, while unselected surfaces render details at zero width without modifying geometry.
22
+ - **Concession-chain auto-close derives a zero width without touching the preferred width** — the panel restores itself when the window widens; consumers must not read the stored details width as the rendered truth.
23
+ - **No scroll anchoring during squeeze reflow** — layout changes may move the reader's viewport.
package/README.zh.md ADDED
@@ -0,0 +1,23 @@
1
+ # @deepseek-ai/dsh-client-ui-layout
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 外壳插件:三栏 AppFrame(拖动手柄与让步链)加 `ctx.layout` 面板几何服务;它注册到运行时拥有的 `root` slot,并声明 `sidebar`、`conversation`、`details` 和 `conversation.empty`。侧边栏的缩放边界是不可见命中条带,详情栏边界则保留其浮动胶囊;让步期间只有详情栏会收缩并随后自动关闭。关闭的侧边栏仍保留 56px 控制栏,详情栏则关闭到零宽度。该包还提供主题呈现器:它消费解析后的 `ctx.theme` 快照,并将其投影到 document(用 `html { color-scheme }` 驱动原生 UA 控件,依据当前配色方案设置 `body[data-ds-dark-theme]`,并将主题的别名 token 设为 body 上的内联变量,同时拥有一个 `<meta name="theme-color">`,其内容随计算后的 body 背景色更新)。在应用调色板和 token 后进行测量,可确保渲染后的背景成为唯一的颜色依据;呈现器在 dispose(资源释放)时会移除其自有的元数据节点,并一并清除其写入的其他全局状态。
6
+
7
+ AppFrame 始终挂载会话栏和详情栏;已连接 Session 通过 `SessionProvider` 渲染。布局 store 是瞬时状态,侧边栏以默认宽度启动,详情栏则保持关闭,且该 store 从不读写 `localStorage`。hero 和其他未选中状态也会将详情栏的渲染宽度派生为零,但不会改变存储的宽度偏好。AppFrame 会跨越这些状态保留最后一个非 blank 会话 id:首个会话保持关闭;显式打开详情栏的操作会使用约定默认宽度;返回同一会话时恢复其未改变的宽度;选择不同会话时,详情栏会在绘制前关闭。会话 owner share 为空,侧边栏 owner share 只包含 `collapsed` 和 `width`;注册方通过标准钩子获取业务数据,并从各自的 inject 接口获取操作。
8
+
9
+ `/client` 导出表层包含插件主体(`apply`/`inject`)、`LayoutService` 和四个 owner-share 接口。AppFrame、面板 store 与让步求解器仍属于包内部。
10
+
11
+ ## 模型体验
12
+
13
+ 无。布局外壳管理浏览器查看状态;这里没有任何内容进入模型请求。
14
+
15
+ #### KV Cache 影响
16
+
17
+ 无;该包既不组装也不发送提供方请求。
18
+
19
+ ## 已知限制与暂缓事项
20
+
21
+ - **面板几何信息是瞬时状态**:重新加载会恢复侧边栏默认值,并使详情栏保持关闭;在不同会话 id 之间切换同样会关闭详情栏,并忘记拖动后的宽度,而未选中表面会以零宽度渲染详情栏,但不会修改几何信息。
22
+ - **让步链自动关闭通过推导零宽度实现,不会改动宽度偏好**:窗口变宽时面板会自行恢复;消费方禁止把 store 中的详情宽度当作实际渲染状态。
23
+ - **挤压重排期间不提供滚动锚定**:布局变化可能移动读者的 viewport。
package/lib/client.js ADDED
@@ -0,0 +1,446 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-layout",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
10
+ /** Viewport width below which the sidebar auto-collapses to the rail (deepsuite
11
+ * LG breakpoint); a manual toggle below it re-expands over the squeezed center
12
+ * (stores.ts narrowExpanded). */
13
+ const SIDEBAR_AUTO_COLLAPSE = 1024;
14
+ /**
15
+ * Clamp a panel width into its contract range.
16
+ * @param px - requested width.
17
+ * @param min - range lower bound.
18
+ * @param max - range upper bound.
19
+ * @returns the clamped width.
20
+ */
21
+ function clampWidth(px, min, max) {
22
+ return Math.min(max, Math.max(min, Math.round(px)));
23
+ }
24
+ /**
25
+ * Solve the three column widths for one viewport frame. Pure: no hysteresis —
26
+ * the output is a function of (viewport, preferences) only, so recovery on
27
+ * re-widening is automatic. Preferences re-clamp here because they cross the
28
+ * store boundary and callers may still supply stale ranges.
29
+ * @param viewport - available frame width in px.
30
+ * @param sidebar - sidebar width preference in px (0 = closed).
31
+ * @param details - details width preference in px (0 = closed).
32
+ * @returns resolved widths; details 0 means visually closed (never unmounted), while a closed sidebar keeps its compact rail.
33
+ */
34
+ function computeColumns(viewport, sidebar, details) {
35
+ const s = sidebar === 0 ? 56 : clampWidth(sidebar, 280, 420);
36
+ const d0 = details === 0 ? 0 : clampWidth(details, 300, 520);
37
+ if (s + d0 + 640 <= viewport) return {
38
+ sidebar: s,
39
+ center: viewport - s - d0,
40
+ details: d0
41
+ };
42
+ const d1 = d0 === 0 ? 0 : Math.max(300, viewport - s - 640);
43
+ if (s + d1 + 640 <= viewport) return {
44
+ sidebar: s,
45
+ center: 640,
46
+ details: d1
47
+ };
48
+ return {
49
+ sidebar: s,
50
+ center: Math.max(0, viewport - s),
51
+ details: 0
52
+ };
53
+ }
54
+ //#endregion
55
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-layout/src/client/AppFrame.module.css.mjs
56
+ const css = ".pI_x6G_frame{background:var(--dsw-alias-bg-base);height:100%;transition:grid-template-columns var(--ds-transition-duration-slow) var(--ds-ease-in-out);grid-template-rows:100%;display:grid;position:relative;overflow:hidden}.pI_x6G_frame[data-dragging]{transition:none}@media (prefers-reduced-motion:reduce){.pI_x6G_frame{transition:none}}.pI_x6G_sidebarCol{background:var(--dsw-specific-sidebar-fill);border-right:1px solid var(--dsw-alias-border-l1);min-width:0;overflow:hidden}.pI_x6G_centerCol{flex-direction:column;min-width:0;display:flex;overflow:hidden}.pI_x6G_detailsCol{border-left:1px solid var(--dsw-alias-border-l2);min-width:0;overflow:hidden}.pI_x6G_frame[data-details-collapsed] .pI_x6G_detailsCol{border-left:none}.pI_x6G_handle{cursor:col-resize;z-index:2;touch-action:none;width:8px;transition:left var(--ds-transition-duration-slow) var(--ds-ease-in-out);margin-left:-4px;position:absolute;top:0;bottom:0}.pI_x6G_frame[data-dragging] .pI_x6G_handle{transition:none}@media (prefers-reduced-motion:reduce){.pI_x6G_handle{transition:none}}.pI_x6G_handle[data-side=details]:after{content:\"\";box-sizing:border-box;background:var(--dsw-alias-button-floating-fill);border:1px solid var(--dsw-alias-border-l2-darkmode-thin);opacity:0;width:12px;height:32px;transition:opacity var(--ds-transition-duration-slow) var(--ds-ease-in-out), background var(--ds-transition-duration-slow) var(--ds-ease-in-out);border-radius:10px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.pI_x6G_detailsCol:hover~.pI_x6G_handle[data-side=details]:after,.pI_x6G_handle[data-side=details]:hover:after,.pI_x6G_handle[data-side=details][data-dragging=true]:after{opacity:1}.pI_x6G_handle[data-side=details]:hover:after,.pI_x6G_handle[data-side=details][data-dragging=true]:after{background:var(--dsw-alias-button-floating-hover);border-color:var(--dsw-alias-border-l3)}";
57
+ const tagId = "@deepseek-ai/dsh-client-ui-layout/AppFrame.module.css";
58
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
59
+ const tag = document.createElement("style");
60
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-layout";
61
+ tag.dataset.pluginCss = tagId;
62
+ tag.textContent = css;
63
+ document.head.appendChild(tag);
64
+ }
65
+ var AppFrame_module_css_default = {
66
+ "sidebarCol": "pI_x6G_sidebarCol",
67
+ "detailsCol": "pI_x6G_detailsCol",
68
+ "frame": "pI_x6G_frame",
69
+ "centerCol": "pI_x6G_centerCol",
70
+ "handle": "pI_x6G_handle"
71
+ };
72
+ //#endregion
73
+ //#region lib/types/client/AppFrame.js
74
+ /**
75
+ * Three-column shell frame, registered into the built-in 'root' slot (the web
76
+ * shell renders only 'root'). Owns the grid tracks (sidebar | center |
77
+ * details), the drag handles (pointer capture + rAF throttle), the concession
78
+ * chain (columns.ts), and the child-slot render decisions: the sidebar slot
79
+ * renders HERE with live parameters from the concession solve, and the
80
+ * session-aware occupants render in fixed column positions; strict entries
81
+ * gate themselves on current-session availability while session-maybe
82
+ * entries retain identity. Pure component: everything arrives
83
+ * through the three framework shares — zero cordis or framework imports,
84
+ * zero self-made hooks.
85
+ */
86
+ /** Center column grid item (session-body building block). */
87
+ function CenterColumn(props) {
88
+ return (0, react_jsx_runtime.jsx)("div", {
89
+ className: AppFrame_module_css_default.centerCol,
90
+ children: props.children
91
+ });
92
+ }
93
+ /** Details column grid item; width 0 keeps the subtree mounted (never unmount on close). */
94
+ function DetailsColumn(props) {
95
+ return (0, react_jsx_runtime.jsx)("div", {
96
+ className: AppFrame_module_css_default.detailsCol,
97
+ children: props.children
98
+ });
99
+ }
100
+ /**
101
+ * One drag handle: pointer capture, rAF-throttled dx reports against the drag-start origin.
102
+ * `side` keys the hover-reveal CSS to the owning column.
103
+ */
104
+ function DragHandle(props) {
105
+ const [dragging, setDragging] = (0, react.useState)(false);
106
+ const origin = (0, react.useRef)(0);
107
+ const latest = (0, react.useRef)(0);
108
+ const frame = (0, react.useRef)(null);
109
+ const callbacks = (0, react.useRef)({
110
+ onStart: props.onStart,
111
+ onDrag: props.onDrag,
112
+ onEnd: props.onEnd
113
+ });
114
+ callbacks.current = {
115
+ onStart: props.onStart,
116
+ onDrag: props.onDrag,
117
+ onEnd: props.onEnd
118
+ };
119
+ const onPointerDown = (0, react.useCallback)((e) => {
120
+ e.preventDefault();
121
+ e.currentTarget.setPointerCapture(e.pointerId);
122
+ origin.current = e.clientX;
123
+ latest.current = e.clientX;
124
+ callbacks.current.onStart();
125
+ setDragging(true);
126
+ }, []);
127
+ const onPointerMove = (0, react.useCallback)((e) => {
128
+ if (!e.currentTarget.hasPointerCapture(e.pointerId)) return;
129
+ latest.current = e.clientX;
130
+ frame.current ??= requestAnimationFrame(() => {
131
+ frame.current = null;
132
+ callbacks.current.onDrag(latest.current - origin.current);
133
+ });
134
+ }, []);
135
+ const onPointerUp = (0, react.useCallback)((e) => {
136
+ if (!e.currentTarget.hasPointerCapture(e.pointerId)) return;
137
+ e.currentTarget.releasePointerCapture(e.pointerId);
138
+ if (frame.current !== null) {
139
+ cancelAnimationFrame(frame.current);
140
+ frame.current = null;
141
+ }
142
+ callbacks.current.onDrag(latest.current - origin.current);
143
+ setDragging(false);
144
+ callbacks.current.onEnd();
145
+ }, []);
146
+ return (0, react_jsx_runtime.jsx)("div", {
147
+ className: AppFrame_module_css_default.handle,
148
+ style: { left: props.left },
149
+ "data-side": props.side,
150
+ "data-dragging": dragging || void 0,
151
+ onPointerDown,
152
+ onPointerMove,
153
+ onPointerUp
154
+ });
155
+ }
156
+ /** The three-column frame (see module doc). */
157
+ function AppFrame({ useStore, useSessions, actions, renderSlot }) {
158
+ const panels = useStore((s) => s);
159
+ const detailsSession = useSessions((s) => {
160
+ const current = s.current;
161
+ return current !== void 0 && s.byId[current]?.blank === false ? current : void 0;
162
+ });
163
+ const frameRef = (0, react.useRef)(null);
164
+ const [viewport, setViewport] = (0, react.useState)(() => window.innerWidth);
165
+ const lastSession = (0, react.useRef)(detailsSession);
166
+ (0, react.useLayoutEffect)(() => {
167
+ if (detailsSession === void 0) return;
168
+ if (lastSession.current !== void 0 && lastSession.current !== detailsSession) actions.closeDetails();
169
+ lastSession.current = detailsSession;
170
+ }, [actions, detailsSession]);
171
+ (0, react.useEffect)(() => {
172
+ const el = frameRef.current;
173
+ /* v8 ignore next -- the ref is always attached by effect time: the frame div renders unconditionally. */
174
+ if (el === null) return;
175
+ let raf = null;
176
+ const observer = new ResizeObserver(() => {
177
+ raf ??= requestAnimationFrame(() => {
178
+ raf = null;
179
+ const width = el.getBoundingClientRect().width;
180
+ if (width > 0) setViewport(width);
181
+ });
182
+ });
183
+ observer.observe(el);
184
+ return () => {
185
+ observer.disconnect();
186
+ if (raf !== null) cancelAnimationFrame(raf);
187
+ };
188
+ }, []);
189
+ const narrow = viewport < SIDEBAR_AUTO_COLLAPSE;
190
+ (0, react.useEffect)(() => {
191
+ actions.setNarrow(narrow);
192
+ }, [actions, narrow]);
193
+ const sidebarCollapsed = narrow ? !panels.narrowExpanded : panels.sidebar === 0;
194
+ const cols = computeColumns(viewport, sidebarCollapsed ? 0 : panels.sidebar === 0 ? 280 : panels.sidebar, detailsSession === void 0 ? 0 : panels.details);
195
+ const colsRef = (0, react.useRef)(cols);
196
+ colsRef.current = cols;
197
+ const sidebarBase = (0, react.useRef)(0);
198
+ const detailsBase = (0, react.useRef)(0);
199
+ const [dragging, setDragging] = (0, react.useState)(false);
200
+ const onDragEnd = (0, react.useCallback)(() => {
201
+ setDragging(false);
202
+ }, []);
203
+ const onSidebarStart = (0, react.useCallback)(() => {
204
+ sidebarBase.current = colsRef.current.sidebar;
205
+ setDragging(true);
206
+ }, []);
207
+ const onDetailsStart = (0, react.useCallback)(() => {
208
+ detailsBase.current = colsRef.current.details;
209
+ setDragging(true);
210
+ }, []);
211
+ const onSidebarDrag = (0, react.useCallback)((dx) => {
212
+ actions.setSidebar(sidebarBase.current + dx);
213
+ }, [actions]);
214
+ const onDetailsDrag = (0, react.useCallback)((dx) => {
215
+ actions.setDetails(detailsBase.current - dx);
216
+ }, [actions]);
217
+ return (0, react_jsx_runtime.jsxs)("div", {
218
+ ref: frameRef,
219
+ className: AppFrame_module_css_default.frame,
220
+ style: { gridTemplateColumns: `${cols.sidebar}px minmax(0, 1fr) ${cols.details}px` },
221
+ "data-sidebar-collapsed": sidebarCollapsed || void 0,
222
+ "data-details-collapsed": cols.details === 0 || void 0,
223
+ "data-dragging": dragging || void 0,
224
+ children: [
225
+ (0, react_jsx_runtime.jsx)("div", {
226
+ className: AppFrame_module_css_default.sidebarCol,
227
+ children: renderSlot("sidebar", {
228
+ collapsed: sidebarCollapsed,
229
+ width: cols.sidebar
230
+ })
231
+ }),
232
+ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(CenterColumn, { children: renderSlot("conversation", {}) }), (0, react_jsx_runtime.jsx)(DetailsColumn, { children: renderSlot("details", {}) })] }),
233
+ !sidebarCollapsed && (0, react_jsx_runtime.jsx)(DragHandle, {
234
+ side: "sidebar",
235
+ left: cols.sidebar,
236
+ onStart: onSidebarStart,
237
+ onDrag: onSidebarDrag,
238
+ onEnd: onDragEnd
239
+ }),
240
+ cols.details > 0 && (0, react_jsx_runtime.jsx)(DragHandle, {
241
+ side: "details",
242
+ left: viewport - cols.details,
243
+ onStart: onDetailsStart,
244
+ onDrag: onDetailsDrag,
245
+ onEnd: onDragEnd
246
+ })
247
+ ]
248
+ });
249
+ }
250
+ //#endregion
251
+ //#region lib/types/client/stores.js
252
+ /**
253
+ * The root entry's transient layout store: panel geometry as plain widths in
254
+ * px (0 = closed). Module level exports the factory only — a module-level
255
+ * handle would pin the store's identity in the module
256
+ * cache (a de-facto singleton surviving plugin reloads). register() receives
257
+ * the factory (exclusive use: the framework instantiates per entry), AppFrame
258
+ * derives its PropsStore share from the return type, and the service face
259
+ * receives the bound actions through the registration's inject hook.
260
+ */
261
+ /**
262
+ * Create the layout panel store handle. The preference IS the width, so
263
+ * closing a panel forgets its drag width — reopening restores the contract
264
+ * default. Actions are the complete write set: drag writes clamp
265
+ * into the panel's contract range and never cross the open/closed line;
266
+ * open/close transitions write 0 / the default explicitly. Below the
267
+ * auto-collapse breakpoint (AppFrame feeds setNarrow) the sidebar toggle
268
+ * flips the narrowExpanded override instead of the preference.
269
+ * @returns the store handle (spec + type + identity + factory in one).
270
+ */
271
+ function createLayoutStore() {
272
+ return (0, _deepseek_ai_dsh_client_runtime_client.defineStore)({
273
+ init: () => ({
274
+ sidebar: 280,
275
+ details: 0,
276
+ narrow: false,
277
+ narrowExpanded: false
278
+ }),
279
+ actions: {
280
+ setSidebar: (d, px) => {
281
+ d.sidebar = clampWidth(px, 280, 420);
282
+ },
283
+ setDetails: (d, px) => {
284
+ d.details = clampWidth(px, 300, 520);
285
+ },
286
+ toggleSidebar: (d) => {
287
+ if (d.narrow) d.narrowExpanded = !d.narrowExpanded;
288
+ else d.sidebar = d.sidebar === 0 ? 280 : 0;
289
+ },
290
+ setNarrow: (d, narrow) => {
291
+ if (d.narrow === narrow) return;
292
+ d.narrow = narrow;
293
+ d.narrowExpanded = false;
294
+ },
295
+ openDetails: (d) => {
296
+ if (d.details === 0) d.details = 360;
297
+ },
298
+ closeDetails: (d) => {
299
+ d.details = 0;
300
+ }
301
+ }
302
+ });
303
+ }
304
+ //#endregion
305
+ //#region lib/types/client/service.js
306
+ /** Cross-plugin panel-action face (ctx.layout). */
307
+ var LayoutService = class {
308
+ #panels;
309
+ /**
310
+ * Adopt the root entry's bound store actions. Called from the root
311
+ * registration's inject hook (a sanctioned assembly side effect), so the
312
+ * face is live from the entry's first render; on entry re-register the
313
+ * fresh actions overwrite the stale set.
314
+ * @param actions - bound actions of the entry's layout store instance.
315
+ */
316
+ attachPanels(actions) {
317
+ this.#panels = actions;
318
+ }
319
+ /** Toggle the sidebar panel (closed ⟷ contract default width). */
320
+ toggleSidebar() {
321
+ this.#require().toggleSidebar();
322
+ }
323
+ /** Open the details panel (no-op when already open). */
324
+ openDetails() {
325
+ this.#require().openDetails();
326
+ }
327
+ /** Close the details panel. */
328
+ closeDetails() {
329
+ this.#require().closeDetails();
330
+ }
331
+ #require() {
332
+ if (this.#panels === void 0) throw new Error("layout: panel actions not wired (root entry not mounted)");
333
+ return this.#panels;
334
+ }
335
+ };
336
+ //#endregion
337
+ //#region lib/types/client/theme-presenter.js
338
+ /** Body attribute selecting the dark base palette in the token stylesheets. */
339
+ const DARK_ATTRIBUTE = "data-ds-dark-theme";
340
+ /** Applies theme snapshots to the document; one instance per plugin fiber. */
341
+ var ThemePresenter = class {
342
+ /** Token names this presenter wrote in the last apply (its retraction set). */
343
+ appliedTokens = [];
344
+ /** The single metadata node this presenter inserts and removes. */
345
+ themeColorMeta;
346
+ /** Create the presenter-owned metadata node before the first snapshot arrives. */
347
+ constructor() {
348
+ this.themeColorMeta = document.createElement("meta");
349
+ this.themeColorMeta.name = "theme-color";
350
+ }
351
+ /**
352
+ * Project a snapshot onto the document: set root `color-scheme` and the body
353
+ * palette attribute from `active.colorScheme` (never the id — `system` is
354
+ * resolved upstream), then replace the previously applied token variables
355
+ * with `active.tokens`. Browser theme-color metadata follows the computed
356
+ * body background after those writes, so the rendered palette remains the
357
+ * color authority.
358
+ * @param snapshot - resolved theme snapshot from ctx.theme.
359
+ */
360
+ apply(snapshot) {
361
+ const scheme = snapshot.active.colorScheme;
362
+ document.documentElement.style.colorScheme = scheme;
363
+ const body = document.body;
364
+ if (scheme === "dark") body.setAttribute(DARK_ATTRIBUTE, "");
365
+ else body.removeAttribute(DARK_ATTRIBUTE);
366
+ for (const name of this.appliedTokens) body.style.removeProperty(name);
367
+ this.appliedTokens = [];
368
+ for (const [name, value] of Object.entries(snapshot.active.tokens)) {
369
+ body.style.setProperty(name, value);
370
+ this.appliedTokens.push(name);
371
+ }
372
+ this.themeColorMeta.content = getComputedStyle(body).backgroundColor;
373
+ if (!this.themeColorMeta.isConnected) document.head.append(this.themeColorMeta);
374
+ }
375
+ /** Retract root color-scheme, the palette attribute, token variables, and the owned metadata node. */
376
+ dispose() {
377
+ document.documentElement.style.removeProperty("color-scheme");
378
+ const body = document.body;
379
+ body.removeAttribute(DARK_ATTRIBUTE);
380
+ for (const name of this.appliedTokens) body.style.removeProperty(name);
381
+ this.appliedTokens = [];
382
+ this.themeColorMeta.remove();
383
+ }
384
+ };
385
+ //#endregion
386
+ //#region lib/types/client/index.js
387
+ /** Required services (cordis fiber inject — the loader passes the whole export surface as an object plugin). */
388
+ const inject = ["slots", "theme"];
389
+ /**
390
+ * Client plugin body: provide ctx.layout, then one register() call — AppFrame
391
+ * into 'root' with the four child-slot declarations, the layout store seat,
392
+ * and the inject hook that hands the store's bound actions to the service.
393
+ * @param ctx - client root context.
394
+ */
395
+ function apply(ctx) {
396
+ const layout = new LayoutService();
397
+ ctx.effect(() => {
398
+ const disposeService = ctx.reflect.provide("layout", layout);
399
+ const disposeRegistration = ctx.slots.register({
400
+ name: "root",
401
+ children: {
402
+ "sidebar": {
403
+ kind: "single",
404
+ scope: "root"
405
+ },
406
+ "conversation": {
407
+ kind: "single",
408
+ scope: "session-maybe"
409
+ },
410
+ "details": {
411
+ kind: "single",
412
+ scope: "session"
413
+ }
414
+ },
415
+ store: createLayoutStore,
416
+ inject: (actions) => {
417
+ layout.attachPanels(actions);
418
+ return {};
419
+ }
420
+ }, AppFrame);
421
+ return () => {
422
+ disposeRegistration();
423
+ disposeService();
424
+ };
425
+ }, "ui-layout: service + root registration");
426
+ ctx.effect(() => {
427
+ const presenter = new ThemePresenter();
428
+ presenter.apply(ctx.theme.getTheme());
429
+ const off = ctx.on("theme/change", (snapshot) => {
430
+ presenter.apply(snapshot);
431
+ });
432
+ return () => {
433
+ off();
434
+ presenter.dispose();
435
+ };
436
+ }, "ui-layout: theme presenter");
437
+ }
438
+ //#endregion
439
+ exports.LayoutService = LayoutService;
440
+ exports.apply = apply;
441
+ exports.inject = inject;
442
+ return module.exports;
443
+ }
444
+ });
445
+
446
+ //# sourceMappingURL=client.js.map
package/lib/index.js ADDED
@@ -0,0 +1,6 @@
1
+ //#region lib/types/index.js
2
+ /** Host loader entry for the browser-only layout plugin. */
3
+ /** Provides no host-side behavior. */
4
+ function apply() {}
5
+ //#endregion
6
+ export { apply };
@@ -0,0 +1,24 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-layout`.
4
+ * @module @deepseek-ai/dsh-client-ui-layout/invariant
5
+ */
6
+ const PACKAGE_NAME = "@deepseek-ai/dsh-client-ui-layout";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-ui-layout-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: the shell viewing-state store behind ctx.layout emits
13
+ * no cordis events; clamp/prune/concession-chain
14
+ * sequencing is asserted directly by this package's columns and service specs.
15
+ */
16
+ const install = () => {};
17
+ /**
18
+ * Register this package's invariant companion.
19
+ * @param ctx - Cordis context carrying the invariant service.
20
+ * @returns the installed registration's disposer after setup succeeds.
21
+ */
22
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
23
+ //#endregion
24
+ export { apply, inject, name };
@@ -0,0 +1,7 @@
1
+ import type { PropsRenderSlots, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { createLayoutStore } from './stores.ts';
3
+ /** Full composed props: runtime share + child-slot render share + store share. */
4
+ export type AppFrameProps = PropsRuntime<'root'> & PropsRenderSlots<'sidebar' | 'conversation' | 'details'> & PropsStore<ReturnType<typeof createLayoutStore>>;
5
+ /** The three-column frame (see module doc). */
6
+ export declare function AppFrame({ useStore, useSessions, actions, renderSlot, }: AppFrameProps): import("react").JSX.Element;
7
+ //# sourceMappingURL=AppFrame.d.ts.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Pure concession-chain column solver for the three-column AppFrame.
3
+ * Chain order is fixed by contract: keep center >= CENTER_MIN by shrinking
4
+ * details, then auto-closing it (derived zero width — preferred width
5
+ * preferences are never rewritten, so widening the window restores them).
6
+ * The sidebar never concedes: its rendered width is always the drag
7
+ * preference (or the collapsed rail), and center absorbs any remaining
8
+ * deficit as the last resort. Inputs are the layout store's plain width
9
+ * preferences (0 = closed); a closed sidebar resolves to the fixed
10
+ * SIDEBAR_COLLAPSED control rail while closed details resolve to zero width.
11
+ * The SIDEBAR_AUTO_COLLAPSE breakpoint is consumed by AppFrame, which decides
12
+ * the effective sidebar preference before solving; the solver itself stays
13
+ * breakpoint-free.
14
+ */
15
+ /** Resolved widths for one frame; center may drop below CENTER_MIN only at the final fallback. */
16
+ export interface Columns {
17
+ sidebar: number;
18
+ center: number;
19
+ details: number;
20
+ }
21
+ /** Center column floor; only the final fallback may go below it. */
22
+ export declare const CENTER_MIN = 640;
23
+ /** Sidebar drag clamp floor. */
24
+ export declare const SIDEBAR_MIN = 280;
25
+ /** Sidebar drag clamp ceiling. */
26
+ export declare const SIDEBAR_MAX = 420;
27
+ /** Sidebar width before any user drag (= the drag floor). */
28
+ export declare const SIDEBAR_DEFAULT = 280;
29
+ /** Closed-sidebar rail: a 24px icon column between 16px horizontal paddings. */
30
+ export declare const SIDEBAR_COLLAPSED = 56;
31
+ /** Viewport width below which the sidebar auto-collapses to the rail (deepsuite
32
+ * LG breakpoint); a manual toggle below it re-expands over the squeezed center
33
+ * (stores.ts narrowExpanded). */
34
+ export declare const SIDEBAR_AUTO_COLLAPSE = 1024;
35
+ /** Details drag clamp floor. */
36
+ export declare const DETAILS_MIN = 300;
37
+ /** Details drag clamp ceiling. */
38
+ export declare const DETAILS_MAX = 520;
39
+ /** Details width before any user drag. */
40
+ export declare const DETAILS_DEFAULT = 360;
41
+ /**
42
+ * Clamp a panel width into its contract range.
43
+ * @param px - requested width.
44
+ * @param min - range lower bound.
45
+ * @param max - range upper bound.
46
+ * @returns the clamped width.
47
+ */
48
+ export declare function clampWidth(px: number, min: number, max: number): number;
49
+ /**
50
+ * Solve the three column widths for one viewport frame. Pure: no hysteresis —
51
+ * the output is a function of (viewport, preferences) only, so recovery on
52
+ * re-widening is automatic. Preferences re-clamp here because they cross the
53
+ * store boundary and callers may still supply stale ranges.
54
+ * @param viewport - available frame width in px.
55
+ * @param sidebar - sidebar width preference in px (0 = closed).
56
+ * @param details - details width preference in px (0 = closed).
57
+ * @returns resolved widths; details 0 means visually closed (never unmounted), while a closed sidebar keeps its compact rail.
58
+ */
59
+ export declare function computeColumns(viewport: number, sidebar: number, details: number): Columns;
60
+ //# sourceMappingURL=columns.d.ts.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Layout plugin, browser half: one register() call contributes AppFrame into
3
+ * the runtime's built-in 'root' slot and, in the same breath, declares the
4
+ * four child slots (declaration = exclusive render authority), seats the
5
+ * layout store (panel geometry), and wires the panel-action service face.
6
+ * ctx.layout is the cross-plugin panel-action contract; navigation state lives
7
+ * with the runtime sessions service. A second effect seats the theme
8
+ * presenter, which projects ctx.theme snapshots onto document.body.
9
+ */
10
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
11
+ export { LayoutService } from './service.ts';
12
+ export type { ILayout } from './service.ts';
13
+ declare module '@deepseek-ai/cordis' {
14
+ interface Context {
15
+ /** The outward face only; the concrete service stays inside this plugin. */
16
+ layout: import('./service.ts').ILayout;
17
+ }
18
+ }
19
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
20
+ interface SlotMap {
21
+ 'sidebar': {
22
+ kind: 'single';
23
+ scope: 'root';
24
+ owner: SidebarOwnerProps;
25
+ };
26
+ 'conversation': {
27
+ kind: 'single';
28
+ scope: 'session-maybe';
29
+ owner: ConvOwnerProps;
30
+ };
31
+ 'details': {
32
+ kind: 'single';
33
+ scope: 'session';
34
+ owner: DetailsOwnerProps;
35
+ };
36
+ }
37
+ }
38
+ /** Sidebar owner share: live column state from the frame's concession solve. */
39
+ export interface SidebarOwnerProps {
40
+ /** True when the sidebar is closed (the column renders the compact control rail). */
41
+ collapsed: boolean;
42
+ /** Rendered column width in px (SIDEBAR_COLLAPSED when collapsed). */
43
+ width: number;
44
+ }
45
+ /** Conversation owner share: business state and actions belong to the registrant. */
46
+ export interface ConvOwnerProps {
47
+ }
48
+ /** Details owner share: empty — sessionId arrives as a framework-standard prop. */
49
+ export interface DetailsOwnerProps {
50
+ }
51
+ /** Required services (cordis fiber inject — the loader passes the whole export surface as an object plugin). */
52
+ export declare const inject: string[];
53
+ /**
54
+ * Client plugin body: provide ctx.layout, then one register() call — AppFrame
55
+ * into 'root' with the four child-slot declarations, the layout store seat,
56
+ * and the inject hook that hands the store's bound actions to the service.
57
+ * @param ctx - client root context.
58
+ */
59
+ export declare function apply(ctx: ClientContext): void;
60
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * LayoutService: the cross-plugin panel-action face behind ctx.layout.
3
+ * Panel geometry itself lives in the root entry's layout store (stores.ts);
4
+ * the current-session selection lives with the runtime sessions service, and
5
+ * the per-session active view dissolved into ui-conversation's session store
6
+ * (its only consumer). What remains here is the contract other plugins'
7
+ * apply worlds reach for panel transitions (sidebar toggle from ui-sidebar,
8
+ * details open/close from ui-conversation) — writes stay inside the store's
9
+ * declared action set, delivered as the registration's bound actions.
10
+ */
11
+ import type { BoundActions } from '@deepseek-ai/dsh-client-ui-slots';
12
+ import type { createLayoutStore } from './stores.ts';
13
+ /** The layout store's bound action set (framework-baked, draft params peeled). */
14
+ export type PanelActions = BoundActions<ReturnType<typeof createLayoutStore>>;
15
+ /**
16
+ * The outward layout face (`ctx.layout`): the panel transitions other
17
+ * plugins may trigger — and exactly what a test fake must supply. The
18
+ * attachPanels wiring hook stays on the concrete class (root-entry assembly
19
+ * only).
20
+ */
21
+ export interface ILayout {
22
+ /** Toggle the sidebar panel (closed ⟷ contract default width). */
23
+ toggleSidebar(): void;
24
+ /** Open the details panel (no-op when already open). */
25
+ openDetails(): void;
26
+ /** Close the details panel. */
27
+ closeDetails(): void;
28
+ }
29
+ /** Cross-plugin panel-action face (ctx.layout). */
30
+ export declare class LayoutService implements ILayout {
31
+ #private;
32
+ /**
33
+ * Adopt the root entry's bound store actions. Called from the root
34
+ * registration's inject hook (a sanctioned assembly side effect), so the
35
+ * face is live from the entry's first render; on entry re-register the
36
+ * fresh actions overwrite the stale set.
37
+ * @param actions - bound actions of the entry's layout store instance.
38
+ */
39
+ attachPanels(actions: PanelActions): void;
40
+ /** Toggle the sidebar panel (closed ⟷ contract default width). */
41
+ toggleSidebar(): void;
42
+ /** Open the details panel (no-op when already open). */
43
+ openDetails(): void;
44
+ /** Close the details panel. */
45
+ closeDetails(): void;
46
+ }
47
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The root entry's transient layout store: panel geometry as plain widths in
3
+ * px (0 = closed). Module level exports the factory only — a module-level
4
+ * handle would pin the store's identity in the module
5
+ * cache (a de-facto singleton surviving plugin reloads). register() receives
6
+ * the factory (exclusive use: the framework instantiates per entry), AppFrame
7
+ * derives its PropsStore share from the return type, and the service face
8
+ * receives the bound actions through the registration's inject hook.
9
+ */
10
+ import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client';
11
+ /**
12
+ * Layout store state: panel width preferences in px (0 = closed), plus the
13
+ * narrow-viewport pair — `narrow` mirrors AppFrame's breakpoint reading
14
+ * (viewport < SIDEBAR_AUTO_COLLAPSE) so toggleSidebar can pick semantics, and
15
+ * `narrowExpanded` is the manual override that re-expands the auto-collapsed
16
+ * sidebar over the squeezed center without rewriting the width preference.
17
+ */
18
+ type LayoutState = {
19
+ sidebar: number;
20
+ details: number;
21
+ narrow: boolean;
22
+ narrowExpanded: boolean;
23
+ };
24
+ /**
25
+ * Annotation twin of the actions literal below (the export needs a declared
26
+ * return type); drift fails assignability at the defineStore call.
27
+ */
28
+ type LayoutActions = {
29
+ setSidebar: (draft: LayoutState, px: number) => void;
30
+ setDetails: (draft: LayoutState, px: number) => void;
31
+ toggleSidebar: (draft: LayoutState) => void;
32
+ setNarrow: (draft: LayoutState, narrow: boolean) => void;
33
+ openDetails: (draft: LayoutState) => void;
34
+ closeDetails: (draft: LayoutState) => void;
35
+ };
36
+ /**
37
+ * Create the layout panel store handle. The preference IS the width, so
38
+ * closing a panel forgets its drag width — reopening restores the contract
39
+ * default. Actions are the complete write set: drag writes clamp
40
+ * into the panel's contract range and never cross the open/closed line;
41
+ * open/close transitions write 0 / the default explicitly. Below the
42
+ * auto-collapse breakpoint (AppFrame feeds setNarrow) the sidebar toggle
43
+ * flips the narrowExpanded override instead of the preference.
44
+ * @returns the store handle (spec + type + identity + factory in one).
45
+ */
46
+ export declare function createLayoutStore(): EngineStoreHandle<LayoutState, LayoutActions>;
47
+ export {};
48
+ //# sourceMappingURL=stores.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Global theme DOM applier: projects the resolved ThemeSnapshot onto the
3
+ * document — `html { color-scheme }` for native UA chrome (scrollbars, form
4
+ * controls), `body[data-ds-dark-theme]` for the token palette, the active
5
+ * theme's alias-token overrides as inline CSS variables on body, and one
6
+ * presenter-owned `meta[name="theme-color"]` for surrounding browser UI. Pure
7
+ * DOM writes, no React involvement; the presenter only ever retracts what it
8
+ * wrote itself, so foreign attributes, metadata, and inline styles survive.
9
+ */
10
+ import type { ThemeSnapshot } from '@deepseek-ai/dsh-client-ui-theme/client';
11
+ /** Body attribute selecting the dark base palette in the token stylesheets. */
12
+ export declare const DARK_ATTRIBUTE = "data-ds-dark-theme";
13
+ /** Applies theme snapshots to the document; one instance per plugin fiber. */
14
+ export declare class ThemePresenter {
15
+ /** Token names this presenter wrote in the last apply (its retraction set). */
16
+ private appliedTokens;
17
+ /** The single metadata node this presenter inserts and removes. */
18
+ private readonly themeColorMeta;
19
+ /** Create the presenter-owned metadata node before the first snapshot arrives. */
20
+ constructor();
21
+ /**
22
+ * Project a snapshot onto the document: set root `color-scheme` and the body
23
+ * palette attribute from `active.colorScheme` (never the id — `system` is
24
+ * resolved upstream), then replace the previously applied token variables
25
+ * with `active.tokens`. Browser theme-color metadata follows the computed
26
+ * body background after those writes, so the rendered palette remains the
27
+ * color authority.
28
+ * @param snapshot - resolved theme snapshot from ctx.theme.
29
+ */
30
+ apply(snapshot: ThemeSnapshot): void;
31
+ /** Retract root color-scheme, the palette attribute, token variables, and the owned metadata node. */
32
+ dispose(): void;
33
+ }
34
+ //# sourceMappingURL=theme-presenter.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** Host loader entry for the browser-only layout plugin. */
2
+ /** Provides no host-side behavior. */
3
+ export declare function apply(): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-layout`.
3
+ * @module @deepseek-ai/dsh-client-ui-layout/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "client-ui-layout-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@deepseek-ai/dsh-client-ui-layout",
3
+ "description": "Shell plugin: three-column AppFrame with drag handles, ctx.layout viewing-state service (navigation + panels)",
4
+ "version": "0.0.1-rc.1",
5
+ "publishConfig": {
6
+ "access": "restricted"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/client/ui-layout"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./client": {
26
+ "types": "./lib/types/client/index.d.ts",
27
+ "default": "./lib/client.js"
28
+ },
29
+ "./src/*": "./src/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "dsh": {
33
+ "client": {
34
+ "inject": [
35
+ "@deepseek-ai/dsh-client-runtime",
36
+ "@deepseek-ai/dsh-client-ui-theme"
37
+ ],
38
+ "platform": "web"
39
+ }
40
+ },
41
+ "license": "BSD-3-Clause",
42
+ "peerDependencies": {
43
+ "react": "^18.2.0",
44
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
45
+ "@deepseek-ai/dsh-client-ui-theme": "^0.0.1-rc.1",
46
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
47
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
48
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1"
49
+ },
50
+ "devDependencies": {
51
+ "@types/react": "~18.3.1",
52
+ "react": "^18.2.0",
53
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
54
+ "@deepseek-ai/dsh-client-ui-theme": "^0.0.1-rc.1",
55
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
56
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
57
+ "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
58
+ "@deepseek-ai/cordis": "^4.0.1-rc.1"
59
+ },
60
+ "files": [
61
+ "lib/index.js",
62
+ "lib/invariant.js",
63
+ "lib/client.js",
64
+ "lib/types/**/*.d.ts"
65
+ ],
66
+ "scripts": {
67
+ "bundle": "tsdown",
68
+ "watch": "tsdown --watch"
69
+ }
70
+ }