Merge remote-tracking branch 'upstream/main'
Browse files- .env.example +7 -1
- .husky/commit-msg +0 -7
- CONTRIBUTING.md +1 -0
- Dockerfile +4 -0
- app/components/chat/Artifact.tsx +13 -1
- app/components/chat/Messages.client.tsx +108 -32
- app/components/sidebar/HistoryItem.tsx +10 -2
- app/components/sidebar/Menu.client.tsx +19 -2
- app/lib/.server/llm/api-key.ts +2 -0
- app/lib/.server/llm/model.ts +11 -0
- app/lib/.server/llm/prompts.ts +2 -2
- app/lib/persistence/db.ts +47 -0
- app/lib/persistence/useChatHistory.ts +22 -3
- app/lib/runtime/action-runner.ts +9 -2
- app/lib/stores/workbench.ts +26 -11
- app/utils/constants.ts +13 -1
- app/utils/diff.spec.ts +11 -0
- app/utils/diff.ts +10 -1
- docker-compose.yaml +2 -0
- eslint.config.mjs +1 -1
- package.json +1 -0
- pnpm-lock.yaml +145 -0
- worker-configuration.d.ts +1 -0
.env.example
CHANGED
@@ -5,6 +5,12 @@
|
|
5 |
# You only need this environment variable set if you want to use Groq models
|
6 |
GROQ_API_KEY=
|
7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
# Get your Open AI API Key by following these instructions -
|
9 |
# https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key
|
10 |
# You only need this environment variable set if you want to use GPT models
|
@@ -55,4 +61,4 @@ LMSTUDIO_API_BASE_URL=
|
|
55 |
XAI_API_KEY=
|
56 |
|
57 |
# Include this environment variable if you want more logging for debugging locally
|
58 |
-
VITE_LOG_LEVEL=debug
|
|
|
5 |
# You only need this environment variable set if you want to use Groq models
|
6 |
GROQ_API_KEY=
|
7 |
|
8 |
+
# Get your HuggingFace API Key here -
|
9 |
+
# https://huggingface.co/settings/tokens
|
10 |
+
# You only need this environment variable set if you want to use HuggingFace models
|
11 |
+
HuggingFace_API_KEY=
|
12 |
+
|
13 |
+
|
14 |
# Get your Open AI API Key by following these instructions -
|
15 |
# https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key
|
16 |
# You only need this environment variable set if you want to use GPT models
|
|
|
61 |
XAI_API_KEY=
|
62 |
|
63 |
# Include this environment variable if you want more logging for debugging locally
|
64 |
+
VITE_LOG_LEVEL=debug
|
.husky/commit-msg
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
#!/usr/bin/env sh
|
2 |
-
|
3 |
-
. "$(dirname "$0")/_/husky.sh"
|
4 |
-
|
5 |
-
npx commitlint --edit $1
|
6 |
-
|
7 |
-
exit 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CONTRIBUTING.md
CHANGED
@@ -72,6 +72,7 @@ pnpm install
|
|
72 |
- Add your LLM API keys (only set the ones you plan to use):
|
73 |
```bash
|
74 |
GROQ_API_KEY=XXX
|
|
|
75 |
OPENAI_API_KEY=XXX
|
76 |
ANTHROPIC_API_KEY=XXX
|
77 |
...
|
|
|
72 |
- Add your LLM API keys (only set the ones you plan to use):
|
73 |
```bash
|
74 |
GROQ_API_KEY=XXX
|
75 |
+
HuggingFace_API_KEY=XXX
|
76 |
OPENAI_API_KEY=XXX
|
77 |
ANTHROPIC_API_KEY=XXX
|
78 |
...
|
Dockerfile
CHANGED
@@ -19,6 +19,7 @@ FROM base AS bolt-ai-production
|
|
19 |
|
20 |
# Define environment variables with default values or let them be overridden
|
21 |
ARG GROQ_API_KEY
|
|
|
22 |
ARG OPENAI_API_KEY
|
23 |
ARG ANTHROPIC_API_KEY
|
24 |
ARG OPEN_ROUTER_API_KEY
|
@@ -28,6 +29,7 @@ ARG VITE_LOG_LEVEL=debug
|
|
28 |
|
29 |
ENV WRANGLER_SEND_METRICS=false \
|
30 |
GROQ_API_KEY=${GROQ_API_KEY} \
|
|
|
31 |
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
32 |
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
33 |
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
@@ -48,6 +50,7 @@ FROM base AS bolt-ai-development
|
|
48 |
|
49 |
# Define the same environment variables for development
|
50 |
ARG GROQ_API_KEY
|
|
|
51 |
ARG OPENAI_API_KEY
|
52 |
ARG ANTHROPIC_API_KEY
|
53 |
ARG OPEN_ROUTER_API_KEY
|
@@ -56,6 +59,7 @@ ARG OLLAMA_API_BASE_URL
|
|
56 |
ARG VITE_LOG_LEVEL=debug
|
57 |
|
58 |
ENV GROQ_API_KEY=${GROQ_API_KEY} \
|
|
|
59 |
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
60 |
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
61 |
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
|
|
19 |
|
20 |
# Define environment variables with default values or let them be overridden
|
21 |
ARG GROQ_API_KEY
|
22 |
+
ARG HuggingFace_API_KEY
|
23 |
ARG OPENAI_API_KEY
|
24 |
ARG ANTHROPIC_API_KEY
|
25 |
ARG OPEN_ROUTER_API_KEY
|
|
|
29 |
|
30 |
ENV WRANGLER_SEND_METRICS=false \
|
31 |
GROQ_API_KEY=${GROQ_API_KEY} \
|
32 |
+
HuggingFace_KEY=${HuggingFace_API_KEY} \
|
33 |
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
34 |
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
35 |
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
|
|
50 |
|
51 |
# Define the same environment variables for development
|
52 |
ARG GROQ_API_KEY
|
53 |
+
ARG HuggingFace
|
54 |
ARG OPENAI_API_KEY
|
55 |
ARG ANTHROPIC_API_KEY
|
56 |
ARG OPEN_ROUTER_API_KEY
|
|
|
59 |
ARG VITE_LOG_LEVEL=debug
|
60 |
|
61 |
ENV GROQ_API_KEY=${GROQ_API_KEY} \
|
62 |
+
HuggingFace_API_KEY=${HuggingFace_API_KEY} \
|
63 |
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
64 |
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
65 |
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
app/components/chat/Artifact.tsx
CHANGED
@@ -7,6 +7,7 @@ import type { ActionState } from '~/lib/runtime/action-runner';
|
|
7 |
import { workbenchStore } from '~/lib/stores/workbench';
|
8 |
import { classNames } from '~/utils/classNames';
|
9 |
import { cubicEasingFn } from '~/utils/easings';
|
|
|
10 |
|
11 |
const highlighterOptions = {
|
12 |
langs: ['shell'],
|
@@ -129,6 +130,14 @@ const actionVariants = {
|
|
129 |
visible: { opacity: 1, y: 0 },
|
130 |
};
|
131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
const ActionList = memo(({ actions }: ActionListProps) => {
|
133 |
return (
|
134 |
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }}>
|
@@ -169,7 +178,10 @@ const ActionList = memo(({ actions }: ActionListProps) => {
|
|
169 |
{type === 'file' ? (
|
170 |
<div>
|
171 |
Create{' '}
|
172 |
-
<code
|
|
|
|
|
|
|
173 |
{action.filePath}
|
174 |
</code>
|
175 |
</div>
|
|
|
7 |
import { workbenchStore } from '~/lib/stores/workbench';
|
8 |
import { classNames } from '~/utils/classNames';
|
9 |
import { cubicEasingFn } from '~/utils/easings';
|
10 |
+
import { WORK_DIR } from '~/utils/constants';
|
11 |
|
12 |
const highlighterOptions = {
|
13 |
langs: ['shell'],
|
|
|
130 |
visible: { opacity: 1, y: 0 },
|
131 |
};
|
132 |
|
133 |
+
function openArtifactInWorkbench(filePath: any) {
|
134 |
+
if (workbenchStore.currentView.get() !== 'code') {
|
135 |
+
workbenchStore.currentView.set('code');
|
136 |
+
}
|
137 |
+
|
138 |
+
workbenchStore.setSelectedFile(`${WORK_DIR}/${filePath}`);
|
139 |
+
}
|
140 |
+
|
141 |
const ActionList = memo(({ actions }: ActionListProps) => {
|
142 |
return (
|
143 |
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }}>
|
|
|
178 |
{type === 'file' ? (
|
179 |
<div>
|
180 |
Create{' '}
|
181 |
+
<code
|
182 |
+
className="bg-bolt-elements-artifacts-inlineCode-background text-bolt-elements-artifacts-inlineCode-text px-1.5 py-1 rounded-md text-bolt-elements-item-contentAccent hover:underline cursor-pointer"
|
183 |
+
onClick={() => openArtifactInWorkbench(action.filePath)}
|
184 |
+
>
|
185 |
{action.filePath}
|
186 |
</code>
|
187 |
</div>
|
app/components/chat/Messages.client.tsx
CHANGED
@@ -3,6 +3,11 @@ import React from 'react';
|
|
3 |
import { classNames } from '~/utils/classNames';
|
4 |
import { AssistantMessage } from './AssistantMessage';
|
5 |
import { UserMessage } from './UserMessage';
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
interface MessagesProps {
|
8 |
id?: string;
|
@@ -13,41 +18,112 @@ interface MessagesProps {
|
|
13 |
|
14 |
export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props: MessagesProps, ref) => {
|
15 |
const { id, isStreaming = false, messages = [] } = props;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
return (
|
18 |
-
<
|
19 |
-
{
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
'
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
<div className="
|
|
|
|
|
|
|
|
|
|
|
39 |
</div>
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
</div>
|
44 |
-
|
45 |
-
)
|
46 |
-
}
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
</
|
52 |
);
|
53 |
});
|
|
|
3 |
import { classNames } from '~/utils/classNames';
|
4 |
import { AssistantMessage } from './AssistantMessage';
|
5 |
import { UserMessage } from './UserMessage';
|
6 |
+
import * as Tooltip from '@radix-ui/react-tooltip';
|
7 |
+
import { useLocation, useNavigate } from '@remix-run/react';
|
8 |
+
import { db, chatId } from '~/lib/persistence/useChatHistory';
|
9 |
+
import { forkChat } from '~/lib/persistence/db';
|
10 |
+
import { toast } from 'react-toastify';
|
11 |
|
12 |
interface MessagesProps {
|
13 |
id?: string;
|
|
|
18 |
|
19 |
export const Messages = React.forwardRef<HTMLDivElement, MessagesProps>((props: MessagesProps, ref) => {
|
20 |
const { id, isStreaming = false, messages = [] } = props;
|
21 |
+
const location = useLocation();
|
22 |
+
const navigate = useNavigate();
|
23 |
+
|
24 |
+
const handleRewind = (messageId: string) => {
|
25 |
+
const searchParams = new URLSearchParams(location.search);
|
26 |
+
searchParams.set('rewindTo', messageId);
|
27 |
+
window.location.search = searchParams.toString();
|
28 |
+
};
|
29 |
+
|
30 |
+
const handleFork = async (messageId: string) => {
|
31 |
+
try {
|
32 |
+
if (!db || !chatId.get()) {
|
33 |
+
toast.error('Chat persistence is not available');
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
+
const urlId = await forkChat(db, chatId.get()!, messageId);
|
38 |
+
window.location.href = `/chat/${urlId}`;
|
39 |
+
} catch (error) {
|
40 |
+
toast.error('Failed to fork chat: ' + (error as Error).message);
|
41 |
+
}
|
42 |
+
};
|
43 |
|
44 |
return (
|
45 |
+
<Tooltip.Provider delayDuration={200}>
|
46 |
+
<div id={id} ref={ref} className={props.className}>
|
47 |
+
{messages.length > 0
|
48 |
+
? messages.map((message, index) => {
|
49 |
+
const { role, content, id: messageId } = message;
|
50 |
+
const isUserMessage = role === 'user';
|
51 |
+
const isFirst = index === 0;
|
52 |
+
const isLast = index === messages.length - 1;
|
53 |
+
|
54 |
+
return (
|
55 |
+
<div
|
56 |
+
key={index}
|
57 |
+
className={classNames('flex gap-4 p-6 w-full rounded-[calc(0.75rem-1px)]', {
|
58 |
+
'bg-bolt-elements-messages-background': isUserMessage || !isStreaming || (isStreaming && !isLast),
|
59 |
+
'bg-gradient-to-b from-bolt-elements-messages-background from-30% to-transparent':
|
60 |
+
isStreaming && isLast,
|
61 |
+
'mt-4': !isFirst,
|
62 |
+
})}
|
63 |
+
>
|
64 |
+
{isUserMessage && (
|
65 |
+
<div className="flex items-center justify-center w-[34px] h-[34px] overflow-hidden bg-white text-gray-600 rounded-full shrink-0 self-start">
|
66 |
+
<div className="i-ph:user-fill text-xl"></div>
|
67 |
+
</div>
|
68 |
+
)}
|
69 |
+
<div className="grid grid-col-1 w-full">
|
70 |
+
{isUserMessage ? <UserMessage content={content} /> : <AssistantMessage content={content} />}
|
71 |
</div>
|
72 |
+
{!isUserMessage && (<div className="flex gap-2">
|
73 |
+
<Tooltip.Root>
|
74 |
+
<Tooltip.Trigger asChild>
|
75 |
+
{messageId && (<button
|
76 |
+
onClick={() => handleRewind(messageId)}
|
77 |
+
key='i-ph:arrow-u-up-left'
|
78 |
+
className={classNames(
|
79 |
+
'i-ph:arrow-u-up-left',
|
80 |
+
'text-xl text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary transition-colors'
|
81 |
+
)}
|
82 |
+
/>)}
|
83 |
+
</Tooltip.Trigger>
|
84 |
+
<Tooltip.Portal>
|
85 |
+
<Tooltip.Content
|
86 |
+
className="bg-bolt-elements-tooltip-background text-bolt-elements-textPrimary px-3 py-2 rounded-lg text-sm shadow-lg"
|
87 |
+
sideOffset={5}
|
88 |
+
style={{zIndex: 1000}}
|
89 |
+
>
|
90 |
+
Revert to this message
|
91 |
+
<Tooltip.Arrow className="fill-bolt-elements-tooltip-background" />
|
92 |
+
</Tooltip.Content>
|
93 |
+
</Tooltip.Portal>
|
94 |
+
</Tooltip.Root>
|
95 |
+
|
96 |
+
<Tooltip.Root>
|
97 |
+
<Tooltip.Trigger asChild>
|
98 |
+
<button
|
99 |
+
onClick={() => handleFork(messageId)}
|
100 |
+
key='i-ph:git-fork'
|
101 |
+
className={classNames(
|
102 |
+
'i-ph:git-fork',
|
103 |
+
'text-xl text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary transition-colors'
|
104 |
+
)}
|
105 |
+
/>
|
106 |
+
</Tooltip.Trigger>
|
107 |
+
<Tooltip.Portal>
|
108 |
+
<Tooltip.Content
|
109 |
+
className="bg-bolt-elements-tooltip-background text-bolt-elements-textPrimary px-3 py-2 rounded-lg text-sm shadow-lg"
|
110 |
+
sideOffset={5}
|
111 |
+
style={{zIndex: 1000}}
|
112 |
+
>
|
113 |
+
Fork chat from this message
|
114 |
+
<Tooltip.Arrow className="fill-bolt-elements-tooltip-background" />
|
115 |
+
</Tooltip.Content>
|
116 |
+
</Tooltip.Portal>
|
117 |
+
</Tooltip.Root>
|
118 |
+
</div>)}
|
119 |
</div>
|
120 |
+
);
|
121 |
+
})
|
122 |
+
: null}
|
123 |
+
{isStreaming && (
|
124 |
+
<div className="text-center w-full text-bolt-elements-textSecondary i-svg-spinners:3-dots-fade text-4xl mt-4"></div>
|
125 |
+
)}
|
126 |
+
</div>
|
127 |
+
</Tooltip.Provider>
|
128 |
);
|
129 |
});
|
app/components/sidebar/HistoryItem.tsx
CHANGED
@@ -5,9 +5,10 @@ import { type ChatHistoryItem } from '~/lib/persistence';
|
|
5 |
interface HistoryItemProps {
|
6 |
item: ChatHistoryItem;
|
7 |
onDelete?: (event: React.UIEvent) => void;
|
|
|
8 |
}
|
9 |
|
10 |
-
export function HistoryItem({ item, onDelete }: HistoryItemProps) {
|
11 |
const [hovering, setHovering] = useState(false);
|
12 |
const hoverRef = useRef<HTMLDivElement>(null);
|
13 |
|
@@ -44,7 +45,14 @@ export function HistoryItem({ item, onDelete }: HistoryItemProps) {
|
|
44 |
{item.description}
|
45 |
<div className="absolute right-0 z-1 top-0 bottom-0 bg-gradient-to-l from-bolt-elements-background-depth-2 group-hover:from-bolt-elements-background-depth-3 to-transparent w-10 flex justify-end group-hover:w-15 group-hover:from-45%">
|
46 |
{hovering && (
|
47 |
-
<div className="flex items-center p-1 text-bolt-elements-textSecondary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
<Dialog.Trigger asChild>
|
49 |
<button
|
50 |
className="i-ph:trash scale-110"
|
|
|
5 |
interface HistoryItemProps {
|
6 |
item: ChatHistoryItem;
|
7 |
onDelete?: (event: React.UIEvent) => void;
|
8 |
+
onDuplicate?: (id: string) => void;
|
9 |
}
|
10 |
|
11 |
+
export function HistoryItem({ item, onDelete, onDuplicate }: HistoryItemProps) {
|
12 |
const [hovering, setHovering] = useState(false);
|
13 |
const hoverRef = useRef<HTMLDivElement>(null);
|
14 |
|
|
|
45 |
{item.description}
|
46 |
<div className="absolute right-0 z-1 top-0 bottom-0 bg-gradient-to-l from-bolt-elements-background-depth-2 group-hover:from-bolt-elements-background-depth-3 to-transparent w-10 flex justify-end group-hover:w-15 group-hover:from-45%">
|
47 |
{hovering && (
|
48 |
+
<div className="flex items-center p-1 text-bolt-elements-textSecondary">
|
49 |
+
{onDuplicate && (
|
50 |
+
<button
|
51 |
+
className="i-ph:copy scale-110 mr-2"
|
52 |
+
onClick={() => onDuplicate?.(item.id)}
|
53 |
+
title="Duplicate chat"
|
54 |
+
/>
|
55 |
+
)}
|
56 |
<Dialog.Trigger asChild>
|
57 |
<button
|
58 |
className="i-ph:trash scale-110"
|
app/components/sidebar/Menu.client.tsx
CHANGED
@@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
|
|
4 |
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
5 |
import { IconButton } from '~/components/ui/IconButton';
|
6 |
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
|
7 |
-
import { db, deleteById, getAll, chatId, type ChatHistoryItem } from '~/lib/persistence';
|
8 |
import { cubicEasingFn } from '~/utils/easings';
|
9 |
import { logger } from '~/utils/logger';
|
10 |
import { HistoryItem } from './HistoryItem';
|
@@ -34,6 +34,7 @@ const menuVariants = {
|
|
34 |
type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
|
35 |
|
36 |
export const Menu = () => {
|
|
|
37 |
const menuRef = useRef<HTMLDivElement>(null);
|
38 |
const [list, setList] = useState<ChatHistoryItem[]>([]);
|
39 |
const [open, setOpen] = useState(false);
|
@@ -99,6 +100,17 @@ export const Menu = () => {
|
|
99 |
};
|
100 |
}, []);
|
101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
return (
|
103 |
<motion.div
|
104 |
ref={menuRef}
|
@@ -128,7 +140,12 @@ export const Menu = () => {
|
|
128 |
{category}
|
129 |
</div>
|
130 |
{items.map((item) => (
|
131 |
-
<HistoryItem
|
|
|
|
|
|
|
|
|
|
|
132 |
))}
|
133 |
</div>
|
134 |
))}
|
|
|
4 |
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
5 |
import { IconButton } from '~/components/ui/IconButton';
|
6 |
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
|
7 |
+
import { db, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence';
|
8 |
import { cubicEasingFn } from '~/utils/easings';
|
9 |
import { logger } from '~/utils/logger';
|
10 |
import { HistoryItem } from './HistoryItem';
|
|
|
34 |
type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
|
35 |
|
36 |
export const Menu = () => {
|
37 |
+
const { duplicateCurrentChat } = useChatHistory();
|
38 |
const menuRef = useRef<HTMLDivElement>(null);
|
39 |
const [list, setList] = useState<ChatHistoryItem[]>([]);
|
40 |
const [open, setOpen] = useState(false);
|
|
|
100 |
};
|
101 |
}, []);
|
102 |
|
103 |
+
const handleDeleteClick = (event: React.UIEvent, item: ChatHistoryItem) => {
|
104 |
+
event.preventDefault();
|
105 |
+
|
106 |
+
setDialogContent({ type: 'delete', item });
|
107 |
+
};
|
108 |
+
|
109 |
+
const handleDuplicate = async (id: string) => {
|
110 |
+
await duplicateCurrentChat(id);
|
111 |
+
loadEntries(); // Reload the list after duplication
|
112 |
+
};
|
113 |
+
|
114 |
return (
|
115 |
<motion.div
|
116 |
ref={menuRef}
|
|
|
140 |
{category}
|
141 |
</div>
|
142 |
{items.map((item) => (
|
143 |
+
<HistoryItem
|
144 |
+
key={item.id}
|
145 |
+
item={item}
|
146 |
+
onDelete={(event) => handleDeleteClick(event, item)}
|
147 |
+
onDuplicate={() => handleDuplicate(item.id)}
|
148 |
+
/>
|
149 |
))}
|
150 |
</div>
|
151 |
))}
|
app/lib/.server/llm/api-key.ts
CHANGED
@@ -23,6 +23,8 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re
|
|
23 |
return env.GOOGLE_GENERATIVE_AI_API_KEY || cloudflareEnv.GOOGLE_GENERATIVE_AI_API_KEY;
|
24 |
case 'Groq':
|
25 |
return env.GROQ_API_KEY || cloudflareEnv.GROQ_API_KEY;
|
|
|
|
|
26 |
case 'OpenRouter':
|
27 |
return env.OPEN_ROUTER_API_KEY || cloudflareEnv.OPEN_ROUTER_API_KEY;
|
28 |
case 'Deepseek':
|
|
|
23 |
return env.GOOGLE_GENERATIVE_AI_API_KEY || cloudflareEnv.GOOGLE_GENERATIVE_AI_API_KEY;
|
24 |
case 'Groq':
|
25 |
return env.GROQ_API_KEY || cloudflareEnv.GROQ_API_KEY;
|
26 |
+
case 'HuggingFace':
|
27 |
+
return env.HuggingFace_API_KEY || cloudflareEnv.HuggingFace_API_KEY;
|
28 |
case 'OpenRouter':
|
29 |
return env.OPEN_ROUTER_API_KEY || cloudflareEnv.OPEN_ROUTER_API_KEY;
|
30 |
case 'Deepseek':
|
app/lib/.server/llm/model.ts
CHANGED
@@ -56,6 +56,15 @@ export function getGroqModel(apiKey: string, model: string) {
|
|
56 |
return openai(model);
|
57 |
}
|
58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
export function getOllamaModel(baseURL: string, model: string) {
|
60 |
let Ollama = ollama(model, {
|
61 |
numCtx: 32768,
|
@@ -110,6 +119,8 @@ export function getModel(provider: string, model: string, env: Env, apiKeys?: Re
|
|
110 |
return getOpenAIModel(apiKey, model);
|
111 |
case 'Groq':
|
112 |
return getGroqModel(apiKey, model);
|
|
|
|
|
113 |
case 'OpenRouter':
|
114 |
return getOpenRouterModel(apiKey, model);
|
115 |
case 'Google':
|
|
|
56 |
return openai(model);
|
57 |
}
|
58 |
|
59 |
+
export function getHuggingFaceModel(apiKey: string, model: string) {
|
60 |
+
const openai = createOpenAI({
|
61 |
+
baseURL: 'https://api-inference.huggingface.co/v1/',
|
62 |
+
apiKey,
|
63 |
+
});
|
64 |
+
|
65 |
+
return openai(model);
|
66 |
+
}
|
67 |
+
|
68 |
export function getOllamaModel(baseURL: string, model: string) {
|
69 |
let Ollama = ollama(model, {
|
70 |
numCtx: 32768,
|
|
|
119 |
return getOpenAIModel(apiKey, model);
|
120 |
case 'Groq':
|
121 |
return getGroqModel(apiKey, model);
|
122 |
+
case 'HuggingFace':
|
123 |
+
return getHuggingFaceModel(apiKey, model);
|
124 |
case 'OpenRouter':
|
125 |
return getOpenRouterModel(apiKey, model);
|
126 |
case 'Google':
|
app/lib/.server/llm/prompts.ts
CHANGED
@@ -88,7 +88,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
|
|
88 |
Example:
|
89 |
|
90 |
<${MODIFICATIONS_TAG_NAME}>
|
91 |
-
<diff path="/
|
92 |
@@ -2,7 +2,10 @@
|
93 |
return a + b;
|
94 |
}
|
@@ -103,7 +103,7 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
|
|
103 |
+
|
104 |
+console.log('The End');
|
105 |
</diff>
|
106 |
-
<file path="/
|
107 |
// full file content here
|
108 |
</file>
|
109 |
</${MODIFICATIONS_TAG_NAME}>
|
|
|
88 |
Example:
|
89 |
|
90 |
<${MODIFICATIONS_TAG_NAME}>
|
91 |
+
<diff path="${WORK_DIR}/src/main.js">
|
92 |
@@ -2,7 +2,10 @@
|
93 |
return a + b;
|
94 |
}
|
|
|
103 |
+
|
104 |
+console.log('The End');
|
105 |
</diff>
|
106 |
+
<file path="${WORK_DIR}/package.json">
|
107 |
// full file content here
|
108 |
</file>
|
109 |
</${MODIFICATIONS_TAG_NAME}>
|
app/lib/persistence/db.ts
CHANGED
@@ -158,3 +158,50 @@ async function getUrlIds(db: IDBDatabase): Promise<string[]> {
|
|
158 |
};
|
159 |
});
|
160 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
};
|
159 |
});
|
160 |
}
|
161 |
+
|
162 |
+
export async function forkChat(db: IDBDatabase, chatId: string, messageId: string): Promise<string> {
|
163 |
+
const chat = await getMessages(db, chatId);
|
164 |
+
if (!chat) throw new Error('Chat not found');
|
165 |
+
|
166 |
+
// Find the index of the message to fork at
|
167 |
+
const messageIndex = chat.messages.findIndex(msg => msg.id === messageId);
|
168 |
+
if (messageIndex === -1) throw new Error('Message not found');
|
169 |
+
|
170 |
+
// Get messages up to and including the selected message
|
171 |
+
const messages = chat.messages.slice(0, messageIndex + 1);
|
172 |
+
|
173 |
+
// Generate new IDs
|
174 |
+
const newId = await getNextId(db);
|
175 |
+
const urlId = await getUrlId(db, newId);
|
176 |
+
|
177 |
+
// Create the forked chat
|
178 |
+
await setMessages(
|
179 |
+
db,
|
180 |
+
newId,
|
181 |
+
messages,
|
182 |
+
urlId,
|
183 |
+
chat.description ? `${chat.description} (fork)` : 'Forked chat'
|
184 |
+
);
|
185 |
+
|
186 |
+
return urlId;
|
187 |
+
}
|
188 |
+
|
189 |
+
export async function duplicateChat(db: IDBDatabase, id: string): Promise<string> {
|
190 |
+
const chat = await getMessages(db, id);
|
191 |
+
if (!chat) {
|
192 |
+
throw new Error('Chat not found');
|
193 |
+
}
|
194 |
+
|
195 |
+
const newId = await getNextId(db);
|
196 |
+
const newUrlId = await getUrlId(db, newId); // Get a new urlId for the duplicated chat
|
197 |
+
|
198 |
+
await setMessages(
|
199 |
+
db,
|
200 |
+
newId,
|
201 |
+
chat.messages,
|
202 |
+
newUrlId, // Use the new urlId
|
203 |
+
`${chat.description || 'Chat'} (copy)`
|
204 |
+
);
|
205 |
+
|
206 |
+
return newUrlId; // Return the urlId instead of id for navigation
|
207 |
+
}
|
app/lib/persistence/useChatHistory.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
-
import { useLoaderData, useNavigate } from '@remix-run/react';
|
2 |
import { useState, useEffect } from 'react';
|
3 |
import { atom } from 'nanostores';
|
4 |
import type { Message } from 'ai';
|
5 |
import { toast } from 'react-toastify';
|
6 |
import { workbenchStore } from '~/lib/stores/workbench';
|
7 |
-
import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db';
|
8 |
|
9 |
export interface ChatHistoryItem {
|
10 |
id: string;
|
@@ -24,6 +24,7 @@ export const description = atom<string | undefined>(undefined);
|
|
24 |
export function useChatHistory() {
|
25 |
const navigate = useNavigate();
|
26 |
const { id: mixedId } = useLoaderData<{ id?: string }>();
|
|
|
27 |
|
28 |
const [initialMessages, setInitialMessages] = useState<Message[]>([]);
|
29 |
const [ready, setReady] = useState<boolean>(false);
|
@@ -44,7 +45,12 @@ export function useChatHistory() {
|
|
44 |
getMessages(db, mixedId)
|
45 |
.then((storedMessages) => {
|
46 |
if (storedMessages && storedMessages.messages.length > 0) {
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
48 |
setUrlId(storedMessages.urlId);
|
49 |
description.set(storedMessages.description);
|
50 |
chatId.set(storedMessages.id);
|
@@ -93,6 +99,19 @@ export function useChatHistory() {
|
|
93 |
|
94 |
await setMessages(db, chatId.get() as string, messages, urlId, description.get());
|
95 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
};
|
97 |
}
|
98 |
|
|
|
1 |
+
import { useLoaderData, useNavigate, useSearchParams } from '@remix-run/react';
|
2 |
import { useState, useEffect } from 'react';
|
3 |
import { atom } from 'nanostores';
|
4 |
import type { Message } from 'ai';
|
5 |
import { toast } from 'react-toastify';
|
6 |
import { workbenchStore } from '~/lib/stores/workbench';
|
7 |
+
import { getMessages, getNextId, getUrlId, openDatabase, setMessages, duplicateChat } from './db';
|
8 |
|
9 |
export interface ChatHistoryItem {
|
10 |
id: string;
|
|
|
24 |
export function useChatHistory() {
|
25 |
const navigate = useNavigate();
|
26 |
const { id: mixedId } = useLoaderData<{ id?: string }>();
|
27 |
+
const [searchParams] = useSearchParams();
|
28 |
|
29 |
const [initialMessages, setInitialMessages] = useState<Message[]>([]);
|
30 |
const [ready, setReady] = useState<boolean>(false);
|
|
|
45 |
getMessages(db, mixedId)
|
46 |
.then((storedMessages) => {
|
47 |
if (storedMessages && storedMessages.messages.length > 0) {
|
48 |
+
const rewindId = searchParams.get('rewindTo');
|
49 |
+
const filteredMessages = rewindId
|
50 |
+
? storedMessages.messages.slice(0, storedMessages.messages.findIndex((m) => m.id === rewindId) + 1)
|
51 |
+
: storedMessages.messages;
|
52 |
+
|
53 |
+
setInitialMessages(filteredMessages);
|
54 |
setUrlId(storedMessages.urlId);
|
55 |
description.set(storedMessages.description);
|
56 |
chatId.set(storedMessages.id);
|
|
|
99 |
|
100 |
await setMessages(db, chatId.get() as string, messages, urlId, description.get());
|
101 |
},
|
102 |
+
duplicateCurrentChat: async (listItemId:string) => {
|
103 |
+
if (!db || (!mixedId && !listItemId)) {
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
|
107 |
+
try {
|
108 |
+
const newId = await duplicateChat(db, mixedId || listItemId);
|
109 |
+
navigate(`/chat/${newId}`);
|
110 |
+
toast.success('Chat duplicated successfully');
|
111 |
+
} catch (error) {
|
112 |
+
toast.error('Failed to duplicate chat');
|
113 |
+
}
|
114 |
+
}
|
115 |
};
|
116 |
}
|
117 |
|
app/lib/runtime/action-runner.ts
CHANGED
@@ -94,7 +94,7 @@ export class ActionRunner {
|
|
94 |
|
95 |
this.#updateAction(actionId, { ...action, ...data.action, executed: !isStreaming });
|
96 |
|
97 |
-
this.#currentExecutionPromise = this.#currentExecutionPromise
|
98 |
.then(() => {
|
99 |
return this.#executeAction(actionId, isStreaming);
|
100 |
})
|
@@ -119,7 +119,14 @@ export class ActionRunner {
|
|
119 |
break;
|
120 |
}
|
121 |
case 'start': {
|
122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
break;
|
124 |
}
|
125 |
}
|
|
|
94 |
|
95 |
this.#updateAction(actionId, { ...action, ...data.action, executed: !isStreaming });
|
96 |
|
97 |
+
return this.#currentExecutionPromise = this.#currentExecutionPromise
|
98 |
.then(() => {
|
99 |
return this.#executeAction(actionId, isStreaming);
|
100 |
})
|
|
|
119 |
break;
|
120 |
}
|
121 |
case 'start': {
|
122 |
+
// making the start app non blocking
|
123 |
+
|
124 |
+
this.#runStartAction(action).then(()=>this.#updateAction(actionId, { status: 'complete' }))
|
125 |
+
.catch(()=>this.#updateAction(actionId, { status: 'failed', error: 'Action failed' }))
|
126 |
+
// adding a delay to avoid any race condition between 2 start actions
|
127 |
+
// i am up for a better approch
|
128 |
+
await new Promise(resolve=>setTimeout(resolve,2000))
|
129 |
+
return
|
130 |
break;
|
131 |
}
|
132 |
}
|
app/lib/stores/workbench.ts
CHANGED
@@ -14,6 +14,7 @@ import { saveAs } from 'file-saver';
|
|
14 |
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
|
15 |
import * as nodePath from 'node:path';
|
16 |
import type { WebContainerProcess } from '@webcontainer/api';
|
|
|
17 |
|
18 |
export interface ArtifactState {
|
19 |
id: string;
|
@@ -42,7 +43,7 @@ export class WorkbenchStore {
|
|
42 |
modifiedFiles = new Set<string>();
|
43 |
artifactIdList: string[] = [];
|
44 |
#boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined;
|
45 |
-
|
46 |
constructor() {
|
47 |
if (import.meta.hot) {
|
48 |
import.meta.hot.data.artifacts = this.artifacts;
|
@@ -52,6 +53,10 @@ export class WorkbenchStore {
|
|
52 |
}
|
53 |
}
|
54 |
|
|
|
|
|
|
|
|
|
55 |
get previews() {
|
56 |
return this.#previewsStore.previews;
|
57 |
}
|
@@ -255,8 +260,11 @@ export class WorkbenchStore {
|
|
255 |
|
256 |
this.artifacts.setKey(messageId, { ...artifact, ...state });
|
257 |
}
|
258 |
-
|
259 |
-
|
|
|
|
|
|
|
260 |
const { messageId } = data;
|
261 |
|
262 |
const artifact = this.#getArtifact(messageId);
|
@@ -265,10 +273,18 @@ export class WorkbenchStore {
|
|
265 |
unreachable('Artifact not found');
|
266 |
}
|
267 |
|
268 |
-
artifact.runner.addAction(data);
|
269 |
}
|
270 |
|
271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
272 |
const { messageId } = data;
|
273 |
|
274 |
const artifact = this.#getArtifact(messageId);
|
@@ -293,11 +309,11 @@ export class WorkbenchStore {
|
|
293 |
this.#editorStore.updateFile(fullPath, data.action.content);
|
294 |
|
295 |
if (!isStreaming) {
|
296 |
-
this.resetCurrentDocument();
|
297 |
await artifact.runner.runAction(data);
|
|
|
298 |
}
|
299 |
} else {
|
300 |
-
artifact.runner.runAction(data);
|
301 |
}
|
302 |
}
|
303 |
|
@@ -312,8 +328,7 @@ export class WorkbenchStore {
|
|
312 |
|
313 |
for (const [filePath, dirent] of Object.entries(files)) {
|
314 |
if (dirent?.type === 'file' && !dirent.isBinary) {
|
315 |
-
|
316 |
-
const relativePath = filePath.replace(/^\/home\/project\//, '');
|
317 |
|
318 |
// split the path into segments
|
319 |
const pathSegments = relativePath.split('/');
|
@@ -343,7 +358,7 @@ export class WorkbenchStore {
|
|
343 |
|
344 |
for (const [filePath, dirent] of Object.entries(files)) {
|
345 |
if (dirent?.type === 'file' && !dirent.isBinary) {
|
346 |
-
const relativePath = filePath
|
347 |
const pathSegments = relativePath.split('/');
|
348 |
let currentHandle = targetHandle;
|
349 |
|
@@ -417,7 +432,7 @@ export class WorkbenchStore {
|
|
417 |
content: Buffer.from(dirent.content).toString('base64'),
|
418 |
encoding: 'base64',
|
419 |
});
|
420 |
-
return { path: filePath
|
421 |
}
|
422 |
})
|
423 |
);
|
|
|
14 |
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
|
15 |
import * as nodePath from 'node:path';
|
16 |
import type { WebContainerProcess } from '@webcontainer/api';
|
17 |
+
import { extractRelativePath } from '~/utils/diff';
|
18 |
|
19 |
export interface ArtifactState {
|
20 |
id: string;
|
|
|
43 |
modifiedFiles = new Set<string>();
|
44 |
artifactIdList: string[] = [];
|
45 |
#boltTerminal: { terminal: ITerminal; process: WebContainerProcess } | undefined;
|
46 |
+
#globalExecutionQueue=Promise.resolve();
|
47 |
constructor() {
|
48 |
if (import.meta.hot) {
|
49 |
import.meta.hot.data.artifacts = this.artifacts;
|
|
|
53 |
}
|
54 |
}
|
55 |
|
56 |
+
addToExecutionQueue(callback: () => Promise<void>) {
|
57 |
+
this.#globalExecutionQueue=this.#globalExecutionQueue.then(()=>callback())
|
58 |
+
}
|
59 |
+
|
60 |
get previews() {
|
61 |
return this.#previewsStore.previews;
|
62 |
}
|
|
|
260 |
|
261 |
this.artifacts.setKey(messageId, { ...artifact, ...state });
|
262 |
}
|
263 |
+
addAction(data: ActionCallbackData) {
|
264 |
+
this._addAction(data)
|
265 |
+
// this.addToExecutionQueue(()=>this._addAction(data))
|
266 |
+
}
|
267 |
+
async _addAction(data: ActionCallbackData) {
|
268 |
const { messageId } = data;
|
269 |
|
270 |
const artifact = this.#getArtifact(messageId);
|
|
|
273 |
unreachable('Artifact not found');
|
274 |
}
|
275 |
|
276 |
+
return artifact.runner.addAction(data);
|
277 |
}
|
278 |
|
279 |
+
runAction(data: ActionCallbackData, isStreaming: boolean = false) {
|
280 |
+
if(isStreaming) {
|
281 |
+
this._runAction(data, isStreaming)
|
282 |
+
}
|
283 |
+
else{
|
284 |
+
this.addToExecutionQueue(()=>this._runAction(data, isStreaming))
|
285 |
+
}
|
286 |
+
}
|
287 |
+
async _runAction(data: ActionCallbackData, isStreaming: boolean = false) {
|
288 |
const { messageId } = data;
|
289 |
|
290 |
const artifact = this.#getArtifact(messageId);
|
|
|
309 |
this.#editorStore.updateFile(fullPath, data.action.content);
|
310 |
|
311 |
if (!isStreaming) {
|
|
|
312 |
await artifact.runner.runAction(data);
|
313 |
+
this.resetAllFileModifications();
|
314 |
}
|
315 |
} else {
|
316 |
+
await artifact.runner.runAction(data);
|
317 |
}
|
318 |
}
|
319 |
|
|
|
328 |
|
329 |
for (const [filePath, dirent] of Object.entries(files)) {
|
330 |
if (dirent?.type === 'file' && !dirent.isBinary) {
|
331 |
+
const relativePath = extractRelativePath(filePath);
|
|
|
332 |
|
333 |
// split the path into segments
|
334 |
const pathSegments = relativePath.split('/');
|
|
|
358 |
|
359 |
for (const [filePath, dirent] of Object.entries(files)) {
|
360 |
if (dirent?.type === 'file' && !dirent.isBinary) {
|
361 |
+
const relativePath = extractRelativePath(filePath);
|
362 |
const pathSegments = relativePath.split('/');
|
363 |
let currentHandle = targetHandle;
|
364 |
|
|
|
432 |
content: Buffer.from(dirent.content).toString('base64'),
|
433 |
encoding: 'base64',
|
434 |
});
|
435 |
+
return { path: extractRelativePath(filePath), sha: blob.sha };
|
436 |
}
|
437 |
})
|
438 |
);
|
app/utils/constants.ts
CHANGED
@@ -71,7 +71,19 @@ const PROVIDER_LIST: ProviderInfo[] = [
|
|
71 |
{ name: 'llama-3.2-1b-preview', label: 'Llama 3.2 1b (Groq)', provider: 'Groq' }
|
72 |
],
|
73 |
getApiKeyLink: 'https://console.groq.com/keys'
|
74 |
-
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
name: 'OpenAI',
|
76 |
staticModels: [
|
77 |
{ name: 'gpt-4o-mini', label: 'GPT-4o Mini', provider: 'OpenAI' },
|
|
|
71 |
{ name: 'llama-3.2-1b-preview', label: 'Llama 3.2 1b (Groq)', provider: 'Groq' }
|
72 |
],
|
73 |
getApiKeyLink: 'https://console.groq.com/keys'
|
74 |
+
},
|
75 |
+
{
|
76 |
+
name: 'HuggingFace',
|
77 |
+
staticModels: [
|
78 |
+
{ name: 'Qwen/Qwen2.5-Coder-32B-Instruct', label: 'Qwen2.5-Coder-32B-Instruct (HuggingFace)', provider: 'HuggingFace' },
|
79 |
+
{ name: '01-ai/Yi-1.5-34B-Chat', label: 'Yi-1.5-34B-Chat (HuggingFace)', provider: 'HuggingFace' },
|
80 |
+
{ name: 'codellama/CodeLlama-34b-Instruct-hf', label: 'CodeLlama-34b-Instruct (HuggingFace)', provider: 'HuggingFace' },
|
81 |
+
{ name: 'NousResearch/Hermes-3-Llama-3.1-8B', label: 'Hermes-3-Llama-3.1-8B (HuggingFace)', provider: 'HuggingFace' }
|
82 |
+
],
|
83 |
+
getApiKeyLink: 'https://huggingface.co/settings/tokens'
|
84 |
+
},
|
85 |
+
|
86 |
+
{
|
87 |
name: 'OpenAI',
|
88 |
staticModels: [
|
89 |
{ name: 'gpt-4o-mini', label: 'GPT-4o Mini', provider: 'OpenAI' },
|
app/utils/diff.spec.ts
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { describe, expect, it } from 'vitest';
|
2 |
+
import { extractRelativePath } from './diff';
|
3 |
+
import { WORK_DIR } from './constants';
|
4 |
+
|
5 |
+
describe('Diff', () => {
|
6 |
+
it('should strip out Work_dir', () => {
|
7 |
+
const filePath = `${WORK_DIR}/index.js`;
|
8 |
+
const result = extractRelativePath(filePath);
|
9 |
+
expect(result).toBe('index.js');
|
10 |
+
});
|
11 |
+
});
|
app/utils/diff.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import { createTwoFilesPatch } from 'diff';
|
2 |
import type { FileMap } from '~/lib/stores/files';
|
3 |
-
import { MODIFICATIONS_TAG_NAME } from './constants';
|
4 |
|
5 |
export const modificationsRegex = new RegExp(
|
6 |
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
|
@@ -75,6 +75,15 @@ export function diffFiles(fileName: string, oldFileContent: string, newFileConte
|
|
75 |
return unifiedDiff;
|
76 |
}
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
/**
|
79 |
* Converts the unified diff to HTML.
|
80 |
*
|
|
|
1 |
import { createTwoFilesPatch } from 'diff';
|
2 |
import type { FileMap } from '~/lib/stores/files';
|
3 |
+
import { MODIFICATIONS_TAG_NAME, WORK_DIR } from './constants';
|
4 |
|
5 |
export const modificationsRegex = new RegExp(
|
6 |
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
|
|
|
75 |
return unifiedDiff;
|
76 |
}
|
77 |
|
78 |
+
const regex = new RegExp(`^${WORK_DIR}\/`);
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Strips out the work directory from the file path.
|
82 |
+
*/
|
83 |
+
export function extractRelativePath(filePath: string) {
|
84 |
+
return filePath.replace(regex, '');
|
85 |
+
}
|
86 |
+
|
87 |
/**
|
88 |
* Converts the unified diff to HTML.
|
89 |
*
|
docker-compose.yaml
CHANGED
@@ -14,6 +14,7 @@ services:
|
|
14 |
# No strictly neded but serving as hints for Coolify
|
15 |
- PORT=5173
|
16 |
- GROQ_API_KEY=${GROQ_API_KEY}
|
|
|
17 |
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
18 |
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
19 |
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
@@ -40,6 +41,7 @@ services:
|
|
40 |
- WATCHPACK_POLLING=true
|
41 |
- PORT=5173
|
42 |
- GROQ_API_KEY=${GROQ_API_KEY}
|
|
|
43 |
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
44 |
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
45 |
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
|
|
14 |
# No strictly neded but serving as hints for Coolify
|
15 |
- PORT=5173
|
16 |
- GROQ_API_KEY=${GROQ_API_KEY}
|
17 |
+
- HuggingFace_API_KEY=${HuggingFace_API_KEY}
|
18 |
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
19 |
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
20 |
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
|
|
41 |
- WATCHPACK_POLLING=true
|
42 |
- PORT=5173
|
43 |
- GROQ_API_KEY=${GROQ_API_KEY}
|
44 |
+
- HuggingFace_API_KEY=${HuggingFace_API_KEY}
|
45 |
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
46 |
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
47 |
- OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY}
|
eslint.config.mjs
CHANGED
@@ -4,7 +4,7 @@ import { getNamingConventionRule, tsFileExtensions } from '@blitz/eslint-plugin/
|
|
4 |
|
5 |
export default [
|
6 |
{
|
7 |
-
ignores: ['**/dist', '**/node_modules', '**/.wrangler', '**/bolt/build'],
|
8 |
},
|
9 |
...blitzPlugin.configs.recommended(),
|
10 |
{
|
|
|
4 |
|
5 |
export default [
|
6 |
{
|
7 |
+
ignores: ['**/dist', '**/node_modules', '**/.wrangler', '**/bolt/build', '**/.history'],
|
8 |
},
|
9 |
...blitzPlugin.configs.recommended(),
|
10 |
{
|
package.json
CHANGED
@@ -54,6 +54,7 @@
|
|
54 |
"@openrouter/ai-sdk-provider": "^0.0.5",
|
55 |
"@radix-ui/react-dialog": "^1.1.1",
|
56 |
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
|
|
57 |
"@remix-run/cloudflare": "^2.10.2",
|
58 |
"@remix-run/cloudflare-pages": "^2.10.2",
|
59 |
"@remix-run/react": "^2.10.2",
|
|
|
54 |
"@openrouter/ai-sdk-provider": "^0.0.5",
|
55 |
"@radix-ui/react-dialog": "^1.1.1",
|
56 |
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
57 |
+
"@radix-ui/react-tooltip": "^1.1.4",
|
58 |
"@remix-run/cloudflare": "^2.10.2",
|
59 |
"@remix-run/cloudflare-pages": "^2.10.2",
|
60 |
"@remix-run/react": "^2.10.2",
|
pnpm-lock.yaml
CHANGED
@@ -95,6 +95,9 @@ importers:
|
|
95 |
'@radix-ui/react-dropdown-menu':
|
96 |
specifier: ^2.1.1
|
97 |
version: 2.1.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
|
|
|
|
|
|
98 |
'@remix-run/cloudflare':
|
99 |
specifier: ^2.10.2
|
100 |
version: 2.10.2(@cloudflare/[email protected])([email protected])
|
@@ -1380,6 +1383,15 @@ packages:
|
|
1380 |
'@types/react':
|
1381 |
optional: true
|
1382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1383 |
'@radix-ui/[email protected]':
|
1384 |
resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
|
1385 |
peerDependencies:
|
@@ -1415,6 +1427,19 @@ packages:
|
|
1415 |
'@types/react-dom':
|
1416 |
optional: true
|
1417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1418 |
'@radix-ui/[email protected]':
|
1419 |
resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==}
|
1420 |
peerDependencies:
|
@@ -1498,6 +1523,19 @@ packages:
|
|
1498 |
'@types/react-dom':
|
1499 |
optional: true
|
1500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1501 |
'@radix-ui/[email protected]':
|
1502 |
resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
|
1503 |
peerDependencies:
|
@@ -1511,6 +1549,19 @@ packages:
|
|
1511 |
'@types/react-dom':
|
1512 |
optional: true
|
1513 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1514 |
'@radix-ui/[email protected]':
|
1515 |
resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
|
1516 |
peerDependencies:
|
@@ -1546,6 +1597,19 @@ packages:
|
|
1546 |
'@types/react':
|
1547 |
optional: true
|
1548 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1549 |
'@radix-ui/[email protected]':
|
1550 |
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
1551 |
peerDependencies:
|
@@ -1600,6 +1664,19 @@ packages:
|
|
1600 |
'@types/react':
|
1601 |
optional: true
|
1602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1603 |
'@radix-ui/[email protected]':
|
1604 |
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
|
1605 |
|
@@ -6720,6 +6797,12 @@ snapshots:
|
|
6720 |
optionalDependencies:
|
6721 |
'@types/react': 18.3.3
|
6722 |
|
|
|
|
|
|
|
|
|
|
|
|
|
6723 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6724 |
dependencies:
|
6725 |
'@radix-ui/primitive': 1.1.0
|
@@ -6761,6 +6844,19 @@ snapshots:
|
|
6761 |
'@types/react': 18.3.3
|
6762 |
'@types/react-dom': 18.3.0
|
6763 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6764 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6765 |
dependencies:
|
6766 |
'@radix-ui/primitive': 1.1.0
|
@@ -6854,6 +6950,16 @@ snapshots:
|
|
6854 |
'@types/react': 18.3.3
|
6855 |
'@types/react-dom': 18.3.0
|
6856 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6857 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6858 |
dependencies:
|
6859 |
'@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
|
@@ -6864,6 +6970,16 @@ snapshots:
|
|
6864 |
'@types/react': 18.3.3
|
6865 |
'@types/react-dom': 18.3.0
|
6866 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6867 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6868 |
dependencies:
|
6869 |
'@radix-ui/react-slot': 1.1.0(@types/[email protected])([email protected])
|
@@ -6897,6 +7013,26 @@ snapshots:
|
|
6897 |
optionalDependencies:
|
6898 |
'@types/react': 18.3.3
|
6899 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6900 |
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
6901 |
dependencies:
|
6902 |
react: 18.3.1
|
@@ -6937,6 +7073,15 @@ snapshots:
|
|
6937 |
optionalDependencies:
|
6938 |
'@types/react': 18.3.3
|
6939 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6940 |
'@radix-ui/[email protected]': {}
|
6941 |
|
6942 |
'@remix-run/[email protected](@cloudflare/[email protected])([email protected])':
|
|
|
95 |
'@radix-ui/react-dropdown-menu':
|
96 |
specifier: ^2.1.1
|
97 |
version: 2.1.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
98 |
+
'@radix-ui/react-tooltip':
|
99 |
+
specifier: ^1.1.4
|
100 |
+
version: 1.1.4(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
101 |
'@remix-run/cloudflare':
|
102 |
specifier: ^2.10.2
|
103 |
version: 2.10.2(@cloudflare/[email protected])([email protected])
|
|
|
1383 |
'@types/react':
|
1384 |
optional: true
|
1385 |
|
1386 |
+
'@radix-ui/[email protected]':
|
1387 |
+
resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==}
|
1388 |
+
peerDependencies:
|
1389 |
+
'@types/react': '*'
|
1390 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1391 |
+
peerDependenciesMeta:
|
1392 |
+
'@types/react':
|
1393 |
+
optional: true
|
1394 |
+
|
1395 |
'@radix-ui/[email protected]':
|
1396 |
resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
|
1397 |
peerDependencies:
|
|
|
1427 |
'@types/react-dom':
|
1428 |
optional: true
|
1429 |
|
1430 |
+
'@radix-ui/[email protected]':
|
1431 |
+
resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==}
|
1432 |
+
peerDependencies:
|
1433 |
+
'@types/react': '*'
|
1434 |
+
'@types/react-dom': '*'
|
1435 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1436 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1437 |
+
peerDependenciesMeta:
|
1438 |
+
'@types/react':
|
1439 |
+
optional: true
|
1440 |
+
'@types/react-dom':
|
1441 |
+
optional: true
|
1442 |
+
|
1443 |
'@radix-ui/[email protected]':
|
1444 |
resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==}
|
1445 |
peerDependencies:
|
|
|
1523 |
'@types/react-dom':
|
1524 |
optional: true
|
1525 |
|
1526 |
+
'@radix-ui/[email protected]':
|
1527 |
+
resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==}
|
1528 |
+
peerDependencies:
|
1529 |
+
'@types/react': '*'
|
1530 |
+
'@types/react-dom': '*'
|
1531 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1532 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1533 |
+
peerDependenciesMeta:
|
1534 |
+
'@types/react':
|
1535 |
+
optional: true
|
1536 |
+
'@types/react-dom':
|
1537 |
+
optional: true
|
1538 |
+
|
1539 |
'@radix-ui/[email protected]':
|
1540 |
resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
|
1541 |
peerDependencies:
|
|
|
1549 |
'@types/react-dom':
|
1550 |
optional: true
|
1551 |
|
1552 |
+
'@radix-ui/[email protected]':
|
1553 |
+
resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==}
|
1554 |
+
peerDependencies:
|
1555 |
+
'@types/react': '*'
|
1556 |
+
'@types/react-dom': '*'
|
1557 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1558 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1559 |
+
peerDependenciesMeta:
|
1560 |
+
'@types/react':
|
1561 |
+
optional: true
|
1562 |
+
'@types/react-dom':
|
1563 |
+
optional: true
|
1564 |
+
|
1565 |
'@radix-ui/[email protected]':
|
1566 |
resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
|
1567 |
peerDependencies:
|
|
|
1597 |
'@types/react':
|
1598 |
optional: true
|
1599 |
|
1600 |
+
'@radix-ui/[email protected]':
|
1601 |
+
resolution: {integrity: sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==}
|
1602 |
+
peerDependencies:
|
1603 |
+
'@types/react': '*'
|
1604 |
+
'@types/react-dom': '*'
|
1605 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1606 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1607 |
+
peerDependenciesMeta:
|
1608 |
+
'@types/react':
|
1609 |
+
optional: true
|
1610 |
+
'@types/react-dom':
|
1611 |
+
optional: true
|
1612 |
+
|
1613 |
'@radix-ui/[email protected]':
|
1614 |
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
1615 |
peerDependencies:
|
|
|
1664 |
'@types/react':
|
1665 |
optional: true
|
1666 |
|
1667 |
+
'@radix-ui/[email protected]':
|
1668 |
+
resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==}
|
1669 |
+
peerDependencies:
|
1670 |
+
'@types/react': '*'
|
1671 |
+
'@types/react-dom': '*'
|
1672 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1673 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
1674 |
+
peerDependenciesMeta:
|
1675 |
+
'@types/react':
|
1676 |
+
optional: true
|
1677 |
+
'@types/react-dom':
|
1678 |
+
optional: true
|
1679 |
+
|
1680 |
'@radix-ui/[email protected]':
|
1681 |
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
|
1682 |
|
|
|
6797 |
optionalDependencies:
|
6798 |
'@types/react': 18.3.3
|
6799 |
|
6800 |
+
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
6801 |
+
dependencies:
|
6802 |
+
react: 18.3.1
|
6803 |
+
optionalDependencies:
|
6804 |
+
'@types/react': 18.3.3
|
6805 |
+
|
6806 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6807 |
dependencies:
|
6808 |
'@radix-ui/primitive': 1.1.0
|
|
|
6844 |
'@types/react': 18.3.3
|
6845 |
'@types/react-dom': 18.3.0
|
6846 |
|
6847 |
+
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6848 |
+
dependencies:
|
6849 |
+
'@radix-ui/primitive': 1.1.0
|
6850 |
+
'@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
|
6851 |
+
'@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
6852 |
+
'@radix-ui/react-use-callback-ref': 1.1.0(@types/[email protected])([email protected])
|
6853 |
+
'@radix-ui/react-use-escape-keydown': 1.1.0(@types/[email protected])([email protected])
|
6854 |
+
react: 18.3.1
|
6855 |
+
react-dom: 18.3.1([email protected])
|
6856 |
+
optionalDependencies:
|
6857 |
+
'@types/react': 18.3.3
|
6858 |
+
'@types/react-dom': 18.3.0
|
6859 |
+
|
6860 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6861 |
dependencies:
|
6862 |
'@radix-ui/primitive': 1.1.0
|
|
|
6950 |
'@types/react': 18.3.3
|
6951 |
'@types/react-dom': 18.3.0
|
6952 |
|
6953 |
+
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6954 |
+
dependencies:
|
6955 |
+
'@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
6956 |
+
'@radix-ui/react-use-layout-effect': 1.1.0(@types/[email protected])([email protected])
|
6957 |
+
react: 18.3.1
|
6958 |
+
react-dom: 18.3.1([email protected])
|
6959 |
+
optionalDependencies:
|
6960 |
+
'@types/react': 18.3.3
|
6961 |
+
'@types/react-dom': 18.3.0
|
6962 |
+
|
6963 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6964 |
dependencies:
|
6965 |
'@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
|
|
|
6970 |
'@types/react': 18.3.3
|
6971 |
'@types/react-dom': 18.3.0
|
6972 |
|
6973 |
+
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6974 |
+
dependencies:
|
6975 |
+
'@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
|
6976 |
+
'@radix-ui/react-use-layout-effect': 1.1.0(@types/[email protected])([email protected])
|
6977 |
+
react: 18.3.1
|
6978 |
+
react-dom: 18.3.1([email protected])
|
6979 |
+
optionalDependencies:
|
6980 |
+
'@types/react': 18.3.3
|
6981 |
+
'@types/react-dom': 18.3.0
|
6982 |
+
|
6983 |
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
6984 |
dependencies:
|
6985 |
'@radix-ui/react-slot': 1.1.0(@types/[email protected])([email protected])
|
|
|
7013 |
optionalDependencies:
|
7014 |
'@types/react': 18.3.3
|
7015 |
|
7016 |
+
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
7017 |
+
dependencies:
|
7018 |
+
'@radix-ui/primitive': 1.1.0
|
7019 |
+
'@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
|
7020 |
+
'@radix-ui/react-context': 1.1.1(@types/[email protected])([email protected])
|
7021 |
+
'@radix-ui/react-dismissable-layer': 1.1.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7022 |
+
'@radix-ui/react-id': 1.1.0(@types/[email protected])([email protected])
|
7023 |
+
'@radix-ui/react-popper': 1.2.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7024 |
+
'@radix-ui/react-portal': 1.1.2(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7025 |
+
'@radix-ui/react-presence': 1.1.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7026 |
+
'@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7027 |
+
'@radix-ui/react-slot': 1.1.0(@types/[email protected])([email protected])
|
7028 |
+
'@radix-ui/react-use-controllable-state': 1.1.0(@types/[email protected])([email protected])
|
7029 |
+
'@radix-ui/react-visually-hidden': 1.1.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7030 |
+
react: 18.3.1
|
7031 |
+
react-dom: 18.3.1([email protected])
|
7032 |
+
optionalDependencies:
|
7033 |
+
'@types/react': 18.3.3
|
7034 |
+
'@types/react-dom': 18.3.0
|
7035 |
+
|
7036 |
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
7037 |
dependencies:
|
7038 |
react: 18.3.1
|
|
|
7073 |
optionalDependencies:
|
7074 |
'@types/react': 18.3.3
|
7075 |
|
7076 |
+
'@radix-ui/[email protected](@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
7077 |
+
dependencies:
|
7078 |
+
'@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
7079 |
+
react: 18.3.1
|
7080 |
+
react-dom: 18.3.1([email protected])
|
7081 |
+
optionalDependencies:
|
7082 |
+
'@types/react': 18.3.3
|
7083 |
+
'@types/react-dom': 18.3.0
|
7084 |
+
|
7085 |
'@radix-ui/[email protected]': {}
|
7086 |
|
7087 |
'@remix-run/[email protected](@cloudflare/[email protected])([email protected])':
|
worker-configuration.d.ts
CHANGED
@@ -2,6 +2,7 @@ interface Env {
|
|
2 |
ANTHROPIC_API_KEY: string;
|
3 |
OPENAI_API_KEY: string;
|
4 |
GROQ_API_KEY: string;
|
|
|
5 |
OPEN_ROUTER_API_KEY: string;
|
6 |
OLLAMA_API_BASE_URL: string;
|
7 |
OPENAI_LIKE_API_KEY: string;
|
|
|
2 |
ANTHROPIC_API_KEY: string;
|
3 |
OPENAI_API_KEY: string;
|
4 |
GROQ_API_KEY: string;
|
5 |
+
HuggingFace_API_KEY: string;
|
6 |
OPEN_ROUTER_API_KEY: string;
|
7 |
OLLAMA_API_BASE_URL: string;
|
8 |
OPENAI_LIKE_API_KEY: string;
|