@deepseek-ai/dsh-client-ui-message-feedback 0.1.0-rc.7 → 0.1.0-rc.8
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 +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +175 -59
- package/lib/types/client/MessageFeedbackActions.d.ts +9 -3
- package/lib/types/client/locales.d.ts +2 -0
- package/package.json +21 -23
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:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: d3bb4e28b95da2fde0d26b7ef83ebca377b4939f
|
|
6
|
+
README.zh.md: d823bb9de2b7d39d0bc5d00164b8ce8dffec4a34
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [中文](README.zh.md)
|
|
4
4
|
|
|
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. 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.
|
|
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.
|
|
6
6
|
|
|
7
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.
|
|
8
8
|
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
单条消息反馈插件的浏览器侧:一对 Like/Dislike 按钮加一个可选备注,作为 `conversation.chat.assistant-actions` 条带的 `feedback` 条目(order 10)贡献。该条带由 `ui-conversation` 声明,渲染在已定稿助手消息的 IconActions 行内、复制与分支之间,因此控件沿用该行的样式与 hover
|
|
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 会接受它们作为目标,界面上也不出现控件。
|
|
6
6
|
|
|
7
7
|
每个 Session 一个 `MessageFeedbackController`,支撑该 Session 内所有消息的控件,因此一次 `messageFeedback.list` 读取即可填充整段对话。该读取延迟到首次 hover 或 focus 才发起,而不是在挂载时触发,因为可见历史中每条已结束的消息都会挂载一次控件。
|
|
8
8
|
|
package/lib/client.js
CHANGED
|
@@ -6,6 +6,7 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let react = require("react");
|
|
9
|
+
let react_dom = require("react-dom");
|
|
9
10
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
11
|
//#region lib/types/client/controller.js
|
|
11
12
|
const INITIAL_VIEW = Object.freeze({
|
|
@@ -325,7 +326,7 @@ window.__ModuleLoader__.load({
|
|
|
325
326
|
};
|
|
326
327
|
//#endregion
|
|
327
328
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-message-feedback/src/client/MessageFeedbackActions.module.css.mjs
|
|
328
|
-
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{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.
|
|
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}";
|
|
329
330
|
const tagId = "@deepseek-ai/dsh-client-ui-message-feedback/MessageFeedbackActions.module.css";
|
|
330
331
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
331
332
|
const tag = document.createElement("style");
|
|
@@ -335,27 +336,49 @@ window.__ModuleLoader__.load({
|
|
|
335
336
|
document.head.appendChild(tag);
|
|
336
337
|
}
|
|
337
338
|
var MessageFeedbackActions_module_css_default = {
|
|
339
|
+
"action": "_8_XoUG_action",
|
|
340
|
+
"failure": "_8_XoUG_failure",
|
|
341
|
+
"noteActions": "_8_XoUG_noteActions",
|
|
338
342
|
"noteCancel": "_8_XoUG_noteCancel",
|
|
343
|
+
"noteInput": "_8_XoUG_noteInput",
|
|
339
344
|
"noteOpen": "_8_XoUG_noteOpen",
|
|
340
|
-
"
|
|
341
|
-
"noteSave": "_8_XoUG_noteSave"
|
|
342
|
-
"noteEditor": "_8_XoUG_noteEditor",
|
|
343
|
-
"action": "_8_XoUG_action",
|
|
344
|
-
"noteInput": "_8_XoUG_noteInput"
|
|
345
|
+
"notePanel": "_8_XoUG_notePanel",
|
|
346
|
+
"noteSave": "_8_XoUG_noteSave"
|
|
345
347
|
};
|
|
346
348
|
//#endregion
|
|
347
349
|
//#region lib/types/client/MessageFeedbackActions.js
|
|
348
350
|
/**
|
|
349
351
|
* Per-message feedback controls: a Like/Dislike pair plus an optional note.
|
|
350
|
-
*
|
|
351
|
-
* reuse that row's chrome and sit between copy and branch.
|
|
352
|
+
* The buttons render inside the assistant message's IconActions row, so they
|
|
353
|
+
* reuse that row's chrome and sit between copy and branch. The note editor is
|
|
354
|
+
* a popover (portaled to `document.body`) anchored to the note trigger, not an
|
|
355
|
+
* inline expansion: a 260px textarea plus buttons cannot fit the row at any
|
|
356
|
+
* viewport, and an inline element pushed the branch action and clock out of the
|
|
357
|
+
* conversation column. Portaling out of the column also escapes its `overflow`
|
|
358
|
+
* clip, so the panel cannot be cropped or detached from the message it annotates.
|
|
352
359
|
* @module @deepseek-ai/dsh-client-ui-message-feedback/client/MessageFeedbackActions
|
|
353
360
|
*/
|
|
361
|
+
/** Safe distance kept between the panel and the viewport edges (the Menu portal margin). */
|
|
362
|
+
const PANEL_MARGIN = 12;
|
|
363
|
+
/** Distance between the trigger's bottom edge and the panel's top. */
|
|
364
|
+
const PANEL_GAP = 4;
|
|
365
|
+
/**
|
|
366
|
+
* Unplaced portal panel: hidden but laid out so `offsetWidth` is real for the
|
|
367
|
+
* clamp. The explicit insets match `Menu`'s measure style — a `position: fixed`
|
|
368
|
+
* element with auto insets otherwise sits at its static position, a different
|
|
369
|
+
* origin than the one the first placement measures from.
|
|
370
|
+
*/
|
|
371
|
+
const MEASURE_STYLE = {
|
|
372
|
+
visibility: "hidden",
|
|
373
|
+
left: 0,
|
|
374
|
+
top: 0
|
|
375
|
+
};
|
|
354
376
|
/**
|
|
355
377
|
* One message's feedback controls.
|
|
356
378
|
* @param props - the owner's message identity, the injected verbs, and the
|
|
357
379
|
* shared feedback hook.
|
|
358
|
-
* @returns the rating buttons
|
|
380
|
+
* @returns the rating buttons and the note trigger, with the note editor
|
|
381
|
+
* portal-open beneath the trigger while it is open.
|
|
359
382
|
*/
|
|
360
383
|
function MessageFeedbackActions({ messageId, ensure, rate, toggle, clearNote, useFeedback, t }) {
|
|
361
384
|
const item = useFeedback((view) => view.items.get(messageId));
|
|
@@ -364,7 +387,11 @@ window.__ModuleLoader__.load({
|
|
|
364
387
|
const [noteOpen, setNoteOpen] = (0, react.useState)(false);
|
|
365
388
|
const [draft, setDraft] = (0, react.useState)("");
|
|
366
389
|
const [pending, setPending] = (0, react.useState)(false);
|
|
367
|
-
const [
|
|
390
|
+
const [rowFailure, setRowFailure] = (0, react.useState)(null);
|
|
391
|
+
const [noteFailure, setNoteFailure] = (0, react.useState)(null);
|
|
392
|
+
const triggerRef = (0, react.useRef)(null);
|
|
393
|
+
const panelRef = (0, react.useRef)(null);
|
|
394
|
+
const inputRef = (0, react.useRef)(null);
|
|
368
395
|
const seeded = (0, react.useRef)(false);
|
|
369
396
|
const seed = (0, react.useCallback)(() => {
|
|
370
397
|
if (seeded.current) return;
|
|
@@ -375,44 +402,113 @@ window.__ModuleLoader__.load({
|
|
|
375
402
|
(0, react.useEffect)(() => () => {
|
|
376
403
|
alive.current = false;
|
|
377
404
|
}, []);
|
|
378
|
-
|
|
405
|
+
/** Bumped whenever an editing session ends, so a late save can tell it is stale. */
|
|
406
|
+
const noteGeneration = (0, react.useRef)(0);
|
|
407
|
+
/** Current panel open-state, readable from a stale closure via a ref. */
|
|
408
|
+
const noteOpenRef = (0, react.useRef)(false);
|
|
409
|
+
(0, react.useEffect)(() => {
|
|
410
|
+
noteOpenRef.current = noteOpen;
|
|
411
|
+
}, [noteOpen]);
|
|
412
|
+
const errorCopy = (0, react.useCallback)((result) => {
|
|
413
|
+
return result.error?.code === "version-conflict" ? t("error.conflict") : t("error.generic");
|
|
414
|
+
}, [t]);
|
|
415
|
+
const settleRating = (0, react.useCallback)((result) => {
|
|
379
416
|
if (!alive.current) return;
|
|
380
417
|
setPending(false);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}, [
|
|
418
|
+
setRowFailure(result.ok ? null : errorCopy(result));
|
|
419
|
+
}, [errorCopy]);
|
|
420
|
+
const closeNote = (0, react.useCallback)(() => {
|
|
421
|
+
noteGeneration.current += 1;
|
|
422
|
+
setNoteOpen(false);
|
|
423
|
+
}, []);
|
|
387
424
|
const onRate = (0, react.useCallback)((next) => {
|
|
388
425
|
setPending(true);
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
toggle(messageId, next).then(
|
|
426
|
+
setRowFailure(null);
|
|
427
|
+
closeNote();
|
|
428
|
+
toggle(messageId, next).then(settleRating);
|
|
392
429
|
}, [
|
|
430
|
+
closeNote,
|
|
393
431
|
messageId,
|
|
394
|
-
|
|
432
|
+
settleRating,
|
|
395
433
|
toggle
|
|
396
434
|
]);
|
|
397
435
|
const onSaveNote = (0, react.useCallback)((current) => {
|
|
398
436
|
const trimmed = draft.trim();
|
|
399
437
|
setPending(true);
|
|
400
|
-
|
|
438
|
+
setNoteFailure(null);
|
|
439
|
+
const generation = noteGeneration.current;
|
|
440
|
+
const staleSeed = item?.note ?? "";
|
|
401
441
|
(trimmed.length === 0 ? clearNote(messageId) : rate(messageId, current, trimmed)).then((result) => {
|
|
402
|
-
|
|
403
|
-
|
|
442
|
+
if (!alive.current) return;
|
|
443
|
+
setPending(false);
|
|
444
|
+
if (result.ok) {
|
|
445
|
+
if (generation === noteGeneration.current) {
|
|
446
|
+
setNoteFailure(null);
|
|
447
|
+
setNoteOpen(false);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
setDraft((draftNow) => draftNow === staleSeed ? trimmed : draftNow);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (generation === noteGeneration.current || !noteOpenRef.current) setNoteFailure(errorCopy(result));
|
|
404
454
|
});
|
|
405
455
|
}, [
|
|
406
456
|
clearNote,
|
|
407
457
|
draft,
|
|
458
|
+
errorCopy,
|
|
459
|
+
item?.note,
|
|
408
460
|
messageId,
|
|
409
|
-
|
|
410
|
-
|
|
461
|
+
noteOpenRef,
|
|
462
|
+
rate
|
|
411
463
|
]);
|
|
412
|
-
const
|
|
464
|
+
const toggleNote = (0, react.useCallback)(() => {
|
|
465
|
+
if (noteOpen) {
|
|
466
|
+
closeNote();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
413
469
|
setDraft(item?.note ?? "");
|
|
470
|
+
setNoteFailure(null);
|
|
414
471
|
setNoteOpen(true);
|
|
415
|
-
}, [
|
|
472
|
+
}, [
|
|
473
|
+
noteOpen,
|
|
474
|
+
closeNote,
|
|
475
|
+
item?.note
|
|
476
|
+
]);
|
|
477
|
+
const pos = (0, _deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
|
|
478
|
+
open: noteOpen,
|
|
479
|
+
anchorRef: triggerRef,
|
|
480
|
+
panelRef,
|
|
481
|
+
gap: PANEL_GAP,
|
|
482
|
+
margin: PANEL_MARGIN
|
|
483
|
+
});
|
|
484
|
+
(0, react.useEffect)(() => {
|
|
485
|
+
if (!noteOpen) return;
|
|
486
|
+
inputRef.current?.focus();
|
|
487
|
+
const onPointerDown = (e) => {
|
|
488
|
+
if (!(e.target instanceof Node)) return;
|
|
489
|
+
if (triggerRef.current?.contains(e.target) === true) return;
|
|
490
|
+
if (panelRef.current?.contains(e.target) === true) return;
|
|
491
|
+
closeNote();
|
|
492
|
+
};
|
|
493
|
+
const onKeyDown = (e) => {
|
|
494
|
+
if (e.key === "Escape") closeNote();
|
|
495
|
+
};
|
|
496
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
497
|
+
document.addEventListener("keydown", onKeyDown);
|
|
498
|
+
return () => {
|
|
499
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
500
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
501
|
+
};
|
|
502
|
+
}, [noteOpen, closeNote]);
|
|
503
|
+
const wasOpen = (0, react.useRef)(false);
|
|
504
|
+
(0, react.useEffect)(() => {
|
|
505
|
+
if (noteOpen) {
|
|
506
|
+
wasOpen.current = true;
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (wasOpen.current) triggerRef.current?.focus();
|
|
510
|
+
wasOpen.current = false;
|
|
511
|
+
}, [noteOpen]);
|
|
416
512
|
const likeLabel = rating === "positive" ? t("action.likeActive") : t("action.like");
|
|
417
513
|
const dislikeLabel = rating === "negative" ? t("action.dislikeActive") : t("action.dislike");
|
|
418
514
|
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
@@ -452,54 +548,72 @@ window.__ModuleLoader__.load({
|
|
|
452
548
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDislikeOutline16, {})
|
|
453
549
|
})
|
|
454
550
|
}),
|
|
455
|
-
rating !== void 0 &&
|
|
551
|
+
rating !== void 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
552
|
+
ref: triggerRef,
|
|
456
553
|
type: "button",
|
|
457
554
|
className: MessageFeedbackActions_module_css_default.noteOpen,
|
|
458
|
-
|
|
555
|
+
"aria-haspopup": "dialog",
|
|
556
|
+
"aria-expanded": noteOpen,
|
|
557
|
+
onClick: toggleNote,
|
|
459
558
|
children: item?.note === void 0 ? t("note.open") : item.note
|
|
460
559
|
}),
|
|
461
|
-
|
|
462
|
-
className: MessageFeedbackActions_module_css_default.
|
|
560
|
+
rowFailure === null && loadFailed && (0, react_jsx_runtime.jsx)("span", {
|
|
561
|
+
className: MessageFeedbackActions_module_css_default.failure,
|
|
562
|
+
role: "status",
|
|
563
|
+
children: t("error.load")
|
|
564
|
+
}),
|
|
565
|
+
rowFailure !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
566
|
+
className: MessageFeedbackActions_module_css_default.failure,
|
|
567
|
+
role: "status",
|
|
568
|
+
children: rowFailure
|
|
569
|
+
}),
|
|
570
|
+
!(rating !== void 0 && noteOpen) && noteFailure !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
571
|
+
className: MessageFeedbackActions_module_css_default.failure,
|
|
572
|
+
role: "status",
|
|
573
|
+
children: noteFailure
|
|
574
|
+
}),
|
|
575
|
+
rating !== void 0 && noteOpen && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
|
|
576
|
+
ref: panelRef,
|
|
577
|
+
className: MessageFeedbackActions_module_css_default.notePanel,
|
|
578
|
+
role: "dialog",
|
|
579
|
+
"aria-label": t("note.dialog"),
|
|
580
|
+
style: pos ?? MEASURE_STYLE,
|
|
463
581
|
children: [
|
|
464
582
|
(0, react_jsx_runtime.jsx)("textarea", {
|
|
583
|
+
ref: inputRef,
|
|
465
584
|
className: MessageFeedbackActions_module_css_default.noteInput,
|
|
466
585
|
"aria-label": t("note.aria"),
|
|
467
586
|
placeholder: t("note.placeholder"),
|
|
468
587
|
value: draft,
|
|
469
|
-
rows:
|
|
588
|
+
rows: 3,
|
|
470
589
|
onChange: (event) => {
|
|
471
590
|
setDraft(event.target.value);
|
|
472
591
|
}
|
|
473
592
|
}),
|
|
474
|
-
(0, react_jsx_runtime.
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
593
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
594
|
+
className: MessageFeedbackActions_module_css_default.noteActions,
|
|
595
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
596
|
+
type: "button",
|
|
597
|
+
className: MessageFeedbackActions_module_css_default.noteSave,
|
|
598
|
+
disabled: pending,
|
|
599
|
+
onClick: () => {
|
|
600
|
+
onSaveNote(rating);
|
|
601
|
+
},
|
|
602
|
+
children: t("note.save")
|
|
603
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
604
|
+
type: "button",
|
|
605
|
+
className: MessageFeedbackActions_module_css_default.noteCancel,
|
|
606
|
+
onClick: closeNote,
|
|
607
|
+
children: t("note.cancel")
|
|
608
|
+
})]
|
|
482
609
|
}),
|
|
483
|
-
(0, react_jsx_runtime.jsx)("
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
setNoteOpen(false);
|
|
488
|
-
},
|
|
489
|
-
children: t("note.cancel")
|
|
610
|
+
noteFailure !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
611
|
+
className: MessageFeedbackActions_module_css_default.failure,
|
|
612
|
+
role: "status",
|
|
613
|
+
children: noteFailure
|
|
490
614
|
})
|
|
491
615
|
]
|
|
492
|
-
}),
|
|
493
|
-
failure === null && loadFailed && (0, react_jsx_runtime.jsx)("span", {
|
|
494
|
-
className: MessageFeedbackActions_module_css_default.failure,
|
|
495
|
-
role: "status",
|
|
496
|
-
children: t("error.load")
|
|
497
|
-
}),
|
|
498
|
-
failure !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
499
|
-
className: MessageFeedbackActions_module_css_default.failure,
|
|
500
|
-
role: "status",
|
|
501
|
-
children: failure
|
|
502
|
-
})
|
|
616
|
+
}), document.body)
|
|
503
617
|
] });
|
|
504
618
|
}
|
|
505
619
|
//#endregion
|
|
@@ -512,6 +626,7 @@ window.__ModuleLoader__.load({
|
|
|
512
626
|
"action.dislike": "有问题的回答",
|
|
513
627
|
"action.dislikeActive": "取消标记",
|
|
514
628
|
"note.open": "补充说明",
|
|
629
|
+
"note.dialog": "反馈",
|
|
515
630
|
"note.placeholder": "这条回答哪里好,或哪里有问题?(可选)",
|
|
516
631
|
"note.save": "保存",
|
|
517
632
|
"note.cancel": "取消",
|
|
@@ -527,6 +642,7 @@ window.__ModuleLoader__.load({
|
|
|
527
642
|
"action.dislike": "Bad response",
|
|
528
643
|
"action.dislikeActive": "Remove rating",
|
|
529
644
|
"note.open": "Add a note",
|
|
645
|
+
"note.dialog": "Feedback",
|
|
530
646
|
"note.placeholder": "What was good, or what went wrong? (optional)",
|
|
531
647
|
"note.save": "Save",
|
|
532
648
|
"note.cancel": "Cancel",
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-message feedback controls: a Like/Dislike pair plus an optional note.
|
|
3
|
-
*
|
|
4
|
-
* reuse that row's chrome and sit between copy and branch.
|
|
3
|
+
* The buttons render inside the assistant message's IconActions row, so they
|
|
4
|
+
* reuse that row's chrome and sit between copy and branch. The note editor is
|
|
5
|
+
* a popover (portaled to `document.body`) anchored to the note trigger, not an
|
|
6
|
+
* inline expansion: a 260px textarea plus buttons cannot fit the row at any
|
|
7
|
+
* viewport, and an inline element pushed the branch action and clock out of the
|
|
8
|
+
* conversation column. Portaling out of the column also escapes its `overflow`
|
|
9
|
+
* clip, so the panel cannot be cropped or detached from the message it annotates.
|
|
5
10
|
* @module @deepseek-ai/dsh-client-ui-message-feedback/client/MessageFeedbackActions
|
|
6
11
|
*/
|
|
7
12
|
import type { MessageFeedbackActionProps } from './slots.ts';
|
|
@@ -9,7 +14,8 @@ import type { MessageFeedbackActionProps } from './slots.ts';
|
|
|
9
14
|
* One message's feedback controls.
|
|
10
15
|
* @param props - the owner's message identity, the injected verbs, and the
|
|
11
16
|
* shared feedback hook.
|
|
12
|
-
* @returns the rating buttons
|
|
17
|
+
* @returns the rating buttons and the note trigger, with the note editor
|
|
18
|
+
* portal-open beneath the trigger while it is open.
|
|
13
19
|
*/
|
|
14
20
|
export declare function MessageFeedbackActions({ messageId, ensure, rate, toggle, clearNote, useFeedback, t }: MessageFeedbackActionProps): import("react").JSX.Element;
|
|
15
21
|
//# sourceMappingURL=MessageFeedbackActions.d.ts.map
|
|
@@ -6,6 +6,7 @@ export declare const zh: {
|
|
|
6
6
|
'action.dislike': string;
|
|
7
7
|
'action.dislikeActive': string;
|
|
8
8
|
'note.open': string;
|
|
9
|
+
'note.dialog': string;
|
|
9
10
|
'note.placeholder': string;
|
|
10
11
|
'note.save': string;
|
|
11
12
|
'note.cancel': string;
|
|
@@ -29,6 +30,7 @@ export declare const en: {
|
|
|
29
30
|
'action.dislike': string;
|
|
30
31
|
'action.dislikeActive': string;
|
|
31
32
|
'note.open': string;
|
|
33
|
+
'note.dialog': string;
|
|
32
34
|
'note.placeholder': string;
|
|
33
35
|
'note.save': string;
|
|
34
36
|
'note.cancel': string;
|
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.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.8",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -42,35 +42,33 @@
|
|
|
42
42
|
},
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"
|
|
46
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.
|
|
47
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.
|
|
48
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.
|
|
50
|
-
"@deepseek-ai/dsh-
|
|
51
|
-
"@deepseek-ai/dsh-
|
|
52
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
|
|
53
|
-
"@deepseek-ai/dsh-message-feedback": "^0.1.0-rc.7",
|
|
54
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.7",
|
|
45
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.8",
|
|
46
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
|
|
47
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
48
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
|
|
50
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
|
|
51
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8",
|
|
55
52
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
53
|
+
"@deepseek-ai/dsh-message-feedback": "^0.1.0-rc.8"
|
|
57
54
|
},
|
|
58
55
|
"devDependencies": {
|
|
59
56
|
"@testing-library/react": "^16.1.0",
|
|
60
57
|
"@types/react": "~18.3.1",
|
|
58
|
+
"@types/react-dom": "~18.3.0",
|
|
61
59
|
"react": "^18.2.0",
|
|
62
60
|
"react-dom": "^18.2.0",
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
"@deepseek-ai/dsh-client-
|
|
65
|
-
"@deepseek-ai/dsh-client-
|
|
66
|
-
"@deepseek-ai/dsh-
|
|
67
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.
|
|
68
|
-
"@deepseek-ai/dsh-client-ui-
|
|
69
|
-
"@deepseek-ai/dsh-client-ui-
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/dsh-
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
61
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
|
|
62
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
63
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
|
|
64
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.8",
|
|
65
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.0-rc.8",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.8",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
|
|
69
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
|
|
70
|
+
"@deepseek-ai/dsh-message-feedback": "^0.1.0-rc.8",
|
|
71
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.8",
|
|
74
72
|
"@deepseek-ai/cordis": "^4.0.1"
|
|
75
73
|
},
|
|
76
74
|
"files": [
|