@khorsheed/dsh-client-message-tools 0.1.0 → 0.3.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.en.md +16 -12
  3. package/README.i18n.yaml +2 -2
  4. package/README.md +35 -31
  5. package/lib/client.js +295 -135
  6. package/lib/index.js +63 -67
  7. package/lib/typert.host.js +3 -3
  8. package/lib/typert.remote-client.js +3 -3
  9. package/lib/types/client/RestoredMessageView.d.ts +1 -1
  10. package/lib/types/client/RestoredMessageView.js +20 -1
  11. package/lib/types/client/UserMessageView.d.ts +1 -1
  12. package/lib/types/client/UserMessageView.js +16 -10
  13. package/lib/types/client/WithdrawnDividerView.d.ts +1 -1
  14. package/lib/types/client/WithdrawnDividerView.js +13 -9
  15. package/lib/types/client/chat-hook.d.ts +36 -0
  16. package/lib/types/client/chat-hook.js +51 -0
  17. package/lib/types/client/dom-hider.d.ts +3 -2
  18. package/lib/types/client/dom-hider.js +20 -13
  19. package/lib/types/client/edit-in-place.d.ts +2 -1
  20. package/lib/types/client/index.d.ts +6 -4
  21. package/lib/types/client/index.js +42 -8
  22. package/lib/types/client/locales.d.ts +8 -0
  23. package/lib/types/client/locales.js +8 -0
  24. package/lib/types/client/withdrawn-node.d.ts +3 -2
  25. package/lib/types/client/withdrawn-node.js +45 -54
  26. package/lib/types/index.js +10 -8
  27. package/lib/types/marker.d.ts +2 -2
  28. package/lib/types/withdraw.js +76 -80
  29. package/package.json +19 -18
  30. package/lib/client.js.map +0 -1
  31. package/lib/tsconfig.client.tsbuildinfo +0 -1
  32. package/lib/tsconfig.host.tsbuildinfo +0 -1
  33. package/lib/typert.remote-client.d.ts.map +0 -1
  34. package/lib/types/client/ModelChip.d.ts.map +0 -1
  35. package/lib/types/client/RestoredMessageView.d.ts.map +0 -1
  36. package/lib/types/client/UserMessageView.d.ts.map +0 -1
  37. package/lib/types/client/WithdrawnDividerView.d.ts.map +0 -1
  38. package/lib/types/client/backfill.d.ts.map +0 -1
  39. package/lib/types/client/dom-hider.d.ts.map +0 -1
  40. package/lib/types/client/edit-in-place.d.ts.map +0 -1
  41. package/lib/types/client/icons.d.ts.map +0 -1
  42. package/lib/types/client/index.d.ts.map +0 -1
  43. package/lib/types/client/locales.d.ts.map +0 -1
  44. package/lib/types/client/slots.d.ts.map +0 -1
  45. package/lib/types/client/withdraw-backfill.d.ts.map +0 -1
  46. package/lib/types/client/withdrawn-node.d.ts.map +0 -1
  47. package/lib/types/index.d.ts.map +0 -1
  48. package/lib/types/invariant.d.ts.map +0 -1
  49. package/lib/types/marker.d.ts.map +0 -1
  50. package/lib/types/types.d.ts.map +0 -1
  51. package/lib/types/withdraw.d.ts.map +0 -1
package/lib/client.js CHANGED
@@ -5,7 +5,6 @@ window.__ModuleLoader__.load({
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
- let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
9
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
9
  let react_jsx_runtime = require("react/jsx-runtime");
11
10
  //#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
@@ -4142,7 +4141,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4142
4141
  },
4143
4142
  sourceLocation: {
4144
4143
  "file": "packages/message-tools/src/index.ts",
4145
- "line": 133,
4144
+ "line": 135,
4146
4145
  "column": 9
4147
4146
  }
4148
4147
  },
@@ -4169,7 +4168,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4169
4168
  },
4170
4169
  sourceLocation: {
4171
4170
  "file": "packages/message-tools/src/index.ts",
4172
- "line": 95,
4171
+ "line": 97,
4173
4172
  "column": 9
4174
4173
  }
4175
4174
  },
@@ -4196,25 +4195,85 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4196
4195
  },
4197
4196
  sourceLocation: {
4198
4197
  "file": "packages/message-tools/src/index.ts",
4199
- "line": 61,
4198
+ "line": 63,
4200
4199
  "column": 9
4201
4200
  }
4202
4201
  }
4203
4202
  ]
4204
4203
  };
4205
4204
  //#endregion
4206
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-session@0.1.1-rc.2_a4e4bb24a1f3580ac25e11cfa3c6b8cc/node_modules/@deepseek-ai/dsh-session/lib/types/surface.js
4205
+ //#region src/client/chat-hook.ts
4207
4206
  /**
4208
- * Surface layer on top of the session event log: an ordered view of events
4209
- * that produce LLM messages. The append-only log remains the source of truth.
4210
- *
4211
- * Browser-safe: web clients consume this subpath export, so it must stay free
4212
- * of `node:` imports (they break the vite bundle).
4213
- *
4214
- * @module @deepseek-ai/dsh-session/surface
4207
+ * Frozen empty chat snapshot for the degraded render path: a composition
4208
+ * without ui-chat never mounts the `conversation.chat.node` slot at all, so
4209
+ * this only guards a stray render — hook call order stays stable and every
4210
+ * selector reads the empty state.
4215
4211
  */
4212
+ const EMPTY_CHAT = {
4213
+ order: [],
4214
+ nodes: {
4215
+ get: () => void 0,
4216
+ source: () => ({
4217
+ getSnapshot: () => void 0,
4218
+ subscribe: () => () => {}
4219
+ }),
4220
+ processSource: () => ({
4221
+ getSnapshot: () => void 0,
4222
+ subscribe: () => () => {}
4223
+ }),
4224
+ values: () => []
4225
+ },
4226
+ locations: {
4227
+ getTurn: () => [],
4228
+ getStep: () => []
4229
+ },
4230
+ navigation: { items: () => [] },
4231
+ timeline: {
4232
+ turnOrder: [],
4233
+ turns: /* @__PURE__ */ new Map()
4234
+ },
4235
+ legacy: {
4236
+ nodes: [],
4237
+ turnTimings: /* @__PURE__ */ new Map(),
4238
+ turnEnds: /* @__PURE__ */ new Map(),
4239
+ partial: null,
4240
+ runningCalls: []
4241
+ }
4242
+ };
4243
+ /**
4244
+ * Pick the live chat hook from the entry's composed props (`useChat` is a
4245
+ * session standard prop on 0.1.2), degrading to the frozen empty snapshot
4246
+ * when the framework composed none.
4247
+ * @param props - the entry's composed props.
4248
+ * @returns the chat selector hook.
4249
+ */
4250
+ function chatHookOf(props) {
4251
+ const direct = props.useChat;
4252
+ if (direct !== void 0) return direct;
4253
+ return (selector) => selector(EMPTY_CHAT);
4254
+ }
4255
+ /**
4256
+ * Resolve the apply-world chat source for one session.
4257
+ * @param ctx - client root context.
4258
+ * @param sessionId - owning session.
4259
+ * @returns the conversation binding's chat target (its snapshot is undefined
4260
+ * until the first subscription activates the view), or undefined when the
4261
+ * uiConversation service or the session binding is absent.
4262
+ */
4263
+ function chatSourceOf(ctx, sessionId) {
4264
+ const uiConversation = ctx.get("uiConversation");
4265
+ if (uiConversation === void 0) return void 0;
4266
+ try {
4267
+ return uiConversation.binding(sessionId).target("chat");
4268
+ } catch {
4269
+ return;
4270
+ }
4271
+ }
4272
+ //#endregion
4273
+ //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-session@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-scope@0._19a3f679910325ead5ac78e2fac662f7/node_modules/@deepseek-ai/dsh-session/lib/types/surface.js
4216
4274
  /** Runtime counterpart of the message-producing event union. */
4217
4275
  const SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
4276
+ "system/message",
4218
4277
  "user/message",
4219
4278
  "assistant/message",
4220
4279
  "tool/result"
@@ -4302,6 +4361,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4302
4361
  //#endregion
4303
4362
  //#region src/client/withdrawn-node.ts
4304
4363
  /**
4364
+ * Materialize one final chat-target node. The Definition contract's
4365
+ * buildViewNode returns the ConversationViewNode base (key/kind/id/target/
4366
+ * data), so the chat extras (anchorSeq/location/visibility) go through this
4367
+ * explicitly typed builder instead of an excess-checked literal.
4368
+ */
4369
+ function chatViewNode(context, kind, anchorSeq, data) {
4370
+ return {
4371
+ key: context.key,
4372
+ kind,
4373
+ id: context.id,
4374
+ target: "chat",
4375
+ anchorSeq,
4376
+ location: context.start?.location ?? { kind: "unresolved" },
4377
+ visibility: "visible",
4378
+ data
4379
+ };
4380
+ }
4381
+ /**
4305
4382
  * The withdrawal divider Definition: one single-event Context per
4306
4383
  * message-tools replacement event, anchored at the replacement's seq so the
4307
4384
  * divider sits exactly where the span was hidden.
@@ -4309,29 +4386,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4309
4386
  const withdrawnDividerDefinition = {
4310
4387
  kind: "message-tools-withdrawn",
4311
4388
  target: "chat",
4312
- match: (event) => isMessageToolsReplacement(event) ? {
4313
- id: String(event.seq),
4314
- role: "start"
4315
- } : null,
4389
+ match: (event) => {
4390
+ if (event.type !== "user/message") return null;
4391
+ return isMessageToolsReplacement(event) ? {
4392
+ id: String(event.seq),
4393
+ role: "start"
4394
+ } : null;
4395
+ },
4316
4396
  start: (_context, match) => {
4317
4397
  const event = match.event;
4318
4398
  if (!isMessageToolsReplacement(event)) throw new Error("message-tools-withdrawn start requires a message-tools replacement event");
4319
4399
  return {
4320
4400
  seq: event.seq,
4321
- hiddenStartSeq: event.surfaceOp.start
4401
+ hiddenStartSeq: event.surfaceOp.startSeq
4322
4402
  };
4323
4403
  },
4324
4404
  update: (context) => context.state,
4325
- buildViewNode: (context) => context.state === void 0 ? null : {
4326
- key: context.key,
4327
- kind: "message-tools-withdrawn",
4328
- id: context.id,
4329
- target: "chat",
4330
- anchorSeq: context.state.seq,
4331
- location: context.start?.location ?? { kind: "unresolved" },
4332
- visibility: "visible",
4333
- data: context.state
4334
- }
4405
+ buildViewNode: (context) => context.state === void 0 ? null : chatViewNode(context, "message-tools-withdrawn", context.state.seq, context.state)
4335
4406
  };
4336
4407
  /**
4337
4408
  * The edited-bubble Definition: one single-event Context per message-tools
@@ -4342,10 +4413,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4342
4413
  const editedMessageDefinition = {
4343
4414
  kind: "message-tools-edited",
4344
4415
  target: "chat",
4345
- match: (event) => isMessageToolsEdit(event) ? {
4346
- id: String(event.seq),
4347
- role: "start"
4348
- } : null,
4416
+ match: (event) => {
4417
+ if (event.type !== "user/message") return null;
4418
+ return isMessageToolsEdit(event) ? {
4419
+ id: String(event.seq),
4420
+ role: "start"
4421
+ } : null;
4422
+ },
4349
4423
  start: (_context, match) => {
4350
4424
  const event = match.event;
4351
4425
  if (!isMessageToolsEdit(event)) throw new Error("message-tools-edited start requires a message-tools edit replacement event");
@@ -4353,20 +4427,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4353
4427
  seq: event.seq,
4354
4428
  time: event.time,
4355
4429
  content: event.data.content,
4356
- hiddenStartSeq: event.surfaceOp.start
4430
+ hiddenStartSeq: event.surfaceOp.startSeq
4357
4431
  };
4358
4432
  },
4359
4433
  update: (context) => context.state,
4360
- buildViewNode: (context) => context.state === void 0 ? null : {
4361
- key: context.key,
4362
- kind: "message-tools-edited",
4363
- id: context.id,
4364
- target: "chat",
4365
- anchorSeq: context.state.seq,
4366
- location: context.start?.location ?? { kind: "unresolved" },
4367
- visibility: "visible",
4368
- data: context.state
4369
- }
4434
+ buildViewNode: (context) => context.state === void 0 ? null : chatViewNode(context, "message-tools-edited", context.state.seq, context.state)
4370
4435
  };
4371
4436
  /**
4372
4437
  * Fold the hidden seq spans out of the live chat nodes, flattened to
@@ -4405,10 +4470,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4405
4470
  const restoredMessageDefinition = {
4406
4471
  kind: "message-tools-restored",
4407
4472
  target: "chat",
4408
- match: (event) => isMessageToolsRestore(event) ? {
4409
- id: String(event.seq),
4410
- role: "start"
4411
- } : null,
4473
+ match: (event) => {
4474
+ if (event.type !== "user/message") return null;
4475
+ return isMessageToolsRestore(event) ? {
4476
+ id: String(event.seq),
4477
+ role: "start"
4478
+ } : null;
4479
+ },
4412
4480
  start: (_context, match) => {
4413
4481
  const event = match.event;
4414
4482
  if (!isMessageToolsRestore(event)) throw new Error("message-tools-restored start requires a message-tools restore event");
@@ -4421,16 +4489,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4421
4489
  };
4422
4490
  },
4423
4491
  update: (context) => context.state,
4424
- buildViewNode: (context) => context.state === void 0 ? null : {
4425
- key: context.key,
4426
- kind: "message-tools-restored",
4427
- id: context.id,
4428
- target: "chat",
4429
- anchorSeq: context.state.seq,
4430
- location: context.start?.location ?? { kind: "unresolved" },
4431
- visibility: "visible",
4432
- data: context.state
4433
- }
4492
+ buildViewNode: (context) => context.state === void 0 ? null : chatViewNode(context, "message-tools-restored", context.state.seq, context.state)
4434
4493
  };
4435
4494
  /**
4436
4495
  * The restore-assistant-line Definition: one single-event Context per
@@ -4442,10 +4501,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4442
4501
  const restoredAssistantMessageDefinition = {
4443
4502
  kind: "message-tools-restored-assistant",
4444
4503
  target: "chat",
4445
- match: (event) => isMessageToolsRestoreAssistant(event) ? {
4446
- id: String(event.seq),
4447
- role: "start"
4448
- } : null,
4504
+ match: (event) => {
4505
+ if (event.type !== "user/message") return null;
4506
+ return isMessageToolsRestoreAssistant(event) ? {
4507
+ id: String(event.seq),
4508
+ role: "start"
4509
+ } : null;
4510
+ },
4449
4511
  start: (_context, match) => {
4450
4512
  const event = match.event;
4451
4513
  if (!isMessageToolsRestoreAssistant(event)) throw new Error("message-tools-restored-assistant start requires a restore-assistant replay event");
@@ -4457,16 +4519,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4457
4519
  };
4458
4520
  },
4459
4521
  update: (context) => context.state,
4460
- buildViewNode: (context) => context.state === void 0 ? null : {
4461
- key: context.key,
4462
- kind: "message-tools-restored-assistant",
4463
- id: context.id,
4464
- target: "chat",
4465
- anchorSeq: context.state.seq,
4466
- location: context.start?.location ?? { kind: "unresolved" },
4467
- visibility: "visible",
4468
- data: context.state
4469
- }
4522
+ buildViewNode: (context) => context.state === void 0 ? null : chatViewNode(context, "message-tools-restored-assistant", context.state.seq, context.state)
4470
4523
  };
4471
4524
  /**
4472
4525
  * Count the user-visible messages hidden by one withdrawn span. This mirrors
@@ -4638,9 +4691,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4638
4691
  let retryObserver;
4639
4692
  let retryTimer;
4640
4693
  let latest;
4641
- const applyRules = (snapshot) => {
4642
- latest = snapshot;
4643
- const nodes = snapshot.chat.nodes.values();
4694
+ const applyRules = (chat) => {
4695
+ latest = chat;
4696
+ const nodes = chat.nodes.values();
4644
4697
  const ranges = foldHiddenRanges(nodes);
4645
4698
  const nextSignature = `${ranges.join(",")}:${nodes.length}`;
4646
4699
  if (nextSignature === signature) return;
@@ -4699,14 +4752,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4699
4752
  else failProbe();
4700
4753
  }, probeRetryWindowMs);
4701
4754
  };
4702
- const onSnapshot = (snapshot) => {
4703
- latest = snapshot;
4755
+ const onSnapshot = (chat) => {
4756
+ latest = chat;
4704
4757
  if (disabled) return;
4705
4758
  if (probed) {
4706
- applyRules(snapshot);
4759
+ applyRules(chat);
4707
4760
  return;
4708
4761
  }
4709
- if (hiddenFlowKeys(snapshot.chat.nodes.values()).length === 0) return;
4762
+ if (hiddenFlowKeys(chat.nodes.values()).length === 0) return;
4710
4763
  if (probeScheduled || retrying) return;
4711
4764
  probeScheduled = true;
4712
4765
  nextFrame(() => {
@@ -4727,19 +4780,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4727
4780
  signature = "";
4728
4781
  style.textContent = "";
4729
4782
  if (current === void 0) return;
4730
- const binding = ctx.sessions.binding(current);
4731
- if (binding === void 0) return;
4732
- onSnapshot(binding.session.getSnapshot());
4733
- stopSession = binding.session.subscribe(() => {
4734
- onSnapshot(binding.session.getSnapshot());
4735
- });
4783
+ const source = chatSourceOf(ctx, current);
4784
+ if (source === void 0) return;
4785
+ const publish = () => {
4786
+ const chat = source.getSnapshot();
4787
+ if (chat !== void 0) onSnapshot(chat);
4788
+ };
4789
+ publish();
4790
+ stopSession = source.subscribe(publish);
4736
4791
  };
4737
4792
  const stopList = ctx.sessions.list.subscribe(bindCurrent);
4738
- const stopProvide = ctx.sessions.currentProvideInfo.subscribe(bindCurrent);
4739
4793
  bindCurrent();
4740
4794
  return () => {
4741
4795
  stopList();
4742
- stopProvide();
4743
4796
  stopSession?.();
4744
4797
  stopRetry();
4745
4798
  style.remove();
@@ -4888,6 +4941,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4888
4941
  "model.empty": "暂无可用模型",
4889
4942
  "model.selectFailed": "模型切换失败",
4890
4943
  "message.extraBlock": "附加内容块",
4944
+ "close": "关闭",
4945
+ "markdown.copy": "复制",
4946
+ "markdown.copied": "已复制",
4947
+ "markdown.footnotes": "脚注",
4891
4948
  "json.truncated": "… 已截断,共 {total} 字符"
4892
4949
  };
4893
4950
  /** English dictionary, checked complete against the zh key set. */
@@ -4920,9 +4977,45 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4920
4977
  "model.empty": "No models available",
4921
4978
  "model.selectFailed": "Could not switch model",
4922
4979
  "message.extraBlock": "Extra content block",
4980
+ "close": "Close",
4981
+ "markdown.copy": "Copy",
4982
+ "markdown.copied": "Copied",
4983
+ "markdown.footnotes": "Footnotes",
4923
4984
  "json.truncated": "… truncated, {total} characters total"
4924
4985
  };
4925
4986
  //#endregion
4987
+ //#region ../../node_modules/.pnpm/zustand@4.4.7_@types+react@18.3.31_immer@10.2.0_react@18.3.1/node_modules/zustand/esm/shallow.mjs
4988
+ function shallow$1(objA, objB) {
4989
+ if (Object.is(objA, objB)) return true;
4990
+ if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) return false;
4991
+ if (objA instanceof Map && objB instanceof Map) {
4992
+ if (objA.size !== objB.size) return false;
4993
+ for (const [key, value] of objA) if (!Object.is(value, objB.get(key))) return false;
4994
+ return true;
4995
+ }
4996
+ if (objA instanceof Set && objB instanceof Set) {
4997
+ if (objA.size !== objB.size) return false;
4998
+ for (const value of objA) if (!objB.has(value)) return false;
4999
+ return true;
5000
+ }
5001
+ const keysA = Object.keys(objA);
5002
+ if (keysA.length !== Object.keys(objB).length) return false;
5003
+ for (let i = 0; i < keysA.length; i++) if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) return false;
5004
+ return true;
5005
+ }
5006
+ //#endregion
5007
+ //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-store@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-store/lib/index.js
5008
+ /**
5009
+ * Shallow equality for selector slices (zustand/shallow semantics; travels
5010
+ * with the engine so hook consumers need no zustand dependency).
5011
+ * @param a - left value.
5012
+ * @param b - right value.
5013
+ * @returns whether the values are shallowly equal.
5014
+ */
5015
+ function shallowEqual(a, b) {
5016
+ return shallow$1(a, b);
5017
+ }
5018
+ //#endregion
4926
5019
  //#region src/client/icons.tsx
4927
5020
  /**
4928
5021
  * Undo arrow (withdraw/restore), rotate-ccw style: a near-full circle arc
@@ -4964,7 +5057,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4964
5057
  if (text !== "") steps.backfill(text);
4965
5058
  }
4966
5059
  //#endregion
4967
- //#region \0dsh-css:/Users/zhuyudan/code/dsh-plugins/packages/message-tools/src/client/ModelChip.module.css.mjs
5060
+ //#region \0dsh-css:src/client/ModelChip.module.css.mjs
4968
5061
  const css$3 = ".he-h5G_chip{align-items:center;gap:6px;display:flex;position:relative}.he-h5G_trigger{height:28px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary));cursor:pointer;background:0 0;border:none;border-radius:24px;align-items:center;gap:4px;padding:0 8px;font-size:13px;font-weight:500;display:inline-flex}.he-h5G_trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-1))}.he-h5G_trigger:disabled{cursor:default;opacity:.6}.he-h5G_triggerEffort{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed))}.he-h5G_menu{z-index:20;border:1px solid var(--dsw-alias-border-l1,var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed)));background:var(--dsw-alias-bg-base,var(--dsw-alias-bg-layer-1));min-width:220px;max-height:320px;box-shadow:0 4px 16px var(--dsw-alias-shadow,var(--dsw-alias-border-l2,#0000001f));border-radius:8px;padding:4px;position:absolute;bottom:calc(100% + 4px);left:0;overflow-y:auto}.he-h5G_group+.he-h5G_group{margin-top:4px}.he-h5G_groupTitle{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));padding:4px 8px;font-size:12px;line-height:18px}.he-h5G_option{width:100%;color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));text-align:left;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;font-size:13px;line-height:20px;display:flex}.he-h5G_option:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-1))}.he-h5G_option:disabled{cursor:default;opacity:.6}.he-h5G_check{width:16px;color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));flex:none;display:inline-flex}.he-h5G_empty{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));padding:8px;font-size:13px}.he-h5G_selectFailed{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary));white-space:nowrap;font-size:12px}";
4969
5062
  const tagId$3 = "@khorsheed/dsh-client-message-tools/ModelChip.module.css";
4970
5063
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
@@ -4976,15 +5069,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4976
5069
  }
4977
5070
  var ModelChip_module_css_default = {
4978
5071
  "triggerEffort": "he-h5G_triggerEffort",
4979
- "groupTitle": "he-h5G_groupTitle",
4980
- "chip": "he-h5G_chip",
4981
- "check": "he-h5G_check",
4982
- "trigger": "he-h5G_trigger",
4983
5072
  "option": "he-h5G_option",
4984
5073
  "empty": "he-h5G_empty",
4985
5074
  "selectFailed": "he-h5G_selectFailed",
5075
+ "groupTitle": "he-h5G_groupTitle",
5076
+ "chip": "he-h5G_chip",
5077
+ "trigger": "he-h5G_trigger",
5078
+ "menu": "he-h5G_menu",
4986
5079
  "group": "he-h5G_group",
4987
- "menu": "he-h5G_menu"
5080
+ "check": "he-h5G_check"
4988
5081
  };
4989
5082
  //#endregion
4990
5083
  //#region src/client/ModelChip.tsx
@@ -5156,8 +5249,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5156
5249
  });
5157
5250
  }
5158
5251
  //#endregion
5159
- //#region \0dsh-css:/Users/zhuyudan/code/dsh-plugins/packages/message-tools/src/client/UserMessageView.module.css.mjs
5160
- const css$2 = ".kCWYAa_userRow{flex-direction:column;align-items:flex-end;gap:6px;display:flex}.kCWYAa_userStack{flex-direction:column;align-items:flex-end;gap:8px;min-width:0;max-width:min(525px,82%);display:flex}.kCWYAa_bubble{background:var(--dsw-specific-bubble,var(--dsw-alias-bg-layer-1));max-width:100%;color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));border-radius:22px;padding:10px 16px;font-size:16px;line-height:24px}.kCWYAa_refChip{color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));white-space:nowrap;vertical-align:baseline;background:#6187d838;border-radius:6px;margin:0 2px;padding:0 8px;font-size:.85em;line-height:1.6;display:inline-block}.kCWYAa_actions{align-items:center;gap:10px;height:28px;display:flex}.kCWYAa_action{width:28px;height:28px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.kCWYAa_action:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary))}.kCWYAa_editor{border:1px solid var(--dsw-alias-border-l1,var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed)));background:var(--dsw-alias-bg-base,var(--dsw-alias-bg-layer-1));border-radius:8px;flex-direction:column;gap:8px;width:100%;padding:12px;display:flex}.kCWYAa_editorInput{border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1,var(--dsw-alias-label-primary-dimmed)));resize:none;width:100%;max-height:240px;color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));font:inherit;background:0 0;border-radius:6px;outline:none;padding:8px;overflow-y:auto}.kCWYAa_editorActions{justify-content:flex-end;align-items:center;gap:8px;display:flex}.kCWYAa_editorTrailing{align-items:center;margin-right:auto;display:flex}.kCWYAa_actionError{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary));align-self:flex-end;padding:2px 4px;font-size:13px;line-height:20px}.kCWYAa_editedLabel{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));align-self:flex-end;font-size:12px;line-height:18px}";
5252
+ //#region \0dsh-css:src/client/UserMessageView.module.css.mjs
5253
+ const css$2 = ".kCWYAa_userRow{flex-direction:column;align-items:flex-end;gap:6px;display:flex}.kCWYAa_userStack{flex-direction:column;align-items:flex-end;gap:8px;min-width:0;max-width:min(525px,82%);display:flex}.kCWYAa_bubble{background:var(--dsw-specific-bubble,var(--dsw-alias-bg-layer-1));max-width:100%;color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));border-radius:22px;padding:10px 16px;font-size:16px;line-height:24px}.kCWYAa_textRun{white-space:pre-wrap;word-break:break-word;font-size:inherit;line-height:inherit}.kCWYAa_refChip{color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));white-space:nowrap;vertical-align:baseline;background:#6187d838;border-radius:6px;margin:0 2px;padding:0 8px;font-size:.85em;line-height:1.6;display:inline-block}.kCWYAa_actions{align-items:center;gap:10px;height:28px;display:flex}.kCWYAa_action{width:28px;height:28px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.kCWYAa_action:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary))}.kCWYAa_editor{border:1px solid var(--dsw-alias-border-l1,var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed)));background:var(--dsw-alias-bg-base,var(--dsw-alias-bg-layer-1));border-radius:8px;flex-direction:column;gap:8px;width:100%;padding:12px;display:flex}.kCWYAa_editorInput{border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1,var(--dsw-alias-label-primary-dimmed)));resize:none;width:100%;max-height:240px;color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));font:inherit;background:0 0;border-radius:6px;outline:none;padding:8px;overflow-y:auto}.kCWYAa_editorActions{justify-content:flex-end;align-items:center;gap:8px;display:flex}.kCWYAa_editorTrailing{align-items:center;margin-right:auto;display:flex}.kCWYAa_actionError{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary));align-self:flex-end;padding:2px 4px;font-size:13px;line-height:20px}.kCWYAa_editedLabel{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));align-self:flex-end;font-size:12px;line-height:18px}";
5161
5254
  const tagId$2 = "@khorsheed/dsh-client-message-tools/UserMessageView.module.css";
5162
5255
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
5163
5256
  const tag = document.createElement("style");
@@ -5167,17 +5260,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5167
5260
  document.head.appendChild(tag);
5168
5261
  }
5169
5262
  var UserMessageView_module_css_default = {
5170
- "actions": "kCWYAa_actions",
5263
+ "actionError": "kCWYAa_actionError",
5264
+ "textRun": "kCWYAa_textRun",
5265
+ "userStack": "kCWYAa_userStack",
5266
+ "action": "kCWYAa_action",
5171
5267
  "bubble": "kCWYAa_bubble",
5172
- "refChip": "kCWYAa_refChip",
5173
- "editorActions": "kCWYAa_editorActions",
5268
+ "editorInput": "kCWYAa_editorInput",
5174
5269
  "editorTrailing": "kCWYAa_editorTrailing",
5175
5270
  "editedLabel": "kCWYAa_editedLabel",
5176
- "userStack": "kCWYAa_userStack",
5177
5271
  "userRow": "kCWYAa_userRow",
5178
- "action": "kCWYAa_action",
5179
- "editorInput": "kCWYAa_editorInput",
5180
- "actionError": "kCWYAa_actionError",
5272
+ "editorActions": "kCWYAa_editorActions",
5273
+ "actions": "kCWYAa_actions",
5274
+ "refChip": "kCWYAa_refChip",
5181
5275
  "editor": "kCWYAa_editor"
5182
5276
  };
5183
5277
  //#endregion
@@ -5194,8 +5288,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5194
5288
  * marks the spot.
5195
5289
  */
5196
5290
  /** Selector identity kept module-level so uSES memoization holds. */
5197
- function selectHiddenRanges(snapshot) {
5198
- return foldHiddenRanges(snapshot.chat.nodes.values());
5291
+ function selectHiddenRanges(chat) {
5292
+ return foldHiddenRanges(chat.nodes.values());
5199
5293
  }
5200
5294
  /** Split user content into joined text, images, and leftover blocks. */
5201
5295
  function contentParts(content) {
@@ -5225,7 +5319,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5225
5319
  const tokenStart = m.index + (m[1]?.length ?? 0);
5226
5320
  /* v8 ignore next -- the token group always participates in a match, so m[2] is never undefined. */
5227
5321
  const label = m[2] ?? "";
5228
- if (tokenStart > cursor) parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MessageText, { text: text.slice(cursor, tokenStart) }, cursor));
5322
+ if (tokenStart > cursor) parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5323
+ className: UserMessageView_module_css_default.textRun,
5324
+ children: text.slice(cursor, tokenStart)
5325
+ }, cursor));
5229
5326
  parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5230
5327
  className: UserMessageView_module_css_default.refChip,
5231
5328
  "data-ref-chip": label.startsWith("@") ? "subagent" : "skill",
@@ -5233,8 +5330,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5233
5330
  }, tokenStart));
5234
5331
  cursor = tokenStart + label.length;
5235
5332
  }
5236
- if (parts.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MessageText, { text });
5237
- if (cursor < text.length) parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MessageText, { text: text.slice(cursor) }, cursor));
5333
+ if (parts.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5334
+ className: UserMessageView_module_css_default.textRun,
5335
+ children: text
5336
+ });
5337
+ if (cursor < text.length) parts.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5338
+ className: UserMessageView_module_css_default.textRun,
5339
+ children: text.slice(cursor)
5340
+ }, cursor));
5238
5341
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: parts });
5239
5342
  }
5240
5343
  /** Copy action: the copy icon swaps to a short-lived check after a successful write. */
@@ -5340,8 +5443,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5340
5443
  });
5341
5444
  }
5342
5445
  /** The shadowed user-message view: bubble plus copy/edit/withdraw actions. */
5343
- const UserMessageView = (0, react.memo)(function UserMessageView({ node, renderMessageImages, t, useSession, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel }) {
5344
- const ranges = useSession(selectHiddenRanges, _deepseek_ai_dsh_client_runtime_client.shallowEqual);
5446
+ const UserMessageView = (0, react.memo)(function UserMessageView({ node, renderMessageImages, t, editMessage, withdrawMessage, backfillDraft, useModelDirectory, modelsAvailable, loadModels, selectModel, ...standard }) {
5447
+ const ranges = chatHookOf(standard)(selectHiddenRanges, shallowEqual);
5345
5448
  const [editing, setEditing] = (0, react.useState)(false);
5346
5449
  const [confirming, setConfirming] = (0, react.useState)(false);
5347
5450
  const [acknowledged, setAcknowledged] = (0, react.useState)(false);
@@ -5480,6 +5583,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5480
5583
  description: t("withdraw.description"),
5481
5584
  acknowledgeLabel: t("withdraw.acknowledge"),
5482
5585
  cancelLabel: t("cancel"),
5586
+ closeLabel: t("close"),
5483
5587
  confirmLabel: t("withdraw.confirm"),
5484
5588
  acknowledged,
5485
5589
  disabled: busy,
@@ -5494,7 +5598,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5494
5598
  });
5495
5599
  });
5496
5600
  //#endregion
5497
- //#region \0dsh-css:/Users/zhuyudan/code/dsh-plugins/packages/message-tools/src/client/RestoredMessageView.module.css.mjs
5601
+ //#region \0dsh-css:src/client/RestoredMessageView.module.css.mjs
5498
5602
  const css$1 = ".hDMXAG_restoredLabel{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));align-items:center;gap:4px;font-size:12px;line-height:18px;display:inline-flex}.hDMXAG_restoredAssistantRow{color:var(--dsw-alias-label-primary,var(--dsw-alias-label-secondary));flex-direction:column;align-items:flex-start;gap:4px;display:flex}";
5499
5603
  const tagId$1 = "@khorsheed/dsh-client-message-tools/RestoredMessageView.module.css";
5500
5604
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
@@ -5510,20 +5614,44 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5510
5614
  };
5511
5615
  //#endregion
5512
5616
  //#region src/client/RestoredMessageView.tsx
5617
+ /**
5618
+ * Restored-assistant line ('message-tools-restored-assistant' keyed
5619
+ * renderer): one withdrawn assistant reply's text replayed at the
5620
+ * conversation tail by a restore. The body renders through the official
5621
+ * `MarkdownText` (the same public renderer behind ui-conversation's
5622
+ * AssistantMarkdown), so typography, code blocks, and lists match a native
5623
+ * assistant reply; the「已恢复 · 助手回复」caption stays as a small tertiary
5624
+ * marker. The model-facing frame text is not displayed (stripped in the
5625
+ * Definition). No action row: the group's user bubbles already carry copy.
5626
+ */
5627
+ /** Localized Markdown chrome (code-fence copy buttons, footnotes) over the message-tools namespace. */
5628
+ function markdownLabels(t) {
5629
+ return {
5630
+ code: {
5631
+ copyLabel: t("markdown.copy"),
5632
+ copiedLabel: t("markdown.copied")
5633
+ },
5634
+ footnotes: t("markdown.footnotes")
5635
+ };
5636
+ }
5513
5637
  /** The restored assistant line: caption plus the reply body in official markdown chrome. */
5514
5638
  function RestoredMessageView({ node, t }) {
5515
5639
  const data = node.data;
5640
+ const labels = (0, react.useMemo)(() => markdownLabels(t), [t]);
5516
5641
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5517
5642
  className: RestoredMessageView_module_css_default.restoredAssistantRow,
5518
5643
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5519
5644
  className: RestoredMessageView_module_css_default.restoredLabel,
5520
5645
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconUndoOutline16, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("restored.assistant") })]
5521
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, { text: data.text })]
5646
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
5647
+ text: data.text,
5648
+ labels
5649
+ })]
5522
5650
  });
5523
5651
  }
5524
5652
  //#endregion
5525
- //#region \0dsh-css:/Users/zhuyudan/code/dsh-plugins/packages/message-tools/src/client/WithdrawnDividerView.module.css.mjs
5526
- const css = ".JtQ9hG_row{padding:2px 0}.JtQ9hG_marker{width:100%;min-width:0;height:24px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:6px;align-items:center;padding:0;display:flex}.JtQ9hG_marker:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-1))}.JtQ9hG_leading{width:16px;height:16px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary));flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex}.JtQ9hG_title{color:var(--dsw-alias-label-primary-dimmed,var(--dsw-alias-label-tertiary));flex:none;font-size:14px;line-height:24px}.JtQ9hG_badge{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));flex:none;align-items:center;gap:4px;margin-left:10px;font-size:12px;line-height:18px;display:inline-flex}.JtQ9hG_body{padding:4px 0 4px 22px}.JtQ9hG_empty{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));font-size:13px;line-height:20px}.JtQ9hG_entryUser,.JtQ9hG_entryAssistant{color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary));padding:4px 0;font-size:14px;line-height:22px}.JtQ9hG_entryKind{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));margin-bottom:2px;font-size:12px;line-height:18px}.JtQ9hG_entryUser{border-left:2px solid var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed));padding-left:8px}.JtQ9hG_entryAssistant{border-left:2px solid var(--dsw-alias-border-l1,var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed)));padding-left:8px}.JtQ9hG_restoreRow{align-items:center;gap:8px;padding-top:8px;display:flex}.JtQ9hG_restoreFailed{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary));font-size:12px}";
5653
+ //#region \0dsh-css:src/client/WithdrawnDividerView.module.css.mjs
5654
+ const css = ".JtQ9hG_row{padding:2px 0}.JtQ9hG_marker{width:100%;min-width:0;height:24px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:6px;align-items:center;padding:0;display:flex}.JtQ9hG_marker:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-1))}.JtQ9hG_leading{width:16px;height:16px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary));flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex}.JtQ9hG_title{color:var(--dsw-alias-label-primary-dimmed,var(--dsw-alias-label-tertiary));flex:none;font-size:14px;line-height:24px}.JtQ9hG_badge{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));flex:none;align-items:center;gap:4px;margin-left:10px;font-size:12px;line-height:18px;display:inline-flex}.JtQ9hG_body{padding:4px 0 4px 22px}.JtQ9hG_empty{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));font-size:13px;line-height:20px}.JtQ9hG_textRun{white-space:pre-wrap;word-break:break-word;font-size:inherit;line-height:inherit}.JtQ9hG_entryUser,.JtQ9hG_entryAssistant{color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary));padding:4px 0;font-size:14px;line-height:22px}.JtQ9hG_entryKind{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-primary-dimmed));margin-bottom:2px;font-size:12px;line-height:18px}.JtQ9hG_entryUser{border-left:2px solid var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed));padding-left:8px}.JtQ9hG_entryAssistant{border-left:2px solid var(--dsw-alias-border-l1,var(--dsw-alias-border-l2,var(--dsw-alias-label-primary-dimmed)));padding-left:8px}.JtQ9hG_restoreRow{align-items:center;gap:8px;padding-top:8px;display:flex}.JtQ9hG_restoreFailed{color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary));font-size:12px}";
5527
5655
  const tagId = "@khorsheed/dsh-client-message-tools/WithdrawnDividerView.module.css";
5528
5656
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
5529
5657
  const tag = document.createElement("style");
@@ -5533,18 +5661,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5533
5661
  document.head.appendChild(tag);
5534
5662
  }
5535
5663
  var WithdrawnDividerView_module_css_default = {
5536
- "entryKind": "JtQ9hG_entryKind",
5537
- "leading": "JtQ9hG_leading",
5538
- "title": "JtQ9hG_title",
5664
+ "textRun": "JtQ9hG_textRun",
5665
+ "badge": "JtQ9hG_badge",
5539
5666
  "entryUser": "JtQ9hG_entryUser",
5540
- "body": "JtQ9hG_body",
5541
5667
  "entryAssistant": "JtQ9hG_entryAssistant",
5542
- "badge": "JtQ9hG_badge",
5543
- "marker": "JtQ9hG_marker",
5668
+ "entryKind": "JtQ9hG_entryKind",
5544
5669
  "restoreRow": "JtQ9hG_restoreRow",
5670
+ "body": "JtQ9hG_body",
5671
+ "leading": "JtQ9hG_leading",
5545
5672
  "restoreFailed": "JtQ9hG_restoreFailed",
5546
- "empty": "JtQ9hG_empty",
5547
- "row": "JtQ9hG_row"
5673
+ "marker": "JtQ9hG_marker",
5674
+ "title": "JtQ9hG_title",
5675
+ "row": "JtQ9hG_row",
5676
+ "empty": "JtQ9hG_empty"
5548
5677
  };
5549
5678
  //#endregion
5550
5679
  //#region src/client/WithdrawnDividerView.tsx
@@ -5561,20 +5690,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5561
5690
  * once withdrawals backfill the draft automatically.
5562
5691
  */
5563
5692
  /** The withdrawal divider: collapsed marker row plus the expandable replay. */
5564
- function WithdrawnDividerView({ node, t, useSession, restoreMessage }) {
5693
+ function WithdrawnDividerView({ node, t, restoreMessage, ...standard }) {
5565
5694
  const data = node.data;
5566
5695
  const [expanded, setExpanded] = (0, react.useState)(false);
5567
5696
  const [entries, setEntries] = (0, react.useState)(null);
5568
5697
  const [busy, setBusy] = (0, react.useState)(false);
5569
5698
  const [failed, setFailed] = (0, react.useState)(false);
5570
- const nodesStore = useSession((snapshot) => snapshot.chat.nodes);
5571
- const count = useSession((snapshot) => countHiddenInSpan(snapshot.chat.nodes.values(), data.hiddenStartSeq, data.seq));
5572
- const restored = useSession((snapshot) => {
5573
- const nodes = snapshot.chat.nodes.values();
5699
+ const useChatSlice = chatHookOf(standard);
5700
+ const nodesStore = useChatSlice((chat) => chat.nodes);
5701
+ const count = useChatSlice((chat) => countHiddenInSpan(chat.nodes.values(), data.hiddenStartSeq, data.seq));
5702
+ const restored = useChatSlice((chat) => {
5703
+ const nodes = chat.nodes.values();
5574
5704
  return hasRestoreForSpan(nodes, data.hiddenStartSeq, foldHiddenRanges(nodes));
5575
5705
  });
5576
- const superseded = useSession((snapshot) => {
5577
- const nodes = snapshot.chat.nodes.values();
5706
+ const superseded = useChatSlice((chat) => {
5707
+ const nodes = chat.nodes.values();
5578
5708
  return isRestoreSuperseded(nodes, data.hiddenStartSeq, foldHiddenRanges(nodes));
5579
5709
  });
5580
5710
  const toggle = () => {
@@ -5627,7 +5757,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5627
5757
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5628
5758
  className: WithdrawnDividerView_module_css_default.entryKind,
5629
5759
  children: entry.kind === "user" ? t("withdrawn.entryUser") : t("withdrawn.entryAssistant")
5630
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MessageText, { text: entry.text })]
5760
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5761
+ className: WithdrawnDividerView_module_css_default.textRun,
5762
+ children: entry.text
5763
+ })]
5631
5764
  }, index)), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5632
5765
  className: WithdrawnDividerView_module_css_default.restoreRow,
5633
5766
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -5663,23 +5796,42 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5663
5796
  });
5664
5797
  /**
5665
5798
  * Required services: slots, sessions, the remote channel, the scope-addressed
5666
- * conversation service, conversation node registration, and the copy.
5799
+ * conversation service, and the copy.
5667
5800
  * `remote.messageTools` is deliberately NOT an inject: this plugin both
5668
5801
  * mounts the namespace (through `$mount` below) and consumes it, and the
5669
5802
  * Cordis property proxy only resolves services declared in `inject` or
5670
5803
  * provided by an ancestor fiber. Declaring it would deadlock the loader; the
5671
5804
  * namespace is read back from the global store with `ctx.get` after the
5672
- * awaited mount.
5805
+ * awaited mount. The event-Definition registry (`uiConversation.events`) is
5806
+ * likewise NOT a static inject: the deferred ctx.inject arm keeps the
5807
+ * registration independent of ui-conversation's own mount order.
5673
5808
  */
5674
5809
  const inject = [
5675
5810
  "slots",
5676
5811
  "sessions",
5677
5812
  "remote",
5678
5813
  "conversation",
5679
- "conversationEvents",
5680
5814
  "locale"
5681
5815
  ];
5682
5816
  /**
5817
+ * Register the four message-tools event Definitions against ui-conversation's
5818
+ * event registry (host 0.1.2 folded the standalone registry service into
5819
+ * `uiConversation.events`, upstream commit be531688f3). The registry is live
5820
+ * (a registration re-assembles mounted views), so the deferred arm needs no
5821
+ * ordering against ui-conversation's own apply; a composition without the
5822
+ * service never fires it and simply has no chat projection at all.
5823
+ * @param ctx - client root context.
5824
+ */
5825
+ function registerEventDefinitions(ctx) {
5826
+ ctx.inject(["uiConversation"], (lctx) => {
5827
+ const registry = lctx.uiConversation.events;
5828
+ registry.register(withdrawnDividerDefinition);
5829
+ registry.register(restoredMessageDefinition);
5830
+ registry.register(restoredAssistantMessageDefinition);
5831
+ registry.register(editedMessageDefinition);
5832
+ });
5833
+ }
5834
+ /**
5683
5835
  * Client plugin body: mount the Remote, register the dictionaries, the
5684
5836
  * withdrawal divider Definition, and the two `conversation.chat.node` entries
5685
5837
  * (the divider row and the shadowed user renderer).
@@ -5721,10 +5873,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5721
5873
  en
5722
5874
  }), "message-tools: dictionaries");
5723
5875
  const t = ctx.locale.bind(NS);
5724
- ctx.conversationEvents.register(withdrawnDividerDefinition);
5725
- ctx.conversationEvents.register(restoredMessageDefinition);
5726
- ctx.conversationEvents.register(restoredAssistantMessageDefinition);
5727
- ctx.conversationEvents.register(editedMessageDefinition);
5876
+ registerEventDefinitions(ctx);
5728
5877
  ctx.effect(() => installDomHider(ctx), "message-tools: dom hider");
5729
5878
  ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
5730
5879
  name: "conversation.chat.node",
@@ -5761,7 +5910,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5761
5910
  if (!carried.ok) throw new Error(`messageTools.edit transport: ${carried.error.code}`);
5762
5911
  if (!carried.value.ok) throw new Error(`messageTools.edit rejected: ${carried.value.error.code}`);
5763
5912
  };
5764
- const waitIdle = () => waitForTurnSettled(() => ctx.sessions.binding(sessionId)?.session.getSnapshot());
5913
+ const settleSnapshot = () => {
5914
+ const session = ctx.sessions.binding(sessionId)?.session.getSnapshot();
5915
+ if (session === void 0) return void 0;
5916
+ const chat = chatSourceOf(ctx, sessionId)?.getSnapshot();
5917
+ if (chat === void 0) return void 0;
5918
+ return {
5919
+ running: session.running,
5920
+ runningCalls: chat.legacy.runningCalls,
5921
+ chat
5922
+ };
5923
+ };
5924
+ const waitIdle = () => waitForTurnSettled(settleSnapshot);
5765
5925
  return {
5766
5926
  editMessage: (targetSeq, text) => editInPlace({
5767
5927
  cancel: () => conversation.cancel(),