@burdenoff/fe-libs 2026.1029.3 → 2026.1029.5

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.
@@ -407,6 +407,16 @@ export interface AssistantModeConfig {
407
407
  id: AssistantMode;
408
408
  label: string;
409
409
  description: string;
410
+ /**
411
+ * One line under the mode's name in the COMPACT presentation's Mode menu
412
+ * (BOFF-10055): what the mode does, in the product's confirmed wording.
413
+ * Translated at display time under `modeShortDescription.<id>`
414
+ * (`productCopy.ts`), which `assistantI18nSeedCopy` emits.
415
+ *
416
+ * ★ Not `description`: that is the long description, already seeded under
417
+ * `modeDescription.<id>` with a different meaning. Absent: no line.
418
+ */
419
+ shortDescription?: string;
410
420
  suggestions: readonly string[];
411
421
  /** Only offered to a workspace admin, `sandbox:admin` here (see useAssistantApiAccess). */
412
422
  requiresAdmin?: boolean;
@@ -41,7 +41,8 @@ import { AssistantModeConfig } from './conversation';
41
41
  * - A mode's `description` and `suggestions`, and the page label from
42
42
  * `describeCurrentPage()`, are the product's words too. Since BOFF-8060 (F5)
43
43
  * they are translated AT DISPLAY TIME under keys derived from their English
44
- * (`suggestion.<mode>.<slug>`, `modeDescription.<mode>`, `page.<slug>`; see
44
+ * (`suggestion.<mode>.<slug>`, `modeDescription.<mode>`,
45
+ * `modeShortDescription.<mode>`, `page.<slug>`; see
45
46
  * `productCopy.ts`). They stay out of the shared manifest, and
46
47
  * `assistantI18nSeedCopy` emits them from the product's own modes and the
47
48
  * `pageLabels` it passes — so a product passes its ENGLISH, never a
@@ -352,6 +353,8 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
352
353
  readonly 'composer.dropHere': "Drop files to attach";
353
354
  readonly 'composer.hint': "Enter to send · Shift+Enter for newline · drag or paste files to attach";
354
355
  readonly 'composer.placeholder': "Ask me anything…";
356
+ readonly 'composer.stop': "Stop the reply";
357
+ readonly 'composer.stopped': "Reply stopped.";
355
358
  readonly 'composer.voiceStart': "Dictate a message";
356
359
  readonly 'composer.voiceStarting': "Starting the microphone — tap to cancel";
357
360
  readonly 'composer.voiceStop': "Stop dictation";
@@ -500,9 +503,19 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
500
503
  readonly 'message.stopSpeaking': "Stop reading aloud";
501
504
  readonly 'message.stopSpeakingShort': "Stop";
502
505
  readonly 'messageList.ariaLabel': "Conversation messages";
506
+ readonly 'modeMenu.disabledBusy': "Mode can't change right now.";
507
+ readonly 'modeMenu.disabledReplying': "Mode can't change while the assistant is replying. Stop the reply to change it.";
508
+ readonly 'modeMenu.staticLabel': "Mode: {{mode}}";
509
+ readonly 'modeMenu.trigger': "Assistant mode: {{mode}}";
503
510
  readonly 'modeSelector.ariaLabel': "Select assistant mode";
504
511
  readonly 'modeSelector.auto': "Auto";
505
512
  readonly 'modeSelector.autoHint': "Answers your questions, and proposes changes for you to approve";
513
+ readonly 'modeSwitch.confirm': "Switch to {{mode}}";
514
+ readonly 'modeSwitch.draftKept': "What you typed stays in the message box.";
515
+ readonly 'modeSwitch.intro': "If you switch now:";
516
+ readonly 'modeSwitch.newConversation': "{{mode}} continues in a new conversation. This one stays in History.";
517
+ readonly 'modeSwitch.title': "Switch to {{mode}}?";
518
+ readonly 'modeSwitch.withdrawn': "{{count}} change(s) waiting for your approval will be withdrawn.";
506
519
  readonly 'offer.announce': "Suggested page: {{page}}";
507
520
  readonly 'offer.expired': "That suggestion has expired. Ask again, or use the menu.";
508
521
  readonly 'offer.hintLink': "Tap, or say “take me there”.";
@@ -708,7 +721,7 @@ export declare const ASSISTANT_I18N_MANIFEST: Readonly<Record<string, string>>;
708
721
  * The part of a mode that decides its keys and English: the pill, plus (when
709
722
  * given) its description and suggestion chips (BOFF-8060, F5).
710
723
  */
711
- export type AssistantModeCopySource = Pick<AssistantModeConfig, 'id' | 'label' | 'producesArtifacts'> & Partial<Pick<AssistantModeConfig, 'description' | 'suggestions'>>;
724
+ export type AssistantModeCopySource = Pick<AssistantModeConfig, 'id' | 'label' | 'producesArtifacts'> & Partial<Pick<AssistantModeConfig, 'description' | 'shortDescription' | 'suggestions'>>;
712
725
  /**
713
726
  * `mode.<id>` for each mode a product offers, in the product's own words.
714
727
  *
@@ -246,6 +246,8 @@ var g = {
246
246
  "composer.dropHere": "Drop files to attach",
247
247
  "composer.hint": "Enter to send · Shift+Enter for newline · drag or paste files to attach",
248
248
  "composer.placeholder": "Ask me anything…",
249
+ "composer.stop": "Stop the reply",
250
+ "composer.stopped": "Reply stopped.",
249
251
  "composer.voiceStart": "Dictate a message",
250
252
  "composer.voiceStarting": "Starting the microphone — tap to cancel",
251
253
  "composer.voiceStop": "Stop dictation",
@@ -394,9 +396,19 @@ var g = {
394
396
  "message.stopSpeaking": "Stop reading aloud",
395
397
  "message.stopSpeakingShort": "Stop",
396
398
  "messageList.ariaLabel": "Conversation messages",
399
+ "modeMenu.disabledBusy": "Mode can't change right now.",
400
+ "modeMenu.disabledReplying": "Mode can't change while the assistant is replying. Stop the reply to change it.",
401
+ "modeMenu.staticLabel": "Mode: {{mode}}",
402
+ "modeMenu.trigger": "Assistant mode: {{mode}}",
397
403
  "modeSelector.ariaLabel": "Select assistant mode",
398
404
  "modeSelector.auto": "Auto",
399
405
  "modeSelector.autoHint": "Answers your questions, and proposes changes for you to approve",
406
+ "modeSwitch.confirm": "Switch to {{mode}}",
407
+ "modeSwitch.draftKept": "What you typed stays in the message box.",
408
+ "modeSwitch.intro": "If you switch now:",
409
+ "modeSwitch.newConversation": "{{mode}} continues in a new conversation. This one stays in History.",
410
+ "modeSwitch.title": "Switch to {{mode}}?",
411
+ "modeSwitch.withdrawn": "{{count}} change(s) waiting for your approval will be withdrawn.",
400
412
  "offer.announce": "Suggested page: {{page}}",
401
413
  "offer.expired": "That suggestion has expired. Ask again, or use the menu.",
402
414
  "offer.hintLink": "Tap, or say “take me there”.",
@@ -168,6 +168,10 @@ export * from './preview/bridgeHostContext';
168
168
  export * from './preview/AssistantPreviewOverlay';
169
169
  export * from './ui/composer/AssistantLanguageSelector';
170
170
  export * from './ui/composer/AssistantModeSelector';
171
+ export * from './ui/composer/AssistantModeMenu';
172
+ export * from './ui/composer/modeMenuKeys';
173
+ export * from './ui/composer/modeSwitchPlan';
174
+ export * from './ui/composer/imeEnter';
171
175
  export * from './ui/composer/AssistantVoiceButton';
172
176
  export * from './ui/composer/AssistantVoiceSelector';
173
177
  export * from './ui/composer/AssistantVoiceGraceSelector';
@@ -13,6 +13,7 @@
13
13
  *
14
14
  * suggestion.<modeId>.<slug(english)> a suggestion chip
15
15
  * modeDescription.<modeId> a mode's description
16
+ * modeShortDescription.<modeId> a mode's one-line Mode-menu text
16
17
  * page.<slug(english)> a page label
17
18
  *
18
19
  * `slug` is the lower-camel ASCII of the English ("Show overdue tasks" ->
@@ -62,6 +63,12 @@ export declare function assistantCopySlug(english: string): string | null;
62
63
  export declare function assistantSuggestionKey(modeId: string, english: string): string | null;
63
64
  /** `modeDescription.<modeId>`. */
64
65
  export declare function assistantModeDescriptionKey(modeId: string): string;
66
+ /**
67
+ * `modeShortDescription.<modeId>`: the one line under a mode in the compact
68
+ * Mode menu (BOFF-10055). A separate family from `modeDescription.<modeId>`,
69
+ * which holds the long description and is already seeded with that meaning.
70
+ */
71
+ export declare function assistantModeShortDescriptionKey(modeId: string): string;
65
72
  /** `page.<slug>`, or null for a label that cannot be keyed. */
66
73
  export declare function assistantPageLabelKey(english: string): string | null;
67
74
  /** Translate one piece of product copy, or return it unchanged when unkeyable. */
@@ -71,6 +78,7 @@ export declare function assistantProductCopyEntries(args: {
71
78
  modes: readonly {
72
79
  id: string;
73
80
  description?: string;
81
+ shortDescription?: string;
74
82
  suggestions?: readonly string[];
75
83
  }[];
76
84
  pageLabels?: readonly string[];
@@ -21,13 +21,16 @@ function o(e) {
21
21
  return `modeDescription.${e}`;
22
22
  }
23
23
  function s(e) {
24
+ return `modeShortDescription.${e}`;
25
+ }
26
+ function c(e) {
24
27
  let t = i(e);
25
28
  return t === null ? null : `page.${t}`;
26
29
  }
27
- function c(e, t, n) {
30
+ function l(e, t, n) {
28
31
  return t === null ? n : e(t, n);
29
32
  }
30
- function l(e) {
33
+ function u(e) {
31
34
  let t = {}, n = (e, n) => {
32
35
  if (e === null) return;
33
36
  let r = t[e];
@@ -35,11 +38,11 @@ function l(e) {
35
38
  t[e] = n;
36
39
  };
37
40
  for (let t of e.modes) {
38
- t.description && t.description.trim() !== "" && n(o(t.id), t.description);
41
+ t.description && t.description.trim() !== "" && n(o(t.id), t.description), t.shortDescription && t.shortDescription.trim() !== "" && n(s(t.id), t.shortDescription);
39
42
  for (let e of t.suggestions ?? []) n(a(t.id, e), e);
40
43
  }
41
- for (let t of e.pageLabels ?? []) n(s(t), t);
44
+ for (let t of e.pageLabels ?? []) n(c(t), t);
42
45
  return t;
43
46
  }
44
47
  //#endregion
45
- export { e as MAX_COPY_SLUG_LENGTH, i as assistantCopySlug, o as assistantModeDescriptionKey, s as assistantPageLabelKey, l as assistantProductCopyEntries, a as assistantSuggestionKey, c as translateProductCopy };
48
+ export { e as MAX_COPY_SLUG_LENGTH, i as assistantCopySlug, o as assistantModeDescriptionKey, s as assistantModeShortDescriptionKey, c as assistantPageLabelKey, u as assistantProductCopyEntries, a as assistantSuggestionKey, l as translateProductCopy };
@@ -4,6 +4,7 @@ import { WorkspaceWriteAccess } from '../useWorkspaceWriteAccess';
4
4
  import { AssistantNavigate } from './AssistantMarkdownRenderer';
5
5
  import { AssistantLiveAvatarSeam } from './assistantLiveAvatarSeam';
6
6
  import { AssistantPreviewController } from '../preview/types';
7
+ import { AssistantPresentation } from './composer/AssistantComposer';
7
8
  export type AIAssistantPanelProps = PanelProps & {
8
9
  /** When provided, renders a dock/float toggle in the header. */
9
10
  docked?: boolean;
@@ -104,6 +105,14 @@ export type AIAssistantPanelProps = PanelProps & {
104
105
  * a prop chain. See `assistantLiveAvatarSeam`.
105
106
  */
106
107
  liveAvatar?: AssistantLiveAvatarSeam;
108
+ /**
109
+ * BOFF-10055: how the assistant is presented. Absent or `classic`: exactly the
110
+ * panel of before. `compact` opts this product into the redesigned composer
111
+ * (Slice 1: the card, the Mode menu, Stop, IME-safe Enter and the mode-switch
112
+ * confirmation). A product passes it through `AssistantOverlay`'s
113
+ * `panelProps`, behind its own flag.
114
+ */
115
+ presentation?: AssistantPresentation;
107
116
  };
108
117
  /**
109
118
  * The panel.
@@ -5,47 +5,47 @@ import { useAssistantTr as n } from "../i18n.js";
5
5
  import { useAssistantStore as r } from "../store.js";
6
6
  import { AssistantSpeechProvider as i } from "./AssistantSpeechProvider.js";
7
7
  import { ASSISTANT_OVERLAY_ATTR as a, ASSISTANT_PANEL_ATTR as o, activePanelOverlay as s, escapeDismisses as c, nextPanelOverlays as l } from "./panelOverlays.js";
8
- import { LiveAvatarSeamProvider as u, LiveAvatarTabContext as d, useLiveAvatarSpeechEngine as f, useLiveAvatarTabOffered as te } from "./assistantLiveAvatarSeam.js";
9
- import { AssistantPreviewProvider as ne } from "../preview/context.js";
10
- import { useAssistantPreviewBridge as re } from "../preview/useAssistantPreviewBridge.js";
11
- import { AssistantConnectSourceProvider as ie } from "./composer/connectSource.js";
12
- import { hasAssistantPreviewSurface as ae } from "../preview/types.js";
13
- import { panelOwnedPreviewBridge as oe, useHostedAssistantPreviewBridge as se } from "../preview/bridgeHostContext.js";
14
- import { AssistantPreviewOverlay as ce } from "../preview/AssistantPreviewOverlay.js";
15
- import { AssistantHeader as le } from "./AssistantHeader.js";
16
- import { AssistantConversationList as ue } from "./AssistantConversationList.js";
17
- import { AssistantConversationDeleteDialogProvider as de } from "./conversationDeleteDialog.js";
18
- import { AssistantControlConfirmProvider as fe } from "./controlConfirmDialog.js";
19
- import { AssistantLeaveConfirmNavigateProvider as pe } from "./leaveConfirm.js";
20
- import { useAssistantServerHistory as me } from "../history/useAssistantServerHistory.js";
21
- import { useFilesServiceWarmup as he } from "../../../storage/warmFilesService.js";
22
- import { AssistantAvatarTabs as ge } from "./AssistantAvatarTabs.js";
23
- import { useAssistantHeartbeat as _e } from "../useAssistantHeartbeat.js";
24
- import { useAssistantKeyboardShortcuts as ve } from "../useAssistantKeyboardShortcuts.js";
25
- import { AssistantChatArea as ye } from "./AssistantChatArea.js";
26
- import { useCallback as p, useId as m, useMemo as h, useRef as g, useState as _ } from "react";
27
- import { jsx as v, jsxs as y } from "react/jsx-runtime";
8
+ import { LiveAvatarSeamProvider as u, LiveAvatarTabContext as d, useLiveAvatarSpeechEngine as te, useLiveAvatarTabOffered as ne } from "./assistantLiveAvatarSeam.js";
9
+ import { AssistantPreviewProvider as re } from "../preview/context.js";
10
+ import { useAssistantPreviewBridge as ie } from "../preview/useAssistantPreviewBridge.js";
11
+ import { AssistantConnectSourceProvider as ae } from "./composer/connectSource.js";
12
+ import { hasAssistantPreviewSurface as oe } from "../preview/types.js";
13
+ import { panelOwnedPreviewBridge as se, useHostedAssistantPreviewBridge as ce } from "../preview/bridgeHostContext.js";
14
+ import { AssistantPreviewOverlay as le } from "../preview/AssistantPreviewOverlay.js";
15
+ import { AssistantHeader as ue } from "./AssistantHeader.js";
16
+ import { AssistantConversationList as de } from "./AssistantConversationList.js";
17
+ import { AssistantConversationDeleteDialogProvider as fe } from "./conversationDeleteDialog.js";
18
+ import { AssistantControlConfirmProvider as pe } from "./controlConfirmDialog.js";
19
+ import { AssistantLeaveConfirmNavigateProvider as me } from "./leaveConfirm.js";
20
+ import { useAssistantServerHistory as he } from "../history/useAssistantServerHistory.js";
21
+ import { useFilesServiceWarmup as ge } from "../../../storage/warmFilesService.js";
22
+ import { AssistantAvatarTabs as _e } from "./AssistantAvatarTabs.js";
23
+ import { useAssistantHeartbeat as ve } from "../useAssistantHeartbeat.js";
24
+ import { useAssistantKeyboardShortcuts as ye } from "../useAssistantKeyboardShortcuts.js";
25
+ import { AssistantChatArea as be } from "./AssistantChatArea.js";
26
+ import { useCallback as f, useId as p, useMemo as m, useRef as h, useState as g } from "react";
27
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
28
28
  //#region src/shared/assistant/ui/AIAssistantPanel.tsx
29
- var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, onNavigate: T, isShellLessTarget: E, connectSourceHref: D, onConnectSource: O, preview: k, liveAvatar: A, serverHistory: j = !1 }) => {
30
- let M = n(), N = f(A);
31
- me(j);
32
- let [P, F] = _(!1), I = ae(k);
29
+ var y = ({ onClose: y, visible: b, docked: x, onToggleDock: S, writeAccess: C, onNavigate: w, isShellLessTarget: T, connectSourceHref: E, onConnectSource: D, preview: O, liveAvatar: k, presentation: A, serverHistory: j = !1 }) => {
30
+ let M = n(), N = te(k);
31
+ he(j);
32
+ let [P, F] = g(!1), I = oe(O);
33
33
  P && !I && F(!1);
34
34
  let L = P && I;
35
- re(oe(k, se()));
36
- let R = g(null);
35
+ ie(se(O, ce()));
36
+ let R = h(null);
37
37
  e(R);
38
- let { conversations: be, activeConversationId: xe, conversationListOpen: z, setConversationListOpen: B, createConversation: Se } = r(), V = z || L, H = A?.tab, Ce = te(H), [U, W] = _(!1);
39
- U && (!H || x === !1) && W(!1);
40
- let G = H !== void 0 && (Ce || U), K = m(), q = h(() => G ? {
38
+ let { conversations: xe, activeConversationId: Se, conversationListOpen: z, setConversationListOpen: B, createConversation: Ce } = r(), V = z || L, H = k?.tab, we = ne(H), [U, W] = g(!1);
39
+ U && (!H || b === !1) && W(!1);
40
+ let G = H !== void 0 && (we || U), K = p(), q = m(() => G ? {
41
41
  chatTab: `${K}-chat-tab`,
42
42
  avatarTab: `${K}-avatar-tab`,
43
43
  chatPanel: `${K}-chat-panel`,
44
44
  avatarPanel: `${K}-avatar-panel`,
45
45
  leaveHint: `${K}-avatar-leave`
46
- } : null, [G, K]), J = p(() => W(!1), []), Y = g(null), X = p((e) => {
46
+ } : null, [G, K]), J = f(() => W(!1), []), Y = h(null), X = f((e) => {
47
47
  Y.current = e;
48
- }, []), we = h(() => ({
48
+ }, []), Te = m(() => ({
49
49
  open: U,
50
50
  close: J,
51
51
  ids: q,
@@ -57,7 +57,7 @@ var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, o
57
57
  q,
58
58
  V,
59
59
  X
60
- ]), Te = (e) => W(e === "avatar"), Z = s({
60
+ ]), Ee = (e) => W(e === "avatar"), Z = s({
61
61
  historyOpen: z,
62
62
  previewOpen: L,
63
63
  avatarOpen: U
@@ -68,7 +68,7 @@ var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, o
68
68
  }, e);
69
69
  B(t.historyOpen), F(t.previewOpen);
70
70
  };
71
- t("assistant.history", h(() => ({ run: () => z ? { status: "already" } : (B(!0), F(!1), { status: "done" }) }), [z, B])), t("assistant.preview", h(() => I ? {
71
+ t("assistant.history", m(() => ({ run: () => z ? { status: "already" } : (B(!0), F(!1), { status: "done" }) }), [z, B])), t("assistant.preview", m(() => I ? {
72
72
  surface: "panel",
73
73
  run: () => L ? { status: "already" } : (B(!1), F(!0), { status: "done" }),
74
74
  close: () => L ? (F(!1), { status: "done" }) : { status: "already" }
@@ -77,10 +77,10 @@ var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, o
77
77
  L,
78
78
  B
79
79
  ]));
80
- let $ = be.find((e) => e.id === xe);
81
- _e($?.backendSessionId ?? null, $?.mode ?? null, x ?? !0), he(x ?? !0);
82
- let Ee = ee(x !== !1);
83
- return ve({
80
+ let $ = xe.find((e) => e.id === Se);
81
+ ve($?.backendSessionId ?? null, $?.mode ?? null, b ?? !0), ge(b ?? !0);
82
+ let De = ee(b !== !1);
83
+ return ye({
84
84
  panelRef: R,
85
85
  onClose: () => {
86
86
  let e = c({
@@ -101,24 +101,24 @@ var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, o
101
101
  W(!1), q && document.getElementById(q.chatTab)?.focus();
102
102
  return;
103
103
  }
104
- b();
104
+ y();
105
105
  }
106
- }), /* @__PURE__ */ v(i, {
107
- visible: x ?? !0,
106
+ }), /* @__PURE__ */ _(i, {
107
+ visible: b ?? !0,
108
108
  engine: N,
109
- children: /* @__PURE__ */ v(u, {
110
- value: A,
111
- children: /* @__PURE__ */ v(d.Provider, {
112
- value: we,
113
- children: /* @__PURE__ */ v(ne, {
114
- value: k,
115
- children: /* @__PURE__ */ v(pe, {
116
- navigate: T,
117
- isShellLessTarget: E,
118
- children: /* @__PURE__ */ v(ie, {
119
- value: D,
120
- onOpen: O,
121
- children: /* @__PURE__ */ v(de, { children: /* @__PURE__ */ v(fe, { children: /* @__PURE__ */ y("div", {
109
+ children: /* @__PURE__ */ _(u, {
110
+ value: k,
111
+ children: /* @__PURE__ */ _(d.Provider, {
112
+ value: Te,
113
+ children: /* @__PURE__ */ _(re, {
114
+ value: O,
115
+ children: /* @__PURE__ */ _(me, {
116
+ navigate: w,
117
+ isShellLessTarget: T,
118
+ children: /* @__PURE__ */ _(ae, {
119
+ value: E,
120
+ onOpen: D,
121
+ children: /* @__PURE__ */ _(fe, { children: /* @__PURE__ */ _(pe, { children: /* @__PURE__ */ v("div", {
122
122
  ref: R,
123
123
  className: "relative flex h-full w-full flex-col bg-bg-surface",
124
124
  role: "complementary",
@@ -128,33 +128,34 @@ var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, o
128
128
  ...Z ? { [a]: Z } : {},
129
129
  tabIndex: -1,
130
130
  children: [
131
- /* @__PURE__ */ y("div", {
131
+ /* @__PURE__ */ v("div", {
132
132
  className: "contents",
133
133
  inert: V || void 0,
134
- children: [/* @__PURE__ */ v(le, {
135
- contextLabel: Ee,
136
- onNewConversation: () => Se(),
134
+ children: [/* @__PURE__ */ _(ue, {
135
+ contextLabel: De,
136
+ onNewConversation: () => Ce(),
137
137
  onOpenHistory: () => Q("history"),
138
138
  historyOpen: z,
139
139
  onTogglePreview: () => Q("preview"),
140
140
  previewOpen: L,
141
- onClose: b,
142
- docked: S,
143
- onToggleDock: C
144
- }), q ? /* @__PURE__ */ v(ge, {
141
+ onClose: y,
142
+ docked: x,
143
+ onToggleDock: S
144
+ }), q ? /* @__PURE__ */ _(_e, {
145
145
  selected: U ? "avatar" : "chat",
146
- onSelect: Te,
146
+ onSelect: Ee,
147
147
  ids: q
148
148
  }) : null]
149
149
  }),
150
- z && /* @__PURE__ */ v(ue, {}),
151
- L && /* @__PURE__ */ v(ce, { onClose: () => F(!1) }),
152
- /* @__PURE__ */ v("div", {
150
+ z && /* @__PURE__ */ _(de, {}),
151
+ L && /* @__PURE__ */ _(le, { onClose: () => F(!1) }),
152
+ /* @__PURE__ */ _("div", {
153
153
  className: "contents",
154
154
  inert: V || void 0,
155
- children: /* @__PURE__ */ v(ye, {
156
- visible: x ?? !0,
157
- writeAccess: w
155
+ children: /* @__PURE__ */ _(be, {
156
+ visible: b ?? !0,
157
+ writeAccess: C,
158
+ presentation: A
158
159
  })
159
160
  })
160
161
  ]
@@ -167,4 +168,4 @@ var b = ({ onClose: b, visible: x, docked: S, onToggleDock: C, writeAccess: w, o
167
168
  });
168
169
  };
169
170
  //#endregion
170
- export { b as AIAssistantPanel };
171
+ export { y as AIAssistantPanel };
@@ -14,6 +14,29 @@ export interface AssistantApiCallsConsentDialogProps {
14
14
  mode?: WriteConsentMode;
15
15
  /** The mode being left, which names the cancel button. Absent: Ask. */
16
16
  currentMode?: WriteConsentMode | null;
17
+ /**
18
+ * The COMPACT presentation's mode switch (BOFF-10055, D7 merged with the
19
+ * consent): what this switch costs, said in the same dialog as the consent,
20
+ * so one decision never needs two dialogs.
21
+ *
22
+ * Absent: the consent dialog exactly as before, byte for byte.
23
+ */
24
+ switchPlan?: AssistantModeSwitchDialogPlan;
25
+ }
26
+ /** What the dialog says about one switch. From `planModeSwitch`, plus the names. */
27
+ export interface AssistantModeSwitchDialogPlan {
28
+ /** First-time write consent applies: the consent's title, body and buttons lead. */
29
+ consent: boolean;
30
+ withdrawnCount: number;
31
+ movesToNewConversation: boolean;
32
+ draftKept: boolean;
33
+ /** The option switched to, named as its menu item names it ("Workflow", "Auto"). */
34
+ targetName: string;
35
+ /**
36
+ * The option being left, when `currentMode` cannot name it (Auto is not a
37
+ * mode). Names the "Stay in …" button.
38
+ */
39
+ currentName?: string;
17
40
  }
18
41
  /**
19
42
  * One-time consent shown the first time a user switches the assistant into a
@@ -3,35 +3,89 @@ import { ModalShell as t } from "../../../ui/modal-shell.js";
3
3
  import { useAssistantTr as n } from "../i18n.js";
4
4
  import { writeModeConsentCopy as r } from "../writeModeConsent.js";
5
5
  import { modeRequiresConfirmWrites as i } from "../api.js";
6
- import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
6
+ import { useRef as a } from "react";
7
+ import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
7
8
  //#region src/shared/assistant/ui/AssistantApiCallsConsentDialog.tsx
8
- var c = { id: "api-calls" }, l = ({ open: l, onConfirm: u, onCancel: d, mode: f = c, currentMode: p }) => {
9
- let m = r(n(), {
10
- target: f,
11
- current: p,
12
- confirmsWrites: i(f.id)
9
+ var l = { id: "api-calls" }, u = ({ open: a, onConfirm: u, onCancel: f, mode: p = l, currentMode: m, switchPlan: h }) => {
10
+ let g = r(n(), {
11
+ target: p,
12
+ current: m,
13
+ confirmsWrites: i(p.id)
13
14
  });
14
- return /* @__PURE__ */ o(t, {
15
- open: l,
16
- onClose: d,
15
+ return h ? /* @__PURE__ */ s(d, {
16
+ open: a,
17
+ onConfirm: u,
18
+ onCancel: f,
19
+ plan: h,
20
+ consentCopy: g
21
+ }) : /* @__PURE__ */ s(t, {
22
+ open: a,
23
+ onClose: f,
17
24
  size: "sm",
18
- title: m.title,
19
- footer: /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o(e, {
25
+ title: g.title,
26
+ footer: /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s(e, {
20
27
  variant: "outline",
21
- onClick: d,
28
+ onClick: f,
22
29
  "data-testid": "assistant-api-calls-consent-cancel",
23
- children: m.cancel
24
- }), /* @__PURE__ */ o(e, {
30
+ children: g.cancel
31
+ }), /* @__PURE__ */ s(e, {
25
32
  variant: "default",
26
33
  onClick: u,
27
34
  "data-testid": "assistant-api-calls-consent-confirm",
28
- children: m.confirm
35
+ children: g.confirm
29
36
  })] }),
30
- children: /* @__PURE__ */ s("div", {
37
+ children: /* @__PURE__ */ c("div", {
31
38
  className: "space-y-2 text-sm text-text-secondary",
32
- children: [/* @__PURE__ */ o("p", { children: m.body }), /* @__PURE__ */ o("p", { children: m.safeguard })]
39
+ children: [/* @__PURE__ */ s("p", { children: g.body }), /* @__PURE__ */ s("p", { children: g.safeguard })]
40
+ })
41
+ });
42
+ }, d = ({ open: r, onConfirm: i, onCancel: l, plan: u, consentCopy: d }) => {
43
+ let f = n(), p = a(null), m = [];
44
+ u.withdrawnCount > 0 && m.push({
45
+ key: "withdrawn",
46
+ text: f("modeSwitch.withdrawn", "{{count}} change(s) waiting for your approval will be withdrawn.", { count: u.withdrawnCount })
47
+ }), u.movesToNewConversation && m.push({
48
+ key: "newConversation",
49
+ text: f("modeSwitch.newConversation", "{{mode}} continues in a new conversation. This one stays in History.", { mode: u.targetName })
50
+ }), u.draftKept && m.length > 0 && m.push({
51
+ key: "draftKept",
52
+ text: f("modeSwitch.draftKept", "What you typed stays in the message box.")
53
+ });
54
+ let h = u.currentName === void 0 ? d.cancel : f("writeConsent.cancel", "Stay in {{mode}}", { mode: u.currentName });
55
+ return /* @__PURE__ */ s(t, {
56
+ open: r,
57
+ onClose: l,
58
+ size: "sm",
59
+ role: "alertdialog",
60
+ testId: "assistant-mode-switch-dialog",
61
+ onOpenAutoFocus: (e) => {
62
+ e.preventDefault(), p.current?.focus();
63
+ },
64
+ title: u.consent ? d.title : f("modeSwitch.title", "Switch to {{mode}}?", { mode: u.targetName }),
65
+ footer: /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s(e, {
66
+ ref: p,
67
+ variant: "outline",
68
+ onClick: l,
69
+ "data-testid": "assistant-mode-switch-cancel",
70
+ children: h
71
+ }), /* @__PURE__ */ s(e, {
72
+ variant: "default",
73
+ onClick: i,
74
+ "data-testid": "assistant-mode-switch-confirm",
75
+ children: u.consent ? d.confirm : f("modeSwitch.confirm", "Switch to {{mode}}", { mode: u.targetName })
76
+ })] }),
77
+ children: /* @__PURE__ */ c("div", {
78
+ className: "space-y-2 text-sm text-text-secondary",
79
+ children: [u.consent ? /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("p", { children: d.body }), /* @__PURE__ */ s("p", { children: d.safeguard })] }) : null, m.length > 0 ? /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s("p", { children: f("modeSwitch.intro", "If you switch now:") }), /* @__PURE__ */ s("ul", {
80
+ className: "list-disc space-y-1 pl-5",
81
+ "data-testid": "assistant-mode-switch-lines",
82
+ children: m.map((e) => /* @__PURE__ */ s("li", {
83
+ "data-testid": `assistant-mode-switch-${e.key}`,
84
+ children: e.text
85
+ }, e.key))
86
+ })] }) : null]
33
87
  })
34
88
  });
35
89
  };
36
90
  //#endregion
37
- export { l as AssistantApiCallsConsentDialog };
91
+ export { u as AssistantApiCallsConsentDialog };
@@ -1,4 +1,5 @@
1
1
  import { FC } from 'react';
2
+ import { AssistantPresentation } from './composer/AssistantComposer';
2
3
  import { WorkspaceWriteAccess } from '../useWorkspaceWriteAccess';
3
4
  /**
4
5
  * `visible` is false when the host HIDES the panel without unmounting it, which
@@ -22,5 +23,10 @@ export interface AssistantChatAreaProps {
22
23
  * false claim in either direction.
23
24
  */
24
25
  writeAccess?: WorkspaceWriteAccess;
26
+ /**
27
+ * BOFF-10055: `compact` turns on the compact presentation's composer, Stop,
28
+ * and the mode-switch confirmation. Absent or `classic`: as before.
29
+ */
30
+ presentation?: AssistantPresentation;
25
31
  }
26
32
  export declare const AssistantChatArea: FC<AssistantChatAreaProps>;