@burdenoff/fe-libs 2026.910.7 → 2026.911.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/shared/assistant/index.d.ts +7 -0
  2. package/dist/shared/assistant/index.d.ts.map +1 -1
  3. package/dist/shared/assistant/preview/AssistantPreviewOverlay.d.ts +5 -0
  4. package/dist/shared/assistant/preview/AssistantPreviewOverlay.d.ts.map +1 -0
  5. package/dist/shared/assistant/preview/AssistantPreviewOverlay.js +68 -0
  6. package/dist/shared/assistant/preview/context.d.ts +9 -0
  7. package/dist/shared/assistant/preview/context.d.ts.map +1 -0
  8. package/dist/shared/assistant/preview/context.js +12 -0
  9. package/dist/shared/assistant/preview/previewTransitions.d.ts +78 -0
  10. package/dist/shared/assistant/preview/previewTransitions.d.ts.map +1 -0
  11. package/dist/shared/assistant/preview/previewTransitions.js +96 -0
  12. package/dist/shared/assistant/preview/types.d.ts +121 -0
  13. package/dist/shared/assistant/preview/types.d.ts.map +1 -0
  14. package/dist/shared/assistant/preview/useAssistantPreviewBridge.d.ts +3 -0
  15. package/dist/shared/assistant/preview/useAssistantPreviewBridge.d.ts.map +1 -0
  16. package/dist/shared/assistant/preview/useAssistantPreviewBridge.js +71 -0
  17. package/dist/shared/assistant/ui/AIAssistantPanel.d.ts +8 -0
  18. package/dist/shared/assistant/ui/AIAssistantPanel.d.ts.map +1 -1
  19. package/dist/shared/assistant/ui/AIAssistantPanel.js +82 -45
  20. package/dist/shared/assistant/ui/AssistantConversationList.d.ts.map +1 -1
  21. package/dist/shared/assistant/ui/AssistantConversationList.js +81 -76
  22. package/dist/shared/assistant/ui/AssistantHeader.d.ts +10 -0
  23. package/dist/shared/assistant/ui/AssistantHeader.d.ts.map +1 -1
  24. package/dist/shared/assistant/ui/AssistantHeader.js +46 -36
  25. package/dist/shared/assistant/ui/panelOverlays.d.ts +48 -0
  26. package/dist/shared/assistant/ui/panelOverlays.d.ts.map +1 -0
  27. package/dist/shared/assistant/ui/panelOverlays.js +16 -0
  28. package/dist/shared/assistant/ui/useOverlayFocus.d.ts +4 -0
  29. package/dist/shared/assistant/ui/useOverlayFocus.d.ts.map +1 -0
  30. package/dist/shared/assistant/ui/useOverlayFocus.js +17 -0
  31. package/dist/shared-assistant.js +41 -35
  32. package/package.json +1 -1
@@ -4,55 +4,92 @@ import { AssistantNavigateProvider as n } from "./AssistantMarkdownRenderer.js";
4
4
  import { useAssistantStore as r } from "../store.js";
5
5
  import { AssistantSpeechProvider as i } from "./AssistantSpeechProvider.js";
6
6
  import { AssistantConnectSourceProvider as a } from "./composer/connectSource.js";
7
- import { AssistantHeader as o } from "./AssistantHeader.js";
8
- import { AssistantConversationList as s } from "./AssistantConversationList.js";
9
- import { useFilesServiceWarmup as c } from "../../../storage/warmFilesService.js";
10
- import { useAssistantHeartbeat as l } from "../useAssistantHeartbeat.js";
11
- import { useAssistantKeyboardShortcuts as u } from "../useAssistantKeyboardShortcuts.js";
12
- import { AssistantChatArea as d } from "./AssistantChatArea.js";
13
- import { useRef as f } from "react";
14
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
7
+ import { AssistantPreviewProvider as o } from "../preview/context.js";
8
+ import { escapeDismisses as s, nextPanelOverlays as c } from "./panelOverlays.js";
9
+ import { useAssistantPreviewBridge as l } from "../preview/useAssistantPreviewBridge.js";
10
+ import { AssistantPreviewOverlay as u } from "../preview/AssistantPreviewOverlay.js";
11
+ import { AssistantHeader as d } from "./AssistantHeader.js";
12
+ import { AssistantConversationList as f } from "./AssistantConversationList.js";
13
+ import { useFilesServiceWarmup as p } from "../../../storage/warmFilesService.js";
14
+ import { useAssistantHeartbeat as m } from "../useAssistantHeartbeat.js";
15
+ import { useAssistantKeyboardShortcuts as h } from "../useAssistantKeyboardShortcuts.js";
16
+ import { AssistantChatArea as g } from "./AssistantChatArea.js";
17
+ import { useRef as _, useState as v } from "react";
18
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
19
  //#region src/shared/assistant/ui/AIAssistantPanel.tsx
16
- var h = ({ onClose: h, visible: g, docked: _, onToggleDock: v, writeAccess: y, onNavigate: b, connectSourceHref: x }) => {
17
- let S = t(), C = f(null), { conversations: w, activeConversationId: T, conversationListOpen: E, toggleConversationList: D, createConversation: O } = r(), k = w.find((e) => e.id === T);
18
- l(k?.backendSessionId ?? null, k?.mode ?? null, g ?? !0), c(g ?? !0);
19
- let A = e(g !== !1);
20
- return u({
21
- panelRef: C,
22
- onClose: h
23
- }), /* @__PURE__ */ p(i, {
24
- visible: g ?? !0,
25
- children: /* @__PURE__ */ p(n, {
26
- value: b,
27
- children: /* @__PURE__ */ p(a, {
28
- value: x,
29
- children: /* @__PURE__ */ m("div", {
30
- ref: C,
31
- className: "relative flex h-full w-full flex-col bg-bg-surface",
32
- role: "complementary",
33
- "aria-label": S("panel.ariaLabel", "AI Assistant"),
34
- "data-testid": "ai-assistant-panel",
35
- tabIndex: -1,
36
- children: [
37
- /* @__PURE__ */ p(o, {
38
- contextLabel: A,
39
- onNewConversation: () => O(),
40
- onOpenHistory: D,
41
- historyOpen: E,
42
- onClose: h,
43
- docked: _,
44
- onToggleDock: v
45
- }),
46
- E && /* @__PURE__ */ p(s, {}),
47
- /* @__PURE__ */ p(d, {
48
- visible: g ?? !0,
49
- writeAccess: y
50
- })
51
- ]
20
+ var x = ({ onClose: x, visible: S, docked: C, onToggleDock: w, writeAccess: T, onNavigate: E, connectSourceHref: D, preview: O }) => {
21
+ let k = t(), [A, j] = v(!1);
22
+ A && !O && j(!1);
23
+ let M = A && !!O;
24
+ l(O ?? null);
25
+ let N = _(null), { conversations: P, activeConversationId: F, conversationListOpen: I, setConversationListOpen: L, createConversation: R } = r(), z = I || M, B = (e) => {
26
+ let t = c({
27
+ historyOpen: I,
28
+ previewOpen: M
29
+ }, e);
30
+ L(t.historyOpen), j(t.previewOpen);
31
+ }, V = P.find((e) => e.id === F);
32
+ m(V?.backendSessionId ?? null, V?.mode ?? null, S ?? !0), p(S ?? !0);
33
+ let H = e(S !== !1);
34
+ return h({
35
+ panelRef: N,
36
+ onClose: () => {
37
+ if (s({
38
+ historyOpen: I,
39
+ previewOpen: M
40
+ }) === "overlay") {
41
+ L(!1), j(!1);
42
+ return;
43
+ }
44
+ x();
45
+ }
46
+ }), /* @__PURE__ */ y(i, {
47
+ visible: S ?? !0,
48
+ children: /* @__PURE__ */ y(o, {
49
+ value: O,
50
+ children: /* @__PURE__ */ y(n, {
51
+ value: E,
52
+ children: /* @__PURE__ */ y(a, {
53
+ value: D,
54
+ children: /* @__PURE__ */ b("div", {
55
+ ref: N,
56
+ className: "relative flex h-full w-full flex-col bg-bg-surface",
57
+ role: "complementary",
58
+ "aria-label": k("panel.ariaLabel", "AI Assistant"),
59
+ "data-testid": "ai-assistant-panel",
60
+ tabIndex: -1,
61
+ children: [
62
+ /* @__PURE__ */ y("div", {
63
+ className: "contents",
64
+ inert: z || void 0,
65
+ children: /* @__PURE__ */ y(d, {
66
+ contextLabel: H,
67
+ onNewConversation: () => R(),
68
+ onOpenHistory: () => B("history"),
69
+ historyOpen: I,
70
+ onTogglePreview: () => B("preview"),
71
+ previewOpen: M,
72
+ onClose: x,
73
+ docked: C,
74
+ onToggleDock: w
75
+ })
76
+ }),
77
+ I && /* @__PURE__ */ y(f, {}),
78
+ M && /* @__PURE__ */ y(u, { onClose: () => j(!1) }),
79
+ /* @__PURE__ */ y("div", {
80
+ className: "contents",
81
+ inert: z || void 0,
82
+ children: /* @__PURE__ */ y(g, {
83
+ visible: S ?? !0,
84
+ writeAccess: T
85
+ })
86
+ })
87
+ ]
88
+ })
52
89
  })
53
90
  })
54
91
  })
55
92
  });
56
93
  };
57
94
  //#endregion
58
- export { h as AIAssistantPanel };
95
+ export { x as AIAssistantPanel };
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantConversationList.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantConversationList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAc1C,eAAO,MAAM,yBAAyB,EAAE,EAgPvC,CAAC"}
1
+ {"version":3,"file":"AssistantConversationList.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantConversationList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAgB1C,eAAO,MAAM,yBAAyB,EAAE,EA8PvC,CAAC"}
@@ -2,151 +2,156 @@ import { useAssistantProduct as e } from "../product.js";
2
2
  import { useAssistantTr as t } from "../i18n.js";
3
3
  import { chatTurns as n } from "../voice/turnVisibility.js";
4
4
  import { useAssistantStore as r } from "../store.js";
5
- import { useState as i } from "react";
6
- import { Check as a, MessageSquare as o, PanelLeftClose as s, Pencil as c, Plus as l, Trash2 as u } from "lucide-react";
7
- import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
5
+ import { useOverlayFocus as i } from "./useOverlayFocus.js";
6
+ import { useState as a } from "react";
7
+ import { Check as o, MessageSquare as s, PanelLeftClose as c, Pencil as l, Plus as u, Trash2 as d } from "lucide-react";
8
+ import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
8
9
  //#region src/shared/assistant/ui/AssistantConversationList.tsx
9
- var m = () => {
10
- let m = t(), { modes: h } = e(), { conversations: g, activeConversationId: _, processingByConversationId: v, setActiveConversation: y, setMode: b, createConversation: x, deleteConversation: S, renameConversation: C, setConversationListOpen: w } = r(), [T, E] = i(null), [D, O] = i(""), k = () => {
11
- x(), w(!1);
12
- }, A = (e) => {
13
- let t = g.find((t) => t.id === e);
14
- y(e), t && b(t.mode), w(!1);
15
- }, j = (e, t) => {
16
- E(e), O(t);
17
- }, M = () => {
18
- T && D.trim() && C(T, D.trim()), E(null);
19
- }, N = (e, t) => {
20
- e.stopPropagation(), S(t);
10
+ var h = () => {
11
+ let h = t(), { modes: g } = e(), { conversations: _, activeConversationId: v, processingByConversationId: y, setActiveConversation: b, setMode: x, createConversation: S, deleteConversation: C, renameConversation: w, setConversationListOpen: T } = r(), [E, D] = a(null), [O, k] = a(""), A = () => {
12
+ S(), T(!1);
13
+ }, j = (e) => {
14
+ let t = _.find((t) => t.id === e);
15
+ b(e), t && x(t.mode), T(!1);
16
+ }, M = (e, t) => {
17
+ D(e), k(t);
18
+ }, N = () => {
19
+ E && O.trim() && w(E, O.trim()), D(null);
20
+ }, P = (e, t) => {
21
+ e.stopPropagation(), C(t);
21
22
  };
22
- return /* @__PURE__ */ p("div", {
23
- className: "absolute inset-0 z-10 flex flex-col bg-bg-surface animate-in slide-in-from-left-2 fade-in duration-200",
23
+ return /* @__PURE__ */ m("div", {
24
+ ref: i("assistant-open-history"),
25
+ tabIndex: -1,
26
+ role: "region",
27
+ "aria-label": h("conversations.title", "History"),
28
+ className: "absolute inset-0 z-10 flex flex-col bg-bg-surface outline-none animate-in slide-in-from-left-2 fade-in duration-200",
24
29
  "data-testid": "conversation-list",
25
- children: [/* @__PURE__ */ p("div", {
30
+ children: [/* @__PURE__ */ m("div", {
26
31
  className: "flex items-center justify-between px-4 py-3 border-b border-border-default bg-bg-elevated",
27
- children: [/* @__PURE__ */ f("span", {
32
+ children: [/* @__PURE__ */ p("span", {
28
33
  className: "text-[13px] font-semibold text-text-primary",
29
- children: m("conversations.title", "History")
30
- }), /* @__PURE__ */ p("div", {
34
+ children: h("conversations.title", "History")
35
+ }), /* @__PURE__ */ m("div", {
31
36
  className: "flex items-center gap-0.5",
32
- children: [/* @__PURE__ */ f("button", {
37
+ children: [/* @__PURE__ */ p("button", {
33
38
  type: "button",
34
- onClick: k,
39
+ onClick: A,
35
40
  className: "p-1.5 rounded-lg text-text-secondary hover:bg-bg-sunken transition-colors",
36
- "aria-label": m("conversations.new", "New conversation"),
41
+ "aria-label": h("conversations.new", "New conversation"),
37
42
  "data-testid": "new-conversation",
38
- children: /* @__PURE__ */ f(l, { className: "size-4" })
39
- }), /* @__PURE__ */ p("button", {
43
+ children: /* @__PURE__ */ p(u, { className: "size-4" })
44
+ }), /* @__PURE__ */ m("button", {
40
45
  type: "button",
41
- onClick: () => w(!1),
46
+ onClick: () => T(!1),
42
47
  className: "flex items-center gap-1 px-2 py-1.5 rounded-lg text-text-secondary hover:bg-bg-sunken transition-colors",
43
- "aria-label": m("conversations.collapse", "Collapse history"),
48
+ "aria-label": h("conversations.collapse", "Collapse history"),
44
49
  "data-testid": "collapse-conversation-list",
45
- children: [/* @__PURE__ */ f(s, { className: "size-4" }), /* @__PURE__ */ f("span", {
50
+ children: [/* @__PURE__ */ p(c, { className: "size-4" }), /* @__PURE__ */ p("span", {
46
51
  className: "text-[11px] font-medium",
47
- children: m("conversations.collapseLabel", "Collapse")
52
+ children: h("conversations.collapseLabel", "Collapse")
48
53
  })]
49
54
  })]
50
55
  })]
51
- }), /* @__PURE__ */ f("div", {
56
+ }), /* @__PURE__ */ p("div", {
52
57
  className: "flex-1 overflow-y-auto",
53
- children: g.length === 0 ? /* @__PURE__ */ p("div", {
58
+ children: _.length === 0 ? /* @__PURE__ */ m("div", {
54
59
  className: "flex flex-col items-center justify-center h-full text-center px-4",
55
60
  children: [
56
- /* @__PURE__ */ f(o, { className: "size-6 text-text-muted mb-2" }),
57
- /* @__PURE__ */ f("p", {
61
+ /* @__PURE__ */ p(s, { className: "size-6 text-text-muted mb-2" }),
62
+ /* @__PURE__ */ p("p", {
58
63
  className: "text-xs text-text-muted",
59
- children: m("conversations.empty", "No conversations yet")
64
+ children: h("conversations.empty", "No conversations yet")
60
65
  }),
61
- /* @__PURE__ */ f("button", {
66
+ /* @__PURE__ */ p("button", {
62
67
  type: "button",
63
- onClick: k,
68
+ onClick: A,
64
69
  className: "mt-2 text-xs text-action-primary-bg hover:underline",
65
- children: m("conversations.startNew", "Start a new conversation")
70
+ children: h("conversations.startNew", "Start a new conversation")
66
71
  })
67
72
  ]
68
- }) : /* @__PURE__ */ f("div", {
73
+ }) : /* @__PURE__ */ p("div", {
69
74
  className: "py-1",
70
- children: g.map((e) => {
71
- let t = e.id === _, r = e.id === T, i = h.find((t) => t.id === e.mode), o = n(e.messages), s = o[o.length - 1], l = o.length === 0 && e.messages.length > 0, g = v[e.id];
72
- return /* @__PURE__ */ f("div", {
75
+ children: _.map((e) => {
76
+ let t = e.id === v, r = e.id === E, i = g.find((t) => t.id === e.mode), a = n(e.messages), s = a[a.length - 1], c = a.length === 0 && e.messages.length > 0, u = y[e.id];
77
+ return /* @__PURE__ */ p("div", {
73
78
  role: "button",
74
79
  tabIndex: 0,
75
80
  onKeyDown: (t) => {
76
- (t.key === "Enter" || t.key === " ") && (t.preventDefault(), !r && A(e.id));
81
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), !r && j(e.id));
77
82
  },
78
- onClick: () => !r && A(e.id),
83
+ onClick: () => !r && j(e.id),
79
84
  className: `group px-3 py-2 cursor-pointer hover:bg-bg-elevated transition-colors ${t ? "bg-bg-elevated border-l-2 border-action-primary-bg" : ""}`,
80
- children: r ? /* @__PURE__ */ p("div", {
85
+ children: r ? /* @__PURE__ */ m("div", {
81
86
  className: "flex items-center gap-1",
82
- children: [/* @__PURE__ */ f("input", {
83
- "aria-label": m("conversations.renameLabel", "Conversation title"),
84
- value: D,
85
- onChange: (e) => O(e.target.value),
87
+ children: [/* @__PURE__ */ p("input", {
88
+ "aria-label": h("conversations.renameLabel", "Conversation title"),
89
+ value: O,
90
+ onChange: (e) => k(e.target.value),
86
91
  onKeyDown: (e) => {
87
- e.key === "Enter" && M(), e.key === "Escape" && E(null);
92
+ e.key === "Enter" && N(), e.key === "Escape" && D(null);
88
93
  },
89
94
  className: "flex-1 text-xs bg-bg-sunken border border-border-default rounded px-2 py-1 text-text-primary focus:outline-none focus:border-action-primary-bg",
90
95
  onClick: (e) => e.stopPropagation()
91
- }), /* @__PURE__ */ f("button", {
96
+ }), /* @__PURE__ */ p("button", {
92
97
  type: "button",
93
98
  onClick: (e) => {
94
- e.stopPropagation(), M();
99
+ e.stopPropagation(), N();
95
100
  },
96
101
  className: "p-1 hover:bg-bg-sunken rounded",
97
- "aria-label": m("conversations.saveTitle", "Save title"),
98
- children: /* @__PURE__ */ f(a, { className: "size-3 text-status-success-text" })
102
+ "aria-label": h("conversations.saveTitle", "Save title"),
103
+ children: /* @__PURE__ */ p(o, { className: "size-3 text-status-success-text" })
99
104
  })]
100
- }) : /* @__PURE__ */ p(d, { children: [
101
- /* @__PURE__ */ p("div", {
105
+ }) : /* @__PURE__ */ m(f, { children: [
106
+ /* @__PURE__ */ m("div", {
102
107
  className: "flex items-center justify-between",
103
- children: [/* @__PURE__ */ f("span", {
108
+ children: [/* @__PURE__ */ p("span", {
104
109
  className: "text-xs font-medium text-text-primary truncate flex-1",
105
110
  children: e.title
106
- }), /* @__PURE__ */ p("div", {
111
+ }), /* @__PURE__ */ m("div", {
107
112
  className: "hidden group-hover:flex group-focus-within:flex items-center gap-0.5 ml-1",
108
- children: [/* @__PURE__ */ f("button", {
113
+ children: [/* @__PURE__ */ p("button", {
109
114
  type: "button",
110
115
  onClick: (t) => {
111
- t.stopPropagation(), j(e.id, e.title);
116
+ t.stopPropagation(), M(e.id, e.title);
112
117
  },
113
118
  className: "p-0.5 hover:bg-bg-sunken rounded",
114
- "aria-label": m("conversations.rename", "Rename conversation"),
115
- children: /* @__PURE__ */ f(c, { className: "size-3 text-text-muted" })
116
- }), /* @__PURE__ */ f("button", {
119
+ "aria-label": h("conversations.rename", "Rename conversation"),
120
+ children: /* @__PURE__ */ p(l, { className: "size-3 text-text-muted" })
121
+ }), /* @__PURE__ */ p("button", {
117
122
  type: "button",
118
- onClick: (t) => N(t, e.id),
123
+ onClick: (t) => P(t, e.id),
119
124
  className: "p-0.5 hover:bg-bg-sunken rounded",
120
- "aria-label": m("conversations.delete", "Delete conversation"),
125
+ "aria-label": h("conversations.delete", "Delete conversation"),
121
126
  "data-testid": "delete-conversation",
122
- children: /* @__PURE__ */ f(u, { className: "size-3 text-status-error-text" })
127
+ children: /* @__PURE__ */ p(d, { className: "size-3 text-status-error-text" })
123
128
  })]
124
129
  })]
125
130
  }),
126
- /* @__PURE__ */ p("div", {
131
+ /* @__PURE__ */ m("div", {
127
132
  className: "flex items-center gap-1.5 mt-0.5",
128
133
  children: [
129
- /* @__PURE__ */ f("span", {
134
+ /* @__PURE__ */ p("span", {
130
135
  className: "text-[10px] text-text-muted px-1 py-0.5 bg-bg-sunken rounded",
131
136
  children: i?.label ?? e.mode
132
137
  }),
133
- /* @__PURE__ */ p("span", {
138
+ /* @__PURE__ */ m("span", {
134
139
  className: "text-[10px] text-text-muted",
135
- children: [o.length, " msgs"]
140
+ children: [a.length, " msgs"]
136
141
  }),
137
- g && g.status !== "idle" && /* @__PURE__ */ f("span", {
142
+ u && u.status !== "idle" && /* @__PURE__ */ p("span", {
138
143
  className: "text-[10px] text-action-primary-bg px-1 py-0.5 bg-bg-sunken rounded",
139
- children: g.status
144
+ children: u.status
140
145
  })
141
146
  ]
142
147
  }),
143
- s && /* @__PURE__ */ f("p", {
148
+ s && /* @__PURE__ */ p("p", {
144
149
  className: "text-[10px] text-text-muted truncate mt-0.5",
145
150
  children: s.content.slice(0, 60)
146
151
  }),
147
- l && /* @__PURE__ */ f("p", {
152
+ c && /* @__PURE__ */ p("p", {
148
153
  className: "text-[10px] text-text-muted truncate mt-0.5",
149
- children: m("conversationList.voiceOnly", "Spoken conversation")
154
+ children: h("conversationList.voiceOnly", "Spoken conversation")
150
155
  })
151
156
  ] })
152
157
  }, e.id);
@@ -156,4 +161,4 @@ var m = () => {
156
161
  });
157
162
  };
158
163
  //#endregion
159
- export { m as AssistantConversationList };
164
+ export { h as AssistantConversationList };
@@ -14,6 +14,16 @@ interface Props {
14
14
  onOpenHistory: () => void;
15
15
  /** True when the history drawer is currently open (drives the toggle state). */
16
16
  historyOpen: boolean;
17
+ /**
18
+ * ★★ OPTIONAL, because this component is published. `AssistantHeader` is
19
+ * `export *`-ed from the assistant barrel and reaches the built package, so a
20
+ * product that composes its own panel chrome renders it directly. Two
21
+ * REQUIRED props would have failed that product's build the moment it bumped
22
+ * fe-libs — a breaking change to ship an OPTIONAL capability, which is
23
+ * exactly backwards. Absent, the toggle simply does not render.
24
+ */
25
+ onTogglePreview?: () => void;
26
+ previewOpen?: boolean;
17
27
  onClose: () => void;
18
28
  docked?: boolean;
19
29
  onToggleDock?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantHeader.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAKhC,UAAU,KAAK;IACb,qEAAqE;IACrE;;;;;;;OAOG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,gFAAgF;IAChF,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,KAAK,CAmHrC,CAAC"}
1
+ {"version":3,"file":"AssistantHeader.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAchC,UAAU,KAAK;IACb,qEAAqE;IACrE;;;;;;;OAOG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,gFAAgF;IAChF,WAAW,EAAE,OAAO,CAAC;IACrB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,KAAK,CA4IrC,CAAC"}
@@ -1,71 +1,81 @@
1
1
  import { useAssistantTr as e } from "../i18n.js";
2
- import { AssistantMoreMenu as t } from "./AssistantMoreMenu.js";
3
- import { History as n, PanelBottom as r, PanelRight as i, Plus as a, Sparkles as o, X as s } from "lucide-react";
4
- import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
2
+ import { useAssistantPreview as t } from "../preview/context.js";
3
+ import { AssistantMoreMenu as n } from "./AssistantMoreMenu.js";
4
+ import { Eye as r, History as i, PanelBottom as a, PanelRight as o, Plus as s, Sparkles as c, X as l } from "lucide-react";
5
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
5
6
  //#region src/shared/assistant/ui/AssistantHeader.tsx
6
- var d = ({ contextLabel: d, onNewConversation: f, onOpenHistory: p, historyOpen: m, onClose: h, docked: g, onToggleDock: _ }) => {
7
- let v = e();
8
- return /* @__PURE__ */ u("div", {
7
+ var p = ({ contextLabel: p, onNewConversation: m, onOpenHistory: h, historyOpen: g, onTogglePreview: _, previewOpen: v, onClose: y, docked: b, onToggleDock: x }) => {
8
+ let S = e(), C = t();
9
+ return /* @__PURE__ */ f("div", {
9
10
  className: "flex items-center gap-2 border-b border-border-default bg-bg-elevated px-3 py-2.5",
10
- children: [/* @__PURE__ */ u("div", {
11
+ children: [/* @__PURE__ */ f("div", {
11
12
  className: "flex min-w-0 flex-1 items-center gap-2",
12
- children: [/* @__PURE__ */ l("span", {
13
+ children: [/* @__PURE__ */ d("span", {
13
14
  className: "flex size-7 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-action-primary-bg/25 to-action-primary-bg/5 text-action-primary-bg",
14
- children: /* @__PURE__ */ l(o, { className: "size-4" })
15
- }), /* @__PURE__ */ u("span", {
15
+ children: /* @__PURE__ */ d(c, { className: "size-4" })
16
+ }), /* @__PURE__ */ f("span", {
16
17
  className: "min-w-0 flex-1",
17
- children: [/* @__PURE__ */ l("span", {
18
+ children: [/* @__PURE__ */ d("span", {
18
19
  className: "block truncate text-[14px] font-semibold leading-tight text-text-primary",
19
- children: v("header.title", "AI Assistant")
20
- }), /* @__PURE__ */ u("span", {
20
+ children: S("header.title", "AI Assistant")
21
+ }), /* @__PURE__ */ f("span", {
21
22
  className: "block truncate text-[11px] leading-tight text-text-muted",
22
- children: [v("header.ready", "Ready"), d && /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("span", {
23
+ children: [S("header.ready", "Ready"), p && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("span", {
23
24
  "aria-hidden": "true",
24
25
  children: " · "
25
- }), v("header.context", "Context: {{page}}", { page: d })] })]
26
+ }), S("header.context", "Context: {{page}}", { page: p })] })]
26
27
  })]
27
28
  })]
28
- }), /* @__PURE__ */ u("div", {
29
+ }), /* @__PURE__ */ f("div", {
29
30
  className: "flex flex-shrink-0 items-center gap-0.5",
30
31
  children: [
31
- /* @__PURE__ */ l("button", {
32
+ /* @__PURE__ */ d("button", {
32
33
  type: "button",
33
- onClick: f,
34
+ onClick: m,
34
35
  className: "rounded-lg p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
35
- "aria-label": v("header.newConversation", "New conversation"),
36
+ "aria-label": S("header.newConversation", "New conversation"),
36
37
  "data-testid": "assistant-new-conversation",
37
- children: /* @__PURE__ */ l(a, { className: "size-4" })
38
+ children: /* @__PURE__ */ d(s, { className: "size-4" })
38
39
  }),
39
- /* @__PURE__ */ l("button", {
40
+ /* @__PURE__ */ d("button", {
40
41
  type: "button",
41
- onClick: p,
42
- className: `rounded-lg p-1.5 transition-colors ${m ? "bg-action-primary-bg/15 text-action-primary-bg" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
43
- "aria-label": v("header.history", "Conversation history"),
44
- "aria-pressed": m,
42
+ onClick: h,
43
+ className: `rounded-lg p-1.5 transition-colors ${g ? "bg-action-primary-bg/15 text-action-primary-bg" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
44
+ "aria-label": S("header.history", "Conversation history"),
45
+ "aria-pressed": g,
45
46
  "data-testid": "assistant-open-history",
46
- children: /* @__PURE__ */ l(n, { className: "size-4" })
47
+ children: /* @__PURE__ */ d(i, { className: "size-4" })
47
48
  }),
48
- /* @__PURE__ */ l(t, {}),
49
- _ && /* @__PURE__ */ l("button", {
49
+ C && _ && /* @__PURE__ */ d("button", {
50
50
  type: "button",
51
51
  onClick: _,
52
+ className: `rounded-lg p-1.5 transition-colors ${v ? "bg-action-primary-bg/15 text-action-primary-bg" : "text-text-secondary hover:bg-bg-sunken hover:text-text-primary"}`,
53
+ "aria-label": C.label ?? S("header.preview", "Preview"),
54
+ "aria-pressed": !!v,
55
+ "data-testid": "assistant-toggle-preview",
56
+ children: /* @__PURE__ */ d(r, { className: "size-4" })
57
+ }),
58
+ /* @__PURE__ */ d(n, {}),
59
+ x && /* @__PURE__ */ d("button", {
60
+ type: "button",
61
+ onClick: x,
52
62
  className: "hidden rounded-lg p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary sm:inline-flex",
53
- "aria-label": g ? v("header.float", "Float chat window") : v("header.dock", "Dock to side panel"),
54
- "aria-pressed": g,
63
+ "aria-label": b ? S("header.float", "Float chat window") : S("header.dock", "Dock to side panel"),
64
+ "aria-pressed": b,
55
65
  "data-testid": "assistant-toggle-dock",
56
- children: l(g ? r : i, { className: "size-4" })
66
+ children: d(b ? a : o, { className: "size-4" })
57
67
  }),
58
- /* @__PURE__ */ l("button", {
68
+ /* @__PURE__ */ d("button", {
59
69
  type: "button",
60
- onClick: h,
70
+ onClick: y,
61
71
  className: "rounded-lg p-1.5 text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary",
62
- "aria-label": v("header.close", "Close assistant"),
72
+ "aria-label": S("header.close", "Close assistant"),
63
73
  "data-testid": "assistant-close",
64
- children: /* @__PURE__ */ l(s, { className: "size-4" })
74
+ children: /* @__PURE__ */ d(l, { className: "size-4" })
65
75
  })
66
76
  ]
67
77
  })]
68
78
  });
69
79
  };
70
80
  //#endregion
71
- export { d as AssistantHeader };
81
+ export { p as AssistantHeader };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Which of the panel's full-surface overlays is showing.
3
+ *
4
+ * ★★ THEY ARE MUTUALLY EXCLUSIVE, and that is not a style choice. The history
5
+ * drawer (`AssistantConversationList.tsx:71`) and the preview surface
6
+ * (`AssistantPreviewOverlay.tsx:57`) are BOTH `absolute inset-0 z-10` with an
7
+ * opaque `bg-bg-surface`, inside the same relative panel. Equal z-index means
8
+ * the later one in DOM order simply paints over the earlier one, so allowing
9
+ * both produces three separate defects at once:
10
+ *
11
+ * - the covered surface is invisible while its own toggle still reports
12
+ * `aria-pressed="true"`, so a screen reader announces a drawer the sighted
13
+ * user cannot see and the sighted user sees a pressed button doing nothing;
14
+ * - closing the top one makes a surface the user had forgotten reappear
15
+ * unbidden, over their conversation;
16
+ * - which one wins depends on JSX order, so a future reorder silently swaps
17
+ * the behaviour with nothing to catch it.
18
+ *
19
+ * Modelled as one slot with two toggles rather than two independent booleans,
20
+ * because "at most one is true" is then true by construction instead of by
21
+ * everyone remembering to close the other one.
22
+ */
23
+ export interface PanelOverlayState {
24
+ readonly historyOpen: boolean;
25
+ readonly previewOpen: boolean;
26
+ }
27
+ /** Nothing showing — the state the panel opens in. */
28
+ export declare const NO_PANEL_OVERLAY: PanelOverlayState;
29
+ /**
30
+ * The state after the user presses one of the two toggles.
31
+ *
32
+ * Pressing the toggle of the overlay already showing closes it; pressing the
33
+ * other one swaps to it. There is deliberately no way to express "both".
34
+ */
35
+ export declare function nextPanelOverlays(current: PanelOverlayState, toggled: "history" | "preview"): PanelOverlayState;
36
+ /**
37
+ * What Escape should dismiss.
38
+ *
39
+ * ★★ THE TOPMOST LAYER, NOT THE WHOLE ASSISTANT. `useAssistantKeyboardShortcuts`
40
+ * calls `onClose()` on Escape (`:39-43`), and both host apps mount the panel as
41
+ * `{isOpen && <AIAssistantPanel/>}` — so with an overlay open, Escape did not
42
+ * close the overlay, it UNMOUNTED the assistant and took the in-flight turn,
43
+ * the composer draft and the attachments with it. The user's mental model of
44
+ * Escape is "back out of the thing I just opened"; here it destroyed a
45
+ * conversation instead.
46
+ */
47
+ export declare function escapeDismisses(current: PanelOverlayState): "overlay" | "panel";
48
+ //# sourceMappingURL=panelOverlays.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panelOverlays.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/panelOverlays.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,iBAG9B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,SAAS,GAAG,SAAS,GAC7B,iBAAiB,CAOnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,GACzB,SAAS,GAAG,OAAO,CAErB"}
@@ -0,0 +1,16 @@
1
+ //#region src/shared/assistant/ui/panelOverlays.ts
2
+ var e = {
3
+ historyOpen: !1,
4
+ previewOpen: !1
5
+ };
6
+ function t(t, n) {
7
+ return (n === "history" ? t.historyOpen : t.previewOpen) ? e : {
8
+ historyOpen: n === "history",
9
+ previewOpen: n === "preview"
10
+ };
11
+ }
12
+ function n(e) {
13
+ return e.historyOpen || e.previewOpen ? "overlay" : "panel";
14
+ }
15
+ //#endregion
16
+ export { e as NO_PANEL_OVERLAY, n as escapeDismisses, t as nextPanelOverlays };
@@ -0,0 +1,4 @@
1
+ export declare function useOverlayFocus<T extends HTMLElement>(
2
+ /** `data-testid` of the control that opens this overlay, for focus return. */
3
+ returnFocusTestId?: string): import('react').RefObject<T | null>;
4
+ //# sourceMappingURL=useOverlayFocus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOverlayFocus.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/useOverlayFocus.ts"],"names":[],"mappings":"AAmDA,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW;AACnD,8EAA8E;AAC9E,iBAAiB,CAAC,EAAE,MAAM,uCAmD3B"}
@@ -0,0 +1,17 @@
1
+ import { useEffect as e, useLayoutEffect as t, useRef as n } from "react";
2
+ //#region src/shared/assistant/ui/useOverlayFocus.ts
3
+ var r = "[data-testid=\"ai-assistant-panel\"]", i = typeof document > "u" ? e : t;
4
+ function a(e) {
5
+ let t = n(null);
6
+ return i(() => {
7
+ let n = t.current, i = n?.closest(r) ?? null;
8
+ return n?.focus({ preventScroll: !0 }), () => {
9
+ let t = document.activeElement;
10
+ if (!(n && (n === t || n.contains(t)))) return;
11
+ let r = e ? i?.querySelector(`[data-testid="${e}"]`) : null;
12
+ (r?.isConnected ? r : i)?.focus({ preventScroll: !0 }), i && (document.activeElement === i || i.contains(document.activeElement)) || i?.focus({ preventScroll: !0 });
13
+ };
14
+ }, [e]), t;
15
+ }
16
+ //#endregion
17
+ export { a as useOverlayFocus };