@deepseek-ai/dsh-client-ui-message-feedback 0.1.5-alpha.2 → 0.1.5-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-message-feedback/README.md
5
- README.md: 8ffcf87a59ec95fe783a0379423b93d4bf965a3e
6
- README.zh.md: a29448eaaddf0f089d8fc12c1315732fd13fc304
5
+ README.md: b37278e8d115a2ad61bb17ef8d884b0d888cd6a7
6
+ README.zh.md: 89183cfa8f3321ec591594ea7e4f091cdf3ff903
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "The Web feedback surface: the Like/Dislike pair in the finalized assistant message's action row, the feedback dialog behind Dislike and `/feedback`, and the acknowledgement toast; for users and maintainers of the feedback experience."
2
+ description: "The Web feedback surface: the Like/Dislike pair in the finalized assistant message's action row, the feedback dialog behind both ratings and `/feedback`, and its acknowledgement and failure toasts; for users and maintainers of the feedback experience."
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- This package is the Web GUI's feedback surface: the Like/Dislike pair in the finalized assistant message's action strip, the feedback dialog with its acknowledgement toast in the composer overlay, and a decoration that opens the dialog from a bare `/feedback`. Like records at once and shows the toast; Dislike opens the dialog, which collects a category and an optional description. One surface per Session backs every entry, so a single list read seeds the whole transcript and one dialog serves the Session and its messages. Ratings, categories, and notes are log-only Session events that never enter model context.
12
+ This package is the Web GUI's feedback surface: the Like/Dislike pair in the finalized assistant message's action strip, the feedback dialog with its acknowledgement and failure toasts in the composer overlay, and a decoration that opens the dialog from a bare `/feedback`. Like and Dislike both open the dialog, which collects a category and an optional description before recording the selected rating. One surface per Session backs every entry, so a single list read seeds the whole transcript and one dialog serves the Session and its messages. Ratings, categories, and notes are log-only Session events that never enter model context.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -25,11 +25,11 @@ This package is the Web GUI's feedback surface: the Like/Dislike pair in the fin
25
25
  <a id="use-this-package"></a>
26
26
  ## Use this package
27
27
 
28
- Mount this plugin alongside `ui-conversation` and `ui-commands`; the Like/Dislike pair then appears in the action row of each turn's closing assistant message, between copy and branch, and the Feedback row of the composer menu opens the dialog. A recorded rating shows the filled glyph and stays visible without hover. Like records immediately and the toast thanks the user for the feedback. Dislike opens the dialog: seven category chips and a detail box, both optional; Submit records a negative judgment carrying whatever was filled in, and the conversation log travels with every feedback event. Clicking the recorded rating retracts it. A bare `/feedback`, picked from the menu or typed and sent without text, opens the same dialog for the Session; `/feedback <text>` keeps the Host command path and its acknowledgement row.
28
+ Mount this plugin alongside `ui-conversation` and `ui-commands`; the Like/Dislike pair then appears in the action row of each turn's closing assistant message, between copy and branch, and the Feedback row of the composer menu opens the dialog. A recorded rating shows the filled glyph and stays visible without hover. Like and Dislike both open the dialog: seven category chips and a detail box are optional, and Submit records the selected judgment with whatever was filled in before the toast thanks the user; the conversation log travels with every feedback event. Clicking the recorded rating retracts it without opening the dialog. A bare `/feedback`, picked from the menu or typed and sent without text, opens the same dialog for the Session; `/feedback <text>` keeps the Host command path and its acknowledgement row.
29
29
 
30
30
  ### Failures
31
31
 
32
- A rating or list-load failure shows inline in the row; a submission failure shows inside the dialog, which stays open so the draft can be corrected. Only finalized messages reach the message entry — an interruption-frozen partial carries no `messageId` and therefore no feedback controls.
32
+ A rating or list-load failure shows inline in the row; a submission failure shows in a warning toast while the dialog stays open so the draft can be corrected. Only finalized messages reach the message entry — an interruption-frozen partial carries no `messageId` and therefore no feedback controls.
33
33
 
34
34
  -----
35
35
 
@@ -41,7 +41,7 @@ A rating or list-load failure shows inline in the row; a submission failure show
41
41
 
42
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, and the `feedback-dialog` entry (order 2) of `conversation.input.overlay`, which renders the Modal and Toast primitives through body portals and centers the toast over the composer card it mounts inside. The `/feedback` decoration is an `action` registered through `ctx.commandUi.decorate`, so a menu pick or a bare Enter consumes the trigger token and opens the dialog while an argued line still reaches the Host command.
43
43
 
44
- Per Session, one `MessageFeedbackController` backs every message control and one `FeedbackDialogController` owns the dialog draft, the submission, and the toast sequence. The message controller reads `messageFeedback.list` once, deferred to the first hover or focus rather than fired on mount, and serializes mutations so each carries the version last observed; a `version-conflict` reply carries the authoritative item and reconciles the view without refetching. `toggle` reports the rating now committed, so the row acknowledges a recorded Like and not a retraction. The dialog controller submits by target: a message target puts a negative judgment with the dialog's note and category through the message controller, and the Session target records through `ctx.remote.sessionFeedback`. Success closes the draft and raises the toast; a late success from a superseded draft raises the toast without closing the new draft; a failure keeps the draft open with its code.
44
+ Per Session, one `MessageFeedbackController` backs every message control and one `FeedbackDialogController` owns the dialog draft, the submission, and the toast sequence. The message controller reads `messageFeedback.list` once, deferred to the first hover or focus rather than fired on mount, and serializes mutations so each carries the version last observed; a `version-conflict` reply carries the authoritative item and reconciles the view without refetching. Before either rating action proceeds, the row checks the committed item: the matching rating calls `retract`, which rechecks the rating in the serialized queue and becomes a no-op after a concurrent change, while any other state opens the dialog with the requested rating. The dialog controller submits by target: a message target puts that rating with the dialog's note and category through the message controller, and the Session target records through `ctx.remote.sessionFeedback`. Success closes the draft and raises the acknowledgement toast; a late success from a superseded draft raises that toast without closing the new draft; a failure keeps the draft open and raises a longer-lived warning toast.
45
45
 
46
46
  </details>
47
47
 
@@ -77,7 +77,6 @@ None; feedback mutations leave the model-visible history unchanged.
77
77
  These limits define the current feedback surface. They are current package constraints, not a general rating comparison or a task backlog.
78
78
 
79
79
  - **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 dialog does not pre-check the limit, so an oversized description for a message fails on submit rather than while typing; a Session remark has no bound.
80
- - **No note on a Like** — only the Dislike dialog collects a category and description; a Like records the bare judgment.
81
80
  - **No cross-tab push** — a second tab's rating becomes visible on reconnect or on the next conflict reply, not immediately; the controller does not consume feedback log events.
82
81
  - **Chat view only** — the trajectory and waterfall views render no feedback controls even though their assistant nodes carry the same `messageId`.
83
82
 
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Web 反馈界面:已定稿助手消息动作行中的 Like/Dislike 对、点踩与 `/feedback` 背后的反馈弹窗,以及确认 toast;供反馈体验的用户与维护者阅读。"
2
+ description: "Web 反馈界面:已定稿助手消息动作行中的 Like/Dislike 对、两种评分与 `/feedback` 共用的反馈弹窗,以及确认和失败 toast;供反馈体验的用户与维护者阅读。"
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 本包是 Web GUI 的反馈界面:已定稿助手消息动作条中的 Like/Dislike 对、输入框浮层中的反馈弹窗及其确认 toast,以及让不带文本的 `/feedback` 打开弹窗的装饰。点赞立即记录并显示 toast;点踩打开弹窗,弹窗收集分类与可选描述。每个 Session 一个 surface 支撑所有条目,因此一次列表读取即可填充整段对话,一个弹窗同时服务 Session 与其消息。评分、分类与备注是仅写日志的 Session 事件,绝不进入模型上下文。
12
+ 本包是 Web GUI 的反馈界面:已定稿助手消息动作条中的 Like/Dislike 对、输入框浮层中的反馈弹窗及其确认与失败 toast,以及让不带文本的 `/feedback` 打开弹窗的装饰。点赞和点踩都会打开弹窗,先收集分类与可选描述,再记录所选评分。每个 Session 一个 surface 支撑所有条目,因此一次列表读取即可填充整段对话,一个弹窗同时服务 Session 与其消息。评分、分类与备注是仅写日志的 Session 事件,绝不进入模型上下文。
13
13
 
14
14
  ## 目录
15
15
 
@@ -25,11 +25,11 @@ kind: "package-reference"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 与 `ui-conversation`、`ui-commands` 一起挂载本插件;Like/Dislike 对随即出现在每个轮次收尾助手消息的动作行中,位于复制与分支之间,输入框菜单里的「反馈」行则打开弹窗。已记录的评分显示实心图标,不需要悬停也一直可见。点赞立即记录,toast 显示感谢反馈。点踩打开弹窗:七个分类标签和一个详情框,都可不填;提交会记录一条带上所填内容的差评,对话日志随每个反馈事件一起投递。再次点击已记录的评分会撤回它。不带文本的 `/feedback`,无论是从菜单选中还是直接输入后发送,都会为 Session 打开同一个弹窗;`/feedback <text>` 仍走宿主命令路径并显示确认行。
28
+ 与 `ui-conversation`、`ui-commands` 一起挂载本插件;Like/Dislike 对随即出现在每个轮次收尾助手消息的动作行中,位于复制与分支之间,输入框菜单里的「反馈」行则打开弹窗。已记录的评分显示实心图标,不需要悬停也一直可见。点赞和点踩都会打开弹窗:七个分类标签和一个详情框都可不填;提交后才会记录带所填内容的对应评分并弹出感谢 toast,对话日志随每个反馈事件一起投递。再次点击已记录的评分会直接撤回,不打开弹窗。不带文本的 `/feedback`,无论是从菜单选中还是直接输入后发送,都会为 Session 打开同一个弹窗;`/feedback <text>` 仍走宿主命令路径并显示确认行。
29
29
 
30
30
  ### 失败
31
31
 
32
- 评分或列表加载失败在行内展示;提交失败在弹窗内展示,弹窗保持打开以便修正草稿。只有已定稿的消息能到达消息条目——被中断冻结的部分输出不带 `messageId`,因此没有反馈控件。
32
+ 评分或列表加载失败在行内展示;提交失败通过警告 toast 展示,弹窗保持打开以便修正草稿。只有已定稿的消息能到达消息条目——被中断冻结的部分输出不带 `messageId`,因此没有反馈控件。
33
33
 
34
34
  -----
35
35
 
@@ -41,7 +41,7 @@ kind: "package-reference"
41
41
 
42
42
  本包贡献 `conversation.chat.assistant-actions` 的 `feedback` 条目(order 10),由 ui-conversation 声明并渲染在已定稿助手消息的 IconActions 行内;同时贡献 `conversation.input.overlay` 的 `feedback-dialog` 条目(order 2),它通过 body portal 渲染 Modal 与 Toast 基元,并让 toast 以其所在的输入框卡片为中心。`/feedback` 装饰是经 `ctx.commandUi.decorate` 注册的 `action`,因此菜单选中或不带参数的回车会消费触发 token 并打开弹窗,而带参数的命令行仍到达宿主命令。
43
43
 
44
- 每个 Session 有一个 `MessageFeedbackController` 支撑所有消息控件,以及一个 `FeedbackDialogController` 拥有弹窗草稿、提交与 toast 序号。消息控制器只读取一次 `messageFeedback.list`,且延迟到首次 hover 或 focus 才发起,而非挂载时触发;变更串行执行,每次都携带最后观察到的版本,`version-conflict` 响应带回权威条目,据此对账视图而不重新拉取。`toggle` 会报告提交后的评分,因此该行只对记录成功的点赞做确认,撤回不做。弹窗控制器按目标提交:消息目标通过消息控制器 put 一条带弹窗备注与分类的差评,Session 目标通过 `ctx.remote.sessionFeedback` 记录。成功会关闭草稿并弹出 toast;被替换的旧草稿迟到的成功只弹 toast、不关闭新草稿;失败让草稿保持打开并带上失败码。
44
+ 每个 Session 有一个 `MessageFeedbackController` 支撑所有消息控件,以及一个 `FeedbackDialogController` 拥有弹窗草稿、提交与 toast 序号。消息控制器只读取一次 `messageFeedback.list`,且延迟到首次 hover 或 focus 才发起,而非挂载时触发;变更串行执行,每次都携带最后观察到的版本,`version-conflict` 响应带回权威条目,据此对账视图而不重新拉取。任一评分操作执行前,该行都会检查已提交条目:评分相同则调用 `retract`,它会在串行队列内重新检查评分并在并发变更后变为无操作;其他状态则携带所选评分打开弹窗。弹窗控制器按目标提交:消息目标通过消息控制器 put 一条带弹窗备注与分类的对应评分,Session 目标通过 `ctx.remote.sessionFeedback` 记录。成功会关闭草稿并弹出确认 toast;被替换的旧草稿迟到的成功只弹确认 toast、不关闭新草稿;失败会保留弹窗并弹出停留时间更长的警告 toast。
45
45
 
46
46
  </details>
47
47
 
@@ -77,7 +77,6 @@ kind: "package-reference"
77
77
  这些限制界定了当前反馈界面。它们是当前包约束,不是通用评分对比或任务积压。
78
78
 
79
79
  - **备注大小是宿主策略**——部署方配置 `maxNoteBytes`(Web bundle 中为 8192),超长备注由宿主以 `note-too-large` 拒绝。弹窗不预先校验该上限,因此针对消息的超长描述在提交时才失败,而不是在输入过程中;Session 级备注没有上限。
80
- - **点赞不带备注**——只有点踩弹窗收集分类与描述;点赞只记录判断本身。
81
80
  - **无跨标签页推送**——另一个标签页的评分要等到重连或下一次冲突响应才可见,不会立即出现;控制器不消费反馈日志事件。
82
81
  - **仅限对话视图**——trajectory 与 waterfall 视图不渲染反馈控件,尽管它们的助手节点也带有相同的 `messageId`。
83
82
 
package/lib/client.js CHANGED
@@ -9,7 +9,7 @@ window.__ModuleLoader__.load({
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
10
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
11
11
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-message-feedback/src/client/FeedbackDialog.module.css.mjs
12
- const css$1 = "._6_jNJa_dialog._6_jNJa_dialog{border-radius:18px;gap:38px;width:min(488px,100%)}._6_jNJa_categories{flex-wrap:wrap;gap:8px;margin-top:-14px;display:flex}._6_jNJa_chip{border:.5px solid var(--dsw-alias-border-l4);corner-shape:round;height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;padding:0 12px;font-size:13px;line-height:18px;transition:background-color .12s,border-color .12s,color .12s}._6_jNJa_chip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._6_jNJa_chip:disabled{cursor:default;opacity:.4}._6_jNJa_chip:focus-visible{outline:2px solid var(--dsw-alias-button-primary-fill);outline-offset:2px}._6_jNJa_chipActive,._6_jNJa_chipActive:hover:not(:disabled){border-color:var(--dsw-alias-button-primary-fill);background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}._6_jNJa_detail{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);width:100%;min-height:116px;max-height:280px;color:var(--dsw-alias-label-primary);font:inherit;field-sizing:content;resize:none;border-radius:16px;margin-top:18px;padding:12px 14px;font-size:14px;line-height:22px;transition:border-color .12s,box-shadow .12s;display:block}._6_jNJa_detail::placeholder{color:var(--dsw-alias-label-caption)}._6_jNJa_detail:focus{border-color:var(--dsw-alias-border-l3);box-shadow:0 0 0 1px var(--dsw-alias-border-l3);outline:none}._6_jNJa_failure{color:var(--dsw-alias-label-tertiary);margin-top:8px;font-size:13px;line-height:20px;display:block}._6_jNJa_submit{border-radius:18px;width:100%;height:44px;font-size:14px;font-weight:500}._6_jNJa_toastIcon{border:1.5px solid var(--dsw-alias-state-success-primary);corner-shape:round;width:18px;height:18px;color:var(--dsw-alias-state-success-primary);border-radius:50%;place-items:center;display:grid}@media (prefers-reduced-motion:reduce){._6_jNJa_chip,._6_jNJa_detail{transition:none}}";
12
+ const css$1 = "._6_jNJa_dialog._6_jNJa_dialog{border-radius:18px;gap:38px;width:min(488px,100%)}._6_jNJa_categories{flex-wrap:wrap;gap:8px;margin-top:-14px;display:flex}._6_jNJa_chip{border:.5px solid var(--dsw-alias-border-l4);corner-shape:round;height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;padding:0 12px;font-size:13px;line-height:18px;transition:background-color .12s,border-color .12s,color .12s}._6_jNJa_chip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}._6_jNJa_chip:disabled{cursor:default;opacity:.4}._6_jNJa_chip:focus-visible{outline:2px solid var(--dsw-alias-button-primary-fill);outline-offset:2px}._6_jNJa_chipActive,._6_jNJa_chipActive:hover:not(:disabled){border-color:var(--dsw-alias-button-primary-fill);background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}._6_jNJa_detail{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);width:100%;min-height:116px;max-height:280px;color:var(--dsw-alias-label-primary);font:inherit;field-sizing:content;resize:none;border-radius:16px;margin-top:18px;padding:12px 14px;font-size:14px;line-height:22px;transition:border-color .12s,box-shadow .12s;display:block}._6_jNJa_detail::placeholder{color:var(--dsw-alias-label-caption)}._6_jNJa_detail:focus{border-color:var(--dsw-alias-border-l3);box-shadow:0 0 0 1px var(--dsw-alias-border-l3);outline:none}._6_jNJa_submit{border-radius:18px;width:100%;height:44px;font-size:14px;font-weight:500}._6_jNJa_toastIcon{border:1.5px solid var(--dsw-alias-state-success-primary);corner-shape:round;width:18px;height:18px;color:var(--dsw-alias-state-success-primary);border-radius:50%;place-items:center;display:grid}@media (prefers-reduced-motion:reduce){._6_jNJa_chip,._6_jNJa_detail{transition:none}}";
13
13
  const tagId$1 = "@deepseek-ai/dsh-client-ui-message-feedback/FeedbackDialog.module.css";
14
14
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
15
15
  const tag = document.createElement("style");
@@ -24,14 +24,13 @@ window.__ModuleLoader__.load({
24
24
  "chipActive": "_6_jNJa_chipActive",
25
25
  "detail": "_6_jNJa_detail",
26
26
  "dialog": "_6_jNJa_dialog",
27
- "failure": "_6_jNJa_failure",
28
27
  "submit": "_6_jNJa_submit",
29
28
  "toastIcon": "_6_jNJa_toastIcon"
30
29
  };
31
30
  //#endregion
32
31
  //#region lib/types/client/FeedbackDialog.js
33
32
  /**
34
- * The feedback dialog and its acknowledgement toast, rendered as one entry
33
+ * The feedback dialog and its acknowledgement and failure toasts, rendered as one entry
35
34
  * of `conversation.input.overlay` so each Session owns exactly one of each.
36
35
  * The Modal and the Toast both portal to `document.body`; the overlay slot
37
36
  * only supplies the per-session controller and the composer card the toast
@@ -55,9 +54,9 @@ window.__ModuleLoader__.load({
55
54
  /**
56
55
  * Render one Session's feedback dialog and toast.
57
56
  * @param props - the dialog hook, the draft verbs, and the locale seat.
58
- * @returns the modal while a target is open, the toast while one is showing.
57
+ * @returns the modal while a target is open and either toast while it is showing.
59
58
  */
60
- function FeedbackDialog({ useDialog, edit, submit, dismiss, dismissToast, t }) {
59
+ function FeedbackDialog({ useDialog, edit, submit, dismiss, dismissFailure, dismissToast, t }) {
61
60
  const state = useDialog((s) => s);
62
61
  const probeRef = (0, react.useRef)(null);
63
62
  const [card, setCard] = (0, react.useState)(null);
@@ -71,13 +70,17 @@ window.__ModuleLoader__.load({
71
70
  (0, react.useEffect)(() => () => {
72
71
  dismissToast(toast);
73
72
  }, [dismissToast, toast]);
74
- const failure = state.failure === null ? null : t(FAILURE_COPY[state.failure] ?? "error.generic");
73
+ const failureCode = state.failure;
74
+ const failure = failureCode === null ? null : t(FAILURE_COPY[failureCode] ?? "error.generic");
75
+ const onFailureDone = (0, react.useCallback)(() => {
76
+ dismissFailure();
77
+ }, [dismissFailure]);
75
78
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
76
79
  (0, react_jsx_runtime.jsx)("span", {
77
80
  ref: probeRef,
78
81
  hidden: true
79
82
  }),
80
- toast > 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
83
+ toast > 0 && failure === null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
81
84
  text: t("toast.recorded"),
82
85
  icon: (0, react_jsx_runtime.jsx)("span", {
83
86
  className: FeedbackDialog_module_css_default.toastIcon,
@@ -86,6 +89,13 @@ window.__ModuleLoader__.load({
86
89
  anchor: card,
87
90
  onDone: onToastDone
88
91
  }, toast),
92
+ failure !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
93
+ text: failure,
94
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {}),
95
+ anchor: card,
96
+ holdMs: 6e3,
97
+ onDone: onFailureDone
98
+ }, `failure-${failureCode}`),
89
99
  (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
90
100
  open: state.target !== null,
91
101
  title: t("dialog.title"),
@@ -101,38 +111,30 @@ window.__ModuleLoader__.load({
101
111
  },
102
112
  children: state.submitting ? t("submitting") : t("submit")
103
113
  }),
104
- children: [
105
- (0, react_jsx_runtime.jsx)("div", {
106
- className: FeedbackDialog_module_css_default.categories,
107
- role: "group",
108
- "aria-label": t("dialog.categories"),
109
- children: CATEGORIES.map((category) => (0, react_jsx_runtime.jsx)("button", {
110
- type: "button",
111
- className: state.category === category ? `${FeedbackDialog_module_css_default.chip} ${FeedbackDialog_module_css_default.chipActive}` : FeedbackDialog_module_css_default.chip,
112
- "aria-pressed": state.category === category,
113
- disabled: state.submitting,
114
- onClick: () => {
115
- edit({ category: state.category === category ? null : category });
116
- },
117
- children: t(`category.${category}`)
118
- }, category))
119
- }),
120
- (0, react_jsx_runtime.jsx)("textarea", {
121
- className: FeedbackDialog_module_css_default.detail,
122
- "aria-label": t("dialog.detail"),
123
- placeholder: t("dialog.hint"),
124
- value: state.text,
125
- readOnly: state.submitting,
126
- onChange: (event) => {
127
- edit({ text: event.target.value });
128
- }
129
- }),
130
- failure !== null && (0, react_jsx_runtime.jsx)("span", {
131
- className: FeedbackDialog_module_css_default.failure,
132
- role: "status",
133
- children: failure
134
- })
135
- ]
114
+ children: [(0, react_jsx_runtime.jsx)("div", {
115
+ className: FeedbackDialog_module_css_default.categories,
116
+ role: "group",
117
+ "aria-label": t("dialog.categories"),
118
+ children: CATEGORIES.map((category) => (0, react_jsx_runtime.jsx)("button", {
119
+ type: "button",
120
+ className: state.category === category ? `${FeedbackDialog_module_css_default.chip} ${FeedbackDialog_module_css_default.chipActive}` : FeedbackDialog_module_css_default.chip,
121
+ "aria-pressed": state.category === category,
122
+ disabled: state.submitting,
123
+ onClick: () => {
124
+ edit({ category: state.category === category ? null : category });
125
+ },
126
+ children: t(`category.${category}`)
127
+ }, category))
128
+ }), (0, react_jsx_runtime.jsx)("textarea", {
129
+ className: FeedbackDialog_module_css_default.detail,
130
+ "aria-label": t("dialog.detail"),
131
+ placeholder: t("dialog.hint"),
132
+ value: state.text,
133
+ readOnly: state.submitting,
134
+ onChange: (event) => {
135
+ edit({ text: event.target.value });
136
+ }
137
+ })]
136
138
  })
137
139
  ] });
138
140
  }
@@ -155,11 +157,10 @@ window.__ModuleLoader__.load({
155
157
  //#region lib/types/client/MessageFeedbackActions.js
156
158
  /**
157
159
  * Per-message feedback controls: the Like/Dislike pair inside the assistant
158
- * message's IconActions row, between copy and branch. Like records at once
159
- * and raises the Session's acknowledgement toast; Dislike opens the Session's
160
- * feedback dialog, whose submission records the negative judgment with its
161
- * category and text. Clicking the recorded rating retracts it. A recorded
162
- * rating shows the filled glyph so the signal survives a pointer leaving the row.
160
+ * message's IconActions row, between copy and branch. Either rating opens the
161
+ * Session's feedback dialog, whose submission records that judgment with its
162
+ * category and text. Clicking the recorded rating retracts it. A recorded rating
163
+ * shows the filled glyph so the signal survives a pointer leaving the row.
163
164
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/MessageFeedbackActions
164
165
  */
165
166
  /**
@@ -168,7 +169,7 @@ window.__ModuleLoader__.load({
168
169
  * shared feedback hook.
169
170
  * @returns the rating buttons with any failure notice beside them.
170
171
  */
171
- function MessageFeedbackActions({ messageId, ensure, current, toggle, openDialog, acknowledge, useFeedback, t }) {
172
+ function MessageFeedbackActions({ messageId, ensure, current, retract, openDialog, useFeedback, t }) {
172
173
  const item = useFeedback((view) => view.items.get(messageId));
173
174
  const loadFailed = useFeedback((view) => view.status === "error");
174
175
  const rating = item?.rating;
@@ -187,35 +188,17 @@ window.__ModuleLoader__.load({
187
188
  const errorCopy = (0, react.useCallback)((result) => {
188
189
  return result.error.code === "version-conflict" ? t("error.conflict") : t("error.generic");
189
190
  }, [t]);
190
- const onLike = (0, react.useCallback)(() => {
191
- setPending(true);
192
- setFailure(null);
193
- toggle(messageId, "positive").then((result) => {
194
- if (!alive.current) return;
195
- setPending(false);
196
- if (!result.ok) {
197
- setFailure(errorCopy(result));
198
- return;
199
- }
200
- if (result.rating === "positive") acknowledge();
201
- });
202
- }, [
203
- acknowledge,
204
- errorCopy,
205
- messageId,
206
- toggle
207
- ]);
208
- const onDislike = (0, react.useCallback)(() => {
191
+ const choose = (0, react.useCallback)((nextRating) => {
209
192
  setPending(true);
210
193
  setFailure(null);
211
194
  ensure().then((loaded) => {
212
195
  if (!alive.current) return;
213
- if (!loaded.ok || current(messageId)?.rating !== "negative") {
196
+ if (!loaded.ok || current(messageId)?.rating !== nextRating) {
214
197
  setPending(false);
215
- openDialog(messageId);
198
+ openDialog(messageId, nextRating);
216
199
  return;
217
200
  }
218
- toggle(messageId, "negative").then((result) => {
201
+ retract(messageId, nextRating).then((result) => {
219
202
  if (!alive.current) return;
220
203
  setPending(false);
221
204
  if (!result.ok) setFailure(errorCopy(result));
@@ -227,8 +210,14 @@ window.__ModuleLoader__.load({
227
210
  errorCopy,
228
211
  messageId,
229
212
  openDialog,
230
- toggle
213
+ retract
231
214
  ]);
215
+ const onLike = (0, react.useCallback)(() => {
216
+ choose("positive");
217
+ }, [choose]);
218
+ const onDislike = (0, react.useCallback)(() => {
219
+ choose("negative");
220
+ }, [choose]);
232
221
  const likeLabel = rating === "positive" ? t("action.likeActive") : t("action.like");
233
222
  const dislikeLabel = rating === "negative" ? t("action.dislikeActive") : t("action.dislike");
234
223
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
@@ -410,25 +399,17 @@ window.__ModuleLoader__.load({
410
399
  });
411
400
  }
412
401
  /**
413
- * Replace one message's rating with the opposite judgment, or retract it when
414
- * the committed rating already matches. The decision reads the committed item
415
- * inside the serialized mutation, so a click that lands before the first list
416
- * read still toggles against the stored value rather than the empty view a
417
- * cold control rendered. A replacement stores the bare judgment; the note
418
- * and category of the judgment it replaces do not carry over.
402
+ * Retract one message's matching committed rating. The serialized operation
403
+ * rechecks the current item and becomes a no-op if another operation already
404
+ * changed or removed it, so a stale retraction can never record a bare rating.
419
405
  * @param messageId - target assistant message.
420
- * @param rating - the judgment the human asked for.
421
- * @returns the settled mutation result with the rating now committed.
406
+ * @param rating - judgment the human asked to retract.
407
+ * @returns the settled mutation result.
422
408
  */
423
- toggle(messageId, rating) {
409
+ retract(messageId, rating) {
424
410
  return this.mutate(async () => {
425
411
  const observed = this.view.items.get(messageId);
426
- const retract = observed?.rating === rating;
427
- const result = retract ? await this.deleteCommitted(messageId, observed) : await this.putCommitted(messageId, rating, {}, observed);
428
- return result.ok ? {
429
- ok: true,
430
- rating: retract ? null : rating
431
- } : result;
412
+ return observed?.rating === rating ? await this.deleteCommitted(messageId, observed) : OK;
432
413
  });
433
414
  }
434
415
  /** Commit one put against the observed version and reconcile a conflict. */
@@ -548,11 +529,10 @@ window.__ModuleLoader__.load({
548
529
  //#endregion
549
530
  //#region lib/types/client/dialog.js
550
531
  /**
551
- * Headless state of one Session's feedback dialog and its acknowledgement
552
- * toast. One form serves two targets: the Session itself (a bare `/feedback`)
553
- * and one assistant message (Dislike). The overlay view renders from the
554
- * store; the message controls raise the toast after a Like through
555
- * {@link FeedbackDialogController.acknowledge}.
532
+ * Headless state of one Session's feedback dialog and its acknowledgement and
533
+ * failure toasts. One form serves two targets: the Session itself (a bare `/feedback`)
534
+ * and one assistant message (Like or Dislike). The overlay view renders from
535
+ * the store and raises the acknowledgement after a successful submission.
556
536
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/dialog
557
537
  */
558
538
  const CLOSED = {
@@ -613,7 +593,8 @@ window.__ModuleLoader__.load({
613
593
  }
614
594
  /**
615
595
  * Submit the draft; an empty draft is a valid submission. Success closes the
616
- * dialog and raises the toast; a failure keeps the dialog open with its code.
596
+ * dialog and raises the acknowledgement toast; a failure keeps the dialog
597
+ * open and publishes its code for the failure toast.
617
598
  * @returns after the submission settles.
618
599
  */
619
600
  async submitDraft() {
@@ -639,7 +620,16 @@ window.__ModuleLoader__.load({
639
620
  this.state.set({
640
621
  ...this.state.getSnapshot(),
641
622
  submitting: false,
642
- failure: result.error.code
623
+ failure: result.error.code,
624
+ toast: 0
625
+ });
626
+ }
627
+ /** Clear the current failure toast without closing its draft. */
628
+ dismissFailure() {
629
+ const s = this.state.getSnapshot();
630
+ this.state.set({
631
+ ...s,
632
+ failure: null
643
633
  });
644
634
  }
645
635
  /** Show the acknowledgement toast; a toast already on screen restarts. */
@@ -675,8 +665,8 @@ window.__ModuleLoader__.load({
675
665
  /**
676
666
  * One Session's feedback surface: the message-feedback object layer and the
677
667
  * dialog controller, plus the routing between them. A message target puts a
678
- * negative judgment through the message controller; the Session target
679
- * records through the `sessionFeedback` Remote.
668
+ * selected judgment through the message controller; the Session target records
669
+ * through the `sessionFeedback` Remote.
680
670
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/surface
681
671
  */
682
672
  /** The per-session pair behind every entry of one Session. */
@@ -695,7 +685,7 @@ window.__ModuleLoader__.load({
695
685
  this.ctx = ctx;
696
686
  this.sessionId = sessionId;
697
687
  this.feedback = new MessageFeedbackController(ctx, sessionId);
698
- this.dialog = new FeedbackDialogController((target, entry) => target.kind === "message" ? this.feedback.rate(target.messageId, "negative", entry) : this.recordSession(entry));
688
+ this.dialog = new FeedbackDialogController((target, entry) => target.kind === "message" ? this.feedback.rate(target.messageId, target.rating, entry) : this.recordSession(entry));
699
689
  }
700
690
  /** Record one Session-level remark through the sessionFeedback Remote. */
701
691
  async recordSession(entry) {
@@ -741,7 +731,7 @@ window.__ModuleLoader__.load({
741
731
  "category.task-result": "任务结果",
742
732
  "category.instruction-following": "指令理解与遵循",
743
733
  "category.product-interaction": "产品功能与交互",
744
- "category.service-stability": "服务稳定性",
734
+ "category.service-stability": "稳定性和速度",
745
735
  "category.resource-cost": "资源使用与费用",
746
736
  "category.security-privacy-permission": "安全隐私与权限",
747
737
  "category.other": "其他",
@@ -764,7 +754,7 @@ window.__ModuleLoader__.load({
764
754
  "category.task-result": "Task result",
765
755
  "category.instruction-following": "Instruction understanding and following",
766
756
  "category.product-interaction": "Product features and interaction",
767
- "category.service-stability": "Service stability",
757
+ "category.service-stability": "Stability and speed",
768
758
  "category.resource-cost": "Resource usage and cost",
769
759
  "category.security-privacy-permission": "Security, privacy, and permissions",
770
760
  "category.other": "Other",
@@ -779,7 +769,7 @@ window.__ModuleLoader__.load({
779
769
  /**
780
770
  * Feedback surface plugin, browser half: the Like/Dislike entry in the
781
771
  * conversation.chat.assistant-actions strip, the feedback dialog and its
782
- * acknowledgement toast in conversation.input.overlay, and the `/feedback`
772
+ * acknowledgement and failure toasts in conversation.input.overlay, and the `/feedback`
783
773
  * decoration that opens the dialog from the composer menu or a bare typed
784
774
  * command. One FeedbackSurface per Session backs every entry in that Session.
785
775
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client
@@ -831,15 +821,13 @@ window.__ModuleLoader__.load({
831
821
  hooks: { feedback },
832
822
  ensure: () => feedback.ensure(),
833
823
  current: (messageId) => feedback.getSnapshot().items.get(messageId),
834
- toggle: (messageId, rating) => feedback.toggle(messageId, rating),
835
- openDialog: (messageId) => {
824
+ retract: (messageId, rating) => feedback.retract(messageId, rating),
825
+ openDialog: (messageId, rating) => {
836
826
  dialog.open({
837
827
  kind: "message",
838
- messageId
828
+ messageId,
829
+ rating
839
830
  });
840
- },
841
- acknowledge: () => {
842
- dialog.acknowledge();
843
831
  }
844
832
  };
845
833
  }
@@ -860,6 +848,9 @@ window.__ModuleLoader__.load({
860
848
  dismiss: () => {
861
849
  dialog.dismiss();
862
850
  },
851
+ dismissFailure: () => {
852
+ dialog.dismissFailure();
853
+ },
863
854
  dismissToast: (seq) => {
864
855
  dialog.dismissToast(seq);
865
856
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The feedback dialog and its acknowledgement toast, rendered as one entry
2
+ * The feedback dialog and its acknowledgement and failure toasts, rendered as one entry
3
3
  * of `conversation.input.overlay` so each Session owns exactly one of each.
4
4
  * The Modal and the Toast both portal to `document.body`; the overlay slot
5
5
  * only supplies the per-session controller and the composer card the toast
@@ -10,7 +10,7 @@ import type { FeedbackDialogProps } from './slots.ts';
10
10
  /**
11
11
  * Render one Session's feedback dialog and toast.
12
12
  * @param props - the dialog hook, the draft verbs, and the locale seat.
13
- * @returns the modal while a target is open, the toast while one is showing.
13
+ * @returns the modal while a target is open and either toast while it is showing.
14
14
  */
15
- export declare function FeedbackDialog({ useDialog, edit, submit, dismiss, dismissToast, t }: FeedbackDialogProps): import("react").JSX.Element;
15
+ export declare function FeedbackDialog({ useDialog, edit, submit, dismiss, dismissFailure, dismissToast, t, }: FeedbackDialogProps): import("react").JSX.Element;
16
16
  //# sourceMappingURL=FeedbackDialog.d.ts.map
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Per-message feedback controls: the Like/Dislike pair inside the assistant
3
- * message's IconActions row, between copy and branch. Like records at once
4
- * and raises the Session's acknowledgement toast; Dislike opens the Session's
5
- * feedback dialog, whose submission records the negative judgment with its
6
- * category and text. Clicking the recorded rating retracts it. A recorded
7
- * rating shows the filled glyph so the signal survives a pointer leaving the row.
3
+ * message's IconActions row, between copy and branch. Either rating opens the
4
+ * Session's feedback dialog, whose submission records that judgment with its
5
+ * category and text. Clicking the recorded rating retracts it. A recorded rating
6
+ * shows the filled glyph so the signal survives a pointer leaving the row.
8
7
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/MessageFeedbackActions
9
8
  */
10
9
  import type { MessageFeedbackActionProps } from './slots.ts';
@@ -14,5 +13,5 @@ import type { MessageFeedbackActionProps } from './slots.ts';
14
13
  * shared feedback hook.
15
14
  * @returns the rating buttons with any failure notice beside them.
16
15
  */
17
- export declare function MessageFeedbackActions({ messageId, ensure, current, toggle, openDialog, acknowledge, useFeedback, t, }: MessageFeedbackActionProps): import("react").JSX.Element;
16
+ export declare function MessageFeedbackActions({ messageId, ensure, current, retract, openDialog, useFeedback, t, }: MessageFeedbackActionProps): import("react").JSX.Element;
18
17
  //# sourceMappingURL=MessageFeedbackActions.d.ts.map
@@ -34,11 +34,6 @@ export interface MessageFeedbackActionFailure {
34
34
  export type MessageFeedbackActionResult = {
35
35
  ok: true;
36
36
  } | MessageFeedbackActionFailure;
37
- /** A settled toggle, carrying the rating now committed so the control can tell a record from a retraction. */
38
- export type MessageFeedbackToggleResult = {
39
- ok: true;
40
- rating: MessageFeedbackRating | null;
41
- } | MessageFeedbackActionFailure;
42
37
  /**
43
38
  * Human-readable text for one business failure code.
44
39
  * @param code - the Host's business failure code.
@@ -99,17 +94,14 @@ export declare class MessageFeedbackController implements HostObservable<Message
99
94
  */
100
95
  rate(messageId: MessageId, rating: MessageFeedbackRating, entry?: FeedbackRecord): Promise<MessageFeedbackActionResult>;
101
96
  /**
102
- * Replace one message's rating with the opposite judgment, or retract it when
103
- * the committed rating already matches. The decision reads the committed item
104
- * inside the serialized mutation, so a click that lands before the first list
105
- * read still toggles against the stored value rather than the empty view a
106
- * cold control rendered. A replacement stores the bare judgment; the note
107
- * and category of the judgment it replaces do not carry over.
97
+ * Retract one message's matching committed rating. The serialized operation
98
+ * rechecks the current item and becomes a no-op if another operation already
99
+ * changed or removed it, so a stale retraction can never record a bare rating.
108
100
  * @param messageId - target assistant message.
109
- * @param rating - the judgment the human asked for.
110
- * @returns the settled mutation result with the rating now committed.
101
+ * @param rating - judgment the human asked to retract.
102
+ * @returns the settled mutation result.
111
103
  */
112
- toggle(messageId: MessageId, rating: MessageFeedbackRating): Promise<MessageFeedbackToggleResult>;
104
+ retract(messageId: MessageId, rating: MessageFeedbackRating): Promise<MessageFeedbackActionResult>;
113
105
  /** Commit one put against the observed version and reconcile a conflict. */
114
106
  private putCommitted;
115
107
  /** Commit one delete against the observed version and reconcile a conflict. */
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Headless state of one Session's feedback dialog and its acknowledgement
3
- * toast. One form serves two targets: the Session itself (a bare `/feedback`)
4
- * and one assistant message (Dislike). The overlay view renders from the
5
- * store; the message controls raise the toast after a Like through
6
- * {@link FeedbackDialogController.acknowledge}.
2
+ * Headless state of one Session's feedback dialog and its acknowledgement and
3
+ * failure toasts. One form serves two targets: the Session itself (a bare `/feedback`)
4
+ * and one assistant message (Like or Dislike). The overlay view renders from
5
+ * the store and raises the acknowledgement after a successful submission.
7
6
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/dialog
8
7
  */
9
8
  import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
10
9
  import type { MessageId } from '@deepseek-ai/dsh-api-remotes/client';
11
10
  import type { FeedbackCategory, FeedbackRecord } from '@deepseek-ai/dsh-command-feedback/types';
11
+ import type { MessageFeedbackRating } from '@deepseek-ai/dsh-message-feedback/types';
12
12
  import type { MessageFeedbackActionResult } from './controller.ts';
13
13
  /** What one open dialog submits to. */
14
14
  export type FeedbackDialogTarget = {
@@ -16,6 +16,7 @@ export type FeedbackDialogTarget = {
16
16
  } | {
17
17
  readonly kind: 'message';
18
18
  readonly messageId: MessageId;
19
+ readonly rating: MessageFeedbackRating;
19
20
  };
20
21
  /** Dialog and toast state the overlay view renders from. */
21
22
  export interface FeedbackDialogState {
@@ -62,12 +63,15 @@ export declare class FeedbackDialogController {
62
63
  edit(draft: Partial<Pick<FeedbackDialogState, 'category' | 'text'>>): void;
63
64
  /**
64
65
  * Submit the draft; an empty draft is a valid submission. Success closes the
65
- * dialog and raises the toast; a failure keeps the dialog open with its code.
66
+ * dialog and raises the acknowledgement toast; a failure keeps the dialog
67
+ * open and publishes its code for the failure toast.
66
68
  * @returns after the submission settles.
67
69
  */
68
70
  submitDraft(): Promise<void>;
71
+ /** Clear the current failure toast without closing its draft. */
72
+ dismissFailure(): void;
69
73
  /** Show the acknowledgement toast; a toast already on screen restarts. */
70
- acknowledge(): void;
74
+ private acknowledge;
71
75
  /**
72
76
  * Retire one toast after its fade; a newer toast is left alone.
73
77
  * @param seq - the toast sequence the view finished showing.
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Feedback surface plugin, browser half: the Like/Dislike entry in the
3
3
  * conversation.chat.assistant-actions strip, the feedback dialog and its
4
- * acknowledgement toast in conversation.input.overlay, and the `/feedback`
4
+ * acknowledgement and failure toasts in conversation.input.overlay, and the `/feedback`
5
5
  * decoration that opens the dialog from the composer menu or a bare typed
6
6
  * command. One FeedbackSurface per Session backs every entry in that Session.
7
7
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client
8
8
  */
9
9
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
10
- export type { MessageFeedbackActionFailure, MessageFeedbackActionResult, MessageFeedbackStatus, MessageFeedbackToggleResult, MessageFeedbackView, } from './controller.ts';
10
+ export type { MessageFeedbackActionFailure, MessageFeedbackActionResult, MessageFeedbackStatus, MessageFeedbackView, } from './controller.ts';
11
11
  export type { FeedbackDialogState, FeedbackDialogTarget, FeedbackSubmit } from './dialog.ts';
12
12
  export type { FeedbackDialogInjected, FeedbackDialogProps, MessageFeedbackActionProps, MessageFeedbackInjected, } from './slots.ts';
13
13
  export type { MessageFeedbackKey } from './locales.ts';
@@ -10,7 +10,7 @@
10
10
  import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
11
  import type { MessageId } from '@deepseek-ai/dsh-api-remotes/client';
12
12
  import type { MessageFeedbackItem, MessageFeedbackRating } from '@deepseek-ai/dsh-message-feedback/types';
13
- import type { MessageFeedbackActionResult, MessageFeedbackToggleResult, MessageFeedbackView } from './controller.ts';
13
+ import type { MessageFeedbackActionResult, MessageFeedbackView } from './controller.ts';
14
14
  import type { FeedbackDialogState } from './dialog.ts';
15
15
  /** Injected business face of one assistant-message feedback entry. */
16
16
  export interface MessageFeedbackInjected {
@@ -26,21 +26,19 @@ export interface MessageFeedbackInjected {
26
26
  */
27
27
  current: (messageId: MessageId) => MessageFeedbackItem | undefined;
28
28
  /**
29
- * Apply the requested judgment, retracting instead when the committed rating
30
- * already matches. The controller decides from the committed item, so a click
31
- * before the first list read still toggles the stored value.
29
+ * Retract the requested judgment only while the committed rating still
30
+ * matches; a concurrent change makes the serialized operation a no-op.
32
31
  * @param messageId - target assistant message.
33
- * @param rating - the judgment the human asked for.
32
+ * @param rating - judgment the human asked to retract.
34
33
  */
35
- toggle: (messageId: MessageId, rating: MessageFeedbackRating) => Promise<MessageFeedbackToggleResult>;
34
+ retract: (messageId: MessageId, rating: MessageFeedbackRating) => Promise<MessageFeedbackActionResult>;
36
35
  /**
37
- * Open the Session's feedback dialog for one message; its submission
38
- * records a negative judgment with the dialog's category and text.
36
+ * Open the Session's feedback dialog for one message and judgment; its
37
+ * submission records that rating with the dialog's category and text.
39
38
  * @param messageId - target assistant message.
39
+ * @param rating - judgment to record on submission.
40
40
  */
41
- openDialog: (messageId: MessageId) => void;
42
- /** Show the Session's acknowledgement toast. */
43
- acknowledge: () => void;
41
+ openDialog: (messageId: MessageId, rating: MessageFeedbackRating) => void;
44
42
  }
45
43
  /** Full props of one assistant-message feedback entry. */
46
44
  export type MessageFeedbackActionProps = PropsRuntime<'conversation.chat.assistant-actions'> & InjectFace<MessageFeedbackInjected> & PropsLocale<'feedback'>;
@@ -59,6 +57,8 @@ export interface FeedbackDialogInjected {
59
57
  submit: () => Promise<void>;
60
58
  /** Close the dialog and discard the draft. */
61
59
  dismiss: () => void;
60
+ /** Retire the current submission-failure toast without closing its draft. */
61
+ dismissFailure: () => void;
62
62
  /**
63
63
  * Retire the toast the view finished showing.
64
64
  * @param seq - the toast sequence.
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * One Session's feedback surface: the message-feedback object layer and the
3
3
  * dialog controller, plus the routing between them. A message target puts a
4
- * negative judgment through the message controller; the Session target
5
- * records through the `sessionFeedback` Remote.
4
+ * selected judgment through the message controller; the Session target records
5
+ * through the `sessionFeedback` Remote.
6
6
  * @module @deepseek-ai/dsh-client-ui-message-feedback/client/surface
7
7
  */
8
8
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-message-feedback",
3
- "description": "The Web feedback surface: per-message Like/Dislike in the assistant-message action strip and the feedback dialog behind Dislike and /feedback, backed by the messageFeedback and sessionFeedback Host Remotes",
4
- "version": "0.1.5-alpha.2",
3
+ "description": "The Web feedback surface: per-message Like/Dislike in the assistant-message action strip and the feedback dialog behind both ratings and /feedback, backed by the messageFeedback and sessionFeedback Host Remotes",
4
+ "version": "0.1.5-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -47,23 +47,23 @@
47
47
  "@types/react-dom": "~18.3.0",
48
48
  "react": "^18.2.0",
49
49
  "react-dom": "^18.2.0",
50
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-alpha.2",
51
- "@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.2",
52
- "@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2",
53
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.2",
54
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.2",
55
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2",
56
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2",
57
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2",
58
- "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
50
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
51
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2",
52
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
53
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
54
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-rc.2",
55
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
56
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
57
+ "@deepseek-ai/dsh-message-feedback": "^0.1.5-rc.2",
58
+ "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
59
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.2",
59
60
  "@deepseek-ai/cordis": "^4.0.2",
60
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-alpha.2",
61
- "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.2",
62
- "@deepseek-ai/dsh-client-ui-commands": "^0.1.5-alpha.2",
63
- "@deepseek-ai/dsh-command-feedback": "^0.1.5-alpha.2",
64
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.2",
65
- "@deepseek-ai/dsh-message-feedback": "^0.1.5-alpha.2",
66
- "@deepseek-ai/dsh-client-store": "^0.1.5-alpha.2"
61
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.2",
62
+ "@deepseek-ai/dsh-client-ui-commands": "^0.1.5-rc.2",
63
+ "@deepseek-ai/dsh-command-feedback": "^0.1.5-rc.2",
64
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.2",
65
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-rc.2",
66
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2"
67
67
  },
68
68
  "files": [
69
69
  "lib/index.js",