ai-design-system 0.1.63 → 0.1.64

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.
@@ -16,12 +16,16 @@ export interface ChatSessionInfo {
16
16
  created_at: string;
17
17
  }
18
18
 
19
+ import type { FileDownloadResult } from '@/components/composites/FileTreeExplorer';
20
+
21
+
19
22
  export interface SessionHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
20
23
  sessions?: ChatSessionInfo[];
21
24
  activeSessionId?: string | null;
22
25
  onNewSession?: () => void;
23
26
  onCloseSession?: (id: string) => void;
24
27
  onSelectSession?: (id: string) => void;
28
+ onDownloadSession?: () => Promise<FileDownloadResult | undefined>;
25
29
  }
26
30
 
27
31
  /**
@@ -31,8 +35,23 @@ export interface SessionHeaderProps extends React.HTMLAttributes<HTMLDivElement>
31
35
  * and a dropdown history of past sessions.
32
36
  */
33
37
  export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps>(
34
- ({ sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, className, ...props }, ref) => {
38
+ ({ sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, onDownloadSession, className, ...props }, ref) => {
35
39
  const activeSession = sessions?.find(s => s.id === activeSessionId);
40
+ const downloadRef = React.useRef<HTMLAnchorElement>(null);
41
+
42
+ const handleDownloadClick = React.useCallback(async () => {
43
+ if (!onDownloadSession) return;
44
+ const result = await onDownloadSession();
45
+ if (!result) return;
46
+ const url = URL.createObjectURL(result.blob);
47
+ const anchor = downloadRef.current;
48
+ if (anchor) {
49
+ anchor.href = url;
50
+ anchor.download = result.filename;
51
+ anchor.click();
52
+ }
53
+ URL.revokeObjectURL(url);
54
+ }, [onDownloadSession]);
36
55
 
37
56
  return (
38
57
  <div
@@ -48,7 +67,12 @@ export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps
48
67
  </span>
49
68
  </div>
50
69
  <div className="flex items-center space-x-1 flex-none">
51
- <Button variant="ghost" size="icon" onClick={onNewSession} className="h-8 w-8">
70
+ {onDownloadSession && (
71
+ <Button variant="ghost" size="icon" onClick={handleDownloadClick} className="h-8 w-8" title="Download Chat History">
72
+ <Icon name="download" className="h-4 w-4" />
73
+ </Button>
74
+ )}
75
+ <Button variant="ghost" size="icon" onClick={onNewSession} className="h-8 w-8" title="New Session">
52
76
  <Icon name="plus" className="h-4 w-4" />
53
77
  </Button>
54
78
 
@@ -77,6 +101,7 @@ export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps
77
101
  )}
78
102
  </DropdownMenuContent>
79
103
  </DropdownMenu>
104
+ <a ref={downloadRef} style={{ display: 'none' }} />
80
105
  </div>
81
106
  </div>
82
107
  );
@@ -13,6 +13,7 @@ import type { ActionRequest, ReviewConfig, ToolUIState, ToolApproval } from "@/c
13
13
  import type { FormEvent } from "react";
14
14
  import { SessionHeader } from "@/components/composites/SessionHeader";
15
15
  import type { ChatSessionInfo } from "@/components/composites/SessionHeader";
16
+ import type { FileDownloadResult } from "@/components/composites/FileTreeExplorer";
16
17
 
17
18
  /**
18
19
  * RefinementPanel Feature
@@ -127,6 +128,10 @@ export interface RefinementPanelProps {
127
128
  * Handler to switch sessions
128
129
  */
129
130
  onSelectSession?: (id: string) => void;
131
+ /**
132
+ * Handler to download the session chat history
133
+ */
134
+ onDownloadSession?: () => Promise<FileDownloadResult | undefined>;
130
135
  }
131
136
 
132
137
  /**
@@ -154,6 +159,7 @@ export const RefinementPanel = React.memo<RefinementPanelProps>(
154
159
  onNewSession,
155
160
  onCloseSession,
156
161
  onSelectSession,
162
+ onDownloadSession,
157
163
  }) => {
158
164
  // File change queue state
159
165
  const [fileChangeState, setFileChangeState] = React.useState<
@@ -298,6 +304,7 @@ export const RefinementPanel = React.memo<RefinementPanelProps>(
298
304
  onNewSession={onNewSession}
299
305
  onCloseSession={onCloseSession}
300
306
  onSelectSession={onSelectSession}
307
+ onDownloadSession={onDownloadSession}
301
308
  />
302
309
 
303
310
  <AIConversation
@@ -12,6 +12,7 @@ import type { RefinementMessage } from "./RefinementPanel";
12
12
  import type { FileChangeData } from "@/components/composites/FileQueue";
13
13
  import type { ActionRequest, ReviewConfig } from "@/components/composites/ApprovalCard";
14
14
  import type { ToolCall } from "@/components/composites/ToolCallDisplay";
15
+ import type { FileDownloadResult } from "@/components/composites/FileTreeExplorer";
15
16
 
16
17
  /**
17
18
  * Return type for the refinement panel hook
@@ -55,6 +56,9 @@ export interface UseRefinementPanelReturn {
55
56
 
56
57
  /** Action handler for tool interactions */
57
58
  handleToolAction?: (toolCall: ToolCall, action: string) => void;
59
+
60
+ /** Handler to download the session chat history */
61
+ handleDownloadSession?: () => Promise<FileDownloadResult | undefined>;
58
62
  }
59
63
 
60
64
  /**
package/dist/index.cjs CHANGED
@@ -6005,8 +6005,22 @@ var ApprovalCard = React3__namespace.memo(
6005
6005
  ApprovalCard.displayName = "ApprovalCard";
6006
6006
  var SessionHeader = React3__namespace.forwardRef(
6007
6007
  (_a, ref) => {
6008
- var _b = _a, { sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, className } = _b, props = __objRest(_b, ["sessions", "activeSessionId", "onNewSession", "onCloseSession", "onSelectSession", "className"]);
6008
+ var _b = _a, { sessions, activeSessionId, onNewSession, onCloseSession, onSelectSession, onDownloadSession, className } = _b, props = __objRest(_b, ["sessions", "activeSessionId", "onNewSession", "onCloseSession", "onSelectSession", "onDownloadSession", "className"]);
6009
6009
  const activeSession = sessions == null ? void 0 : sessions.find((s) => s.id === activeSessionId);
6010
+ const downloadRef = React3__namespace.useRef(null);
6011
+ const handleDownloadClick = React3__namespace.useCallback(async () => {
6012
+ if (!onDownloadSession) return;
6013
+ const result = await onDownloadSession();
6014
+ if (!result) return;
6015
+ const url = URL.createObjectURL(result.blob);
6016
+ const anchor = downloadRef.current;
6017
+ if (anchor) {
6018
+ anchor.href = url;
6019
+ anchor.download = result.filename;
6020
+ anchor.click();
6021
+ }
6022
+ URL.revokeObjectURL(url);
6023
+ }, [onDownloadSession]);
6010
6024
  return /* @__PURE__ */ jsxRuntime.jsxs(
6011
6025
  "div",
6012
6026
  __spreadProps(__spreadValues({
@@ -6016,7 +6030,8 @@ var SessionHeader = React3__namespace.forwardRef(
6016
6030
  children: [
6017
6031
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center space-x-2 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium truncate", children: activeSessionId ? (activeSession == null ? void 0 : activeSession.title) || "Untitled Session" : "New Session" }) }),
6018
6032
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-1 flex-none", children: [
6019
- /* @__PURE__ */ jsxRuntime.jsx(Button2, { variant: "ghost", size: "icon", onClick: onNewSession, className: "h-8 w-8", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "h-4 w-4" }) }),
6033
+ onDownloadSession && /* @__PURE__ */ jsxRuntime.jsx(Button2, { variant: "ghost", size: "icon", onClick: handleDownloadClick, className: "h-8 w-8", title: "Download Chat History", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "download", className: "h-4 w-4" }) }),
6034
+ /* @__PURE__ */ jsxRuntime.jsx(Button2, { variant: "ghost", size: "icon", onClick: onNewSession, className: "h-8 w-8", title: "New Session", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", className: "h-4 w-4" }) }),
6020
6035
  /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu2, { children: [
6021
6036
  /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button2, { variant: "ghost", size: "icon", className: "h-8 w-8", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "clock", className: "h-4 w-4" }) }) }),
6022
6037
  /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "end", className: "w-64 max-h-80 overflow-y-auto", children: [
@@ -6035,7 +6050,8 @@ var SessionHeader = React3__namespace.forwardRef(
6035
6050
  session.id
6036
6051
  )) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-sm text-center text-muted-foreground", children: "No previous sessions" })
6037
6052
  ] })
6038
- ] })
6053
+ ] }),
6054
+ /* @__PURE__ */ jsxRuntime.jsx("a", { ref: downloadRef, style: { display: "none" } })
6039
6055
  ] })
6040
6056
  ]
6041
6057
  })
@@ -6064,7 +6080,8 @@ var RefinementPanel = React3__namespace.memo(
6064
6080
  activeSessionId,
6065
6081
  onNewSession,
6066
6082
  onCloseSession,
6067
- onSelectSession
6083
+ onSelectSession,
6084
+ onDownloadSession
6068
6085
  }) => {
6069
6086
  const [fileChangeState, setFileChangeState] = React3__namespace.useState("approval-requested");
6070
6087
  const [approval, setApproval] = React3__namespace.useState({});
@@ -6177,7 +6194,8 @@ var RefinementPanel = React3__namespace.memo(
6177
6194
  activeSessionId,
6178
6195
  onNewSession,
6179
6196
  onCloseSession,
6180
- onSelectSession
6197
+ onSelectSession,
6198
+ onDownloadSession
6181
6199
  }
6182
6200
  ),
6183
6201
  /* @__PURE__ */ jsxRuntime.jsx(