@chat-js/cli 0.3.0 → 0.4.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 (34) hide show
  1. package/dist/index.js +11 -6
  2. package/package.json +1 -1
  3. package/templates/chat-app/app/(chat)/api/chat/prepare/route.ts +94 -0
  4. package/templates/chat-app/app/(chat)/api/chat/route.ts +97 -14
  5. package/templates/chat-app/chat.config.ts +141 -124
  6. package/templates/chat-app/components/chat-sync.tsx +6 -3
  7. package/templates/chat-app/components/feedback-actions.tsx +7 -3
  8. package/templates/chat-app/components/message-editor.tsx +8 -3
  9. package/templates/chat-app/components/message-siblings.tsx +14 -1
  10. package/templates/chat-app/components/model-selector.tsx +669 -407
  11. package/templates/chat-app/components/multimodal-input.tsx +252 -18
  12. package/templates/chat-app/components/parallel-response-cards.tsx +157 -0
  13. package/templates/chat-app/components/part/text-message-part.tsx +9 -5
  14. package/templates/chat-app/components/retry-button.tsx +25 -8
  15. package/templates/chat-app/components/user-message.tsx +136 -125
  16. package/templates/chat-app/hooks/chat-sync-hooks.ts +11 -0
  17. package/templates/chat-app/hooks/use-navigate-to-message.ts +39 -0
  18. package/templates/chat-app/lib/ai/types.ts +74 -3
  19. package/templates/chat-app/lib/config-schema.ts +5 -0
  20. package/templates/chat-app/lib/db/migrations/0044_gray_red_shift.sql +5 -0
  21. package/templates/chat-app/lib/db/migrations/meta/0044_snapshot.json +1567 -0
  22. package/templates/chat-app/lib/db/migrations/meta/_journal.json +8 -1
  23. package/templates/chat-app/lib/db/queries.ts +84 -4
  24. package/templates/chat-app/lib/db/schema.ts +4 -1
  25. package/templates/chat-app/lib/message-conversion.ts +14 -2
  26. package/templates/chat-app/lib/stores/hooks-threads.ts +37 -1
  27. package/templates/chat-app/lib/stores/with-threads.test.ts +137 -0
  28. package/templates/chat-app/lib/stores/with-threads.ts +157 -4
  29. package/templates/chat-app/lib/thread-utils.ts +23 -2
  30. package/templates/chat-app/providers/chat-input-provider.tsx +40 -2
  31. package/templates/chat-app/scripts/db-branch-delete.sh +7 -1
  32. package/templates/chat-app/scripts/db-branch-use.sh +7 -1
  33. package/templates/chat-app/scripts/with-db.sh +7 -1
  34. package/templates/chat-app/vitest.config.ts +2 -0
@@ -13,7 +13,12 @@ import React, {
13
13
  } from "react";
14
14
  import type { LexicalChatInputRef } from "@/components/lexical-chat-input";
15
15
  import type { AppModelId } from "@/lib/ai/app-models";
16
- import type { Attachment, UiToolName } from "@/lib/ai/types";
16
+ import {
17
+ getPrimarySelectedModelId,
18
+ type Attachment,
19
+ type SelectedModelValue,
20
+ type UiToolName,
21
+ } from "@/lib/ai/types";
17
22
  import { useChatModels } from "./chat-models-provider";
18
23
  import { useDefaultModel, useModelChange } from "./default-model-provider";
19
24
 
@@ -24,10 +29,12 @@ interface ChatInputContextType {
24
29
  getInputValue: () => string;
25
30
  handleInputChange: (value: string) => void;
26
31
  handleModelChange: (modelId: AppModelId) => Promise<void>;
32
+ handleModelSelectionChange: (selection: SelectedModelValue) => Promise<void>;
27
33
  handleSubmit: (submitFn: () => void, isEditMode?: boolean) => void;
28
34
  isEmpty: boolean;
29
35
  isProjectContext: boolean;
30
36
  selectedModelId: AppModelId;
37
+ selectedModelSelection: SelectedModelValue;
31
38
  selectedTool: UiToolName | null;
32
39
  setAttachments: Dispatch<SetStateAction<Attachment[]>>;
33
40
  setSelectedTool: Dispatch<SetStateAction<UiToolName | null>>;
@@ -45,6 +52,7 @@ interface ChatInputProviderProps {
45
52
  isProjectContext?: boolean;
46
53
  localStorageEnabled?: boolean;
47
54
  overrideModelId?: AppModelId; // For message editing where we want to use the original model
55
+ overrideModelSelection?: SelectedModelValue; // For message editing with multi-model selection
48
56
  }
49
57
 
50
58
  export function ChatInputProvider({
@@ -53,6 +61,7 @@ export function ChatInputProvider({
53
61
  initialTool = null,
54
62
  initialAttachments = [],
55
63
  overrideModelId,
64
+ overrideModelSelection,
56
65
  localStorageEnabled = true,
57
66
  isProjectContext = false,
58
67
  }: ChatInputProviderProps) {
@@ -95,6 +104,10 @@ export function ChatInputProvider({
95
104
  const [selectedModelId, setSelectedModelId] = useState<AppModelId>(
96
105
  overrideModelId || defaultModel
97
106
  );
107
+ const [selectedModelSelection, setSelectedModelSelection] =
108
+ useState<SelectedModelValue>(
109
+ overrideModelSelection ?? overrideModelId ?? defaultModel
110
+ );
98
111
 
99
112
  // IMPORTANT: do not read localStorage during initial render.
100
113
  // Next SSRs client components; localStorage is client-only and will cause hydration mismatches
@@ -128,7 +141,7 @@ export function ChatInputProvider({
128
141
 
129
142
  const { getModelById } = useChatModels();
130
143
 
131
- const handleModelChange = useCallback(
144
+ const persistPrimaryModelChange = useCallback(
132
145
  async (modelId: AppModelId) => {
133
146
  const modelDef = getModelById(modelId);
134
147
 
@@ -146,6 +159,29 @@ export function ChatInputProvider({
146
159
  [selectedTool, changeModel, getModelById]
147
160
  );
148
161
 
162
+ const handleModelChange = useCallback(
163
+ async (modelId: AppModelId) => {
164
+ setSelectedModelSelection(modelId);
165
+ await persistPrimaryModelChange(modelId);
166
+ },
167
+ [persistPrimaryModelChange]
168
+ );
169
+
170
+ const handleModelSelectionChange = useCallback(
171
+ async (selection: SelectedModelValue) => {
172
+ setSelectedModelSelection(selection);
173
+
174
+ const primaryModelId = getPrimarySelectedModelId(selection);
175
+
176
+ if (!primaryModelId) {
177
+ return;
178
+ }
179
+
180
+ await persistPrimaryModelChange(primaryModelId);
181
+ },
182
+ [persistPrimaryModelChange]
183
+ );
184
+
149
185
  const clearInput = useCallback(() => {
150
186
  editorRef.current?.clear();
151
187
  setLocalStorageInput("");
@@ -207,7 +243,9 @@ export function ChatInputProvider({
207
243
  attachments,
208
244
  setAttachments,
209
245
  selectedModelId,
246
+ selectedModelSelection,
210
247
  handleModelChange,
248
+ handleModelSelectionChange,
211
249
  getInputValue,
212
250
  handleInputChange,
213
251
  getInitialInput,
@@ -2,7 +2,13 @@
2
2
  set -e
3
3
 
4
4
  BRANCH_NAME="${1:-dev-local}"
5
- BRANCH_FILE=".neon-branch"
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ MONOREPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
7
+ if [ -f "$MONOREPO_ROOT/turbo.json" ]; then
8
+ BRANCH_FILE="$MONOREPO_ROOT/.neon-branch"
9
+ else
10
+ BRANCH_FILE="$(cd "$SCRIPT_DIR/.." && pwd)/.neon-branch"
11
+ fi
6
12
 
7
13
  # Check if we're currently on this branch
8
14
  if [ -f "$BRANCH_FILE" ] && [ "$(cat "$BRANCH_FILE")" = "$BRANCH_NAME" ]; then
@@ -2,7 +2,13 @@
2
2
  # Switch active database branch (like git checkout)
3
3
  set -e
4
4
 
5
- BRANCH_FILE=".neon-branch"
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ MONOREPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
7
+ if [ -f "$MONOREPO_ROOT/turbo.json" ]; then
8
+ BRANCH_FILE="$MONOREPO_ROOT/.neon-branch"
9
+ else
10
+ BRANCH_FILE="$(cd "$SCRIPT_DIR/.." && pwd)/.neon-branch"
11
+ fi
6
12
  BRANCH_NAME="${1:-}"
7
13
 
8
14
  if [ -z "$BRANCH_NAME" ]; then
@@ -2,7 +2,13 @@
2
2
  # Wrapper that uses branch DATABASE_URL if .neon-branch exists, otherwise uses .env.local
3
3
  set -e
4
4
 
5
- BRANCH_FILE=".neon-branch"
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ MONOREPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
7
+ if [ -f "$MONOREPO_ROOT/turbo.json" ]; then
8
+ BRANCH_FILE="$MONOREPO_ROOT/.neon-branch"
9
+ else
10
+ BRANCH_FILE="$(cd "$SCRIPT_DIR/.." && pwd)/.neon-branch"
11
+ fi
6
12
 
7
13
  if [ -f "$BRANCH_FILE" ]; then
8
14
  BRANCH_NAME=$(cat "$BRANCH_FILE")
@@ -1,6 +1,8 @@
1
+ import tsconfigPaths from "vite-tsconfig-paths";
1
2
  import { defineConfig } from "vitest/config";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [tsconfigPaths()],
4
6
  test: {
5
7
  exclude: ["**/node_modules/**", "**/*.e2e.ts"],
6
8
  },