@burdenoff/fe-libs 2026.1029.9 → 2026.1029.11

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.
@@ -254,6 +254,216 @@ export declare function assistantOverlayClassName(input: {
254
254
  expanded: boolean;
255
255
  docked: boolean;
256
256
  }): string;
257
+ /**
258
+ * What the one assistant container is showing (DESIGN §8.2):
259
+ * - `window`: the Quick assistant, a small non-modal window above the launcher
260
+ * (a bottom sheet on a phone);
261
+ * - `panel`: the full assistant, docked, floating or a sheet.
262
+ *
263
+ * ★ A surface is a CLASS and a geometry on one persistent element, never a
264
+ * place in the tree: the in-flight turn, the draft, its attachments, the
265
+ * microphone and the voice session all live below it.
266
+ */
267
+ export type AssistantSurface = 'window' | 'panel';
268
+ /** Narrow a stored value to a surface. */
269
+ export declare function isAssistantSurface(value: unknown): value is AssistantSurface;
270
+ /** The storage keys only the compact presentation uses. */
271
+ export interface AssistantCompactOverlayKeys {
272
+ /** localStorage: the person's EXPLICIT Dock/Float pick (D1-A), `docked` or `floating`. */
273
+ layoutStorageKey: string;
274
+ /** sessionStorage: which surface is showing, `window` or `panel`. */
275
+ surfaceStorageKey: string;
276
+ /** sessionStorage: the Quick assistant window is minimized (`1`/`0`). */
277
+ minimizedStorageKey: string;
278
+ }
279
+ /**
280
+ * The compact keys for a product prefix: `<prefix>:ai-panel:layout`, `:surface`
281
+ * and `:minimized`. Separate from `assistantOverlayKeys` so the classic keys
282
+ * (and what a host reads of them) cannot change.
283
+ */
284
+ export declare function assistantCompactOverlayKeys(prefix: string): AssistantCompactOverlayKeys;
285
+ /** Dock or Float, as a person picked it. */
286
+ export type AssistantLayoutChoice = 'docked' | 'floating';
287
+ /** The explicit Dock/Float pick (D1-A), or null when none was made or storage fails. */
288
+ export declare function readAssistantLayoutChoice(storage: () => Pick<Storage, 'getItem'> | undefined, key: string): AssistantLayoutChoice | null;
289
+ /** Persist an explicit Dock/Float pick. An unavailable store keeps it in memory only. */
290
+ export declare function writeAssistantLayoutChoice(storage: () => Pick<Storage, 'setItem'> | undefined, key: string, choice: AssistantLayoutChoice): void;
291
+ /**
292
+ * The LEGACY dock key (`<prefix>:ai-panel:docked`) as a choice: `"1"` is docked,
293
+ * `"0"` is floating, anything else (absent, unreadable) is no choice.
294
+ *
295
+ * ★ The classic overlay writes `"0"` on mount, so a browser that has run any
296
+ * overlay holds a `"0"` that does not mean "I chose Float". The compact
297
+ * presentation therefore reads it only AFTER its own explicit key, and never
298
+ * writes it (D1-A).
299
+ */
300
+ export declare function readAssistantLegacyDockChoice(storage: () => Pick<Storage, 'getItem'> | undefined, key: string): AssistantLayoutChoice | null;
301
+ /**
302
+ * Docked is the default from this viewport width up (N2, DESIGN-DELTA A2): at
303
+ * 1024 the default sidebar leaves the page 328 px beside a 440 px dock, at 1280
304
+ * it leaves 584. Between 640 and 1279 the default is a floating window.
305
+ */
306
+ export declare const ASSISTANT_COMPACT_DOCKED_MIN_VIEWPORT_PX = 1280;
307
+ /** The media query for `ASSISTANT_COMPACT_DOCKED_MIN_VIEWPORT_PX`. */
308
+ export declare const ASSISTANT_COMPACT_DOCKED_DEFAULT_QUERY = "(min-width: 1280px)";
309
+ /**
310
+ * Is the full assistant docked? D1-A: the person's explicit pick, then the
311
+ * legacy stored `"1"`/`"0"`, then the viewport default (docked from 1280 px).
312
+ * On a phone the answer is irrelevant: every layout class is `sm:`.
313
+ */
314
+ export declare function resolveCompactDocked(input: {
315
+ explicit: AssistantLayoutChoice | null;
316
+ legacy: AssistantLayoutChoice | null;
317
+ wideViewport: boolean;
318
+ }): boolean;
319
+ /** Width of the Quick assistant window on a desktop. Must match `sm:w-[360px]` below. */
320
+ export declare const ASSISTANT_COMPACT_WINDOW_WIDTH_PX = 360;
321
+ /** The window's height ceiling on a desktop. Must match the `600px` in its class below. */
322
+ export declare const ASSISTANT_COMPACT_WINDOW_HEIGHT_PX = 600;
323
+ /**
324
+ * The expanded width, docked or floating (N2): a FIXED 760 px, so the published
325
+ * inset stays a pure constant. Must match `sm:w-[760px]` below.
326
+ */
327
+ export declare const ASSISTANT_COMPACT_EXPANDED_WIDTH_PX = 760;
328
+ /** What a panel's geometry depends on besides the open flag and the viewport. */
329
+ export interface AssistantCompactGeometryInput {
330
+ isOpen: boolean;
331
+ docked: boolean;
332
+ isDesktop: boolean;
333
+ surface: AssistantSurface;
334
+ /** Expand was pressed (the panel only reports it from 1536 px up). */
335
+ expanded: boolean;
336
+ }
337
+ /**
338
+ * The width the docked full panel takes from the page, in px: 440, or 760 when
339
+ * expanded. Nothing for the window, the floating panel, a phone sheet or a
340
+ * closed assistant. A pure constant of its inputs.
341
+ */
342
+ export declare function publishedCompactDockWidth(input: AssistantCompactGeometryInput): number;
343
+ /**
344
+ * How far the open full panel reaches in from the page's right edge, in px:
345
+ * the dock width while docked (440, 760 expanded), the floating width plus its
346
+ * right gap while floating (544, 784 expanded). Nothing is published for the
347
+ * Quick assistant window (it is a small non-modal overlay and the page stays
348
+ * whole), for a phone sheet, or while closed.
349
+ */
350
+ export declare function publishedCompactPanelInset(input: AssistantCompactGeometryInput): number;
351
+ /**
352
+ * Where the window's bottom edge sits: above the launcher. A variable the
353
+ * overlay sets on the container, from the launcher's own bottom offset, so a
354
+ * host that lifts the launcher lifts the window with it. Falls back to 6rem,
355
+ * the classic floating window's anchor.
356
+ */
357
+ export declare const ASSISTANT_WINDOW_BOTTOM_VAR = "--assistant-window-bottom";
358
+ /** The launcher is `size-14` (3.5rem); the window rests 1rem above it. */
359
+ export declare const ASSISTANT_WINDOW_LAUNCHER_CLEARANCE = "4.5rem";
360
+ /** The window's bottom offset for a launcher inset: the launcher's own, plus its size and a gap. */
361
+ export declare function assistantWindowBottom(launcherBottomInset: string): string;
362
+ /**
363
+ * CSS custom properties the overlay sets on the PHONE sheet from the visual
364
+ * viewport (DELTA A5): the height the sheet may use, and how far the on-screen
365
+ * keyboard covers the layout viewport's bottom edge. Set on the container, not
366
+ * on `<html>`: they are the sheet's own, and a keyboard animation writes them
367
+ * on every frame.
368
+ */
369
+ export declare const ASSISTANT_VIEWPORT_HEIGHT_VAR = "--assistant-viewport-height";
370
+ export declare const ASSISTANT_KEYBOARD_INSET_VAR = "--assistant-keyboard-inset";
371
+ /**
372
+ * Shared by every compact surface, on a phone: a full-width bottom sheet with a
373
+ * rounded top edge. ★ No height here (see the heights below), and no surface
374
+ * colour: the window and the panel each name their own, so one class never
375
+ * fights another for the same property.
376
+ *
377
+ * ★ The bottom edge is the LARGER of the tab bar and the keyboard. With the
378
+ * keyboard up the tab bar is behind it, so the sheet rests on the keyboard;
379
+ * without one the inset is 0 and the tab bar wins, as in classic.
380
+ */
381
+ export declare const ASSISTANT_COMPACT_SHEET_BASE_CLASS: string;
382
+ /** The full panel's colours on every viewport: classic's surface and shadow. */
383
+ export declare const ASSISTANT_COMPACT_PANEL_SURFACE_CLASS = "bg-bg-surface shadow-2xl";
384
+ /**
385
+ * The window's colours on every viewport: an elevated surface and the elevation-4
386
+ * shadow.
387
+ *
388
+ * ★ The shadow is read from the TOKEN: `--shadow-elevation-4` is a `:root` custom
389
+ * property with no `@theme` entry, so `shadow-elevation-4` is not a utility and
390
+ * would paint nothing. The arbitrary-value form is how the other call sites read
391
+ * `--shadow-focus`.
392
+ */
393
+ export declare const ASSISTANT_COMPACT_WINDOW_SURFACE_CLASS = "bg-bg-elevated shadow-[var(--shadow-elevation-4)]";
394
+ /**
395
+ * Phone sheet heights, in `dvh` and capped by the visual viewport (DELTA A5 a):
396
+ * the sheet never grows taller than what the keyboard leaves, so the composer
397
+ * and Stop stay on screen and only the message list gives way. The window
398
+ * opens at about 62 %, the full panel at 60 %; either expands to 88 %.
399
+ */
400
+ export declare const ASSISTANT_COMPACT_SHEET_WINDOW_CLASS = "h-[62dvh] max-h-[min(62dvh,var(--assistant-viewport-height,100dvh))]";
401
+ export declare const ASSISTANT_COMPACT_SHEET_PANEL_CLASS = "h-[60dvh] max-h-[min(60dvh,var(--assistant-viewport-height,100dvh))]";
402
+ export declare const ASSISTANT_COMPACT_SHEET_EXPANDED_CLASS = "h-[88dvh] max-h-[min(88dvh,var(--assistant-viewport-height,100dvh))]";
403
+ /**
404
+ * Desktop: the Quick assistant window. 360 wide, `min(600px, 100dvh − header −
405
+ * launcher area)` tall, resting above the launcher; non-modal (no scrim).
406
+ */
407
+ export declare const ASSISTANT_COMPACT_WINDOW_DESKTOP_CLASS: string;
408
+ /** Desktop: the docked full panel, 440 wide. */
409
+ export declare const ASSISTANT_COMPACT_DOCKED_DESKTOP_CLASS: string;
410
+ /** Desktop: the docked full panel, expanded to a fixed 760. */
411
+ export declare const ASSISTANT_COMPACT_DOCKED_EXPANDED_DESKTOP_CLASS: string;
412
+ /** Desktop: the floating full panel, 520 × 680 clamped to the viewport, as classic. */
413
+ export declare const ASSISTANT_COMPACT_FLOATING_DESKTOP_CLASS: string;
414
+ /**
415
+ * Desktop: the floating full panel, expanded: a fixed 760 wide and `viewport −
416
+ * header − 48px` tall, so it keeps 24 px under the header and 24 px above the
417
+ * bottom edge. The launcher is hidden while the full panel is open (L1), so
418
+ * nothing needs the room it would have kept.
419
+ */
420
+ export declare const ASSISTANT_COMPACT_FLOATING_EXPANDED_DESKTOP_CLASS: string;
421
+ /**
422
+ * The container's class for the compact presentation.
423
+ *
424
+ * ★★ Like the classic one, every axis selects a CLASS on ONE persistent
425
+ * element, never a mount: surface, minimized, dock, float, sheet and expanded.
426
+ * A minimized window keeps its element (and the panel inside it) and only stops
427
+ * being displayed.
428
+ *
429
+ * @param sheetExpanded the phone sheet is raised from its grab handle (88 %)
430
+ * @param expanded the panel is Expanded to a fixed 760 px (desktop)
431
+ */
432
+ export declare function assistantCompactOverlayClassName(input: {
433
+ surface: AssistantSurface;
434
+ minimized: boolean;
435
+ docked: boolean;
436
+ expanded: boolean;
437
+ sheetExpanded: boolean;
438
+ }): string;
439
+ /**
440
+ * Does the launcher show? Classic hides it on a phone while the assistant is
441
+ * open. The compact presentation hides it at ALL widths while the full panel is
442
+ * open (L1, the header button is the way back), and on a phone while the window
443
+ * is open (the sheet would be painted over by it). A minimized window shows it:
444
+ * it is the way back in.
445
+ */
446
+ export declare function assistantCompactLauncherVisibility(input: {
447
+ isOpen: boolean;
448
+ surface: AssistantSurface;
449
+ minimized: boolean;
450
+ }): 'always' | 'desktop-only' | 'hidden';
451
+ /**
452
+ * The visual viewport, reduced to what the sheet needs. The keyboard inset is how
453
+ * far the on-screen keyboard covers the bottom of the LAYOUT viewport; the
454
+ * height is what is left for the sheet. `scale` guards pinch-zoom: a zoomed
455
+ * visual viewport is smaller than the layout one without any keyboard, and the
456
+ * sheet must not shrink for it.
457
+ */
458
+ export declare function assistantVisualViewportMetrics(input: {
459
+ layoutHeight: number;
460
+ viewportHeight: number;
461
+ viewportOffsetTop: number;
462
+ scale: number;
463
+ }): {
464
+ heightPx: number;
465
+ keyboardInsetPx: number;
466
+ };
257
467
  export interface AssistantNavigateOptions {
258
468
  /** The product router's navigate, e.g. React Router's `useNavigate()`. */
259
469
  navigate: (path: string) => void;
@@ -16,48 +16,118 @@ function r(e, t) {
16
16
  return t ?? !e;
17
17
  }
18
18
  var i = "Ctrl+Shift+A";
19
- function a(e) {
19
+ function ee(e) {
20
20
  return (e.ctrlKey || e.metaKey) && e.shiftKey && typeof e.key == "string" && e.key.toLowerCase() === "a";
21
21
  }
22
- var o = "[role=\"dialog\"],[role=\"alertdialog\"],dialog[open]";
23
- function s(e, t) {
24
- return c(e) ? e.closest(t) : null;
22
+ var te = "[role=\"dialog\"],[role=\"alertdialog\"],dialog[open]";
23
+ function a(e, t) {
24
+ return o(e) ? e.closest(t) : null;
25
25
  }
26
- function c(e) {
26
+ function o(e) {
27
27
  return typeof e == "object" && !!e && "closest" in e && typeof e.closest == "function";
28
28
  }
29
- function l(e) {
29
+ function ne(e) {
30
30
  return e.key !== "Escape" || !e.isOpen || e.alreadyHandled || e.dialogOpen || e.pressedInsideDialog || e.panelOwnsEscape ? "ignore" : "close";
31
31
  }
32
- var u = "max(var(--mobile-tabbar-height, 0px), var(--safe-bottom, 0px), env(safe-area-inset-bottom, 0px))";
33
- function d(e) {
34
- return `calc(${e} + ${u})`;
32
+ var s = "max(var(--mobile-tabbar-height, 0px), var(--safe-bottom, 0px), env(safe-area-inset-bottom, 0px))";
33
+ function c(e) {
34
+ return `calc(${e} + ${s})`;
35
35
  }
36
- var f = "--assistant-launcher-lift";
37
- function p(e) {
38
- return `calc(var(${f}, 0px) + ${d(e)})`;
36
+ var l = "--assistant-launcher-lift";
37
+ function u(e) {
38
+ return `calc(var(${l}, 0px) + ${c(e)})`;
39
39
  }
40
- var m = "1.5rem";
41
- function h(e) {
42
- return e > 0 ? `calc(${m} + ${e}px)` : m;
40
+ var d = "1.5rem";
41
+ function re(e) {
42
+ return e > 0 ? `calc(${d} + ${e}px)` : d;
43
43
  }
44
- var g = 440, _ = "--assistant-dock-width", v = "--assistant-sheet-bottom", y = "--assistant-overlay-top", b = 56, x = "(min-width: 640px)";
45
- function S(e) {
44
+ var f = 440, p = "--assistant-dock-width", m = "--assistant-sheet-bottom", h = "--assistant-overlay-top", g = 56, _ = "(min-width: 640px)";
45
+ function v(e) {
46
46
  return e.isOpen && e.docked && e.isDesktop ? 440 : 0;
47
47
  }
48
- var C = 520, w = 24, T = "--assistant-panel-inset";
49
- function E(e) {
48
+ var y = 520, ie = 24, ae = "--assistant-panel-inset";
49
+ function oe(e) {
50
50
  return !e.isOpen || !e.isDesktop ? 0 : e.docked ? 440 : 544;
51
51
  }
52
- var D = "fixed z-50 flex flex-col overflow-hidden border border-border-default bg-bg-surface shadow-2xl inset-x-0 bottom-[var(--assistant-sheet-bottom,0px)] top-auto rounded-t-2xl animate-in slide-in-from-bottom-4 fade-in duration-200", O = "h-[60vh] max-h-[60vh]", k = "h-[88vh] max-h-[88vh]", A = "sm:inset-auto sm:top-auto sm:bottom-24 sm:right-6 sm:rounded-2xl sm:h-[680px] sm:max-h-[calc(100vh-6rem-var(--assistant-overlay-top,56px)-0.5rem)] sm:w-[520px] sm:max-w-[calc(100vw-2rem)]", j = "sm:left-auto sm:right-0 sm:top-[var(--assistant-overlay-top)] sm:bottom-0 sm:rounded-none sm:h-auto sm:max-h-none sm:w-[440px] sm:max-w-[calc(100vw-2rem)]";
53
- function M(e) {
52
+ var b = "fixed z-50 flex flex-col overflow-hidden border border-border-default bg-bg-surface shadow-2xl inset-x-0 bottom-[var(--assistant-sheet-bottom,0px)] top-auto rounded-t-2xl animate-in slide-in-from-bottom-4 fade-in duration-200", x = "h-[60vh] max-h-[60vh]", S = "h-[88vh] max-h-[88vh]", C = "sm:inset-auto sm:top-auto sm:bottom-24 sm:right-6 sm:rounded-2xl sm:h-[680px] sm:max-h-[calc(100vh-6rem-var(--assistant-overlay-top,56px)-0.5rem)] sm:w-[520px] sm:max-w-[calc(100vw-2rem)]", w = "sm:left-auto sm:right-0 sm:top-[var(--assistant-overlay-top)] sm:bottom-0 sm:rounded-none sm:h-auto sm:max-h-none sm:w-[440px] sm:max-w-[calc(100vw-2rem)]";
53
+ function T(e) {
54
+ return [
55
+ b,
56
+ e.expanded ? S : x,
57
+ e.docked ? w : C
58
+ ].join(" ");
59
+ }
60
+ function E(e) {
61
+ return e === "window" || e === "panel";
62
+ }
63
+ function D(e) {
64
+ return {
65
+ layoutStorageKey: `${e}:ai-panel:layout`,
66
+ surfaceStorageKey: `${e}:ai-panel:surface`,
67
+ minimizedStorageKey: `${e}:ai-panel:minimized`
68
+ };
69
+ }
70
+ function O(e, t) {
71
+ try {
72
+ let n = e()?.getItem(t);
73
+ return n === "docked" || n === "floating" ? n : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ function k(e, t, n) {
79
+ try {
80
+ e()?.setItem(t, n);
81
+ } catch {}
82
+ }
83
+ function A(e, t) {
84
+ try {
85
+ let n = e()?.getItem(t);
86
+ return n === "1" ? "docked" : n === "0" ? "floating" : null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ var j = 1280, M = `(min-width: ${j}px)`;
92
+ function N(e) {
93
+ let t = e.explicit ?? e.legacy;
94
+ return t === null ? e.wideViewport : t === "docked";
95
+ }
96
+ var P = 360, F = 600, I = 760;
97
+ function L(e) {
98
+ return !e.isOpen || !e.isDesktop || e.surface !== "panel" || !e.docked ? 0 : e.expanded ? 760 : 440;
99
+ }
100
+ function R(e) {
101
+ if (!e.isOpen || !e.isDesktop || e.surface !== "panel") return 0;
102
+ let t = e.expanded ? 760 : e.docked ? 440 : 520;
103
+ return e.docked ? t : t + 24;
104
+ }
105
+ var z = "--assistant-window-bottom", B = "4.5rem";
106
+ function V(e) {
107
+ return `calc(${u(e)} + ${B})`;
108
+ }
109
+ var se = "--assistant-viewport-height", H = "--assistant-keyboard-inset", U = "fixed z-50 flex flex-col overflow-hidden border border-border-default inset-x-0 bottom-[max(var(--assistant-sheet-bottom,0px),var(--assistant-keyboard-inset,0px))] top-auto rounded-t-2xl animate-in slide-in-from-bottom-4 fade-in duration-200", W = "bg-bg-surface shadow-2xl", G = "bg-bg-elevated shadow-[var(--shadow-elevation-4)]", K = "h-[62dvh] max-h-[min(62dvh,var(--assistant-viewport-height,100dvh))]", q = "h-[60dvh] max-h-[min(60dvh,var(--assistant-viewport-height,100dvh))]", J = "h-[88dvh] max-h-[min(88dvh,var(--assistant-viewport-height,100dvh))]", Y = "sm:inset-auto sm:top-auto sm:bottom-[var(--assistant-window-bottom,6rem)] sm:right-6 sm:rounded-3xl sm:h-[min(600px,calc(100dvh-var(--assistant-window-bottom,6rem)-var(--assistant-overlay-top,56px)-0.5rem))] sm:max-h-none sm:w-[360px] sm:max-w-[calc(100vw-2rem)]", X = "sm:left-auto sm:right-0 sm:top-[var(--assistant-overlay-top)] sm:bottom-0 sm:rounded-none sm:h-auto sm:max-h-none sm:w-[440px] sm:max-w-[calc(100vw-2rem)]", Z = "sm:left-auto sm:right-0 sm:top-[var(--assistant-overlay-top)] sm:bottom-0 sm:rounded-none sm:h-auto sm:max-h-none sm:w-[760px] sm:max-w-[calc(100vw-2rem)]", Q = "sm:inset-auto sm:top-auto sm:bottom-24 sm:right-6 sm:rounded-2xl sm:h-[680px] sm:max-h-[calc(100dvh-6rem-var(--assistant-overlay-top,56px)-0.5rem)] sm:w-[520px] sm:max-w-[calc(100vw-2rem)]", $ = "sm:inset-auto sm:top-auto sm:bottom-6 sm:right-6 sm:rounded-2xl sm:h-[calc(100dvh-var(--assistant-overlay-top,56px)-3rem)] sm:max-h-none sm:w-[760px] sm:max-w-[calc(100vw-2rem)]";
110
+ function ce(e) {
111
+ if (e.minimized) return "hidden";
112
+ let t = e.surface === "window", n = e.sheetExpanded ? J : t ? K : q, r = t ? Y : e.docked ? e.expanded ? Z : X : e.expanded ? $ : Q;
54
113
  return [
55
- D,
56
- e.expanded ? k : O,
57
- e.docked ? j : A
114
+ U,
115
+ t ? G : W,
116
+ n,
117
+ r
58
118
  ].join(" ");
59
119
  }
60
- function N(t) {
120
+ function le(e) {
121
+ return !e.isOpen || e.minimized ? "always" : e.surface === "panel" ? "hidden" : "desktop-only";
122
+ }
123
+ function ue(e) {
124
+ let t = e.scale > 1.01, n = e.layoutHeight - e.viewportHeight - e.viewportOffsetTop, r = t ? 0 : Math.max(0, Math.round(n));
125
+ return {
126
+ heightPx: Math.max(0, Math.round(t ? e.layoutHeight : e.viewportHeight)),
127
+ keyboardInsetPx: r
128
+ };
129
+ }
130
+ function de(t) {
61
131
  return (n, r) => {
62
132
  if (t.isShellLessTarget?.(n)) return t.openInNewTab(n), { kind: "new-tab" };
63
133
  let i = e(r);
@@ -68,22 +138,22 @@ function N(t) {
68
138
  } : (t.navigate(n), t.isDesktop() || t.collapseSheet(), { kind: "routed" });
69
139
  };
70
140
  }
71
- function P(e) {
141
+ function fe(e) {
72
142
  if (typeof CustomEvent > "u" || !(e instanceof CustomEvent)) return "generic";
73
143
  let t = e.detail;
74
144
  return typeof t != "object" || !t || !("kind" in t) ? "generic" : t.kind === "sandbox" || t.kind === "ai-credits" ? t.kind : "generic";
75
145
  }
76
- function F(e, t) {
146
+ function pe(e, t) {
77
147
  try {
78
148
  return e()?.getItem(t) === "1";
79
149
  } catch {
80
150
  return !1;
81
151
  }
82
152
  }
83
- function I(e, t, n) {
153
+ function me(e, t, n) {
84
154
  try {
85
155
  e()?.setItem(t, n ? "1" : "0");
86
156
  } catch {}
87
157
  }
88
158
  //#endregion
89
- export { u as ASSISTANT_BOTTOM_CHROME_CLEARANCE, o as ASSISTANT_DIALOG_SELECTOR, j as ASSISTANT_DOCKED_DESKTOP_CLASS, g as ASSISTANT_DOCK_WIDTH_PX, _ as ASSISTANT_DOCK_WIDTH_VAR, A as ASSISTANT_FLOATING_DESKTOP_CLASS, w as ASSISTANT_FLOATING_RIGHT_PX, C as ASSISTANT_FLOATING_WIDTH_PX, f as ASSISTANT_LAUNCHER_LIFT_VAR, m as ASSISTANT_LAUNCHER_RIGHT_INSET, b as ASSISTANT_OVERLAY_DEFAULT_TOP_PX, x as ASSISTANT_OVERLAY_DESKTOP_QUERY, y as ASSISTANT_OVERLAY_TOP_VAR, T as ASSISTANT_PANEL_INSET_VAR, D as ASSISTANT_SHEET_BASE_CLASS, v as ASSISTANT_SHEET_BOTTOM_VAR, O as ASSISTANT_SHEET_COMPACT_CLASS, k as ASSISTANT_SHEET_EXPANDED_CLASS, i as ASSISTANT_TOGGLE_SHORTCUT_LABEL, d as aboveAssistantBottomChrome, p as assistantLauncherBottom, h as assistantLauncherRight, M as assistantOverlayClassName, l as assistantOverlayEscapeAction, t as assistantOverlayKeys, P as assistantQuotaKindOf, s as closestAround, N as createAssistantNavigate, a as isAssistantToggleShortcut, r as nextAssistantOpenState, S as publishedAssistantDockWidth, E as publishedAssistantPanelInset, F as readAssistantOverlayFlag, n as requestedAssistantOpenState, I as writeAssistantOverlayFlag };
159
+ export { s as ASSISTANT_BOTTOM_CHROME_CLEARANCE, M as ASSISTANT_COMPACT_DOCKED_DEFAULT_QUERY, X as ASSISTANT_COMPACT_DOCKED_DESKTOP_CLASS, Z as ASSISTANT_COMPACT_DOCKED_EXPANDED_DESKTOP_CLASS, j as ASSISTANT_COMPACT_DOCKED_MIN_VIEWPORT_PX, I as ASSISTANT_COMPACT_EXPANDED_WIDTH_PX, Q as ASSISTANT_COMPACT_FLOATING_DESKTOP_CLASS, $ as ASSISTANT_COMPACT_FLOATING_EXPANDED_DESKTOP_CLASS, W as ASSISTANT_COMPACT_PANEL_SURFACE_CLASS, U as ASSISTANT_COMPACT_SHEET_BASE_CLASS, J as ASSISTANT_COMPACT_SHEET_EXPANDED_CLASS, q as ASSISTANT_COMPACT_SHEET_PANEL_CLASS, K as ASSISTANT_COMPACT_SHEET_WINDOW_CLASS, Y as ASSISTANT_COMPACT_WINDOW_DESKTOP_CLASS, F as ASSISTANT_COMPACT_WINDOW_HEIGHT_PX, G as ASSISTANT_COMPACT_WINDOW_SURFACE_CLASS, P as ASSISTANT_COMPACT_WINDOW_WIDTH_PX, te as ASSISTANT_DIALOG_SELECTOR, w as ASSISTANT_DOCKED_DESKTOP_CLASS, f as ASSISTANT_DOCK_WIDTH_PX, p as ASSISTANT_DOCK_WIDTH_VAR, C as ASSISTANT_FLOATING_DESKTOP_CLASS, ie as ASSISTANT_FLOATING_RIGHT_PX, y as ASSISTANT_FLOATING_WIDTH_PX, H as ASSISTANT_KEYBOARD_INSET_VAR, l as ASSISTANT_LAUNCHER_LIFT_VAR, d as ASSISTANT_LAUNCHER_RIGHT_INSET, g as ASSISTANT_OVERLAY_DEFAULT_TOP_PX, _ as ASSISTANT_OVERLAY_DESKTOP_QUERY, h as ASSISTANT_OVERLAY_TOP_VAR, ae as ASSISTANT_PANEL_INSET_VAR, b as ASSISTANT_SHEET_BASE_CLASS, m as ASSISTANT_SHEET_BOTTOM_VAR, x as ASSISTANT_SHEET_COMPACT_CLASS, S as ASSISTANT_SHEET_EXPANDED_CLASS, i as ASSISTANT_TOGGLE_SHORTCUT_LABEL, se as ASSISTANT_VIEWPORT_HEIGHT_VAR, z as ASSISTANT_WINDOW_BOTTOM_VAR, B as ASSISTANT_WINDOW_LAUNCHER_CLEARANCE, c as aboveAssistantBottomChrome, le as assistantCompactLauncherVisibility, ce as assistantCompactOverlayClassName, D as assistantCompactOverlayKeys, u as assistantLauncherBottom, re as assistantLauncherRight, T as assistantOverlayClassName, ne as assistantOverlayEscapeAction, t as assistantOverlayKeys, fe as assistantQuotaKindOf, ue as assistantVisualViewportMetrics, V as assistantWindowBottom, a as closestAround, de as createAssistantNavigate, E as isAssistantSurface, ee as isAssistantToggleShortcut, r as nextAssistantOpenState, v as publishedAssistantDockWidth, oe as publishedAssistantPanelInset, L as publishedCompactDockWidth, R as publishedCompactPanelInset, O as readAssistantLayoutChoice, A as readAssistantLegacyDockChoice, pe as readAssistantOverlayFlag, n as requestedAssistantOpenState, N as resolveCompactDocked, k as writeAssistantLayoutChoice, me as writeAssistantOverlayFlag };
@@ -0,0 +1,19 @@
1
+ import { FC } from 'react';
2
+ export interface AssistantWindowHeaderProps {
3
+ /** Switch the same instance to the full assistant. Nothing is re-sent. */
4
+ onOpenFull: () => void;
5
+ /** Hide the window and keep everything in it (CW-M). */
6
+ onMinimize: () => void;
7
+ }
8
+ /**
9
+ * The Quick assistant window's slim header (BOFF-10178, Slice 3):
10
+ * `[✦] Quick assistant ………… [Open full assistant] [Minimize]`.
11
+ *
12
+ * ★ No Close (CW-M): the window is hidden, never closed, so a draft, its
13
+ * attachments and the conversation stay exactly where they are. Closing is the
14
+ * full panel's. The view toggle, language pill and voice cue are Slice 5.
15
+ *
16
+ * ★ Both controls keep one NAME each (WCAG 2.5.3): the tooltip is the same
17
+ * words.
18
+ */
19
+ export declare const AssistantWindowHeader: FC<AssistantWindowHeaderProps>;
@@ -0,0 +1,54 @@
1
+ import { useAssistantTr as e } from "../../i18n.js";
2
+ import { Minus as t, PanelRight as n, Sparkles as r } from "lucide-react";
3
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
+ //#region src/shared/assistant/ui/compact/AssistantWindowHeader.tsx
5
+ var o = "flex size-10 shrink-0 items-center justify-center rounded-button transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]", s = "text-text-secondary hover:bg-bg-sunken hover:text-text-primary", c = ({ onOpenFull: c, onMinimize: l }) => {
6
+ let u = e(), d = u("window.openFull", "Open full assistant"), f = u("window.minimize", "Minimize");
7
+ return /* @__PURE__ */ a("div", {
8
+ className: "flex min-h-14 items-center gap-0.5 border-b border-border-default px-3 py-2",
9
+ "data-testid": "assistant-window-header",
10
+ "data-presentation": "compact",
11
+ "data-surface": "window",
12
+ children: [
13
+ /* @__PURE__ */ a("div", {
14
+ className: "flex min-w-0 flex-1 items-center gap-3",
15
+ children: [/* @__PURE__ */ i("span", {
16
+ 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",
17
+ children: /* @__PURE__ */ i(r, {
18
+ className: "size-4",
19
+ "aria-hidden": "true"
20
+ })
21
+ }), /* @__PURE__ */ i("span", {
22
+ className: "block min-w-0 flex-1 truncate text-base font-semibold leading-tight text-text-primary",
23
+ children: u("window.title", "Quick assistant")
24
+ })]
25
+ }),
26
+ /* @__PURE__ */ i("button", {
27
+ type: "button",
28
+ onClick: c,
29
+ className: `${o} ${s}`,
30
+ "aria-label": d,
31
+ title: d,
32
+ "data-testid": "assistant-open-full",
33
+ children: /* @__PURE__ */ i(n, {
34
+ className: "size-5",
35
+ "aria-hidden": "true"
36
+ })
37
+ }),
38
+ /* @__PURE__ */ i("button", {
39
+ type: "button",
40
+ onClick: l,
41
+ className: `${o} ${s}`,
42
+ "aria-label": f,
43
+ title: f,
44
+ "data-testid": "assistant-minimize",
45
+ children: /* @__PURE__ */ i(t, {
46
+ className: "size-5",
47
+ "aria-hidden": "true"
48
+ })
49
+ })
50
+ ]
51
+ });
52
+ };
53
+ //#endregion
54
+ export { c as AssistantWindowHeader };
@@ -32,6 +32,14 @@ interface Props {
32
32
  * into them; and a product composing its own chrome keeps the icon it has.
33
33
  */
34
34
  showLabel?: boolean;
35
+ /**
36
+ * BOFF-10178 (compact only): keep the source menu inside the VISUAL viewport.
37
+ * On a phone with the keyboard up the sheet is only as tall as the visual
38
+ * viewport leaves it (`--assistant-viewport-height`), and the menu opens
39
+ * upward inside it, so its height is capped to what the sheet can hold and it
40
+ * scrolls past that. Absent, the menu is exactly what it was.
41
+ */
42
+ capMenuHeight?: boolean;
35
43
  }
36
44
  /**
37
45
  * Leading circular "+" attach control in the composer.
@@ -9,88 +9,88 @@ import { useCallback as c, useEffect as l, useMemo as u, useRef as d, useState a
9
9
  import { Camera as p, FileText as m, FolderOpen as h, ImageIcon as g, Plus as _ } from "lucide-react";
10
10
  import { jsx as v, jsxs as y } from "react/jsx-runtime";
11
11
  //#region src/shared/assistant/ui/composer/AssistantAttachmentButton.tsx
12
- var b = "flex size-9 items-center justify-center rounded-full border border-border-subtle bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong disabled:cursor-not-allowed disabled:text-text-muted disabled:hover:bg-bg-elevated", x = `${b} sm:w-auto sm:gap-1.5 sm:px-3`, S = {
12
+ var b = " max-h-[calc(var(--assistant-viewport-height,100dvh)-10rem)] overflow-y-auto", x = "flex size-9 items-center justify-center rounded-full border border-border-subtle bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong disabled:cursor-not-allowed disabled:text-text-muted disabled:hover:bg-bg-elevated", S = `${x} sm:w-auto sm:gap-1.5 sm:px-3`, C = {
13
13
  camera: p,
14
14
  gallery: g,
15
15
  documents: m,
16
16
  files: h
17
- }, C = ({ onFiles: p, onPickerOpen: m, direction: h = "up", canAddMore: g, disabled: C = !1, showLabel: w = !1 }) => {
18
- let T = t(), E = d(null), D = d(null), O = d(null), [k, A] = f(!1), j = d(m);
19
- j.current = m;
20
- let M = u(() => n(r()), []), N = i(a()), P = g || N, F = g ? N ? `${T("composer.attach", "Attach files or photos")} · ${T("composer.connectSource", "Connect a source")}` : T("composer.attach", "Attach files or photos") : T("composer.attachFull", "Attachment limit reached ({{max}})", { max: 5 }), I = w && g && N ? T("composer.connectSource", "Connect a source") : null, L = s(O, () => A(!1));
17
+ }, w = ({ onFiles: p, onPickerOpen: m, direction: h = "up", canAddMore: g, disabled: w = !1, showLabel: T = !1, capMenuHeight: E = !1 }) => {
18
+ let D = t(), O = d(null), k = d(null), A = d(null), [j, M] = f(!1), N = d(m);
19
+ N.current = m;
20
+ let P = u(() => n(r()), []), F = i(a()), I = g || F, L = g ? F ? `${D("composer.attach", "Attach files or photos")} · ${D("composer.connectSource", "Connect a source")}` : D("composer.attach", "Attach files or photos") : D("composer.attachFull", "Attachment limit reached ({{max}})", { max: 5 }), R = T && g && F ? D("composer.connectSource", "Connect a source") : null, z = s(A, () => M(!1));
21
21
  l(() => {
22
- if (!k) return;
22
+ if (!j) return;
23
23
  let e = (e) => {
24
24
  let t = e.target;
25
- D.current?.contains(t) || O.current?.contains(t) || A(!1);
25
+ k.current?.contains(t) || A.current?.contains(t) || M(!1);
26
26
  };
27
27
  return window.addEventListener("mousedown", e), () => {
28
28
  window.removeEventListener("mousedown", e);
29
29
  };
30
- }, [k]);
31
- let R = c((e) => {
30
+ }, [j]);
31
+ let B = c((e) => {
32
32
  p(e.target.files), e.target.value = "";
33
- }, [p]), z = c((e) => {
34
- let t = E.current;
35
- t && (t.accept = e.accept, t.multiple = e.multiple, e.capture ? t.setAttribute("capture", e.capture) : t.removeAttribute("capture"), A(!1), j.current?.(), t.click());
33
+ }, [p]), V = c((e) => {
34
+ let t = O.current;
35
+ t && (t.accept = e.accept, t.multiple = e.multiple, e.capture ? t.setAttribute("capture", e.capture) : t.removeAttribute("capture"), M(!1), N.current?.(), t.click());
36
36
  }, []);
37
37
  return /* @__PURE__ */ y("div", {
38
38
  className: "relative flex-shrink-0",
39
39
  children: [
40
40
  /* @__PURE__ */ v("input", {
41
- ref: E,
41
+ ref: O,
42
42
  type: "file",
43
43
  multiple: !0,
44
44
  className: "hidden",
45
- onChange: R,
45
+ onChange: B,
46
46
  tabIndex: -1,
47
47
  "aria-hidden": "true",
48
48
  "data-testid": "assistant-attach-input"
49
49
  }),
50
50
  /* @__PURE__ */ y("button", {
51
- ref: O,
51
+ ref: A,
52
52
  type: "button",
53
- onClick: () => A((e) => !e),
54
- disabled: C || !P,
55
- title: F,
56
- "aria-label": F,
53
+ onClick: () => M((e) => !e),
54
+ disabled: w || !I,
55
+ title: L,
56
+ "aria-label": L,
57
57
  "aria-haspopup": "menu",
58
- "aria-expanded": k,
59
- className: I ? x : b,
58
+ "aria-expanded": j,
59
+ className: R ? S : x,
60
60
  "data-testid": "assistant-attach",
61
- children: [/* @__PURE__ */ v(_, { className: "size-4" }), I ? /* @__PURE__ */ v("span", {
61
+ children: [/* @__PURE__ */ v(_, { className: "size-4" }), R ? /* @__PURE__ */ v("span", {
62
62
  className: "hidden max-w-[10rem] truncate text-[13px] font-medium leading-tight sm:block",
63
63
  "data-testid": "assistant-attach-label",
64
- children: I
64
+ children: R
65
65
  }) : null]
66
66
  }),
67
- k ? /* @__PURE__ */ v(e, {
68
- ...L,
67
+ j ? /* @__PURE__ */ v(e, {
68
+ ...z,
69
69
  children: /* @__PURE__ */ y("div", {
70
- ref: D,
70
+ ref: k,
71
71
  role: "menu",
72
- "aria-label": T("composer.attachSource", "Attach from"),
73
- className: `absolute ${h === "up" ? "bottom-full mb-2" : "top-full mt-2"} left-0 z-30 min-w-44 overflow-hidden rounded-xl border border-border-default bg-bg-elevated py-1 shadow-lg`,
72
+ "aria-label": D("composer.attachSource", "Attach from"),
73
+ className: `absolute ${h === "up" ? "bottom-full mb-2" : "top-full mt-2"} left-0 z-30 min-w-44 overflow-hidden rounded-xl border border-border-default bg-bg-elevated py-1 shadow-lg${E ? b : ""}`,
74
74
  "data-testid": "assistant-attach-menu",
75
- children: [M.map((e) => {
76
- let t = S[e.id];
75
+ children: [P.map((e) => {
76
+ let t = C[e.id];
77
77
  return /* @__PURE__ */ y("button", {
78
78
  type: "button",
79
79
  role: "menuitem",
80
80
  disabled: !g,
81
- onClick: () => z(e),
81
+ onClick: () => V(e),
82
82
  className: "flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm text-text-primary transition-colors hover:bg-bg-sunken focus:outline-none focus-visible:bg-bg-sunken disabled:cursor-not-allowed disabled:text-text-muted disabled:hover:bg-transparent",
83
83
  "data-testid": `assistant-attach-source-${e.id}`,
84
84
  children: [/* @__PURE__ */ v(t, {
85
85
  className: "size-4 flex-shrink-0 text-text-secondary",
86
86
  "aria-hidden": "true"
87
- }), T(e.labelKey, e.labelFallback)]
87
+ }), D(e.labelKey, e.labelFallback)]
88
88
  }, e.id);
89
- }), /* @__PURE__ */ v(o, { onActivate: () => A(!1) })]
89
+ }), /* @__PURE__ */ v(o, { onActivate: () => M(!1) })]
90
90
  })
91
91
  }) : null
92
92
  ]
93
93
  });
94
94
  };
95
95
  //#endregion
96
- export { C as AssistantAttachmentButton };
96
+ export { w as AssistantAttachmentButton };
@@ -324,7 +324,8 @@ var m = ({ writeAccessDenied: m = !1, onSend: ye, disabled: h = !1, readOnly: be
324
324
  onFiles: R,
325
325
  canAddMore: $e,
326
326
  disabled: S || O,
327
- showLabel: !0
327
+ showLabel: !0,
328
+ capMenuHeight: !0
328
329
  }),
329
330
  /* @__PURE__ */ f(se, {
330
331
  mode: Se,
@@ -0,0 +1,3 @@
1
+ import { RefObject } from 'react';
2
+ /** Publish the visual viewport's height and the keyboard inset on `target` while `enabled`. */
3
+ export declare function useAssistantVisualViewport(target: RefObject<HTMLElement | null>, enabled: boolean): void;
@@ -0,0 +1,24 @@
1
+ import { ASSISTANT_KEYBOARD_INSET_VAR as e, ASSISTANT_VIEWPORT_HEIGHT_VAR as t, assistantVisualViewportMetrics as n } from "./assistantOverlayState.js";
2
+ import { useEffect as r } from "react";
3
+ //#region src/shared/assistant/ui/useAssistantVisualViewport.ts
4
+ function i(i, a) {
5
+ r(() => {
6
+ if (!a || typeof window > "u") return;
7
+ let r = i.current, o = window.visualViewport;
8
+ if (!r || !o) return;
9
+ let s = () => {
10
+ let { heightPx: i, keyboardInsetPx: a } = n({
11
+ layoutHeight: document.documentElement.clientHeight || window.innerHeight,
12
+ viewportHeight: o.height,
13
+ viewportOffsetTop: o.offsetTop,
14
+ scale: o.scale
15
+ });
16
+ r.style.setProperty(t, `${i}px`), r.style.setProperty(e, `${a}px`);
17
+ };
18
+ return s(), o.addEventListener("resize", s), o.addEventListener("scroll", s), window.addEventListener("resize", s), () => {
19
+ o.removeEventListener("resize", s), o.removeEventListener("scroll", s), window.removeEventListener("resize", s), r.style.removeProperty(t), r.style.removeProperty(e);
20
+ };
21
+ }, [i, a]);
22
+ }
23
+ //#endregion
24
+ export { i as useAssistantVisualViewport };