@ykurilov/agent-chat-react 0.62.0 → 0.63.1

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.
@@ -3983,7 +3983,7 @@ function useThinkingStatus(active = true) {
3983
3983
 
3984
3984
  // src/agent-chat.tsx
3985
3985
  import { getToolName as getToolName5, isToolUIPart as isToolUIPart3 } from "ai";
3986
- import { Fragment, memo as memo4, useCallback as useCallback16, useEffect as useEffect14, useId as useId2, useMemo as useMemo15, useRef as useRef16, useState as useState17 } from "react";
3986
+ import { Fragment, memo as memo4, useCallback as useCallback17, useEffect as useEffect14, useId as useId2, useMemo as useMemo16, useRef as useRef17, useState as useState18 } from "react";
3987
3987
 
3988
3988
  // src/expanded-parts.ts
3989
3989
  import { useCallback as useCallback6, useMemo as useMemo5, useSyncExternalStore as useSyncExternalStore2 } from "react";
@@ -4121,7 +4121,7 @@ function isTextField(target) {
4121
4121
  if (!(target instanceof Element)) return false;
4122
4122
  return target.closest("textarea, input, select, [contenteditable]:not([contenteditable='false'])") !== null;
4123
4123
  }
4124
- function MessageFeedView({ scrollRef, messages, renderMessage, keepLastMounted, openAtMessageId, ref }) {
4124
+ function MessageFeedView({ scrollRef, messages, renderMessage, keepLastMounted, openAtMessageId, keepMountedMessageId, ref }) {
4125
4125
  const rootRef = useRef7(null);
4126
4126
  const virtualizerRef = useRef7(null);
4127
4127
  const stuck = useRef7(true);
@@ -4312,11 +4312,14 @@ function MessageFeedView({ scrollRef, messages, renderMessage, keepLastMounted,
4312
4312
  }, [scrollRef, stick]);
4313
4313
  useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
4314
4314
  const virtualizer = virtualizerRef.current;
4315
+ const editedIndex = keepMountedMessageId === void 0 ? -1 : messages.findIndex((message) => message.id === keepMountedMessageId);
4315
4316
  const tailStart = positioned && followingBottom && virtualizer && lastIndex >= 0 ? Math.min(lastIndex, Math.max(0, virtualizer.findItemIndex(virtualizer.scrollOffset - BUFFER_PX))) : lastIndex + 1;
4316
4317
  const keepMounted = useMemo6(() => {
4317
- const kept = Array.from({ length: Math.max(0, lastIndex + 1 - tailStart) }, (_, offset) => tailStart + offset);
4318
- return keepLastMounted && lastIndex >= 0 && kept.at(-1) !== lastIndex ? [...kept, lastIndex] : kept;
4319
- }, [keepLastMounted, lastIndex, tailStart]);
4318
+ const kept = new Set(Array.from({ length: Math.max(0, lastIndex + 1 - tailStart) }, (_, offset) => tailStart + offset));
4319
+ if (keepLastMounted && lastIndex >= 0) kept.add(lastIndex);
4320
+ if (editedIndex >= 0) kept.add(editedIndex);
4321
+ return [...kept].sort((a, b) => a - b);
4322
+ }, [editedIndex, keepLastMounted, lastIndex, tailStart]);
4320
4323
  return /* @__PURE__ */ jsx3("div", { ref: rootRef, children: scrollerReady && /* @__PURE__ */ jsx3(
4321
4324
  Virtualizer,
4322
4325
  {
@@ -4334,6 +4337,68 @@ function MessageFeedView({ scrollRef, messages, renderMessage, keepLastMounted,
4334
4337
  }
4335
4338
  var MessageFeed = memo(MessageFeedView);
4336
4339
 
4340
+ // src/message-editing/use-message-editing.ts
4341
+ import { useCallback as useCallback8, useLayoutEffect as useLayoutEffect2, useMemo as useMemo7, useRef as useRef8, useState as useState8 } from "react";
4342
+ function useMessageEditing(messages, { saving, onDraftLost }) {
4343
+ const [editor, setEditor] = useState8(null);
4344
+ const draft = useRef8(null);
4345
+ const messagesRef = useRef8(messages);
4346
+ messagesRef.current = messages;
4347
+ const reportLost = useRef8(onDraftLost);
4348
+ reportLost.current = onDraftLost;
4349
+ useLayoutEffect2(() => {
4350
+ if (!editor || saving.current || messages.some((message) => message.id === editor.id)) return;
4351
+ const same = sameMessage(messages, editor);
4352
+ if (same) {
4353
+ setEditor({ ...editor, id: same.id });
4354
+ return;
4355
+ }
4356
+ const typed = draft.current;
4357
+ draft.current = null;
4358
+ setEditor(null);
4359
+ if (typed !== null && typed.trim().length > 0 && typed !== editor.original) reportLost.current(typed);
4360
+ }, [editor, messages, saving]);
4361
+ const open = useCallback8((messageId) => {
4362
+ const index = messagesRef.current.findIndex((message2) => message2.id === messageId);
4363
+ const message = messagesRef.current[index];
4364
+ if (!message) return;
4365
+ draft.current = null;
4366
+ setEditor({ id: messageId, attempt: 0, original: messageText(message), index });
4367
+ }, []);
4368
+ const close = useCallback8(() => {
4369
+ draft.current = null;
4370
+ setEditor(null);
4371
+ }, []);
4372
+ const type = useCallback8((text) => {
4373
+ draft.current = text;
4374
+ }, []);
4375
+ const refuse = useCallback8((messageId, text, error) => {
4376
+ draft.current = text;
4377
+ setEditor((current) => current && { ...current, id: messageId, attempt: current.attempt + 1, error });
4378
+ }, []);
4379
+ const draftOf = useCallback8((message) => draft.current ?? messageText(message), []);
4380
+ return useMemo7(
4381
+ () => ({
4382
+ id: editor?.id ?? null,
4383
+ attempt: editor?.attempt ?? 0,
4384
+ ...editor?.error !== void 0 && { error: editor.error },
4385
+ draftOf,
4386
+ open,
4387
+ type,
4388
+ close,
4389
+ refuse
4390
+ }),
4391
+ [close, draftOf, editor, open, refuse, type]
4392
+ );
4393
+ }
4394
+ function sameMessage(messages, editor) {
4395
+ const isSame = (message) => message?.role === "user" && messageText(message) === editor.original;
4396
+ const atPlace = messages[editor.index];
4397
+ if (isSame(atPlace)) return atPlace;
4398
+ const matches = messages.filter(isSame);
4399
+ return matches.length === 1 ? matches[0] : void 0;
4400
+ }
4401
+
4337
4402
  // src/answer-editing/answer-editing.ts
4338
4403
  function answerEdited(mark) {
4339
4404
  return mark !== void 0 && (mark.textEdited || mark.editedToolCallIds.length > 0);
@@ -4367,7 +4432,7 @@ ${source}`;
4367
4432
  }
4368
4433
 
4369
4434
  // src/diagrams/use-diagram-blocks.ts
4370
- import { useCallback as useCallback8, useEffect as useEffect7, useId, useMemo as useMemo7, useState as useState8 } from "react";
4435
+ import { useCallback as useCallback9, useEffect as useEffect7, useId, useMemo as useMemo8, useState as useState9 } from "react";
4371
4436
 
4372
4437
  // src/diagrams/unique-svg-ids.ts
4373
4438
  var ID_ATTRIBUTE = /\sid="([^"]+)"/g;
@@ -4385,11 +4450,11 @@ function withUniqueIds(svg, suffix) {
4385
4450
  // src/diagrams/use-diagram-blocks.ts
4386
4451
  var countMermaidBlocks = (markdown) => splitMermaidBlocks(markdown).filter((segment) => segment.kind === "mermaid").length;
4387
4452
  function useDiagramBlocks({ markdown, streaming, messageId, firstBlock, renders, colors, open }) {
4388
- const blocks = useMemo7(
4453
+ const blocks = useMemo8(
4389
4454
  () => splitMermaidBlocks(markdown).flatMap((segment) => segment.kind === "mermaid" ? [segment] : []),
4390
4455
  [markdown]
4391
4456
  );
4392
- const [version, setVersion] = useState8(0);
4457
+ const [version, setVersion] = useState9(0);
4393
4458
  const insertion = useId();
4394
4459
  useEffect7(() => {
4395
4460
  let alive = true;
@@ -4402,7 +4467,7 @@ function useDiagramBlocks({ markdown, streaming, messageId, firstBlock, renders,
4402
4467
  alive = false;
4403
4468
  };
4404
4469
  }, [blocks, colors, renders, streaming]);
4405
- const renderDiagram = useCallback8(
4470
+ const renderDiagram = useCallback9(
4406
4471
  (source, blockIndex) => {
4407
4472
  const block = blocks[blockIndex];
4408
4473
  if (streaming && block && !block.closed) return { state: "streaming" };
@@ -4413,7 +4478,7 @@ function useDiagramBlocks({ markdown, streaming, messageId, firstBlock, renders,
4413
4478
  // eslint-disable-next-line react-hooks/exhaustive-deps -- `version`: готов новый рисунок, функция новая, скин рисует блок заново
4414
4479
  [blocks, colors, insertion, renders, streaming, version]
4415
4480
  );
4416
- const onOpenDiagram = useCallback8(
4481
+ const onOpenDiagram = useCallback9(
4417
4482
  (blockIndex, trigger) => {
4418
4483
  const block = blocks[blockIndex];
4419
4484
  const result = block && renders.get(block.source, colors);
@@ -4428,7 +4493,7 @@ function useDiagramBlocks({ markdown, streaming, messageId, firstBlock, renders,
4428
4493
  }
4429
4494
 
4430
4495
  // src/diagrams/use-diagram-colors.ts
4431
- import { useMemo as useMemo8, useSyncExternalStore as useSyncExternalStore3 } from "react";
4496
+ import { useMemo as useMemo9, useSyncExternalStore as useSyncExternalStore3 } from "react";
4432
4497
  var FALLBACK_COLORS = {
4433
4498
  light: { background: "#ffffff", text: "#1f2328", line: "#6e7781", node: "#f3f4f6", accent: "#0969da", dark: false },
4434
4499
  dark: { background: "#16181b", text: "#e6e8eb", line: "#8b949e", node: "#24282d", accent: "#58a6ff", dark: true }
@@ -4458,21 +4523,21 @@ function usePageDark(enabled) {
4458
4523
  }
4459
4524
  function useDiagramColors(skin, enabled) {
4460
4525
  const dark = usePageDark(enabled);
4461
- return useMemo8(() => {
4526
+ return useMemo9(() => {
4462
4527
  if (!enabled) return void 0;
4463
4528
  return skin.diagramColors?.(dark) ?? FALLBACK_COLORS[dark ? "dark" : "light"];
4464
4529
  }, [dark, enabled, skin]);
4465
4530
  }
4466
4531
 
4467
4532
  // src/live-demos/use-live-demo-blocks.tsx
4468
- import { useCallback as useCallback9, useMemo as useMemo10, useState as useState10 } from "react";
4533
+ import { useCallback as useCallback10, useMemo as useMemo11, useState as useState11 } from "react";
4469
4534
 
4470
4535
  // src/live-demos/live-demo-frame.tsx
4471
- import { memo as memo2, useEffect as useEffect8, useMemo as useMemo9, useRef as useRef8, useState as useState9 } from "react";
4536
+ import { memo as memo2, useEffect as useEffect8, useMemo as useMemo10, useRef as useRef9, useState as useState10 } from "react";
4472
4537
  import { jsx as jsx4 } from "react/jsx-runtime";
4473
4538
  var INITIAL_HEIGHT = 160;
4474
4539
  function useTwoThirdsOfScreen() {
4475
- const [limit, setLimit] = useState9(() => Math.round(window.innerHeight * 2 / 3));
4540
+ const [limit, setLimit] = useState10(() => Math.round(window.innerHeight * 2 / 3));
4476
4541
  useEffect8(() => {
4477
4542
  const update = () => setLimit(Math.round(window.innerHeight * 2 / 3));
4478
4543
  window.addEventListener("resize", update);
@@ -4481,15 +4546,15 @@ function useTwoThirdsOfScreen() {
4481
4546
  return limit;
4482
4547
  }
4483
4548
  var LiveDemoFrame = memo2(function LiveDemoFrame2({ source, colors, title, onLeftPage }) {
4484
- const srcDoc = useMemo9(() => liveDemoDocument(source, colors), [source, colors]);
4549
+ const srcDoc = useMemo10(() => liveDemoDocument(source, colors), [source, colors]);
4485
4550
  return /* @__PURE__ */ jsx4(Frame, { srcDoc, dark: colors.dark, title, onLeftPage }, srcDoc);
4486
4551
  });
4487
4552
  function Frame({ srcDoc, dark, title, onLeftPage }) {
4488
- const frame = useRef8(null);
4489
- const loads = useRef8(0);
4490
- const [height, setHeight] = useState9(INITIAL_HEIGHT);
4553
+ const frame = useRef9(null);
4554
+ const loads = useRef9(0);
4555
+ const [height, setHeight] = useState10(INITIAL_HEIGHT);
4491
4556
  const limit = useTwoThirdsOfScreen();
4492
- const leftPage = useRef8(onLeftPage);
4557
+ const leftPage = useRef9(onLeftPage);
4493
4558
  leftPage.current = onLeftPage;
4494
4559
  useEffect8(() => {
4495
4560
  const onMessage = (event) => {
@@ -4522,19 +4587,19 @@ function Frame({ srcDoc, dark, title, onLeftPage }) {
4522
4587
  import { jsx as jsx5 } from "react/jsx-runtime";
4523
4588
  function useDemoColors(skin, enabled) {
4524
4589
  const dark = usePageDark(enabled);
4525
- return useMemo10(() => {
4590
+ return useMemo11(() => {
4526
4591
  if (!enabled) return void 0;
4527
4592
  return skin.demoColors?.(dark) ?? FALLBACK_DEMO_COLORS[dark ? "dark" : "light"];
4528
4593
  }, [dark, enabled, skin]);
4529
4594
  }
4530
4595
  function useLiveDemoBlocks({ markdown, streaming, colors, blocked }) {
4531
4596
  const labels = useChatLabels();
4532
- const blocks = useMemo10(
4597
+ const blocks = useMemo11(
4533
4598
  () => colors ? splitAnswerFences(markdown, { demos: true }).flatMap((segment) => segment.kind === "demo" ? [segment] : []) : [],
4534
4599
  [colors, markdown]
4535
4600
  );
4536
- const [leftPage, setLeftPage] = useState10(() => /* @__PURE__ */ new Set());
4537
- const renderDemo = useCallback9(
4601
+ const [leftPage, setLeftPage] = useState11(() => /* @__PURE__ */ new Set());
4602
+ const renderDemo = useCallback10(
4538
4603
  (source, blockIndex) => {
4539
4604
  if (blocks[blockIndex]?.closed === false) return streaming ? { state: "streaming" } : { state: "code", source, reason: "unfinished" };
4540
4605
  if (!liveDemoFits(source)) return { state: "code", source, reason: "tooLarge" };
@@ -4607,12 +4672,12 @@ function escapeLinkText(text) {
4607
4672
  }
4608
4673
 
4609
4674
  // src/subject-chat/subject-chat-loading.tsx
4610
- import { useRef as useRef9 } from "react";
4675
+ import { useRef as useRef10 } from "react";
4611
4676
  import { jsx as jsx6 } from "react/jsx-runtime";
4612
4677
  var nothing = () => {
4613
4678
  };
4614
4679
  function SubjectChatLoading({ skin }) {
4615
- const scrollRef = useRef9(null);
4680
+ const scrollRef = useRef10(null);
4616
4681
  return /* @__PURE__ */ jsx6(
4617
4682
  skin.Layout,
4618
4683
  {
@@ -4625,15 +4690,15 @@ function SubjectChatLoading({ skin }) {
4625
4690
  }
4626
4691
 
4627
4692
  // src/subject-chat/use-subject-chat-id.ts
4628
- import { useCallback as useCallback10, useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
4693
+ import { useCallback as useCallback11, useEffect as useEffect9, useRef as useRef11, useState as useState12 } from "react";
4629
4694
  function useSubjectChatId(api, subject, onAppRefusal) {
4630
- const [attempt, setAttempt] = useState11(0);
4631
- const onAppRefusalRef = useRef10(onAppRefusal);
4695
+ const [attempt, setAttempt] = useState12(0);
4696
+ const onAppRefusalRef = useRef11(onAppRefusal);
4632
4697
  onAppRefusalRef.current = onAppRefusal;
4633
4698
  const request2 = `${attempt}
4634
4699
  ${api}
4635
4700
  ${subject}`;
4636
- const [found, setFound] = useState11(null);
4701
+ const [found, setFound] = useState12(null);
4637
4702
  useEffect9(() => {
4638
4703
  const controller = new AbortController();
4639
4704
  const settle = (state) => {
@@ -4662,7 +4727,7 @@ ${subject}`;
4662
4727
  })();
4663
4728
  return () => controller.abort();
4664
4729
  }, [api, subject, request2]);
4665
- const retry = useCallback10(() => setAttempt((value) => value + 1), []);
4730
+ const retry = useCallback11(() => setAttempt((value) => value + 1), []);
4666
4731
  return { ...found?.request === request2 ? found.state : { state: "loading" }, retry };
4667
4732
  }
4668
4733
 
@@ -4709,20 +4774,20 @@ function generatedViewerItems(describe) {
4709
4774
  }
4710
4775
 
4711
4776
  // src/media-viewer/use-media-viewer.ts
4712
- import { useCallback as useCallback11, useEffect as useEffect10, useMemo as useMemo11, useRef as useRef11, useState as useState12 } from "react";
4777
+ import { useCallback as useCallback12, useEffect as useEffect10, useMemo as useMemo12, useRef as useRef12, useState as useState13 } from "react";
4713
4778
  var ENTRY_FIELD = "agentChatViewer";
4714
4779
  var entryCounter = 0;
4715
4780
  var currentEntry = () => history.state?.[ENTRY_FIELD];
4716
4781
  function useMediaViewer({ getItems, resetKey }) {
4717
- const [opened, setOpened] = useState12(null);
4718
- const trigger = useRef11(null);
4719
- const openedRef = useRef11(opened);
4782
+ const [opened, setOpened] = useState13(null);
4783
+ const trigger = useRef12(null);
4784
+ const openedRef = useRef12(opened);
4720
4785
  openedRef.current = opened;
4721
- const getItemsRef = useRef11(getItems);
4786
+ const getItemsRef = useRef12(getItems);
4722
4787
  getItemsRef.current = getItems;
4723
- const items = useMemo11(() => opened ? opened.items ?? getItems() : [], [opened, getItems]);
4788
+ const items = useMemo12(() => opened ? opened.items ?? getItems() : [], [opened, getItems]);
4724
4789
  const index = opened ? items.findIndex((item) => item.key === opened.key) : -1;
4725
- const close = useCallback11((fromHistory, returnFocus) => {
4790
+ const close = useCallback12((fromHistory, returnFocus) => {
4726
4791
  const current = openedRef.current;
4727
4792
  if (!current) return;
4728
4793
  openedRef.current = null;
@@ -4732,7 +4797,7 @@ function useMediaViewer({ getItems, resetKey }) {
4732
4797
  trigger.current = null;
4733
4798
  if (returnFocus && source?.isConnected) source.focus();
4734
4799
  }, []);
4735
- const open = useCallback11(
4800
+ const open = useCallback12(
4736
4801
  (key, source, own) => {
4737
4802
  if (!(own ?? getItemsRef.current()).some((item) => item.key === key)) return;
4738
4803
  trigger.current = source;
@@ -4758,14 +4823,14 @@ function useMediaViewer({ getItems, resetKey }) {
4758
4823
  useEffect10(() => {
4759
4824
  if (gone) close(false, true);
4760
4825
  }, [gone, close]);
4761
- const shownFor = useRef11(resetKey);
4826
+ const shownFor = useRef12(resetKey);
4762
4827
  useEffect10(() => {
4763
4828
  if (shownFor.current === resetKey) return;
4764
4829
  shownFor.current = resetKey;
4765
4830
  close(false, false);
4766
4831
  }, [resetKey, close]);
4767
4832
  useEffect10(() => () => close(false, false), [close]);
4768
- const onIndexChange = useCallback11(
4833
+ const onIndexChange = useCallback12(
4769
4834
  (next) => {
4770
4835
  const item = items[next];
4771
4836
  const current = openedRef.current;
@@ -4776,8 +4841,8 @@ function useMediaViewer({ getItems, resetKey }) {
4776
4841
  },
4777
4842
  [items]
4778
4843
  );
4779
- const onClose = useCallback11(() => close(false, true), [close]);
4780
- const viewer = useMemo11(
4844
+ const onClose = useCallback12(() => close(false, true), [close]);
4845
+ const viewer = useMemo12(
4781
4846
  () => opened && index >= 0 ? { items, index, onIndexChange, onClose } : null,
4782
4847
  [opened, index, items, onIndexChange, onClose]
4783
4848
  );
@@ -5034,7 +5099,7 @@ function suggestedRepliesOf(messages, busy) {
5034
5099
  }
5035
5100
 
5036
5101
  // src/widgets/widget-part.tsx
5037
- import { memo as memo3, useCallback as useCallback12, useMemo as useMemo12, useState as useState13 } from "react";
5102
+ import { memo as memo3, useCallback as useCallback13, useMemo as useMemo13, useState as useState14 } from "react";
5038
5103
 
5039
5104
  // src/widgets/custom-widget-guard.tsx
5040
5105
  import { Component } from "react";
@@ -5063,12 +5128,12 @@ var WidgetPart = memo3(function WidgetPart2({
5063
5128
  onAnswer
5064
5129
  }) {
5065
5130
  const labels = useChatLabels();
5066
- const messages = useMemo12(() => formCheckMessages(labels.widgets), [labels.widgets]);
5067
- const [fieldErrors, setFieldErrors] = useState13({});
5131
+ const messages = useMemo13(() => formCheckMessages(labels.widgets), [labels.widgets]);
5132
+ const [fieldErrors, setFieldErrors] = useState14({});
5068
5133
  const toolCallId = call.toolCallId;
5069
- const answer = useCallback12((value) => onAnswer(toolCallId, value), [onAnswer, toolCallId]);
5070
- const [crashed, setCrashed] = useState13(false);
5071
- const onCrash = useCallback12(() => setCrashed(true), []);
5134
+ const answer = useCallback13((value) => onAnswer(toolCallId, value), [onAnswer, toolCallId]);
5135
+ const [crashed, setCrashed] = useState14(false);
5136
+ const onCrash = useCallback13(() => setCrashed(true), []);
5072
5137
  const active = call.state === "pending";
5073
5138
  const content = Custom && !crashed && /* @__PURE__ */ jsx7(CustomWidgetGuard, { onCrash, children: /* @__PURE__ */ jsx7(
5074
5139
  Custom,
@@ -5096,7 +5161,7 @@ var WidgetPart = memo3(function WidgetPart2({
5096
5161
  });
5097
5162
 
5098
5163
  // src/use-voice-dictation.ts
5099
- import { useCallback as useCallback13, useEffect as useEffect11, useRef as useRef12, useState as useState14 } from "react";
5164
+ import { useCallback as useCallback14, useEffect as useEffect11, useRef as useRef13, useState as useState15 } from "react";
5100
5165
 
5101
5166
  // src/dictation-field.ts
5102
5167
  function createDictationField(startValue) {
@@ -5147,17 +5212,17 @@ function createDictationField(startValue) {
5147
5212
  // src/use-voice-dictation.ts
5148
5213
  function useVoiceDictation({ voice, value, setValue, chatId }) {
5149
5214
  const labels = useChatLabels();
5150
- const [state, setState] = useState14("idle");
5151
- const [error, setError] = useState14();
5152
- const [polishing, setPolishing] = useState14(false);
5153
- const session = useRef12(null);
5154
- const field = useRef12(null);
5155
- const run = useRef12(0);
5156
- const latestValue = useRef12(value);
5157
- const knownValue = useRef12(value);
5158
- const pendingWrites = useRef12([]);
5215
+ const [state, setState] = useState15("idle");
5216
+ const [error, setError] = useState15();
5217
+ const [polishing, setPolishing] = useState15(false);
5218
+ const session = useRef13(null);
5219
+ const field = useRef13(null);
5220
+ const run = useRef13(0);
5221
+ const latestValue = useRef13(value);
5222
+ const knownValue = useRef13(value);
5223
+ const pendingWrites = useRef13([]);
5159
5224
  latestValue.current = value;
5160
- const drop = useCallback13(() => {
5225
+ const drop = useCallback14(() => {
5161
5226
  run.current += 1;
5162
5227
  session.current?.cancel();
5163
5228
  session.current = null;
@@ -5178,7 +5243,7 @@ function useVoiceDictation({ voice, value, setValue, chatId }) {
5178
5243
  setError(void 0);
5179
5244
  }, [value]);
5180
5245
  useEffect11(() => drop, [chatId, drop]);
5181
- const start = useCallback13(() => {
5246
+ const start = useCallback14(() => {
5182
5247
  if (!voice) return;
5183
5248
  const mine = ++run.current;
5184
5249
  const dictation = createDictationField(latestValue.current);
@@ -5224,7 +5289,7 @@ function useVoiceDictation({ voice, value, setValue, chatId }) {
5224
5289
  { chatId, ...labels.locale && { language: labels.locale } }
5225
5290
  );
5226
5291
  }, [voice, setValue, chatId, labels.locale]);
5227
- const onToggle = useCallback13(() => {
5292
+ const onToggle = useCallback14(() => {
5228
5293
  if (state === "idle") start();
5229
5294
  else if (state === "connecting") drop();
5230
5295
  else if (state === "listening") {
@@ -5232,7 +5297,7 @@ function useVoiceDictation({ voice, value, setValue, chatId }) {
5232
5297
  session.current?.stop();
5233
5298
  }
5234
5299
  }, [state, start, drop]);
5235
- const cancel = useCallback13(() => {
5300
+ const cancel = useCallback14(() => {
5236
5301
  if (session.current) drop();
5237
5302
  }, [drop]);
5238
5303
  const view = voice ? { state, onToggle, ...polishing && { polishing }, ...error && { error: voiceErrorText(labels.voiceErrors, error.kind, error.details) } } : void 0;
@@ -5241,33 +5306,33 @@ function useVoiceDictation({ voice, value, setValue, chatId }) {
5241
5306
 
5242
5307
  // src/use-voice-dialog.ts
5243
5308
  import { getToolName as getToolName4, isToolUIPart as isToolUIPart2 } from "ai";
5244
- import { useCallback as useCallback14, useEffect as useEffect12, useMemo as useMemo13, useRef as useRef13, useState as useState15 } from "react";
5309
+ import { useCallback as useCallback15, useEffect as useEffect12, useMemo as useMemo14, useRef as useRef14, useState as useState16 } from "react";
5245
5310
  var finished = (view) => view.state === "off" || view.state === "error";
5246
5311
  function useVoiceDialog({ engine, chatId, chat, videos }) {
5247
5312
  const labels = useChatLabels();
5248
- const labelsRef = useRef13(labels);
5313
+ const labelsRef = useRef14(labels);
5249
5314
  labelsRef.current = labels;
5250
- const [engineView, setEngineView] = useState15(null);
5251
- const chatRef = useRef13(chat);
5315
+ const [engineView, setEngineView] = useState16(null);
5316
+ const chatRef = useRef14(chat);
5252
5317
  chatRef.current = chat;
5253
- const active = useRef13(false);
5254
- const generation = useRef13(0);
5255
- const textListeners = useRef13(/* @__PURE__ */ new Set());
5256
- const endListeners = useRef13(/* @__PURE__ */ new Set());
5257
- const delivered = useRef13(/* @__PURE__ */ new Map());
5258
- const spokenWidgets = useRef13(/* @__PURE__ */ new Set());
5259
- const spokenVideos = useRef13(/* @__PURE__ */ new Set());
5260
- const videosRef = useRef13(videos);
5318
+ const active = useRef14(false);
5319
+ const generation = useRef14(0);
5320
+ const textListeners = useRef14(/* @__PURE__ */ new Set());
5321
+ const endListeners = useRef14(/* @__PURE__ */ new Set());
5322
+ const delivered = useRef14(/* @__PURE__ */ new Map());
5323
+ const spokenWidgets = useRef14(/* @__PURE__ */ new Set());
5324
+ const spokenVideos = useRef14(/* @__PURE__ */ new Set());
5325
+ const videosRef = useRef14(videos);
5261
5326
  videosRef.current = videos;
5262
- const activityListeners = useRef13(/* @__PURE__ */ new Set());
5263
- const announced = useRef13(/* @__PURE__ */ new Map());
5264
- const emitActivity = useCallback14((activity) => {
5327
+ const activityListeners = useRef14(/* @__PURE__ */ new Set());
5328
+ const announced = useRef14(/* @__PURE__ */ new Map());
5329
+ const emitActivity = useCallback15((activity) => {
5265
5330
  for (const listener of [...activityListeners.current]) listener(activity);
5266
5331
  }, []);
5267
- const emitEnd = useCallback14((end) => {
5332
+ const emitEnd = useCallback15((end) => {
5268
5333
  for (const listener of [...endListeners.current]) listener(end);
5269
5334
  }, []);
5270
- const bridge = useMemo13(
5335
+ const bridge = useMemo14(
5271
5336
  () => ({
5272
5337
  send: (text, options) => {
5273
5338
  if (!chatRef.current.sendVoiceMessage(text, options)) queueMicrotask(() => emitEnd({ messageId: "", status: "error" }));
@@ -5349,7 +5414,7 @@ function useVoiceDialog({ engine, chatId, chat, videos }) {
5349
5414
  }
5350
5415
  if (parts.size > 0) known.set(last.id, parts);
5351
5416
  }, [chat.messages, emitActivity]);
5352
- const wasBusy = useRef13(chat.busy);
5417
+ const wasBusy = useRef14(chat.busy);
5353
5418
  useEffect12(() => {
5354
5419
  const ended = wasBusy.current && !chat.busy;
5355
5420
  wasBusy.current = chat.busy;
@@ -5373,7 +5438,7 @@ function useVoiceDialog({ engine, chatId, chat, videos }) {
5373
5438
  },
5374
5439
  [engine, chatId]
5375
5440
  );
5376
- const toggle = useCallback14(() => {
5441
+ const toggle = useCallback15(() => {
5377
5442
  if (!engine) return;
5378
5443
  if (engineView?.state === "error") {
5379
5444
  generation.current += 1;
@@ -5407,7 +5472,7 @@ function useVoiceDialog({ engine, chatId, chat, videos }) {
5407
5472
  engine.close();
5408
5473
  setEngineView(null);
5409
5474
  }, [bridge, chatId, engine, engineView]);
5410
- return useMemo13(() => {
5475
+ return useMemo14(() => {
5411
5476
  if (!engine) return void 0;
5412
5477
  const view = engineView ?? { state: "off", speechAvailable: true, onToggle: toggle };
5413
5478
  return { ...view, onToggle: toggle };
@@ -5481,11 +5546,11 @@ function widgetPhrase(call, labels) {
5481
5546
  }
5482
5547
 
5483
5548
  // src/message-actions.ts
5484
- import { useCallback as useCallback15, useMemo as useMemo14, useRef as useRef14, useState as useState16 } from "react";
5549
+ import { useCallback as useCallback16, useMemo as useMemo15, useRef as useRef15, useState as useState17 } from "react";
5485
5550
  function useMessageActionRuns(chatId) {
5486
- const [running, setRunning] = useState16(() => /* @__PURE__ */ new Map());
5487
- const runningRef = useRef14(/* @__PURE__ */ new Set());
5488
- const run = useCallback15(
5551
+ const [running, setRunning] = useState17(() => /* @__PURE__ */ new Map());
5552
+ const runningRef = useRef15(/* @__PURE__ */ new Set());
5553
+ const run = useCallback16(
5489
5554
  (action, message, text) => {
5490
5555
  const key = `${message.id}
5491
5556
  ${action.id}`;
@@ -5507,7 +5572,7 @@ ${action.id}`;
5507
5572
  },
5508
5573
  [chatId]
5509
5574
  );
5510
- return useMemo14(() => ({ running, run }), [running, run]);
5575
+ return useMemo15(() => ({ running, run }), [running, run]);
5511
5576
  }
5512
5577
  function withRunning(current, messageId, actionId, on) {
5513
5578
  const ids = current.get(messageId) ?? [];
@@ -5525,14 +5590,14 @@ function renderComposerActions(actions, helper) {
5525
5590
  }
5526
5591
 
5527
5592
  // src/turn-finished.ts
5528
- import { useEffect as useEffect13, useRef as useRef15 } from "react";
5593
+ import { useEffect as useEffect13, useRef as useRef16 } from "react";
5529
5594
  function useTurnFinishedReport(chatId, busy, messages, onTurnFinished) {
5530
- const report = useRef15(onTurnFinished);
5595
+ const report = useRef16(onTurnFinished);
5531
5596
  report.current = onTurnFinished;
5532
- const turnRunning = useRef15(false);
5533
- const latest = useRef15(messages);
5597
+ const turnRunning = useRef16(false);
5598
+ const latest = useRef16(messages);
5534
5599
  latest.current = messages;
5535
- const finishedBefore = useRef15(/* @__PURE__ */ new Set());
5600
+ const finishedBefore = useRef16(/* @__PURE__ */ new Set());
5536
5601
  const last = messages.at(-1);
5537
5602
  const answerId = last?.role === "assistant" ? last.id : void 0;
5538
5603
  const answerStatus = last?.role === "assistant" ? last.metadata?.status : void 0;
@@ -5743,7 +5808,7 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
5743
5808
  const attachments = attachmentsPlugin?.useController({ api, chatId, modelSeesImages: chatModelSeesImages });
5744
5809
  const describeFiles = attachments?.describe;
5745
5810
  const imageClients = useSameList(imageClientsOf(images));
5746
- const extensions = useMemo15(
5811
+ const extensions = useMemo16(
5747
5812
  () => ({
5748
5813
  ...widgets && { widgets },
5749
5814
  ...diagrams && { diagrams },
@@ -5756,59 +5821,59 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
5756
5821
  }),
5757
5822
  [widgets, diagrams, liveDemos, imageClients, videos, appEvents, workView, stepPhrases]
5758
5823
  );
5759
- const scrollRef = useRef16(null);
5760
- const feedRef = useRef16(null);
5824
+ const scrollRef = useRef17(null);
5825
+ const feedRef = useRef17(null);
5761
5826
  const labels = useChatLabels();
5762
5827
  const expanded = useExpandedParts(chatId);
5763
5828
  const announcement = useFeedAnnouncement(chat.messages, chat.busy, chatId);
5764
5829
  const { busy, canRetry, retry, canEdit, canRegenerate, regenerate, edit, selectBranch, respondToApproval, approvalPendingId, approvalFailure } = chat;
5765
5830
  const { answerWidget, widgetPendingId, widgetFailure, sendReply, capabilities, closed } = chat;
5766
5831
  const chatClosed = closed !== null;
5767
- const reportAccess = useRef16(onChatAccessChange);
5832
+ const reportAccess = useRef17(onChatAccessChange);
5768
5833
  reportAccess.current = onChatAccessChange;
5769
5834
  useEffect14(() => {
5770
5835
  if (chat.loaded) reportAccess.current?.({ capabilities, closed });
5771
5836
  }, [capabilities, chat.loaded, closed]);
5772
5837
  const { chatState } = chat;
5773
- const reportState = useRef16(onStateChange);
5838
+ const reportState = useRef17(onStateChange);
5774
5839
  reportState.current = onStateChange;
5775
5840
  useEffect14(() => {
5776
5841
  if (chat.loaded) reportState.current?.(chatState);
5777
5842
  }, [chat.loaded, chatState]);
5778
- const reportUnavailable = useRef16(onChatUnavailable);
5843
+ const reportUnavailable = useRef17(onChatUnavailable);
5779
5844
  reportUnavailable.current = onChatUnavailable;
5780
5845
  const unavailable2 = chat.notFound || chat.loadFailed || chat.accessRefusal !== null;
5781
5846
  useEffect14(() => {
5782
5847
  if (unavailable2) reportUnavailable.current?.();
5783
5848
  }, [unavailable2]);
5784
- const [widgetAnnouncement, setWidgetAnnouncement] = useState17(null);
5849
+ const [widgetAnnouncement, setWidgetAnnouncement] = useState18(null);
5785
5850
  const announced = widgetAnnouncement && widgetAnnouncement.chatId === chatId && (announcement === "" || announcement === widgetAnnouncement.feed) ? widgetAnnouncement.text : announcement;
5786
- const announcementRef = useRef16(announcement);
5851
+ const announcementRef = useRef17(announcement);
5787
5852
  announcementRef.current = announcement;
5788
- const onWidgetAnswer = useCallback16(
5853
+ const onWidgetAnswer = useCallback17(
5789
5854
  (toolCallId, answer) => void answerWidget(toolCallId, answer, () => setWidgetAnnouncement({ text: labels.widgets.answerSent, feed: announcementRef.current, chatId })),
5790
5855
  [answerWidget, chatId, labels]
5791
5856
  );
5792
- const suggestedReplies = useMemo15(() => suggestedRepliesOf(chat.messages, busy), [busy, chat.messages]);
5857
+ const suggestedReplies = useMemo16(() => suggestedRepliesOf(chat.messages, busy), [busy, chat.messages]);
5793
5858
  const exhaustedSpending = chat.spending?.state === "exhausted";
5794
- const pickReply = useCallback16(
5859
+ const pickReply = useCallback17(
5795
5860
  (text) => {
5796
5861
  if (sendReply(text)) feedRef.current?.scrollToBottom();
5797
5862
  },
5798
5863
  [sendReply]
5799
5864
  );
5800
- const reportBusy = useRef16(onBusyChange);
5865
+ const reportBusy = useRef17(onBusyChange);
5801
5866
  reportBusy.current = onBusyChange;
5802
5867
  useEffect14(() => {
5803
5868
  reportBusy.current?.(busy);
5804
5869
  }, [busy, chatId]);
5805
5870
  const lastIndex = chat.messages.length - 1;
5806
5871
  const { corrections } = chat;
5807
- const feedMessages = useMemo15(() => corrections.length > 0 ? [...chat.messages, ...corrections] : chat.messages, [chat.messages, corrections]);
5808
- const correctionIds = useMemo15(() => new Set(corrections.map((correction) => correction.id)), [corrections]);
5872
+ const feedMessages = useMemo16(() => corrections.length > 0 ? [...chat.messages, ...corrections] : chat.messages, [chat.messages, corrections]);
5873
+ const correctionIds = useMemo16(() => new Set(corrections.map((correction) => correction.id)), [corrections]);
5809
5874
  const steeredAnswerIdsKey = steeredAnswerIdsOf(feedMessages);
5810
- const steeredAnswerIds = useMemo15(() => new Set(steeredAnswerIdsKey.split(" ")), [steeredAnswerIdsKey]);
5811
- const reportFeed = useRef16(onFeedChange);
5875
+ const steeredAnswerIds = useMemo16(() => new Set(steeredAnswerIdsKey.split(" ")), [steeredAnswerIdsKey]);
5876
+ const reportFeed = useRef17(onFeedChange);
5812
5877
  reportFeed.current = onFeedChange;
5813
5878
  useEffect14(() => {
5814
5879
  reportFeed.current?.({ messages: chat.messages, busy });
@@ -5816,45 +5881,54 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
5816
5881
  useTurnFinishedReport(chatId, busy, chat.messages, onTurnFinished);
5817
5882
  const appEventReport = useAppEventReport(chatId, chat.loaded, busy, chat.messages, onAppEvent);
5818
5883
  const actionRuns = useMessageActionRuns(chatId);
5819
- const dividers = useMemo15(() => dividersByMessage(chat.compactions), [chat.compactions]);
5884
+ const dividers = useMemo16(() => dividersByMessage(chat.compactions), [chat.compactions]);
5820
5885
  const voiceAnswers = useVoiceAnswerIds(chat.messages);
5821
- const forkByMessage = useMemo15(() => new Map(chat.forks.map((fork) => [fork.messageId, fork])), [chat.forks]);
5822
- const [editing, setEditing] = useState17(null);
5823
- const editingId = editing?.id ?? null;
5824
- const [editingAnswerId, setEditingAnswerId] = useState17(null);
5825
- const saving = useRef16(false);
5826
- const [feedView, setFeedView] = useState17({ epoch: 0 });
5886
+ const forkByMessage = useMemo16(() => new Map(chat.forks.map((fork) => [fork.messageId, fork])), [chat.forks]);
5887
+ const [editingAnswerId, setEditingAnswerId] = useState18(null);
5888
+ const saving = useRef17(false);
5889
+ const composerValue = useRef17(chat.value);
5890
+ composerValue.current = chat.value;
5891
+ const { setValue: setComposerValue } = chat;
5892
+ const draftLost = useCallback17(
5893
+ (text) => setComposerValue(composerValue.current.trim().length > 0 ? `${composerValue.current}
5894
+
5895
+ ${text}` : text),
5896
+ [setComposerValue]
5897
+ );
5898
+ const editing = useMessageEditing(chat.messages, { saving, onDraftLost: draftLost });
5899
+ const editingId = editing.id;
5900
+ const [feedView, setFeedView] = useState18({ epoch: 0 });
5827
5901
  const imagesShown = imageClients.length > 0 && skin.GeneratedImages ? imageClients : void 0;
5828
- const viewerImages = useMemo15(() => imagesShown && generatedViewerItems(describeGeneratedImages(imagesShown, { api, chatId })), [imagesShown, api, chatId]);
5829
- const getViewerItems = useCallback16(
5902
+ const viewerImages = useMemo16(() => imagesShown && generatedViewerItems(describeGeneratedImages(imagesShown, { api, chatId })), [imagesShown, api, chatId]);
5903
+ const getViewerItems = useCallback17(
5830
5904
  () => collectViewerItems({ messages: chat.messages, describeAttachments: describeFiles, extraItems: viewerImages }),
5831
5905
  [chat.messages, describeFiles, viewerImages]
5832
5906
  );
5833
5907
  const mediaViewer = useMediaViewer({ getItems: getViewerItems, resetKey: `${chatId}:${feedView.epoch}` });
5834
- const describeImages = useMemo15(
5908
+ const describeImages = useMemo16(
5835
5909
  () => imagesShown && describeGeneratedImages(imagesShown, { api, chatId, open: skin.MediaViewer && mediaViewer.open }),
5836
5910
  [imagesShown, api, chatId, skin.MediaViewer, mediaViewer.open]
5837
5911
  );
5838
- const describeAttachments = useMemo15(
5912
+ const describeAttachments = useMemo16(
5839
5913
  () => describeWithViewer(describeFiles, skin.MediaViewer && mediaViewer.open),
5840
5914
  [describeFiles, skin.MediaViewer, mediaViewer.open]
5841
5915
  );
5842
- const diagramRenders = useMemo15(() => diagrams && createDiagramRenders(diagrams), [diagrams]);
5916
+ const diagramRenders = useMemo16(() => diagrams && createDiagramRenders(diagrams), [diagrams]);
5843
5917
  const diagramColors = useDiagramColors(skin, diagrams !== void 0);
5844
- const diagramSetup = useMemo15(
5918
+ const diagramSetup = useMemo16(
5845
5919
  () => diagramRenders && diagramColors ? { renders: diagramRenders, colors: diagramColors, ...skin.MediaViewer && { open: mediaViewer.open } } : void 0,
5846
5920
  [diagramColors, diagramRenders, mediaViewer.open, skin.MediaViewer]
5847
5921
  );
5848
5922
  const demoColors = useDemoColors(skin, liveDemos === true);
5849
- const demos = useMemo15(() => demoColors && { colors: demoColors, blocked: chat.externalContent }, [demoColors, chat.externalContent]);
5850
- const switchBranch = useCallback16(
5923
+ const demos = useMemo16(() => demoColors && { colors: demoColors, blocked: chat.externalContent }, [demoColors, chat.externalContent]);
5924
+ const switchBranch = useCallback17(
5851
5925
  async (messageId) => {
5852
5926
  if (await selectBranch(messageId)) setFeedView((view) => ({ epoch: view.epoch + 1, openAtMessageId: messageId }));
5853
5927
  },
5854
5928
  [selectBranch]
5855
5929
  );
5856
- const switchers = useMemo15(() => /* @__PURE__ */ new Map(), [busy, capabilities.branches, forkByMessage, skin, switchBranch]);
5857
- const switcherOf = useCallback16(
5930
+ const switchers = useMemo16(() => /* @__PURE__ */ new Map(), [busy, capabilities.branches, forkByMessage, skin, switchBranch]);
5931
+ const switcherOf = useCallback17(
5858
5932
  (messageId) => {
5859
5933
  const cached = switchers.get(messageId);
5860
5934
  if (cached !== void 0) return cached;
@@ -5878,33 +5952,33 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
5878
5952
  },
5879
5953
  [busy, capabilities.branches, forkByMessage, skin, switchBranch, switchers]
5880
5954
  );
5881
- const saveEdit = useCallback16(
5955
+ const saveEdit = useCallback17(
5882
5956
  async (messageId, text) => {
5883
5957
  if (saving.current) return;
5884
5958
  saving.current = true;
5885
5959
  try {
5886
5960
  const outcome = await edit(messageId, text);
5887
5961
  if (outcome.status === "started") {
5888
- setEditing(null);
5962
+ editing.close();
5889
5963
  feedRef.current?.scrollToBottom();
5890
5964
  return;
5891
5965
  }
5892
5966
  const kind = outcome.status === "refused" ? EDIT_REFUSAL_ERRORS[outcome.reason] : outcome.reason === "busy" || outcome.reason === "offline" ? outcome.reason : "rejected";
5893
5967
  const messageLimit = outcome.status === "refused" && outcome.reason === "spending_limit" && outcome.unit === "messages";
5894
5968
  const error = outcome.status === "refused" && outcome.text || (messageLimit ? labels.editMessageLimit : labels.editErrors[kind]);
5895
- setEditing((current) => ({ id: messageId, attempt: (current?.attempt ?? 0) + 1, text, error }));
5969
+ editing.refuse(messageId, text, error);
5896
5970
  } finally {
5897
5971
  saving.current = false;
5898
5972
  }
5899
5973
  },
5900
- [edit, labels]
5974
+ [edit, editing, labels]
5901
5975
  );
5902
5976
  const { tasks: backgroundTaskList, decisions: taskDecisions, decide: decideTask } = backgroundTasks;
5903
- const noticeControls = useMemo15(
5977
+ const noticeControls = useMemo16(
5904
5978
  () => ({ tasks: backgroundTaskList, decisions: taskDecisions, decide: (taskId, approvalId, decision) => void decideTask(taskId, approvalId, decision), failureText: labels.approvalFailed }),
5905
5979
  [backgroundTaskList, decideTask, labels, taskDecisions]
5906
5980
  );
5907
- const renderMessage = useCallback16(
5981
+ const renderMessage = useCallback17(
5908
5982
  (message, index) => {
5909
5983
  if (correctionIds.has(message.id)) return /* @__PURE__ */ jsx9(UserMessage, { skin, message, describe: describeAttachments, branchSwitcher: void 0, pending: true });
5910
5984
  const branchSwitcher = switcherOf(message.id);
@@ -5918,14 +5992,15 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
5918
5992
  MessageEditorRow,
5919
5993
  {
5920
5994
  skin,
5921
- initialText: editing?.text ?? messageText(message),
5995
+ initialText: editing.draftOf(message),
5922
5996
  describe: describeAttachments,
5923
5997
  message,
5924
- ...editing?.error && { error: editing.error },
5998
+ ...editing.error && { error: editing.error },
5999
+ onDraftChange: editing.type,
5925
6000
  onSave: (text) => void saveEdit(message.id, text),
5926
- onCancel: () => setEditing(null)
6001
+ onCancel: editing.close
5927
6002
  },
5928
- editing?.attempt
6003
+ editing.attempt
5929
6004
  ) : /* @__PURE__ */ jsx9(
5930
6005
  UserMessage,
5931
6006
  {
@@ -5933,7 +6008,7 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
5933
6008
  message,
5934
6009
  describe: describeAttachments,
5935
6010
  branchSwitcher,
5936
- ...canEdit && { onEdit: () => setEditing({ id: message.id, attempt: 0 }) }
6011
+ ...canEdit && { onEdit: () => editing.open(message.id) }
5937
6012
  }
5938
6013
  ) : editingAnswerId === message.id && answerEditing ? /* @__PURE__ */ jsx9(Fragment2, { children: answerEditing.renderEditor({
5939
6014
  message,
@@ -6040,6 +6115,7 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
6040
6115
  scrollRef,
6041
6116
  messages: feedMessages,
6042
6117
  keepLastMounted: busy,
6118
+ ...editingId !== null && { keepMountedMessageId: editingId },
6043
6119
  ...feedView.openAtMessageId && { openAtMessageId: feedView.openAtMessageId },
6044
6120
  renderMessage
6045
6121
  },
@@ -6193,7 +6269,7 @@ var UserMessage = memo4(function UserMessage2({
6193
6269
  onEdit,
6194
6270
  pending
6195
6271
  }) {
6196
- const files = useMemo15(() => describe?.(message), [describe, message]);
6272
+ const files = useMemo16(() => describe?.(message), [describe, message]);
6197
6273
  return /* @__PURE__ */ jsx9(
6198
6274
  skin.UserMessage,
6199
6275
  {
@@ -6211,12 +6287,13 @@ function MessageEditorRow({
6211
6287
  initialText,
6212
6288
  describe,
6213
6289
  error,
6290
+ onDraftChange,
6214
6291
  onSave,
6215
6292
  onCancel
6216
6293
  }) {
6217
- const [draft, setDraft] = useState17(initialText);
6218
- const [shownError, setShownError] = useState17(error);
6219
- const files = useMemo15(() => describe?.(message), [describe, message]);
6294
+ const [draft, setDraft] = useState18(initialText);
6295
+ const [shownError, setShownError] = useState18(error);
6296
+ const files = useMemo16(() => describe?.(message), [describe, message]);
6220
6297
  const canSave = draft.trim().length > 0 || files !== void 0;
6221
6298
  return /* @__PURE__ */ jsx9(
6222
6299
  skin.MessageEditor,
@@ -6224,6 +6301,7 @@ function MessageEditorRow({
6224
6301
  value: draft,
6225
6302
  onValueChange: (value) => {
6226
6303
  setDraft(value);
6304
+ onDraftChange(value);
6227
6305
  setShownError(void 0);
6228
6306
  },
6229
6307
  onSave: () => canSave && onSave(draft),
@@ -6562,13 +6640,13 @@ function approvalProps(part, controls) {
6562
6640
  return { approval: { state: approval.reason === ALWAYS_IN_CHAT ? "always" : "allowed" } };
6563
6641
  }
6564
6642
  function useSameList(list) {
6565
- const kept = useRef16(list);
6643
+ const kept = useRef17(list);
6566
6644
  if (kept.current.length !== list.length || kept.current.some((item, index) => item !== list[index])) kept.current = list;
6567
6645
  return kept.current;
6568
6646
  }
6569
6647
 
6570
6648
  // src/chat-list/use-chat-list.ts
6571
- import { useCallback as useCallback17, useEffect as useEffect15, useRef as useRef17, useState as useState18 } from "react";
6649
+ import { useCallback as useCallback18, useEffect as useEffect15, useRef as useRef18, useState as useState19 } from "react";
6572
6650
 
6573
6651
  // src/chat-list/active-chat-memory.ts
6574
6652
  var defaultRememberKey = (api) => `agent-chat:active-chat:${api}`;
@@ -6650,15 +6728,15 @@ var NOTHING_LOADED = {
6650
6728
  var collapse = (title) => title.trim().replace(/\s+/g, " ");
6651
6729
  function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCreateRefused }) {
6652
6730
  const memoryKey = rememberKey ?? defaultRememberKey(api);
6653
- const [status, setStatus] = useState18("loading");
6654
- const [items, setItems] = useState18([]);
6655
- const [activeChatId, setActiveChatId] = useState18(null);
6656
- const [error, setError] = useState18(null);
6657
- const [creationRefusal, setCreationRefusal] = useState18(null);
6658
- const [creating, setCreating] = useState18(false);
6659
- const [deleteRequest, setDeleteRequest] = useState18(null);
6660
- const [deleting, setDeleting] = useState18(false);
6661
- const [stateKey, setStateKey] = useState18(memoryKey);
6731
+ const [status, setStatus] = useState19("loading");
6732
+ const [items, setItems] = useState19([]);
6733
+ const [activeChatId, setActiveChatId] = useState19(null);
6734
+ const [error, setError] = useState19(null);
6735
+ const [creationRefusal, setCreationRefusal] = useState19(null);
6736
+ const [creating, setCreating] = useState19(false);
6737
+ const [deleteRequest, setDeleteRequest] = useState19(null);
6738
+ const [deleting, setDeleting] = useState19(false);
6739
+ const [stateKey, setStateKey] = useState19(memoryKey);
6662
6740
  const keyChanged = stateKey !== memoryKey;
6663
6741
  if (keyChanged) {
6664
6742
  setStateKey(memoryKey);
@@ -6671,30 +6749,30 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6671
6749
  setDeleteRequest(null);
6672
6750
  setDeleting(false);
6673
6751
  }
6674
- const keyRef = useRef17(memoryKey);
6752
+ const keyRef = useRef18(memoryKey);
6675
6753
  keyRef.current = memoryKey;
6676
- const fetchRef = useRef17(customFetch);
6754
+ const fetchRef = useRef18(customFetch);
6677
6755
  fetchRef.current = customFetch;
6678
- const refusedRef = useRef17(onCreateRefused);
6756
+ const refusedRef = useRef18(onCreateRefused);
6679
6757
  refusedRef.current = onCreateRefused;
6680
- const send = useCallback17((input, init) => (fetchRef.current ?? globalThis.fetch)(input, init), []);
6681
- const itemsRef = useRef17(items);
6758
+ const send = useCallback18((input, init) => (fetchRef.current ?? globalThis.fetch)(input, init), []);
6759
+ const itemsRef = useRef18(items);
6682
6760
  itemsRef.current = keyChanged ? [] : items;
6683
- const activeRef = useRef17(activeChatId);
6761
+ const activeRef = useRef18(activeChatId);
6684
6762
  activeRef.current = keyChanged ? null : activeChatId;
6685
- const initialRef = useRef17(initialChatId);
6686
- const loadRequest = useRef17(0);
6687
- const listRequest = useRef17(0);
6688
- const loadedKey = useRef17(null);
6689
- const alive = useRef17(true);
6763
+ const initialRef = useRef18(initialChatId);
6764
+ const loadRequest = useRef18(0);
6765
+ const listRequest = useRef18(0);
6766
+ const loadedKey = useRef18(null);
6767
+ const alive = useRef18(true);
6690
6768
  useEffect15(() => {
6691
6769
  alive.current = true;
6692
6770
  return () => {
6693
6771
  alive.current = false;
6694
6772
  };
6695
6773
  }, []);
6696
- const stillFor = useCallback17((key) => alive.current && keyRef.current === key, []);
6697
- const failCreate = useCallback17(
6774
+ const stillFor = useCallback18((key) => alive.current && keyRef.current === key, []);
6775
+ const failCreate = useCallback18(
6698
6776
  (cause) => {
6699
6777
  const refusal = cause instanceof ChatCreationRefusedError ? cause.refusal : null;
6700
6778
  setCreationRefusal(refusal);
@@ -6703,7 +6781,7 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6703
6781
  },
6704
6782
  []
6705
6783
  );
6706
- const activate = useCallback17(
6784
+ const activate = useCallback18(
6707
6785
  (chatId) => {
6708
6786
  activeRef.current = chatId;
6709
6787
  setActiveChatId(chatId);
@@ -6711,7 +6789,7 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6711
6789
  },
6712
6790
  [memoryKey]
6713
6791
  );
6714
- const createAndOpen = useCallback17(async () => {
6792
+ const createAndOpen = useCallback18(async () => {
6715
6793
  const id = await createChatOnServer(api, send);
6716
6794
  if (!stillFor(memoryKey)) return false;
6717
6795
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -6719,7 +6797,7 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6719
6797
  activate(id);
6720
6798
  return true;
6721
6799
  }, [activate, api, memoryKey, send, stillFor]);
6722
- const load = useCallback17(async () => {
6800
+ const load = useCallback18(async () => {
6723
6801
  if (loadedKey.current !== memoryKey) {
6724
6802
  if (loadedKey.current !== null) initialRef.current = void 0;
6725
6803
  loadedKey.current = memoryKey;
@@ -6752,7 +6830,7 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6752
6830
  useEffect15(() => {
6753
6831
  void load();
6754
6832
  }, [load]);
6755
- const refresh = useCallback17(async () => {
6833
+ const refresh = useCallback18(async () => {
6756
6834
  const request2 = ++listRequest.current;
6757
6835
  try {
6758
6836
  const loaded = await fetchChatList(api, send);
@@ -6760,7 +6838,7 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6760
6838
  } catch {
6761
6839
  }
6762
6840
  }, [api, memoryKey, send, stillFor]);
6763
- const create = useCallback17(async () => {
6841
+ const create = useCallback18(async () => {
6764
6842
  setError(null);
6765
6843
  setCreating(true);
6766
6844
  try {
@@ -6778,14 +6856,14 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6778
6856
  if (stillFor(memoryKey)) setCreating(false);
6779
6857
  }
6780
6858
  }, [activate, api, createAndOpen, failCreate, memoryKey, send, stillFor]);
6781
- const select = useCallback17(
6859
+ const select = useCallback18(
6782
6860
  (chatId) => {
6783
6861
  setError(null);
6784
6862
  activate(chatId);
6785
6863
  },
6786
6864
  [activate]
6787
6865
  );
6788
- const rename = useCallback17(
6866
+ const rename = useCallback18(
6789
6867
  async (chatId, title) => {
6790
6868
  const normalized = collapse(title);
6791
6869
  const current = itemsRef.current.find((item) => item.id === chatId);
@@ -6804,12 +6882,12 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6804
6882
  },
6805
6883
  [api, memoryKey, send, stillFor]
6806
6884
  );
6807
- const requestDelete = useCallback17((chatId) => {
6885
+ const requestDelete = useCallback18((chatId) => {
6808
6886
  const item = itemsRef.current.find((entry) => entry.id === chatId);
6809
6887
  if (item) setDeleteRequest({ id: item.id, title: item.title });
6810
6888
  }, []);
6811
- const cancelDelete = useCallback17(() => setDeleteRequest(null), []);
6812
- const confirmDelete = useCallback17(async () => {
6889
+ const cancelDelete = useCallback18(() => setDeleteRequest(null), []);
6890
+ const confirmDelete = useCallback18(async () => {
6813
6891
  if (!deleteRequest) return;
6814
6892
  const { id } = deleteRequest;
6815
6893
  setError(null);
@@ -6845,7 +6923,7 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6845
6923
  if (stillFor(memoryKey)) failCreate(cause);
6846
6924
  }
6847
6925
  }, [activate, api, createAndOpen, deleteRequest, failCreate, memoryKey, send, stillFor]);
6848
- const reload = useCallback17(() => void load(), [load]);
6926
+ const reload = useCallback18(() => void load(), [load]);
6849
6927
  return {
6850
6928
  ...keyChanged ? NOTHING_LOADED : { status, items, activeChatId, error, creationRefusal, creating, deleteRequest, deleting },
6851
6929
  select,
@@ -6860,18 +6938,18 @@ function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCr
6860
6938
  }
6861
6939
 
6862
6940
  // src/chat-list/use-chat-list-view.ts
6863
- import { useCallback as useCallback18 } from "react";
6941
+ import { useCallback as useCallback19 } from "react";
6864
6942
  function useChatListView(list, onChosen) {
6865
6943
  const labels = useChatLabels();
6866
6944
  const { select, create: createChat } = list;
6867
- const onSelect = useCallback18(
6945
+ const onSelect = useCallback19(
6868
6946
  (chatId) => {
6869
6947
  select(chatId);
6870
6948
  onChosen();
6871
6949
  },
6872
6950
  [onChosen, select]
6873
6951
  );
6874
- const onCreate = useCallback18(() => {
6952
+ const onCreate = useCallback19(() => {
6875
6953
  void createChat().then((opened) => opened && onChosen());
6876
6954
  }, [createChat, onChosen]);
6877
6955
  const deleteRequest = list.deleteRequest && {
@@ -6915,11 +6993,11 @@ var CHAT_ORIGINS = /* @__PURE__ */ new Set(["telegram", "vk", "max"]);
6915
6993
  var chatOriginOf = (origin) => origin !== void 0 && CHAT_ORIGINS.has(origin) ? origin : void 0;
6916
6994
 
6917
6995
  // src/chat-list/use-workspace-list-sync.ts
6918
- import { useEffect as useEffect16, useRef as useRef18 } from "react";
6996
+ import { useEffect as useEffect16, useRef as useRef19 } from "react";
6919
6997
  var TITLE_POLL_MS = 1e3;
6920
6998
  function useWorkspaceListSync(list, busy, enabled = true) {
6921
6999
  const { items, activeChatId, refresh } = list;
6922
- const previousBusy = useRef18(null);
7000
+ const previousBusy = useRef19(null);
6923
7001
  useEffect16(() => {
6924
7002
  const before = previousBusy.current;
6925
7003
  previousBusy.current = busy;
@@ -6964,4 +7042,4 @@ export {
6964
7042
  activeChatTitle,
6965
7043
  useWorkspaceListSync
6966
7044
  };
6967
- //# sourceMappingURL=chunk-7DVTSVKQ.js.map
7045
+ //# sourceMappingURL=chunk-Y5HL6THS.js.map