@morlay/ui-conversation-message-actions 0.0.19-alpha.5 → 0.0.19-alpha.6
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/dist/client.cjs +39 -64
- package/dist/index.mjs +8 -9
- package/dist/testing.d.cts +203 -414
- package/dist/testing.d.mts +203 -414
- package/dist/testing.mjs +2160 -825
- package/package.json +24 -24
- package/src/client/chat-node/MessageIconActions.tsx +12 -56
- package/src/client/chat-node/message-chrome.ts +0 -15
- package/src/client/composer-stats/StatsPills.tsx +6 -6
- package/src/client/controller.ts +31 -5
- package/src/index.ts +17 -12
- package/src/plan.ts +1 -1
- package/src/testing.ts +2 -4
package/dist/client.cjs
CHANGED
|
@@ -29,13 +29,18 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
turn,
|
|
30
30
|
cascade
|
|
31
31
|
}),
|
|
32
|
-
recall: (message, texts) => this.
|
|
33
|
-
action: "recall",
|
|
34
|
-
sessionId: this.sessionId,
|
|
35
|
-
eventSeq: message.eventSeq
|
|
36
|
-
}, () => this.setComposerDraft(texts.join("\n\n")))
|
|
32
|
+
recall: (message, texts) => this.recallAndEdit(message, texts)
|
|
37
33
|
};
|
|
38
34
|
}
|
|
35
|
+
async recallAndEdit(message, texts) {
|
|
36
|
+
const applied = await this.mutate({
|
|
37
|
+
action: "recall",
|
|
38
|
+
sessionId: this.sessionId,
|
|
39
|
+
eventSeq: message.eventSeq
|
|
40
|
+
}, () => this.setComposerDraft(texts.join("\n\n")));
|
|
41
|
+
if (applied) this.returnViewportToEnd();
|
|
42
|
+
return applied;
|
|
43
|
+
}
|
|
39
44
|
async mutate(operation, onApplied) {
|
|
40
45
|
if (this.pending !== null) return false;
|
|
41
46
|
this.pending = operation.action;
|
|
@@ -86,6 +91,23 @@ window.__ModuleLoader__.load({
|
|
|
86
91
|
}
|
|
87
92
|
console.warn("[session-editor] 未找到会话窗口重建入口(binding.resync / refresh 都不可用),窗口可能停留在 rewind 之前的状态");
|
|
88
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* 撤回后把会话视口送回底部:rewind 把窗口换短,而上游 ChatView 只在读者已经贴底时
|
|
96
|
+
* 才跟随(`tipMoved && atBottom`),停在中途的视口会悬在被截断的位置上;撤回的下一步
|
|
97
|
+
* 是改完再发,所以这里无条件送到底。容器用上游既有契约 `[data-conversation-scroll]`
|
|
98
|
+
* (ChatView / ConversationWidthControls / StatsPills 同用)。
|
|
99
|
+
*
|
|
100
|
+
* 窗口替换的渲染落在 resync 之后:写早了会被下一轮布局覆盖,因此等两帧再写。
|
|
101
|
+
*/
|
|
102
|
+
returnViewportToEnd() {
|
|
103
|
+
const scrollport = document.querySelector("[data-conversation-scroll]");
|
|
104
|
+
if (scrollport === null) return;
|
|
105
|
+
requestAnimationFrame(() => {
|
|
106
|
+
requestAnimationFrame(() => {
|
|
107
|
+
scrollport.scrollTop = scrollport.scrollHeight;
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
89
111
|
setComposerDraft(text) {
|
|
90
112
|
if (this.sessions.binding(this.sessionId) === void 0) return;
|
|
91
113
|
const scoped = this.sessions.scope(this.sessionId);
|
|
@@ -108,19 +130,6 @@ window.__ModuleLoader__.load({
|
|
|
108
130
|
next.setHours(24, 0, 0, 0);
|
|
109
131
|
return Math.max(next.getTime() - ms, 1);
|
|
110
132
|
}
|
|
111
|
-
function formatRunDuration(ms, t) {
|
|
112
|
-
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
113
|
-
const minutes = Math.floor(total / 60);
|
|
114
|
-
const seconds = total % 60;
|
|
115
|
-
return minutes > 0 ? t("duration.minutes", {
|
|
116
|
-
minutes,
|
|
117
|
-
seconds: String(seconds).padStart(2, "0")
|
|
118
|
-
}) : t("duration.seconds", { seconds });
|
|
119
|
-
}
|
|
120
|
-
function formatLatencySeconds(ms) {
|
|
121
|
-
const s = Math.max(0, ms) / 1e3;
|
|
122
|
-
return s < 10 ? String(Math.round(s * 10) / 10) : String(Math.round(s));
|
|
123
|
-
}
|
|
124
133
|
function formatTokensPerSecond(tps) {
|
|
125
134
|
const clamped = Math.max(0, tps);
|
|
126
135
|
return clamped >= 10 ? String(Math.round(clamped)) : String(Math.round(clamped * 10) / 10);
|
|
@@ -223,7 +232,7 @@ window.__ModuleLoader__.load({
|
|
|
223
232
|
};
|
|
224
233
|
//#endregion
|
|
225
234
|
//#region src/client/chat-node/MessageIconActions.tsx
|
|
226
|
-
function MessageIconActions({ text, time,
|
|
235
|
+
function MessageIconActions({ text, time, clock, onBranch, branchUnavailable = false, className, extraActions, onEdit, onRetry, t }) {
|
|
227
236
|
const day = useCalendarDay();
|
|
228
237
|
const reasonId = (0, react.useId)();
|
|
229
238
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
@@ -250,44 +259,10 @@ window.__ModuleLoader__.load({
|
|
|
250
259
|
}, 1e3);
|
|
251
260
|
});
|
|
252
261
|
}, [copied, text]);
|
|
253
|
-
const clockEl = time === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.
|
|
262
|
+
const clockEl = time === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
254
263
|
"data-time-label": "",
|
|
255
264
|
..._morlay_dsh_client_ui_primitives_client.styling.props(clock === "start" ? styles$3.timeStart : styles$3.timeEnd),
|
|
256
|
-
children:
|
|
257
|
-
formatMessageClock(time, t, day),
|
|
258
|
-
runMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
259
|
-
" ",
|
|
260
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
261
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$3.runTimeDot),
|
|
262
|
-
"aria-hidden": true,
|
|
263
|
-
children: "·"
|
|
264
|
-
}),
|
|
265
|
-
" ",
|
|
266
|
-
t("message.ranFor", { duration: formatRunDuration(runMs, t) })
|
|
267
|
-
] }),
|
|
268
|
-
ttftMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
269
|
-
" ",
|
|
270
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
271
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$3.runTimeDot),
|
|
272
|
-
"aria-hidden": true,
|
|
273
|
-
children: "·"
|
|
274
|
-
}),
|
|
275
|
-
" ",
|
|
276
|
-
t("message.turnTime.ttft"),
|
|
277
|
-
" ",
|
|
278
|
-
t("duration.seconds", { seconds: formatLatencySeconds(ttftMs) })
|
|
279
|
-
] }),
|
|
280
|
-
tokensPerSecond !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
281
|
-
" ",
|
|
282
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
283
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$3.runTimeDot),
|
|
284
|
-
"aria-hidden": true,
|
|
285
|
-
children: "·"
|
|
286
|
-
}),
|
|
287
|
-
" ",
|
|
288
|
-
t("message.tokensPerSecond", { tps: formatTokensPerSecond(tokensPerSecond) })
|
|
289
|
-
] })
|
|
290
|
-
]
|
|
265
|
+
children: formatMessageClock(time, t, day)
|
|
291
266
|
});
|
|
292
267
|
const actionsClass = _morlay_dsh_client_ui_primitives_client.styling.className(styles$3.actions);
|
|
293
268
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -303,7 +278,7 @@ window.__ModuleLoader__.load({
|
|
|
303
278
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$3.action),
|
|
304
279
|
"aria-label": copied ? t("copied") : t("copy"),
|
|
305
280
|
onClick: onCopy,
|
|
306
|
-
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
281
|
+
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {})
|
|
307
282
|
})
|
|
308
283
|
}),
|
|
309
284
|
extraActions,
|
|
@@ -315,7 +290,7 @@ window.__ModuleLoader__.load({
|
|
|
315
290
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$3.action),
|
|
316
291
|
"aria-label": "编辑",
|
|
317
292
|
onClick: onEdit,
|
|
318
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
293
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutlineRegular, {})
|
|
319
294
|
})
|
|
320
295
|
}),
|
|
321
296
|
onRetry !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
@@ -326,7 +301,7 @@ window.__ModuleLoader__.load({
|
|
|
326
301
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$3.action),
|
|
327
302
|
"aria-label": "重试此回合",
|
|
328
303
|
onClick: onRetry,
|
|
329
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
304
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {})
|
|
330
305
|
})
|
|
331
306
|
}),
|
|
332
307
|
onBranch !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
@@ -340,7 +315,7 @@ window.__ModuleLoader__.load({
|
|
|
340
315
|
"aria-describedby": branchUnavailable ? reasonId : void 0,
|
|
341
316
|
"data-unavailable": branchUnavailable || void 0,
|
|
342
317
|
onClick: branchUnavailable ? void 0 : onBranch,
|
|
343
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
318
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutlineRegular, {})
|
|
344
319
|
})
|
|
345
320
|
}),
|
|
346
321
|
onBranch !== void 0 && branchUnavailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1052,7 +1027,7 @@ window.__ModuleLoader__.load({
|
|
|
1052
1027
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$1.anchor),
|
|
1053
1028
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1054
1029
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$1.pill),
|
|
1055
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1030
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGaugeOutlineRegular, {}), label]
|
|
1056
1031
|
})
|
|
1057
1032
|
});
|
|
1058
1033
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -1067,7 +1042,7 @@ window.__ModuleLoader__.load({
|
|
|
1067
1042
|
onClick: () => {
|
|
1068
1043
|
setOpen(!open);
|
|
1069
1044
|
},
|
|
1070
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1045
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGaugeOutlineRegular, {}), label]
|
|
1071
1046
|
}), open && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1072
1047
|
ref: panelRef,
|
|
1073
1048
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.panel),
|
|
@@ -1079,7 +1054,7 @@ window.__ModuleLoader__.load({
|
|
|
1079
1054
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.title),
|
|
1080
1055
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1081
1056
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.titleLabel),
|
|
1082
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1057
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGaugeOutlineRegular, {}), t("stats.dialog.title")]
|
|
1083
1058
|
})
|
|
1084
1059
|
}),
|
|
1085
1060
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -1118,7 +1093,7 @@ window.__ModuleLoader__.load({
|
|
|
1118
1093
|
onClick: () => {
|
|
1119
1094
|
setOpen(!open);
|
|
1120
1095
|
},
|
|
1121
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1096
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDatabaseOutlineRegular, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1122
1097
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$1.label),
|
|
1123
1098
|
children: [totalText, cacheHitText !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1124
1099
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles$1.sep),
|
|
@@ -1137,7 +1112,7 @@ window.__ModuleLoader__.load({
|
|
|
1137
1112
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.title),
|
|
1138
1113
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1139
1114
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.titleLabel),
|
|
1140
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1115
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDatabaseOutlineRegular, {}), t("stats.dialog.usageTitle")]
|
|
1141
1116
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1142
1117
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.titleValue),
|
|
1143
1118
|
children: exactCount(total, t)
|
package/dist/index.mjs
CHANGED
|
@@ -68,7 +68,7 @@ var SessionEditor = class extends Service {
|
|
|
68
68
|
];
|
|
69
69
|
constructor(ctx) {
|
|
70
70
|
super(ctx, "sessionEditor");
|
|
71
|
-
registerHttpRoutes(ctx);
|
|
71
|
+
registerHttpRoutes(ctx, this);
|
|
72
72
|
}
|
|
73
73
|
readBranchPrefix(id, atSeq, mode, signal) {
|
|
74
74
|
return this.ctx.sessionBranch.readBranchPrefix(id, atSeq, mode, signal);
|
|
@@ -367,17 +367,16 @@ async function handleRoute(editor, request, response) {
|
|
|
367
367
|
respondJson(response, error instanceof TypeError ? 400 : 409, { error: message });
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
|
-
function registerHttpRoutes(ctx) {
|
|
371
|
-
ctx.
|
|
372
|
-
const webServer =
|
|
373
|
-
if (webServer === void 0) return
|
|
374
|
-
|
|
375
|
-
return webServer.register({
|
|
370
|
+
function registerHttpRoutes(ctx, editor) {
|
|
371
|
+
ctx.inject(["webServer"], (scope) => {
|
|
372
|
+
const webServer = scope.get("webServer");
|
|
373
|
+
if (webServer === void 0) return;
|
|
374
|
+
scope.effect(() => webServer.register({
|
|
376
375
|
kind: "exact",
|
|
377
376
|
path: SESSION_EDITOR_PATH,
|
|
378
377
|
handler: (request, response) => handleRoute(editor, request, response)
|
|
379
|
-
});
|
|
380
|
-
}
|
|
378
|
+
}), "session-editor: HTTP route");
|
|
379
|
+
});
|
|
381
380
|
}
|
|
382
381
|
//#endregion
|
|
383
382
|
export { SessionEditor, SessionEditor as default, closedTurns, editableMessages, retryableTurns };
|