@sybilion/uilib 1.3.52 → 1.3.54
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.
- package/dist/esm/components/ui/Card/Card.js +6 -6
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +1 -0
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +12 -0
- package/dist/esm/components/ui/Dialog/Dialog.js +2 -2
- package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +4 -4
- package/dist/esm/types/src/components/ui/Card/Card.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Card/Card.types.d.ts +1 -0
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSheet.d.ts +2 -0
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +2 -0
- package/dist/esm/types/src/components/ui/Dialog/Dialog.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Dialog/Dialog.types.d.ts +1 -0
- package/package.json +3 -2
- package/src/components/ui/Card/Card.tsx +13 -4
- package/src/components/ui/Card/Card.types.ts +1 -0
- package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +3 -0
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +20 -0
- package/src/components/ui/Dialog/Dialog.tsx +2 -0
- package/src/components/ui/Dialog/Dialog.types.ts +1 -0
- package/src/components/widgets/DriversComparisonChart/DriversComparisonChart.tsx +43 -2
|
@@ -24,12 +24,12 @@ function CardDescription({ className, ...props }) {
|
|
|
24
24
|
function CardAction({ className, ...props }) {
|
|
25
25
|
return (jsx("div", { "data-slot": "card-action", className: cn(S.action, className), ...props }));
|
|
26
26
|
}
|
|
27
|
-
function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, ...props }) {
|
|
28
|
-
const
|
|
27
|
+
function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, yScrollbarClassName, ...props }) {
|
|
28
|
+
const scrollInnerRef = useRef(null);
|
|
29
29
|
const contentDiv = (jsx("div", { "data-slot": "card-content", className: cn(className, centered && S.centered, fullHeight && S.fullHeight), ...props, children: children }));
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
if (!noScroll && autoScrollBottom) {
|
|
32
|
-
const inner =
|
|
32
|
+
const inner = scrollInnerRef.current;
|
|
33
33
|
if (!inner)
|
|
34
34
|
return;
|
|
35
35
|
if (inner.scrollHeight > SCROLL_OFFSET) {
|
|
@@ -46,9 +46,9 @@ function CardContent({ className, centered, fullHeight, noScroll, children, auto
|
|
|
46
46
|
}, [noScroll, autoScrollBottom]);
|
|
47
47
|
if (noScroll)
|
|
48
48
|
return contentDiv;
|
|
49
|
-
return (
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
return (jsx(Scroll, { y: true, autoHide: true, fadeSize: "m", className: S.scroll, yScrollbarClassName: yScrollbarClassName, onInnerRef: el => {
|
|
50
|
+
scrollInnerRef.current = el;
|
|
51
|
+
}, children: contentDiv }));
|
|
52
52
|
}
|
|
53
53
|
function CardFooter({ className, ...props }) {
|
|
54
54
|
return (jsx("div", { "data-slot": "card-footer", className: cn(S.footer, className), ...props }));
|
|
@@ -149,6 +149,16 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
149
149
|
}
|
|
150
150
|
};
|
|
151
151
|
const isEmpty = isChatEmpty(chat) && !isLoading;
|
|
152
|
+
const openNewChatWithPrefill = useCallback((prompt) => {
|
|
153
|
+
const sessionId = newChat();
|
|
154
|
+
if (sessionId == null) {
|
|
155
|
+
logger.warn('Chat prefill: sign in to use the assistant.');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const trimmed = prompt.trim();
|
|
159
|
+
setPromptLinkPrefill(trimmed || null);
|
|
160
|
+
onOpenChange(true);
|
|
161
|
+
}, [newChat, onOpenChange]);
|
|
152
162
|
/**
|
|
153
163
|
* App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
|
|
154
164
|
* from `location.search`). If the selected session already has messages, `newChat()` first.
|
|
@@ -401,6 +411,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
401
411
|
}
|
|
402
412
|
}
|
|
403
413
|
}
|
|
414
|
+
setPromptLinkPrefill(null);
|
|
404
415
|
try {
|
|
405
416
|
if (chatId)
|
|
406
417
|
endLocalDemoFlow(chatId);
|
|
@@ -769,6 +780,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
769
780
|
onOpenChange,
|
|
770
781
|
toggleOpen,
|
|
771
782
|
newChat,
|
|
783
|
+
openNewChatWithPrefill,
|
|
772
784
|
chatPanelContainer,
|
|
773
785
|
};
|
|
774
786
|
}
|
|
@@ -7,7 +7,7 @@ import { APP_MODAL_ID } from '../../../constants/appMount.js';
|
|
|
7
7
|
import { XIcon } from '@phosphor-icons/react';
|
|
8
8
|
import S from './Dialog.styl.js';
|
|
9
9
|
|
|
10
|
-
function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, footer, footerClassName, footerAlignment = 'center', size = 'default', className, noScroll, maxHeight, autoScrollBottom, width, }) {
|
|
10
|
+
function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, contentScrollbarClassName, footer, footerClassName, footerAlignment = 'center', size = 'default', className, noScroll, maxHeight, autoScrollBottom, width, }) {
|
|
11
11
|
const [isMounted, setIsMounted] = useState(false);
|
|
12
12
|
const [modalContainer, setModalContainer] = useState(null);
|
|
13
13
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
@@ -123,7 +123,7 @@ function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disabl
|
|
|
123
123
|
maxHeight: maxHeight
|
|
124
124
|
? `min(${maxHeight}, calc(100% - var(--p-4)))`
|
|
125
125
|
: null,
|
|
126
|
-
}, children: [(title || subtitle || icon) && (jsx(CardHeader, { icon: icon, title: title, description: subtitle })), !disableCloseButton && (jsx("button", { className: S.dialogClose, onClick: handleCloseClick, "aria-label": "Close dialog", type: "button", children: jsx(XIcon, { size: 16 }) })), content && (jsx(CardContent, { className: contentClassName, noScroll: noScroll, autoScrollBottom: autoScrollBottom, children: content })), footer && (jsx(CardFooter, { className: cn(S.footer, S[`align-${footerAlignment}`], footerClassName), children: footer }))] })] }));
|
|
126
|
+
}, children: [(title || subtitle || icon) && (jsx(CardHeader, { icon: icon, title: title, description: subtitle })), !disableCloseButton && (jsx("button", { className: S.dialogClose, onClick: handleCloseClick, "aria-label": "Close dialog", type: "button", children: jsx(XIcon, { size: 16 }) })), content && (jsx(CardContent, { className: contentClassName, yScrollbarClassName: contentScrollbarClassName, noScroll: noScroll, autoScrollBottom: autoScrollBottom, children: content })), footer && (jsx(CardFooter, { className: cn(S.footer, S[`align-${footerAlignment}`], footerClassName), children: footer }))] })] }));
|
|
127
127
|
const onTriggerClick = !disabled ? () => onOpenChange(true) : undefined;
|
|
128
128
|
return (jsxs(Fragment, { children: [trigger && jsx("div", { onClick: onTriggerClick, children: trigger }), open &&
|
|
129
129
|
!disabled &&
|
|
@@ -7,10 +7,10 @@ import { getForecastColor } from '../../ui/ChartAreaInteractive/ChartLines.js';
|
|
|
7
7
|
import '../../ui/Page/AppShell/AppShell.js';
|
|
8
8
|
import 'react-router-dom';
|
|
9
9
|
import '../../ui/Sidebar/Sidebar.js';
|
|
10
|
-
import 'lucide-react';
|
|
10
|
+
import { InfoIcon } from 'lucide-react';
|
|
11
11
|
import '../../ui/Page/Breadcrumbs/Breadcrumbs.styl.js';
|
|
12
12
|
import '../../ui/Page/pageContext.js';
|
|
13
|
-
import '../../ui/Tooltip/Tooltip.js';
|
|
13
|
+
import { Tooltip, TooltipTrigger, TooltipContent } from '../../ui/Tooltip/Tooltip.js';
|
|
14
14
|
import '../../ui/Page/PageHeader/PageHeader.styl.js';
|
|
15
15
|
import '../../ui/Page/PageEmptyCanvas/PageEmptyCanvas.styl.js';
|
|
16
16
|
import '../../ui/Page/PageContent/PageContent.styl.js';
|
|
@@ -24,7 +24,7 @@ import '../../ui/Tabs/Tabs.styl.js';
|
|
|
24
24
|
import '../../ui/Page/PageTabs/PageTabs.styl.js';
|
|
25
25
|
import '../../ui/Page/PageColumns/PageColumns.styl.js';
|
|
26
26
|
import '../../ui/Page/SectionHeader/SectionHeader.styl.js';
|
|
27
|
-
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '../../ui/Table/Table.js';
|
|
27
|
+
import { Table, TableHeader, TableRow, TableHead, TableCellValue, TableBody, TableCell } from '../../ui/Table/Table.js';
|
|
28
28
|
import { TextShimmer } from '../../ui/TextShimmer/TextShimmer.js';
|
|
29
29
|
import { TIME_RANGES } from '../../ui/TimeRangeControls/TimeRangeControls.constants.js';
|
|
30
30
|
import S from './DriversComparisonChart.styl.js';
|
|
@@ -127,7 +127,7 @@ function DriversComparisonChart({ payload, datasetHistorical = [], loading = fal
|
|
|
127
127
|
const showEmptyOverlay = (runAnalysisHint || Boolean(statusHint)) && !loading;
|
|
128
128
|
return (jsxs("div", { className: cn(S.root, className), children: [jsxs("div", { className: cn(S.chartShell, loading && S.chartShellLoading), children: [jsx("div", { className: S.chartSlot, children: jsxs("div", { className: S.chartWithOverlay, children: [jsx("div", { className: cn(S.chartInteractiveLayer, showEmptyOverlay && S.chartInteractiveDimmed), children: jsx(ChartAreaInteractive, { disableHistoricalAnimation: true, disableTimeRangeSelector: true, enableTimeRangeBrush: true, chartContainerClassName: S.chartContainer, chartData: driversComparisonChartData, forecastData: chartForecastData, timeRange: timeRange, onTimeRangeChange: handleTimeRangeChange, pinMonth: undefined, onPinMonthChange: () => { }, isDarkTheme: isDarkTheme, loading: chartLoading, hasCombinedData: mergedWithHistorical.length > 0, forecastLineStyle: "solid", showLegend: false, hiddenSeries: hiddenSeries, toggleLegendSeries: toggleSeries, ensureAnalysisSeriesVisible: showSeries }) }), showEmptyOverlay && (jsx("div", { className: S.chartEmptyOverlay, role: "status", "aria-live": "polite", children: jsx("div", { className: S.chartEmptyBlurb, children: jsx(ChartEmptyState, { variant: "inline", hint: runAnalysisHint
|
|
129
129
|
? 'Run a completed analysis to load the drivers comparison chart for an analysis.'
|
|
130
|
-
: undefined, status: statusHint ?? undefined, statusTone: statusTone }) }) }))] }) }), loading && (jsx("div", { className: S.loadingLayer, "aria-busy": "true", "aria-live": "polite", children: jsx("div", { className: S.loadingMessage, children: jsx(TextShimmer, { as: "span", className: S.loadingText, children: "Loading drivers comparison\u2026" }) }) }))] }), jsx("div", { className: S.seriesSection, children: tableSeriesRows.length === 0 ? (jsx("div", { className: S.seriesEmptyWrap, children: jsx("div", { className: S.chartEmptyBlurb, children: jsx(ChartEmptyState, { variant: "inline", align: "center", status: "No series" }) }) })) : (jsx("div", { className: S.seriesTableWrapper, children: jsx(PageXScroll, { size: "md", fullWidth: true, innerClassName: S.seriesTableContainer, scrollbarClassName: S.seriesScrollbar, children: jsxs(Table, { withBackground: true, withPaddings: true, className: S.seriesTable, children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: S.seriesColSeries, children: "Driver name" }), jsx(TableHead, { children: "Importance" }), jsx(TableHead, { children: "Lag" })] }) }), jsx(TableBody, { children: tableSeriesRows.map(row => {
|
|
130
|
+
: undefined, status: statusHint ?? undefined, statusTone: statusTone }) }) }))] }) }), loading && (jsx("div", { className: S.loadingLayer, "aria-busy": "true", "aria-live": "polite", children: jsx("div", { className: S.loadingMessage, children: jsx(TextShimmer, { as: "span", className: S.loadingText, children: "Loading drivers comparison\u2026" }) }) }))] }), jsx("div", { className: S.seriesSection, children: tableSeriesRows.length === 0 ? (jsx("div", { className: S.seriesEmptyWrap, children: jsx("div", { className: S.chartEmptyBlurb, children: jsx(ChartEmptyState, { variant: "inline", align: "center", status: "No series" }) }) })) : (jsx("div", { className: S.seriesTableWrapper, children: jsx(PageXScroll, { size: "md", fullWidth: true, innerClassName: S.seriesTableContainer, scrollbarClassName: S.seriesScrollbar, children: jsxs(Table, { withBackground: true, withPaddings: true, className: S.seriesTable, children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: S.seriesColSeries, children: "Driver name" }), jsx(TableHead, { children: jsxs(TableCellValue, { children: ["Importance", jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { children: jsx(InfoIcon, { size: 16, style: { cursor: 'help' } }) }) }), jsx(TooltipContent, { side: "top", maxWidth: 300, children: "How much this driver contributes to price movements in the forecast model. Higher = stronger influence. Reflects relative weight of each driver, scored from 0% to 100%." })] })] }) }), jsx(TableHead, { children: jsxs(TableCellValue, { children: ["Lag", jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { children: jsx(InfoIcon, { size: 16, style: { cursor: 'help' } }) }) }), jsx(TooltipContent, { side: "top", maxWidth: 300, children: "Lag shows how far ahead this driver predicts price movement. A range (e.g. 9\u201312 months) means the predictive signal is strongest somewhere in that window \u2014 not at a single fixed point." })] })] }) })] }) }), jsx(TableBody, { children: tableSeriesRows.map(row => {
|
|
131
131
|
const dataKey = toForecastDataKey(row.id);
|
|
132
132
|
const hidden = hiddenSeries.has(dataKey);
|
|
133
133
|
return (jsxs(TableRow, { className: cn(hidden && S.rowHidden), onClick: () => toggleSeries(row.id), children: [jsx(TableCell, { children: jsxs("span", { className: S.seriesLabel, children: [jsx("span", { className: S.colorSwatch, style: {
|
|
@@ -6,6 +6,6 @@ declare function CardHeader({ className, icon, iconWithBackground, textsClassNam
|
|
|
6
6
|
declare function CardTitle({ className, ...props }: CardTitleProps): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
declare function CardDescription({ className, ...props }: CardDescriptionProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
declare function CardAction({ className, ...props }: CardActionProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
declare function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, ...props }: CardContentProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare function CardContent({ className, centered, fullHeight, noScroll, children, autoScrollBottom, yScrollbarClassName, ...props }: CardContentProps): import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
declare function CardFooter({ className, ...props }: CardFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
11
11
|
export { Card, LinkCard, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
|
|
@@ -24,6 +24,7 @@ export interface CardContentProps extends React.ComponentProps<'div'> {
|
|
|
24
24
|
fullHeight?: boolean;
|
|
25
25
|
noScroll?: boolean;
|
|
26
26
|
autoScrollBottom?: boolean;
|
|
27
|
+
yScrollbarClassName?: string;
|
|
27
28
|
}
|
|
28
29
|
export interface CardFooterProps extends React.ComponentProps<'div'> {
|
|
29
30
|
}
|
|
@@ -3,6 +3,8 @@ export type ChatSheetActions = {
|
|
|
3
3
|
open: () => void;
|
|
4
4
|
/** Starts a new chat session and opens the panel (same as the new-chat keyboard shortcut). */
|
|
5
5
|
openNewChat: () => void;
|
|
6
|
+
/** Starts a new chat, opens the panel, and pre-fills the composer (user sends manually). */
|
|
7
|
+
openNewChatWithPrefill: (prompt: string) => void;
|
|
6
8
|
};
|
|
7
9
|
export interface ChatSheetProps extends Omit<UseChatPanelChromeModelInput, 'embedAsPage'> {
|
|
8
10
|
title?: string;
|
|
@@ -35,6 +35,8 @@ export type UseChatPanelChromeModelResult = {
|
|
|
35
35
|
onOpenChange: (open: boolean) => void;
|
|
36
36
|
toggleOpen: () => void;
|
|
37
37
|
newChat: () => void;
|
|
38
|
+
/** New session + open panel + one-shot composer pre-fill (does not send). */
|
|
39
|
+
openNewChatWithPrefill: (prompt: string) => void;
|
|
38
40
|
chatPanelContainer: HTMLElement | null;
|
|
39
41
|
};
|
|
40
42
|
export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, transformSendPayload, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { DialogProps } from './Dialog.types';
|
|
2
|
-
export declare function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, footer, footerClassName, footerAlignment, size, className, noScroll, maxHeight, autoScrollBottom, width, }: DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, contentScrollbarClassName, footer, footerClassName, footerAlignment, size, className, noScroll, maxHeight, autoScrollBottom, width, }: DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export { default as DialogStyles } from './Dialog.styl';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sybilion/uilib",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.54",
|
|
4
4
|
"description": "Sybilion Design System — React UI components (Webpack + Stylus)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -203,5 +203,6 @@
|
|
|
203
203
|
"webpack": "^5.75.0",
|
|
204
204
|
"webpack-cli": "^5.0.1",
|
|
205
205
|
"webpack-dev-server": "^5.2.3"
|
|
206
|
-
}
|
|
206
|
+
},
|
|
207
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
207
208
|
}
|
|
@@ -129,9 +129,10 @@ function CardContent({
|
|
|
129
129
|
noScroll,
|
|
130
130
|
children,
|
|
131
131
|
autoScrollBottom,
|
|
132
|
+
yScrollbarClassName,
|
|
132
133
|
...props
|
|
133
134
|
}: CardContentProps) {
|
|
134
|
-
const
|
|
135
|
+
const scrollInnerRef = useRef<HTMLDivElement | null>(null);
|
|
135
136
|
|
|
136
137
|
const contentDiv = (
|
|
137
138
|
<div
|
|
@@ -149,7 +150,7 @@ function CardContent({
|
|
|
149
150
|
|
|
150
151
|
useEffect(() => {
|
|
151
152
|
if (!noScroll && autoScrollBottom) {
|
|
152
|
-
const inner =
|
|
153
|
+
const inner = scrollInnerRef.current;
|
|
153
154
|
|
|
154
155
|
if (!inner) return;
|
|
155
156
|
|
|
@@ -170,8 +171,16 @@ function CardContent({
|
|
|
170
171
|
if (noScroll) return contentDiv;
|
|
171
172
|
|
|
172
173
|
return (
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
<Scroll
|
|
175
|
+
y
|
|
176
|
+
autoHide
|
|
177
|
+
fadeSize="m"
|
|
178
|
+
className={S.scroll}
|
|
179
|
+
yScrollbarClassName={yScrollbarClassName}
|
|
180
|
+
onInnerRef={el => {
|
|
181
|
+
scrollInnerRef.current = el;
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
175
184
|
{contentDiv}
|
|
176
185
|
</Scroll>
|
|
177
186
|
);
|
|
@@ -30,6 +30,7 @@ export interface CardContentProps extends React.ComponentProps<'div'> {
|
|
|
30
30
|
fullHeight?: boolean;
|
|
31
31
|
noScroll?: boolean;
|
|
32
32
|
autoScrollBottom?: boolean;
|
|
33
|
+
yScrollbarClassName?: string;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export interface CardFooterProps extends React.ComponentProps<'div'> {}
|
|
@@ -11,6 +11,8 @@ export type ChatSheetActions = {
|
|
|
11
11
|
open: () => void;
|
|
12
12
|
/** Starts a new chat session and opens the panel (same as the new-chat keyboard shortcut). */
|
|
13
13
|
openNewChat: () => void;
|
|
14
|
+
/** Starts a new chat, opens the panel, and pre-fills the composer (user sends manually). */
|
|
15
|
+
openNewChatWithPrefill: (prompt: string) => void;
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export interface ChatSheetProps extends Omit<
|
|
@@ -74,6 +76,7 @@ export function ChatSheet({
|
|
|
74
76
|
model.newChat();
|
|
75
77
|
model.onOpenChange(true);
|
|
76
78
|
},
|
|
79
|
+
openNewChatWithPrefill: model.openNewChatWithPrefill,
|
|
77
80
|
};
|
|
78
81
|
}
|
|
79
82
|
|
|
@@ -91,6 +91,8 @@ export type UseChatPanelChromeModelResult = {
|
|
|
91
91
|
onOpenChange: (open: boolean) => void;
|
|
92
92
|
toggleOpen: () => void;
|
|
93
93
|
newChat: () => void;
|
|
94
|
+
/** New session + open panel + one-shot composer pre-fill (does not send). */
|
|
95
|
+
openNewChatWithPrefill: (prompt: string) => void;
|
|
94
96
|
chatPanelContainer: HTMLElement | null;
|
|
95
97
|
};
|
|
96
98
|
|
|
@@ -312,6 +314,21 @@ export function useChatPanelChromeModel({
|
|
|
312
314
|
|
|
313
315
|
const isEmpty = isChatEmpty(chat) && !isLoading;
|
|
314
316
|
|
|
317
|
+
const openNewChatWithPrefill = useCallback(
|
|
318
|
+
(prompt: string) => {
|
|
319
|
+
const sessionId = newChat();
|
|
320
|
+
if (sessionId == null) {
|
|
321
|
+
logger.warn('Chat prefill: sign in to use the assistant.');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const trimmed = prompt.trim();
|
|
326
|
+
setPromptLinkPrefill(trimmed || null);
|
|
327
|
+
onOpenChange(true);
|
|
328
|
+
},
|
|
329
|
+
[newChat, onOpenChange],
|
|
330
|
+
);
|
|
331
|
+
|
|
315
332
|
/**
|
|
316
333
|
* App link: `?prompt=…` — open panel, pre-fill composer, strip param (read once on mount
|
|
317
334
|
* from `location.search`). If the selected session already has messages, `newChat()` first.
|
|
@@ -606,6 +623,8 @@ export function useChatPanelChromeModel({
|
|
|
606
623
|
}
|
|
607
624
|
}
|
|
608
625
|
|
|
626
|
+
setPromptLinkPrefill(null);
|
|
627
|
+
|
|
609
628
|
try {
|
|
610
629
|
if (chatId) endLocalDemoFlow(chatId);
|
|
611
630
|
let payload = buildChatSendMessagePayload(message, stagedAttachments);
|
|
@@ -1044,6 +1063,7 @@ export function useChatPanelChromeModel({
|
|
|
1044
1063
|
onOpenChange,
|
|
1045
1064
|
toggleOpen,
|
|
1046
1065
|
newChat,
|
|
1066
|
+
openNewChatWithPrefill,
|
|
1047
1067
|
chatPanelContainer,
|
|
1048
1068
|
};
|
|
1049
1069
|
}
|
|
@@ -31,6 +31,7 @@ export function Dialog({
|
|
|
31
31
|
icon,
|
|
32
32
|
content,
|
|
33
33
|
contentClassName,
|
|
34
|
+
contentScrollbarClassName,
|
|
34
35
|
footer,
|
|
35
36
|
footerClassName,
|
|
36
37
|
footerAlignment = 'center',
|
|
@@ -215,6 +216,7 @@ export function Dialog({
|
|
|
215
216
|
{content && (
|
|
216
217
|
<CardContent
|
|
217
218
|
className={contentClassName}
|
|
219
|
+
yScrollbarClassName={contentScrollbarClassName}
|
|
218
220
|
noScroll={noScroll}
|
|
219
221
|
autoScrollBottom={autoScrollBottom}
|
|
220
222
|
>
|
|
@@ -13,13 +13,20 @@ import {
|
|
|
13
13
|
Table,
|
|
14
14
|
TableBody,
|
|
15
15
|
TableCell,
|
|
16
|
+
TableCellValue,
|
|
16
17
|
TableHead,
|
|
17
18
|
TableHeader,
|
|
18
19
|
TableRow,
|
|
19
20
|
} from '#uilib/components/ui/Table';
|
|
20
21
|
import { TextShimmer } from '#uilib/components/ui/TextShimmer/TextShimmer';
|
|
21
22
|
import { TIME_RANGES } from '#uilib/components/ui/TimeRangeControls/TimeRangeControls.constants';
|
|
23
|
+
import {
|
|
24
|
+
Tooltip,
|
|
25
|
+
TooltipContent,
|
|
26
|
+
TooltipTrigger,
|
|
27
|
+
} from '#uilib/components/ui/Tooltip';
|
|
22
28
|
import type { BacktestsComponentPayload } from '@sybilion/platform-sdk';
|
|
29
|
+
import { InfoIcon } from 'lucide-react';
|
|
23
30
|
|
|
24
31
|
import S from './DriversComparisonChart.styl';
|
|
25
32
|
import {
|
|
@@ -282,8 +289,42 @@ export function DriversComparisonChart({
|
|
|
282
289
|
<TableHead className={S.seriesColSeries}>
|
|
283
290
|
Driver name
|
|
284
291
|
</TableHead>
|
|
285
|
-
<TableHead>
|
|
286
|
-
|
|
292
|
+
<TableHead>
|
|
293
|
+
<TableCellValue>
|
|
294
|
+
Importance
|
|
295
|
+
<Tooltip>
|
|
296
|
+
<TooltipTrigger asChild>
|
|
297
|
+
<span>
|
|
298
|
+
<InfoIcon size={16} style={{ cursor: 'help' }} />
|
|
299
|
+
</span>
|
|
300
|
+
</TooltipTrigger>
|
|
301
|
+
<TooltipContent side="top" maxWidth={300}>
|
|
302
|
+
How much this driver contributes to price movements
|
|
303
|
+
in the forecast model. Higher = stronger influence.
|
|
304
|
+
Reflects relative weight of each driver, scored from
|
|
305
|
+
0% to 100%.
|
|
306
|
+
</TooltipContent>
|
|
307
|
+
</Tooltip>
|
|
308
|
+
</TableCellValue>
|
|
309
|
+
</TableHead>
|
|
310
|
+
<TableHead>
|
|
311
|
+
<TableCellValue>
|
|
312
|
+
Lag
|
|
313
|
+
<Tooltip>
|
|
314
|
+
<TooltipTrigger asChild>
|
|
315
|
+
<span>
|
|
316
|
+
<InfoIcon size={16} style={{ cursor: 'help' }} />
|
|
317
|
+
</span>
|
|
318
|
+
</TooltipTrigger>
|
|
319
|
+
<TooltipContent side="top" maxWidth={300}>
|
|
320
|
+
Lag shows how far ahead this driver predicts price
|
|
321
|
+
movement. A range (e.g. 9–12 months) means the
|
|
322
|
+
predictive signal is strongest somewhere in that
|
|
323
|
+
window — not at a single fixed point.
|
|
324
|
+
</TooltipContent>
|
|
325
|
+
</Tooltip>
|
|
326
|
+
</TableCellValue>
|
|
327
|
+
</TableHead>
|
|
287
328
|
</TableRow>
|
|
288
329
|
</TableHeader>
|
|
289
330
|
<TableBody>
|