ai-design-system 0.1.116 → 0.1.117
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/components/composites/SessionHeader/SessionHeader.tsx +30 -6
- package/components/features/ChatPanel/ChatPanel.tsx +15 -0
- package/components/index.ts +3 -0
- package/dist/index.cjs +52 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +101 -64
- package/dist/index.js +49 -5
- package/dist/index.js.map +1 -1
- package/lib/date.ts +70 -0
- package/package.json +1 -1
|
@@ -17,10 +17,24 @@ export interface ChatSessionInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
import type { FileDownloadResult } from '@/components/composites/FileTreeExplorer';
|
|
20
|
+
import { formatDayLabel } from '@/lib/date';
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
export interface SessionHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
23
24
|
title?: string;
|
|
25
|
+
/**
|
|
26
|
+
* What a session is called.
|
|
27
|
+
* - "title": the session's own title (default).
|
|
28
|
+
* - "date": the day it was created, as "21 Aug 2025", in the reader's time
|
|
29
|
+
* zone. For a surface that keeps one session per day, where the day is
|
|
30
|
+
* what tells sessions apart and a first message is not.
|
|
31
|
+
* @default "title"
|
|
32
|
+
*/
|
|
33
|
+
titleFormat?: 'title' | 'date';
|
|
34
|
+
/**
|
|
35
|
+
* IANA time zone for "date" titles. Defaults to the reader's own.
|
|
36
|
+
*/
|
|
37
|
+
timeZone?: string;
|
|
24
38
|
sessions?: ChatSessionInfo[];
|
|
25
39
|
activeSessionId?: string | null;
|
|
26
40
|
onNewSession?: () => void;
|
|
@@ -48,7 +62,7 @@ export interface SessionHeaderProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
48
62
|
* and a dropdown history of past sessions.
|
|
49
63
|
*/
|
|
50
64
|
export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps>(
|
|
51
|
-
({ title, sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, onDownloadSession, showActions = true, showNewSession = true, showDownloadSession = true, onOverview, showOverview = true, className, ...props }, ref) => {
|
|
65
|
+
({ title, titleFormat = 'title', timeZone, sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, onDownloadSession, showActions = true, showNewSession = true, showDownloadSession = true, onOverview, showOverview = true, className, ...props }, ref) => {
|
|
52
66
|
const activeSession = sessions?.find(s => s.id === activeSessionId);
|
|
53
67
|
const downloadRef = React.useRef<HTMLAnchorElement>(null);
|
|
54
68
|
|
|
@@ -66,9 +80,16 @@ export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps
|
|
|
66
80
|
URL.revokeObjectURL(url);
|
|
67
81
|
}, [onDownloadSession]);
|
|
68
82
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
const byDate = titleFormat === 'date';
|
|
84
|
+
const sessionLabel = (session: ChatSessionInfo | undefined): string => {
|
|
85
|
+
if (!byDate) return session?.title || 'Untitled Session';
|
|
86
|
+
// A session not yet in the list is the one being started now: today's.
|
|
87
|
+
return formatDayLabel(session?.created_at ?? new Date(), { timeZone }) || 'Untitled Session';
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const displayTitle = title || (activeSessionId
|
|
91
|
+
? sessionLabel(activeSession)
|
|
92
|
+
: byDate ? formatDayLabel(new Date(), { timeZone }) : 'New Session');
|
|
72
93
|
|
|
73
94
|
return (
|
|
74
95
|
<div
|
|
@@ -121,8 +142,11 @@ export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps
|
|
|
121
142
|
onClick={() => onSelectSession?.(session.id)}
|
|
122
143
|
className="flex flex-col items-start py-2 cursor-pointer"
|
|
123
144
|
>
|
|
124
|
-
<span className="text-sm font-medium truncate w-full">{session
|
|
125
|
-
|
|
145
|
+
<span className="text-sm font-medium truncate w-full">{sessionLabel(session)}</span>
|
|
146
|
+
{/* The date is the title in "date" mode; repeating it underneath says nothing. */}
|
|
147
|
+
{!byDate && (
|
|
148
|
+
<span className="text-xs text-muted-foreground">{new Date(session.created_at).toLocaleString()}</span>
|
|
149
|
+
)}
|
|
126
150
|
</DropdownMenuItem>
|
|
127
151
|
))
|
|
128
152
|
) : (
|
|
@@ -220,6 +220,17 @@ export interface ChatPanelProps {
|
|
|
220
220
|
* Title override for the session header
|
|
221
221
|
*/
|
|
222
222
|
sessionTitle?: string;
|
|
223
|
+
/**
|
|
224
|
+
* What sessions are called in the header and the history list: their own
|
|
225
|
+
* title ("title", default) or the day they were created ("date", shown as
|
|
226
|
+
* "21 Aug 2025" in the reader's time zone).
|
|
227
|
+
* @default "title"
|
|
228
|
+
*/
|
|
229
|
+
sessionTitleFormat?: 'title' | 'date';
|
|
230
|
+
/**
|
|
231
|
+
* IANA time zone for "date" session titles. Defaults to the reader's own.
|
|
232
|
+
*/
|
|
233
|
+
sessionTimeZone?: string;
|
|
223
234
|
/**
|
|
224
235
|
* Whether to show action buttons in the session header
|
|
225
236
|
* @default true
|
|
@@ -304,6 +315,8 @@ export const ChatPanel = React.memo<ChatPanelProps>(
|
|
|
304
315
|
enableSpeech = true,
|
|
305
316
|
showHeader = true,
|
|
306
317
|
sessionTitle,
|
|
318
|
+
sessionTitleFormat = 'title',
|
|
319
|
+
sessionTimeZone,
|
|
307
320
|
showSessionActions = true,
|
|
308
321
|
showNewSession = true,
|
|
309
322
|
showDownloadSession = true,
|
|
@@ -468,6 +481,8 @@ export const ChatPanel = React.memo<ChatPanelProps>(
|
|
|
468
481
|
{showHeader && (
|
|
469
482
|
<SessionHeader
|
|
470
483
|
title={sessionTitle}
|
|
484
|
+
titleFormat={sessionTitleFormat}
|
|
485
|
+
timeZone={sessionTimeZone}
|
|
471
486
|
showActions={showSessionActions}
|
|
472
487
|
showNewSession={showNewSession}
|
|
473
488
|
showDownloadSession={showDownloadSession}
|
package/components/index.ts
CHANGED
|
@@ -94,6 +94,9 @@ export type { ExternalToast, ToastT, ToasterProps } from 'sonner';
|
|
|
94
94
|
|
|
95
95
|
// Utilities
|
|
96
96
|
export { cn } from '@/lib/utils';
|
|
97
|
+
// Calendar days in the reader's time zone
|
|
98
|
+
export { formatDayLabel, dayKey, isSameDay, userTimeZone } from '@/lib/date';
|
|
99
|
+
export type { DayOptions } from '@/lib/date';
|
|
97
100
|
export { ButtonSwitcher } from './composites';
|
|
98
101
|
export type { ButtonSwitcherProps, ButtonSwitcherItem } from './composites';
|
|
99
102
|
|
package/dist/index.cjs
CHANGED
|
@@ -8136,9 +8136,43 @@ var ApprovalCard = React3__namespace.memo(
|
|
|
8136
8136
|
}
|
|
8137
8137
|
);
|
|
8138
8138
|
ApprovalCard.displayName = "ApprovalCard";
|
|
8139
|
+
|
|
8140
|
+
// lib/date.ts
|
|
8141
|
+
function userTimeZone() {
|
|
8142
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
8143
|
+
}
|
|
8144
|
+
function dayParts(value, options) {
|
|
8145
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
8146
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
8147
|
+
const zone = (options == null ? void 0 : options.timeZone) ? { timeZone: options.timeZone } : {};
|
|
8148
|
+
const named = new Intl.DateTimeFormat("en-US", __spreadValues({ day: "numeric", month: "short", year: "numeric" }, zone)).formatToParts(date);
|
|
8149
|
+
const numeric = new Intl.DateTimeFormat("en-US", __spreadValues({ month: "2-digit" }, zone)).formatToParts(date);
|
|
8150
|
+
const part = (parts, type) => {
|
|
8151
|
+
var _a, _b;
|
|
8152
|
+
return (_b = (_a = parts.find((p) => p.type === type)) == null ? void 0 : _a.value) != null ? _b : "";
|
|
8153
|
+
};
|
|
8154
|
+
return {
|
|
8155
|
+
day: part(named, "day"),
|
|
8156
|
+
month: part(named, "month"),
|
|
8157
|
+
monthNumber: part(numeric, "month"),
|
|
8158
|
+
year: part(named, "year")
|
|
8159
|
+
};
|
|
8160
|
+
}
|
|
8161
|
+
function formatDayLabel(value, options) {
|
|
8162
|
+
const parts = dayParts(value, options);
|
|
8163
|
+
return parts ? `${parts.day} ${parts.month} ${parts.year}` : "";
|
|
8164
|
+
}
|
|
8165
|
+
function dayKey(value, options) {
|
|
8166
|
+
const parts = dayParts(value, options);
|
|
8167
|
+
return parts ? `${parts.year}-${parts.monthNumber}-${parts.day.padStart(2, "0")}` : "";
|
|
8168
|
+
}
|
|
8169
|
+
function isSameDay(a, b, options) {
|
|
8170
|
+
const key = dayKey(a, options);
|
|
8171
|
+
return key !== "" && key === dayKey(b, options);
|
|
8172
|
+
}
|
|
8139
8173
|
var SessionHeader = React3__namespace.forwardRef(
|
|
8140
8174
|
(_a, ref) => {
|
|
8141
|
-
var _b = _a, { title, sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, onDownloadSession, showActions = true, showNewSession = true, showDownloadSession = true, onOverview, showOverview = true, className } = _b, props = __objRest(_b, ["title", "sessions", "activeSessionId", "onNewSession", "onCloseSession", "onSelectSession", "onDownloadSession", "showActions", "showNewSession", "showDownloadSession", "onOverview", "showOverview", "className"]);
|
|
8175
|
+
var _b = _a, { title, titleFormat = "title", timeZone, sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, onDownloadSession, showActions = true, showNewSession = true, showDownloadSession = true, onOverview, showOverview = true, className } = _b, props = __objRest(_b, ["title", "titleFormat", "timeZone", "sessions", "activeSessionId", "onNewSession", "onCloseSession", "onSelectSession", "onDownloadSession", "showActions", "showNewSession", "showDownloadSession", "onOverview", "showOverview", "className"]);
|
|
8142
8176
|
const activeSession = sessions == null ? void 0 : sessions.find((s) => s.id === activeSessionId);
|
|
8143
8177
|
const downloadRef = React3__namespace.useRef(null);
|
|
8144
8178
|
const handleDownloadClick = React3__namespace.useCallback(async () => {
|
|
@@ -8154,7 +8188,13 @@ var SessionHeader = React3__namespace.forwardRef(
|
|
|
8154
8188
|
}
|
|
8155
8189
|
URL.revokeObjectURL(url);
|
|
8156
8190
|
}, [onDownloadSession]);
|
|
8157
|
-
const
|
|
8191
|
+
const byDate = titleFormat === "date";
|
|
8192
|
+
const sessionLabel = (session) => {
|
|
8193
|
+
var _a2;
|
|
8194
|
+
if (!byDate) return (session == null ? void 0 : session.title) || "Untitled Session";
|
|
8195
|
+
return formatDayLabel((_a2 = session == null ? void 0 : session.created_at) != null ? _a2 : /* @__PURE__ */ new Date(), { timeZone }) || "Untitled Session";
|
|
8196
|
+
};
|
|
8197
|
+
const displayTitle = title || (activeSessionId ? sessionLabel(activeSession) : byDate ? formatDayLabel(/* @__PURE__ */ new Date(), { timeZone }) : "New Session");
|
|
8158
8198
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8159
8199
|
"div",
|
|
8160
8200
|
__spreadProps(__spreadValues({
|
|
@@ -8188,8 +8228,8 @@ var SessionHeader = React3__namespace.forwardRef(
|
|
|
8188
8228
|
onClick: () => onSelectSession == null ? void 0 : onSelectSession(session.id),
|
|
8189
8229
|
className: "flex flex-col items-start py-2 cursor-pointer",
|
|
8190
8230
|
children: [
|
|
8191
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium truncate w-full", children: session
|
|
8192
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: new Date(session.created_at).toLocaleString() })
|
|
8231
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium truncate w-full", children: sessionLabel(session) }),
|
|
8232
|
+
!byDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: new Date(session.created_at).toLocaleString() })
|
|
8193
8233
|
]
|
|
8194
8234
|
},
|
|
8195
8235
|
session.id
|
|
@@ -8407,6 +8447,8 @@ var ChatPanel = React3__namespace.memo(
|
|
|
8407
8447
|
enableSpeech = true,
|
|
8408
8448
|
showHeader = true,
|
|
8409
8449
|
sessionTitle,
|
|
8450
|
+
sessionTitleFormat = "title",
|
|
8451
|
+
sessionTimeZone,
|
|
8410
8452
|
showSessionActions = true,
|
|
8411
8453
|
showNewSession = true,
|
|
8412
8454
|
showDownloadSession = true,
|
|
@@ -8536,6 +8578,8 @@ var ChatPanel = React3__namespace.memo(
|
|
|
8536
8578
|
SessionHeader,
|
|
8537
8579
|
{
|
|
8538
8580
|
title: sessionTitle,
|
|
8581
|
+
titleFormat: sessionTitleFormat,
|
|
8582
|
+
timeZone: sessionTimeZone,
|
|
8539
8583
|
showActions: showSessionActions,
|
|
8540
8584
|
showNewSession,
|
|
8541
8585
|
showDownloadSession,
|
|
@@ -18607,10 +18651,14 @@ exports.WorkflowCanvas = WorkflowCanvas;
|
|
|
18607
18651
|
exports.WorkflowObservabilityFeature = WorkflowObservabilityFeature;
|
|
18608
18652
|
exports.bmcToCanvas = bmcToCanvas;
|
|
18609
18653
|
exports.cn = cn;
|
|
18654
|
+
exports.dayKey = dayKey;
|
|
18655
|
+
exports.formatDayLabel = formatDayLabel;
|
|
18610
18656
|
exports.getLayoutedElements = getLayoutedElements;
|
|
18611
18657
|
exports.getPreset = getPreset;
|
|
18658
|
+
exports.isSameDay = isSameDay;
|
|
18612
18659
|
exports.useOptionalPromptInputController = useOptionalPromptInputController;
|
|
18613
18660
|
exports.usePromptInputAttachments = usePromptInputAttachments;
|
|
18614
18661
|
exports.usePromptInputController = usePromptInputController;
|
|
18662
|
+
exports.userTimeZone = userTimeZone;
|
|
18615
18663
|
//# sourceMappingURL=index.cjs.map
|
|
18616
18664
|
//# sourceMappingURL=index.cjs.map
|