ai-design-system 0.1.117 → 0.1.119

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.
@@ -55,7 +55,7 @@ export const AccountButton = React.forwardRef<HTMLButtonElement, AccountButtonPr
55
55
  <div
56
56
  className={cn(
57
57
  "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold select-none text-white",
58
- avatarColor ? "" : "bg-emerald-700"
58
+ avatarColor ? "" : "bg-gradient-to-br from-teal-800 to-emerald-600"
59
59
  )}
60
60
  style={avatarColor ? { backgroundColor: avatarColor } : undefined}
61
61
  >
@@ -3,6 +3,7 @@
3
3
  import * as React from "react";
4
4
  import { useState, useRef, useEffect } from "react";
5
5
  import { Icon } from "@/components/primitives/Icon";
6
+ import { Button } from "@/components/primitives/Button";
6
7
  import { Input } from "@/components/primitives/Input";
7
8
  import { Textarea } from "@/components/primitives/Textarea";
8
9
  import { Label } from "@/components/primitives/Label";
@@ -48,6 +49,12 @@ export interface ApprovalCardProps {
48
49
  state?: ToolUIState;
49
50
  approval?: ToolApproval;
50
51
  className?: string;
52
+ /**
53
+ * Opens the file a question is about (`args.file_path`). With it, the
54
+ * question shows the file's name as a link; without it, or when the question
55
+ * names no file, nothing is shown.
56
+ */
57
+ onOpenFile?: (path: string) => void;
51
58
  }
52
59
 
53
60
  function formatValue(value: unknown): string {
@@ -92,6 +99,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
92
99
  state = "approval-requested",
93
100
  approval = {},
94
101
  className,
102
+ onOpenFile,
95
103
  }) => {
96
104
  const [isEditing, setIsEditing] = useState(false);
97
105
  const [prevArgs, setPrevArgs] = useState(actionRequest.args);
@@ -99,6 +107,12 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
99
107
  const [rejectReason, setRejectReason] = useState("");
100
108
  const [showRejectInput, setShowRejectInput] = useState(false);
101
109
 
110
+ // The file the question is about, when the asker named one.
111
+ const questionFile =
112
+ typeof actionRequest.args?.file_path === "string" && actionRequest.args.file_path.trim()
113
+ ? actionRequest.args.file_path
114
+ : null;
115
+
102
116
  const hideSkipButton = (actionRequest.args?.hide_skip_button as boolean) ?? false;
103
117
  const submitButtonText = (actionRequest.args?.button_submit_text as string) || 'Continue';
104
118
  const skipButtonText = (actionRequest.args?.button_skip_text as string) || 'Skip';
@@ -267,29 +281,45 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
267
281
  <StreamingMarkdown controls={false}>
268
282
  {currentQuestion.question}
269
283
  </StreamingMarkdown>
284
+ {questionFile && onOpenFile && (
285
+ <Button
286
+ type="button"
287
+ variant="link"
288
+ onClick={() => onOpenFile(questionFile)}
289
+ className="mt-2 h-auto p-0 inline-flex max-w-full items-center gap-1.5 text-sm font-medium text-primary hover:underline"
290
+ data-testid="approval-card-file-link"
291
+ >
292
+ <Icon name="file-text" className="h-4 w-4 flex-shrink-0" />
293
+ <span className="truncate">{questionFile.split("/").pop()}</span>
294
+ </Button>
295
+ )}
270
296
  </div>
271
297
  </div>
272
298
  {questions.length > 1 && (
273
299
  <div className="flex items-center gap-1.5 text-xs text-muted-foreground flex-shrink-0 font-normal">
274
- <button
300
+ <Button
275
301
  type="button"
302
+ variant="ghost"
303
+ size="icon-sm"
276
304
  onClick={() => setCurrentQuestionIndex((prev) => Math.max(0, prev - 1))}
277
305
  disabled={currentQuestionIndex === 0}
278
- className="hover:text-foreground disabled:opacity-30 p-0.5 transition-colors"
306
+ className="h-6 w-6 p-0 hover:text-foreground disabled:opacity-30 transition-colors"
279
307
  >
280
308
  <Icon name="chevron-left" size="sm" />
281
- </button>
309
+ </Button>
282
310
  <span className="font-medium">{currentQuestionIndex + 1} of {questions.length}</span>
283
- <button
311
+ <Button
284
312
  type="button"
313
+ variant="ghost"
314
+ size="icon-sm"
285
315
  onClick={() =>
286
316
  setCurrentQuestionIndex((prev) => Math.min(questions.length - 1, prev + 1))
287
317
  }
288
318
  disabled={currentQuestionIndex === questions.length - 1}
289
- className="hover:text-foreground disabled:opacity-30 p-0.5 transition-colors"
319
+ className="h-6 w-6 p-0 hover:text-foreground disabled:opacity-30 transition-colors"
290
320
  >
291
321
  <Icon name="chevron-right" size="sm" />
292
- </button>
322
+ </Button>
293
323
  </div>
294
324
  )}
295
325
  </div>
@@ -301,8 +331,8 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
301
331
  const isOther = index === allOptions.length - 1;
302
332
  const isSelected = isMultiSelect
303
333
  ? (selectedAnswers[currentQuestionIndex] as string[])?.includes(
304
- isOther ? otherTexts[currentQuestionIndex] || "" : option
305
- )
334
+ isOther ? otherTexts[currentQuestionIndex] || "" : option
335
+ )
306
336
  : selectedOptionIndices[currentQuestionIndex] === index;
307
337
 
308
338
  return (
@@ -500,7 +530,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
500
530
  }}
501
531
  disabled={isProcessing}
502
532
  >
503
- <Icon name="check" size="sm" className="mr-2" />
533
+ <Icon name="check" size="sm" className="mr-2" />
504
534
  Save & Approve
505
535
  </ConfirmationAction>
506
536
  </ConfirmationActions>
@@ -576,7 +606,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
576
606
  }}
577
607
  disabled={isProcessing}
578
608
  >
579
- <Icon name="x" size="sm" className="mr-2" />
609
+ <Icon name="x" size="sm" className="mr-2" />
580
610
  Confirm Rejection
581
611
  </ConfirmationAction>
582
612
  </>
@@ -589,7 +619,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
589
619
  onClick={() => setShowRejectInput(true)}
590
620
  disabled={isProcessing}
591
621
  >
592
- <Icon name="x" size="sm" className="mr-2" />
622
+ <Icon name="x" size="sm" className="mr-2" />
593
623
  {skipButtonText}
594
624
  </ConfirmationAction>
595
625
  )}
@@ -610,7 +640,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
610
640
  onClick={onApprove}
611
641
  disabled={isProcessing}
612
642
  >
613
- <Icon name="check" size="sm" className="mr-2" />
643
+ <Icon name="check" size="sm" className="mr-2" />
614
644
  {submitButtonText}
615
645
  </ConfirmationAction>
616
646
  </>
@@ -145,7 +145,7 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
145
145
  className={cn(
146
146
  "[&>[data-slot=input-group]]:rounded-2xl",
147
147
  className?.includes("border") &&
148
- "[&>[data-slot=input-group]]:border-0 [&>[data-slot=input-group]]:bg-transparent [&>[data-slot=input-group]]:shadow-none",
148
+ "[&>[data-slot=input-group]]:border-0 [&>[data-slot=input-group]]:bg-transparent [&>[data-slot=input-group]]:shadow-none",
149
149
  className
150
150
  )}
151
151
  {...props}
@@ -155,15 +155,15 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
155
155
  placeholder={placeholder}
156
156
  disabled={disabled}
157
157
  onChange={isControlled ? handleControlledChange : undefined}
158
- className="min-h-9 py-2 px-3 text-sm resize-none border-0 shadow-none focus-visible:ring-0 bg-transparent flex-1 field-sizing-content"
158
+ className="min-h-12 py-3 pl-3 pr-2 text-sm resize-none border-0 shadow-none focus-visible:ring-0 bg-transparent flex-1 field-sizing-content"
159
159
  />
160
- <InputGroupAddon align="inline-end" className="self-end pb-1 pr-1.5 shrink-0">
160
+ <InputGroupAddon align="inline-end" className="self-end pb-3 pr-3 pt-3 shrink-0 has-[>button]:mr-0 !mr-0">
161
161
  <PromptInputSubmit
162
162
  disabled={disabled || (loading && !onStop)}
163
163
  status={loading ? (onStop ? "streaming" : "submitted") : undefined}
164
164
  onClick={isStopping ? (e: React.MouseEvent) => { e.preventDefault(); onStop?.(); } : undefined}
165
165
  className={cn(
166
- "rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors",
166
+ "rounded-lg text-white transition-colors",
167
167
  isStopping && "bg-transparent hover:bg-accent"
168
168
  )}
169
169
  >
@@ -181,7 +181,7 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
181
181
  onError={onError}
182
182
  className={cn(
183
183
  className?.includes("border") &&
184
- "[&>[data-slot=input-group]]:border-0 [&>[data-slot=input-group]]:bg-transparent [&>[data-slot=input-group]]:shadow-none",
184
+ "[&>[data-slot=input-group]]:border-0 [&>[data-slot=input-group]]:bg-transparent [&>[data-slot=input-group]]:shadow-none",
185
185
  className
186
186
  )}
187
187
  {...props}
@@ -218,7 +218,7 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
218
218
  status={loading ? (onStop ? "streaming" : "submitted") : undefined}
219
219
  onClick={isStopping ? (e: React.MouseEvent) => { e.preventDefault(); onStop?.(); } : undefined}
220
220
  className={cn(
221
- "rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors",
221
+ "rounded-lg text-white transition-colors",
222
222
  isStopping && "bg-transparent hover:bg-accent"
223
223
  )}
224
224
  >
@@ -21,31 +21,12 @@ export default meta;
21
21
  type Story = StoryObj<typeof meta>;
22
22
 
23
23
  const mockStories: StoryItem[] = [
24
- {
25
- id: "story-1",
26
- title: "Devin iOS",
27
- status: "ready",
28
- },
29
- {
30
- id: "story-2",
31
- title: "Waypoint",
32
- status: "ready",
33
- },
34
- {
35
- id: "story-3",
36
- title: "Design Sys",
37
- status: "generating",
38
- },
39
- {
40
- id: "story-4",
41
- title: "Workspace",
42
- status: "ready",
43
- },
44
- {
45
- id: "story-5",
46
- title: "ZeroOps",
47
- status: "draft",
48
- },
24
+ { id: "story-1", title: "Devin iOS", status: "ready", unread: true },
25
+ { id: "story-2", title: "Waypoint", status: "ready" },
26
+ { id: "story-3", title: "Design Sys", status: "working" },
27
+ { id: "story-4", title: "Workspace", status: "working", unread: true },
28
+ { id: "story-5", title: "Render", status: "failed", unread: true },
29
+ { id: "story-6", title: "ZeroOps", status: "stopped" },
49
30
  ];
50
31
 
51
32
  /**
@@ -13,7 +13,20 @@ export interface StoryItem {
13
13
  subtitle?: string;
14
14
  icon?: string;
15
15
  time?: string;
16
- status?: "ready" | "generating" | "draft";
16
+ /**
17
+ * The video's state, drawn as the story's colour:
18
+ * - "failed": red — the video has stopped on an error
19
+ * - "ready": green — the finished video is available
20
+ * - "stopped": grey — not ready, and nothing is running to finish it
21
+ * - "working": orange — in progress
22
+ * "generating" and "draft" are the earlier names of "working" and "stopped".
23
+ */
24
+ status?: "failed" | "ready" | "stopped" | "working" | "generating" | "draft";
25
+ /**
26
+ * Whether the story holds a notification the user has not seen. Shown as the
27
+ * badge, on any status, and only then.
28
+ */
29
+ unread?: boolean;
17
30
  thumbnailUrl?: string;
18
31
  avatarColor?: string;
19
32
  avatarInitials?: string;
@@ -28,22 +41,17 @@ export interface StoriesBarProps extends React.HTMLAttributes<HTMLDivElement> {
28
41
 
29
42
  function getStoryStatusStyle(status?: StoryItem["status"]) {
30
43
  switch (status) {
31
- case "generating":
32
- return {
33
- iconColor: "text-amber-500",
34
- badgeBg: "bg-amber-500",
35
- };
44
+ case "failed":
45
+ return { iconColor: "text-red-500", badgeBg: "bg-red-500" };
36
46
  case "ready":
37
- return {
38
- iconColor: "text-emerald-500",
39
- badgeBg: "bg-emerald-500",
40
- };
47
+ return { iconColor: "text-emerald-500", badgeBg: "bg-emerald-500" };
48
+ case "working":
49
+ case "generating":
50
+ return { iconColor: "text-amber-500", badgeBg: "bg-amber-500" };
51
+ case "stopped":
41
52
  case "draft":
42
53
  default:
43
- return {
44
- iconColor: "text-muted-foreground",
45
- badgeBg: "bg-muted-foreground",
46
- };
54
+ return { iconColor: "text-muted-foreground", badgeBg: "bg-muted-foreground" };
47
55
  }
48
56
  }
49
57
 
@@ -61,7 +69,6 @@ export const StoriesBar = React.forwardRef<HTMLDivElement, StoriesBarProps>(
61
69
  <div className="flex items-center gap-2.5 px-3 py-1.5 overflow-x-auto no-scrollbar scroll-smooth">
62
70
  {stories.map((story) => {
63
71
  const isActive = story.id === activeStoryId;
64
- const isGenerating = story.status === "generating";
65
72
  const statusStyle = getStoryStatusStyle(story.status);
66
73
  const iconName = story.icon || defaultIcon;
67
74
 
@@ -100,17 +107,15 @@ export const StoriesBar = React.forwardRef<HTMLDivElement, StoriesBarProps>(
100
107
  )}
101
108
  </div>
102
109
 
103
- {isGenerating ? (
104
- <span className="absolute bottom-0 right-0 flex h-2 w-2">
105
- <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-500 opacity-75" />
106
- <span className="relative inline-flex rounded-full h-2 w-2 bg-amber-500" />
107
- </span>
108
- ) : (
110
+ {/* The badge: an unseen notification, on any status. */}
111
+ {story.unread && (
109
112
  <span
110
113
  className={cn(
111
114
  "absolute bottom-0 right-0 inline-flex rounded-full h-2 w-2 ring-1.5 ring-background",
112
115
  statusStyle.badgeBg
113
116
  )}
117
+ data-testid={`story-badge-${story.id}`}
118
+ aria-label="New notification"
114
119
  />
115
120
  )}
116
121
  </div>
@@ -592,14 +592,14 @@ export const StoryPlayer = React.forwardRef<HTMLDivElement, StoryPlayerProps>(
592
592
  data-testid="btn-prev-story"
593
593
  aria-label="Previous story"
594
594
  onClick={handlePrev}
595
- className="flex-1 h-full cursor-pointer bg-transparent hover:bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
595
+ className="flex-1 h-full cursor-pointer bg-transparent hover:!bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
596
596
  />
597
597
  <Button
598
598
  variant="ghost"
599
599
  data-testid="btn-next-story"
600
600
  aria-label="Next story"
601
601
  onClick={handleNext}
602
- className="flex-1 h-full cursor-pointer bg-transparent hover:bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
602
+ className="flex-1 h-full cursor-pointer bg-transparent hover:!bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
603
603
  />
604
604
  </div>
605
605
  </div>
@@ -95,6 +95,8 @@ export interface ChatPanelProps {
95
95
  * Handler to edit HITL approval request arguments
96
96
  */
97
97
  onApprovalEdit?: (editedArgs: Record<string, unknown>) => void;
98
+ /** Opens the file a pending question is about (its `file_path`). The card shows the link only when this is given. */
99
+ onOpenFile?: (path: string) => void;
98
100
  /**
99
101
  * Processing state for HITL approval request
100
102
  */
@@ -284,6 +286,7 @@ export const ChatPanel = React.memo<ChatPanelProps>(
284
286
  onApprovalApprove,
285
287
  onApprovalReject,
286
288
  onApprovalEdit,
289
+ onOpenFile,
287
290
  isApprovalProcessing = false,
288
291
  loading = false,
289
292
  onStop,
@@ -446,6 +449,7 @@ export const ChatPanel = React.memo<ChatPanelProps>(
446
449
  isProcessing={isApprovalProcessing}
447
450
  state={approvalCardState}
448
451
  approval={approvalCardApproval}
452
+ onOpenFile={onOpenFile}
449
453
  />
450
454
  </div>
451
455
  );
package/dist/index.cjs CHANGED
@@ -7194,10 +7194,10 @@ var PromptInput2 = React3__namespace.memo(
7194
7194
  placeholder,
7195
7195
  disabled,
7196
7196
  onChange: isControlled ? handleControlledChange : void 0,
7197
- className: "min-h-9 py-2 px-3 text-sm resize-none border-0 shadow-none focus-visible:ring-0 bg-transparent flex-1 field-sizing-content"
7197
+ className: "min-h-12 py-3 pl-3 pr-2 text-sm resize-none border-0 shadow-none focus-visible:ring-0 bg-transparent flex-1 field-sizing-content"
7198
7198
  }
7199
7199
  ),
7200
- /* @__PURE__ */ jsxRuntime.jsx(InputGroupAddon2, { align: "inline-end", className: "self-end pb-1 pr-1.5 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
7200
+ /* @__PURE__ */ jsxRuntime.jsx(InputGroupAddon2, { align: "inline-end", className: "self-end pb-3 pr-3 pt-3 shrink-0 has-[>button]:mr-0 !mr-0", children: /* @__PURE__ */ jsxRuntime.jsx(
7201
7201
  PromptInputSubmit,
7202
7202
  {
7203
7203
  disabled: disabled || loading && !onStop,
@@ -7207,7 +7207,7 @@ var PromptInput2 = React3__namespace.memo(
7207
7207
  onStop == null ? void 0 : onStop();
7208
7208
  } : void 0,
7209
7209
  className: cn(
7210
- "rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors",
7210
+ "rounded-lg text-white transition-colors",
7211
7211
  isStopping && "bg-transparent hover:bg-accent"
7212
7212
  ),
7213
7213
  children: loading ? void 0 : /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "corner-down-left", size: "sm" })
@@ -7269,7 +7269,7 @@ var PromptInput2 = React3__namespace.memo(
7269
7269
  onStop == null ? void 0 : onStop();
7270
7270
  } : void 0,
7271
7271
  className: cn(
7272
- "rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors",
7272
+ "rounded-lg text-white transition-colors",
7273
7273
  isStopping && "bg-transparent hover:bg-accent"
7274
7274
  ),
7275
7275
  children: loading ? void 0 : /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "corner-down-left", size: "sm" })
@@ -7665,17 +7665,19 @@ var ApprovalCard = React3__namespace.memo(
7665
7665
  isProcessing = false,
7666
7666
  state = "approval-requested",
7667
7667
  approval = {},
7668
- className
7668
+ className,
7669
+ onOpenFile
7669
7670
  }) => {
7670
- var _a, _b, _c, _d, _e, _f;
7671
+ var _a, _b, _c, _d, _e, _f, _g;
7671
7672
  const [isEditing, setIsEditing] = React3.useState(false);
7672
7673
  const [prevArgs, setPrevArgs] = React3.useState(actionRequest.args);
7673
7674
  const [editedArgs, setEditedArgs] = React3.useState(() => actionRequest.args);
7674
7675
  const [rejectReason, setRejectReason] = React3.useState("");
7675
7676
  const [showRejectInput, setShowRejectInput] = React3.useState(false);
7676
- const hideSkipButton = (_b = (_a = actionRequest.args) == null ? void 0 : _a.hide_skip_button) != null ? _b : false;
7677
- const submitButtonText = ((_c = actionRequest.args) == null ? void 0 : _c.button_submit_text) || "Continue";
7678
- const skipButtonText = ((_d = actionRequest.args) == null ? void 0 : _d.button_skip_text) || "Skip";
7677
+ const questionFile = typeof ((_a = actionRequest.args) == null ? void 0 : _a.file_path) === "string" && actionRequest.args.file_path.trim() ? actionRequest.args.file_path : null;
7678
+ const hideSkipButton = (_c = (_b = actionRequest.args) == null ? void 0 : _b.hide_skip_button) != null ? _c : false;
7679
+ const submitButtonText = ((_d = actionRequest.args) == null ? void 0 : _d.button_submit_text) || "Continue";
7680
+ const skipButtonText = ((_e = actionRequest.args) == null ? void 0 : _e.button_skip_text) || "Skip";
7679
7681
  const [currentQuestionIndex, setCurrentQuestionIndex] = React3.useState(0);
7680
7682
  const [selectedAnswers, setSelectedAnswers] = React3.useState({});
7681
7683
  const [otherTexts, setOtherTexts] = React3.useState({});
@@ -7685,7 +7687,7 @@ var ApprovalCard = React3__namespace.memo(
7685
7687
  setPrevArgs(actionRequest.args);
7686
7688
  setEditedArgs(actionRequest.args);
7687
7689
  }
7688
- const allowed = (_e = reviewConfig == null ? void 0 : reviewConfig.allowedDecisions) != null ? _e : ["approve", "reject"];
7690
+ const allowed = (_f = reviewConfig == null ? void 0 : reviewConfig.allowedDecisions) != null ? _f : ["approve", "reject"];
7689
7691
  const canEdit = allowed.includes("edit");
7690
7692
  const canReject = allowed.includes("reject");
7691
7693
  const isQuestionAction = actionRequest.name === "ask_question" || actionRequest.args && (typeof actionRequest.args.question === "string" || Array.isArray(actionRequest.args.questions) && actionRequest.args.questions.length > 0);
@@ -7744,7 +7746,7 @@ var ApprovalCard = React3__namespace.memo(
7744
7746
  return false;
7745
7747
  };
7746
7748
  const allQuestionsAnswered = questions.length > 0 && questions.every((_, idx) => isQuestionAnswered(idx));
7747
- const isApprovalType = ((_f = actionRequest.args) == null ? void 0 : _f.type) === "approval";
7749
+ const isApprovalType = ((_g = actionRequest.args) == null ? void 0 : _g.type) === "approval";
7748
7750
  const isLastQuestion = currentQuestionIndex === questions.length - 1;
7749
7751
  const handleContinue = () => {
7750
7752
  if (!isApprovalType && !allQuestionsAnswered) {
@@ -7799,16 +7801,34 @@ var ApprovalCard = React3__namespace.memo(
7799
7801
  /* @__PURE__ */ jsxRuntime.jsx(ConfirmationTitle, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
7800
7802
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 min-w-0 flex-1", children: [
7801
7803
  /* @__PURE__ */ jsxRuntime.jsx(QuestionIcon, { className: "h-5 w-5 text-muted-foreground mt-0.5 flex-shrink-0" }),
7802
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0 max-h-60 overflow-y-auto pr-1 text-sm leading-relaxed text-foreground [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", children: /* @__PURE__ */ jsxRuntime.jsx(StreamingMarkdown, { controls: false, children: currentQuestion.question }) })
7804
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 max-h-60 overflow-y-auto pr-1 text-sm leading-relaxed text-foreground [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", children: [
7805
+ /* @__PURE__ */ jsxRuntime.jsx(StreamingMarkdown, { controls: false, children: currentQuestion.question }),
7806
+ questionFile && onOpenFile && /* @__PURE__ */ jsxRuntime.jsxs(
7807
+ Button2,
7808
+ {
7809
+ type: "button",
7810
+ variant: "link",
7811
+ onClick: () => onOpenFile(questionFile),
7812
+ className: "mt-2 h-auto p-0 inline-flex max-w-full items-center gap-1.5 text-sm font-medium text-primary hover:underline",
7813
+ "data-testid": "approval-card-file-link",
7814
+ children: [
7815
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "file-text", className: "h-4 w-4 flex-shrink-0" }),
7816
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: questionFile.split("/").pop() })
7817
+ ]
7818
+ }
7819
+ )
7820
+ ] })
7803
7821
  ] }),
7804
7822
  questions.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground flex-shrink-0 font-normal", children: [
7805
7823
  /* @__PURE__ */ jsxRuntime.jsx(
7806
- "button",
7824
+ Button2,
7807
7825
  {
7808
7826
  type: "button",
7827
+ variant: "ghost",
7828
+ size: "icon-sm",
7809
7829
  onClick: () => setCurrentQuestionIndex((prev) => Math.max(0, prev - 1)),
7810
7830
  disabled: currentQuestionIndex === 0,
7811
- className: "hover:text-foreground disabled:opacity-30 p-0.5 transition-colors",
7831
+ className: "h-6 w-6 p-0 hover:text-foreground disabled:opacity-30 transition-colors",
7812
7832
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-left", size: "sm" })
7813
7833
  }
7814
7834
  ),
@@ -7818,12 +7838,14 @@ var ApprovalCard = React3__namespace.memo(
7818
7838
  questions.length
7819
7839
  ] }),
7820
7840
  /* @__PURE__ */ jsxRuntime.jsx(
7821
- "button",
7841
+ Button2,
7822
7842
  {
7823
7843
  type: "button",
7844
+ variant: "ghost",
7845
+ size: "icon-sm",
7824
7846
  onClick: () => setCurrentQuestionIndex((prev) => Math.min(questions.length - 1, prev + 1)),
7825
7847
  disabled: currentQuestionIndex === questions.length - 1,
7826
- className: "hover:text-foreground disabled:opacity-30 p-0.5 transition-colors",
7848
+ className: "h-6 w-6 p-0 hover:text-foreground disabled:opacity-30 transition-colors",
7827
7849
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-right", size: "sm" })
7828
7850
  }
7829
7851
  )
@@ -8416,6 +8438,7 @@ var ChatPanel = React3__namespace.memo(
8416
8438
  onApprovalApprove,
8417
8439
  onApprovalReject,
8418
8440
  onApprovalEdit,
8441
+ onOpenFile,
8419
8442
  isApprovalProcessing = false,
8420
8443
  loading = false,
8421
8444
  onStop,
@@ -8548,7 +8571,8 @@ var ChatPanel = React3__namespace.memo(
8548
8571
  onEdit: handleApprovalEdit,
8549
8572
  isProcessing: isApprovalProcessing,
8550
8573
  state: approvalCardState,
8551
- approval: approvalCardApproval
8574
+ approval: approvalCardApproval,
8575
+ onOpenFile
8552
8576
  }
8553
8577
  ) });
8554
8578
  } else if (fileChanges.length > 0) {
@@ -14959,22 +14983,17 @@ var WorkspaceEmptyState = React3__namespace.memo(
14959
14983
  WorkspaceEmptyState.displayName = "WorkspaceEmptyState";
14960
14984
  function getStoryStatusStyle(status) {
14961
14985
  switch (status) {
14962
- case "generating":
14963
- return {
14964
- iconColor: "text-amber-500",
14965
- badgeBg: "bg-amber-500"
14966
- };
14986
+ case "failed":
14987
+ return { iconColor: "text-red-500", badgeBg: "bg-red-500" };
14967
14988
  case "ready":
14968
- return {
14969
- iconColor: "text-emerald-500",
14970
- badgeBg: "bg-emerald-500"
14971
- };
14989
+ return { iconColor: "text-emerald-500", badgeBg: "bg-emerald-500" };
14990
+ case "working":
14991
+ case "generating":
14992
+ return { iconColor: "text-amber-500", badgeBg: "bg-amber-500" };
14993
+ case "stopped":
14972
14994
  case "draft":
14973
14995
  default:
14974
- return {
14975
- iconColor: "text-muted-foreground",
14976
- badgeBg: "bg-muted-foreground"
14977
- };
14996
+ return { iconColor: "text-muted-foreground", badgeBg: "bg-muted-foreground" };
14978
14997
  }
14979
14998
  }
14980
14999
  var StoriesBar = React3__namespace.forwardRef(
@@ -14990,7 +15009,6 @@ var StoriesBar = React3__namespace.forwardRef(
14990
15009
  }, props), {
14991
15010
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2.5 px-3 py-1.5 overflow-x-auto no-scrollbar scroll-smooth", children: stories.map((story) => {
14992
15011
  const isActive = story.id === activeStoryId;
14993
- const isGenerating = story.status === "generating";
14994
15012
  const statusStyle = getStoryStatusStyle(story.status);
14995
15013
  const iconName = story.icon || defaultIcon;
14996
15014
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -15029,16 +15047,15 @@ var StoriesBar = React3__namespace.forwardRef(
15029
15047
  )
15030
15048
  }
15031
15049
  ),
15032
- isGenerating ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-0 right-0 flex h-2 w-2", children: [
15033
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-amber-500 opacity-75" }),
15034
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-amber-500" })
15035
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
15050
+ story.unread && /* @__PURE__ */ jsxRuntime.jsx(
15036
15051
  "span",
15037
15052
  {
15038
15053
  className: cn(
15039
15054
  "absolute bottom-0 right-0 inline-flex rounded-full h-2 w-2 ring-1.5 ring-background",
15040
15055
  statusStyle.badgeBg
15041
- )
15056
+ ),
15057
+ "data-testid": `story-badge-${story.id}`,
15058
+ "aria-label": "New notification"
15042
15059
  }
15043
15060
  )
15044
15061
  ] }),
@@ -15557,7 +15574,7 @@ var StoryPlayer = React3__namespace.forwardRef(
15557
15574
  "data-testid": "btn-prev-story",
15558
15575
  "aria-label": "Previous story",
15559
15576
  onClick: handlePrev,
15560
- className: "flex-1 h-full cursor-pointer bg-transparent hover:bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
15577
+ className: "flex-1 h-full cursor-pointer bg-transparent hover:!bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
15561
15578
  }
15562
15579
  ),
15563
15580
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -15567,7 +15584,7 @@ var StoryPlayer = React3__namespace.forwardRef(
15567
15584
  "data-testid": "btn-next-story",
15568
15585
  "aria-label": "Next story",
15569
15586
  onClick: handleNext,
15570
- className: "flex-1 h-full cursor-pointer bg-transparent hover:bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
15587
+ className: "flex-1 h-full cursor-pointer bg-transparent hover:!bg-transparent p-0 rounded-none border-none focus-visible:ring-0 focus-visible:outline-none"
15571
15588
  }
15572
15589
  )
15573
15590
  ] })
@@ -15629,7 +15646,7 @@ var AccountButton = React3__namespace.forwardRef(
15629
15646
  {
15630
15647
  className: cn(
15631
15648
  "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold select-none text-white",
15632
- avatarColor ? "" : "bg-emerald-700"
15649
+ avatarColor ? "" : "bg-gradient-to-br from-teal-800 to-emerald-600"
15633
15650
  ),
15634
15651
  style: avatarColor ? { backgroundColor: avatarColor } : void 0,
15635
15652
  children: initial