@prettier-ai/dsh-client-ui-chat 0.1.2-alpha.1 → 0.1.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -8,6 +8,7 @@ window.__ModuleLoader__.load({
8
8
  let react = require("react");
9
9
  let _prettier_ai_dsh_client_ui_primitives = require("@prettier-ai/dsh-client-ui-primitives");
10
10
  let _prettier_ai_dsh_client_store = require("@prettier-ai/dsh-client-store");
11
+ let react_dom = require("react-dom");
11
12
  //#region ../../util/workspace-path/src/index.ts
12
13
  /**
13
14
  * Browser-safe Workspace path and display helpers.
@@ -946,7 +947,7 @@ window.__ModuleLoader__.load({
946
947
  }
947
948
  //#endregion
948
949
  //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-chat/src/client/chat/MessageIconActions.module.css.mjs
949
- const css$12 = ".BmHDMW_actions{height:calc(28px + var(--dsh-content-font-delta,0px));align-items:center;gap:10px;display:flex}.BmHDMW_timeStart{font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-right:12px}.BmHDMW_timeEnd{font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-left:12px}.BmHDMW_runTimeDot{margin:0 10px}@media (hover:hover){[data-time-hover-root] :is(.BmHDMW_timeStart,.BmHDMW_timeEnd){opacity:0;transition:opacity 80ms}[data-time-hover-root]:hover :is(.BmHDMW_timeStart,.BmHDMW_timeEnd),[data-time-hover-root]:focus-within :is(.BmHDMW_timeStart,.BmHDMW_timeEnd){opacity:1}}.BmHDMW_action{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.BmHDMW_action svg{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px))}.BmHDMW_action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.BmHDMW_action[data-unavailable]{cursor:default;opacity:.4}.BmHDMW_action[data-unavailable]:hover{color:var(--dsw-alias-label-tertiary);background:0 0}.BmHDMW_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
950
+ const css$12 = ".BmHDMW_actions{height:calc(28px + var(--dsh-content-font-delta,0px));align-items:center;gap:8px;display:flex}.BmHDMW_timeStart{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-right:12px}.BmHDMW_timeEnd{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);white-space:nowrap}@media (hover:hover){[data-actions-reveal=hover] .BmHDMW_actions{opacity:0;transition:opacity 80ms}[data-actions-reveal=hover]:hover .BmHDMW_actions,[data-actions-reveal=hover]:focus-within .BmHDMW_actions{opacity:1}}.BmHDMW_action{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.BmHDMW_action svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px))}.BmHDMW_action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.BmHDMW_action[data-unavailable]{cursor:default;opacity:.4}.BmHDMW_action[data-unavailable]:hover{color:var(--dsw-alias-label-tertiary);background:0 0}.BmHDMW_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
950
951
  const tagId$12 = "@prettier-ai/dsh-client-ui-chat/MessageIconActions.module.css";
951
952
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$12) + "]") === null) {
952
953
  const tag = document.createElement("style");
@@ -958,7 +959,6 @@ window.__ModuleLoader__.load({
958
959
  var MessageIconActions_module_css_default = {
959
960
  "action": "BmHDMW_action",
960
961
  "actions": "BmHDMW_actions",
961
- "runTimeDot": "BmHDMW_runTimeDot",
962
962
  "timeEnd": "BmHDMW_timeEnd",
963
963
  "timeStart": "BmHDMW_timeStart",
964
964
  "visuallyHidden": "BmHDMW_visuallyHidden"
@@ -970,7 +970,7 @@ window.__ModuleLoader__.load({
970
970
  * @param props - Copy text, event time, clock side, branch callback, className.
971
971
  * @returns The actions row element.
972
972
  */
973
- function MessageIconActions({ text, time, runMs, ttftMs, tokensPerSecond, clock, onBranch, branchUnavailable = false, className, extraActions, t }) {
973
+ function MessageIconActions({ text, time, clock, onBranch, branchUnavailable = false, className, extraActions, usageAction, t }) {
974
974
  const day = useCalendarDay();
975
975
  const reasonId = (0, react.useId)();
976
976
  const [copied, setCopied] = (0, react.useState)(false);
@@ -997,41 +997,9 @@ window.__ModuleLoader__.load({
997
997
  }, 1e3);
998
998
  });
999
999
  }, [copied, text]);
1000
- const clockEl = time === void 0 ? null : (0, react_jsx_runtime.jsxs)("span", {
1000
+ const clockEl = time === void 0 ? null : (0, react_jsx_runtime.jsx)("span", {
1001
1001
  className: clock === "start" ? MessageIconActions_module_css_default.timeStart : MessageIconActions_module_css_default.timeEnd,
1002
- children: [
1003
- formatMessageClock(time, t, day),
1004
- runMs !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1005
- " ",
1006
- (0, react_jsx_runtime.jsx)("span", {
1007
- className: MessageIconActions_module_css_default.runTimeDot,
1008
- "aria-hidden": true,
1009
- children: "·"
1010
- }),
1011
- " ",
1012
- t("message.ranFor", { duration: formatRunDuration(runMs, t) })
1013
- ] }),
1014
- ttftMs !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1015
- " ",
1016
- (0, react_jsx_runtime.jsx)("span", {
1017
- className: MessageIconActions_module_css_default.runTimeDot,
1018
- "aria-hidden": true,
1019
- children: "·"
1020
- }),
1021
- " ",
1022
- t("message.ttft", { seconds: formatLatencySeconds(ttftMs) })
1023
- ] }),
1024
- tokensPerSecond !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1025
- " ",
1026
- (0, react_jsx_runtime.jsx)("span", {
1027
- className: MessageIconActions_module_css_default.runTimeDot,
1028
- "aria-hidden": true,
1029
- children: "·"
1030
- }),
1031
- " ",
1032
- t("message.tokensPerSecond", { tps: formatTokensPerSecond(tokensPerSecond) })
1033
- ] })
1034
- ]
1002
+ children: formatMessageClock(time, t, day)
1035
1003
  });
1036
1004
  return (0, react_jsx_runtime.jsxs)("div", {
1037
1005
  className: className === void 0 ? MessageIconActions_module_css_default.actions : `${MessageIconActions_module_css_default.actions} ${className}`,
@@ -1068,6 +1036,7 @@ window.__ModuleLoader__.load({
1068
1036
  className: MessageIconActions_module_css_default.visuallyHidden,
1069
1037
  children: t("message.branchUnavailable")
1070
1038
  }),
1039
+ usageAction,
1071
1040
  clock === "end" ? clockEl : null
1072
1041
  ]
1073
1042
  });
@@ -1200,7 +1169,7 @@ window.__ModuleLoader__.load({
1200
1169
  });
1201
1170
  }
1202
1171
  /** Right-aligned bubble shared by user and steering rows. */
1203
- function UserStyleBubble({ content, renderMessageImages, actions, pending = false, echo = false, referenceLabels = [], previewImages, t }) {
1172
+ function UserStyleBubble({ content, renderMessageImages, actions, pending = false, echo = false, referenceLabels = [], previewImages, reveal = "always", t }) {
1204
1173
  const { text, images: contentImages, rest } = contentParts(content);
1205
1174
  const images = previewImages ?? contentImages;
1206
1175
  const truncated = (total) => t("json.truncated", { total });
@@ -1209,7 +1178,7 @@ window.__ModuleLoader__.load({
1209
1178
  className: MessageItem_module_css_default.userRow,
1210
1179
  "data-pending-steering": pending || void 0,
1211
1180
  "data-submission-echo": echo || void 0,
1212
- "data-time-hover-root": true,
1181
+ "data-actions-reveal": reveal,
1213
1182
  children: [(0, react_jsx_runtime.jsxs)("div", {
1214
1183
  className: MessageItem_module_css_default.userStack,
1215
1184
  children: [
@@ -1286,12 +1255,20 @@ window.__ModuleLoader__.load({
1286
1255
  });
1287
1256
  }
1288
1257
  /** User and admitted-steering keyed Chat renderer. */
1289
- const UserMessageNodeView = (0, react.memo)(function UserMessageNodeView({ node, renderMessageImages, t }) {
1258
+ const UserMessageNodeView = (0, react.memo)(function UserMessageNodeView({ node, renderMessageImages, useChat, t }) {
1290
1259
  const data = node.data;
1260
+ const isLatestUserRow = useChat((snapshot) => {
1261
+ for (let index = snapshot.order.length - 1; index >= 0; index -= 1) {
1262
+ const candidate = snapshot.nodes.get(snapshot.order[index] ?? "");
1263
+ if (candidate?.kind === "user" || candidate?.kind === "steering") return candidate.key === node.key;
1264
+ }
1265
+ return true;
1266
+ });
1291
1267
  return (0, react_jsx_runtime.jsx)(UserStyleBubble, {
1292
1268
  content: data.content,
1293
1269
  renderMessageImages,
1294
1270
  ...data.referenceLabels === void 0 ? {} : { referenceLabels: data.referenceLabels },
1271
+ reveal: isLatestUserRow ? "always" : "hover",
1295
1272
  t,
1296
1273
  actions: (text) => (0, react_jsx_runtime.jsx)(MessageIconActions, {
1297
1274
  text,
@@ -2408,18 +2385,21 @@ window.__ModuleLoader__.load({
2408
2385
  "message.maxTokens": "已达到输出 token 上限",
2409
2386
  "message.maxTokens.hint": "回答被截断,已有输出保留在对话中。发送“继续”可让模型接着输出。",
2410
2387
  "message.ranFor": "用时 {duration}",
2411
- "message.ttft": "首 token {seconds}秒",
2412
2388
  "message.tokensPerSecond": "{tps} tok/s",
2413
2389
  "message.turnUsage.title": "本轮用量",
2414
- "message.turnUsage.summaryWithCache": "{total} · 缓存命中率 {percent}%",
2390
+ "message.turnUsage.consumed": "用量 {total}",
2415
2391
  "message.turnUsage.model": "提供方 / 模型",
2392
+ "message.turnUsage.cacheHit": "缓存命中",
2416
2393
  "message.turnUsage.input": "未缓存输入",
2417
2394
  "message.turnUsage.cacheRead": "缓存读取",
2418
2395
  "message.turnUsage.cacheWrite": "缓存写入",
2419
2396
  "message.turnUsage.output": "输出",
2420
2397
  "message.turnUsage.reasoning": "(其中推理 {tokens})",
2421
- "message.turnUsage.total": "总计",
2422
2398
  "message.turnUsage.count": "{count} tok",
2399
+ "message.turnTime.title": "本轮用时和速度",
2400
+ "message.turnTime.duration": "本轮总用时",
2401
+ "message.turnTime.speed": "输出速度(TPS)",
2402
+ "message.turnTime.ttft": "首 token 用时(TTFT)",
2423
2403
  "duration.seconds": "{seconds}秒",
2424
2404
  "duration.minutes": "{minutes}分{seconds}秒",
2425
2405
  "command.running": "执行中…",
@@ -2517,18 +2497,21 @@ window.__ModuleLoader__.load({
2517
2497
  "message.maxTokens": "Output token limit reached",
2518
2498
  "message.maxTokens.hint": "The reply was cut off; earlier output is preserved in the conversation. Send \"continue\" to let the model resume.",
2519
2499
  "message.ranFor": "Ran for {duration}",
2520
- "message.ttft": "TTFT {seconds}s",
2521
2500
  "message.tokensPerSecond": "{tps} tok/s",
2522
2501
  "message.turnUsage.title": "Turn usage",
2523
- "message.turnUsage.summaryWithCache": "{total} · Cache hit {percent}%",
2502
+ "message.turnUsage.consumed": "Usage {total}",
2524
2503
  "message.turnUsage.model": "Provider / model",
2504
+ "message.turnUsage.cacheHit": "Cache hit",
2525
2505
  "message.turnUsage.input": "Uncached input",
2526
2506
  "message.turnUsage.cacheRead": "Cached input",
2527
2507
  "message.turnUsage.cacheWrite": "Cache write",
2528
2508
  "message.turnUsage.output": "Output",
2529
2509
  "message.turnUsage.reasoning": " ({tokens} reasoning)",
2530
- "message.turnUsage.total": "Total",
2531
2510
  "message.turnUsage.count": "{count} tok",
2511
+ "message.turnTime.title": "Turn time and speed",
2512
+ "message.turnTime.duration": "Total run time",
2513
+ "message.turnTime.speed": "Tokens per second (TPS)",
2514
+ "message.turnTime.ttft": "Time to first token (TTFT)",
2532
2515
  "duration.seconds": "{seconds}s",
2533
2516
  "duration.minutes": "{minutes}m {seconds}s",
2534
2517
  "command.running": "Running…",
@@ -3113,9 +3096,9 @@ window.__ModuleLoader__.load({
3113
3096
  return `99.${"9".repeat(distinguishingPlaces - 1)}${10 - roundedLoss}`;
3114
3097
  }
3115
3098
  //#endregion
3116
- //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-chat/src/client/chat/TurnUsageDisclosure.module.css.mjs
3117
- const css$4 = ".psHEaW_root{min-width:0}.psHEaW_root[data-open]{padding-bottom:4px}.psHEaW_root [data-disclosure-row]:focus-visible{outline:2px solid var(--dsw-alias-label-tertiary);outline-offset:-2px;border-radius:6px}.psHEaW_chevron{color:var(--dsw-alias-label-secondary)}.psHEaW_separator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.psHEaW_summary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);font-variant-numeric:tabular-nums;line-height:calc(24px + var(--dsh-content-font-delta,0px));text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.psHEaW_details{box-sizing:border-box;width:calc(100% - 22px - var(--dsh-content-font-delta,0px));margin:4px 0 0 calc(22px + var(--dsh-content-font-delta,0px));background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-tertiary);border-radius:8px;grid-template-columns:minmax(76px,auto) minmax(0,1fr);gap:6px 16px;padding:10px 16px 12px 12px;font-size:12px;line-height:18px;display:grid}.psHEaW_details dt,.psHEaW_details dd{min-width:0;margin:0}.psHEaW_details dd{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-align:right}.psHEaW_details .psHEaW_route{overflow-wrap:anywhere}.psHEaW_reasoning{color:var(--dsw-alias-label-tertiary);white-space:nowrap}.psHEaW_totalLabel,.psHEaW_details .psHEaW_totalValue{border-top:1px solid var(--dsw-alias-separator-primary);color:var(--dsw-alias-label-primary);padding-top:6px}@media (width<=480px){.psHEaW_details{gap-inline:10px;grid-template-columns:minmax(72px,auto) minmax(0,1fr)}}";
3118
- const tagId$4 = "@prettier-ai/dsh-client-ui-chat/TurnUsageDisclosure.module.css";
3099
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-chat/src/client/chat/TurnUsagePanel.module.css.mjs
3100
+ const css$4 = ".ZVOUHW_root{min-width:0;display:inline-flex}.ZVOUHW_root+.ZVOUHW_root{margin-left:-6px}.ZVOUHW_trigger{min-width:0;height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);font-variant-numeric:tabular-nums;line-height:calc(24px + var(--dsh-content-font-delta,0px));white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:28px;align-items:center;gap:4px;padding:6px 8px;display:inline-flex}.ZVOUHW_label{text-overflow:ellipsis;min-width:0;overflow:hidden}.ZVOUHW_trigger svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px));flex:none}.ZVOUHW_trigger:hover,.ZVOUHW_trigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}@media (width<=480px){.ZVOUHW_trigger{width:calc(28px + var(--dsh-content-font-delta,0px));justify-content:center;padding:6px}.ZVOUHW_trigger .ZVOUHW_label{display:none}.ZVOUHW_root+.ZVOUHW_root{margin-left:0}}.ZVOUHW_panel{z-index:1100;box-sizing:border-box;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);width:max-content;min-width:min(300px,100vw - 24px);max-width:min(440px,100vw - 24px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-secondary);cursor:default;border-radius:12px;padding:16px;font-size:12px;line-height:18px;position:fixed}.ZVOUHW_title{color:var(--dsw-alias-label-primary);justify-content:space-between;gap:16px;margin-bottom:8px;font-weight:500;display:flex}.ZVOUHW_titleRule{border-top:1px solid var(--dsw-alias-border-l2);margin-bottom:10px}.ZVOUHW_titleValue{font-variant-numeric:tabular-nums}.ZVOUHW_titleLabel{align-items:center;gap:6px;min-width:0;display:inline-flex}.ZVOUHW_titleLabel svg{flex:none;width:14px;height:14px}.ZVOUHW_details{color:var(--dsw-alias-label-tertiary);grid-template-columns:minmax(76px,auto) minmax(0,1fr);gap:6px 16px;margin:0;display:grid}.ZVOUHW_details dt,.ZVOUHW_details dd{min-width:0;margin:0}.ZVOUHW_details dd{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-align:right}.ZVOUHW_details .ZVOUHW_route{overflow-wrap:anywhere}.ZVOUHW_reasoning{color:var(--dsw-alias-label-tertiary);white-space:nowrap}";
3101
+ const tagId$4 = "@prettier-ai/dsh-client-ui-chat/TurnUsagePanel.module.css";
3119
3102
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
3120
3103
  const tag = document.createElement("style");
3121
3104
  tag.dataset.plugin = "@prettier-ai/dsh-client-ui-chat";
@@ -3123,81 +3106,194 @@ window.__ModuleLoader__.load({
3123
3106
  tag.textContent = css$4;
3124
3107
  document.head.appendChild(tag);
3125
3108
  }
3126
- var TurnUsageDisclosure_module_css_default = {
3127
- "chevron": "psHEaW_chevron",
3128
- "details": "psHEaW_details",
3129
- "reasoning": "psHEaW_reasoning",
3130
- "root": "psHEaW_root",
3131
- "route": "psHEaW_route",
3132
- "separator": "psHEaW_separator",
3133
- "summary": "psHEaW_summary",
3134
- "totalLabel": "psHEaW_totalLabel",
3135
- "totalValue": "psHEaW_totalValue"
3109
+ var TurnUsagePanel_module_css_default = {
3110
+ "details": "ZVOUHW_details",
3111
+ "label": "ZVOUHW_label",
3112
+ "panel": "ZVOUHW_panel",
3113
+ "reasoning": "ZVOUHW_reasoning",
3114
+ "root": "ZVOUHW_root",
3115
+ "route": "ZVOUHW_route",
3116
+ "title": "ZVOUHW_title",
3117
+ "titleLabel": "ZVOUHW_titleLabel",
3118
+ "titleRule": "ZVOUHW_titleRule",
3119
+ "titleValue": "ZVOUHW_titleValue",
3120
+ "trigger": "ZVOUHW_trigger"
3136
3121
  };
3137
3122
  //#endregion
3138
- //#region lib/types/client/chat/TurnUsageDisclosure.js
3123
+ //#region lib/types/client/chat/TurnUsagePanel.js
3139
3124
  function formatCompactCount(value, t) {
3140
3125
  return t("message.turnUsage.count", { count: formatTokens(value, t) });
3141
3126
  }
3142
3127
  function formatExactCount(value, t) {
3143
3128
  return t("message.turnUsage.count", { count: formatExactTokens(value, t) });
3144
3129
  }
3145
- /** Compact per-Turn usage summary with an opt-in bucket breakdown. */
3146
- function TurnUsageDisclosure({ usage, t }) {
3130
+ /** Viewport margin the placement clamp keeps (the Menu portal margin). */
3131
+ const PANEL_MARGIN = 12;
3132
+ /** Distance between the trigger's top edge and the panel's bottom. */
3133
+ const PANEL_GAP = 8;
3134
+ /**
3135
+ * Unplaced portal panel: hidden but laid out so the clamp measures real
3136
+ * dimensions (the `useAnchoredPosition` measure pass).
3137
+ */
3138
+ const MEASURE_STYLE = {
3139
+ visibility: "hidden",
3140
+ left: 0,
3141
+ top: 0
3142
+ };
3143
+ /** One trigger-anchored dialog seat: open state, viewport-clamped placement, outside-close. */
3144
+ function useStatDialog() {
3147
3145
  const [open, setOpen] = (0, react.useState)(false);
3146
+ const rootRef = (0, react.useRef)(null);
3147
+ const panelRef = (0, react.useRef)(null);
3148
+ const pos = (0, _prettier_ai_dsh_client_ui_primitives.useAnchoredPosition)({
3149
+ open,
3150
+ anchorRef: rootRef,
3151
+ panelRef,
3152
+ side: "top",
3153
+ gap: PANEL_GAP,
3154
+ margin: PANEL_MARGIN
3155
+ });
3156
+ (0, _prettier_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, setOpen, panelRef);
3157
+ (0, react.useEffect)(() => {
3158
+ if (!open) return;
3159
+ const onKeyDown = (e) => {
3160
+ if (e.key === "Escape") setOpen(false);
3161
+ };
3162
+ document.addEventListener("keydown", onKeyDown);
3163
+ return () => {
3164
+ document.removeEventListener("keydown", onKeyDown);
3165
+ };
3166
+ }, [open]);
3167
+ return {
3168
+ open,
3169
+ setOpen,
3170
+ rootRef,
3171
+ panelRef,
3172
+ pos
3173
+ };
3174
+ }
3175
+ /**
3176
+ * Turn-usage IconActions pill with a click-open Turn-usage details dialog.
3177
+ * @param props - Turn usage buckets and locale seat.
3178
+ * @returns The trigger and, while open, its portaled dialog anchored above the trigger.
3179
+ */
3180
+ function TurnUsagePanel({ usage, t }) {
3181
+ const { open, setOpen, rootRef, panelRef, pos } = useStatDialog();
3148
3182
  const cacheHit = usage.cacheReadTokens === void 0 ? null : formatCacheHitPercent(usage.cacheReadTokens, usage.totalTokens - usage.outputTokens, 1);
3149
3183
  const total = formatCompactCount(usage.totalTokens, t);
3150
- const summary = cacheHit === null ? total : t("message.turnUsage.summaryWithCache", {
3151
- total,
3152
- percent: cacheHit
3153
- });
3154
3184
  const routes = usage.routes?.map((route) => `${route.provider}/${route.model}`).join(", ") ?? "";
3155
- return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.DisclosureRow, {
3156
- icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconDataOutline16, {}),
3157
- title: t("message.turnUsage.title"),
3158
- open,
3159
- expandable: true,
3160
- onToggle: () => {
3161
- setOpen((value) => !value);
3162
- },
3163
- expandOnRowClick: true,
3164
- keepContentWhenOpen: true,
3165
- collapsedContent: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
3166
- className: TurnUsageDisclosure_module_css_default.separator,
3167
- "aria-hidden": true
3168
- }), (0, react_jsx_runtime.jsx)("span", {
3169
- className: TurnUsageDisclosure_module_css_default.summary,
3170
- children: summary
3171
- })] }),
3172
- className: TurnUsageDisclosure_module_css_default.root,
3173
- chevronClassName: TurnUsageDisclosure_module_css_default.chevron,
3174
- children: (0, react_jsx_runtime.jsxs)("dl", {
3175
- className: TurnUsageDisclosure_module_css_default.details,
3176
- "data-turn-usage-details": true,
3185
+ return (0, react_jsx_runtime.jsxs)("span", {
3186
+ ref: rootRef,
3187
+ className: TurnUsagePanel_module_css_default.root,
3188
+ children: [(0, react_jsx_runtime.jsxs)("button", {
3189
+ type: "button",
3190
+ className: TurnUsagePanel_module_css_default.trigger,
3191
+ "aria-haspopup": "dialog",
3192
+ "aria-expanded": open,
3193
+ onClick: () => {
3194
+ setOpen(!open);
3195
+ },
3196
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconDatabaseOutline16, {}), (0, react_jsx_runtime.jsx)("span", {
3197
+ className: TurnUsagePanel_module_css_default.label,
3198
+ children: t("message.turnUsage.consumed", { total })
3199
+ })]
3200
+ }), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
3201
+ ref: panelRef,
3202
+ className: TurnUsagePanel_module_css_default.panel,
3203
+ role: "dialog",
3204
+ "aria-label": t("message.turnUsage.title"),
3205
+ style: pos ?? MEASURE_STYLE,
3177
3206
  children: [
3178
- routes !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.model") }), (0, react_jsx_runtime.jsx)("dd", {
3179
- className: TurnUsageDisclosure_module_css_default.route,
3180
- children: routes
3181
- })] }),
3182
- (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.input") }),
3183
- (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.uncachedInputTokens, t) }),
3184
- usage.cacheReadTokens !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheRead") }), (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.cacheReadTokens, t) })] }),
3185
- usage.cacheWriteTokens !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheWrite") }), (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.cacheWriteTokens, t) })] }),
3186
- (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.output") }),
3187
- (0, react_jsx_runtime.jsxs)("dd", { children: [formatExactCount(usage.outputTokens, t), usage.reasoningTokens !== void 0 && (0, react_jsx_runtime.jsx)("span", {
3188
- className: TurnUsageDisclosure_module_css_default.reasoning,
3189
- children: t("message.turnUsage.reasoning", { tokens: formatExactCount(usage.reasoningTokens, t) })
3190
- })] }),
3191
- (0, react_jsx_runtime.jsx)("dt", {
3192
- className: TurnUsageDisclosure_module_css_default.totalLabel,
3193
- children: t("message.turnUsage.total")
3207
+ (0, react_jsx_runtime.jsxs)("div", {
3208
+ className: TurnUsagePanel_module_css_default.title,
3209
+ children: [(0, react_jsx_runtime.jsxs)("span", {
3210
+ className: TurnUsagePanel_module_css_default.titleLabel,
3211
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconDatabaseOutline16, {}), t("message.turnUsage.title")]
3212
+ }), (0, react_jsx_runtime.jsx)("span", {
3213
+ className: TurnUsagePanel_module_css_default.titleValue,
3214
+ children: formatExactCount(usage.totalTokens, t)
3215
+ })]
3194
3216
  }),
3195
- (0, react_jsx_runtime.jsx)("dd", {
3196
- className: TurnUsageDisclosure_module_css_default.totalValue,
3197
- children: formatExactCount(usage.totalTokens, t)
3217
+ (0, react_jsx_runtime.jsx)("div", {
3218
+ className: TurnUsagePanel_module_css_default.titleRule,
3219
+ "aria-hidden": true
3220
+ }),
3221
+ (0, react_jsx_runtime.jsxs)("dl", {
3222
+ className: TurnUsagePanel_module_css_default.details,
3223
+ "data-turn-usage-details": true,
3224
+ children: [
3225
+ routes !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.model") }), (0, react_jsx_runtime.jsx)("dd", {
3226
+ className: TurnUsagePanel_module_css_default.route,
3227
+ children: routes
3228
+ })] }),
3229
+ cacheHit !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheHit") }), (0, react_jsx_runtime.jsx)("dd", { children: `${cacheHit}%` })] }),
3230
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.input") }),
3231
+ (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.uncachedInputTokens, t) }),
3232
+ usage.cacheReadTokens !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheRead") }), (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.cacheReadTokens, t) })] }),
3233
+ usage.cacheWriteTokens !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheWrite") }), (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.cacheWriteTokens, t) })] }),
3234
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.output") }),
3235
+ (0, react_jsx_runtime.jsxs)("dd", { children: [formatExactCount(usage.outputTokens, t), usage.reasoningTokens !== void 0 && (0, react_jsx_runtime.jsx)("span", {
3236
+ className: TurnUsagePanel_module_css_default.reasoning,
3237
+ children: t("message.turnUsage.reasoning", { tokens: formatExactCount(usage.reasoningTokens, t) })
3238
+ })] })
3239
+ ]
3198
3240
  })
3199
3241
  ]
3200
- })
3242
+ }), document.body)]
3243
+ });
3244
+ }
3245
+ /**
3246
+ * Turn-time IconActions pill with a click-open Turn-time details dialog.
3247
+ * @param props - Turn timing facts and locale seat.
3248
+ * @returns The clock-and-duration trigger and, while open, its portaled dialog anchored above the trigger.
3249
+ */
3250
+ function TurnTimePanel({ runMs, tokensPerSecond, ttftMs, t }) {
3251
+ const { open, setOpen, rootRef, panelRef, pos } = useStatDialog();
3252
+ return (0, react_jsx_runtime.jsxs)("span", {
3253
+ ref: rootRef,
3254
+ className: TurnUsagePanel_module_css_default.root,
3255
+ children: [(0, react_jsx_runtime.jsxs)("button", {
3256
+ type: "button",
3257
+ className: TurnUsagePanel_module_css_default.trigger,
3258
+ "aria-haspopup": "dialog",
3259
+ "aria-expanded": open,
3260
+ onClick: () => {
3261
+ setOpen(!open);
3262
+ },
3263
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconClockOutline16, {}), (0, react_jsx_runtime.jsx)("span", {
3264
+ className: TurnUsagePanel_module_css_default.label,
3265
+ children: t("message.ranFor", { duration: formatRunDuration(runMs, t) })
3266
+ })]
3267
+ }), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
3268
+ ref: panelRef,
3269
+ className: TurnUsagePanel_module_css_default.panel,
3270
+ role: "dialog",
3271
+ "aria-label": t("message.turnTime.title"),
3272
+ style: pos ?? MEASURE_STYLE,
3273
+ children: [
3274
+ (0, react_jsx_runtime.jsx)("div", {
3275
+ className: TurnUsagePanel_module_css_default.title,
3276
+ children: (0, react_jsx_runtime.jsxs)("span", {
3277
+ className: TurnUsagePanel_module_css_default.titleLabel,
3278
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconClockOutline16, {}), t("message.turnTime.title")]
3279
+ })
3280
+ }),
3281
+ (0, react_jsx_runtime.jsx)("div", {
3282
+ className: TurnUsagePanel_module_css_default.titleRule,
3283
+ "aria-hidden": true
3284
+ }),
3285
+ (0, react_jsx_runtime.jsxs)("dl", {
3286
+ className: TurnUsagePanel_module_css_default.details,
3287
+ "data-turn-time-details": true,
3288
+ children: [
3289
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnTime.duration") }),
3290
+ (0, react_jsx_runtime.jsx)("dd", { children: formatRunDuration(runMs, t) }),
3291
+ tokensPerSecond !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnTime.speed") }), (0, react_jsx_runtime.jsx)("dd", { children: t("message.tokensPerSecond", { tps: formatTokensPerSecond(tokensPerSecond) }) })] }),
3292
+ ttftMs !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnTime.ttft") }), (0, react_jsx_runtime.jsx)("dd", { children: t("duration.seconds", { seconds: formatLatencySeconds(ttftMs) }) })] })
3293
+ ]
3294
+ })
3295
+ ]
3296
+ }), document.body)]
3201
3297
  });
3202
3298
  }
3203
3299
  //#endregion
@@ -3212,7 +3308,7 @@ window.__ModuleLoader__.load({
3212
3308
  }
3213
3309
  //#endregion
3214
3310
  //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-chat/src/client/chat/TurnTailNodeView.module.css.mjs
3215
- const css$3 = ".v8MmmG_root{flex-direction:column;gap:16px;display:flex}.v8MmmG_footer{flex-direction:column;gap:4px;min-width:0;display:flex}.v8MmmG_actions{margin-left:-6px}";
3311
+ const css$3 = ".v8MmmG_root{flex-direction:column;gap:16px;display:flex}.v8MmmG_actions{margin-left:-6px}";
3216
3312
  const tagId$3 = "@prettier-ai/dsh-client-ui-chat/TurnTailNodeView.module.css";
3217
3313
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
3218
3314
  const tag = document.createElement("style");
@@ -3223,7 +3319,6 @@ window.__ModuleLoader__.load({
3223
3319
  }
3224
3320
  var TurnTailNodeView_module_css_default = {
3225
3321
  "actions": "v8MmmG_actions",
3226
- "footer": "v8MmmG_footer",
3227
3322
  "root": "v8MmmG_root"
3228
3323
  };
3229
3324
  //#endregion
@@ -3232,6 +3327,7 @@ window.__ModuleLoader__.load({
3232
3327
  const TurnTailNodeView = (0, react.memo)(function TurnTailNodeView({ node, openFile, forkAt, renderSlot, renderSlotChain, t, useChat }) {
3233
3328
  const data = node.data;
3234
3329
  const hasLaterChatNode = useChat((snapshot) => snapshot.locations.getTurn(data.turn).at(-1) !== node.key);
3330
+ const isLatestTurn = useChat((snapshot) => snapshot.timeline.turnOrder.at(-1) === data.turn);
3235
3331
  const turn = node.location.kind === "turn" || node.location.kind === "step" ? node.location.turn : void 0;
3236
3332
  if (turn === void 0) return null;
3237
3333
  const closing = data.closing;
@@ -3250,27 +3346,27 @@ window.__ModuleLoader__.load({
3250
3346
  return (0, react_jsx_runtime.jsxs)("div", {
3251
3347
  className: TurnTailNodeView_module_css_default.root,
3252
3348
  "data-turn-tail": data.turn,
3253
- "data-time-hover-root": true,
3254
- children: [tail, (0, react_jsx_runtime.jsxs)("div", {
3255
- className: TurnTailNodeView_module_css_default.footer,
3256
- children: [data.tokenUsage === void 0 ? null : (0, react_jsx_runtime.jsx)(TurnUsageDisclosure, {
3349
+ "data-actions-reveal": isLatestTurn ? "always" : "hover",
3350
+ children: [tail, (0, react_jsx_runtime.jsx)(MessageIconActions, {
3351
+ text: assistantText(closing.blocks),
3352
+ time: closing.time,
3353
+ clock: "end",
3354
+ onBranch: () => {
3355
+ forkAt(closing.finalNode.seq);
3356
+ },
3357
+ branchUnavailable: data.branchUnavailable || hasLaterChatNode,
3358
+ className: TurnTailNodeView_module_css_default.actions,
3359
+ extraActions: assistantActions,
3360
+ usageAction: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [data.tokenUsage !== void 0 && (0, react_jsx_runtime.jsx)(TurnUsagePanel, {
3257
3361
  usage: data.tokenUsage,
3258
3362
  t
3259
- }), (0, react_jsx_runtime.jsx)(MessageIconActions, {
3260
- text: assistantText(closing.blocks),
3261
- time: closing.time,
3363
+ }), runMs !== void 0 && (0, react_jsx_runtime.jsx)(TurnTimePanel, {
3262
3364
  runMs,
3263
- ttftMs: data.ttftMs,
3264
3365
  tokensPerSecond: data.tokensPerSecond,
3265
- clock: "end",
3266
- onBranch: () => {
3267
- forkAt(closing.finalNode.seq);
3268
- },
3269
- branchUnavailable: data.branchUnavailable || hasLaterChatNode,
3270
- className: TurnTailNodeView_module_css_default.actions,
3271
- extraActions: assistantActions,
3366
+ ttftMs: data.ttftMs,
3272
3367
  t
3273
- })]
3368
+ })] }),
3369
+ t
3274
3370
  })]
3275
3371
  });
3276
3372
  });
package/lib/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { settingsNamespace } from "@prettier-ai/dsh-settings";
2
1
  import z from "@prettier-ai/schemastery";
3
2
  //#region lib/types/chat-settings.js
4
3
  /** Chat transcript preferences stored in the Host user-settings document. */
@@ -18,7 +17,7 @@ const ChatSettingsSchema = z.object({ [TRANSCRIPT_VIEW_FIELD]: z.union([...TRANS
18
17
  /** Register the durable Chat settings section when a provider exists. */
19
18
  function apply(ctx) {
20
19
  ctx.inject(["settings"], (settingsCtx) => {
21
- settingsCtx.settings.register(settingsNamespace(CHAT_SETTINGS_NAMESPACE), ChatSettingsSchema);
20
+ settingsCtx.settings.register(CHAT_SETTINGS_NAMESPACE, ChatSettingsSchema);
22
21
  });
23
22
  }
24
23
  //#endregion
@@ -5,12 +5,6 @@ export interface MessageIconActionsProps {
5
5
  text: string;
6
6
  /** Unix epoch ms for the clock label; omitted for transient messages. */
7
7
  time?: number | undefined;
8
- /** Turn wall time in ms, appended to the clock as `· Ran for 15s`; omitted when the turn's start is unknown. */
9
- runMs?: number | undefined;
10
- /** Turn first-step TTFT in ms, appended as `· TTFT 1.2s`; omitted when unrecorded. */
11
- ttftMs?: number | undefined;
12
- /** Turn decode throughput, appended as `· 34 tok/s`; omitted when unrecorded. */
13
- tokensPerSecond?: number | undefined;
14
8
  /** Clock before icons (user) or after (assistant). */
15
9
  clock: 'start' | 'end';
16
10
  /** Fork the session at this message; omission hides the branch action. */
@@ -24,6 +18,11 @@ export interface MessageIconActionsProps {
24
18
  * built-in copy and branch controls.
25
19
  */
26
20
  extraActions?: ReactNode;
21
+ /**
22
+ * Icon-row Turn-usage trigger (the TurnUsagePanel pill), seated after the
23
+ * branch control at the end of the icon cluster.
24
+ */
25
+ usageAction?: ReactNode;
27
26
  /** The owning view's locale seat, passed down as a plain prop. */
28
27
  t: ChatViewSlotProps['t'];
29
28
  }
@@ -32,5 +31,5 @@ export interface MessageIconActionsProps {
32
31
  * @param props - Copy text, event time, clock side, branch callback, className.
33
32
  * @returns The actions row element.
34
33
  */
35
- export declare function MessageIconActions({ text, time, runMs, ttftMs, tokensPerSecond, clock, onBranch, branchUnavailable, className, extraActions, t, }: MessageIconActionsProps): import("react").JSX.Element;
34
+ export declare function MessageIconActions({ text, time, clock, onBranch, branchUnavailable, className, extraActions, usageAction, t, }: MessageIconActionsProps): import("react").JSX.Element;
36
35
  //# sourceMappingURL=MessageIconActions.d.ts.map
@@ -26,7 +26,7 @@ export declare function PendingSubmissionBubble({ submission, renderMessageImage
26
26
  t: ChatViewSlotProps['t'];
27
27
  }): ReactNode;
28
28
  /** User and admitted-steering keyed Chat renderer. */
29
- export declare const UserMessageNodeView: import("react").MemoExoticComponent<({ node, renderMessageImages, t, }: ChatNodeViewProps<"user" | "steering">) => import("react").JSX.Element>;
29
+ export declare const UserMessageNodeView: import("react").MemoExoticComponent<({ node, renderMessageImages, useChat, t, }: ChatNodeViewProps<"user" | "steering">) => import("react").JSX.Element>;
30
30
  /** Injected-context keyed Chat renderer. */
31
31
  export declare const ContextMessageNodeView: import("react").MemoExoticComponent<({ node, t }: ChatNodeViewProps<"context">) => import("react").JSX.Element>;
32
32
  /** Automatic compaction keyed Chat renderer. */
@@ -0,0 +1,30 @@
1
+ import type { TurnTokenUsage } from '../contract/chat-nodes.ts';
2
+ import type { ChatViewSlotProps } from '../contract/slots.ts';
3
+ export interface TurnUsagePanelProps {
4
+ usage: TurnTokenUsage;
5
+ /** The owning view's locale seat, passed down as a plain prop. */
6
+ t: ChatViewSlotProps['t'];
7
+ }
8
+ export interface TurnTimePanelProps {
9
+ /** Turn wall time in ms, the pill's label. */
10
+ runMs: number;
11
+ /** Turn decode throughput, a dialog row when known. */
12
+ tokensPerSecond?: number | undefined;
13
+ /** Turn first-step TTFT in ms, a dialog row when known. */
14
+ ttftMs?: number | undefined;
15
+ /** The owning view's locale seat, passed down as a plain prop. */
16
+ t: ChatViewSlotProps['t'];
17
+ }
18
+ /**
19
+ * Turn-usage IconActions pill with a click-open Turn-usage details dialog.
20
+ * @param props - Turn usage buckets and locale seat.
21
+ * @returns The trigger and, while open, its portaled dialog anchored above the trigger.
22
+ */
23
+ export declare function TurnUsagePanel({ usage, t }: TurnUsagePanelProps): import("react").JSX.Element;
24
+ /**
25
+ * Turn-time IconActions pill with a click-open Turn-time details dialog.
26
+ * @param props - Turn timing facts and locale seat.
27
+ * @returns The clock-and-duration trigger and, while open, its portaled dialog anchored above the trigger.
28
+ */
29
+ export declare function TurnTimePanel({ runMs, tokensPerSecond, ttftMs, t }: TurnTimePanelProps): import("react").JSX.Element;
30
+ //# sourceMappingURL=TurnUsagePanel.d.ts.map
@@ -86,18 +86,21 @@ export declare const zh: {
86
86
  'message.maxTokens': string;
87
87
  'message.maxTokens.hint': string;
88
88
  'message.ranFor': string;
89
- 'message.ttft': string;
90
89
  'message.tokensPerSecond': string;
91
90
  'message.turnUsage.title': string;
92
- 'message.turnUsage.summaryWithCache': string;
91
+ 'message.turnUsage.consumed': string;
93
92
  'message.turnUsage.model': string;
93
+ 'message.turnUsage.cacheHit': string;
94
94
  'message.turnUsage.input': string;
95
95
  'message.turnUsage.cacheRead': string;
96
96
  'message.turnUsage.cacheWrite': string;
97
97
  'message.turnUsage.output': string;
98
98
  'message.turnUsage.reasoning': string;
99
- 'message.turnUsage.total': string;
100
99
  'message.turnUsage.count': string;
100
+ 'message.turnTime.title': string;
101
+ 'message.turnTime.duration': string;
102
+ 'message.turnTime.speed': string;
103
+ 'message.turnTime.ttft': string;
101
104
  'duration.seconds': string;
102
105
  'duration.minutes': string;
103
106
  'command.running': string;
@@ -197,18 +200,21 @@ export declare const en: {
197
200
  'message.maxTokens': string;
198
201
  'message.maxTokens.hint': string;
199
202
  'message.ranFor': string;
200
- 'message.ttft': string;
201
203
  'message.tokensPerSecond': string;
202
204
  'message.turnUsage.title': string;
203
- 'message.turnUsage.summaryWithCache': string;
205
+ 'message.turnUsage.consumed': string;
204
206
  'message.turnUsage.model': string;
207
+ 'message.turnUsage.cacheHit': string;
205
208
  'message.turnUsage.input': string;
206
209
  'message.turnUsage.cacheRead': string;
207
210
  'message.turnUsage.cacheWrite': string;
208
211
  'message.turnUsage.output': string;
209
212
  'message.turnUsage.reasoning': string;
210
- 'message.turnUsage.total': string;
211
213
  'message.turnUsage.count': string;
214
+ 'message.turnTime.title': string;
215
+ 'message.turnTime.duration': string;
216
+ 'message.turnTime.speed': string;
217
+ 'message.turnTime.ttft': string;
212
218
  'duration.seconds': string;
213
219
  'duration.minutes': string;
214
220
  'command.running': string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prettier-ai/dsh-client-ui-chat",
3
3
  "description": "Chat Conversation target, node definitions, renderers, and details surface",
4
- "version": "0.1.2-alpha.1",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -47,66 +47,44 @@
47
47
  },
48
48
  "license": "MIT",
49
49
  "peerDependencies": {
50
- "@prettier-ai/cordis": "^4.0.1",
51
- "@prettier-ai/dsh-api-session-controller": "^0.1.2-alpha.1",
52
- "@prettier-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1",
53
- "@prettier-ai/dsh-agent": "^0.1.2-alpha.1",
54
- "@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.1",
55
- "@prettier-ai/dsh-client-locale": "^0.1.2-alpha.1",
56
- "@prettier-ai/dsh-attachment": "^0.1.2-alpha.1",
57
- "@prettier-ai/dsh-client-ui-approval": "^0.1.2-alpha.1",
58
- "@prettier-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1",
59
- "@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1",
60
- "@prettier-ai/dsh-client-ui-session": "^0.1.2-alpha.1",
61
- "@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.1",
62
- "@prettier-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1",
63
- "@prettier-ai/dsh-compaction": "^0.1.2-alpha.1",
64
- "@prettier-ai/dsh-commands": "^0.1.2-alpha.1",
65
- "@prettier-ai/dsh-llm": "^0.1.2-alpha.1",
66
- "@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
67
- "@prettier-ai/dsh-llm-retry": "^0.1.2-alpha.1",
68
- "@prettier-ai/dsh-client-ui-layout": "^0.1.2-alpha.1",
69
- "@prettier-ai/dsh-session": "^0.1.2-alpha.1",
70
- "@prettier-ai/dsh-settings": "^0.1.2-alpha.1",
71
- "@prettier-ai/dsh-session-stats": "^0.1.2-alpha.1",
72
- "@prettier-ai/dsh-tools": "^0.1.2-alpha.1",
73
- "@prettier-ai/dsh-token-meter": "^0.1.2-alpha.1",
74
- "@prettier-ai/dsh-util-workspace-path": "^0.1.2-alpha.1"
50
+ "@prettier-ai/cordis": "^4.0.2"
75
51
  },
76
52
  "devDependencies": {
77
53
  "@types/react": "~18.3.1",
54
+ "@types/react-dom": "~18.3.0",
78
55
  "react": "^18.2.0",
79
- "@prettier-ai/cordis": "^4.0.1",
80
- "@prettier-ai/dsh-agent": "^0.1.2-alpha.1",
81
- "@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.1",
82
- "@prettier-ai/dsh-api-session-controller": "^0.1.2-alpha.1",
83
- "@prettier-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1",
84
- "@prettier-ai/dsh-attachment": "^0.1.2-alpha.1",
85
- "@prettier-ai/dsh-client-locale": "^0.1.2-alpha.1",
86
- "@prettier-ai/dsh-client-ui-approval": "^0.1.2-alpha.1",
87
- "@prettier-ai/dsh-client-ui-primitives": "^0.1.2-alpha.1",
88
- "@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1",
89
- "@prettier-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1",
90
- "@prettier-ai/dsh-client-ui-layout": "^0.1.2-alpha.1",
91
- "@prettier-ai/dsh-client-ui-session": "^0.1.2-alpha.1",
92
- "@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.1",
93
- "@prettier-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1",
94
- "@prettier-ai/dsh-commands": "^0.1.2-alpha.1",
95
- "@prettier-ai/dsh-compaction": "^0.1.2-alpha.1",
96
- "@prettier-ai/dsh-client-ui-slots": "^0.1.2-alpha.1",
97
- "@prettier-ai/dsh-client-store": "^0.1.2-alpha.1",
98
- "@prettier-ai/dsh-llm": "^0.1.2-alpha.1",
99
- "@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
100
- "@prettier-ai/dsh-llm-retry": "^0.1.2-alpha.1",
101
- "@prettier-ai/dsh-session-stats": "^0.1.2-alpha.1",
102
- "@prettier-ai/dsh-settings": "^0.1.2-alpha.1",
103
- "@prettier-ai/dsh-session": "^0.1.2-alpha.1",
104
- "@prettier-ai/dsh-token-meter": "^0.1.2-alpha.1",
105
- "@prettier-ai/dsh-tools": "^0.1.2-alpha.1",
106
- "@prettier-ai/dsh-util-workspace-path": "^0.1.2-alpha.1"
56
+ "react-dom": "^18.2.0",
57
+ "@prettier-ai/cordis": "^4.0.2",
58
+ "@prettier-ai/dsh-agent": "^0.1.2-alpha.2",
59
+ "@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.2",
60
+ "@prettier-ai/dsh-api-workspace-controller": "^0.1.2-alpha.2",
61
+ "@prettier-ai/dsh-api-session-controller": "^0.1.2-alpha.2",
62
+ "@prettier-ai/dsh-attachment": "^0.1.2-alpha.2",
63
+ "@prettier-ai/dsh-client-locale": "^0.1.2-alpha.2",
64
+ "@prettier-ai/dsh-client-store": "^0.1.2-alpha.2",
65
+ "@prettier-ai/dsh-client-ui-approval": "^0.1.2-alpha.2",
66
+ "@prettier-ai/dsh-client-ui-conversation": "^0.1.2-alpha.2",
67
+ "@prettier-ai/dsh-client-ui-layout": "^0.1.2-alpha.2",
68
+ "@prettier-ai/dsh-client-ui-primitives": "^0.1.2-alpha.2",
69
+ "@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.2",
70
+ "@prettier-ai/dsh-client-ui-session": "^0.1.2-alpha.2",
71
+ "@prettier-ai/dsh-client-ui-workspace": "^0.1.2-alpha.2",
72
+ "@prettier-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
73
+ "@prettier-ai/dsh-commands": "^0.1.2-alpha.2",
74
+ "@prettier-ai/dsh-compaction": "^0.1.2-alpha.2",
75
+ "@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
76
+ "@prettier-ai/dsh-llm": "^0.1.2-alpha.2",
77
+ "@prettier-ai/dsh-session": "^0.1.2-alpha.2",
78
+ "@prettier-ai/dsh-token-meter": "^0.1.2-alpha.2",
79
+ "@prettier-ai/dsh-llm-retry": "^0.1.2-alpha.2",
80
+ "@prettier-ai/dsh-invariants": "^0.1.2-alpha.2",
81
+ "@prettier-ai/dsh-session-stats": "^0.1.2-alpha.2",
82
+ "@prettier-ai/dsh-util-workspace-path": "^0.1.2-alpha.2",
83
+ "@prettier-ai/dsh-settings": "^0.1.2-alpha.2",
84
+ "@prettier-ai/dsh-tools": "^0.1.2-alpha.2"
107
85
  },
108
86
  "dependencies": {
109
- "@prettier-ai/schemastery": "^3.18.1"
87
+ "@prettier-ai/schemastery": "^3.18.2"
110
88
  },
111
89
  "files": [
112
90
  "lib/index.js",
@@ -1,9 +0,0 @@
1
- import type { TurnTokenUsage } from '../contract/chat-nodes.ts';
2
- import type { ChatViewSlotProps } from '../contract/slots.ts';
3
- export interface TurnUsageDisclosureProps {
4
- usage: TurnTokenUsage;
5
- t: ChatViewSlotProps['t'];
6
- }
7
- /** Compact per-Turn usage summary with an opt-in bucket breakdown. */
8
- export declare function TurnUsageDisclosure({ usage, t }: TurnUsageDisclosureProps): import("react").JSX.Element;
9
- //# sourceMappingURL=TurnUsageDisclosure.d.ts.map