@burdenoff/fe-libs 2026.1007.5 → 2026.1008.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.
Files changed (119) hide show
  1. package/dist/shared/assistant/api.d.ts +11 -0
  2. package/dist/shared/assistant/api.d.ts.map +1 -1
  3. package/dist/shared/assistant/api.js +6 -2
  4. package/dist/shared/assistant/attachmentCommit.d.ts +175 -0
  5. package/dist/shared/assistant/attachmentCommit.d.ts.map +1 -0
  6. package/dist/shared/assistant/attachmentCommit.js +638 -0
  7. package/dist/shared/assistant/attachments.d.ts +39 -19
  8. package/dist/shared/assistant/attachments.d.ts.map +1 -1
  9. package/dist/shared/assistant/conversation.d.ts +14 -0
  10. package/dist/shared/assistant/conversation.d.ts.map +1 -1
  11. package/dist/shared/assistant/createSandboxAssistantTransport.d.ts +8 -1
  12. package/dist/shared/assistant/createSandboxAssistantTransport.d.ts.map +1 -1
  13. package/dist/shared/assistant/createSandboxAssistantTransport.js +70 -51
  14. package/dist/shared/assistant/history/historyEngine.js +280 -280
  15. package/dist/shared/assistant/history/historyMapping.d.ts.map +1 -1
  16. package/dist/shared/assistant/history/historyMapping.js +19 -12
  17. package/dist/shared/assistant/i18nManifest.d.ts +23 -2
  18. package/dist/shared/assistant/i18nManifest.d.ts.map +1 -1
  19. package/dist/shared/assistant/i18nManifest.js +26 -3
  20. package/dist/shared/assistant/index.d.ts +9 -0
  21. package/dist/shared/assistant/index.d.ts.map +1 -1
  22. package/dist/shared/assistant/localUiCommandCopy.d.ts +23 -0
  23. package/dist/shared/assistant/localUiCommandCopy.d.ts.map +1 -1
  24. package/dist/shared/assistant/localUiCommandCopy.js +60 -23
  25. package/dist/shared/assistant/localUiCommands.d.ts +29 -0
  26. package/dist/shared/assistant/localUiCommands.d.ts.map +1 -1
  27. package/dist/shared/assistant/localUiCommands.js +18 -7
  28. package/dist/shared/assistant/types.d.ts +19 -8
  29. package/dist/shared/assistant/types.d.ts.map +1 -1
  30. package/dist/shared/assistant/ui/AssistantChatArea.d.ts.map +1 -1
  31. package/dist/shared/assistant/ui/AssistantChatArea.js +1624 -1266
  32. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts +18 -3
  33. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts.map +1 -1
  34. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.js +1 -1
  35. package/dist/shared/assistant/ui/AssistantMessageItem.d.ts +5 -0
  36. package/dist/shared/assistant/ui/AssistantMessageItem.d.ts.map +1 -1
  37. package/dist/shared/assistant/ui/AssistantMessageItem.js +89 -84
  38. package/dist/shared/assistant/ui/AssistantMessageList.d.ts +5 -0
  39. package/dist/shared/assistant/ui/AssistantMessageList.d.ts.map +1 -1
  40. package/dist/shared/assistant/ui/AssistantMessageList.js +26 -25
  41. package/dist/shared/assistant/ui/AssistantUiOfferLink.d.ts +45 -0
  42. package/dist/shared/assistant/ui/AssistantUiOfferLink.d.ts.map +1 -0
  43. package/dist/shared/assistant/ui/AssistantUiOfferLink.js +56 -0
  44. package/dist/shared/assistant/ui/assistantOverlayState.d.ts +9 -0
  45. package/dist/shared/assistant/ui/assistantOverlayState.d.ts.map +1 -1
  46. package/dist/shared/assistant/ui/assistantOverlayState.js +37 -28
  47. package/dist/shared/assistant/ui/composer/AssistantAttachmentChips.d.ts +8 -5
  48. package/dist/shared/assistant/ui/composer/AssistantAttachmentChips.d.ts.map +1 -1
  49. package/dist/shared/assistant/ui/composer/AssistantAttachmentChips.js +26 -9
  50. package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts +3 -2
  51. package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts.map +1 -1
  52. package/dist/shared/assistant/ui/composer/AssistantComposer.js +214 -125
  53. package/dist/shared/assistant/ui/composer/AssistantConnectSourceItem.d.ts.map +1 -1
  54. package/dist/shared/assistant/ui/composer/AssistantConnectSourceItem.js +1 -1
  55. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts +15 -0
  56. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts.map +1 -1
  57. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.js +127 -118
  58. package/dist/shared/assistant/ui/voice/VoiceConversationView.d.ts +6 -1
  59. package/dist/shared/assistant/ui/voice/VoiceConversationView.d.ts.map +1 -1
  60. package/dist/shared/assistant/ui/voice/VoiceConversationView.js +7 -7
  61. package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.d.ts +6 -1
  62. package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.d.ts.map +1 -1
  63. package/dist/shared/assistant/ui/voice/VoiceTranscriptDrawer.js +15 -12
  64. package/dist/shared/assistant/uiActions/actOnResolve.d.ts +108 -0
  65. package/dist/shared/assistant/uiActions/actOnResolve.d.ts.map +1 -0
  66. package/dist/shared/assistant/uiActions/actOnResolve.js +51 -0
  67. package/dist/shared/assistant/uiActions/linkContext.d.ts +29 -0
  68. package/dist/shared/assistant/uiActions/linkContext.d.ts.map +1 -1
  69. package/dist/shared/assistant/uiActions/linkContext.js +37 -10
  70. package/dist/shared/assistant/uiActions/navIntent.d.ts +58 -0
  71. package/dist/shared/assistant/uiActions/navIntent.d.ts.map +1 -0
  72. package/dist/shared/assistant/uiActions/navIntent.js +79 -0
  73. package/dist/shared/assistant/uiActions/navigateAction.d.ts +6 -1
  74. package/dist/shared/assistant/uiActions/navigateAction.d.ts.map +1 -1
  75. package/dist/shared/assistant/uiActions/navigateAction.js +18 -14
  76. package/dist/shared/assistant/uiActions/navigationOutcome.d.ts +16 -2
  77. package/dist/shared/assistant/uiActions/navigationOutcome.d.ts.map +1 -1
  78. package/dist/shared/assistant/uiActions/navigationOutcome.js +6 -2
  79. package/dist/shared/assistant/uiActions/offerYesOnSurface.d.ts +39 -0
  80. package/dist/shared/assistant/uiActions/offerYesOnSurface.d.ts.map +1 -0
  81. package/dist/shared/assistant/uiActions/offerYesOnSurface.js +34 -0
  82. package/dist/shared/assistant/uiActions/uiCatalogue.d.ts +14 -0
  83. package/dist/shared/assistant/uiActions/uiCatalogue.d.ts.map +1 -1
  84. package/dist/shared/assistant/uiActions/uiCatalogue.js +23 -3
  85. package/dist/shared/assistant/uiActions/uiOffers.d.ts +249 -0
  86. package/dist/shared/assistant/uiActions/uiOffers.d.ts.map +1 -0
  87. package/dist/shared/assistant/uiActions/uiOffers.js +195 -0
  88. package/dist/shared/assistant/useAssistantAttachments.d.ts +20 -2
  89. package/dist/shared/assistant/useAssistantAttachments.d.ts.map +1 -1
  90. package/dist/shared/assistant/useAssistantAttachments.js +146 -202
  91. package/dist/shared/assistant/useAttachmentCommitter.d.ts +17 -0
  92. package/dist/shared/assistant/useAttachmentCommitter.d.ts.map +1 -0
  93. package/dist/shared/assistant/useAttachmentCommitter.js +41 -0
  94. package/dist/shared/assistant/voiceCommit.d.ts +36 -0
  95. package/dist/shared/assistant/voiceCommit.d.ts.map +1 -0
  96. package/dist/shared/assistant/voiceCommit.js +24 -0
  97. package/dist/shared/assistant/writeProposals.d.ts +6 -0
  98. package/dist/shared/assistant/writeProposals.d.ts.map +1 -1
  99. package/dist/shared/assistant/writeReplyBinder.d.ts +40 -0
  100. package/dist/shared/assistant/writeReplyBinder.d.ts.map +1 -1
  101. package/dist/shared/assistant/writeReplyBinder.js +100 -83
  102. package/dist/shared/assistant/writeReplySend.d.ts +14 -0
  103. package/dist/shared/assistant/writeReplySend.d.ts.map +1 -1
  104. package/dist/shared/assistant/writeReplySend.js +32 -29
  105. package/dist/shared/hooks/index.d.ts +2 -0
  106. package/dist/shared/hooks/index.d.ts.map +1 -1
  107. package/dist/shared/navigation/unsavedChanges.d.ts +83 -0
  108. package/dist/shared/navigation/unsavedChanges.d.ts.map +1 -0
  109. package/dist/shared/navigation/unsavedChanges.js +46 -0
  110. package/dist/shared-assistant.js +247 -238
  111. package/dist/shared-hooks.js +10 -9
  112. package/dist/shared.js +20 -19
  113. package/dist/storage/filesPort.d.ts +111 -0
  114. package/dist/storage/filesPort.d.ts.map +1 -0
  115. package/dist/storage/filesPort.js +262 -0
  116. package/dist/storage/index.d.ts +2 -0
  117. package/dist/storage/index.d.ts.map +1 -1
  118. package/dist/storage/index.js +9 -8
  119. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { FC, MouseEvent, ReactNode } from 'react';
2
2
  import { assistantRoutePath, assistantUrlTransform, classifyAssistantHref, AssistantLinkKind } from '../uiActions/assistantHref';
3
+ import { NavigationInitiator, UnsavedChange } from '../../navigation/unsavedChanges';
3
4
  interface Props {
4
5
  content: string;
5
6
  }
@@ -18,15 +19,29 @@ export type AssistantNavigateResult =
18
19
  /** A shell-less target: opened in a new tab, which cannot be verified. */
19
20
  | {
20
21
  kind: "new-tab";
21
- } | {
22
+ }
23
+ /**
24
+ * ★ BOFF-8060 (N4): not routed. `unsaved-changes` names what is unsaved, for
25
+ * the "You have unsaved changes in {label}" reply.
26
+ */
27
+ | {
22
28
  kind: "refused";
23
29
  reason: "unsaved-changes" | "not-routable";
30
+ unsaved?: readonly UnsavedChange[];
24
31
  };
32
+ /**
33
+ * ★ BOFF-8060 (N4): who started this navigation. The person's own (a link they
34
+ * clicked in a reply) is at sidebar parity; the assistant's (a typed or spoken
35
+ * command, a "yes", act-on-resolve) is refused while the page holds unsaved
36
+ * changes. A navigator that does not say is treated as the assistant's.
37
+ */
38
+ export type AssistantNavigateInitiator = NavigationInitiator;
25
39
  /**
26
40
  * Route an in-app PATH — `/fields?crop=42#yield`, never a full URL. Supplied by
27
- * the product; absent means "no SPA routing".
41
+ * the product; absent means "no SPA routing". A product navigator may ignore
42
+ * `initiator`; `createAssistantNavigate` reads it.
28
43
  */
29
- export type AssistantNavigate = (path: string) => AssistantNavigateResult | void;
44
+ export type AssistantNavigate = (path: string, initiator?: AssistantNavigateInitiator) => AssistantNavigateResult | void;
30
45
  /**
31
46
  * ★★ The cases where the BROWSER must win, not the router. Losing these is how
32
47
  * "open in new tab", "copy link" and middle-click quietly stop working — the
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantMarkdownRenderer.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMarkdownRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAIL,KAAK,EAAE,EACP,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAYf,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;AAEpC,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAsCD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,CAAC;AAC5E,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,MAAM,uBAAuB;AACjC,6DAA6D;AAC3D;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE;AACpB,0EAA0E;GACxE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,iBAAiB,GAAG,cAAc,CAAA;CAAE,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,uBAAuB,GAAG,IAAI,CAAC;AAEjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,OAAO,CASjF;AAID;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC;IACzC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB,CAIA,CAAC;AAEF,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAE/D;AAuTD,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,KAAK,CAiB/C,CAAC"}
1
+ {"version":3,"file":"AssistantMarkdownRenderer.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMarkdownRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAIL,KAAK,EAAE,EACP,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAYf,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EACd,MAAM,iCAAiC,CAAC;AAEzC,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAsCD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,CAAC;AAC5E,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,MAAM,uBAAuB;AACjC,6DAA6D;AAC3D;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE;AACpB,0EAA0E;GACxE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE;AACrB;;;GAGG;GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,iBAAiB,GAAG,cAAc,CAAC;IAC3C,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC,CAAC;AAEN;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAE7D;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,0BAA0B,KACnC,uBAAuB,GAAG,IAAI,CAAC;AAEpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,OAAO,CASjF;AAID;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC;IACzC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB,CAIA,CAAC;AAEF,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAE/D;AAwTD,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,KAAK,CAiB/C,CAAC"}
@@ -34,7 +34,7 @@ var C = "text-action-primary-bg underline-offset-2 hover:underline", w = ({ href
34
34
  className: C,
35
35
  "data-testid": "assistant-markdown-link",
36
36
  onClick: (e) => {
37
- y(e) || (e.preventDefault(), n(i));
37
+ y(e) || (e.preventDefault(), n(i, "person"));
38
38
  },
39
39
  children: t
40
40
  }) : r === "internal" || r === "fragment" ? /* @__PURE__ */ g("a", {
@@ -60,6 +60,11 @@ interface Props {
60
60
  legacyWriteCardHidden?: boolean;
61
61
  /** ★ BOFF-7803 5a (FE7): this reply's write-proposal cards, if any. */
62
62
  writeProposals?: ReactNode;
63
+ /**
64
+ * ★ BOFF-8060 (N3): the page the model offered, as an "Open {page}" link,
65
+ * drawn directly under the reply's text. Absent, nothing is drawn.
66
+ */
67
+ uiOffers?: ReactNode;
63
68
  }
64
69
  /**
65
70
  * Purely decorative "still typing" caret: hidden from assistive tech (the list's
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantMessageItem.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAyB3E,OAAO,KAAK,EACV,+BAA+B,EAG/B,OAAO,EACR,MAAM,iBAAiB,CAAC;AAMzB,UAAU,KAAK;IACb,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,uEAAuE;IACvE,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AAyCD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,EAY5B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CA2W1C,CAAC"}
1
+ {"version":3,"file":"AssistantMessageItem.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAyB3E,OAAO,KAAK,EACV,+BAA+B,EAG/B,OAAO,EACR,MAAM,iBAAiB,CAAC;AAMzB,UAAU,KAAK;IACb,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,uEAAuE;IACvE,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAyCD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,EAY5B,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CA4X1C,CAAC"}
@@ -8,103 +8,107 @@ import { AssistantMarkdownRenderer as a } from "./AssistantMarkdownRenderer.js";
8
8
  import { downloadAssistantArtifact as o } from "../api.js";
9
9
  import { languageConfirmationCopy as s } from "../appControls/languageReply.js";
10
10
  import { getAssistantDisplayContent as ee } from "../displayContent.js";
11
- import { AssistantToolActivity as te, parseToolActivity as ne } from "./AssistantToolActivity.js";
12
- import { usePrefersReducedMotion as c } from "./usePrefersReducedMotion.js";
13
- import { AssistantAvatar as re } from "./AssistantAvatar.js";
14
- import { useAssistantSpeech as l } from "./AssistantSpeechProvider.js";
11
+ import { AssistantToolActivity as te, parseToolActivity as c } from "./AssistantToolActivity.js";
12
+ import { usePrefersReducedMotion as l } from "./usePrefersReducedMotion.js";
13
+ import { AssistantAvatar as ne } from "./AssistantAvatar.js";
14
+ import { useAssistantSpeech as re } from "./AssistantSpeechProvider.js";
15
15
  import { AssistantMessageAttachments as ie } from "./AssistantMessageAttachments.js";
16
- import { AssistantPendingWriteCard as u } from "./AssistantPendingWriteCard.js";
17
- import { AssistantSpeakButton as d } from "./AssistantSpeakButton.js";
18
- import { SaveToFilesButton as f } from "../../../storage/SaveToFilesButton.js";
16
+ import { AssistantPendingWriteCard as ae } from "./AssistantPendingWriteCard.js";
17
+ import { AssistantSpeakButton as oe } from "./AssistantSpeakButton.js";
18
+ import { SaveToFilesButton as u } from "../../../storage/SaveToFilesButton.js";
19
19
  import "../../../storage/index.js";
20
- import { AssistantTurnActivity as ae } from "./AssistantTurnActivity.js";
21
- import { useMemo as p, useRef as oe, useState as m } from "react";
22
- import { Check as h, Copy as g, RotateCcw as se } from "lucide-react";
23
- import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
20
+ import { AssistantTurnActivity as se } from "./AssistantTurnActivity.js";
21
+ import { useMemo as d, useRef as f, useState as p } from "react";
22
+ import { Check as ce, Copy as m, RotateCcw as h } from "lucide-react";
23
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
24
24
  //#region src/shared/assistant/ui/AssistantMessageItem.tsx
25
- function b(e) {
25
+ function le(e) {
26
26
  return e.role === "assistant" ? e.metadata : void 0;
27
27
  }
28
- function ce(e) {
28
+ function y(e) {
29
29
  return e?.artifacts ?? [];
30
30
  }
31
- function x(e) {
31
+ function b(e) {
32
32
  return window.atob(e);
33
33
  }
34
- function S(e, t, n) {
35
- let r = x(e), i = new Uint8Array(r.length);
34
+ function x(e, t, n) {
35
+ let r = b(e), i = new Uint8Array(r.length);
36
36
  for (let e = 0; e < r.length; e += 1) i[e] = r.charCodeAt(e);
37
37
  let a = new Blob([i], { type: n }), o = URL.createObjectURL(a), s = document.createElement("a");
38
38
  s.href = o, s.download = t, document.body.appendChild(s), s.click(), document.body.removeChild(s), URL.revokeObjectURL(o);
39
39
  }
40
- var C = () => /* @__PURE__ */ v("span", {
40
+ var S = () => /* @__PURE__ */ _("span", {
41
41
  "aria-hidden": "true",
42
42
  "data-testid": "assistant-streaming-caret",
43
- className: "ml-0.5 inline-block h-4 w-1.5 rounded-sm bg-text-secondary align-middle" + (c() ? "" : " motion-safe:animate-pulse")
44
- }), w = ({ onApprovePendingWrite: c, approvingWriteRef: x = null, pendingWriteExpired: w = !1, pendingWriteApproved: le = !1, pendingWriteGoAheadSent: ue = !1, message: T, processing: E, isStreaming: D, isFailed: O, isLastAssistant: k, isNewestAssistant: A, onRetry: j, legacyWriteCardHidden: M = !1, writeProposals: N }) => {
45
- let P = r(), { locale: F } = e(), I = s(T, P), L = I ? I.translated ? F : "en" : T.metadata?.lang, R = I ? `${T.id}:language:${L}` : T.id, z = oe(R), B = n(), { getAccessToken: V, getWorkspaceToken: H } = t(), U = l(), [de, W] = m(null), [fe, G] = m(!1), K = T.role === "user", q = p(() => b(T), [T]), J = p(() => ce(q), [q]), Y = E === void 0 ? D ? {
43
+ className: "ml-0.5 inline-block h-4 w-1.5 rounded-sm bg-text-secondary align-middle" + (l() ? "" : " motion-safe:animate-pulse")
44
+ }), C = ({ onApprovePendingWrite: l, approvingWriteRef: b = null, pendingWriteExpired: C = !1, pendingWriteApproved: ue = !1, pendingWriteGoAheadSent: de = !1, message: w, processing: T, isStreaming: E, isFailed: D, isLastAssistant: O, isNewestAssistant: k, onRetry: A, legacyWriteCardHidden: j = !1, writeProposals: M, uiOffers: N }) => {
45
+ let P = r(), { locale: F } = e(), I = s(w, P), L = I ? I.translated ? F : "en" : w.metadata?.lang, R = I ? `${w.id}:language:${L}` : w.id, z = f(R), B = n(), { getAccessToken: V, getWorkspaceToken: fe } = t(), H = re(), [pe, U] = p(null), [W, G] = p(!1), K = w.role === "user", q = d(() => le(w), [w]), J = d(() => y(q), [q]), Y = T === void 0 ? E ? {
46
46
  status: "streaming",
47
- messageId: T.id,
48
- updatedAt: T.timestamp
49
- } : null : E, X = ee(T, Y, P), { steps: Z, text: Q } = p(() => K ? {
47
+ messageId: w.id,
48
+ updatedAt: w.timestamp
49
+ } : null : T, X = ee(w, Y, P), { steps: Z, text: Q } = d(() => K ? {
50
50
  steps: [],
51
- text: T.content
52
- } : X.kind === "output" ? ne(X.text) : {
51
+ text: w.content
52
+ } : X.kind === "output" ? c(X.text) : {
53
53
  steps: [],
54
54
  text: ""
55
55
  }, [
56
56
  K,
57
- T.content,
57
+ w.content,
58
58
  X.kind,
59
59
  X.text
60
- ]), pe = () => {
60
+ ]), me = () => {
61
61
  i(Q).then((e) => {
62
62
  e && (G(!0), setTimeout(() => G(!1), 2e3));
63
63
  });
64
- }, me = async (e) => {
64
+ }, he = async (e) => {
65
65
  if (!q?.sandboxId || !q.sessionId) return;
66
66
  let t = new URLSearchParams(window.location.search).get("workspace") ?? "";
67
67
  if (t) {
68
- W(e.path);
68
+ U(e.path);
69
69
  try {
70
70
  let n = await o(q.sandboxId, t, q.sessionId, e.path, q.mode, {
71
71
  accessToken: V(),
72
- workspaceToken: H()
72
+ workspaceToken: fe()
73
73
  });
74
- S(n.contentBase64, n.filename, n.mimeType);
74
+ x(n.contentBase64, n.filename, n.mimeType);
75
75
  } finally {
76
- W(null);
76
+ U(null);
77
77
  }
78
78
  }
79
- }, $ = new Date(T.timestamp).toLocaleTimeString([], {
79
+ }, $ = new Date(w.timestamp).toLocaleTimeString([], {
80
80
  hour: "2-digit",
81
81
  minute: "2-digit"
82
82
  });
83
- if (T.role === "system") return /* @__PURE__ */ v("div", {
83
+ if (w.role === "system") return /* @__PURE__ */ _("div", {
84
84
  className: "flex justify-center px-2 py-1",
85
85
  "data-testid": "assistant-message-system",
86
- children: /* @__PURE__ */ v("p", {
86
+ children: w.metadata?.localAction?.kind === "navigate" ? /* @__PURE__ */ _("div", {
87
+ className: "max-w-[90%] break-words rounded-lg border border-border-subtle bg-bg-sunken/60 px-3 py-2 text-center text-xs leading-relaxed text-text-muted",
88
+ "data-testid": "assistant-message-system-navigation",
89
+ children: /* @__PURE__ */ _(a, { content: w.content })
90
+ }) : /* @__PURE__ */ _("p", {
87
91
  className: "max-w-[90%] whitespace-pre-wrap break-words rounded-lg border border-border-subtle bg-bg-sunken/60 px-3 py-2 text-center text-xs leading-relaxed text-text-muted",
88
- children: T.content
92
+ children: w.content
89
93
  })
90
94
  });
91
95
  if (K) {
92
- let e = T.metadata?.attachments ?? [];
93
- return /* @__PURE__ */ v("div", {
96
+ let e = w.metadata?.attachments ?? [];
97
+ return /* @__PURE__ */ _("div", {
94
98
  className: "flex justify-end",
95
99
  "data-testid": "assistant-message-user",
96
- children: /* @__PURE__ */ y("div", {
100
+ children: /* @__PURE__ */ v("div", {
97
101
  className: "max-w-[85%]",
98
102
  children: [
99
- T.content && /* @__PURE__ */ v("div", {
103
+ w.content && /* @__PURE__ */ _("div", {
100
104
  className: "rounded-2xl rounded-br-md bg-action-primary-bg/12 px-3.5 py-2.5 text-[15px] leading-relaxed text-text-primary",
101
- children: /* @__PURE__ */ v("p", {
105
+ children: /* @__PURE__ */ _("p", {
102
106
  className: "whitespace-pre-wrap",
103
- children: T.content
107
+ children: w.content
104
108
  })
105
109
  }),
106
- /* @__PURE__ */ v(ie, { attachments: e }),
107
- /* @__PURE__ */ v("div", {
110
+ /* @__PURE__ */ _(ie, { attachments: e }),
111
+ /* @__PURE__ */ _("div", {
108
112
  className: "mt-1 pr-1 text-right text-[10px] text-text-muted",
109
113
  children: $
110
114
  })
@@ -112,46 +116,47 @@ var C = () => /* @__PURE__ */ v("span", {
112
116
  })
113
117
  });
114
118
  }
115
- return /* @__PURE__ */ y("div", {
119
+ return /* @__PURE__ */ v("div", {
116
120
  className: "flex gap-2.5",
117
121
  "data-testid": "assistant-message-assistant",
118
- children: [/* @__PURE__ */ v(re, { active: D }), /* @__PURE__ */ y("div", {
122
+ children: [/* @__PURE__ */ _(ne, { active: E }), /* @__PURE__ */ v("div", {
119
123
  className: "min-w-0 flex-1",
120
124
  children: [
121
- Z.length > 0 && /* @__PURE__ */ v(te, { steps: Z }),
122
- q?.pendingWrite && !M ? /* @__PURE__ */ v(u, {
125
+ Z.length > 0 && /* @__PURE__ */ _(te, { steps: Z }),
126
+ q?.pendingWrite && !j ? /* @__PURE__ */ _(ae, {
123
127
  pendingWrite: q.pendingWrite,
124
- onApprove: c,
125
- approving: x !== null && x === q.pendingWrite.reference,
126
- expired: w,
127
- approved: le,
128
- goAheadSent: ue,
129
- awaitingTurn: !!D
128
+ onApprove: l,
129
+ approving: b !== null && b === q.pendingWrite.reference,
130
+ expired: C,
131
+ approved: ue,
132
+ goAheadSent: de,
133
+ awaitingTurn: !!E
130
134
  }) : null,
131
- N,
132
- Q && /* @__PURE__ */ v("div", {
135
+ M,
136
+ Q && /* @__PURE__ */ _("div", {
133
137
  className: "text-[15px] leading-relaxed text-text-primary",
134
- children: /* @__PURE__ */ v(a, { content: Q })
138
+ children: /* @__PURE__ */ _(a, { content: Q })
135
139
  }),
136
- X.kind === "working" && /* @__PURE__ */ v(ae, { processing: Y }),
137
- X.kind === "stopped" && /* @__PURE__ */ v("p", {
140
+ X.kind === "working" && /* @__PURE__ */ _(se, { processing: Y }),
141
+ X.kind === "stopped" && /* @__PURE__ */ _("p", {
138
142
  className: "text-[15px] leading-relaxed text-text-secondary",
139
143
  "data-testid": "assistant-turn-stopped",
140
144
  children: X.text
141
145
  }),
142
- D && /* @__PURE__ */ v(C, {}),
143
- J.length > 0 && q?.sandboxId && q.sessionId && /* @__PURE__ */ y("div", {
146
+ E && /* @__PURE__ */ _(S, {}),
147
+ N,
148
+ J.length > 0 && q?.sandboxId && q.sessionId && /* @__PURE__ */ v("div", {
144
149
  className: "mt-3 space-y-2 border-t border-border-subtle pt-2",
145
- children: [/* @__PURE__ */ v("p", {
150
+ children: [/* @__PURE__ */ _("p", {
146
151
  className: "text-[10px] font-medium uppercase tracking-wide text-text-muted",
147
152
  children: P("message.downloads", "Downloads")
148
- }), /* @__PURE__ */ v("div", {
153
+ }), /* @__PURE__ */ _("div", {
149
154
  className: "flex flex-wrap gap-2",
150
155
  children: J.map((e) => {
151
156
  let t = `${e.name}${e.type === "directory" ? ".tar.gz" : ""}`;
152
- return /* @__PURE__ */ v(f, {
157
+ return /* @__PURE__ */ _(u, {
153
158
  label: t,
154
- disabled: de === e.path,
159
+ disabled: pe === e.path,
155
160
  testId: `assistant-artifact-${e.name}`,
156
161
  source: {
157
162
  kind: "sandbox-artifact",
@@ -161,58 +166,58 @@ var C = () => /* @__PURE__ */ v("span", {
161
166
  filename: t
162
167
  },
163
168
  defaultFolderPath: B.artifacts,
164
- onDownload: () => me(e)
169
+ onDownload: () => he(e)
165
170
  }, e.path);
166
171
  })
167
172
  })]
168
173
  }),
169
- !D && (Q || O || k) && /* @__PURE__ */ y("div", {
174
+ !E && (Q || D || O) && /* @__PURE__ */ v("div", {
170
175
  className: "mt-1.5 flex items-center gap-1",
171
176
  children: [
172
- Q && /* @__PURE__ */ v("button", {
177
+ Q && /* @__PURE__ */ _("button", {
173
178
  type: "button",
174
- onClick: pe,
179
+ onClick: me,
175
180
  className: "flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] text-text-muted transition-colors hover:bg-bg-elevated hover:text-text-secondary",
176
181
  "aria-label": P("message.copy", "Copy message"),
177
182
  "data-testid": "assistant-message-copy",
178
- children: fe ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(h, { className: "size-3 text-status-success-text" }), /* @__PURE__ */ v("span", { children: P("message.copied", "Copied") })] }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(g, { className: "size-3" }), /* @__PURE__ */ v("span", { children: P("message.copyShort", "Copy") })] })
183
+ children: W ? /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(ce, { className: "size-3 text-status-success-text" }), /* @__PURE__ */ _("span", { children: P("message.copied", "Copied") })] }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(m, { className: "size-3" }), /* @__PURE__ */ _("span", { children: P("message.copyShort", "Copy") })] })
179
184
  }),
180
- Q && U.isSupported && (() => {
181
- let e = U.blockedFor(R) ?? U.disabledReason, t = U.isSpeaking(T.id) || U.isSpeaking(z.current), n = !!e && !!A, r = n ? `speak-reason-${T.id}` : void 0;
182
- return /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(d, {
185
+ Q && H.isSupported && (() => {
186
+ let e = H.blockedFor(R) ?? H.disabledReason, t = H.isSpeaking(w.id) || H.isSpeaking(z.current), n = !!e && !!k, r = n ? `speak-reason-${w.id}` : void 0;
187
+ return /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(oe, {
183
188
  isSpeaking: t,
184
189
  disabled: e !== null,
185
190
  disabledReason: e,
186
191
  describedById: r,
187
192
  onClick: () => {
188
193
  if (t) {
189
- U.stop();
194
+ H.stop();
190
195
  return;
191
196
  }
192
- z.current = R, U.toggle(R, Q, L);
197
+ z.current = R, H.toggle(R, Q, L);
193
198
  }
194
- }), n && /* @__PURE__ */ v("span", {
199
+ }), n && /* @__PURE__ */ _("span", {
195
200
  id: r,
196
201
  className: "text-[11px] text-text-muted",
197
202
  "data-testid": "assistant-speak-reason",
198
203
  children: e
199
204
  })] });
200
205
  })(),
201
- (O || k) && j && !T.metadata?.localAction && /* @__PURE__ */ y("button", {
206
+ (D || O) && A && !w.metadata?.localAction && /* @__PURE__ */ v("button", {
202
207
  type: "button",
203
- onClick: j,
204
- className: `flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] transition-colors hover:bg-bg-elevated ${O ? "text-status-error-text" : "text-text-muted hover:text-text-secondary"}`,
208
+ onClick: A,
209
+ className: `flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] transition-colors hover:bg-bg-elevated ${D ? "text-status-error-text" : "text-text-muted hover:text-text-secondary"}`,
205
210
  "aria-label": P("message.retry", "Retry"),
206
211
  "data-testid": "assistant-message-retry",
207
- children: [/* @__PURE__ */ v(se, { className: "size-3" }), /* @__PURE__ */ v("span", { children: P("message.retry", "Retry") })]
212
+ children: [/* @__PURE__ */ _(h, { className: "size-3" }), /* @__PURE__ */ _("span", { children: P("message.retry", "Retry") })]
208
213
  })
209
214
  ]
210
215
  }),
211
- /* @__PURE__ */ y("div", {
216
+ /* @__PURE__ */ v("div", {
212
217
  className: "mt-1 text-[10px] text-text-muted",
213
- children: [$, T.tokens && /* @__PURE__ */ y("span", {
218
+ children: [$, w.tokens && /* @__PURE__ */ v("span", {
214
219
  className: "ml-2",
215
- children: [T.tokens.input + T.tokens.output, " tokens"]
220
+ children: [w.tokens.input + w.tokens.output, " tokens"]
216
221
  })]
217
222
  })
218
223
  ]
@@ -220,4 +225,4 @@ var C = () => /* @__PURE__ */ v("span", {
220
225
  });
221
226
  };
222
227
  //#endregion
223
- export { w as AssistantMessageItem, C as StreamingCaret };
228
+ export { C as AssistantMessageItem, S as StreamingCaret };
@@ -48,6 +48,11 @@ interface Props {
48
48
  * Approve (and no "go ahead") can come from it. Absent, nothing is hidden.
49
49
  */
50
50
  hidesLegacyWriteCard?: (message: Message) => boolean;
51
+ /**
52
+ * ★ BOFF-8060 (N3): a reply's "Open {page}" offer link, drawn under its
53
+ * text. Absent, none are drawn.
54
+ */
55
+ renderUiOffers?: (message: Message) => ReactNode;
51
56
  }
52
57
  /** At most one auto-scroll per this long while a reply is streaming in live. */
53
58
  export declare const LIVE_SCROLL_GAP_MS = 250;
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantMessageList.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGnE,OAAO,KAAK,EAAE,+BAA+B,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAShF,UAAU,KAAK;IACb;;;OAGG;IACH,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;IACvD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;CACtD;AAED,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CAyJ1C,CAAC"}
1
+ {"version":3,"file":"AssistantMessageList.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGnE,OAAO,KAAK,EAAE,+BAA+B,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAShF,UAAU,KAAK;IACb;;;OAGG;IACH,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACpD,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;IACvD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IACrD;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;CAClD;AAED,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,KAAK,CA2J1C,CAAC"}
@@ -6,61 +6,62 @@ import { useLiveOverlay as o } from "../liveOverlay.js";
6
6
  import { useEffect as s, useRef as c } from "react";
7
7
  import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
8
  //#region src/shared/assistant/ui/AssistantMessageList.tsx
9
- var d = 250, f = ({ onApprovePendingWrite: d, approvingWriteRef: f = null, approvableMessageId: p = null, retryableMessageId: m = null, messages: h, processing: g, streamingMessageId: _, failedMessageId: v, onRetry: y, renderWriteProposals: b, hidesLegacyWriteCard: x }) => {
10
- let S = e(), C = (() => {
9
+ var d = 250, f = ({ onApprovePendingWrite: d, approvingWriteRef: f = null, approvableMessageId: p = null, retryableMessageId: m = null, messages: h, processing: g, streamingMessageId: _, failedMessageId: v, onRetry: y, renderWriteProposals: b, hidesLegacyWriteCard: x, renderUiOffers: S }) => {
10
+ let C = e(), w = (() => {
11
11
  for (let e = h.length - 1; e >= 0; --e) if (h[e].role === "assistant") return h[e].id;
12
12
  return null;
13
- })(), w = n(h), T = r(h), E = t(h), D = c(null), O = c(null);
13
+ })(), T = n(h), E = r(h), D = t(h), O = c(null), k = c(null);
14
14
  s(() => {
15
- let e = O.current;
15
+ let e = k.current;
16
16
  if (e && e.scrollHeight - e.scrollTop - e.clientHeight < 100) {
17
17
  let e = i();
18
- D.current?.scrollIntoView({ behavior: e ? "auto" : "smooth" });
18
+ O.current?.scrollIntoView({ behavior: e ? "auto" : "smooth" });
19
19
  }
20
20
  }, [h, _]);
21
- let k = o((e) => e.byMessageId), A = _ === null ? null : k[_]?.content ?? null, j = c(0), M = c(null);
21
+ let A = o((e) => e.byMessageId), j = _ === null ? null : A[_]?.content ?? null, M = c(0), N = c(null);
22
22
  return s(() => {
23
- if (A === null) return;
23
+ if (j === null) return;
24
24
  let e = () => {
25
- M.current = null, j.current = Date.now();
26
- let e = O.current;
27
- e && e.scrollHeight - e.scrollTop - e.clientHeight < 100 && D.current?.scrollIntoView({ behavior: "auto" });
28
- }, t = j.current + 250 - Date.now();
29
- t <= 0 ? e() : M.current === null && (M.current = setTimeout(e, t));
30
- }, [A]), s(() => () => {
31
- M.current !== null && clearTimeout(M.current);
25
+ N.current = null, M.current = Date.now();
26
+ let e = k.current;
27
+ e && e.scrollHeight - e.scrollTop - e.clientHeight < 100 && O.current?.scrollIntoView({ behavior: "auto" });
28
+ }, t = M.current + 250 - Date.now();
29
+ t <= 0 ? e() : N.current === null && (N.current = setTimeout(e, t));
30
+ }, [j]), s(() => () => {
31
+ N.current !== null && clearTimeout(N.current);
32
32
  }, []), s(() => {
33
- D.current?.scrollIntoView({ behavior: "instant" });
33
+ O.current?.scrollIntoView({ behavior: "instant" });
34
34
  }, []), /* @__PURE__ */ u("div", {
35
- ref: O,
35
+ ref: k,
36
36
  className: "scrollbar-slim flex-1 space-y-4 overflow-y-auto p-3",
37
37
  role: "log",
38
38
  "aria-live": "polite",
39
39
  "aria-relevant": "additions",
40
40
  "aria-busy": _ !== null,
41
- "aria-label": S("messageList.ariaLabel", "Conversation messages"),
41
+ "aria-label": C("messageList.ariaLabel", "Conversation messages"),
42
42
  children: [h.map((e) => {
43
- let t = w.has(e.id), n = !t && (e.id === p || e.id === _);
43
+ let t = T.has(e.id), n = !t && (e.id === p || e.id === _);
44
44
  return /* @__PURE__ */ l(a, {
45
45
  onApprovePendingWrite: n ? d : void 0,
46
46
  approvingWriteRef: f,
47
47
  pendingWriteApproved: t,
48
- pendingWriteGoAheadSent: T.has(e.id),
48
+ pendingWriteGoAheadSent: E.has(e.id),
49
49
  pendingWriteExpired: !t && !n,
50
- message: e.id === _ && A !== null ? {
50
+ message: e.id === _ && j !== null ? {
51
51
  ...e,
52
- content: A
52
+ content: j
53
53
  } : e,
54
54
  processing: g,
55
55
  isStreaming: e.id === _,
56
56
  isFailed: e.id === v,
57
57
  isLastAssistant: e.id === m,
58
- isNewestAssistant: e.id === C,
59
- onRetry: E.has(e.id) ? void 0 : y,
58
+ isNewestAssistant: e.id === w,
59
+ onRetry: D.has(e.id) ? void 0 : y,
60
60
  legacyWriteCardHidden: x?.(e) ?? !1,
61
- writeProposals: b?.(e)
61
+ writeProposals: b?.(e),
62
+ uiOffers: S?.(e)
62
63
  }, e.id);
63
- }), /* @__PURE__ */ l("div", { ref: D })]
64
+ }), /* @__PURE__ */ l("div", { ref: O })]
64
65
  });
65
66
  };
66
67
  //#endregion
@@ -0,0 +1,45 @@
1
+ import { FC, MouseEvent } from 'react';
2
+ /**
3
+ * ★ BOFF-8060 (N3, design §4): the page the model offered, as a link under the
4
+ * reply — never a confirm button.
5
+ *
6
+ * - A real `<a href>` to the REGISTRY path: middle-click and Ctrl/Cmd/Shift/Alt
7
+ * click open it the browser's own way, at sidebar parity, and are not
8
+ * intercepted (nor recorded).
9
+ * - A plain click is handed to the panel (`onOpen`), which re-validates the
10
+ * offer against the live registry and navigates through its registered
11
+ * handler (`runNavigation`). This component never navigates by itself.
12
+ * - The text is "Open {page}" with the REGISTRY label, never the model's words.
13
+ * - Announced once through a polite live region; focus is never moved, and
14
+ * nothing animates.
15
+ * - Semantic tokens only (rule 7); logical padding; the arrow mirrors in RTL.
16
+ */
17
+ export interface AssistantUiOfferLinkItem {
18
+ offerId: string;
19
+ /** The registry label. */
20
+ label: string;
21
+ /** The registry path (`navTargetPath`). */
22
+ path: string;
23
+ }
24
+ export interface AssistantUiOfferLinkProps extends AssistantUiOfferLinkItem {
25
+ /** A plain, unmodified primary click, with the label the link showed. */
26
+ onOpen: (offerId: string, label: string) => void;
27
+ }
28
+ /** A click the browser should handle itself: new tab, new window, download. */
29
+ export declare function isModifiedClick(event: Pick<MouseEvent, "button" | "metaKey" | "ctrlKey" | "shiftKey" | "altKey">): boolean;
30
+ export declare const UI_OFFER_LINK_CLASS = "inline-flex items-center gap-2 min-h-11 ps-3 pe-3 rounded-md bg-bg-elevated text-text-link border border-border-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-strong";
31
+ export declare const AssistantUiOfferLink: FC<AssistantUiOfferLinkProps>;
32
+ export interface AssistantUiOfferLinksProps {
33
+ offers: readonly AssistantUiOfferLinkItem[];
34
+ /** `read`: "…or “yes”" may be said; `link`: tap or "take me there" only. */
35
+ hint: "read" | "link";
36
+ onOpen: (offerId: string, label: string) => void;
37
+ }
38
+ /**
39
+ * The reply's offers and the hint under them. Renders nothing for no offer.
40
+ *
41
+ * The live region starts empty and is filled once after mount, so a screen
42
+ * reader announces it once; it is never changed again for the same offers.
43
+ */
44
+ export declare const AssistantUiOfferLinks: FC<AssistantUiOfferLinksProps>;
45
+ //# sourceMappingURL=AssistantUiOfferLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantUiOfferLink.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantUiOfferLink.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AAUtE;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACzE,yEAAyE;IACzE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClD;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,CACT,UAAU,EACV,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CACzD,GACA,OAAO,CAQT;AAED,eAAO,MAAM,mBAAmB,6MAC4K,CAAC;AAE7M,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,yBAAyB,CAuB9D,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC5C,4EAA4E;IAC5E,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClD;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC,0BAA0B,CA+ChE,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { useAssistantTr as e } from "../i18n.js";
2
+ import { offerAnnounceText as t, offerHintText as n, offerOpenText as r } from "../localUiCommandCopy.js";
3
+ import { useEffect as i, useState as a } from "react";
4
+ import { ArrowRight as o } from "lucide-react";
5
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ //#region src/shared/assistant/ui/AssistantUiOfferLink.tsx
7
+ function l(e) {
8
+ return e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey;
9
+ }
10
+ var u = "inline-flex items-center gap-2 min-h-11 ps-3 pe-3 rounded-md bg-bg-elevated text-text-link border border-border-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-strong", d = ({ offerId: t, label: n, path: i, onOpen: a }) => /* @__PURE__ */ c("a", {
11
+ href: i,
12
+ className: u,
13
+ "data-testid": "assistant-ui-offer-link",
14
+ "data-offer-id": t,
15
+ onClick: (e) => {
16
+ e.defaultPrevented || l(e) || (e.preventDefault(), a(t, n));
17
+ },
18
+ children: [/* @__PURE__ */ s("span", { children: r(e(), n) }), /* @__PURE__ */ s(o, {
19
+ className: "size-4 rtl:rotate-180",
20
+ "aria-hidden": "true"
21
+ })]
22
+ }), f = ({ offers: r, hint: o, onOpen: l }) => {
23
+ let u = e(), f = r[0], p = f ? t(u, f.label) : "", m = f?.offerId ?? "", [h, g] = a({
24
+ key: "",
25
+ text: ""
26
+ });
27
+ return i(() => {
28
+ m && g((e) => e.key === m ? e : {
29
+ key: m,
30
+ text: p
31
+ });
32
+ }, [m, p]), f ? /* @__PURE__ */ c("div", {
33
+ className: "mt-2 flex flex-col items-start gap-1",
34
+ "data-testid": "assistant-ui-offers",
35
+ children: [
36
+ r.map((e) => /* @__PURE__ */ s(d, {
37
+ ...e,
38
+ onOpen: l
39
+ }, e.offerId)),
40
+ /* @__PURE__ */ s("p", {
41
+ className: "text-xs text-text-secondary",
42
+ "data-testid": "assistant-ui-offer-hint",
43
+ children: n(u, o)
44
+ }),
45
+ /* @__PURE__ */ s("span", {
46
+ className: "sr-only",
47
+ role: "status",
48
+ "aria-live": "polite",
49
+ "data-testid": "assistant-ui-offer-announce",
50
+ children: h.key === m ? h.text : ""
51
+ })
52
+ ]
53
+ }) : null;
54
+ };
55
+ //#endregion
56
+ export { d as AssistantUiOfferLink, f as AssistantUiOfferLinks, u as UI_OFFER_LINK_CLASS, l as isModifiedClick };
@@ -235,6 +235,15 @@ export interface AssistantNavigateOptions {
235
235
  * for renders beneath the sheet, so it steps aside — but closing unmounts the
236
236
  * panel, and collapsing is a class swap on the same node. On desktop the panel
237
237
  * sits beside the page and nothing moves.
238
+ *
239
+ * ★★ BOFF-8060 (N4, the leave-page guard): a navigation the ASSISTANT started
240
+ * is refused while the page holds unsaved changes (`useUnsavedChanges`), and
241
+ * the refusal names them so the panel can say where. Nothing is routed, so the
242
+ * draft stays exactly as it was. The person's own navigation (`initiator:
243
+ * "person"`, a link they clicked) is never refused here — sidebar parity — and
244
+ * neither is a shell-less target, which opens in a new tab and leaves this
245
+ * page, and its draft, alone. A caller that does not say who started it is
246
+ * treated as the assistant.
238
247
  */
239
248
  export declare function createAssistantNavigate(options: AssistantNavigateOptions): AssistantNavigate;
240
249
  /** The quota wall a `<product>:quota-exhausted` event names. */