@chat-js/cli 0.6.1 → 0.6.2

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 (153) hide show
  1. package/dist/index.js +16938 -16786
  2. package/package.json +1 -1
  3. package/templates/chat-app/app/(auth)/login/page.tsx +3 -3
  4. package/templates/chat-app/app/(chat)/api/chat/route.ts +4 -60
  5. package/templates/chat-app/app/not-found.tsx +2 -2
  6. package/templates/chat-app/chat.config.ts +3 -0
  7. package/templates/chat-app/components/ai-elements/actions.tsx +44 -44
  8. package/templates/chat-app/components/ai-elements/artifact.tsx +92 -92
  9. package/templates/chat-app/components/ai-elements/code-block.tsx +143 -143
  10. package/templates/chat-app/components/ai-elements/context.tsx +313 -313
  11. package/templates/chat-app/components/ai-elements/conversation.tsx +65 -65
  12. package/templates/chat-app/components/ai-elements/extra/conversation-content-scroll-area.tsx +29 -29
  13. package/templates/chat-app/components/ai-elements/extra/mcp-tool-header.tsx +27 -27
  14. package/templates/chat-app/components/ai-elements/message.tsx +341 -344
  15. package/templates/chat-app/components/ai-elements/parseIncompleteMarkdown.tsx +122 -122
  16. package/templates/chat-app/components/ai-elements/prompt-input.tsx +1059 -1059
  17. package/templates/chat-app/components/ai-elements/reasoning.tsx +131 -131
  18. package/templates/chat-app/components/ai-elements/response.tsx +15 -12
  19. package/templates/chat-app/components/ai-elements/sandbox.tsx +84 -84
  20. package/templates/chat-app/components/ai-elements/shimmer.tsx +47 -47
  21. package/templates/chat-app/components/ai-elements/suggestion.tsx +33 -33
  22. package/templates/chat-app/components/ai-elements/tool.tsx +118 -118
  23. package/templates/chat-app/components/app-sidebar-history-conditional.tsx +3 -3
  24. package/templates/chat-app/components/app-sidebar.tsx +3 -3
  25. package/templates/chat-app/components/connectors-dropdown.tsx +6 -3
  26. package/templates/chat-app/components/deep-research-progress.tsx +1 -1
  27. package/templates/chat-app/components/header-breadcrumb.tsx +14 -11
  28. package/templates/chat-app/components/internal-link.tsx +73 -0
  29. package/templates/chat-app/components/login-form.tsx +5 -5
  30. package/templates/chat-app/components/message-parts.tsx +1 -71
  31. package/templates/chat-app/components/model-selector.tsx +3 -3
  32. package/templates/chat-app/components/new-chat-button.tsx +4 -4
  33. package/templates/chat-app/components/part/document-common.tsx +3 -3
  34. package/templates/chat-app/components/part/document-tool.tsx +3 -3
  35. package/templates/chat-app/components/part/message-annotations.tsx +2 -2
  36. package/templates/chat-app/components/part/tool-part.tsx +92 -0
  37. package/templates/chat-app/components/project-chat-item.tsx +2 -2
  38. package/templates/chat-app/components/research-progress.tsx +2 -2
  39. package/templates/chat-app/components/research-task.tsx +1 -1
  40. package/templates/chat-app/components/research-tasks.tsx +1 -1
  41. package/templates/chat-app/components/settings/connectors-settings.tsx +4 -4
  42. package/templates/chat-app/components/settings/mcp-details-page.tsx +5 -5
  43. package/templates/chat-app/components/settings/settings-nav.tsx +3 -3
  44. package/templates/chat-app/components/sidebar-chat-item.tsx +4 -12
  45. package/templates/chat-app/components/sidebar-project-item.tsx +4 -11
  46. package/templates/chat-app/components/sidebar-top-row.tsx +7 -7
  47. package/templates/chat-app/components/sidebar-user-nav.tsx +3 -3
  48. package/templates/chat-app/components/signup-form.tsx +8 -5
  49. package/templates/chat-app/components/source-badge.tsx +3 -9
  50. package/templates/chat-app/components/sources.tsx +1 -1
  51. package/templates/chat-app/components/ui/accordion.tsx +32 -32
  52. package/templates/chat-app/components/ui/alert-dialog.tsx +103 -103
  53. package/templates/chat-app/components/ui/alert.tsx +36 -36
  54. package/templates/chat-app/components/ui/avatar.tsx +28 -28
  55. package/templates/chat-app/components/ui/badge.tsx +22 -22
  56. package/templates/chat-app/components/ui/breadcrumb.tsx +72 -72
  57. package/templates/chat-app/components/ui/button-group.tsx +58 -58
  58. package/templates/chat-app/components/ui/button.tsx +45 -45
  59. package/templates/chat-app/components/ui/card.tsx +65 -65
  60. package/templates/chat-app/components/ui/checkbox.tsx +16 -16
  61. package/templates/chat-app/components/ui/collapsible.tsx +1 -1
  62. package/templates/chat-app/components/ui/command.tsx +137 -137
  63. package/templates/chat-app/components/ui/dialog.tsx +94 -94
  64. package/templates/chat-app/components/ui/drawer.tsx +68 -68
  65. package/templates/chat-app/components/ui/dropdown-menu.tsx +184 -184
  66. package/templates/chat-app/components/ui/empty.tsx +76 -76
  67. package/templates/chat-app/components/ui/extra/action-container.tsx +3 -3
  68. package/templates/chat-app/components/ui/extra/scroll-area-viewport-ref.tsx +24 -24
  69. package/templates/chat-app/components/ui/form.tsx +112 -112
  70. package/templates/chat-app/components/ui/hover-card.tsx +25 -25
  71. package/templates/chat-app/components/ui/input-group.tsx +126 -126
  72. package/templates/chat-app/components/ui/input.tsx +13 -13
  73. package/templates/chat-app/components/ui/label.tsx +12 -12
  74. package/templates/chat-app/components/ui/popover.tsx +25 -25
  75. package/templates/chat-app/components/ui/progress.tsx +19 -19
  76. package/templates/chat-app/components/ui/resizable.tsx +27 -27
  77. package/templates/chat-app/components/ui/scroll-area.tsx +30 -30
  78. package/templates/chat-app/components/ui/select.tsx +108 -108
  79. package/templates/chat-app/components/ui/separator.tsx +16 -16
  80. package/templates/chat-app/components/ui/sheet.tsx +91 -91
  81. package/templates/chat-app/components/ui/sidebar.tsx +615 -615
  82. package/templates/chat-app/components/ui/skeleton.tsx +7 -7
  83. package/templates/chat-app/components/ui/slider.tsx +50 -50
  84. package/templates/chat-app/components/ui/spinner.tsx +8 -8
  85. package/templates/chat-app/components/ui/switch.tsx +16 -16
  86. package/templates/chat-app/components/ui/table.tsx +71 -71
  87. package/templates/chat-app/components/ui/tabs.tsx +31 -31
  88. package/templates/chat-app/components/ui/textarea.tsx +10 -10
  89. package/templates/chat-app/components/ui/toggle.tsx +31 -31
  90. package/templates/chat-app/components/ui/tooltip.tsx +48 -48
  91. package/templates/chat-app/components/upgrade-cta/limit-display.tsx +7 -7
  92. package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +3 -3
  93. package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +3 -3
  94. package/templates/chat-app/hooks/use-mobile.ts +13 -13
  95. package/templates/chat-app/lib/ai/core-chat-agent.ts +25 -14
  96. package/templates/chat-app/lib/ai/eval-agent.ts +4 -5
  97. package/templates/chat-app/lib/ai/installed-tools.ts +12 -0
  98. package/templates/chat-app/lib/ai/mcp/mcp-client.ts +2 -2
  99. package/templates/chat-app/lib/ai/models.generated.ts +4236 -4585
  100. package/templates/chat-app/lib/ai/tool-renderer-registry.ts +31 -0
  101. package/templates/chat-app/lib/ai/types.ts +15 -20
  102. package/templates/chat-app/lib/config-requirements.ts +11 -6
  103. package/templates/chat-app/lib/config-schema.ts +13 -0
  104. package/templates/chat-app/lib/stores/hooks-message-parts.ts +1 -1
  105. package/templates/chat-app/lib/utils.ts +157 -157
  106. package/templates/chat-app/package.json +1 -1
  107. package/templates/chat-app/scripts/check-env.ts +229 -2
  108. package/templates/chat-app/tools/chatjs/_shared/lib/tool-part.ts +5 -0
  109. package/templates/chat-app/{components/part/weather.tsx → tools/chatjs/get-weather/renderer.tsx} +24 -38
  110. package/templates/chat-app/{components/part/retrieve-url.tsx → tools/chatjs/retrieve-url/renderer.tsx} +20 -15
  111. package/templates/chat-app/{lib/ai/tools/retrieve-url.ts → tools/chatjs/retrieve-url/tool.ts} +46 -7
  112. package/templates/chat-app/tools/chatjs/tools.ts +16 -0
  113. package/templates/chat-app/tools/chatjs/ui.ts +17 -0
  114. package/templates/chat-app/tools/chatjs/word-count/renderer.tsx +50 -0
  115. package/templates/chat-app/tools/chatjs/word-count/tool.ts +30 -0
  116. package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.ts +3 -5
  117. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/deep-research.ts +2 -3
  118. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/pipeline.ts +1 -1
  119. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/types.ts +1 -1
  120. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/utils.ts +7 -7
  121. package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/types.ts +1 -1
  122. package/templates/chat-app/{lib/ai/tools → tools/platform}/generate-video.ts +4 -6
  123. package/templates/chat-app/{lib/ai/tools → tools/platform}/read-document.ts +2 -2
  124. package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/multi-query-web-search.ts +1 -1
  125. package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/web-search.ts +1 -1
  126. package/templates/chat-app/{lib/ai/tools → tools/platform}/tools.ts +20 -20
  127. package/templates/chat-app/{lib/ai/tools → tools/platform}/web-search.ts +7 -5
  128. package/templates/electron/CHANGELOG.md +7 -1
  129. package/templates/electron/package.json +1 -1
  130. package/templates/chat-app/lib/ai/tools/tools-definitions.ts +0 -83
  131. /package/templates/chat-app/{lib/ai/tools/get-weather.ts → tools/chatjs/get-weather/tool.ts} +0 -0
  132. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.javascript.ts +0 -0
  133. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.python.ts +0 -0
  134. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.shared.test.ts +0 -0
  135. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.shared.ts +0 -0
  136. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.types.ts +0 -0
  137. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/configuration.ts +0 -0
  138. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/prompts.ts +0 -0
  139. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/researcher-agent.ts +0 -0
  140. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/supervisor-agent.ts +0 -0
  141. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/code-guidelines.ts +0 -0
  142. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-code-document.ts +0 -0
  143. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-sheet-document.ts +0 -0
  144. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-text-document.ts +0 -0
  145. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-code-document.ts +0 -0
  146. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-sheet-document.ts +0 -0
  147. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-text-document.ts +0 -0
  148. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/sheet-guidelines.ts +0 -0
  149. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/text-guidelines.ts +0 -0
  150. /package/templates/chat-app/{lib/ai/tools → tools/platform}/generate-image.ts +0 -0
  151. /package/templates/chat-app/{lib/ai/tools → tools/platform}/research-updates-schema.ts +0 -0
  152. /package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/search-utils.ts +0 -0
  153. /package/templates/chat-app/{lib/ai/tools → tools/platform}/types.ts +0 -0
@@ -2,63 +2,63 @@
2
2
 
3
3
  import { motion } from "motion/react";
4
4
  import {
5
- type CSSProperties,
6
- type ElementType,
7
- type JSX,
8
- memo,
9
- useMemo,
5
+ type CSSProperties,
6
+ type ElementType,
7
+ type JSX,
8
+ memo,
9
+ useMemo,
10
10
  } from "react";
11
11
  import { cn } from "@/lib/utils";
12
12
 
13
13
  export type TextShimmerProps = {
14
- children: string;
15
- as?: ElementType;
16
- className?: string;
17
- duration?: number;
18
- spread?: number;
14
+ children: string;
15
+ as?: ElementType;
16
+ className?: string;
17
+ duration?: number;
18
+ spread?: number;
19
19
  };
20
20
 
21
21
  const ShimmerComponent = ({
22
- children,
23
- as: Component = "p",
24
- className,
25
- duration = 2,
26
- spread = 2,
22
+ children,
23
+ as: Component = "p",
24
+ className,
25
+ duration = 2,
26
+ spread = 2,
27
27
  }: TextShimmerProps) => {
28
- const MotionComponent = motion.create(
29
- Component as keyof JSX.IntrinsicElements
30
- );
28
+ const MotionComponent = motion.create(
29
+ Component as keyof JSX.IntrinsicElements,
30
+ );
31
31
 
32
- const dynamicSpread = useMemo(
33
- () => (children?.length ?? 0) * spread,
34
- [children, spread]
35
- );
32
+ const dynamicSpread = useMemo(
33
+ () => (children?.length ?? 0) * spread,
34
+ [children, spread],
35
+ );
36
36
 
37
- return (
38
- <MotionComponent
39
- animate={{ backgroundPosition: "0% center" }}
40
- className={cn(
41
- "relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
42
- "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
43
- className
44
- )}
45
- initial={{ backgroundPosition: "100% center" }}
46
- style={
47
- {
48
- "--spread": `${dynamicSpread}px`,
49
- backgroundImage:
50
- "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))",
51
- } as CSSProperties
52
- }
53
- transition={{
54
- repeat: Number.POSITIVE_INFINITY,
55
- duration,
56
- ease: "linear",
57
- }}
58
- >
59
- {children}
60
- </MotionComponent>
61
- );
37
+ return (
38
+ <MotionComponent
39
+ animate={{ backgroundPosition: "0% center" }}
40
+ className={cn(
41
+ "relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
42
+ "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
43
+ className,
44
+ )}
45
+ initial={{ backgroundPosition: "100% center" }}
46
+ style={
47
+ {
48
+ "--spread": `${dynamicSpread}px`,
49
+ backgroundImage:
50
+ "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))",
51
+ } as CSSProperties
52
+ }
53
+ transition={{
54
+ repeat: Number.POSITIVE_INFINITY,
55
+ duration,
56
+ ease: "linear",
57
+ }}
58
+ >
59
+ {children}
60
+ </MotionComponent>
61
+ );
62
62
  };
63
63
 
64
64
  export const Shimmer = memo(ShimmerComponent);
@@ -8,46 +8,46 @@ import { cn } from "@/lib/utils";
8
8
  export type SuggestionsProps = ComponentProps<typeof ScrollArea>;
9
9
 
10
10
  export const Suggestions = ({
11
- className,
12
- children,
13
- ...props
11
+ className,
12
+ children,
13
+ ...props
14
14
  }: SuggestionsProps) => (
15
- <ScrollArea className="w-full overflow-x-auto whitespace-nowrap" {...props}>
16
- <div className={cn("flex w-max flex-nowrap items-center gap-2", className)}>
17
- {children}
18
- </div>
19
- <ScrollBar className="hidden" orientation="horizontal" />
20
- </ScrollArea>
15
+ <ScrollArea className="w-full overflow-x-auto whitespace-nowrap" {...props}>
16
+ <div className={cn("flex w-max flex-nowrap items-center gap-2", className)}>
17
+ {children}
18
+ </div>
19
+ <ScrollBar className="hidden" orientation="horizontal" />
20
+ </ScrollArea>
21
21
  );
22
22
 
23
23
  export type SuggestionProps = Omit<ComponentProps<typeof Button>, "onClick"> & {
24
- suggestion: string;
25
- onClick?: (suggestion: string) => void;
24
+ suggestion: string;
25
+ onClick?: (suggestion: string) => void;
26
26
  };
27
27
 
28
28
  export const Suggestion = ({
29
- suggestion,
30
- onClick,
31
- className,
32
- variant = "outline",
33
- size = "sm",
34
- children,
35
- ...props
29
+ suggestion,
30
+ onClick,
31
+ className,
32
+ variant = "outline",
33
+ size = "sm",
34
+ children,
35
+ ...props
36
36
  }: SuggestionProps) => {
37
- const handleClick = () => {
38
- onClick?.(suggestion);
39
- };
37
+ const handleClick = () => {
38
+ onClick?.(suggestion);
39
+ };
40
40
 
41
- return (
42
- <Button
43
- className={cn("cursor-pointer rounded-full px-4", className)}
44
- onClick={handleClick}
45
- size={size}
46
- type="button"
47
- variant={variant}
48
- {...props}
49
- >
50
- {children || suggestion}
51
- </Button>
52
- );
41
+ return (
42
+ <Button
43
+ className={cn("cursor-pointer rounded-full px-4", className)}
44
+ onClick={handleClick}
45
+ size={size}
46
+ type="button"
47
+ variant={variant}
48
+ {...props}
49
+ >
50
+ {children || suggestion}
51
+ </Button>
52
+ );
53
53
  };
@@ -2,20 +2,20 @@
2
2
 
3
3
  import type { ToolUIPart } from "ai";
4
4
  import {
5
- CheckCircleIcon,
6
- ChevronDownIcon,
7
- CircleIcon,
8
- ClockIcon,
9
- WrenchIcon,
10
- XCircleIcon,
5
+ CheckCircleIcon,
6
+ ChevronDownIcon,
7
+ CircleIcon,
8
+ ClockIcon,
9
+ WrenchIcon,
10
+ XCircleIcon,
11
11
  } from "lucide-react";
12
12
  import type { ComponentProps, ReactNode } from "react";
13
13
  import { isValidElement } from "react";
14
14
  import { Badge } from "@/components/ui/badge";
15
15
  import {
16
- Collapsible,
17
- CollapsibleContent,
18
- CollapsibleTrigger,
16
+ Collapsible,
17
+ CollapsibleContent,
18
+ CollapsibleTrigger,
19
19
  } from "@/components/ui/collapsible";
20
20
  import { cn } from "@/lib/utils";
21
21
  import { CodeBlock } from "./code-block";
@@ -23,141 +23,141 @@ import { CodeBlock } from "./code-block";
23
23
  export type ToolProps = ComponentProps<typeof Collapsible>;
24
24
 
25
25
  export const Tool = ({ className, ...props }: ToolProps) => (
26
- <Collapsible
27
- className={cn("not-prose mb-4 w-full rounded-md border", className)}
28
- {...props}
29
- />
26
+ <Collapsible
27
+ className={cn("not-prose mb-4 w-full rounded-md border", className)}
28
+ {...props}
29
+ />
30
30
  );
31
31
 
32
32
  export type ToolHeaderProps = {
33
- title?: string;
34
- type: ToolUIPart["type"];
35
- state: ToolUIPart["state"];
36
- className?: string;
33
+ title?: string;
34
+ type: ToolUIPart["type"];
35
+ state: ToolUIPart["state"];
36
+ className?: string;
37
37
  };
38
38
 
39
39
  export const getStatusBadge = (status: ToolUIPart["state"]) => {
40
- const labels: Record<ToolUIPart["state"], string> = {
41
- "input-streaming": "Pending",
42
- "input-available": "Running",
43
- "approval-requested": "Awaiting Approval",
44
- "approval-responded": "Responded",
45
- "output-available": "Completed",
46
- "output-error": "Error",
47
- "output-denied": "Denied",
48
- };
49
-
50
- const icons: Record<ToolUIPart["state"], ReactNode> = {
51
- "input-streaming": <CircleIcon className="size-4" />,
52
- "input-available": <ClockIcon className="size-4 animate-pulse" />,
53
- "approval-requested": <ClockIcon className="size-4 text-yellow-600" />,
54
- "approval-responded": <CheckCircleIcon className="size-4 text-blue-600" />,
55
- "output-available": <CheckCircleIcon className="size-4 text-green-600" />,
56
- "output-error": <XCircleIcon className="size-4 text-red-600" />,
57
- "output-denied": <XCircleIcon className="size-4 text-orange-600" />,
58
- };
59
-
60
- return (
61
- <Badge className="gap-1.5 rounded-full text-xs" variant="secondary">
62
- {icons[status]}
63
- {labels[status]}
64
- </Badge>
65
- );
40
+ const labels: Record<ToolUIPart["state"], string> = {
41
+ "input-streaming": "Pending",
42
+ "input-available": "Running",
43
+ "approval-requested": "Awaiting Approval",
44
+ "approval-responded": "Responded",
45
+ "output-available": "Completed",
46
+ "output-error": "Error",
47
+ "output-denied": "Denied",
48
+ };
49
+
50
+ const icons: Record<ToolUIPart["state"], ReactNode> = {
51
+ "input-streaming": <CircleIcon className="size-4" />,
52
+ "input-available": <ClockIcon className="size-4 animate-pulse" />,
53
+ "approval-requested": <ClockIcon className="size-4 text-yellow-600" />,
54
+ "approval-responded": <CheckCircleIcon className="size-4 text-blue-600" />,
55
+ "output-available": <CheckCircleIcon className="size-4 text-green-600" />,
56
+ "output-error": <XCircleIcon className="size-4 text-red-600" />,
57
+ "output-denied": <XCircleIcon className="size-4 text-orange-600" />,
58
+ };
59
+
60
+ return (
61
+ <Badge className="gap-1.5 rounded-full text-xs" variant="secondary">
62
+ {icons[status]}
63
+ {labels[status]}
64
+ </Badge>
65
+ );
66
66
  };
67
67
 
68
68
  export const ToolHeader = ({
69
- className,
70
- title,
71
- type,
72
- state,
73
- ...props
69
+ className,
70
+ title,
71
+ type,
72
+ state,
73
+ ...props
74
74
  }: ToolHeaderProps) => (
75
- <CollapsibleTrigger
76
- className={cn(
77
- "flex w-full items-center justify-between gap-4 p-3",
78
- className
79
- )}
80
- {...props}
81
- >
82
- <div className="flex items-center gap-2">
83
- <WrenchIcon className="size-4 text-muted-foreground" />
84
- <span className="font-medium text-sm">
85
- {title ?? type.split("-").slice(1).join("-")}
86
- </span>
87
- {getStatusBadge(state)}
88
- </div>
89
- <ChevronDownIcon className="size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" />
90
- </CollapsibleTrigger>
75
+ <CollapsibleTrigger
76
+ className={cn(
77
+ "flex w-full items-center justify-between gap-4 p-3",
78
+ className,
79
+ )}
80
+ {...props}
81
+ >
82
+ <div className="flex items-center gap-2">
83
+ <WrenchIcon className="size-4 text-muted-foreground" />
84
+ <span className="font-medium text-sm">
85
+ {title ?? type.split("-").slice(1).join("-")}
86
+ </span>
87
+ {getStatusBadge(state)}
88
+ </div>
89
+ <ChevronDownIcon className="size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" />
90
+ </CollapsibleTrigger>
91
91
  );
92
92
 
93
93
  export type ToolContentProps = ComponentProps<typeof CollapsibleContent>;
94
94
 
95
95
  export const ToolContent = ({ className, ...props }: ToolContentProps) => (
96
- <CollapsibleContent
97
- className={cn(
98
- "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
99
- className
100
- )}
101
- {...props}
102
- />
96
+ <CollapsibleContent
97
+ className={cn(
98
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
99
+ className,
100
+ )}
101
+ {...props}
102
+ />
103
103
  );
104
104
 
105
105
  export type ToolInputProps = ComponentProps<"div"> & {
106
- input: ToolUIPart["input"];
106
+ input: ToolUIPart["input"];
107
107
  };
108
108
 
109
109
  export const ToolInput = ({ className, input, ...props }: ToolInputProps) => (
110
- <div className={cn("space-y-2 overflow-hidden p-4", className)} {...props}>
111
- <h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
112
- Parameters
113
- </h4>
114
- <div className="rounded-md bg-muted/50">
115
- <CodeBlock code={JSON.stringify(input, null, 2)} language="json" />
116
- </div>
117
- </div>
110
+ <div className={cn("space-y-2 overflow-hidden p-4", className)} {...props}>
111
+ <h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
112
+ Parameters
113
+ </h4>
114
+ <div className="rounded-md bg-muted/50">
115
+ <CodeBlock code={JSON.stringify(input, null, 2)} language="json" />
116
+ </div>
117
+ </div>
118
118
  );
119
119
 
120
120
  export type ToolOutputProps = ComponentProps<"div"> & {
121
- output: ToolUIPart["output"];
122
- errorText: ToolUIPart["errorText"];
121
+ output: ToolUIPart["output"];
122
+ errorText: ToolUIPart["errorText"];
123
123
  };
124
124
 
125
125
  export const ToolOutput = ({
126
- className,
127
- output,
128
- errorText,
129
- ...props
126
+ className,
127
+ output,
128
+ errorText,
129
+ ...props
130
130
  }: ToolOutputProps) => {
131
- if (!(output || errorText)) {
132
- return null;
133
- }
134
-
135
- let Output = <div>{output as ReactNode}</div>;
136
-
137
- if (typeof output === "object" && !isValidElement(output)) {
138
- Output = (
139
- <CodeBlock code={JSON.stringify(output, null, 2)} language="json" />
140
- );
141
- } else if (typeof output === "string") {
142
- Output = <CodeBlock code={output} language="json" />;
143
- }
144
-
145
- return (
146
- <div className={cn("space-y-2 p-4", className)} {...props}>
147
- <h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
148
- {errorText ? "Error" : "Result"}
149
- </h4>
150
- <div
151
- className={cn(
152
- "overflow-x-auto rounded-md text-xs [&_table]:w-full",
153
- errorText
154
- ? "bg-destructive/10 text-destructive"
155
- : "bg-muted/50 text-foreground"
156
- )}
157
- >
158
- {errorText && <div>{errorText}</div>}
159
- {Output}
160
- </div>
161
- </div>
162
- );
131
+ if (!(output || errorText)) {
132
+ return null;
133
+ }
134
+
135
+ let Output = <div>{output as ReactNode}</div>;
136
+
137
+ if (typeof output === "object" && !isValidElement(output)) {
138
+ Output = (
139
+ <CodeBlock code={JSON.stringify(output, null, 2)} language="json" />
140
+ );
141
+ } else if (typeof output === "string") {
142
+ Output = <CodeBlock code={output} language="json" />;
143
+ }
144
+
145
+ return (
146
+ <div className={cn("space-y-2 p-4", className)} {...props}>
147
+ <h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">
148
+ {errorText ? "Error" : "Result"}
149
+ </h4>
150
+ <div
151
+ className={cn(
152
+ "overflow-x-auto rounded-md text-xs [&_table]:w-full",
153
+ errorText
154
+ ? "bg-destructive/10 text-destructive"
155
+ : "bg-muted/50 text-foreground",
156
+ )}
157
+ >
158
+ {errorText && <div>{errorText}</div>}
159
+ {Output}
160
+ </div>
161
+ </div>
162
+ );
163
163
  };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { LogIn } from "lucide-react";
4
- import Link from "next/link";
4
+ import { InternalLink } from "@/components/internal-link";
5
5
  import { SidebarHistory } from "@/components/sidebar-history";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import {
@@ -39,10 +39,10 @@ export function AppSidebarHistoryConditional() {
39
39
  </EmptyHeader>
40
40
  <EmptyContent>
41
41
  <Button asChild size="sm" variant="outline">
42
- <Link href="/login">
42
+ <InternalLink href="/login">
43
43
  <LogIn />
44
44
  Sign In
45
- </Link>
45
+ </InternalLink>
46
46
  </Button>
47
47
  </EmptyContent>
48
48
  </Empty>
@@ -1,5 +1,5 @@
1
1
  import { Cpu } from "lucide-react";
2
- import Link from "next/link";
2
+ import { InternalLink } from "@/components/internal-link";
3
3
  import { NewChatButton } from "@/components/new-chat-button";
4
4
  import { SearchChatsButton } from "@/components/search-chats";
5
5
  import { SidebarTopRow } from "@/components/sidebar-top-row";
@@ -36,12 +36,12 @@ export function AppSidebar() {
36
36
  </SidebarMenuItem>
37
37
  <SidebarMenuItem>
38
38
  <SidebarMenuButton asChild tooltip="Models">
39
- <Link href="/settings/models">
39
+ <InternalLink href="/settings/models">
40
40
  <Cpu className="size-4" />
41
41
  <span className="group-data-[collapsible=icon]:hidden">
42
42
  Models
43
43
  </span>
44
- </Link>
44
+ </InternalLink>
45
45
  </SidebarMenuButton>
46
46
  </SidebarMenuItem>
47
47
  </SidebarMenu>
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { Globe, Plug, Settings } from "lucide-react";
5
- import Link from "next/link";
6
5
  import { memo } from "react";
7
6
  import { toast } from "sonner";
7
+ import { InternalLink } from "@/components/internal-link";
8
8
  import { config } from "@/lib/config";
9
9
  import { useSession } from "@/providers/session-provider";
10
10
  import { useTRPC } from "@/trpc/react";
@@ -122,10 +122,13 @@ function PureConnectorsDropdown() {
122
122
  })}
123
123
  <DropdownMenuSeparator />
124
124
  <DropdownMenuItem asChild>
125
- <Link className="flex items-center gap-2" href="/settings/connectors">
125
+ <InternalLink
126
+ className="flex items-center gap-2"
127
+ href="/settings/connectors"
128
+ >
126
129
  <Settings className="size-4" />
127
130
  <span>Manage Connectors</span>
128
- </Link>
131
+ </InternalLink>
129
132
  </DropdownMenuItem>
130
133
  </DropdownMenuContent>
131
134
  </DropdownMenu>
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from "react";
2
- import type { ResearchUpdate } from "@/lib/ai/tools/research-updates-schema";
2
+ import type { ResearchUpdate } from "@/tools/platform/research-updates-schema";
3
3
  import { ResearchProgress } from "./research-progress";
4
4
 
5
5
  interface ReasonSearchResearchProgressProps {
@@ -4,6 +4,7 @@ import { type KeyboardEvent, memo, useEffect, useState } from "react";
4
4
  import { toast } from "sonner";
5
5
  import { ChatMenuItems } from "@/components/chat-menu-items";
6
6
  import { DeleteChatDialog } from "@/components/delete-chat-dialog";
7
+ import { InternalLink } from "@/components/internal-link";
7
8
  import { ProjectIcon } from "@/components/project-icon";
8
9
  import {
9
10
  Breadcrumb,
@@ -349,17 +350,19 @@ function ProjectBreadcrumb({
349
350
  return (
350
351
  <>
351
352
  <BreadcrumbItem>
352
- <BreadcrumbLink
353
- aria-label={projectLabel}
354
- className="flex items-center"
355
- href={`/project/${projectId}`}
356
- title={projectLabel}
357
- >
358
- {projectIcon && projectColor ? (
359
- <ProjectIcon color={projectColor} icon={projectIcon} size={16} />
360
- ) : (
361
- projectLabel
362
- )}
353
+ <BreadcrumbLink asChild>
354
+ <InternalLink
355
+ aria-label={projectLabel}
356
+ className="flex items-center"
357
+ href={`/project/${projectId}`}
358
+ title={projectLabel}
359
+ >
360
+ {projectIcon && projectColor ? (
361
+ <ProjectIcon color={projectColor} icon={projectIcon} size={16} />
362
+ ) : (
363
+ projectLabel
364
+ )}
365
+ </InternalLink>
363
366
  </BreadcrumbLink>
364
367
  </BreadcrumbItem>
365
368
  <BreadcrumbSeparator />
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ import type { Route } from "next";
4
+ import Link from "next/link";
5
+ import { useRouter } from "next/navigation";
6
+ import type * as React from "react";
7
+ import { isElectronRenderer } from "@/lib/electron-auth";
8
+
9
+ type InternalLinkProps = Omit<React.ComponentProps<typeof Link>, "href"> & {
10
+ href: React.ComponentProps<typeof Link>["href"] | string;
11
+ onNavigate?: () => void;
12
+ };
13
+
14
+ export function InternalLink({
15
+ onAuxClick,
16
+ onClick,
17
+ onNavigate,
18
+ href,
19
+ ...props
20
+ }: InternalLinkProps) {
21
+ const router = useRouter();
22
+ const isElectron = isElectronRenderer();
23
+
24
+ const navigate = (event: React.MouseEvent<HTMLAnchorElement>) => {
25
+ onNavigate?.();
26
+ const targetHref = event.currentTarget.getAttribute("href");
27
+ if (targetHref) {
28
+ router.push(targetHref as Route);
29
+ }
30
+ };
31
+
32
+ const handleClick: React.MouseEventHandler<HTMLAnchorElement> = (event) => {
33
+ onClick?.(event);
34
+
35
+ if (event.defaultPrevented || event.button !== 0) {
36
+ return;
37
+ }
38
+
39
+ if (isElectron && (event.metaKey || event.ctrlKey || event.shiftKey)) {
40
+ event.preventDefault();
41
+ navigate(event);
42
+ return;
43
+ }
44
+
45
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
46
+ return;
47
+ }
48
+
49
+ onNavigate?.();
50
+ };
51
+
52
+ const handleAuxClick: React.MouseEventHandler<HTMLAnchorElement> = (
53
+ event
54
+ ) => {
55
+ onAuxClick?.(event);
56
+
57
+ if (event.defaultPrevented || event.button !== 1 || !isElectron) {
58
+ return;
59
+ }
60
+
61
+ event.preventDefault();
62
+ navigate(event);
63
+ };
64
+
65
+ return (
66
+ <Link
67
+ {...props}
68
+ href={href as Route}
69
+ onAuxClick={handleAuxClick}
70
+ onClick={handleClick}
71
+ />
72
+ );
73
+ }