@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
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: ultracite
3
+ description: 'Skill: ultracite'
4
+ ---
5
+
6
+ # Ultracite Code Standards
7
+
8
+ This project uses **Ultracite**, a zero-config Biome preset that enforces strict code quality standards through automated formatting and linting.
9
+
10
+ ## Quick Reference
11
+
12
+ - **Format code**: `npx ultracite@6.3.3 fix`
13
+ - **Check for issues**: `npx ultracite@6.3.3 check`
14
+ - **Diagnose setup**: `npx ultracite@6.3.3 doctor`
15
+
16
+ Biome (the underlying engine) provides extremely fast Rust-based linting and formatting. Most issues are automatically fixable.
17
+
18
+ ---
19
+
20
+ ## Core Principles
21
+
22
+ Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity.
23
+
24
+ ### Type Safety & Explicitness
25
+
26
+ - Use explicit types for function parameters and return values when they enhance clarity
27
+ - Prefer `unknown` over `any` when the type is genuinely unknown
28
+ - Use const assertions (`as const`) for immutable values and literal types
29
+ - Leverage TypeScript's type narrowing instead of type assertions
30
+ - Use meaningful variable names instead of magic numbers - extract constants with descriptive names
31
+
32
+ ### Modern JavaScript/TypeScript
33
+
34
+ - Use arrow functions for callbacks and short functions
35
+ - Prefer `for...of` loops over `.forEach()` and indexed `for` loops
36
+ - Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access
37
+ - Prefer template literals over string concatenation
38
+ - Use destructuring for object and array assignments
39
+ - Use `const` by default, `let` only when reassignment is needed, never `var`
40
+
41
+ ### Async & Promises
42
+
43
+ - Always `await` promises in async functions - don't forget to use the return value
44
+ - Use `async/await` syntax instead of promise chains for better readability
45
+ - Handle errors appropriately in async code with try-catch blocks
46
+ - Don't use async functions as Promise executors
47
+
48
+ ### React & JSX
49
+
50
+ - Use function components over class components
51
+ - Call hooks at the top level only, never conditionally
52
+ - Specify all dependencies in hook dependency arrays correctly
53
+ - Use the `key` prop for elements in iterables (prefer unique IDs over array indices)
54
+ - Nest children between opening and closing tags instead of passing as props
55
+ - Don't define components inside other components
56
+ - Use semantic HTML and ARIA attributes for accessibility:
57
+ - Provide meaningful alt text for images
58
+ - Use proper heading hierarchy
59
+ - Add labels for form inputs
60
+ - Include keyboard event handlers alongside mouse events
61
+ - Use semantic elements (`<button>`, `<nav>`, etc.) instead of divs with roles
62
+
63
+ ### Error Handling & Debugging
64
+
65
+ - Remove `console.log`, `debugger`, and `alert` statements from production code
66
+ - Throw `Error` objects with descriptive messages, not strings or other values
67
+ - Use `try-catch` blocks meaningfully - don't catch errors just to rethrow them
68
+ - Prefer early returns over nested conditionals for error cases
69
+
70
+ ### Code Organization
71
+
72
+ - Keep functions focused and under reasonable cognitive complexity limits
73
+ - Extract complex conditions into well-named boolean variables
74
+ - Use early returns to reduce nesting
75
+ - Prefer simple conditionals over nested ternary operators
76
+ - Group related code together and separate concerns
77
+
78
+ ### Security
79
+
80
+ - Add `rel="noopener"` when using `target="_blank"` on links
81
+ - Avoid `dangerouslySetInnerHTML` unless absolutely necessary
82
+ - Don't use `eval()` or assign directly to `document.cookie`
83
+ - Validate and sanitize user input
84
+
85
+ ### Performance
86
+
87
+ - Avoid spread syntax in accumulators within loops
88
+ - Use top-level regex literals instead of creating them in loops
89
+ - Prefer specific imports over namespace imports
90
+ - Avoid barrel files (index files that re-export everything)
91
+ - Use proper image components (e.g., Next.js `<Image>`) over `<img>` tags
92
+
93
+ ### Framework-Specific Guidance
94
+
95
+ **Next.js:**
96
+ - Use Next.js `<Image>` component for images
97
+ - Use `next/head` or App Router metadata API for head elements
98
+ - Use Server Components for async data fetching instead of async Client Components
99
+
100
+ **React 19+:**
101
+ - Use ref as a prop instead of `React.forwardRef`
102
+
103
+ **Solid/Svelte/Vue/Qwik:**
104
+ - Use `class` and `for` attributes (not `className` or `htmlFor`)
105
+
106
+ ---
107
+
108
+ ## Testing
109
+
110
+ - Write assertions inside `it()` or `test()` blocks
111
+ - Avoid done callbacks in async tests - use async/await instead
112
+ - Don't use `.only` or `.skip` in committed code
113
+ - Keep test suites reasonably flat - avoid excessive `describe` nesting
114
+
115
+ ## When Biome Can't Help
116
+
117
+ Biome's linter will catch most issues automatically. Focus your attention on:
118
+
119
+ 1. **Business logic correctness** - Biome can't validate your algorithms
120
+ 2. **Meaningful naming** - Use descriptive names for functions, variables, and types
121
+ 3. **Architecture decisions** - Component structure, data flow, and API design
122
+ 4. **Edge cases** - Handle boundary conditions and error states
123
+ 5. **User experience** - Accessibility, performance, and usability considerations
124
+ 6. **Documentation** - Add comments for complex logic, but prefer self-documenting code
125
+
126
+ ---
127
+
128
+ Most formatting and common issues are automatically fixed by Biome. Run `npx ultracite@6.3.3 fix` before committing to ensure compliance.
@@ -11,7 +11,7 @@ export async function generateTitleFromUserMessage({
11
11
  message: ChatMessage;
12
12
  }) {
13
13
  const { text: title } = await generateText({
14
- model: await getLanguageModel(config.models.defaults.title),
14
+ model: await getLanguageModel(config.ai.workflows.title),
15
15
  system: `\n
16
16
  - you will generate a short title based on the first message a user begins a conversation with
17
17
  - ensure it is not more than 40 characters long
@@ -43,17 +43,18 @@ export async function GET(
43
43
  return new ChatSDKError("bad_request:api").toResponse();
44
44
  }
45
45
 
46
- // Get message and validate it exists with an active stream
47
- const messageWithParts = await getChatMessageWithPartsById({ id: messageId });
46
+ const [messageWithParts, session, chat] = await Promise.all([
47
+ getChatMessageWithPartsById({ id: messageId }),
48
+ auth.api.getSession({ headers: await headers() }),
49
+ getChatById({ id: chatId }),
50
+ ]);
51
+
48
52
  if (!messageWithParts || messageWithParts.chatId !== chatId) {
49
53
  return new ChatSDKError("not_found:stream").toResponse();
50
54
  }
51
55
 
52
- // Validate chat ownership
53
- const session = await auth.api.getSession({ headers: await headers() });
54
56
  const userId = session?.user?.id || null;
55
57
 
56
- const chat = await getChatById({ id: chatId });
57
58
  if (!chat) {
58
59
  return new ChatSDKError("not_found:chat").toResponse();
59
60
  }
@@ -415,7 +415,7 @@ async function createChatStream({
415
415
  const responseMessages = response.messages;
416
416
 
417
417
  // Generate and stream follow-up suggestions
418
- if (config.features.followupSuggestions) {
418
+ if (config.ai.tools.followupSuggestions.enabled) {
419
419
  const followupSuggestionsResult = generateFollowupSuggestions([
420
420
  ...contextForLLM,
421
421
  ...responseMessages,
@@ -826,14 +826,19 @@ export async function POST(request: NextRequest) {
826
826
  const explicitlyRequestedTools =
827
827
  determineExplicitlyRequestedTools(selectedTool);
828
828
 
829
- const contextResult = await prepareRequestContext({
830
- userMessage,
831
- chatId,
832
- isAnonymous,
833
- anonymousPreviousMessages,
834
- modelDefinition,
835
- explicitlyRequestedTools,
836
- });
829
+ const [contextResult, mcpConnectors] = await Promise.all([
830
+ prepareRequestContext({
831
+ userMessage,
832
+ chatId,
833
+ isAnonymous,
834
+ anonymousPreviousMessages,
835
+ modelDefinition,
836
+ explicitlyRequestedTools,
837
+ }),
838
+ config.ai.tools.mcp.enabled && userId && !isAnonymous
839
+ ? getMcpConnectorsByUserId({ userId })
840
+ : Promise.resolve([]),
841
+ ]);
837
842
 
838
843
  if (contextResult.error) {
839
844
  return contextResult.error;
@@ -841,12 +846,6 @@ export async function POST(request: NextRequest) {
841
846
 
842
847
  const { previousMessages, allowedTools } = contextResult;
843
848
 
844
- // Fetch MCP connectors for authenticated users (only if MCP integration enabled)
845
- const mcpConnectors: McpConnector[] =
846
- config.features.mcp && userId && !isAnonymous
847
- ? await getMcpConnectorsByUserId({ userId })
848
- : [];
849
-
850
849
  // Create AbortController with timeout
851
850
  const abortController = new AbortController();
852
851
  const timeoutId = setTimeout(() => {
@@ -3,9 +3,9 @@
3
3
  import { AnonymousSessionInit } from "@/components/anonymous-session-init";
4
4
  import { ChatIdProvider } from "@/providers/chat-id-provider";
5
5
 
6
- type ChatProvidersProps = {
6
+ interface ChatProvidersProps {
7
7
  children: React.ReactNode;
8
- };
8
+ }
9
9
 
10
10
  export function ChatProviders({ children }: ChatProvidersProps) {
11
11
  return (
@@ -19,17 +19,18 @@ export default async function ChatLayout({
19
19
  }: {
20
20
  children: React.ReactNode;
21
21
  }) {
22
- const cookieStore = await cookies();
23
- const session = await auth.api.getSession({ headers: await headers() });
22
+ const [cookieStore, headersRes, chatModels] = await Promise.all([
23
+ cookies(),
24
+ headers(),
25
+ getChatModels(),
26
+ ]);
27
+ const session = await auth.api.getSession({ headers: headersRes });
24
28
  const isCollapsed = cookieStore.get("sidebar:state")?.value !== "true";
25
29
 
26
30
  const cookieModel = cookieStore.get("chat-model")?.value;
27
31
  const isAnonymous = !session?.user;
28
32
 
29
- // Always fetch chat models - needed for ChatModelsProvider and cookie validation
30
- const chatModels = await getChatModels();
31
-
32
- const default_chat_model = config.models.defaults.chat;
33
+ const default_chat_model = config.ai.workflows.chat;
33
34
  // Check if the model from cookie exists in available models
34
35
  let defaultModel: AppModelId =
35
36
  (cookieModel as AppModelId) ?? default_chat_model;
@@ -37,9 +37,10 @@ export async function GET(request: NextRequest) {
37
37
  }
38
38
 
39
39
  async function cleanupOrphanedAttachments() {
40
- // Skip cleanup if neither imageGeneration nor attachments is enabled
41
- const { imageGeneration, attachments } = config.features;
42
- if (!(imageGeneration || attachments)) {
40
+ // Skip cleanup if neither image tool nor attachments is enabled
41
+ const imageGenerationEnabled = config.ai.tools.image.enabled;
42
+ const attachmentsEnabled = config.features.attachments;
43
+ if (!(imageGenerationEnabled || attachmentsEnabled)) {
43
44
  return { deletedCount: 0, deletedUrls: [], skipped: true };
44
45
  }
45
46
 
@@ -425,19 +425,19 @@
425
425
 
426
426
  .lexical-content-editable {
427
427
  position: relative;
428
- outline: none;
429
- user-select: text;
430
- white-space: pre-wrap;
431
- word-break: break-word;
428
+ overflow-y: auto;
432
429
  font-family: inherit;
433
430
  font-size: inherit;
434
431
  line-height: 1.5;
435
432
  color: inherit;
436
- background: transparent;
437
- border: none;
433
+ word-break: break-word;
434
+ white-space: pre-wrap;
438
435
  resize: none;
439
- overflow-y: auto;
436
+ user-select: text;
437
+ outline: none;
440
438
  scrollbar-width: thin;
439
+ background: transparent;
440
+ border: none;
441
441
  }
442
442
 
443
443
  .lexical-content-editable::-webkit-scrollbar {
@@ -458,15 +458,15 @@
458
458
  }
459
459
 
460
460
  .lexical-placeholder {
461
- color: var(--muted-foreground);
462
- pointer-events: none;
463
- user-select: none;
464
461
  position: absolute;
465
462
  top: 0;
466
- left: 0;
467
463
  right: 0;
468
464
  bottom: 0;
465
+ left: 0;
469
466
  z-index: 1;
467
+ color: var(--muted-foreground);
468
+ pointer-events: none;
469
+ user-select: none;
470
470
  }
471
471
 
472
472
  .lexical-root {
@@ -474,8 +474,8 @@
474
474
  }
475
475
 
476
476
  .lexical-root p {
477
- margin: 0;
478
477
  padding: 0;
478
+ margin: 0;
479
479
  }
480
480
 
481
481
  .lexical-root p:first-child {
@@ -505,25 +505,25 @@
505
505
  }
506
506
 
507
507
  .editor-container {
508
- background: #fff;
509
- margin: 20px auto 20px auto;
510
- border-radius: 2px;
511
- max-width: 600px;
512
- color: #000;
513
508
  position: relative;
514
- line-height: 20px;
509
+ max-width: 600px;
510
+ margin: 20px auto 20px auto;
515
511
  font-weight: 400;
512
+ line-height: 20px;
513
+ color: #000;
516
514
  text-align: left;
515
+ background: #fff;
516
+ border-radius: 2px;
517
517
  }
518
518
 
519
519
  .editor-input {
520
- resize: none;
521
- font-size: 15px;
522
520
  position: relative;
523
- tab-size: 1;
524
- outline: 0;
525
521
  padding: 10px 10px 5px;
522
+ font-size: 15px;
523
+ tab-size: 1;
526
524
  caret-color: currentColor;
525
+ resize: none;
526
+ outline: 0;
527
527
  }
528
528
 
529
529
  /*
@@ -88,7 +88,7 @@ export default async function RootLayout({
88
88
  <body className="antialiased">
89
89
  <Script
90
90
  src="https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js"
91
- strategy="beforeInteractive"
91
+ strategy="afterInteractive"
92
92
  />
93
93
  <NuqsAdapter>
94
94
  <ThemeProvider
@@ -2,11 +2,11 @@
2
2
  "extends": ["ultracite/core", "ultracite/react", "ultracite/next"],
3
3
  "files": {
4
4
  "includes": [
5
- "**/*",
6
5
  "!.next",
7
- "!.next",
8
- "!.devtools",
9
6
  "!.devtools",
7
+ "!node_modules",
8
+ "!playwright-report",
9
+ "!test-results",
10
10
  "!components/ui",
11
11
  "!components/ai-elements",
12
12
  "!components/streamdown",
@@ -45,14 +45,7 @@ const config = {
45
45
  paymentProcessors: [],
46
46
  },
47
47
  features: {
48
- sandbox: true, // Vercel-native, no key needed
49
- webSearch: true, // Requires TAVILY_API_KEY or FIRECRAWL_API_KEY
50
- urlRetrieval: true, // Requires FIRECRAWL_API_KEY
51
- deepResearch: true, // Requires webSearch
52
- mcp: true, // Requires MCP_ENCRYPTION_KEY
53
- imageGeneration: true, // Requires BLOB_READ_WRITE_TOKEN
54
48
  attachments: true, // Requires BLOB_READ_WRITE_TOKEN
55
- followupSuggestions: true,
56
49
  },
57
50
  legal: {
58
51
  minimumAge: 13,
@@ -74,7 +67,7 @@ const config = {
74
67
  github: true, // Requires AUTH_GITHUB_ID + AUTH_GITHUB_SECRET
75
68
  vercel: true, // Requires VERCEL_APP_CLIENT_ID + VERCEL_APP_CLIENT_SECRET
76
69
  },
77
- models: {
70
+ ai: {
78
71
  gateway: "vercel",
79
72
  providerOrder: ["openai", "google", "anthropic", "xai"],
80
73
  disabledModels: ["morph/morph-v3-large", "morph/morph-v3-fast"],
@@ -100,22 +93,56 @@ const config = {
100
93
  "openai/gpt-5-nano",
101
94
  "anthropic/claude-haiku-4.5",
102
95
  ],
103
- defaults: {
96
+ workflows: {
104
97
  chat: "openai/gpt-5-mini",
105
98
  title: "openai/gpt-5-nano",
106
99
  pdf: "openai/gpt-5-mini",
107
- artifact: "openai/gpt-5-nano",
108
- artifactSuggestion: "openai/gpt-5-mini",
109
- followupSuggestions: "openai/gpt-5-nano",
110
- suggestions: "openai/gpt-5-mini",
111
- polishText: "openai/gpt-5-mini",
112
- formatSheet: "openai/gpt-5-mini",
113
- analyzeSheet: "openai/gpt-5-mini",
114
- codeEdits: "openai/gpt-5-mini",
115
100
  chatImageCompatible: "openai/gpt-4o-mini",
116
- image: "google/gemini-3-pro-image",
117
- deepResearch: "google/gemini-2.5-flash-lite",
118
- deepResearchFinalReport: "google/gemini-3-flash",
101
+ },
102
+ tools: {
103
+ webSearch: {
104
+ enabled: true, // Requires TAVILY_API_KEY or FIRECRAWL_API_KEY
105
+ },
106
+ urlRetrieval: {
107
+ enabled: true, // Requires FIRECRAWL_API_KEY
108
+ },
109
+ codeExecution: {
110
+ enabled: true, // Vercel-native, no key needed
111
+ },
112
+ mcp: {
113
+ enabled: true, // Requires MCP_ENCRYPTION_KEY
114
+ },
115
+ followupSuggestions: {
116
+ enabled: true,
117
+ default: "openai/gpt-5-nano",
118
+ },
119
+ text: {
120
+ polish: "openai/gpt-5-mini",
121
+ },
122
+ sheet: {
123
+ format: "openai/gpt-5-mini",
124
+ analyze: "openai/gpt-5-mini",
125
+ },
126
+ code: {
127
+ edits: "openai/gpt-5-mini",
128
+ },
129
+ image: {
130
+ enabled: true, // Requires BLOB_READ_WRITE_TOKEN
131
+ default: "google/gemini-3-pro-image",
132
+ },
133
+ video: {
134
+ enabled: true, // Requires BLOB_READ_WRITE_TOKEN
135
+ default: "xai/grok-imagine-video",
136
+ },
137
+ deepResearch: {
138
+ enabled: true, // Requires webSearch
139
+ defaultModel: "google/gemini-2.5-flash-lite",
140
+ finalReportModel: "google/gemini-3-flash",
141
+ allowClarification: true,
142
+ maxResearcherIterations: 1,
143
+ maxConcurrentResearchUnits: 2,
144
+ maxSearchQueries: 2,
145
+ },
119
146
  },
120
147
  },
121
148
  anonymous: {
@@ -29,7 +29,6 @@ export function AnonymousSessionInit() {
29
29
  const trpc = useTRPC();
30
30
 
31
31
  useEffect(() => {
32
- // Only initialize for non-authenticated users after session is loaded
33
32
  if (isPending) {
34
33
  return;
35
34
  }
@@ -37,34 +36,27 @@ export function AnonymousSessionInit() {
37
36
  return;
38
37
  }
39
38
 
40
- // Get raw session data and validate/migrate
41
39
  const existingSession = getAnonymousSession();
42
40
 
43
41
  if (existingSession) {
44
- // Validate the existing session schema
45
42
  if (!isValidAnonymousSession(existingSession)) {
46
43
  console.warn(
47
44
  "Invalid session schema detected during init, clearing and creating new session"
48
45
  );
49
46
  clearAnonymousSession();
50
- const newSession = createAnonymousSession();
51
- setAnonymousSession(newSession);
52
- // Ensure UI refetches credits after creating a valid anonymous session
47
+ setAnonymousSession(createAnonymousSession());
53
48
  queryClient.invalidateQueries({
54
49
  queryKey: trpc.credits.getAvailableCredits.queryKey(),
55
50
  });
56
51
  return;
57
52
  }
58
53
  } else {
59
- // Create new session if none exists
60
- const newSession = createAnonymousSession();
61
- setAnonymousSession(newSession);
62
- // Ensure UI refetches credits after first-time session creation
54
+ setAnonymousSession(createAnonymousSession());
63
55
  queryClient.invalidateQueries({
64
56
  queryKey: trpc.credits.getAvailableCredits.queryKey(),
65
57
  });
66
58
  }
67
- }, [session, isPending, queryClient, trpc.credits.getAvailableCredits]);
59
+ }, [isPending, queryClient, trpc, session?.user]);
68
60
 
69
- return null; // This component doesn't render anything
61
+ return null;
70
62
  }
@@ -7,16 +7,16 @@ import { Button } from "./ui/button";
7
7
  import { Toggle } from "./ui/toggle";
8
8
  import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
9
9
 
10
- type ArtifactActionsProps = {
10
+ interface ArtifactActionsProps {
11
11
  artifact: UIArtifact;
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: any;
17
+ mode: "edit" | "diff";
17
18
  setMetadata: Dispatch<SetStateAction<any>>;
18
- isReadonly: boolean;
19
- };
19
+ }
20
20
 
21
21
  function PureArtifactActions({
22
22
  artifact,
@@ -34,16 +34,16 @@ import { VersionFooter } from "./version-footer";
34
34
 
35
35
  export const artifactDefinitions = [textArtifact, codeArtifact, sheetArtifact];
36
36
 
37
- export type UIArtifact = {
38
- title: string;
37
+ export interface UIArtifact {
38
+ content: string;
39
+ date?: string;
39
40
  documentId: string;
41
+ isVisible: boolean;
40
42
  kind: ArtifactKind;
41
- content: string;
42
43
  messageId: string;
43
- isVisible: boolean;
44
44
  status: "streaming" | "idle";
45
- date?: string;
46
- };
45
+ title: string;
46
+ }
47
47
 
48
48
  function PureArtifactPanel({
49
49
  isReadonly,
@@ -48,7 +48,7 @@ const PureAssistantMessage = ({
48
48
  key={`action-${messageId}`}
49
49
  messageId={messageId}
50
50
  />
51
- {isReadonly || !config.features.followupSuggestions ? null : (
51
+ {isReadonly || !config.ai.tools.followupSuggestions.enabled ? null : (
52
52
  <FollowUpSuggestionsParts messageId={messageId} />
53
53
  )}
54
54
  </MessageContent>
@@ -10,9 +10,9 @@ import {
10
10
  import { useSidebar } from "@/components/ui/sidebar";
11
11
  import { cn } from "@/lib/utils";
12
12
 
13
- type ChatLayoutContextValue = {
13
+ interface ChatLayoutContextValue {
14
14
  isSecondaryPanelVisible: boolean;
15
- };
15
+ }
16
16
 
17
17
  const ChatLayoutContext = createContext<ChatLayoutContextValue | null>(null);
18
18
 
@@ -43,6 +43,7 @@ function PureChatWelcome({
43
43
  <WelcomeMessage />
44
44
  </div>
45
45
  <MultimodalInput
46
+ autoFocus
46
47
  chatId={chatId}
47
48
  parentMessageId={parentMessageId}
48
49
  status={status}
@@ -4,15 +4,16 @@ import {
4
4
  Images,
5
5
  type LucideIcon,
6
6
  Telescope,
7
+ Video,
7
8
  } from "lucide-react";
8
9
  import type { UiToolName } from "@/lib/ai/types";
9
10
  import { config } from "@/lib/config";
10
11
 
11
- type ToolDefinition = {
12
- name: string;
12
+ interface ToolDefinition {
13
13
  icon: LucideIcon;
14
+ name: string;
14
15
  shortName: string;
15
- };
16
+ }
16
17
 
17
18
  export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
18
19
  webSearch: { name: "Web Search", icon: GlobeIcon, shortName: "Search" },
@@ -22,6 +23,7 @@ export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
22
23
  shortName: "Research",
23
24
  },
24
25
  generateImage: { name: "Create an image", icon: Images, shortName: "Image" },
26
+ generateVideo: { name: "Create a video", icon: Video, shortName: "Video" },
25
27
  createTextDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
26
28
  createCodeDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
27
29
  createSheetDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
@@ -37,10 +39,11 @@ export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
37
39
  export const enabledTools: UiToolName[] = [
38
40
  // Canvas tools are always available
39
41
  "createTextDocument",
40
- // Web search tools require webSearch integration
41
- ...(config.features.webSearch
42
- ? (["webSearch", "deepResearch"] as const)
43
- : []),
42
+ // Web search tool
43
+ ...(config.ai.tools.webSearch.enabled ? (["webSearch"] as const) : []),
44
+ // Deep research tool
45
+ ...(config.ai.tools.deepResearch.enabled ? (["deepResearch"] as const) : []),
44
46
  // Image generation requires imageGeneration integration
45
- ...(config.features.imageGeneration ? (["generateImage"] as const) : []),
47
+ ...(config.ai.tools.image.enabled ? (["generateImage"] as const) : []),
48
+ ...(config.ai.tools.video.enabled ? (["generateVideo"] as const) : []),
46
49
  ];
@@ -4,14 +4,14 @@ import { Pencil, PinIcon, Trash2 } from "lucide-react";
4
4
  import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
5
5
  import { ShareMenuItem } from "@/components/upgrade-cta/share-menu-item";
6
6
 
7
- type ChatMenuItemsProps = {
7
+ interface ChatMenuItemsProps {
8
8
  isPinned: boolean;
9
- onRename: () => void;
10
- onTogglePin: () => void;
11
9
  onDelete: () => void;
10
+ onRename: () => void;
12
11
  onShare?: () => void;
12
+ onTogglePin: () => void;
13
13
  showShare?: boolean;
14
- };
14
+ }
15
15
 
16
16
  export function ChatMenuItems({
17
17
  isPinned,