@deepseek-ai/dsh-client-ui-message-feedback 0.1.1-rc.2 → 0.1.2-alpha.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-message-feedback/README.md
5
- README.md: d3bb4e28b95da2fde0d26b7ef83ebca377b4939f
6
- README.zh.md: d823bb9de2b7d39d0bc5d00164b8ce8dffec4a34
5
+ README.md: 2610d0d362438977512ce753eecb66cb77497812
6
+ README.zh.md: 58e4dc10d8d42c14cb711261b401d9cf3bec8f98
package/README.md CHANGED
@@ -1,15 +1,62 @@
1
+ ---
2
+ description: "Per-message feedback for the Web GUI: the Like/Dislike pair and optional note in the finalized assistant message's action row; for users and maintainers of the feedback experience."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-client-ui-message-feedback
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Per-message feedback plugin, browser half: a Like/Dislike pair plus an optional note, contributed as the `feedback` entry (order 10) of the `conversation.chat.assistant-actions` strip. The strip is declared by `ui-conversation` and rendered inside the finalized assistant message's IconActions row, between copy and branch, so the controls inherit that row's chrome and hover behavior. The note editor itself does not sit in that row: it is a `role="dialog"` popover portaled to `document.body` and anchored under its trigger, so the row keeps its single line whether the editor is open or closed and the panel is not clipped by the conversation column. A rating or list-load failure shows inline in the row; a note-save failure shows inside the popover, which stays open so the draft can be corrected. Only finalized messages reach the slot — an interruption-frozen partial carries no `messageId` and therefore no feedback controls. The strip renders once per turn, on the closing assistant message that owns the turn's IconActions row: earlier steps of a multi-step turn produce tool rows rather than a rateable body, so they present no controls even though the Host would accept them as targets.
10
+ ## Summary
11
+
12
+ This package adds per-message feedback to the Web GUI: a Like/Dislike pair plus an optional note, contributed as the `feedback` entry of the finalized assistant message's action strip. It renders on the closing assistant message of each turn — earlier steps of a multi-step turn produce tool rows rather than a rateable body. One controller per Session backs every message control in that Session, so a single list read seeds the whole transcript. Feedback is a sidecar: ratings and notes never enter the session log, the model context, or telemetry.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount this plugin alongside `ui-conversation`; the Like/Dislike pair then appears in the action row of each turn's closing assistant message, between copy and branch. Clicking the recorded rating retracts the feedback; switching sides carries the existing note forward. The note editor is a dialog popover anchored under its trigger, so the row keeps its single line whether the editor is open or closed.
29
+
30
+ ### Failures
31
+
32
+ A rating or list-load failure shows inline in the row; a note-save failure shows inside the popover, which stays open so the draft can be corrected. Only finalized messages reach the slot — an interruption-frozen partial carries no `messageId` and therefore no feedback controls.
33
+
34
+ -----
35
+
36
+ <a id="understand-the-implementation"></a>
37
+ ## Understand the implementation
38
+
39
+ <details>
40
+ <summary>Implementation internals — click to expand</summary>
6
41
 
7
- One `MessageFeedbackController` per Session backs every message control in that Session, so a single `messageFeedback.list` read seeds the whole transcript. The read is deferred to the first hover or focus rather than fired on mount, because the controls mount once per settled message in the visible history.
42
+ The package contributes the `feedback` entry (order 10) of `conversation.chat.assistant-actions`, declared by ui-conversation and rendered inside the finalized assistant message's IconActions row. One `MessageFeedbackController` per Session backs every message control in that Session, so a single `messageFeedback.list` read seeds the whole transcript; the read is deferred to the first hover or focus rather than fired on mount. Mutations go through `ctx.remote.messageFeedback`; the Host owns per-item compare-and-set. Every `put` and `delete` carries the `version` this controller last observed, and a `version-conflict` reply carries the authoritative item, so a lost race reconciles from the reply itself instead of refetching. Mutations serialize per Session, so a queued operation always compares against the committed version.
8
43
 
9
- Mutations go through `ctx.remote.messageFeedback`; the Host owns per-item compare-and-set. Every `put` and `delete` carries the `version` this controller last observed, and a `version-conflict` reply carries the authoritative item, so a lost race reconciles from the reply itself instead of refetching the Session. Mutations serialize per Session, so a queued operation always compares against the committed version. Re-clicking the recorded rating retracts the feedback; switching sides carries the existing note forward.
44
+ </details>
10
45
 
11
- The `/client` exports are the plugin body (`apply`/`inject`), the `MessageFeedbackActions` component, the `MessageFeedbackController` class, and the injected face types.
46
+ -----
12
47
 
48
+ <a id="further-exploration"></a>
49
+ ## Further Exploration
50
+
51
+ Read these pages when the feedback surface is not enough. They move from the browser strip to the sidecar backend and the conversation shell.
52
+
53
+ - [dsh-message-feedback](../../feedback/message-feedback/README.md) — the sidecar backend that owns per-item compare-and-set.
54
+ - [ui-conversation](../ui-conversation/README.md) — declares the assistant-actions strip and renders the action row.
55
+ - [Client package map](../README.md) — adjacent browser UI packages.
56
+
57
+ -----
58
+
59
+ <a id="model-experience"></a>
13
60
  ## Model Experience
14
61
 
15
62
  None, as feedback is a sidecar that never enters the append-only Session log, the model context, or telemetry; no rating or note is ever visible to the model.
@@ -20,6 +67,21 @@ None; no feedback mutation touches the history tail.
20
67
 
21
68
  ## Known Limitations and Deferred Work
22
69
 
70
+ <a id="known-limitations-and-deferred-work"></a>
71
+
72
+
73
+ These limits define the current feedback surface. They are current package constraints, not a general rating comparison or a task backlog.
74
+
23
75
  - **Note size is a Host policy** — the deployment configures `maxNoteBytes` (8192 in the Web bundle) and the Host rejects an oversized note with `note-too-large`. The editor does not pre-check the limit, so an oversized note fails on save rather than while typing.
24
76
  - **No cross-tab push** — a second tab's rating becomes visible on reconnect or on the next conflict reply, not immediately; the sidecar publishes no live frames.
25
- - **Chat view only** — the trajectory and waterfall views render no feedback controls even though their assistant nodes now carry the same `messageId`.
77
+ - **Chat view only** — the trajectory and waterfall views render no feedback controls even though their assistant nodes carry the same `messageId`.
78
+
79
+ <a id="dev-note"></a>
80
+ ### Dev Note
81
+
82
+ <details>
83
+ <summary>Working context for maintainers — click to expand</summary>
84
+
85
+ None.
86
+
87
+ </details>
package/README.zh.md CHANGED
@@ -1,15 +1,62 @@
1
+ ---
2
+ description: "Web GUI 的逐消息反馈:已定稿助手消息动作行中的 Like/Dislike 对与可选备注;供反馈体验的用户与维护者阅读。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-client-ui-message-feedback
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 单条消息反馈插件的浏览器侧:一对 Like/Dislike 按钮加一个可选备注,作为 `conversation.chat.assistant-actions` 条带的 `feedback` 条目(order 10)贡献。该条带由 `ui-conversation` 声明,渲染在已定稿助手消息的 IconActions 行内、复制与分支之间,因此控件沿用该行的样式与 hover 行为。备注编辑器本身不在这一行里:它是一个 `role="dialog"` 的浮层,portal 到 `document.body` 并锚定在其触发按钮下方,因此无论编辑器是否打开该行都保持单行,面板也不会被会话列裁掉。评分或列表加载失败在行内展示;备注保存失败在浮层内展示,且面板保持打开以便修正草稿。只有已定稿的消息能到达这个 slot——被中断冻结的部分输出不带 `messageId`,因此也没有反馈控件。该操作栏每个 Turn 渲染一次,位于持有该 Turn IconActions 行的收尾助手消息上:多步骤 Turn 中较早的步骤产出的是工具行而非可评分正文,因此即使 Host 会接受它们作为目标,界面上也不出现控件。
10
+ ## 概述
11
+
12
+ 本包为 Web GUI 增加逐消息反馈:一对 Like/Dislike 按钮加一个可选备注,作为已定稿助手消息动作条的 `feedback` 条目贡献。它渲染在每个轮次的收尾助手消息上——多步骤轮次中较早的步骤产出工具行而非可评分正文。每个 Session 一个控制器支撑该 Session 内所有消息的控件,因此一次列表读取即可填充整段对话。反馈是 sidecar:评分与备注绝不进入会话日志、模型上下文或遥测。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 与 `ui-conversation` 一起挂载本插件;Like/Dislike 对随即出现在每个轮次收尾助手消息的动作行中,位于复制与分支之间。再次点击已记录的评分会撤回反馈;切换到另一侧会保留既有备注。备注编辑器是一个锚定在其触发按钮下方的对话框浮层,因此无论编辑器是否打开,该行都保持单行。
29
+
30
+ ### 失败
31
+
32
+ 评分或列表加载失败在行内展示;备注保存失败在浮层内展示,面板保持打开以便修正草稿。只有已定稿的消息能到达该槽位——被中断冻结的部分输出不带 `messageId`,因此没有反馈控件。
33
+
34
+ -----
35
+
36
+ <a id="understand-the-implementation"></a>
37
+ ## 理解实现
38
+
39
+ <details>
40
+ <summary>实现细节——点击展开</summary>
6
41
 
7
- 每个 Session 一个 `MessageFeedbackController`,支撑该 Session 内所有消息的控件,因此一次 `messageFeedback.list` 读取即可填充整段对话。该读取延迟到首次 hover 或 focus 才发起,而不是在挂载时触发,因为可见历史中每条已结束的消息都会挂载一次控件。
42
+ 本包贡献 `conversation.chat.assistant-actions` 的 `feedback` 条目(order 10),由 ui-conversation 声明并渲染在已定稿助手消息的 IconActions 行内。每个 Session 一个 `MessageFeedbackController` 支撑该 Session 内所有消息的控件,因此一次 `messageFeedback.list` 读取即可填充整段对话;该读取延迟到首次 hover 或 focus 才发起,而非挂载时触发。变更经 `ctx.remote.messageFeedback` 提交,按条目的比较并交换由宿主负责。每次 `put` 与 `delete` 都携带本控制器最后观察到的 `version`;`version-conflict` 响应带回权威条目,因此竞争失败时直接用该响应本身对账,无需重新拉取。变更按 Session 串行,排队中的操作总是与已提交的版本比较。
8
43
 
9
- 变更通过 `ctx.remote.messageFeedback` 提交,按条目的 compare-and-set 由 Host 负责。每次 `put` 和 `delete` 都携带本 controller 最后观察到的 `version`;`version-conflict` 响应会带回权威条目,因此竞争失败时直接用该响应对账,无需重新拉取整个 Session。变更按 Session 串行,排队中的操作总是与已提交的版本比较。再次点击已记录的评分会撤回反馈;切换到另一侧会保留已有备注。
44
+ </details>
10
45
 
11
- `/client` 导出插件本体(`apply`/`inject`)、`MessageFeedbackActions` 组件、`MessageFeedbackController` 类以及注入面类型。
46
+ -----
12
47
 
48
+ <a id="further-exploration"></a>
49
+ ## 进一步探索
50
+
51
+ 当反馈面不够用时阅读以下页面。它们从浏览器条带进入 sidecar 后端与会话外壳。
52
+
53
+ - [dsh-message-feedback](../../feedback/message-feedback/README.zh.md)——拥有按条目比较并交换的 sidecar 后端。
54
+ - [ui-conversation](../ui-conversation/README.zh.md)——声明助手动作条并渲染动作行。
55
+ - [客户端包映射](../README.zh.md)——相邻的浏览器 UI 包。
56
+
57
+ -----
58
+
59
+ <a id="model-experience"></a>
13
60
  ## 模型体验
14
61
 
15
62
  无。反馈是 sidecar,不进入 append-only 的 Session 日志、模型上下文或遥测;任何评分与备注对模型都不可见。
@@ -18,8 +65,23 @@
18
65
 
19
66
  无;任何反馈变更都不触碰历史尾部。
20
67
 
21
- ## 已知限制与暂缓事项
68
+ ## 已知限制与延期工作
69
+
70
+ <a id="known-limitations-and-deferred-work"></a>
71
+
72
+
73
+ 这些限制界定了当前反馈表面。它们是当前包约束,不是通用评分对比或任务积压。
74
+
75
+ - **备注大小是宿主策略**——部署方配置 `maxNoteBytes`(Web bundle 中为 8192),超长备注由宿主以 `note-too-large` 拒绝。编辑器不预先校验该上限,因此超长备注在保存时才失败,而不是在输入过程中。
76
+ - **无跨标签页推送**——另一个标签页的评分要等到重连或下一次冲突响应才可见,不会立即出现;该 sidecar 不发布实时帧。
77
+ - **仅限对话视图**——trajectory 与 waterfall 视图不渲染反馈控件,尽管它们的助手节点也带有相同的 `messageId`。
78
+
79
+ <a id="dev-note"></a>
80
+ ### 开发备注
81
+
82
+ <details>
83
+ <summary>维护者的工作上下文——点击展开</summary>
84
+
85
+ 无。
22
86
 
23
- - **备注大小是 Host 策略** —— 部署方配置 `maxNoteBytes`(Web bundle 中为 8192),超长备注由 Host 以 `note-too-large` 拒绝。编辑器不预先校验该上限,因此超长备注在保存时才失败,而不是在输入过程中。
24
- - **无跨标签页推送** —— 另一个标签页的评分要等到重连或下一次冲突响应才可见,不会立即出现;该 sidecar 不发布实时帧。
25
- - **仅限对话视图** —— trajectory 与 waterfall 视图不渲染反馈控件,尽管它们的助手节点现在也带有相同的 `messageId`。
87
+ </details>
package/lib/client.js CHANGED
@@ -58,7 +58,7 @@ window.__ModuleLoader__.load({
58
58
  * control in that Session, so a single list read seeds them all.
59
59
  */
60
60
  var MessageFeedbackController = class {
61
- remote;
61
+ ctx;
62
62
  sessionId;
63
63
  view = INITIAL_VIEW;
64
64
  listeners = /* @__PURE__ */ new Set();
@@ -66,11 +66,11 @@ window.__ModuleLoader__.load({
66
66
  operationTail = Promise.resolve();
67
67
  disposed = false;
68
68
  /**
69
- * @param remote - the messageFeedback Remote namespace.
69
+ * @param ctx - the browser plugin context carrying the messageFeedback Remote namespace.
70
70
  * @param sessionId - Session owning every addressed assistant message.
71
71
  */
72
- constructor(remote, sessionId) {
73
- this.remote = remote;
72
+ constructor(ctx, sessionId) {
73
+ this.ctx = ctx;
74
74
  this.sessionId = sessionId;
75
75
  }
76
76
  /** Return the cached immutable view. */
@@ -185,7 +185,7 @@ window.__ModuleLoader__.load({
185
185
  }
186
186
  /** Commit one put against the observed version and reconcile a conflict. */
187
187
  async putCommitted(messageId, rating, note, observed) {
188
- const carried = await this.remote.put({
188
+ const carried = await this.ctx.remote.messageFeedback.put({
189
189
  sessionId: this.sessionId,
190
190
  messageId,
191
191
  rating,
@@ -203,7 +203,7 @@ window.__ModuleLoader__.load({
203
203
  }
204
204
  /** Commit one delete against the observed version and reconcile a conflict. */
205
205
  async deleteCommitted(messageId, observed) {
206
- const carried = await this.remote.delete({
206
+ const carried = await this.ctx.remote.messageFeedback.delete({
207
207
  sessionId: this.sessionId,
208
208
  messageId,
209
209
  ifVersion: observed.version
@@ -224,55 +224,37 @@ window.__ModuleLoader__.load({
224
224
  }
225
225
  /** Fetch the whole sidecar and publish it as the seeded view. */
226
226
  async load() {
227
- try {
228
- const carried = await this.remote.list({ sessionId: this.sessionId });
229
- if (this.disposed) return OK;
230
- if (!carried.ok) {
231
- this.publish({
232
- status: "error",
233
- items: this.view.items,
234
- error: carried.error.message
235
- });
236
- return carrierFailure(carried.error);
237
- }
238
- const result = carried.value;
239
- if (!result.ok) {
240
- this.publish({
241
- status: "error",
242
- items: this.view.items,
243
- error: describe(result.error.code)
244
- });
245
- return fail(result.error.code);
246
- }
247
- const items = /* @__PURE__ */ new Map();
248
- for (const item of result.value.items) items.set(item.messageId, item);
227
+ const carried = await this.ctx.remote.messageFeedback.list({ sessionId: this.sessionId });
228
+ if (this.disposed) return OK;
229
+ if (!carried.ok) {
249
230
  this.publish({
250
- status: "ready",
251
- items,
252
- error: null
231
+ status: "error",
232
+ items: this.view.items,
233
+ error: carried.error.message
253
234
  });
254
- return OK;
255
- } catch (error) {
256
- if (this.disposed) return OK;
257
- const message = error instanceof Error ? error.message : "message feedback list failed";
235
+ return carrierFailure(carried.error);
236
+ }
237
+ const result = carried.value;
238
+ if (!result.ok) {
258
239
  this.publish({
259
240
  status: "error",
260
241
  items: this.view.items,
261
- error: message
242
+ error: describe(result.error.code)
262
243
  });
263
- return {
264
- ok: false,
265
- error: {
266
- code: "transport",
267
- message
268
- }
269
- };
244
+ return fail(result.error.code);
270
245
  }
246
+ const items = /* @__PURE__ */ new Map();
247
+ for (const item of result.value.items) items.set(item.messageId, item);
248
+ this.publish({
249
+ status: "ready",
250
+ items,
251
+ error: null
252
+ });
253
+ return OK;
271
254
  }
272
255
  /**
273
256
  * Serialize one mutation behind this Session's prior mutation so queued
274
- * operations always compare against the committed version, and translate a
275
- * transport throw into the same settled shape the controls already render.
257
+ * operations always compare against the committed version.
276
258
  */
277
259
  mutate(operation, options = {}) {
278
260
  const guarded = async () => {
@@ -282,17 +264,7 @@ window.__ModuleLoader__.load({
282
264
  if (!loaded.ok) return loaded;
283
265
  if (this.disposed) return DISPOSED;
284
266
  }
285
- try {
286
- return await operation();
287
- } catch (error) {
288
- return {
289
- ok: false,
290
- error: {
291
- code: "transport",
292
- message: error instanceof Error ? error.message : "message feedback mutation failed"
293
- }
294
- };
295
- }
267
+ return await operation();
296
268
  };
297
269
  const result = this.operationTail.then(guarded, guarded);
298
270
  this.operationTail = result.then(() => void 0);
@@ -326,7 +298,7 @@ window.__ModuleLoader__.load({
326
298
  };
327
299
  //#endregion
328
300
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-message-feedback/src/client/MessageFeedbackActions.module.css.mjs
329
- const css = "._8_XoUG_action{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}._8_XoUG_action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._8_XoUG_action:disabled{cursor:default;opacity:.4}._8_XoUG_action[data-active]{color:var(--dsw-alias-label-primary)}._8_XoUG_noteOpen{max-width:220px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;background:0 0;border:none;border-radius:14px;padding:0 8px;font-size:13px;line-height:28px;overflow:hidden}._8_XoUG_noteOpen:hover,._8_XoUG_noteOpen[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._8_XoUG_notePanel{z-index:1100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);width:320px;max-width:min(360px,100vw - 24px);max-height:calc(100vh - 24px);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:12px;flex-direction:column;gap:8px;padding:8px;display:flex;position:fixed;overflow-y:auto}._8_XoUG_noteInput{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);font:inherit;resize:vertical;border-radius:8px;padding:6px 8px;font-size:13px}._8_XoUG_noteActions{justify-content:flex-end;gap:6px;display:flex}._8_XoUG_noteSave,._8_XoUG_noteCancel{cursor:pointer;border:none;border-radius:14px;height:28px;padding:0 10px;font-size:13px}._8_XoUG_noteSave{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}._8_XoUG_noteSave:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}._8_XoUG_noteSave:disabled{cursor:default;opacity:.4}._8_XoUG_noteCancel{color:var(--dsw-alias-label-tertiary);background:0 0}._8_XoUG_noteCancel:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._8_XoUG_failure{color:var(--dsw-alias-label-tertiary);padding-left:4px;font-size:13px;line-height:20px}";
301
+ const css = "._8_XoUG_action{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}._8_XoUG_action svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px))}._8_XoUG_action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._8_XoUG_action:disabled{cursor:default;opacity:.4}._8_XoUG_action[data-active]{color:var(--dsw-alias-label-primary)}._8_XoUG_noteOpen{max-width:220px;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(28px + var(--dsh-content-font-delta,0px));white-space:nowrap;text-overflow:ellipsis;cursor:pointer;background:0 0;border:none;border-radius:14px;padding:0 8px;overflow:hidden}._8_XoUG_noteOpen:hover,._8_XoUG_noteOpen[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._8_XoUG_notePanel{z-index:1100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);width:320px;max-width:min(360px,100vw - 24px);max-height:calc(100vh - 24px);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:12px;flex-direction:column;gap:8px;padding:8px;display:flex;position:fixed;overflow-y:auto}._8_XoUG_noteInput{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);font:inherit;resize:vertical;border-radius:8px;padding:6px 8px;font-size:13px}._8_XoUG_noteActions{justify-content:flex-end;gap:6px;display:flex}._8_XoUG_noteSave,._8_XoUG_noteCancel{cursor:pointer;border:none;border-radius:14px;height:28px;padding:0 10px;font-size:13px}._8_XoUG_noteSave{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}._8_XoUG_noteSave:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}._8_XoUG_noteSave:disabled{cursor:default;opacity:.4}._8_XoUG_noteCancel{color:var(--dsw-alias-label-tertiary);background:0 0}._8_XoUG_noteCancel:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}._8_XoUG_failure{color:var(--dsw-alias-label-tertiary);padding-left:4px;font-size:13px;line-height:20px}";
330
302
  const tagId = "@deepseek-ai/dsh-client-ui-message-feedback/MessageFeedbackActions.module.css";
331
303
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
332
304
  const tag = document.createElement("style");
@@ -684,7 +656,7 @@ window.__ModuleLoader__.load({
684
656
  const controllerFor = (sessionId) => {
685
657
  let controller = controllers.get(sessionId);
686
658
  if (controller === void 0) {
687
- controller = new MessageFeedbackController(ctx.remote.messageFeedback, sessionId);
659
+ controller = new MessageFeedbackController(ctx, sessionId);
688
660
  controllers.set(sessionId, controller);
689
661
  }
690
662
  return controller;
@@ -6,33 +6,11 @@
6
6
  * instead of refetching the whole Session.
7
7
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/controller
8
8
  */
9
- import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
9
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
10
10
  import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
11
- import type { MessageId, SessionId } from '@deepseek-ai/dsh-client-connection/client';
12
- import type { MessageFeedbackDeleteResult, MessageFeedbackItem, MessageFeedbackListResult, MessageFeedbackPutResult, MessageFeedbackRating } from '@deepseek-ai/dsh-message-feedback/types';
13
- /**
14
- * The three Remote calls this controller needs. The generated face wraps every
15
- * business result in {@link RemoteResult}: a carrier failure arrives as the
16
- * `ok: false` branch rather than a rejection, so this controller reads one
17
- * envelope and never wraps a call to recover a transport error.
18
- */
19
- export interface MessageFeedbackRemote {
20
- list: (request: {
21
- sessionId: SessionId;
22
- }) => Promise<RemoteResult<MessageFeedbackListResult>>;
23
- put: (request: {
24
- sessionId: SessionId;
25
- messageId: MessageId;
26
- rating: MessageFeedbackRating;
27
- note?: string;
28
- ifVersion: MessageFeedbackItem['version'] | null;
29
- }) => Promise<RemoteResult<MessageFeedbackPutResult>>;
30
- delete: (request: {
31
- sessionId: SessionId;
32
- messageId: MessageId;
33
- ifVersion: MessageFeedbackItem['version'];
34
- }) => Promise<RemoteResult<MessageFeedbackDeleteResult>>;
35
- }
11
+ import type { MessageId } from '@deepseek-ai/dsh-api-remotes/client';
12
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
13
+ import type { MessageFeedbackItem, MessageFeedbackRating } from '@deepseek-ai/dsh-message-feedback/types';
36
14
  /** Load state of the one list read that seeds every per-message control. */
37
15
  export type MessageFeedbackStatus = 'cold' | 'loading' | 'ready' | 'error';
38
16
  /** Immutable view published to every per-message control in one Session. */
@@ -58,7 +36,7 @@ export type MessageFeedbackActionResult = {
58
36
  * control in that Session, so a single list read seeds them all.
59
37
  */
60
38
  export declare class MessageFeedbackController implements HostObservable<MessageFeedbackView> {
61
- private readonly remote;
39
+ private readonly ctx;
62
40
  private readonly sessionId;
63
41
  private view;
64
42
  private readonly listeners;
@@ -66,10 +44,10 @@ export declare class MessageFeedbackController implements HostObservable<Message
66
44
  private operationTail;
67
45
  private disposed;
68
46
  /**
69
- * @param remote - the messageFeedback Remote namespace.
47
+ * @param ctx - the browser plugin context carrying the messageFeedback Remote namespace.
70
48
  * @param sessionId - Session owning every addressed assistant message.
71
49
  */
72
- constructor(remote: MessageFeedbackRemote, sessionId: SessionId);
50
+ constructor(ctx: ClientContext, sessionId: SessionId);
73
51
  /** Return the cached immutable view. */
74
52
  getSnapshot: () => MessageFeedbackView;
75
53
  /** Subscribe to view replacement. */
@@ -145,8 +123,7 @@ export declare class MessageFeedbackController implements HostObservable<Message
145
123
  private load;
146
124
  /**
147
125
  * Serialize one mutation behind this Session's prior mutation so queued
148
- * operations always compare against the committed version, and translate a
149
- * transport throw into the same settled shape the controls already render.
126
+ * operations always compare against the committed version.
150
127
  */
151
128
  private mutate;
152
129
  /**
@@ -6,8 +6,8 @@
6
6
  * messageFeedback Remote; the Host owns per-item compare-and-set.
7
7
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client
8
8
  */
9
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
10
- export type { MessageFeedbackActionResult, MessageFeedbackStatus, MessageFeedbackView, MessageFeedbackRemote, } from './controller.ts';
9
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
10
+ export type { MessageFeedbackActionResult, MessageFeedbackStatus, MessageFeedbackView, } from './controller.ts';
11
11
  export type { MessageFeedbackActionProps, MessageFeedbackInjected } from './slots.ts';
12
12
  export type { MessageFeedbackKey } from './locales.ts';
13
13
  /** Required services: the slot registry, the Remote namespace, and the copy. */
@@ -8,7 +8,7 @@
8
8
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/slots
9
9
  */
10
10
  import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
- import type { MessageId } from '@deepseek-ai/dsh-client-connection/client';
11
+ import type { MessageId } from '@deepseek-ai/dsh-api-remotes/client';
12
12
  import type { MessageFeedbackRating } from '@deepseek-ai/dsh-message-feedback/types';
13
13
  import type { MessageFeedbackActionResult, MessageFeedbackView } from './controller.ts';
14
14
  /** Injected business face of one assistant-message feedback entry. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-message-feedback",
3
3
  "description": "Per-message feedback controls contributed to the assistant-message action strip, backed by the messageFeedback Host Remote",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,25 +32,17 @@
32
32
  "dsh": {
33
33
  "client": {
34
34
  "inject": [
35
- "@deepseek-ai/dsh-client-runtime",
36
35
  "@deepseek-ai/dsh-api-remotes",
37
36
  "@deepseek-ai/dsh-client-locale",
38
- "@deepseek-ai/dsh-client-ui-conversation"
37
+ "@deepseek-ai/dsh-client-ui-conversation",
38
+ "@deepseek-ai/dsh-client-ui-renderer"
39
39
  ],
40
40
  "platform": "web"
41
41
  }
42
42
  },
43
43
  "license": "MIT",
44
44
  "peerDependencies": {
45
- "@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
46
- "@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
47
- "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
48
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.1-rc.2",
49
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
50
- "@deepseek-ai/dsh-message-feedback": "^0.1.1-rc.2",
51
- "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
52
- "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
53
- "@deepseek-ai/cordis": "^4.0.1"
45
+ "@deepseek-ai/cordis": "^4.0.2"
54
46
  },
55
47
  "devDependencies": {
56
48
  "@testing-library/react": "^16.1.0",
@@ -58,18 +50,21 @@
58
50
  "@types/react-dom": "~18.3.0",
59
51
  "react": "^18.2.0",
60
52
  "react-dom": "^18.2.0",
61
- "@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
62
- "@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
63
- "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
64
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.1-rc.2",
65
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.1-rc.2",
66
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.1-rc.2",
67
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.2",
68
- "@deepseek-ai/dsh-message-feedback": "^0.1.1-rc.2",
69
- "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
70
- "@deepseek-ai/cordis": "^4.0.1",
71
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
72
- "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2"
53
+ "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.2",
54
+ "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.2",
55
+ "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2",
56
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.2-alpha.2",
57
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.2",
58
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.2",
59
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
60
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
61
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
62
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.2",
63
+ "@deepseek-ai/cordis": "^4.0.2",
64
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.2",
65
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.2",
66
+ "@deepseek-ai/dsh-message-feedback": "^0.1.2-alpha.2",
67
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
73
68
  },
74
69
  "files": [
75
70
  "lib/index.js",