@chat-js/cli 0.1.4 → 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 (160) hide show
  1. package/dist/index.js +391 -244
  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 -1
  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
  159. package/templates/chat-app/next-env.d.ts +0 -6
  160. package/templates/chat-app/tsconfig.tsbuildinfo +0 -1
@@ -62,7 +62,7 @@ export function ChatSync({
62
62
  resume: isLastMessagePartial,
63
63
  transport: new DefaultChatTransport({
64
64
  api: "/api/chat",
65
- fetch: fetchWithErrorHandlers,
65
+ fetch: fetchWithErrorHandlers as typeof fetch,
66
66
  prepareSendMessagesRequest({ messages, id: requestId, body }) {
67
67
  setAutoResume(true);
68
68
 
@@ -6,10 +6,10 @@ import { toast } from "sonner";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import { useCloneChat } from "@/hooks/chat-sync-hooks";
8
8
 
9
- type CloneChatButtonProps = {
9
+ interface CloneChatButtonProps {
10
10
  chatId: string;
11
11
  className?: string;
12
- };
12
+ }
13
13
 
14
14
  export function CloneChatButton({ chatId, className }: CloneChatButtonProps) {
15
15
  const router = useRouter();
@@ -8,15 +8,15 @@ import { EditorView } from "@codemirror/view";
8
8
  import { basicSetup } from "codemirror";
9
9
  import { memo, useEffect, useRef } from "react";
10
10
 
11
- type EditorProps = {
11
+ interface EditorProps {
12
12
  content: string;
13
- onSaveContent: (updatedContent: string, debounce: boolean) => void;
14
- status: "streaming" | "idle";
15
- isCurrentVersion: boolean;
16
13
  currentVersionIndex: number;
14
+ isCurrentVersion: boolean;
17
15
  isReadonly?: boolean;
18
16
  language?: string;
19
- };
17
+ onSaveContent: (updatedContent: string, debounce: boolean) => void;
18
+ status: "streaming" | "idle";
19
+ }
20
20
 
21
21
  function getLanguageExtension(language: string) {
22
22
  switch (language) {
@@ -30,7 +30,7 @@ function PureConnectorsDropdown() {
30
30
 
31
31
  const { data: connectors } = useQuery({
32
32
  ...trpc.mcp.listConnected.queryOptions(),
33
- enabled: config.features.mcp && isAuthenticated,
33
+ enabled: config.ai.tools.mcp.enabled && isAuthenticated,
34
34
  });
35
35
 
36
36
  const queryKey = trpc.mcp.listConnected.queryKey();
@@ -59,7 +59,7 @@ function PureConnectorsDropdown() {
59
59
  },
60
60
  })
61
61
  );
62
- if (!config.features.mcp) {
62
+ if (!config.ai.tools.mcp.enabled) {
63
63
  return null;
64
64
  }
65
65
  if (!connectors || connectors.length === 0) {
@@ -4,16 +4,16 @@ import { useArtifactSelector } from "@/hooks/use-artifact";
4
4
  import { cn } from "@/lib/utils";
5
5
  import { Button } from "./ui/button";
6
6
 
7
- export type ConsoleOutputContent = {
7
+ export interface ConsoleOutputContent {
8
8
  type: "text" | "image";
9
9
  value: string;
10
- };
10
+ }
11
11
 
12
- export type ConsoleOutput = {
12
+ export interface ConsoleOutput {
13
+ contents: ConsoleOutputContent[];
13
14
  id: string;
14
15
  status: "in_progress" | "loading_packages" | "completed" | "failed";
15
- contents: ConsoleOutputContent[];
16
- };
16
+ }
17
17
 
18
18
  function getConsoleStatusText(consoleOutput: ConsoleOutput): string | null {
19
19
  if (consoleOutput.status === "in_progress") {
@@ -7,59 +7,57 @@ import type { ChatMessage, CustomUIDataTypes } from "@/lib/ai/types";
7
7
  import type { useTRPC } from "@/trpc/react";
8
8
  import type { UIArtifact } from "./artifact-panel";
9
9
 
10
- export type ArtifactActionContext<M = any> = {
10
+ export interface ArtifactActionContext<M = any> {
11
11
  content: string;
12
- handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
13
12
  currentVersionIndex: number;
13
+ handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
14
14
  isCurrentVersion: boolean;
15
- mode: "edit" | "diff";
15
+ isReadonly?: boolean;
16
16
  metadata: M;
17
+ mode: "edit" | "diff";
17
18
  setMetadata: Dispatch<SetStateAction<M>>;
18
- isReadonly?: boolean;
19
- };
19
+ }
20
20
 
21
- type ArtifactAction<M = any> = {
21
+ interface ArtifactAction<M = any> {
22
+ description: string;
22
23
  icon: ReactNode;
24
+ isDisabled?: (context: ArtifactActionContext<M>) => boolean;
23
25
  label?: string;
24
- description: string;
25
26
  onClick: (context: ArtifactActionContext<M>) => Promise<void> | void;
26
- isDisabled?: (context: ArtifactActionContext<M>) => boolean;
27
- };
27
+ }
28
28
 
29
- export type ArtifactToolbarContext = {
29
+ export interface ArtifactToolbarContext {
30
30
  sendMessage: UseChatHelpers<ChatMessage>["sendMessage"];
31
31
  storeApi: ReturnType<typeof useChatStoreApi<ChatMessage>>;
32
- };
32
+ }
33
33
 
34
- export type ArtifactToolbarItem = {
34
+ export interface ArtifactToolbarItem {
35
35
  description: string;
36
36
  icon: ReactNode;
37
37
  onClick: (context: ArtifactToolbarContext) => void;
38
- };
38
+ }
39
39
 
40
- type ArtifactContent<M = any> = {
41
- title: string;
40
+ interface ArtifactContent<M = any> {
42
41
  content: string;
43
- mode: "edit" | "diff";
44
- isCurrentVersion: boolean;
45
42
  currentVersionIndex: number;
46
- status: "streaming" | "idle";
47
- onSaveContent: (updatedContent: string, debounce: boolean) => void;
48
- isInline: boolean;
49
43
  getDocumentContentById: (index: number) => string;
44
+ isCurrentVersion: boolean;
45
+ isInline: boolean;
50
46
  isLoading: boolean;
47
+ isReadonly?: boolean;
51
48
  metadata: M;
49
+ mode: "edit" | "diff";
50
+ onSaveContent: (updatedContent: string, debounce: boolean) => void;
52
51
  setMetadata: Dispatch<SetStateAction<M>>;
53
- isReadonly?: boolean;
54
- };
52
+ status: "streaming" | "idle";
53
+ title: string;
54
+ }
55
55
 
56
- type ArtifactConfig<T extends string, M = any> = {
57
- kind: T;
58
- description: string;
56
+ interface ArtifactConfig<T extends string, M = any> {
57
+ actions: ArtifactAction<M>[];
59
58
  content: ComponentType<ArtifactContent<M>>;
59
+ description: string;
60
60
  footer?: ComponentType<ArtifactContent<M>>;
61
- actions: ArtifactAction<M>[];
62
- toolbar: ArtifactToolbarItem[];
63
61
  initialize?: ({
64
62
  documentId,
65
63
  setMetadata,
@@ -73,12 +71,14 @@ type ArtifactConfig<T extends string, M = any> = {
73
71
  queryClient: QueryClient;
74
72
  isAuthenticated: boolean;
75
73
  }) => void;
74
+ kind: T;
76
75
  onStreamPart?: (args: {
77
76
  setMetadata: Dispatch<SetStateAction<M>>;
78
77
  setArtifact: Dispatch<SetStateAction<UIArtifact>>;
79
78
  streamPart: DataUIPart<CustomUIDataTypes>;
80
79
  }) => void;
81
- };
80
+ toolbar: ArtifactToolbarItem[];
81
+ }
82
82
 
83
83
  export class Artifact<T extends string, M = any> {
84
84
  readonly kind: T;
@@ -5,12 +5,12 @@ import type React from "react";
5
5
  import { createContext, useContext, useMemo, useState } from "react";
6
6
  import type { CustomUIDataTypes } from "@/lib/ai/types";
7
7
 
8
- type DataStreamContextValue = {
8
+ interface DataStreamContextValue {
9
9
  dataStream: DataUIPart<CustomUIDataTypes>[];
10
10
  setDataStream: React.Dispatch<
11
11
  React.SetStateAction<DataUIPart<CustomUIDataTypes>[]>
12
12
  >;
13
- };
13
+ }
14
14
 
15
15
  const DataStreamContext = createContext<DataStreamContextValue | null>(null);
16
16
 
@@ -2,9 +2,9 @@ import { useMemo } from "react";
2
2
  import type { ResearchUpdate } from "@/lib/ai/tools/research-updates-schema";
3
3
  import { ResearchProgress } from "./research-progress";
4
4
 
5
- type ReasonSearchResearchProgressProps = {
5
+ interface ReasonSearchResearchProgressProps {
6
6
  updates: ResearchUpdate[];
7
- };
7
+ }
8
8
 
9
9
  export const ReasonSearchResearchProgress = ({
10
10
  updates,
@@ -16,11 +16,11 @@ import {
16
16
  import { useDeleteChat } from "@/hooks/chat-sync-hooks";
17
17
  import { useChatId } from "@/providers/chat-id-provider";
18
18
 
19
- type DeleteChatDialogProps = {
19
+ interface DeleteChatDialogProps {
20
20
  deleteId: string | null;
21
- showDeleteDialog: boolean;
22
21
  setShowDeleteDialog: (show: boolean) => void;
23
- };
22
+ showDeleteDialog: boolean;
23
+ }
24
24
 
25
25
  export function DeleteChatDialog({
26
26
  deleteId,
@@ -17,11 +17,11 @@ import {
17
17
  } from "@/components/ui/alert-dialog";
18
18
  import { useTRPC } from "@/trpc/react";
19
19
 
20
- type DeleteProjectDialogProps = {
20
+ interface DeleteProjectDialogProps {
21
21
  deleteId: string | null;
22
- showDeleteDialog: boolean;
23
22
  setShowDeleteDialog: (show: boolean) => void;
24
- };
23
+ showDeleteDialog: boolean;
24
+ }
25
25
 
26
26
  export function DeleteProjectDialog({
27
27
  deleteId,
@@ -167,10 +167,10 @@ function DiffContentPlugin({
167
167
  return null;
168
168
  }
169
169
 
170
- type DiffEditorProps = {
171
- oldContent: string;
170
+ interface DiffEditorProps {
172
171
  newContent: string;
173
- };
172
+ oldContent: string;
173
+ }
174
174
 
175
175
  export const DiffView = ({ oldContent, newContent }: DiffEditorProps) => {
176
176
  const initialConfig = {
@@ -3,16 +3,16 @@ import { cn } from "@/lib/utils";
3
3
  import { Favicon } from "./favicon";
4
4
 
5
5
  // Define a simpler interface for the sources needed by this component
6
- type FaviconSource = {
7
- url: string;
6
+ interface FaviconSource {
8
7
  title?: string; // Title is optional, mainly for alt text
9
- };
8
+ url: string;
9
+ }
10
10
 
11
- type FaviconGroupProps = {
12
- sources: FaviconSource[]; // Use the simpler interface
13
- maxVisible?: number;
11
+ interface FaviconGroupProps {
14
12
  className?: string;
15
- };
13
+ maxVisible?: number;
14
+ sources: FaviconSource[]; // Use the simpler interface
15
+ }
16
16
 
17
17
  export const FaviconGroup: React.FC<FaviconGroupProps> = ({
18
18
  sources,
@@ -32,14 +32,14 @@ import { cn } from "@/lib/utils";
32
32
  import { useChatId } from "@/providers/chat-id-provider";
33
33
  import { ShareDialog } from "./share-button";
34
34
 
35
- type HeaderBreadcrumbProps = {
35
+ interface HeaderBreadcrumbProps {
36
36
  chatId: string;
37
+ className?: string;
38
+ hasMessages?: boolean;
39
+ isReadonly: boolean;
37
40
  projectId?: string;
38
41
  user?: Session["user"];
39
- isReadonly: boolean;
40
- hasMessages?: boolean;
41
- className?: string;
42
- };
42
+ }
43
43
 
44
44
  export function HeaderBreadcrumb({
45
45
  chatId: _chatId,
@@ -181,7 +181,7 @@ export function HeaderBreadcrumb({
181
181
  );
182
182
  }
183
183
 
184
- type ChatBreadcrumbProps = {
184
+ interface ChatBreadcrumbProps {
185
185
  canManageChat: boolean;
186
186
  chatLabel: string;
187
187
  chatTitleDraft: string;
@@ -195,7 +195,7 @@ type ChatBreadcrumbProps = {
195
195
  openChatDeleteDialog: () => void;
196
196
  showShare?: boolean;
197
197
  startChatRename: () => void;
198
- };
198
+ }
199
199
 
200
200
  const PureChatBreadcrumb = memo(function InnerChatBreadcrumb({
201
201
  canManageChat,
@@ -258,14 +258,14 @@ const PureChatBreadcrumb = memo(function InnerChatBreadcrumb({
258
258
  return <BreadcrumbPage>{chatLabel}</BreadcrumbPage>;
259
259
  });
260
260
 
261
- type PerformChatRenameArgs = {
261
+ interface PerformChatRenameArgs {
262
262
  chatId: string;
263
263
  chatTitleDraft: string;
264
264
  privateChat: { title?: string; isPinned?: boolean } | null | undefined;
265
265
  renameChat: (args: { chatId: string; title: string }) => Promise<unknown>;
266
266
  setChatTitleDraft: (value: string) => void;
267
267
  setIsChatEditing: (value: boolean) => void;
268
- };
268
+ }
269
269
 
270
270
  async function performChatRename({
271
271
  chatId,
@@ -295,10 +295,10 @@ async function performChatRename({
295
295
  }
296
296
  }
297
297
 
298
- type InputKeyHandlerOptions = {
298
+ interface InputKeyHandlerOptions {
299
299
  onEnter: () => void;
300
300
  onEscape: () => void;
301
- };
301
+ }
302
302
 
303
303
  function createInputKeyDownHandler({
304
304
  onEnter,
@@ -1,14 +1,14 @@
1
1
  import { Loader2 } from "lucide-react";
2
2
  import { cn } from "@/lib/utils";
3
3
 
4
- type ImageEditorProps = {
5
- title: string;
4
+ interface ImageEditorProps {
6
5
  content: string;
7
- isCurrentVersion: boolean;
8
6
  currentVersionIndex: number;
9
- status: string;
7
+ isCurrentVersion: boolean;
10
8
  isInline: boolean;
11
- };
9
+ status: string;
10
+ title: string;
11
+ }
12
12
 
13
13
  export function ImageEditor({
14
14
  title,
@@ -12,13 +12,13 @@ import {
12
12
  } from "@/components/ui/dialog";
13
13
  import { cn } from "@/lib/utils";
14
14
 
15
- type ImageModalProps = {
15
+ interface ImageModalProps {
16
+ imageName?: string;
17
+ imageUrl: string;
16
18
  isOpen: boolean;
17
19
  onClose: () => void;
18
- imageUrl: string;
19
- imageName?: string;
20
20
  showActions?: boolean;
21
- };
21
+ }
22
22
 
23
23
  async function handleCopyImage(
24
24
  e: React.MouseEvent,
@@ -0,0 +1,269 @@
1
+ "use client";
2
+
3
+ import ReactECharts from "echarts-for-react/lib/index";
4
+ import type { EChartsOption } from "echarts-for-react/lib/types";
5
+ import { motion } from "motion/react";
6
+ import { useTheme } from "next-themes";
7
+ import { Card } from "@/components/ui/card";
8
+
9
+ const CHART_COLORS = [
10
+ "#22c55e",
11
+ "#3b82f6",
12
+ "#f59e0b",
13
+ "#8b5cf6",
14
+ "#ec4899",
15
+ "#06b6d4",
16
+ "#ef4444",
17
+ "#84cc16",
18
+ ];
19
+
20
+ interface LineScatterElement {
21
+ label: string;
22
+ points: [number | string, number][];
23
+ }
24
+
25
+ interface BarElement {
26
+ group: string;
27
+ label: string;
28
+ value: number;
29
+ }
30
+
31
+ interface BaseChartCommon {
32
+ title: string;
33
+ x_label?: string;
34
+ y_label?: string;
35
+ }
36
+
37
+ export type LineChart = BaseChartCommon & {
38
+ type: "line";
39
+ x_scale?: "datetime";
40
+ elements: LineScatterElement[];
41
+ };
42
+
43
+ export type ScatterChart = BaseChartCommon & {
44
+ type: "scatter";
45
+ x_scale?: "datetime";
46
+ elements: LineScatterElement[];
47
+ };
48
+
49
+ export type BarChart = BaseChartCommon & {
50
+ type: "bar";
51
+ x_scale?: undefined;
52
+ elements: BarElement[];
53
+ };
54
+
55
+ export type BaseChart = LineChart | ScatterChart | BarChart;
56
+
57
+ function InteractiveChart({ chart }: { chart: BaseChart }) {
58
+ const { resolvedTheme } = useTheme();
59
+ const textColor = "#e5e5e5";
60
+ const gridColor = "rgba(255, 255, 255, 0.1)";
61
+ const tooltipBg = "#171717";
62
+
63
+ const sharedOptions: EChartsOption = {
64
+ backgroundColor: "transparent",
65
+ grid: {
66
+ top: 50,
67
+ right: 32,
68
+ bottom: 32,
69
+ left: 32,
70
+ containLabel: true,
71
+ },
72
+ legend: {
73
+ textStyle: { color: textColor },
74
+ top: 8,
75
+ icon: "circle",
76
+ itemWidth: 8,
77
+ itemHeight: 8,
78
+ itemGap: 16,
79
+ },
80
+ tooltip: {
81
+ trigger: "axis",
82
+ backgroundColor: tooltipBg,
83
+ borderWidth: 0,
84
+ padding: [6, 10],
85
+ className:
86
+ "echarts-tooltip rounded-lg! border! border-neutral-200! dark:border-neutral-800!",
87
+ textStyle: {
88
+ color: textColor,
89
+ fontSize: 13,
90
+ fontFamily: "system-ui, -apple-system, sans-serif",
91
+ },
92
+ },
93
+ };
94
+
95
+ const getChartOptions = (): EChartsOption => {
96
+ const defaultAxisOptions = {
97
+ axisLine: { show: true, lineStyle: { color: gridColor } },
98
+ axisTick: { show: false },
99
+ axisLabel: {
100
+ color: textColor,
101
+ margin: 8,
102
+ fontSize: 11,
103
+ hideOverlap: true,
104
+ },
105
+ nameTextStyle: {
106
+ color: textColor,
107
+ fontSize: 13,
108
+ padding: [0, 0, 0, 0],
109
+ },
110
+ splitLine: {
111
+ show: true,
112
+ lineStyle: { color: gridColor, type: "dashed" },
113
+ },
114
+ };
115
+
116
+ if (chart.type === "line" || chart.type === "scatter") {
117
+ const series = chart.elements.map((e, index) => ({
118
+ name: e.label,
119
+ type: chart.type,
120
+ data: e.points.map((p: [number | string, number]) => {
121
+ const x =
122
+ chart.x_scale === "datetime" ? new Date(p[0]).getTime() : p[0];
123
+ return [x, p[1]];
124
+ }),
125
+ smooth: true,
126
+ symbolSize: chart.type === "scatter" ? 10 : 0,
127
+ lineStyle: {
128
+ width: 2,
129
+ color: CHART_COLORS[index % CHART_COLORS.length],
130
+ },
131
+ itemStyle: {
132
+ color: CHART_COLORS[index % CHART_COLORS.length],
133
+ },
134
+ areaStyle:
135
+ chart.type === "line"
136
+ ? {
137
+ color: {
138
+ type: "linear",
139
+ x: 0,
140
+ y: 0,
141
+ x2: 0,
142
+ y2: 1,
143
+ colorStops: [
144
+ {
145
+ offset: 0,
146
+ color: `${CHART_COLORS[index % CHART_COLORS.length]}15`,
147
+ },
148
+ { offset: 1, color: "rgba(23, 23, 23, 0)" },
149
+ ],
150
+ },
151
+ }
152
+ : undefined,
153
+ }));
154
+
155
+ return {
156
+ ...sharedOptions,
157
+ xAxis: {
158
+ type: chart.x_scale === "datetime" ? "time" : "value",
159
+ name: chart.x_label,
160
+ nameLocation: "middle",
161
+ nameGap: 40,
162
+ scale: true,
163
+ ...defaultAxisOptions,
164
+ axisLabel: {
165
+ ...defaultAxisOptions.axisLabel,
166
+ formatter:
167
+ chart.x_scale === "datetime"
168
+ ? (value: number) => {
169
+ const date = new Date(value);
170
+ return date.toLocaleDateString("en-US", {
171
+ month: "short",
172
+ year: "numeric",
173
+ });
174
+ }
175
+ : undefined,
176
+ },
177
+ },
178
+ yAxis: {
179
+ type: "value",
180
+ name: chart.y_label,
181
+ nameLocation: "middle",
182
+ nameGap: 50,
183
+ position: "right",
184
+ scale: true,
185
+ ...defaultAxisOptions,
186
+ },
187
+ series,
188
+ };
189
+ }
190
+
191
+ if (chart.type === "bar") {
192
+ const data = chart.elements.reduce(
193
+ (acc: Record<string, BarElement[]>, item) => {
194
+ const key = item.group;
195
+ if (!acc[key]) {
196
+ acc[key] = [];
197
+ }
198
+ acc[key].push(item);
199
+ return acc;
200
+ },
201
+ {}
202
+ );
203
+
204
+ const series = Object.entries(data).map(([group, elements], index) => ({
205
+ name: group,
206
+ type: "bar",
207
+ stack: "total",
208
+ data: elements?.map((e) => [e.label, e.value]),
209
+ itemStyle: {
210
+ color: CHART_COLORS[index % CHART_COLORS.length],
211
+ },
212
+ emphasis: {
213
+ itemStyle: {
214
+ shadowBlur: 10,
215
+ shadowColor: "rgba(0,0,0,0.3)",
216
+ },
217
+ },
218
+ }));
219
+
220
+ return {
221
+ ...sharedOptions,
222
+ xAxis: {
223
+ type: "category",
224
+ name: chart.x_label,
225
+ nameLocation: "middle",
226
+ nameGap: 40,
227
+ ...defaultAxisOptions,
228
+ },
229
+ yAxis: {
230
+ type: "value",
231
+ name: chart.y_label,
232
+ nameLocation: "middle",
233
+ nameGap: 50,
234
+ position: "right",
235
+ ...defaultAxisOptions,
236
+ },
237
+ series,
238
+ };
239
+ }
240
+
241
+ return sharedOptions;
242
+ };
243
+
244
+ return (
245
+ <motion.div
246
+ animate={{ opacity: 1, y: 0 }}
247
+ initial={{ opacity: 0, y: 20 }}
248
+ transition={{ duration: 0.5 }}
249
+ >
250
+ <Card className="overflow-hidden border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-900">
251
+ <div className="p-6">
252
+ {chart.title && (
253
+ <h3 className="mb-4 font-medium text-lg text-neutral-900 dark:text-neutral-100">
254
+ {chart.title}
255
+ </h3>
256
+ )}
257
+ <ReactECharts
258
+ notMerge={true}
259
+ option={getChartOptions()}
260
+ style={{ height: "400px", width: "100%" }}
261
+ theme={resolvedTheme === "dark" ? "dark" : undefined}
262
+ />
263
+ </div>
264
+ </Card>
265
+ </motion.div>
266
+ );
267
+ }
268
+
269
+ export default InteractiveChart;