@chat-js/cli 0.1.3 → 0.2.0

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 (158) hide show
  1. package/dist/index.js +400 -246
  2. package/package.json +1 -1
  3. package/templates/chat-app/.claude/skiller.toml +18 -0
  4. package/templates/chat-app/.claude/skills/chat-context/SKILL.md +6 -0
  5. package/templates/chat-app/.claude/skills/chat-context/chat-context.mdc +36 -0
  6. package/templates/chat-app/.claude/skills/lazy-prefetch-pattern/lazy-prefetch-pattern.mdc +27 -0
  7. package/templates/chat-app/.claude/skills/react/react.mdc +29 -0
  8. package/templates/chat-app/.claude/skills/trpc-patterns/trpc-patterns.mdc +77 -0
  9. package/templates/chat-app/.claude/skills/typescript/typescript.mdc +53 -0
  10. package/templates/chat-app/.claude/skills/ultracite/ultracite.mdc +129 -0
  11. package/templates/chat-app/.cursor/skills/chat-context/SKILL.md +37 -0
  12. package/templates/chat-app/.cursor/skills/lazy-prefetch-pattern/SKILL.md +26 -0
  13. package/templates/chat-app/.cursor/skills/react/SKILL.md +28 -0
  14. package/templates/chat-app/.cursor/skills/trpc-patterns/SKILL.md +76 -0
  15. package/templates/chat-app/.cursor/skills/typescript/SKILL.md +52 -0
  16. package/templates/chat-app/.cursor/skills/ultracite/SKILL.md +128 -0
  17. package/templates/chat-app/app/(chat)/actions.ts +1 -1
  18. package/templates/chat-app/app/(chat)/api/chat/[id]/stream/route.ts +6 -5
  19. package/templates/chat-app/app/(chat)/api/chat/route.ts +14 -15
  20. package/templates/chat-app/app/(chat)/chat-providers.tsx +2 -2
  21. package/templates/chat-app/app/(chat)/layout.tsx +7 -6
  22. package/templates/chat-app/app/api/cron/cleanup/route.ts +4 -3
  23. package/templates/chat-app/app/globals.css +22 -22
  24. package/templates/chat-app/app/layout.tsx +1 -1
  25. package/templates/chat-app/biome.jsonc +3 -3
  26. package/templates/chat-app/chat.config.ts +47 -20
  27. package/templates/chat-app/components/anonymous-session-init.tsx +4 -12
  28. package/templates/chat-app/components/artifact-actions.tsx +5 -5
  29. package/templates/chat-app/components/artifact-panel.tsx +6 -6
  30. package/templates/chat-app/components/assistant-message.tsx +1 -1
  31. package/templates/chat-app/components/chat/chat-layout.tsx +2 -2
  32. package/templates/chat-app/components/chat/chat-welcome.tsx +1 -0
  33. package/templates/chat-app/components/chat-features-definitions.ts +11 -8
  34. package/templates/chat-app/components/chat-menu-items.tsx +4 -4
  35. package/templates/chat-app/components/chat-sync.tsx +1 -2
  36. package/templates/chat-app/components/clone-chat-button.tsx +2 -2
  37. package/templates/chat-app/components/code-editor.tsx +5 -5
  38. package/templates/chat-app/components/connectors-dropdown.tsx +2 -2
  39. package/templates/chat-app/components/console.tsx +5 -5
  40. package/templates/chat-app/components/create-artifact.tsx +28 -28
  41. package/templates/chat-app/components/data-stream-provider.tsx +2 -2
  42. package/templates/chat-app/components/deep-research-progress.tsx +2 -2
  43. package/templates/chat-app/components/delete-chat-dialog.tsx +3 -3
  44. package/templates/chat-app/components/delete-project-dialog.tsx +3 -3
  45. package/templates/chat-app/components/diffview.tsx +3 -3
  46. package/templates/chat-app/components/favicon-group.tsx +7 -7
  47. package/templates/chat-app/components/header-breadcrumb.tsx +11 -11
  48. package/templates/chat-app/components/image-editor.tsx +5 -5
  49. package/templates/chat-app/components/image-modal.tsx +4 -4
  50. package/templates/chat-app/components/interactive-chart-impl.tsx +269 -0
  51. package/templates/chat-app/components/interactive-charts.tsx +18 -246
  52. package/templates/chat-app/components/lexical-chat-input.tsx +10 -10
  53. package/templates/chat-app/components/message-editor.tsx +3 -3
  54. package/templates/chat-app/components/message-parts.tsx +8 -3
  55. package/templates/chat-app/components/messages-pane.tsx +4 -4
  56. package/templates/chat-app/components/messages.tsx +5 -5
  57. package/templates/chat-app/components/model-selector.tsx +4 -1
  58. package/templates/chat-app/components/multimodal-input.tsx +14 -5
  59. package/templates/chat-app/components/part/code-execution.tsx +4 -1
  60. package/templates/chat-app/components/part/document-common.tsx +8 -8
  61. package/templates/chat-app/components/part/document-preview.tsx +34 -16
  62. package/templates/chat-app/components/part/document-tool.tsx +3 -3
  63. package/templates/chat-app/components/part/dynamic-tool.tsx +3 -3
  64. package/templates/chat-app/components/part/generate-video.tsx +54 -0
  65. package/templates/chat-app/components/part/message-reasoning.tsx +3 -3
  66. package/templates/chat-app/components/project-details-dialog.tsx +4 -4
  67. package/templates/chat-app/components/project-home.tsx +1 -0
  68. package/templates/chat-app/components/project-icon-picker.tsx +5 -5
  69. package/templates/chat-app/components/project-icon.tsx +4 -4
  70. package/templates/chat-app/components/project-menu-items.tsx +3 -3
  71. package/templates/chat-app/components/research-tasks.tsx +3 -3
  72. package/templates/chat-app/components/sandbox.tsx +4 -4
  73. package/templates/chat-app/components/search-chats-dialog.tsx +11 -11
  74. package/templates/chat-app/components/settings/connectors-settings.tsx +1 -1
  75. package/templates/chat-app/components/settings/settings-nav.tsx +1 -1
  76. package/templates/chat-app/components/sheet-editor.tsx +5 -5
  77. package/templates/chat-app/components/sidebar-chats-list.tsx +5 -5
  78. package/templates/chat-app/components/suggested-actions.tsx +3 -3
  79. package/templates/chat-app/components/text-editor.tsx +5 -5
  80. package/templates/chat-app/components/toolbar.tsx +6 -6
  81. package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +5 -5
  82. package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +4 -4
  83. package/templates/chat-app/components/upgrade-cta/share-menu-item.tsx +3 -3
  84. package/templates/chat-app/components/user-message.tsx +3 -3
  85. package/templates/chat-app/components/version-footer.tsx +4 -4
  86. package/templates/chat-app/hooks/chat-sync-hooks.ts +0 -55
  87. package/templates/chat-app/hooks/use-artifact.tsx +3 -3
  88. package/templates/chat-app/hooks/use-auto-focus.ts +37 -7
  89. package/templates/chat-app/hooks/use-media-query.tsx +2 -4
  90. package/templates/chat-app/lib/ai/active-gateway.ts +1 -1
  91. package/templates/chat-app/lib/ai/ai-gateway-models-schemas.ts +30 -6
  92. package/templates/chat-app/lib/ai/app-model-id.ts +1 -1
  93. package/templates/chat-app/lib/ai/app-models.ts +4 -4
  94. package/templates/chat-app/lib/ai/eval-agent.ts +5 -5
  95. package/templates/chat-app/lib/ai/followup-suggestions.ts +1 -1
  96. package/templates/chat-app/lib/ai/gateway-model-defaults.ts +131 -41
  97. package/templates/chat-app/lib/ai/gateways/gateway-provider.ts +10 -6
  98. package/templates/chat-app/lib/ai/gateways/openai-compatible-gateway.ts +9 -4
  99. package/templates/chat-app/lib/ai/gateways/openai-gateway.ts +9 -4
  100. package/templates/chat-app/lib/ai/gateways/openrouter-gateway.ts +17 -12
  101. package/templates/chat-app/lib/ai/gateways/registry.ts +9 -0
  102. package/templates/chat-app/lib/ai/gateways/vercel-gateway.ts +36 -4
  103. package/templates/chat-app/lib/ai/mcp/cache.ts +13 -13
  104. package/templates/chat-app/lib/ai/model-data.ts +21 -20
  105. package/templates/chat-app/lib/ai/models.generated.ts +4397 -3592
  106. package/templates/chat-app/lib/ai/models.ts +1 -1
  107. package/templates/chat-app/lib/ai/providers.ts +10 -0
  108. package/templates/chat-app/lib/ai/text-splitter.ts +3 -4
  109. package/templates/chat-app/lib/ai/to-model-data.ts +1 -0
  110. package/templates/chat-app/lib/ai/tools/code-execution.ts +122 -53
  111. package/templates/chat-app/lib/ai/tools/deep-research/configuration.ts +35 -32
  112. package/templates/chat-app/lib/ai/tools/deep-research/pipeline.ts +2 -2
  113. package/templates/chat-app/lib/ai/tools/deep-research/types.ts +9 -9
  114. package/templates/chat-app/lib/ai/tools/documents/types.ts +4 -4
  115. package/templates/chat-app/lib/ai/tools/generate-image.ts +42 -20
  116. package/templates/chat-app/lib/ai/tools/generate-video.ts +166 -0
  117. package/templates/chat-app/lib/ai/tools/get-weather.ts +20 -20
  118. package/templates/chat-app/lib/ai/tools/read-document.ts +3 -3
  119. package/templates/chat-app/lib/ai/tools/steps/multi-query-web-search.ts +11 -11
  120. package/templates/chat-app/lib/ai/tools/steps/web-search.ts +6 -6
  121. package/templates/chat-app/lib/ai/tools/tools-definitions.ts +10 -5
  122. package/templates/chat-app/lib/ai/tools/tools.ts +15 -6
  123. package/templates/chat-app/lib/ai/tools/types.ts +2 -2
  124. package/templates/chat-app/lib/ai/types.ts +22 -13
  125. package/templates/chat-app/lib/artifacts/code/client.tsx +5 -5
  126. package/templates/chat-app/lib/artifacts/sheet/client.tsx +2 -2
  127. package/templates/chat-app/lib/artifacts/text/client.tsx +18 -3
  128. package/templates/chat-app/lib/clone-messages.test.ts +6 -1
  129. package/templates/chat-app/lib/config-requirements.ts +19 -10
  130. package/templates/chat-app/lib/config-schema.ts +189 -103
  131. package/templates/chat-app/lib/config.ts +4 -4
  132. package/templates/chat-app/lib/credits/cost-accumulator.ts +11 -8
  133. package/templates/chat-app/lib/env-schema.ts +1 -1
  134. package/templates/chat-app/lib/features-config.ts +6 -6
  135. package/templates/chat-app/lib/stores/with-threads.ts +3 -3
  136. package/templates/chat-app/lib/thread-utils.ts +2 -2
  137. package/templates/chat-app/lib/types/anonymous.ts +4 -4
  138. package/templates/chat-app/lib/types/ui-chat.ts +7 -7
  139. package/templates/chat-app/lib/utils/download-assets.ts +3 -3
  140. package/templates/chat-app/lib/utils/rate-limit.ts +8 -8
  141. package/templates/chat-app/next.config.ts +0 -25
  142. package/templates/chat-app/package.json +15 -15
  143. package/templates/chat-app/playwright.config.ts +5 -5
  144. package/templates/chat-app/providers/chat-id-provider.tsx +5 -5
  145. package/templates/chat-app/providers/chat-input-provider.tsx +15 -15
  146. package/templates/chat-app/providers/chat-models-provider.tsx +3 -3
  147. package/templates/chat-app/providers/default-model-provider.tsx +5 -5
  148. package/templates/chat-app/providers/parse-chat-id-from-pathname.test.ts +16 -0
  149. package/templates/chat-app/providers/session-provider.tsx +2 -2
  150. package/templates/chat-app/scripts/check-env.ts +36 -4
  151. package/templates/chat-app/tests/artifacts.e2e.ts +7 -0
  152. package/templates/chat-app/tests/auth.setup.e2e.ts +10 -0
  153. package/templates/chat-app/tests/chat.e2e.ts +7 -0
  154. package/templates/chat-app/tests/reasoning.e2e.ts +7 -0
  155. package/templates/chat-app/tests/reasoning.setup.e2e.ts +10 -0
  156. package/templates/chat-app/trpc/routers/chat.router.ts +1 -1
  157. package/templates/chat-app/trpc/routers/mcp.router.ts +3 -3
  158. package/templates/chat-app/vitest.config.ts +7 -0
@@ -1,248 +1,20 @@
1
- import ReactECharts from "echarts-for-react/lib/index";
2
- import type { EChartsOption } from "echarts-for-react/lib/types";
3
- import { motion } from "motion/react";
4
- import { useTheme } from "next-themes";
1
+ import dynamic from "next/dynamic";
5
2
  import { Card } from "@/components/ui/card";
6
3
 
7
- const CHART_COLORS = [
8
- "#22c55e", // green
9
- "#3b82f6", // blue
10
- "#f59e0b", // amber
11
- "#8b5cf6", // purple
12
- "#ec4899", // pink
13
- "#06b6d4", // cyan
14
- "#ef4444", // red
15
- "#84cc16", // lime
16
- ];
17
-
18
- export type BaseChart = {
19
- type: string;
20
- title: string;
21
- x_label?: string;
22
- y_label?: string;
23
- elements: any[];
24
- x_scale?: string;
25
- };
26
-
27
- function InteractiveChart({ chart }: { chart: BaseChart }) {
28
- const { resolvedTheme } = useTheme();
29
- // TOOD: Update for multitheme support
30
- // const textColor = theme === 'dark' ? '#e5e5e5' : '#171717';
31
- const textColor = "#e5e5e5";
32
- // const gridColor =
33
- // theme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
34
- const gridColor = "rgba(255, 255, 255, 0.1)";
35
- // const tooltipBg = theme === 'dark' ? '#171717' : '#ffffff';
36
- const tooltipBg = "#171717";
37
-
38
- const sharedOptions: EChartsOption = {
39
- backgroundColor: "transparent",
40
- grid: {
41
- top: 50,
42
- right: 32,
43
- bottom: 32,
44
- left: 32,
45
- containLabel: true,
46
- },
47
- legend: {
48
- textStyle: { color: textColor },
49
- top: 8,
50
- icon: "circle",
51
- itemWidth: 8,
52
- itemHeight: 8,
53
- itemGap: 16,
54
- },
55
- tooltip: {
56
- trigger: "axis",
57
- backgroundColor: tooltipBg,
58
- borderWidth: 0,
59
- padding: [6, 10],
60
- className:
61
- "echarts-tooltip rounded-lg! border! border-neutral-200! dark:border-neutral-800!",
62
- textStyle: {
63
- color: textColor,
64
- fontSize: 13,
65
- fontFamily: "system-ui, -apple-system, sans-serif",
66
- },
67
- },
68
- };
69
-
70
- const getChartOptions = (): EChartsOption => {
71
- const defaultAxisOptions = {
72
- axisLine: { show: true, lineStyle: { color: gridColor } },
73
- axisTick: { show: false },
74
- axisLabel: {
75
- color: textColor,
76
- margin: 8,
77
- fontSize: 11,
78
- hideOverlap: true,
79
- },
80
- nameTextStyle: {
81
- color: textColor,
82
- fontSize: 13,
83
- padding: [0, 0, 0, 0],
84
- },
85
- splitLine: {
86
- show: true,
87
- lineStyle: { color: gridColor, type: "dashed" },
88
- },
89
- };
90
-
91
- if (chart.type === "line" || chart.type === "scatter") {
92
- const series = chart.elements.map((e, index) => ({
93
- name: e.label,
94
- type: chart.type,
95
- data: e.points.map((p: [number | string, number]) => {
96
- // Handle datetime x-axis
97
- const x =
98
- chart.x_scale === "datetime" ? new Date(p[0]).getTime() : p[0];
99
- return [x, p[1]];
100
- }),
101
- smooth: true,
102
- symbolSize: chart.type === "scatter" ? 10 : 0,
103
- lineStyle: {
104
- width: 2,
105
- color: CHART_COLORS[index % CHART_COLORS.length],
106
- },
107
- itemStyle: {
108
- color: CHART_COLORS[index % CHART_COLORS.length],
109
- },
110
- areaStyle:
111
- chart.type === "line"
112
- ? {
113
- color: {
114
- type: "linear",
115
- x: 0,
116
- y: 0,
117
- x2: 0,
118
- y2: 1,
119
- colorStops: [
120
- {
121
- offset: 0,
122
- color: `${CHART_COLORS[index % CHART_COLORS.length]}15`, // 15 = 10% opacity
123
- },
124
- {
125
- offset: 1,
126
- color: "rgba(23, 23, 23, 0)",
127
- // theme === 'dark'
128
- // ? 'rgba(23, 23, 23, 0)'
129
- // : 'rgba(255, 255, 255, 0)',
130
- },
131
- ],
132
- },
133
- }
134
- : undefined,
135
- }));
136
-
137
- return {
138
- ...sharedOptions,
139
- xAxis: {
140
- type: chart.x_scale === "datetime" ? "time" : "value",
141
- name: chart.x_label,
142
- nameLocation: "middle",
143
- nameGap: 40,
144
- scale: true,
145
- ...defaultAxisOptions,
146
- axisLabel: {
147
- ...defaultAxisOptions.axisLabel,
148
- formatter:
149
- chart.x_scale === "datetime"
150
- ? (value: number) => {
151
- const date = new Date(value);
152
- return date.toLocaleDateString("en-US", {
153
- month: "short",
154
- year: "numeric",
155
- });
156
- }
157
- : undefined,
158
- },
159
- },
160
- yAxis: {
161
- type: "value",
162
- name: chart.y_label,
163
- nameLocation: "middle",
164
- nameGap: 50,
165
- position: "right",
166
- scale: true,
167
- ...defaultAxisOptions,
168
- },
169
- series,
170
- };
171
- }
172
-
173
- if (chart.type === "bar") {
174
- const data = chart.elements.reduce((acc: Record<string, any[]>, item) => {
175
- const key = item.group;
176
- if (!acc[key]) {
177
- acc[key] = [];
178
- }
179
- acc[key].push(item);
180
- return acc;
181
- }, {});
182
-
183
- const series = Object.entries(data).map(([group, elements], index) => ({
184
- name: group,
185
- type: "bar",
186
- stack: "total",
187
- data: elements?.map((e) => [e.label, e.value]),
188
- itemStyle: {
189
- color: CHART_COLORS[index % CHART_COLORS.length],
190
- },
191
- emphasis: {
192
- itemStyle: {
193
- shadowBlur: 10,
194
- shadowColor: "rgba(0,0,0,0.3)",
195
- },
196
- },
197
- }));
198
-
199
- return {
200
- ...sharedOptions,
201
- xAxis: {
202
- type: "category",
203
- name: chart.x_label,
204
- nameLocation: "middle",
205
- nameGap: 40,
206
- ...defaultAxisOptions,
207
- },
208
- yAxis: {
209
- type: "value",
210
- name: chart.y_label,
211
- nameLocation: "middle",
212
- nameGap: 50,
213
- position: "right",
214
- ...defaultAxisOptions,
215
- },
216
- series,
217
- };
218
- }
219
-
220
- return sharedOptions;
221
- };
222
-
223
- return (
224
- <motion.div
225
- animate={{ opacity: 1, y: 0 }}
226
- initial={{ opacity: 0, y: 20 }}
227
- transition={{ duration: 0.5 }}
228
- >
229
- <Card className="overflow-hidden border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-900">
230
- <div className="p-6">
231
- {chart.title && (
232
- <h3 className="mb-4 font-medium text-lg text-neutral-900 dark:text-neutral-100">
233
- {chart.title}
234
- </h3>
235
- )}
236
- <ReactECharts
237
- notMerge={true}
238
- option={getChartOptions()}
239
- style={{ height: "400px", width: "100%" }}
240
- theme={resolvedTheme === "dark" ? "dark" : undefined}
241
- />
242
- </div>
243
- </Card>
244
- </motion.div>
245
- );
246
- }
247
-
248
- export default InteractiveChart;
4
+ export type { BaseChart } from "./interactive-chart-impl";
5
+
6
+ const ChartSkeleton = () => (
7
+ <Card className="overflow-hidden border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-900">
8
+ <div className="flex h-[400px] items-center justify-center p-6">
9
+ <div className="size-8 animate-pulse rounded-md bg-muted" />
10
+ </div>
11
+ </Card>
12
+ );
13
+
14
+ export default dynamic(
15
+ () => import("./interactive-chart-impl").then((m) => m.default),
16
+ {
17
+ loading: () => <ChartSkeleton />,
18
+ ssr: false,
19
+ }
20
+ );
@@ -78,24 +78,24 @@ function EditorRefPlugin({
78
78
  return null;
79
79
  }
80
80
 
81
- type LexicalChatInputRef = {
82
- focus: () => void;
81
+ interface LexicalChatInputRef {
83
82
  clear: () => void;
83
+ focus: () => void;
84
84
  getValue: () => string;
85
- };
85
+ }
86
86
 
87
- type LexicalChatInputProps = {
87
+ interface LexicalChatInputProps {
88
+ autoFocus?: boolean;
89
+ className?: string;
90
+ "data-testid"?: string;
88
91
  initialValue?: string;
92
+ maxRows?: number;
93
+ onEnterSubmit?: (event: KeyboardEvent) => boolean;
89
94
  onInputChange?: (value: string) => void;
90
95
  onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;
91
96
  onPaste?: (event: ClipboardEvent<HTMLDivElement>) => void;
92
- onEnterSubmit?: (event: KeyboardEvent) => boolean;
93
97
  placeholder?: string;
94
- autoFocus?: boolean;
95
- className?: string;
96
- maxRows?: number;
97
- "data-testid"?: string;
98
- };
98
+ }
99
99
 
100
100
  const theme = {
101
101
  root: "lexical-root",
@@ -10,12 +10,12 @@ import {
10
10
  import { ChatInputProvider } from "@/providers/chat-input-provider";
11
11
  import { MultimodalInput } from "./multimodal-input";
12
12
 
13
- export type MessageEditorProps = {
13
+ export interface MessageEditorProps {
14
14
  chatId: string;
15
15
  message: ChatMessage;
16
- setMode: Dispatch<SetStateAction<"view" | "edit">>;
17
16
  parentMessageId: string | null;
18
- };
17
+ setMode: Dispatch<SetStateAction<"view" | "edit">>;
18
+ }
19
19
 
20
20
  function MessageEditorContent({
21
21
  chatId,
@@ -19,6 +19,7 @@ import { DeepResearch } from "./part/deep-research";
19
19
  import { DocumentTool } from "./part/document-tool";
20
20
  import { DynamicToolPart } from "./part/dynamic-tool";
21
21
  import { GenerateImage } from "./part/generate-image";
22
+ import { GenerateVideo } from "./part/generate-video";
22
23
  import { ReasoningPart } from "./part/message-reasoning";
23
24
  import { ReadDocument } from "./part/read-document";
24
25
 
@@ -27,11 +28,11 @@ import { TextMessagePart } from "./part/text-message-part";
27
28
  import { Weather } from "./part/weather";
28
29
  import { WebSearch } from "./part/web-search";
29
30
 
30
- type MessagePartsProps = {
31
- messageId: string;
31
+ interface MessagePartsProps {
32
32
  isLoading: boolean;
33
33
  isReadonly: boolean;
34
- };
34
+ messageId: string;
35
+ }
35
36
 
36
37
  function ToolPart({
37
38
  part,
@@ -77,6 +78,10 @@ function ToolPart({
77
78
  return <GenerateImage tool={part} />;
78
79
  }
79
80
 
81
+ if (type === "tool-generateVideo") {
82
+ return <GenerateVideo tool={part} />;
83
+ }
84
+
80
85
  if (type === "tool-deepResearch") {
81
86
  return <DeepResearch messageId={messageId} part={part} />;
82
87
  }
@@ -8,12 +8,12 @@ import { cn } from "@/lib/utils";
8
8
  import { Messages } from "./messages";
9
9
  import { MultimodalInput } from "./multimodal-input";
10
10
 
11
- type MessagesPaneProps = {
11
+ interface MessagesPaneProps {
12
12
  chatId: string;
13
- status: UseChatHelpers<ChatMessage>["status"];
14
- isReadonly: boolean;
15
13
  className?: string;
16
- };
14
+ isReadonly: boolean;
15
+ status: UseChatHelpers<ChatMessage>["status"];
16
+ }
17
17
 
18
18
  function PureMessagesPane({
19
19
  chatId,
@@ -12,9 +12,9 @@ import { PreviewMessage } from "./message";
12
12
  import { ResponseErrorMessage } from "./response-error-message";
13
13
  import { ThinkingMessage } from "./thinking-message";
14
14
 
15
- type PureMessagesInternalProps = {
15
+ interface PureMessagesInternalProps {
16
16
  isReadonly: boolean;
17
- };
17
+ }
18
18
 
19
19
  const PureMessagesInternal = memo(
20
20
  ({ isReadonly }: PureMessagesInternalProps) => {
@@ -55,11 +55,11 @@ const PureMessagesInternal = memo(
55
55
  }
56
56
  );
57
57
 
58
- type MessagesProps = {
58
+ interface MessagesProps {
59
+ className?: string;
59
60
  isReadonly: boolean;
60
61
  onModelChange?: (modelId: string) => void;
61
- className?: string;
62
- };
62
+ }
63
63
 
64
64
  function PureMessages({ isReadonly, className }: MessagesProps) {
65
65
  return (
@@ -168,7 +168,10 @@ function PureModelSelector({
168
168
  const [featureFilters, setFeatureFilters] =
169
169
  useState<FeatureFilter>(initialFilters);
170
170
 
171
- type ModelItem = { model: AppModelDefinition; disabled: boolean };
171
+ interface ModelItem {
172
+ disabled: boolean;
173
+ model: AppModelDefinition;
174
+ }
172
175
 
173
176
  const models = useMemo<ModelItem[]>(
174
177
  () =>
@@ -83,6 +83,7 @@ function PureMultimodalInput({
83
83
  chatId,
84
84
  status,
85
85
  className,
86
+ autoFocus = false,
86
87
  isEditMode = false,
87
88
  parentMessageId,
88
89
  onSendMessage,
@@ -90,6 +91,7 @@ function PureMultimodalInput({
90
91
  chatId: string;
91
92
  status: UseChatHelpers<ChatMessage>["status"];
92
93
  className?: string;
94
+ autoFocus?: boolean;
93
95
  isEditMode?: boolean;
94
96
  parentMessageId: string | null;
95
97
  onSendMessage?: (message: ChatMessage) => void | Promise<void>;
@@ -150,7 +152,7 @@ function PureMultimodalInput({
150
152
 
151
153
  // Helper function to auto-switch to PDF-compatible model
152
154
  const switchToPdfCompatibleModel = useCallback(() => {
153
- const pdfModel = config.models.defaults.pdf;
155
+ const pdfModel = config.ai.workflows.pdf;
154
156
  const defaultPdfModelDef = getModelById(pdfModel);
155
157
  if (defaultPdfModelDef) {
156
158
  toast.success(`Switched to ${defaultPdfModelDef.name} (supports PDF)`);
@@ -161,7 +163,7 @@ function PureMultimodalInput({
161
163
 
162
164
  // Helper function to auto-switch to image-compatible model
163
165
  const switchToImageCompatibleModel = useCallback(() => {
164
- const imageModel = config.models.defaults.chatImageCompatible;
166
+ const imageModel = config.ai.workflows.chatImageCompatible;
165
167
  const defaultImageModelDef = getModelById(imageModel);
166
168
  if (defaultImageModelDef) {
167
169
  toast.success(
@@ -642,7 +644,7 @@ function PureMultimodalInput({
642
644
  />
643
645
 
644
646
  <LexicalChatInput
645
- autoFocus
647
+ autoFocus={autoFocus}
646
648
  className="max-h-[max(35svh,5rem)] min-h-[60px] overflow-y-scroll sm:min-h-[80px]"
647
649
  data-testid="multimodal-input"
648
650
  initialValue={getInitialInput()}
@@ -918,10 +920,12 @@ const ChatInputBottomControls = memo(PureChatInputBottomControls);
918
920
  export const MultimodalInput = memo(
919
921
  PureMultimodalInput,
920
922
  (prevProps, nextProps) => {
921
- // More specific equality checks to prevent unnecessary re-renders
922
923
  if (prevProps.status !== nextProps.status) {
923
924
  return false;
924
925
  }
926
+ if (prevProps.autoFocus !== nextProps.autoFocus) {
927
+ return false;
928
+ }
925
929
  if (prevProps.isEditMode !== nextProps.isEditMode) {
926
930
  return false;
927
931
  }
@@ -931,7 +935,12 @@ export const MultimodalInput = memo(
931
935
  if (prevProps.className !== nextProps.className) {
932
936
  return false;
933
937
  }
934
-
938
+ if (prevProps.parentMessageId !== nextProps.parentMessageId) {
939
+ return false;
940
+ }
941
+ if (prevProps.onSendMessage !== nextProps.onSendMessage) {
942
+ return false;
943
+ }
935
944
  return true;
936
945
  }
937
946
  );
@@ -19,7 +19,10 @@ function isBaseChart(input: unknown): input is BaseChart {
19
19
  return hasType && hasTitle && hasElements;
20
20
  }
21
21
 
22
- type PngChart = { base64: string; format: string };
22
+ interface PngChart {
23
+ base64: string;
24
+ format: string;
25
+ }
23
26
 
24
27
  function isPngChart(input: unknown): input is PngChart {
25
28
  if (typeof input !== "object" || input === null) {
@@ -48,16 +48,16 @@ const getActionText = (
48
48
  }
49
49
  };
50
50
 
51
- type DocumentToolResultProps = {
52
- type: "create" | "update";
51
+ interface DocumentToolResultProps {
52
+ isReadonly: boolean;
53
+ messageId: string;
53
54
  result: {
54
55
  id: string;
55
56
  title: string;
56
57
  kind: ArtifactKind;
57
58
  };
58
- isReadonly: boolean;
59
- messageId: string;
60
- };
59
+ type: "create" | "update";
60
+ }
61
61
 
62
62
  function PureDocumentToolResult({
63
63
  type,
@@ -103,11 +103,11 @@ function PureDocumentToolResult({
103
103
 
104
104
  export const DocumentToolResult = memo(PureDocumentToolResult, () => true);
105
105
 
106
- type DocumentToolCallProps = {
107
- type: "create" | "update";
106
+ interface DocumentToolCallProps {
108
107
  args: { title?: string };
109
108
  isReadonly: boolean;
110
- };
109
+ type: "create" | "update";
110
+ }
111
111
 
112
112
  function PureDocumentToolCall({
113
113
  type,
@@ -2,6 +2,7 @@
2
2
 
3
3
  import equal from "fast-deep-equal";
4
4
  import { File, Loader2, Maximize, Pencil } from "lucide-react";
5
+ import dynamic from "next/dynamic";
5
6
  import { type MouseEvent, memo, useCallback, useMemo, useRef } from "react";
6
7
  import { useDocuments } from "@/hooks/chat-sync-hooks";
7
8
  import { useArtifact } from "@/hooks/use-artifact";
@@ -9,33 +10,50 @@ import type { ArtifactKind } from "@/lib/artifacts/artifact-kind";
9
10
  import type { Document } from "@/lib/db/schema";
10
11
  import { cn } from "@/lib/utils";
11
12
  import type { UIArtifact } from "../artifact-panel";
12
- import { CodeEditor } from "../code-editor";
13
13
  import { InlineDocumentSkeleton } from "../document-skeleton";
14
- import { ImageEditor } from "../image-editor";
15
- import { SpreadsheetEditor } from "../sheet-editor";
16
- import { Editor } from "../text-editor";
17
14
  import { DocumentToolCall, DocumentToolResult } from "./document-common";
18
15
 
19
- type DocumentPreviewInput = {
20
- title: string;
21
- kind: ArtifactKind;
16
+ const CodeEditor = dynamic(
17
+ () => import("../code-editor").then((m) => ({ default: m.CodeEditor })),
18
+ { loading: () => <InlineDocumentSkeleton />, ssr: false }
19
+ );
20
+
21
+ const Editor = dynamic(
22
+ () => import("../text-editor").then((m) => ({ default: m.Editor })),
23
+ { loading: () => <InlineDocumentSkeleton />, ssr: false }
24
+ );
25
+
26
+ const ImageEditor = dynamic(
27
+ () => import("../image-editor").then((m) => ({ default: m.ImageEditor })),
28
+ { loading: () => <InlineDocumentSkeleton />, ssr: false }
29
+ );
30
+
31
+ const SpreadsheetEditor = dynamic(
32
+ () =>
33
+ import("../sheet-editor").then((m) => ({ default: m.SpreadsheetEditor })),
34
+ { loading: () => <InlineDocumentSkeleton />, ssr: false }
35
+ );
36
+
37
+ interface DocumentPreviewInput {
22
38
  content: string;
23
- };
39
+ kind: ArtifactKind;
40
+ title: string;
41
+ }
24
42
 
25
- type DocumentPreviewOutput = {
43
+ interface DocumentPreviewOutput {
26
44
  documentId: string;
27
- title: string;
28
45
  kind: ArtifactKind;
29
- };
46
+ title: string;
47
+ }
30
48
 
31
- type DocumentPreviewProps = {
32
- isReadonly: boolean;
33
- output?: DocumentPreviewOutput;
49
+ interface DocumentPreviewProps {
34
50
  input?: DocumentPreviewInput;
51
+ isLastArtifact?: boolean;
52
+ isReadonly: boolean;
35
53
  messageId: string;
54
+ output?: DocumentPreviewOutput;
36
55
  type?: "create" | "update";
37
- isLastArtifact?: boolean;
38
- };
56
+ }
39
57
 
40
58
  export function DocumentPreview({
41
59
  isReadonly,
@@ -16,11 +16,11 @@ type DocumentTool = Extract<
16
16
  { type: DocumentToolType }
17
17
  >;
18
18
 
19
- type DocumentToolComponentProps = {
20
- tool: DocumentTool;
19
+ interface DocumentToolComponentProps {
21
20
  isReadonly: boolean;
22
21
  messageId: string;
23
- };
22
+ tool: DocumentTool;
23
+ }
24
24
 
25
25
  function PureDocumentTool({
26
26
  tool,
@@ -16,11 +16,11 @@ import { parseToolId } from "@/lib/ai/mcp-name-id";
16
16
  import { useTRPC } from "@/trpc/react";
17
17
  import { getGoogleFaviconUrl } from "../get-google-favicon-url";
18
18
 
19
- type DynamicToolPartProps = {
20
- messageId: string;
19
+ interface DynamicToolPartProps {
21
20
  isReadonly: boolean;
21
+ messageId: string;
22
22
  part: DynamicToolUIPart;
23
- };
23
+ }
24
24
 
25
25
  export function DynamicToolPart({ part }: DynamicToolPartProps) {
26
26
  const trpc = useTRPC();