@sybilion/uilib 1.3.86 → 1.3.88

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.
@@ -180,7 +180,6 @@ Host provides:
180
180
  - \`chartData\`, \`forecastData\` built from API
181
181
  - \`timeRange\` / \`onTimeRangeChange\` or brush-only range
182
182
  - Optional \`mode\`: pin | intervals | thresholds + overlay state
183
- - Analysis selector and fetch outside widget
184
183
 
185
184
  Report tile: \`dataset_card\` — host loads dataset + analysis; chart inside dashboard card.
186
185
 
@@ -201,7 +200,7 @@ Host provides:
201
200
  - Controlled \`selectedDriver\` + \`setSelectedDriver\`
202
201
  - \`isLoading\` while fetching drivers
203
202
 
204
- Report tile: \`drivers_map\` — tile resolves analysis id, fetches drivers, passes list + selection (see EmbeddedAnalysisSelector pattern).
203
+ Report tile: \`drivers_map\` — tile resolves analysis id, fetches drivers, passes list + selection.
205
204
 
206
205
  Requires: \`drivers\`; \`isLoading\`; \`selectedDriver\`; \`setSelectedDriver\`.
207
206
 
@@ -217,10 +216,9 @@ Not when: simple forecast card or driver backtests — use ChartAreaInteractive
217
216
  Host provides:
218
217
 
219
218
  - \`performanceData\` (PerformanceChartPayload) and \`historicalData\` from performance API
220
- - Analysis selection and fetch outside widget
221
219
  - Optional \`forecastData\`, \`customPerformanceMatrix\`, \`userSeries\` for spaghetti
222
220
 
223
- Report tile: \`performance_chart\` — host loads performance payload + dataset series; built-in analysis selector.
221
+ Report tile: \`performance_chart\` — host loads performance payload + dataset series.
224
222
 
225
223
  Requires: \`performanceData\` — model/drift forecasts and metrics; \`historicalData\` — baseline series; \`loading\` / \`chartLoading\` / \`performanceDataLoading\` — spinners; \`runAnalysisHint\` / \`statusHint\` — empty states.
226
224
 
@@ -240,9 +238,9 @@ Host provides:
240
238
  - \`seriesInitKey\` when selected analysis changes
241
239
  - \`viewTab\` / \`onViewTabChange\`: \`lagged\` (calendar-aligned, raw dates) or \`overlapped\` (driver series shifted backward by parsed lag months)
242
240
 
243
- View tabs: host should render uilib \`Tabs variant="button"\` with **Lagged** / **Overlapped** in the toolbar (analysis selector left, tabs right). Chart applies \`applyDriversComparisonViewToPayload\` internally.
241
+ View tabs: host should render uilib \`Tabs variant="button"\` with **Lagged** / **Overlapped** in the toolbar. Chart applies \`applyDriversComparisonViewToPayload\` internally.
244
242
 
245
- Report tile: \`drivers_comparison_chart\` — host loads normalized backtests payload + dataset historical; built-in analysis selector.
243
+ Report tile: \`drivers_comparison_chart\` — host loads normalized backtests payload + dataset historical.
246
244
 
247
245
  Requires: \`payload\` — target + driver normalized_series; \`loading\` / \`chartLoading\` — spinners; \`seriesInitKey\` — reset visible series on analysis or view tab change; \`runAnalysisHint\` / \`statusHint\` — empty/error text.
248
246
 
@@ -179,7 +179,6 @@ Host provides:
179
179
  - `chartData`, `forecastData` built from API
180
180
  - `timeRange` / `onTimeRangeChange` or brush-only range
181
181
  - Optional `mode`: pin | intervals | thresholds + overlay state
182
- - Analysis selector and fetch outside widget
183
182
 
184
183
  Report tile: `dataset_card` — host loads dataset + analysis; chart inside dashboard card.
185
184
 
@@ -200,7 +199,7 @@ Host provides:
200
199
  - Controlled `selectedDriver` + `setSelectedDriver`
201
200
  - `isLoading` while fetching drivers
202
201
 
203
- Report tile: `drivers_map` — tile resolves analysis id, fetches drivers, passes list + selection (see EmbeddedAnalysisSelector pattern).
202
+ Report tile: `drivers_map` — tile resolves analysis id, fetches drivers, passes list + selection.
204
203
 
205
204
  Requires: `drivers`; `isLoading`; `selectedDriver`; `setSelectedDriver`.
206
205
 
@@ -216,10 +215,9 @@ Not when: simple forecast card or driver backtests — use ChartAreaInteractive
216
215
  Host provides:
217
216
 
218
217
  - `performanceData` (PerformanceChartPayload) and `historicalData` from performance API
219
- - Analysis selection and fetch outside widget
220
218
  - Optional `forecastData`, `customPerformanceMatrix`, `userSeries` for spaghetti
221
219
 
222
- Report tile: `performance_chart` — host loads performance payload + dataset series; built-in analysis selector.
220
+ Report tile: `performance_chart` — host loads performance payload + dataset series.
223
221
 
224
222
  Requires: `performanceData` — model/drift forecasts and metrics; `historicalData` — baseline series; `loading` / `chartLoading` / `performanceDataLoading` — spinners; `runAnalysisHint` / `statusHint` — empty states.
225
223
 
@@ -239,9 +237,9 @@ Host provides:
239
237
  - `seriesInitKey` when selected analysis changes
240
238
  - `viewTab` / `onViewTabChange`: `lagged` (calendar-aligned, raw dates) or `overlapped` (driver series shifted backward by parsed lag months)
241
239
 
242
- View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar (analysis selector left, tabs right). Chart applies `applyDriversComparisonViewToPayload` internally.
240
+ View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar. Chart applies `applyDriversComparisonViewToPayload` internally.
243
241
 
244
- Report tile: `drivers_comparison_chart` — host loads normalized backtests payload + dataset historical; built-in analysis selector.
242
+ Report tile: `drivers_comparison_chart` — host loads normalized backtests payload + dataset historical.
245
243
 
246
244
  Requires: `payload` — target + driver normalized_series; `loading` / `chartLoading` — spinners; `seriesInitKey` — reset visible series on analysis or view tab change; `runAnalysisHint` / `statusHint` — empty/error text.
247
245
 
@@ -199,7 +199,6 @@ Host provides:
199
199
  - \`chartData\`, \`forecastData\` built from API
200
200
  - \`timeRange\` / \`onTimeRangeChange\` or brush-only range
201
201
  - Optional \`mode\`: pin | intervals | thresholds + overlay state
202
- - Analysis selector and fetch outside widget
203
202
 
204
203
  Report tile: \`dataset_card\` — host loads dataset + analysis; chart inside dashboard card.
205
204
 
@@ -220,7 +219,7 @@ Host provides:
220
219
  - Controlled \`selectedDriver\` + \`setSelectedDriver\`
221
220
  - \`isLoading\` while fetching drivers
222
221
 
223
- Report tile: \`drivers_map\` — tile resolves analysis id, fetches drivers, passes list + selection (see EmbeddedAnalysisSelector pattern).
222
+ Report tile: \`drivers_map\` — tile resolves analysis id, fetches drivers, passes list + selection.
224
223
 
225
224
  Requires: \`drivers\`; \`isLoading\`; \`selectedDriver\`; \`setSelectedDriver\`.
226
225
 
@@ -236,10 +235,9 @@ Not when: simple forecast card or driver backtests — use ChartAreaInteractive
236
235
  Host provides:
237
236
 
238
237
  - \`performanceData\` (PerformanceChartPayload) and \`historicalData\` from performance API
239
- - Analysis selection and fetch outside widget
240
238
  - Optional \`forecastData\`, \`customPerformanceMatrix\`, \`userSeries\` for spaghetti
241
239
 
242
- Report tile: \`performance_chart\` — host loads performance payload + dataset series; built-in analysis selector.
240
+ Report tile: \`performance_chart\` — host loads performance payload + dataset series.
243
241
 
244
242
  Requires: \`performanceData\` — model/drift forecasts and metrics; \`historicalData\` — baseline series; \`loading\` / \`chartLoading\` / \`performanceDataLoading\` — spinners; \`runAnalysisHint\` / \`statusHint\` — empty states.
245
243
 
@@ -259,9 +257,9 @@ Host provides:
259
257
  - \`seriesInitKey\` when selected analysis changes
260
258
  - \`viewTab\` / \`onViewTabChange\`: \`lagged\` (calendar-aligned, raw dates) or \`overlapped\` (driver series shifted backward by parsed lag months)
261
259
 
262
- View tabs: host should render uilib \`Tabs variant="button"\` with **Lagged** / **Overlapped** in the toolbar (analysis selector left, tabs right). Chart applies \`applyDriversComparisonViewToPayload\` internally.
260
+ View tabs: host should render uilib \`Tabs variant="button"\` with **Lagged** / **Overlapped** in the toolbar. Chart applies \`applyDriversComparisonViewToPayload\` internally.
263
261
 
264
- Report tile: \`drivers_comparison_chart\` — host loads normalized backtests payload + dataset historical; built-in analysis selector.
262
+ Report tile: \`drivers_comparison_chart\` — host loads normalized backtests payload + dataset historical.
265
263
 
266
264
  Requires: \`payload\` — target + driver normalized_series; \`loading\` / \`chartLoading\` — spinners; \`seriesInitKey\` — reset visible series on analysis or view tab change; \`runAnalysisHint\` / \`statusHint\` — empty/error text.
267
265
 
@@ -198,7 +198,6 @@ Host provides:
198
198
  - `chartData`, `forecastData` built from API
199
199
  - `timeRange` / `onTimeRangeChange` or brush-only range
200
200
  - Optional `mode`: pin | intervals | thresholds + overlay state
201
- - Analysis selector and fetch outside widget
202
201
 
203
202
  Report tile: `dataset_card` — host loads dataset + analysis; chart inside dashboard card.
204
203
 
@@ -219,7 +218,7 @@ Host provides:
219
218
  - Controlled `selectedDriver` + `setSelectedDriver`
220
219
  - `isLoading` while fetching drivers
221
220
 
222
- Report tile: `drivers_map` — tile resolves analysis id, fetches drivers, passes list + selection (see EmbeddedAnalysisSelector pattern).
221
+ Report tile: `drivers_map` — tile resolves analysis id, fetches drivers, passes list + selection.
223
222
 
224
223
  Requires: `drivers`; `isLoading`; `selectedDriver`; `setSelectedDriver`.
225
224
 
@@ -235,10 +234,9 @@ Not when: simple forecast card or driver backtests — use ChartAreaInteractive
235
234
  Host provides:
236
235
 
237
236
  - `performanceData` (PerformanceChartPayload) and `historicalData` from performance API
238
- - Analysis selection and fetch outside widget
239
237
  - Optional `forecastData`, `customPerformanceMatrix`, `userSeries` for spaghetti
240
238
 
241
- Report tile: `performance_chart` — host loads performance payload + dataset series; built-in analysis selector.
239
+ Report tile: `performance_chart` — host loads performance payload + dataset series.
242
240
 
243
241
  Requires: `performanceData` — model/drift forecasts and metrics; `historicalData` — baseline series; `loading` / `chartLoading` / `performanceDataLoading` — spinners; `runAnalysisHint` / `statusHint` — empty states.
244
242
 
@@ -258,9 +256,9 @@ Host provides:
258
256
  - `seriesInitKey` when selected analysis changes
259
257
  - `viewTab` / `onViewTabChange`: `lagged` (calendar-aligned, raw dates) or `overlapped` (driver series shifted backward by parsed lag months)
260
258
 
261
- View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar (analysis selector left, tabs right). Chart applies `applyDriversComparisonViewToPayload` internally.
259
+ View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar. Chart applies `applyDriversComparisonViewToPayload` internally.
262
260
 
263
- Report tile: `drivers_comparison_chart` — host loads normalized backtests payload + dataset historical; built-in analysis selector.
261
+ Report tile: `drivers_comparison_chart` — host loads normalized backtests payload + dataset historical.
264
262
 
265
263
  Requires: `payload` — target + driver normalized_series; `loading` / `chartLoading` — spinners; `seriesInitKey` — reset visible series on analysis or view tab change; `runAnalysisHint` / `statusHint` — empty/error text.
266
264
 
@@ -29,6 +29,7 @@ function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, r
29
29
  model.onOpenChange(true);
30
30
  },
31
31
  openNewChatWithPrefill: model.openNewChatWithPrefill,
32
+ openNewChatAndSubmit: model.openNewChatAndSubmit,
32
33
  };
33
34
  }
34
35
  if (inline) {
@@ -40,6 +40,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
40
40
  const [promptLinkPrefill, setPromptLinkPrefill] = useState(null);
41
41
  /** Deduplicate Strict Mode double `useEffect` on the same mount. */
42
42
  const promptParamHandledInEffectRef = useRef(false);
43
+ const pendingOpenChatAndSubmitRef = useRef(null);
43
44
  const prevShellChatPanelOpenRef = useRef(shellChatPanelOpen);
44
45
  /** This instance requested shell chat width (ignore other ChatSheet instances on the page). */
45
46
  const openedShellChatRef = useRef(false);
@@ -200,6 +201,18 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
200
201
  setPromptLinkPrefill(trimmed || null);
201
202
  onOpenChange(true);
202
203
  }, [startEmptyNewChat, onOpenChange]);
204
+ const openNewChatAndSubmit = useCallback((prompt) => {
205
+ const sessionId = startEmptyNewChat();
206
+ if (sessionId == null) {
207
+ logger.warn('Chat submit: sign in to use the assistant.');
208
+ return;
209
+ }
210
+ const trimmed = prompt.trim();
211
+ if (!trimmed)
212
+ return;
213
+ pendingOpenChatAndSubmitRef.current = { sessionId, prompt: trimmed };
214
+ onOpenChange(true);
215
+ }, [startEmptyNewChat, onOpenChange]);
203
216
  /**
204
217
  * App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
205
218
  * from `location.search`). If the selected session already has messages, `newChat()` first.
@@ -498,7 +511,14 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
498
511
  onScriptComplete,
499
512
  transformSendPayload,
500
513
  ]);
501
- const submitPreset = useCallback(async (preset) => {
514
+ useEffect(() => {
515
+ const pending = pendingOpenChatAndSubmitRef.current;
516
+ if (!pending || currentChatId !== pending.sessionId)
517
+ return;
518
+ pendingOpenChatAndSubmitRef.current = null;
519
+ void handlePromptSubmit(pending.prompt);
520
+ }, [currentChatId, handlePromptSubmit]);
521
+ const submitPreset = useCallback(async (preset, options) => {
502
522
  const script = preset.script;
503
523
  const scriptGraph = isPresetScriptGraph(script);
504
524
  const hasLinearScript = Array.isArray(script) && script.length > 0;
@@ -511,7 +531,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
511
531
  if (!currentChatId)
512
532
  return;
513
533
  endLocalDemoFlow(currentChatId);
514
- await handlePromptSubmit(preset.text);
534
+ await handlePromptSubmit(options?.message ?? preset.text);
515
535
  return;
516
536
  }
517
537
  setLocalUiBusy(true);
@@ -798,7 +818,10 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
798
818
  chatWidthPx,
799
819
  })) {
800
820
  // Chat open uses `startViewTransition`; nested transition here aborts first → AbortError overlay.
801
- setSidebarNavOpen(false, { viewTransition: false });
821
+ setSidebarNavOpen(false, {
822
+ viewTransition: false,
823
+ layoutAutoClose: true,
824
+ });
802
825
  }
803
826
  };
804
827
  collapseNavIfNoSpace();
@@ -948,6 +971,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
948
971
  toggleOpen,
949
972
  newChat: startEmptyNewChat,
950
973
  openNewChatWithPrefill,
974
+ openNewChatAndSubmit,
951
975
  chatPanelContainer,
952
976
  };
953
977
  }
@@ -85,6 +85,8 @@ function SidebarProvider({ defaultOpen = getCookie('isSidebarOpen') ?? true, cla
85
85
  const [chatPanelOpen, _setChatPanelOpen] = useState(false);
86
86
  const sidebarWidthRef = useRef(sidebarWidthPx);
87
87
  const chatWidthRef = useRef(chatWidthPx);
88
+ /** Left nav was auto-closed because chat opened on a narrow shell; reopen when chat closes. */
89
+ const sidebarAutoClosedForChatRef = useRef(false);
88
90
  sidebarWidthRef.current = sidebarWidthPx;
89
91
  chatWidthRef.current = chatWidthPx;
90
92
  const mergeWrapperRef = useCallback((el) => {
@@ -226,10 +228,8 @@ function SidebarProvider({ defaultOpen = getCookie('isSidebarOpen') ?? true, cla
226
228
  }
227
229
  }
228
230
  else if (openingChat && isOpen) {
231
+ sidebarAutoClosedForChatRef.current = true;
229
232
  setIsOpen(false);
230
- if (getCookiePreferences(userId)?.functional) {
231
- setCookie('isSidebarOpen', 'false', 60 * 60 * 24 * 7);
232
- }
233
233
  }
234
234
  }, [
235
235
  isSidebarSheetLayout,
@@ -238,18 +238,34 @@ function SidebarProvider({ defaultOpen = getCookie('isSidebarOpen') ?? true, cla
238
238
  chatPanelOpen,
239
239
  sidebarWidthPx,
240
240
  chatWidthPx,
241
- userId,
242
241
  ]);
243
242
  const setChatPanelOpen = useCallback((open) => {
244
243
  if (open) {
245
244
  closeOppositeSidebarIfNoSpace(false, true);
245
+ _setChatPanelOpen(true);
246
+ return;
247
+ }
248
+ _setChatPanelOpen(false);
249
+ if (sidebarAutoClosedForChatRef.current) {
250
+ sidebarAutoClosedForChatRef.current = false;
251
+ setIsOpen(true);
252
+ if (getCookiePreferences(userId)?.functional) {
253
+ setCookie('isSidebarOpen', 'true', 60 * 60 * 24 * 7);
254
+ }
246
255
  }
247
- _setChatPanelOpen(open);
248
- }, [closeOppositeSidebarIfNoSpace]);
256
+ }, [closeOppositeSidebarIfNoSpace, userId]);
249
257
  const setOpen = useCallback((value, options) => {
258
+ const layoutAutoClose = options?.layoutAutoClose === true;
250
259
  if (value) {
260
+ sidebarAutoClosedForChatRef.current = false;
251
261
  closeOppositeSidebarIfNoSpace(true, false);
252
262
  }
263
+ else if (layoutAutoClose) {
264
+ sidebarAutoClosedForChatRef.current = true;
265
+ }
266
+ else {
267
+ sidebarAutoClosedForChatRef.current = false;
268
+ }
253
269
  const useViewTransition = options?.viewTransition !== false &&
254
270
  !isSidebarSheetLayout &&
255
271
  'startViewTransition' in document &&
@@ -262,7 +278,7 @@ function SidebarProvider({ defaultOpen = getCookie('isSidebarOpen') ?? true, cla
262
278
  else {
263
279
  setIsOpen(value);
264
280
  }
265
- if (getCookiePreferences(userId)?.functional) {
281
+ if (!layoutAutoClose && getCookiePreferences(userId)?.functional) {
266
282
  setCookie('isSidebarOpen', value.toString(), 60 * 60 * 24 * 7);
267
283
  }
268
284
  }, [isSidebarSheetLayout, userId, closeOppositeSidebarIfNoSpace]);
@@ -1,6 +1,10 @@
1
1
  import type { ChatPreset } from '#uilib/components/ui/Chat/Chat.types';
2
+ export type SubmitPresetOptions = {
3
+ /** Composer text when preset row includes addon context (e.g. live news URL). */
4
+ message?: string;
5
+ };
2
6
  export type ChatEmptyStateContext = {
3
- submitPreset: (preset: ChatPreset) => void | Promise<void>;
7
+ submitPreset: (preset: ChatPreset, options?: SubmitPresetOptions) => void | Promise<void>;
4
8
  };
5
9
  export interface ChatEmptyStateProps {
6
10
  icon?: React.ReactNode;
@@ -5,6 +5,8 @@ export type ChatSheetActions = {
5
5
  openNewChat: () => void;
6
6
  /** Starts a new chat, opens the panel, and pre-fills the composer (user sends manually). */
7
7
  openNewChatWithPrefill: (prompt: string) => void;
8
+ /** Starts a new chat, opens panel, and sends prompt immediately. */
9
+ openNewChatAndSubmit: (prompt: string) => void;
8
10
  };
9
11
  export interface ChatSheetProps extends Omit<UseChatPanelChromeModelInput, 'embedAsPage'> {
10
12
  title?: string;
@@ -44,6 +44,8 @@ export type UseChatPanelChromeModelResult = {
44
44
  newChat: () => void;
45
45
  /** New session + open panel + one-shot composer pre-fill (does not send). */
46
46
  openNewChatWithPrefill: (prompt: string) => void;
47
+ /** New session + open panel + immediate send. */
48
+ openNewChatAndSubmit: (prompt: string) => void;
47
49
  chatPanelContainer: HTMLElement | null;
48
50
  };
49
51
  export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, renderSystemMessage, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, copyHistoryOnNewChat, transformSendPayload, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
@@ -17,7 +17,7 @@ export type { ChatPromptComposerHandle } from './ChatPrompt/ChatPromptComposer';
17
17
  export { CHAT_PROMPT_COMMAND_CHIP_CLASS, chatPromptChipHtml, createChatPromptComposerHandle, getChatPromptTokenRangeBeforePos, insertChatPromptContentAtCaret, } from './ChatPrompt/chatPromptComposerInsert';
18
18
  export type { ChatPromptComposerInsertOptions } from './ChatPrompt/ChatPromptComposer.types';
19
19
  export { ChatPresets } from './ChatPresets';
20
- export type { ChatEmptyStateConfig, ChatEmptyStateContext, ChatEmptyStateProps, } from './ChatEmptyState/ChatEmptyState.types';
20
+ export type { ChatEmptyStateConfig, ChatEmptyStateContext, ChatEmptyStateProps, SubmitPresetOptions, } from './ChatEmptyState/ChatEmptyState.types';
21
21
  export type { Chat as ChatType, ChatAttachmentDropItem, ChatMessageClassNames, ChatMessageTextClassNames, ChatMeta, ChatMetaValue, ChatSendMessagePayload, ChatMessageProps, ChatProps, ChatPreset as ChatPresetType, Message, UserTextFileAttachment, } from './Chat.types';
22
22
  export { MessageRole } from './Chat.types';
23
23
  export type { SlashCommandItem, SlashItemCommandContext, SlashOnItemCommand, } from '#uilib/tiptap/slash-mention/types';
@@ -12,6 +12,8 @@ type SetPanelWidthOptions = {
12
12
  /** Pass `viewTransition: false` to avoid nesting `document.startViewTransition` (e.g. chat open already animating). */
13
13
  export type SetSidebarOpenOptions = {
14
14
  viewTransition?: boolean;
15
+ /** Nav closed for layout (chat/width); restore when chat closes — do not persist closed cookie. */
16
+ layoutAutoClose?: boolean;
15
17
  };
16
18
  type SidebarContextProps = {
17
19
  isOpen: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.3.86",
3
+ "version": "1.3.88",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -10,7 +10,6 @@ Host provides:
10
10
  - `chartData`, `forecastData` built from API
11
11
  - `timeRange` / `onTimeRangeChange` or brush-only range
12
12
  - Optional `mode`: pin | intervals | thresholds + overlay state
13
- - Analysis selector and fetch outside widget
14
13
 
15
14
  Report tile: `dataset_card` — host loads dataset + analysis; chart inside dashboard card.
16
15
 
@@ -1,7 +1,15 @@
1
1
  import type { ChatPreset } from '#uilib/components/ui/Chat/Chat.types';
2
2
 
3
+ export type SubmitPresetOptions = {
4
+ /** Composer text when preset row includes addon context (e.g. live news URL). */
5
+ message?: string;
6
+ };
7
+
3
8
  export type ChatEmptyStateContext = {
4
- submitPreset: (preset: ChatPreset) => void | Promise<void>;
9
+ submitPreset: (
10
+ preset: ChatPreset,
11
+ options?: SubmitPresetOptions,
12
+ ) => void | Promise<void>;
5
13
  };
6
14
 
7
15
  export interface ChatEmptyStateProps {
@@ -13,6 +13,8 @@ export type ChatSheetActions = {
13
13
  openNewChat: () => void;
14
14
  /** Starts a new chat, opens the panel, and pre-fills the composer (user sends manually). */
15
15
  openNewChatWithPrefill: (prompt: string) => void;
16
+ /** Starts a new chat, opens panel, and sends prompt immediately. */
17
+ openNewChatAndSubmit: (prompt: string) => void;
16
18
  };
17
19
 
18
20
  export interface ChatSheetProps extends Omit<
@@ -79,6 +81,7 @@ export function ChatSheet({
79
81
  model.onOpenChange(true);
80
82
  },
81
83
  openNewChatWithPrefill: model.openNewChatWithPrefill,
84
+ openNewChatAndSubmit: model.openNewChatAndSubmit,
82
85
  };
83
86
  }
84
87
 
@@ -51,7 +51,10 @@ import {
51
51
  } from '../../Sidebar/Sidebar';
52
52
  import { Chat } from '../Chat';
53
53
  import type { ChatChromeProps } from '../ChatChrome';
54
- import type { ChatAttachmentDropItem, ChatPresetsRenderOptions } from '../ChatChrome/ChatChrome.types';
54
+ import type {
55
+ ChatAttachmentDropItem,
56
+ ChatPresetsRenderOptions,
57
+ } from '../ChatChrome/ChatChrome.types';
55
58
  import type { ChatEmptyStateConfig } from '../ChatEmptyState/ChatEmptyState.types';
56
59
  import type { ChatEmptyStateProps } from '../ChatEmptyState/ChatEmptyState.types';
57
60
  import {
@@ -118,6 +121,8 @@ export type UseChatPanelChromeModelResult = {
118
121
  newChat: () => void;
119
122
  /** New session + open panel + one-shot composer pre-fill (does not send). */
120
123
  openNewChatWithPrefill: (prompt: string) => void;
124
+ /** New session + open panel + immediate send. */
125
+ openNewChatAndSubmit: (prompt: string) => void;
121
126
  chatPanelContainer: HTMLElement | null;
122
127
  };
123
128
 
@@ -209,6 +214,10 @@ export function useChatPanelChromeModel({
209
214
  );
210
215
  /** Deduplicate Strict Mode double `useEffect` on the same mount. */
211
216
  const promptParamHandledInEffectRef = useRef(false);
217
+ const pendingOpenChatAndSubmitRef = useRef<{
218
+ sessionId: string;
219
+ prompt: string;
220
+ } | null>(null);
212
221
  const prevShellChatPanelOpenRef = useRef(shellChatPanelOpen);
213
222
  /** This instance requested shell chat width (ignore other ChatSheet instances on the page). */
214
223
  const openedShellChatRef = useRef(false);
@@ -405,6 +414,23 @@ export function useChatPanelChromeModel({
405
414
  [startEmptyNewChat, onOpenChange],
406
415
  );
407
416
 
417
+ const openNewChatAndSubmit = useCallback(
418
+ (prompt: string) => {
419
+ const sessionId = startEmptyNewChat();
420
+ if (sessionId == null) {
421
+ logger.warn('Chat submit: sign in to use the assistant.');
422
+ return;
423
+ }
424
+
425
+ const trimmed = prompt.trim();
426
+ if (!trimmed) return;
427
+
428
+ pendingOpenChatAndSubmitRef.current = { sessionId, prompt: trimmed };
429
+ onOpenChange(true);
430
+ },
431
+ [startEmptyNewChat, onOpenChange],
432
+ );
433
+
408
434
  /**
409
435
  * App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
410
436
  * from `location.search`). If the selected session already has messages, `newChat()` first.
@@ -763,8 +789,15 @@ export function useChatPanelChromeModel({
763
789
  ],
764
790
  );
765
791
 
792
+ useEffect(() => {
793
+ const pending = pendingOpenChatAndSubmitRef.current;
794
+ if (!pending || currentChatId !== pending.sessionId) return;
795
+ pendingOpenChatAndSubmitRef.current = null;
796
+ void handlePromptSubmit(pending.prompt);
797
+ }, [currentChatId, handlePromptSubmit]);
798
+
766
799
  const submitPreset = useCallback(
767
- async (preset: ChatPreset) => {
800
+ async (preset: ChatPreset, options?: { message?: string }) => {
768
801
  const script = preset.script;
769
802
  const scriptGraph = isPresetScriptGraph(script);
770
803
  const hasLinearScript = Array.isArray(script) && script.length > 0;
@@ -779,7 +812,7 @@ export function useChatPanelChromeModel({
779
812
  if (!isLocalDemo) {
780
813
  if (!currentChatId) return;
781
814
  endLocalDemoFlow(currentChatId);
782
- await handlePromptSubmit(preset.text);
815
+ await handlePromptSubmit(options?.message ?? preset.text);
783
816
  return;
784
817
  }
785
818
 
@@ -1089,7 +1122,10 @@ export function useChatPanelChromeModel({
1089
1122
  })
1090
1123
  ) {
1091
1124
  // Chat open uses `startViewTransition`; nested transition here aborts first → AbortError overlay.
1092
- setSidebarNavOpen(false, { viewTransition: false });
1125
+ setSidebarNavOpen(false, {
1126
+ viewTransition: false,
1127
+ layoutAutoClose: true,
1128
+ });
1093
1129
  }
1094
1130
  };
1095
1131
 
@@ -1301,6 +1337,7 @@ export function useChatPanelChromeModel({
1301
1337
  toggleOpen,
1302
1338
  newChat: startEmptyNewChat,
1303
1339
  openNewChatWithPrefill,
1340
+ openNewChatAndSubmit,
1304
1341
  chatPanelContainer,
1305
1342
  };
1306
1343
  }
@@ -44,6 +44,7 @@ export type {
44
44
  ChatEmptyStateConfig,
45
45
  ChatEmptyStateContext,
46
46
  ChatEmptyStateProps,
47
+ SubmitPresetOptions,
47
48
  } from './ChatEmptyState/ChatEmptyState.types';
48
49
  export type {
49
50
  Chat as ChatType,
@@ -93,7 +93,11 @@ function readInitialChatWidthPx(): number {
93
93
  type SetPanelWidthOptions = { persist?: boolean };
94
94
 
95
95
  /** Pass `viewTransition: false` to avoid nesting `document.startViewTransition` (e.g. chat open already animating). */
96
- export type SetSidebarOpenOptions = { viewTransition?: boolean };
96
+ export type SetSidebarOpenOptions = {
97
+ viewTransition?: boolean;
98
+ /** Nav closed for layout (chat/width); restore when chat closes — do not persist closed cookie. */
99
+ layoutAutoClose?: boolean;
100
+ };
97
101
 
98
102
  type SidebarContextProps = {
99
103
  isOpen: boolean;
@@ -178,6 +182,8 @@ function SidebarProvider({
178
182
  const [chatPanelOpen, _setChatPanelOpen] = useState(false);
179
183
  const sidebarWidthRef = useRef(sidebarWidthPx);
180
184
  const chatWidthRef = useRef(chatWidthPx);
185
+ /** Left nav was auto-closed because chat opened on a narrow shell; reopen when chat closes. */
186
+ const sidebarAutoClosedForChatRef = useRef(false);
181
187
  sidebarWidthRef.current = sidebarWidthPx;
182
188
  chatWidthRef.current = chatWidthPx;
183
189
 
@@ -332,10 +338,8 @@ function SidebarProvider({
332
338
  window.dispatchEvent(new CustomEvent(DISMISS_CHAT_FOR_LAYOUT_EVENT));
333
339
  }
334
340
  } else if (openingChat && isOpen) {
341
+ sidebarAutoClosedForChatRef.current = true;
335
342
  setIsOpen(false);
336
- if (getCookiePreferences(userId)?.functional) {
337
- setCookie('isSidebarOpen', 'false', 60 * 60 * 24 * 7);
338
- }
339
343
  }
340
344
  },
341
345
  [
@@ -345,7 +349,6 @@ function SidebarProvider({
345
349
  chatPanelOpen,
346
350
  sidebarWidthPx,
347
351
  chatWidthPx,
348
- userId,
349
352
  ],
350
353
  );
351
354
 
@@ -353,16 +356,32 @@ function SidebarProvider({
353
356
  (open: boolean) => {
354
357
  if (open) {
355
358
  closeOppositeSidebarIfNoSpace(false, true);
359
+ _setChatPanelOpen(true);
360
+ return;
361
+ }
362
+ _setChatPanelOpen(false);
363
+ if (sidebarAutoClosedForChatRef.current) {
364
+ sidebarAutoClosedForChatRef.current = false;
365
+ setIsOpen(true);
366
+ if (getCookiePreferences(userId)?.functional) {
367
+ setCookie('isSidebarOpen', 'true', 60 * 60 * 24 * 7);
368
+ }
356
369
  }
357
- _setChatPanelOpen(open);
358
370
  },
359
- [closeOppositeSidebarIfNoSpace],
371
+ [closeOppositeSidebarIfNoSpace, userId],
360
372
  );
361
373
 
362
374
  const setOpen = useCallback(
363
375
  (value: boolean, options?: SetSidebarOpenOptions) => {
376
+ const layoutAutoClose = options?.layoutAutoClose === true;
377
+
364
378
  if (value) {
379
+ sidebarAutoClosedForChatRef.current = false;
365
380
  closeOppositeSidebarIfNoSpace(true, false);
381
+ } else if (layoutAutoClose) {
382
+ sidebarAutoClosedForChatRef.current = true;
383
+ } else {
384
+ sidebarAutoClosedForChatRef.current = false;
366
385
  }
367
386
 
368
387
  const useViewTransition =
@@ -379,7 +398,7 @@ function SidebarProvider({
379
398
  setIsOpen(value);
380
399
  }
381
400
 
382
- if (getCookiePreferences(userId)?.functional) {
401
+ if (!layoutAutoClose && getCookiePreferences(userId)?.functional) {
383
402
  setCookie('isSidebarOpen', value.toString(), 60 * 60 * 24 * 7);
384
403
  }
385
404
  },
@@ -11,7 +11,7 @@ Host provides:
11
11
  - Controlled `selectedDriver` + `setSelectedDriver`
12
12
  - `isLoading` while fetching drivers
13
13
 
14
- Report tile: `drivers_map` — tile resolves analysis id, fetches drivers, passes list + selection (see EmbeddedAnalysisSelector pattern).
14
+ Report tile: `drivers_map` — tile resolves analysis id, fetches drivers, passes list + selection.
15
15
 
16
16
  Requires: `drivers`; `isLoading`; `selectedDriver`; `setSelectedDriver`.
17
17
 
@@ -12,9 +12,9 @@ Host provides:
12
12
  - `seriesInitKey` when selected analysis changes
13
13
  - `viewTab` / `onViewTabChange`: `lagged` (calendar-aligned, raw dates) or `overlapped` (driver series shifted backward by parsed lag months)
14
14
 
15
- View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar (analysis selector left, tabs right). Chart applies `applyDriversComparisonViewToPayload` internally.
15
+ View tabs: host should render uilib `Tabs variant="button"` with **Lagged** / **Overlapped** in the toolbar. Chart applies `applyDriversComparisonViewToPayload` internally.
16
16
 
17
- Report tile: `drivers_comparison_chart` — host loads normalized backtests payload + dataset historical; built-in analysis selector.
17
+ Report tile: `drivers_comparison_chart` — host loads normalized backtests payload + dataset historical.
18
18
 
19
19
  Requires: `payload` — target + driver normalized_series; `loading` / `chartLoading` — spinners; `seriesInitKey` — reset visible series on analysis or view tab change; `runAnalysisHint` / `statusHint` — empty/error text.
20
20
 
@@ -8,10 +8,9 @@ Not when: simple forecast card or driver backtests — use ChartAreaInteractive
8
8
  Host provides:
9
9
 
10
10
  - `performanceData` (PerformanceChartPayload) and `historicalData` from performance API
11
- - Analysis selection and fetch outside widget
12
11
  - Optional `forecastData`, `customPerformanceMatrix`, `userSeries` for spaghetti
13
12
 
14
- Report tile: `performance_chart` — host loads performance payload + dataset series; built-in analysis selector.
13
+ Report tile: `performance_chart` — host loads performance payload + dataset series.
15
14
 
16
15
  Requires: `performanceData` — model/drift forecasts and metrics; `historicalData` — baseline series; `loading` / `chartLoading` / `performanceDataLoading` — spinners; `runAnalysisHint` / `statusHint` — empty states.
17
16