ai-design-system 0.1.46 → 0.1.48

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.
Files changed (64) hide show
  1. package/components/ai-elements/code-block.tsx +2 -4
  2. package/components/ai-elements/conversation.tsx +1 -1
  3. package/components/ai-elements/prompt-input.tsx +4 -6
  4. package/components/blocks/AIConversation/AIConversation.tsx +9 -4
  5. package/components/blocks/DataTable/DataTable.tsx +2 -2
  6. package/components/blocks/InboxPanel/InboxPanel.tsx +12 -1
  7. package/components/blocks/SectionLayout/SectionLayout.tsx +2 -2
  8. package/components/blocks/index.ts +0 -11
  9. package/components/composites/AppHeader/AppHeader.tsx +5 -1
  10. package/components/composites/AppHeader/interfaces.ts +1 -1
  11. package/components/composites/ApprovalCard/ApprovalCard.tsx +81 -53
  12. package/components/composites/CommentBox/CommentBox.tsx +3 -3
  13. package/components/{blocks → composites}/DashboardHeader/DashboardHeader.tsx +2 -0
  14. package/components/composites/DataTable/DragHandleCell.tsx +2 -2
  15. package/components/composites/DataTable/EnhancedDataTable.behaviors.stories.tsx +1 -1
  16. package/components/composites/DataTable/EnhancedDataTable.tsx +20 -9
  17. package/components/composites/DataTable/InlineEditCell.tsx +0 -2
  18. package/components/composites/DataTable/RowDetailDrawer.tsx +2 -3
  19. package/components/composites/DataTable/StatusCell.tsx +4 -4
  20. package/components/composites/DataTable/index.ts +0 -2
  21. package/components/composites/DocumentTabBar/DocumentTabBar.tsx +5 -4
  22. package/components/composites/EmptyState/EmptyState.stories.tsx +26 -0
  23. package/components/composites/EmptyState/EmptyState.tsx +38 -0
  24. package/components/composites/EmptyState/index.ts +1 -0
  25. package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +2 -1
  26. package/components/composites/ProjectSwitcher/ProjectSwitcher.tsx +105 -0
  27. package/components/composites/ProjectSwitcher/index.ts +1 -0
  28. package/components/{blocks → composites}/PromptInput/PromptInput.stories.tsx +1 -1
  29. package/components/{blocks → composites}/PromptInput/PromptInput.tsx +3 -1
  30. package/components/composites/SpecialistMessage/SpecialistMessage.tsx +14 -6
  31. package/components/composites/StateNode/StateNode.tsx +5 -5
  32. package/components/composites/TransitionNode/TransitionNode.tsx +4 -4
  33. package/components/composites/TriggerNode/TriggerNode.tsx +2 -2
  34. package/components/composites/WorkflowToolbar/WorkflowToolbar.tsx +3 -4
  35. package/components/composites/index.ts +24 -0
  36. package/components/features/DashboardFeature/DashboardFeature.behaviors.stories.tsx +4 -0
  37. package/components/features/DashboardFeature/DashboardFeature.mocks.ts +1 -1
  38. package/components/features/DashboardFeature/DashboardFeature.stories.tsx +21 -0
  39. package/components/features/DashboardFeature/DashboardFeature.tsx +95 -24
  40. package/components/features/DashboardFeature/useDashboardFeature.d.ts +21 -0
  41. package/components/features/DashboardFeature/useDashboardFeature.mock.ts +39 -30
  42. package/components/features/PageLayout/PageLayout.stories.tsx +33 -10
  43. package/components/features/PageLayout/PageLayout.tsx +1 -1
  44. package/components/features/PageLayout/useDashboardIntegration.mock.ts +108 -0
  45. package/components/features/RefinementPanel/RefinementPanel.tsx +31 -26
  46. package/components/features/RefinementPanel/useRefinementPanel.d.ts +13 -8
  47. package/components/features/RefinementPanel/useRefinementPanel.mock.ts +6 -0
  48. package/components/features/WorkflowBuilder/WorkflowBuilder.tsx +3 -6
  49. package/components/index.ts +2 -1
  50. package/components/primitives/Icon/Icon.tsx +48 -1
  51. package/components/ui/dropdown-menu.tsx +2 -2
  52. package/dist/index.cjs +918 -358
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.css +28 -0
  55. package/dist/index.d.ts +128 -8
  56. package/dist/index.js +919 -360
  57. package/dist/index.js.map +1 -1
  58. package/package.json +1 -1
  59. /package/components/{blocks → composites}/DashboardChart/DashboardChart.tsx +0 -0
  60. /package/components/{blocks → composites}/DashboardChart/index.ts +0 -0
  61. /package/components/{blocks → composites}/DashboardHeader/index.ts +0 -0
  62. /package/components/{blocks → composites}/LayoutProvider/LayoutProvider.tsx +0 -0
  63. /package/components/{blocks → composites}/LayoutProvider/index.ts +0 -0
  64. /package/components/{blocks → composites}/PromptInput/index.ts +0 -0
@@ -3,7 +3,7 @@
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { cn } from "@/lib/utils";
5
5
  import type { Element } from "hast";
6
- import { CheckIcon, CopyIcon } from "lucide-react";
6
+ import { CheckIcon } from "lucide-react";
7
7
  import {
8
8
  type ComponentProps,
9
9
  createContext,
@@ -163,8 +163,6 @@ export const CodeBlockCopyButton = ({
163
163
  }
164
164
  };
165
165
 
166
- const Icon = isCopied ? CheckIcon : CopyIcon;
167
-
168
166
  return (
169
167
  <Button
170
168
  className={cn("shrink-0", className)}
@@ -173,7 +171,7 @@ export const CodeBlockCopyButton = ({
173
171
  variant="ghost"
174
172
  {...props}
175
173
  >
176
- {children ?? <Icon size={14} />}
174
+ {children ?? <CheckIcon size={14} />}
177
175
  </Button>
178
176
  );
179
177
  };
@@ -11,7 +11,7 @@ export type ConversationProps = ComponentProps<typeof StickToBottom>;
11
11
 
12
12
  export const Conversation = ({ className, ...props }: ConversationProps) => (
13
13
  <StickToBottom
14
- className={cn("relative flex-1 overflow-y-auto", className)}
14
+ className={cn("relative flex-1 overflow-hidden", className)}
15
15
  initial="smooth"
16
16
  resize="smooth"
17
17
  role="log"
@@ -40,23 +40,21 @@ import {
40
40
  ImageIcon,
41
41
  Loader2Icon,
42
42
  MicIcon,
43
- PaperclipIcon,
44
43
  PlusIcon,
45
44
  SendIcon,
46
45
  SquareIcon,
47
46
  XIcon,
48
47
  } from "lucide-react";
48
+ import React from "react";
49
49
  import { nanoid } from "nanoid";
50
50
  import {
51
51
  type ChangeEvent,
52
52
  type ChangeEventHandler,
53
- Children,
54
53
  type ClipboardEventHandler,
55
54
  type ComponentProps,
56
55
  createContext,
57
56
  type FormEvent,
58
57
  type FormEventHandler,
59
- Fragment,
60
58
  type HTMLAttributes,
61
59
  type KeyboardEventHandler,
62
60
  type PropsWithChildren,
@@ -295,7 +293,7 @@ export function PromptInputAttachment({
295
293
  />
296
294
  ) : (
297
295
  <div className="flex size-5 items-center justify-center text-muted-foreground">
298
- <PaperclipIcon className="size-3" />
296
+ <div className="size-3" />
299
297
  </div>
300
298
  )}
301
299
  </div>
@@ -365,7 +363,7 @@ export function PromptInputAttachments({
365
363
  }
366
364
 
367
365
  return attachments.files.map((file) => (
368
- <Fragment key={file.id}>{children(file)}</Fragment>
366
+ <React.Fragment key={file.id}>{children(file)}</React.Fragment>
369
367
  ));
370
368
  }
371
369
 
@@ -900,7 +898,7 @@ export const PromptInputButton = ({
900
898
  ...props
901
899
  }: PromptInputButtonProps) => {
902
900
  const newSize =
903
- size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
901
+ size ?? (React.Children.count(props.children) > 1 ? "sm" : "icon-sm");
904
902
 
905
903
  return (
906
904
  <InputGroupButton
@@ -92,9 +92,14 @@ export const AIConversation = React.memo<AIConversationProps>(
92
92
  [emptyState?.description]
93
93
  )
94
94
 
95
+ const toMessageString = (content: unknown): string =>
96
+ typeof content === 'string' ? content : ''
97
+
95
98
  const renderedMessages = React.useMemo(
96
99
  () =>
97
100
  messages.map((message) => {
101
+ const contentStr = toMessageString(message.content)
102
+
98
103
  // Render based on role field
99
104
  if (message.role === "user") {
100
105
  return (
@@ -102,7 +107,7 @@ export const AIConversation = React.memo<AIConversationProps>(
102
107
  key={message.id}
103
108
  message={{
104
109
  id: message.id,
105
- content: message.content,
110
+ content: contentStr,
106
111
  avatarSrc: message.avatarSrc,
107
112
  avatarName: message.avatarName,
108
113
  }}
@@ -122,7 +127,7 @@ export const AIConversation = React.memo<AIConversationProps>(
122
127
  (tc) => tc.name !== "task" && tc.name !== "ask_user"
123
128
  ) || []
124
129
 
125
- const hasContent = message.content && message.content.trim() !== ""
130
+ const hasContent = contentStr.trim() !== ""
126
131
  if (!hasContent && directToolCalls.length === 0 && subAgents.length === 0 && !message.isLoading) {
127
132
  return null;
128
133
  }
@@ -132,7 +137,7 @@ export const AIConversation = React.memo<AIConversationProps>(
132
137
  key={message.id}
133
138
  message={{
134
139
  id: message.id,
135
- content: message.content,
140
+ content: contentStr,
136
141
  avatarSrc: message.avatarSrc,
137
142
  avatarName: message.avatarName,
138
143
  isLoading: message.isLoading,
@@ -170,7 +175,7 @@ export const AIConversation = React.memo<AIConversationProps>(
170
175
  message={{
171
176
  id: message.id,
172
177
  name: message.avatarName || "Agent",
173
- content: message.content,
178
+ content: contentStr,
174
179
  toolCalls: message.toolCalls?.filter((tc) => tc.name !== "task"),
175
180
  status: "completed",
176
181
  }}
@@ -1,5 +1,4 @@
1
1
  import * as React from "react"
2
- import { Plus } from "lucide-react"
3
2
 
4
3
  import {
5
4
  EnhancedDataTable,
@@ -8,6 +7,7 @@ import {
8
7
  } from "@/components/composites/DataTable"
9
8
  import type { DynamicTableSchema } from "ui-schema-contracts"
10
9
  import { Button } from "@/components/primitives/Button"
10
+ import { Icon } from "@/components/primitives/Icon"
11
11
 
12
12
  export interface DataTableProps {
13
13
  rows: DashboardRow[]
@@ -29,7 +29,7 @@ export const DataTable = React.memo<DataTableProps>(({ rows, tableSchema, handle
29
29
  rightActions={
30
30
  handlers?.onAddSection ? (
31
31
  <Button variant="outline" size="sm" className="h-8" onClick={handlers.onAddSection}>
32
- <Plus className="size-4" />
32
+ <Icon name="plus" size="sm" />
33
33
  <span className="hidden lg:inline">Add Section</span>
34
34
  </Button>
35
35
  ) : null
@@ -13,6 +13,8 @@ export interface InboxPanelProps {
13
13
  isLoading?: boolean
14
14
  emptyMessage?: string
15
15
  className?: string
16
+ title?: string
17
+ headerAction?: React.ReactNode
16
18
  }
17
19
 
18
20
  export const InboxPanel = React.memo<InboxPanelProps>(
@@ -26,10 +28,19 @@ export const InboxPanel = React.memo<InboxPanelProps>(
26
28
  isLoading = false,
27
29
  emptyMessage,
28
30
  className,
31
+ title,
32
+ headerAction,
29
33
  }) => {
30
34
  return (
31
35
  <section className={`flex min-h-0 flex-1 flex-col overflow-hidden ${className ?? ""}`}>
32
- <div className="p-4">
36
+ {title || headerAction ? (
37
+ <div className="flex items-center justify-between px-4 pt-4 pb-2">
38
+ {title ? <h2 className="text-sm font-semibold text-muted-foreground">{title}</h2> : <div />}
39
+ {headerAction ? <div>{headerAction}</div> : null}
40
+ </div>
41
+ ) : null}
42
+
43
+ <div className="px-4 pb-2">
33
44
  <Input
34
45
  aria-label="Search inbox items"
35
46
  className="h-8"
@@ -28,11 +28,11 @@ export const SectionLayout = React.memo<SectionLayoutProps>(
28
28
  ...section,
29
29
  resizable,
30
30
  content: (
31
- <div className="h-full min-h-0 flex flex-col overflow-auto">
31
+ <div className="h-full min-h-0 flex flex-col overflow-hidden">
32
32
  {section.header && (
33
33
  <AppHeader {...section.header} />
34
34
  )}
35
- <div className="min-h-0 flex-1 overflow-hidden">
35
+ <div className="min-h-0 flex-1 overflow-hidden flex flex-col">
36
36
  {section.content}
37
37
  </div>
38
38
  </div>
@@ -10,26 +10,15 @@ export type { DocumentEditorWithCommentsProps } from './DocumentEditorWithCommen
10
10
  export { FileChangeQueue } from './FileChangeQueue'
11
11
  export type { FileChangeQueueProps } from './FileChangeQueue'
12
12
 
13
- export { PromptInput } from './PromptInput'
14
- export type { PromptInputBlockProps } from './PromptInput'
15
-
16
13
  // Blocks
17
- export * from './LayoutProvider'
18
14
  export * from './SectionLayout'
19
- export type { LayoutProviderProps } from './LayoutProvider'
20
15
 
21
16
  export { WorkflowCanvas, getLayoutedElements } from './WorkflowCanvas'
22
17
  export type { WorkflowCanvasProps, WorkflowNode, WorkflowEdge, WorkflowNodeData } from './WorkflowCanvas'
23
18
 
24
- export { DashboardHeader } from './DashboardHeader'
25
- export type { DashboardHeaderProps } from './DashboardHeader'
26
-
27
19
  export { DashboardMetrics } from './DashboardMetrics'
28
20
  export type { DashboardMetricsProps } from './DashboardMetrics'
29
21
 
30
- export { DashboardChart } from './DashboardChart'
31
- export type { DashboardChartProps } from './DashboardChart'
32
-
33
22
  export { DataTable } from './DataTable'
34
23
  export type { DataTableProps } from './DataTable'
35
24
 
@@ -22,7 +22,11 @@ export const AppHeader = React.memo<AppHeaderProps>(({
22
22
  {showSidebarToggle && showTitle && title && (
23
23
  <Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
24
24
  )}
25
- {showTitle && title && <h1 className="max-w-[28rem] truncate text-base font-medium">{title}</h1>}
25
+ {showTitle && title && (
26
+ typeof title === 'string'
27
+ ? <h1 className="max-w-[28rem] truncate text-base font-medium">{title}</h1>
28
+ : title
29
+ )}
26
30
  </div>
27
31
 
28
32
  <div className="justify-self-center">
@@ -6,7 +6,7 @@ export interface TabItem {
6
6
  }
7
7
 
8
8
  export interface AppHeaderProps {
9
- title?: string;
9
+ title?: React.ReactNode;
10
10
  actions?: React.ReactNode;
11
11
  tabs?: TabItem[];
12
12
  defaultTab?: string;
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as React from "react";
4
4
  import { useState } from "react";
5
- import { Check, Pencil, X, ChevronLeft, ChevronRight } from "lucide-react";
5
+ import { Icon } from "@/components/primitives/Icon";
6
6
  import { Input } from "@/components/primitives/Input";
7
7
  import { Textarea } from "@/components/primitives/Textarea";
8
8
  import { Label } from "@/components/primitives/Label";
@@ -98,6 +98,10 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
98
98
  const [rejectReason, setRejectReason] = useState("");
99
99
  const [showRejectInput, setShowRejectInput] = useState(false);
100
100
 
101
+ const hideSkipButton = (actionRequest.args?.hide_skip_button as boolean) ?? false;
102
+ const submitButtonText = (actionRequest.args?.button_submit_text as string) || 'Continue';
103
+ const skipButtonText = (actionRequest.args?.button_skip_text as string) || 'Skip';
104
+
101
105
  // Question states
102
106
  const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
103
107
  const [selectedAnswers, setSelectedAnswers] = useState<Record<number, string | string[]>>({});
@@ -117,13 +121,15 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
117
121
  const isQuestionAction =
118
122
  actionRequest.name === "ask_question" ||
119
123
  (actionRequest.args &&
120
- (typeof actionRequest.args.question === "string" ||
121
- Array.isArray(actionRequest.args.questions)));
124
+ ((typeof actionRequest.args.question === "string") ||
125
+ (Array.isArray(actionRequest.args.questions) && actionRequest.args.questions.length > 0)));
122
126
 
123
127
  // Parse questions
124
128
  let questions: Question[] = [];
125
129
  if (actionRequest.args && Array.isArray(actionRequest.args.questions)) {
126
130
  questions = actionRequest.args.questions as Question[];
131
+ } else if (actionRequest.args && actionRequest.args.questions && typeof actionRequest.args.questions === "object") {
132
+ questions = Object.values(actionRequest.args.questions) as Question[];
127
133
  } else if (actionRequest.args && typeof actionRequest.args.question === "string") {
128
134
  questions = [
129
135
  {
@@ -144,19 +150,37 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
144
150
  // Render interactive question view
145
151
  if (isQuestionAction && currentQuestion) {
146
152
  const handleContinue = () => {
153
+ const qIdx = currentQuestionIndex;
154
+ const selIdx = selectedOptionIndices[qIdx];
155
+ const hasOtherSelected = selIdx === options.length;
156
+ const hasRegularSelection = selIdx !== null && selIdx !== undefined && !hasOtherSelected;
157
+ const hasMultiSelectItems = isMultiSelect && Array.isArray(selectedAnswers[qIdx]) && (selectedAnswers[qIdx] as string[]).length > 0;
158
+
159
+ const isApprovalType = actionRequest.args?.type === 'approval';
160
+ const isOtherEmpty = hasOtherSelected && !otherTexts[qIdx]?.trim();
161
+ const hasNoValidSelection = (!hasRegularSelection && !hasMultiSelectItems && !hasOtherSelected) || isOtherEmpty;
162
+
163
+ let answer: string | string[] = "Approved";
164
+
165
+ if (hasOtherSelected) {
166
+ answer = otherTexts[qIdx] || "Approved";
167
+ } else if (isMultiSelect) {
168
+ answer = (selectedAnswers[qIdx] as string[]).join(", ") || "Approved";
169
+ } else if (hasRegularSelection) {
170
+ answer = selectedAnswers[qIdx] as string || "Approved";
171
+ }
172
+
173
+ if (!isApprovalType && hasNoValidSelection) return;
174
+
147
175
  if (currentQuestionIndex === questions.length - 1) {
148
176
  const finalAnswers = questions.map((q, idx) => {
149
- if (selectedOptionIndices[idx] === q.options?.length) {
150
- return otherTexts[idx] || "";
151
- }
177
+ if (idx === qIdx) return answer;
152
178
  return selectedAnswers[idx] || "";
153
179
  });
154
-
155
- if (onEdit) {
156
- onEdit({ ...actionRequest.args, answers: finalAnswers });
157
- }
158
- onApprove();
180
+ if (onEdit) onEdit({ ...actionRequest.args, answers: finalAnswers });
181
+ else onApprove();
159
182
  } else {
183
+ setSelectedAnswers((prev) => ({ ...prev, [qIdx]: answer }));
160
184
  setCurrentQuestionIndex((prev) => prev + 1);
161
185
  }
162
186
  };
@@ -198,7 +222,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
198
222
  disabled={currentQuestionIndex === 0}
199
223
  className="hover:text-foreground disabled:opacity-30 p-0.5 transition-colors"
200
224
  >
201
- <ChevronLeft className="h-4 w-4" />
225
+ <Icon name="chevron-left" size="sm" />
202
226
  </button>
203
227
  <span className="font-medium">{currentQuestionIndex + 1} of {questions.length}</span>
204
228
  <button
@@ -209,7 +233,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
209
233
  disabled={currentQuestionIndex === questions.length - 1}
210
234
  className="hover:text-foreground disabled:opacity-30 p-0.5 transition-colors"
211
235
  >
212
- <ChevronRight className="h-4 w-4" />
236
+ <Icon name="chevron-right" size="sm" />
213
237
  </button>
214
238
  </div>
215
239
  )}
@@ -297,17 +321,19 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
297
321
  </div>
298
322
 
299
323
  <ConfirmationActions>
300
- <ConfirmationAction
301
- variant="outline"
302
- type="button"
303
- onClick={() => {
304
- if (onReject) {
305
- onReject("Skipped question");
306
- }
307
- }}
308
- >
309
- Skip
310
- </ConfirmationAction>
324
+ {!hideSkipButton && (
325
+ <ConfirmationAction
326
+ variant="outline"
327
+ type="button"
328
+ onClick={() => {
329
+ if (onReject) {
330
+ onReject("Skipped question");
331
+ }
332
+ }}
333
+ >
334
+ {skipButtonText}
335
+ </ConfirmationAction>
336
+ )}
311
337
 
312
338
  <ConfirmationAction
313
339
  type="button"
@@ -317,13 +343,13 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
317
343
  >
318
344
  {currentQuestionIndex === questions.length - 1 ? (
319
345
  <>
320
- <Check className="h-4 w-4 mr-2" />
321
- Continue
346
+ <Icon name="check" size="sm" className="mr-2" />
347
+ {submitButtonText}
322
348
  </>
323
349
  ) : (
324
350
  <>
325
351
  Next
326
- <ChevronRight className="h-4 w-4 ml-2" />
352
+ <Icon name="chevron-right" size="sm" className="ml-2" />
327
353
  </>
328
354
  )}
329
355
  </ConfirmationAction>
@@ -344,7 +370,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
344
370
  {isEditing ? (
345
371
  <div className="space-y-4">
346
372
  <div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
347
- <Pencil className="h-4 w-4" />
373
+ <Icon name="pencil" size="sm" />
348
374
  <span>
349
375
  Edit —{" "}
350
376
  <code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded">
@@ -409,7 +435,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
409
435
  }}
410
436
  disabled={isProcessing}
411
437
  >
412
- <Check className="h-4 w-4 mr-2" />
438
+ <Icon name="check" size="sm" className="mr-2" />
413
439
  Save & Approve
414
440
  </ConfirmationAction>
415
441
  </ConfirmationActions>
@@ -423,24 +449,26 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
423
449
  )}
424
450
 
425
451
  <div className="mt-3 space-y-3">
426
- {Object.entries(actionRequest.args).map(([key, value]) => {
427
- const strValue = formatValue(value);
428
- const isMultiline = strValue.includes("\n");
429
- return (
430
- <div key={key} className="space-y-1">
431
- <div className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">
432
- {key}
452
+ {Object.entries(actionRequest.args)
453
+ .filter(([_, value]) => typeof value === "string" || typeof value === "number" || typeof value === "boolean")
454
+ .map(([key, value]) => {
455
+ const strValue = formatValue(value);
456
+ const isMultiline = strValue.includes("\n");
457
+ return (
458
+ <div key={key} className="space-y-1">
459
+ <div className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">
460
+ {key}
461
+ </div>
462
+ {isMultiline ? (
463
+ <pre className="text-xs text-card-foreground whitespace-pre-wrap break-all font-mono bg-muted/50 rounded-md px-2.5 py-1.5 border border-border">
464
+ {strValue}
465
+ </pre>
466
+ ) : (
467
+ <div className="text-sm text-card-foreground break-all">{strValue}</div>
468
+ )}
433
469
  </div>
434
- {isMultiline ? (
435
- <pre className="text-xs text-card-foreground whitespace-pre-wrap break-all font-mono bg-muted/50 rounded-md px-2.5 py-1.5 border border-border">
436
- {strValue}
437
- </pre>
438
- ) : (
439
- <div className="text-sm text-card-foreground break-all">{strValue}</div>
440
- )}
441
- </div>
442
- );
443
- })}
470
+ );
471
+ })}
444
472
  </div>
445
473
  </div>
446
474
 
@@ -483,21 +511,21 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
483
511
  }}
484
512
  disabled={isProcessing}
485
513
  >
486
- <X className="h-4 w-4 mr-2" />
514
+ <Icon name="x" size="sm" className="mr-2" />
487
515
  Confirm Rejection
488
516
  </ConfirmationAction>
489
517
  </>
490
518
  ) : (
491
519
  <>
492
- {canReject && (
520
+ {canReject && !hideSkipButton && (
493
521
  <ConfirmationAction
494
522
  type="button"
495
523
  variant="outline"
496
524
  onClick={() => setShowRejectInput(true)}
497
525
  disabled={isProcessing}
498
526
  >
499
- <X className="h-4 w-4 mr-2" />
500
- Reject
527
+ <Icon name="x" size="sm" className="mr-2" />
528
+ {skipButtonText}
501
529
  </ConfirmationAction>
502
530
  )}
503
531
  {canEdit && (
@@ -507,7 +535,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
507
535
  onClick={() => setIsEditing(true)}
508
536
  disabled={isProcessing}
509
537
  >
510
- <Pencil className="h-4 w-4 mr-2" />
538
+ <Icon name="pencil" size="sm" className="mr-2" />
511
539
  Edit
512
540
  </ConfirmationAction>
513
541
  )}
@@ -517,8 +545,8 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
517
545
  onClick={onApprove}
518
546
  disabled={isProcessing}
519
547
  >
520
- <Check className="h-4 w-4 mr-2" />
521
- Approve
548
+ <Icon name="check" size="sm" className="mr-2" />
549
+ {submitButtonText}
522
550
  </ConfirmationAction>
523
551
  </>
524
552
  )}
@@ -56,7 +56,7 @@ import {
56
56
  InputGroupTextarea,
57
57
  InputGroupButton,
58
58
  } from '@/components/primitives/InputGroup'
59
- import { Send } from 'lucide-react'
59
+ import { Icon } from '@/components/primitives/Icon'
60
60
  import { formatCommentDate } from '@/utils/ai-editor'
61
61
  import type { CommentBoxProps, Comment } from '@/types/ai-editor'
62
62
  import { cn } from '@/lib/utils'
@@ -132,7 +132,7 @@ const CommentThreadDisplay = React.memo<{
132
132
  }
133
133
  }}
134
134
  >
135
- <Send className="h-4 w-4" />
135
+ <Icon name="send" size="sm" />
136
136
  </InputGroupButton>
137
137
  </InputGroup>
138
138
  </ConversationContent>
@@ -208,7 +208,7 @@ export const CommentBox = React.memo<CommentBoxProps>(
208
208
  }
209
209
  }}
210
210
  >
211
- <Send className="h-4 w-4" />
211
+ <Icon name="send" size="sm" />
212
212
  </InputGroupButton>
213
213
  </InputGroup>
214
214
  </div>
@@ -18,3 +18,5 @@ export const DashboardHeader = React.memo<DashboardHeaderProps>(({ ctaLabel = "Q
18
18
  })
19
19
 
20
20
  DashboardHeader.displayName = "DashboardHeader"
21
+
22
+
@@ -1,7 +1,7 @@
1
1
  import { useSortable } from "@dnd-kit/sortable"
2
- import { GripVertical } from "lucide-react"
3
2
 
4
3
  import { Button } from "@/components/primitives/Button"
4
+ import { Icon } from "@/components/primitives/Icon"
5
5
 
6
6
  export interface DragHandleCellProps {
7
7
  id: number | string
@@ -19,7 +19,7 @@ export function DragHandleCell({ id }: DragHandleCellProps) {
19
19
  className="size-7 text-muted-foreground hover:bg-transparent"
20
20
  aria-label="Drag to reorder"
21
21
  >
22
- <GripVertical className="size-3" />
22
+ <Icon name="grip-vertical" size="xs" />
23
23
  </Button>
24
24
  )
25
25
  }
@@ -19,7 +19,7 @@ const tableSchema = dynamicTableSchema.parse({
19
19
  enablePagination: true,
20
20
  enableRowSelection: true,
21
21
  columns: [
22
- { key: "header", label: "Header", sortable: true, hideable: false },
22
+ { key: "header", label: "Header", renderType: "link", sortable: true, hideable: false },
23
23
  { key: "type", label: "Type", sortable: true },
24
24
  { key: "status", label: "Status", renderType: "badge", sortable: true },
25
25
  { key: "target", label: "Target", align: "right", sortable: true },