mirror of
https://github.com/stackblitz/bolt.new
synced 2025-06-26 18:17:50 +00:00
chore: pnpm lint fix
ran pnpm lint fix and fixed all lint errors
This commit is contained in:
commit
bc5b798760
@ -1,15 +1,14 @@
|
|||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
import React, { type RefCallback } from 'react';
|
import React, { type RefCallback } from 'react';
|
||||||
import { ClientOnly } from 'remix-utils/client-only';
|
import { ClientOnly } from 'remix-utils/client-only';
|
||||||
|
import styles from './BaseChat.module.scss';
|
||||||
|
import FilePreview from './FilePreview';
|
||||||
|
import { Messages } from './Messages.client';
|
||||||
|
import { SendButton } from './SendButton.client';
|
||||||
import { Menu } from '~/components/sidebar/Menu.client';
|
import { Menu } from '~/components/sidebar/Menu.client';
|
||||||
import { IconButton } from '~/components/ui/IconButton';
|
import { IconButton } from '~/components/ui/IconButton';
|
||||||
import { Workbench } from '~/components/workbench/Workbench.client';
|
import { Workbench } from '~/components/workbench/Workbench.client';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { Messages } from './Messages.client';
|
|
||||||
import { SendButton } from './SendButton.client';
|
|
||||||
import FilePreview from './FilePreview';
|
|
||||||
|
|
||||||
import styles from './BaseChat.module.scss';
|
|
||||||
|
|
||||||
interface BaseChatProps {
|
interface BaseChatProps {
|
||||||
textareaRef?: React.RefObject<HTMLTextAreaElement> | undefined;
|
textareaRef?: React.RefObject<HTMLTextAreaElement> | undefined;
|
||||||
@ -208,7 +207,12 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
|||||||
<FilePreview
|
<FilePreview
|
||||||
files={files}
|
files={files}
|
||||||
imageDataList={imageDataList || []}
|
imageDataList={imageDataList || []}
|
||||||
onRemove={onRemoveFile || (() => {})}
|
onRemove={
|
||||||
|
onRemoveFile ||
|
||||||
|
function noop() {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useChat } from 'ai/react';
|
|||||||
import { useAnimate } from 'framer-motion';
|
import { useAnimate } from 'framer-motion';
|
||||||
import { memo, useEffect, useRef, useState } from 'react';
|
import { memo, useEffect, useRef, useState } from 'react';
|
||||||
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
||||||
|
import { BaseChat } from './BaseChat';
|
||||||
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
|
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
|
||||||
import { useChatHistory } from '~/lib/persistence';
|
import { useChatHistory } from '~/lib/persistence';
|
||||||
import { chatStore } from '~/lib/stores/chat';
|
import { chatStore } from '~/lib/stores/chat';
|
||||||
@ -11,7 +12,6 @@ import { workbenchStore } from '~/lib/stores/workbench';
|
|||||||
import { fileModificationsToHTML } from '~/utils/diff';
|
import { fileModificationsToHTML } from '~/utils/diff';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
import { createScopedLogger, renderLogger } from '~/utils/logger';
|
import { createScopedLogger, renderLogger } from '~/utils/logger';
|
||||||
import { BaseChat } from './BaseChat';
|
|
||||||
|
|
||||||
const toastAnimation = cssTransition({
|
const toastAnimation = cssTransition({
|
||||||
enter: 'animated fadeInRight',
|
enter: 'animated fadeInRight',
|
||||||
@ -151,9 +151,10 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
|
|||||||
const handleFileUpload = (fileList: FileList) => {
|
const handleFileUpload = (fileList: FileList) => {
|
||||||
const newFiles = Array.from(fileList);
|
const newFiles = Array.from(fileList);
|
||||||
setFiles((prevFiles) => [...prevFiles, ...newFiles]);
|
setFiles((prevFiles) => [...prevFiles, ...newFiles]);
|
||||||
|
|
||||||
newFiles.forEach((file) => {
|
newFiles.forEach((file) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
setImageDataList((prev) => [...prev, reader.result as string]);
|
setImageDataList((prev) => [...prev, reader.result as string]);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { bundledLanguages, codeToHtml, isSpecialLang, type BundledLanguage, type SpecialLanguage } from 'shiki';
|
import { bundledLanguages, codeToHtml, isSpecialLang, type BundledLanguage, type SpecialLanguage } from 'shiki';
|
||||||
|
import styles from './CodeBlock.module.scss';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { createScopedLogger } from '~/utils/logger';
|
import { createScopedLogger } from '~/utils/logger';
|
||||||
|
|
||||||
import styles from './CodeBlock.module.scss';
|
|
||||||
|
|
||||||
const logger = createScopedLogger('CodeBlock');
|
const logger = createScopedLogger('CodeBlock');
|
||||||
|
|
||||||
interface CodeBlockProps {
|
interface CodeBlockProps {
|
||||||
|
|||||||
@ -32,4 +32,4 @@ const FilePreview: React.FC<FilePreviewProps> = ({ files, imageDataList, onRemov
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FilePreview;
|
export default FilePreview;
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import ReactMarkdown, { type Components } from 'react-markdown';
|
import ReactMarkdown, { type Components } from 'react-markdown';
|
||||||
import type { BundledLanguage } from 'shiki';
|
import type { BundledLanguage } from 'shiki';
|
||||||
import { createScopedLogger } from '~/utils/logger';
|
|
||||||
import { rehypePlugins, remarkPlugins, allowedHTMLElements } from '~/utils/markdown';
|
|
||||||
import { Artifact } from './Artifact';
|
import { Artifact } from './Artifact';
|
||||||
import { CodeBlock } from './CodeBlock';
|
import { CodeBlock } from './CodeBlock';
|
||||||
|
|
||||||
import styles from './Markdown.module.scss';
|
import styles from './Markdown.module.scss';
|
||||||
|
import { createScopedLogger } from '~/utils/logger';
|
||||||
|
import { rehypePlugins, remarkPlugins, allowedHTMLElements } from '~/utils/markdown';
|
||||||
|
|
||||||
const logger = createScopedLogger('MarkdownComponent');
|
const logger = createScopedLogger('MarkdownComponent');
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { classNames } from '~/utils/classNames';
|
|
||||||
import { AssistantMessage } from './AssistantMessage';
|
import { AssistantMessage } from './AssistantMessage';
|
||||||
import { UserMessage } from './UserMessage';
|
import { UserMessage } from './UserMessage';
|
||||||
|
import { classNames } from '~/utils/classNames';
|
||||||
|
|
||||||
interface MessagesProps {
|
interface MessagesProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { modificationsRegex } from '~/utils/diff';
|
|
||||||
import { Markdown } from './Markdown';
|
import { Markdown } from './Markdown';
|
||||||
|
import { modificationsRegex } from '~/utils/diff';
|
||||||
|
|
||||||
interface MessageContent {
|
interface MessageContent {
|
||||||
type: string;
|
type: string;
|
||||||
@ -18,7 +18,11 @@ export function UserMessage({ content }: UserMessageProps) {
|
|||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{content.map((item, index) => {
|
{content.map((item, index) => {
|
||||||
if (item.type === 'text') {
|
if (item.type === 'text') {
|
||||||
return <Markdown key={index} limitedMarkdown>{sanitizeUserMessage(item.text || '')}</Markdown>;
|
return (
|
||||||
|
<Markdown key={index} limitedMarkdown>
|
||||||
|
{sanitizeUserMessage(item.text || '')}
|
||||||
|
</Markdown>
|
||||||
|
);
|
||||||
} else if (item.type === 'image') {
|
} else if (item.type === 'image') {
|
||||||
return (
|
return (
|
||||||
<div key={index} className="max-w-[300px]">
|
<div key={index} className="max-w-[300px]">
|
||||||
@ -26,6 +30,7 @@ export function UserMessage({ content }: UserMessageProps) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -17,14 +17,14 @@ import {
|
|||||||
type Tooltip,
|
type Tooltip,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
import { memo, useEffect, useRef, useState, type MutableRefObject } from 'react';
|
import { memo, useEffect, useRef, useState, type MutableRefObject } from 'react';
|
||||||
import type { Theme } from '~/types/theme';
|
|
||||||
import { classNames } from '~/utils/classNames';
|
|
||||||
import { debounce } from '~/utils/debounce';
|
|
||||||
import { createScopedLogger, renderLogger } from '~/utils/logger';
|
|
||||||
import { BinaryContent } from './BinaryContent';
|
import { BinaryContent } from './BinaryContent';
|
||||||
import { getTheme, reconfigureTheme } from './cm-theme';
|
import { getTheme, reconfigureTheme } from './cm-theme';
|
||||||
import { indentKeyBinding } from './indent';
|
import { indentKeyBinding } from './indent';
|
||||||
import { getLanguage } from './languages';
|
import { getLanguage } from './languages';
|
||||||
|
import type { Theme } from '~/types/theme';
|
||||||
|
import { classNames } from '~/utils/classNames';
|
||||||
|
import { debounce } from '~/utils/debounce';
|
||||||
|
import { createScopedLogger, renderLogger } from '~/utils/logger';
|
||||||
|
|
||||||
const logger = createScopedLogger('CodeMirrorEditor');
|
const logger = createScopedLogger('CodeMirrorEditor');
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Compartment, type Extension } from '@codemirror/state';
|
import { Compartment, type Extension } from '@codemirror/state';
|
||||||
import { EditorView } from '@codemirror/view';
|
import { EditorView } from '@codemirror/view';
|
||||||
import { vscodeDark, vscodeLight } from '@uiw/codemirror-theme-vscode';
|
import { vscodeDark, vscodeLight } from '@uiw/codemirror-theme-vscode';
|
||||||
import type { Theme } from '~/types/theme.js';
|
|
||||||
import type { EditorSettings } from './CodeMirrorEditor.js';
|
import type { EditorSettings } from './CodeMirrorEditor.js';
|
||||||
|
import type { Theme } from '~/types/theme.js';
|
||||||
|
|
||||||
export const darkTheme = EditorView.theme({}, { dark: true });
|
export const darkTheme = EditorView.theme({}, { dark: true });
|
||||||
export const themeSelection = new Compartment();
|
export const themeSelection = new Compartment();
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { ClientOnly } from 'remix-utils/client-only';
|
import { ClientOnly } from 'remix-utils/client-only';
|
||||||
import { chatStore } from '~/lib/stores/chat';
|
|
||||||
import { classNames } from '~/utils/classNames';
|
|
||||||
import { HeaderActionButtons } from './HeaderActionButtons.client';
|
import { HeaderActionButtons } from './HeaderActionButtons.client';
|
||||||
import { ChatDescription } from '~/lib/persistence/ChatDescription.client';
|
import { ChatDescription } from '~/lib/persistence/ChatDescription.client';
|
||||||
|
import { chatStore } from '~/lib/stores/chat';
|
||||||
|
import { classNames } from '~/utils/classNames';
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const chat = useStore(chatStore);
|
const chat = useStore(chatStore);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
import { useStore } from '@nanostores/react';
|
||||||
import { chatStore } from '~/lib/stores/chat';
|
import { chatStore } from '~/lib/stores/chat';
|
||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
import { workbenchStore } from '~/lib/stores/workbench';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
|
|
||||||
interface HeaderActionButtonsProps {}
|
interface HeaderActionButtonsProps {}
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export function HistoryItem({ item, onDelete, onRename, onExport }: HistoryItemP
|
|||||||
|
|
||||||
function mouseEnter() {
|
function mouseEnter() {
|
||||||
setHovering(true);
|
setHovering(true);
|
||||||
|
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
|
import { saveAs } from 'file-saver';
|
||||||
import { motion, type Variants } from 'framer-motion';
|
import { motion, type Variants } from 'framer-motion';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { saveAs } from 'file-saver';
|
import { HistoryItem } from './HistoryItem';
|
||||||
|
import { binDates } from './date-binning';
|
||||||
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
|
||||||
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
|
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
|
||||||
import { db, deleteById, getAll, chatId, type ChatHistoryItem, setMessages } from '~/lib/persistence';
|
import { db, deleteById, getAll, chatId, type ChatHistoryItem, setMessages } from '~/lib/persistence';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
import { logger } from '~/utils/logger';
|
import { logger } from '~/utils/logger';
|
||||||
import { HistoryItem } from './HistoryItem';
|
|
||||||
import { binDates } from './date-binning';
|
|
||||||
|
|
||||||
const menuVariants = {
|
const menuVariants = {
|
||||||
closed: {
|
closed: {
|
||||||
@ -68,22 +68,26 @@ export function Menu() {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renameItem = useCallback((id: string, newDescription: string) => {
|
const renameItem = useCallback(
|
||||||
if (db) {
|
(id: string, newDescription: string) => {
|
||||||
const item = list.find((item) => item.id === id);
|
if (db) {
|
||||||
if (item) {
|
const item = list.find((item) => item.id === id);
|
||||||
setMessages(db, id, item.messages, item.urlId, newDescription)
|
|
||||||
.then(() => {
|
if (item) {
|
||||||
loadEntries();
|
setMessages(db, id, item.messages, item.urlId, newDescription)
|
||||||
toast.success('Chat renamed successfully');
|
.then(() => {
|
||||||
})
|
loadEntries();
|
||||||
.catch((error) => {
|
toast.success('Chat renamed successfully');
|
||||||
toast.error('Failed to rename chat');
|
})
|
||||||
logger.error(error);
|
.catch((error) => {
|
||||||
});
|
toast.error('Failed to rename chat');
|
||||||
|
logger.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, [list]);
|
[list],
|
||||||
|
);
|
||||||
|
|
||||||
const exportItem = useCallback((item: ChatHistoryItem) => {
|
const exportItem = useCallback((item: ChatHistoryItem) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import * as RadixDialog from '@radix-ui/react-dialog';
|
import * as RadixDialog from '@radix-ui/react-dialog';
|
||||||
import { motion, type Variants } from 'framer-motion';
|
import { motion, type Variants } from 'framer-motion';
|
||||||
import React, { memo, type ReactNode } from 'react';
|
import React, { memo, type ReactNode } from 'react';
|
||||||
|
import { IconButton } from './IconButton';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
import { IconButton } from './IconButton';
|
|
||||||
|
|
||||||
export { Close as DialogClose, Root as DialogRoot } from '@radix-ui/react-dialog';
|
export { Close as DialogClose, Root as DialogRoot } from '@radix-ui/react-dialog';
|
||||||
|
|
||||||
|
|||||||
@ -64,15 +64,23 @@ export const IconButton = memo(
|
|||||||
|
|
||||||
function getIconSize(size: IconSize) {
|
function getIconSize(size: IconSize) {
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 'sm':
|
case 'sm': {
|
||||||
return 'text-sm';
|
return 'text-sm';
|
||||||
case 'md':
|
}
|
||||||
|
case 'md': {
|
||||||
return 'text-base';
|
return 'text-base';
|
||||||
case 'lg':
|
}
|
||||||
|
case 'lg': {
|
||||||
return 'text-lg';
|
return 'text-lg';
|
||||||
case 'xl':
|
}
|
||||||
|
case 'xl': {
|
||||||
return 'text-xl';
|
return 'text-xl';
|
||||||
case 'xxl':
|
}
|
||||||
|
case 'xxl': {
|
||||||
return 'text-2xl';
|
return 'text-2xl';
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return 'text-base';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { themeStore, toggleTheme } from '~/lib/stores/theme';
|
|
||||||
import { IconButton } from './IconButton';
|
import { IconButton } from './IconButton';
|
||||||
|
import { themeStore, toggleTheme } from '~/lib/stores/theme';
|
||||||
|
|
||||||
interface ThemeSwitchProps {
|
interface ThemeSwitchProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels';
|
import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels';
|
||||||
|
import { FileBreadcrumb } from './FileBreadcrumb';
|
||||||
|
import { FileTree } from './FileTree';
|
||||||
|
import { Terminal, type TerminalRef } from './terminal/Terminal';
|
||||||
import {
|
import {
|
||||||
CodeMirrorEditor,
|
CodeMirrorEditor,
|
||||||
type EditorDocument,
|
type EditorDocument,
|
||||||
@ -20,9 +23,6 @@ import { classNames } from '~/utils/classNames';
|
|||||||
import { WORK_DIR } from '~/utils/constants';
|
import { WORK_DIR } from '~/utils/constants';
|
||||||
import { renderLogger } from '~/utils/logger';
|
import { renderLogger } from '~/utils/logger';
|
||||||
import { isMobile } from '~/utils/mobile';
|
import { isMobile } from '~/utils/mobile';
|
||||||
import { FileBreadcrumb } from './FileBreadcrumb';
|
|
||||||
import { FileTree } from './FileTree';
|
|
||||||
import { Terminal, type TerminalRef } from './terminal/Terminal';
|
|
||||||
|
|
||||||
interface EditorPanelProps {
|
interface EditorPanelProps {
|
||||||
files?: FileMap;
|
files?: FileMap;
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||||
import { AnimatePresence, motion, type Variants } from 'framer-motion';
|
import { AnimatePresence, motion, type Variants } from 'framer-motion';
|
||||||
import { memo, useEffect, useRef, useState } from 'react';
|
import { memo, useEffect, useRef, useState } from 'react';
|
||||||
|
import FileTree from './FileTree';
|
||||||
import type { FileMap } from '~/lib/stores/files';
|
import type { FileMap } from '~/lib/stores/files';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { WORK_DIR } from '~/utils/constants';
|
import { WORK_DIR } from '~/utils/constants';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
import { renderLogger } from '~/utils/logger';
|
import { renderLogger } from '~/utils/logger';
|
||||||
import FileTree from './FileTree';
|
|
||||||
|
|
||||||
const WORK_DIR_REGEX = new RegExp(`^${WORK_DIR.split('/').slice(0, -1).join('/').replaceAll('/', '\\/')}/`);
|
const WORK_DIR_REGEX = new RegExp(`^${WORK_DIR.split('/').slice(0, -1).join('/').replaceAll('/', '\\/')}/`);
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { PortDropdown } from './PortDropdown';
|
||||||
import { IconButton } from '~/components/ui/IconButton';
|
import { IconButton } from '~/components/ui/IconButton';
|
||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
import { workbenchStore } from '~/lib/stores/workbench';
|
||||||
import { PortDropdown } from './PortDropdown';
|
|
||||||
|
|
||||||
export const Preview = memo(() => {
|
export const Preview = memo(() => {
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
import { motion, type HTMLMotionProps, type Variants } from 'framer-motion';
|
import { motion, type HTMLMotionProps, type Variants } from 'framer-motion';
|
||||||
|
import JSZip from 'jszip';
|
||||||
import { computed } from 'nanostores';
|
import { computed } from 'nanostores';
|
||||||
import { memo, useCallback, useEffect } from 'react';
|
import { memo, useCallback, useEffect } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import JSZip from 'jszip';
|
import { EditorPanel } from './EditorPanel';
|
||||||
import { saveAs } from 'file-saver';
|
import { Preview } from './Preview';
|
||||||
import {
|
import {
|
||||||
type OnChangeCallback as OnEditorChange,
|
type OnChangeCallback as OnEditorChange,
|
||||||
type OnScrollCallback as OnEditorScroll,
|
type OnScrollCallback as OnEditorScroll,
|
||||||
@ -16,8 +18,6 @@ import { workbenchStore, type WorkbenchViewType } from '~/lib/stores/workbench';
|
|||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { cubicEasingFn } from '~/utils/easings';
|
import { cubicEasingFn } from '~/utils/easings';
|
||||||
import { renderLogger } from '~/utils/logger';
|
import { renderLogger } from '~/utils/logger';
|
||||||
import { EditorPanel } from './EditorPanel';
|
|
||||||
import { Preview } from './Preview';
|
|
||||||
|
|
||||||
interface WorkspaceProps {
|
interface WorkspaceProps {
|
||||||
chatStarted?: boolean;
|
chatStarted?: boolean;
|
||||||
@ -155,10 +155,7 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
|
|||||||
<div className="ml-auto" />
|
<div className="ml-auto" />
|
||||||
{selectedView === 'code' && (
|
{selectedView === 'code' && (
|
||||||
<>
|
<>
|
||||||
<PanelHeaderButton
|
<PanelHeaderButton className="mr-1 text-sm" onClick={downloadZip}>
|
||||||
className="mr-1 text-sm"
|
|
||||||
onClick={downloadZip}
|
|
||||||
>
|
|
||||||
<div className="i-ph:download-bold" />
|
<div className="i-ph:download-bold" />
|
||||||
Download
|
Download
|
||||||
</PanelHeaderButton>
|
</PanelHeaderButton>
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import { FitAddon } from '@xterm/addon-fit';
|
|||||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||||
import { Terminal as XTerm } from '@xterm/xterm';
|
import { Terminal as XTerm } from '@xterm/xterm';
|
||||||
import { forwardRef, memo, useEffect, useImperativeHandle, useRef } from 'react';
|
import { forwardRef, memo, useEffect, useImperativeHandle, useRef } from 'react';
|
||||||
|
import { getTerminalTheme } from './theme';
|
||||||
import type { Theme } from '~/lib/stores/theme';
|
import type { Theme } from '~/lib/stores/theme';
|
||||||
import { createScopedLogger } from '~/utils/logger';
|
import { createScopedLogger } from '~/utils/logger';
|
||||||
import { getTerminalTheme } from './theme';
|
|
||||||
|
|
||||||
const logger = createScopedLogger('Terminal');
|
const logger = createScopedLogger('Terminal');
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { streamText as _streamText } from 'ai';
|
import { streamText as _streamText } from 'ai';
|
||||||
import { getAPIKey } from '~/lib/.server/llm/api-key';
|
|
||||||
import { getAnthropicModel } from '~/lib/.server/llm/model';
|
|
||||||
import { MAX_TOKENS } from './constants';
|
import { MAX_TOKENS } from './constants';
|
||||||
import { getSystemPrompt } from './prompts';
|
import { getSystemPrompt } from './prompts';
|
||||||
|
import { getAPIKey } from '~/lib/.server/llm/api-key';
|
||||||
|
import { getAnthropicModel } from '~/lib/.server/llm/model';
|
||||||
|
|
||||||
interface ToolResult<Name extends string, Args, Result> {
|
interface ToolResult<Name extends string, Args, Result> {
|
||||||
toolCallId: string;
|
toolCallId: string;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
import { createScopedLogger } from '~/utils/logger';
|
|
||||||
import type { ChatHistoryItem } from './useChatHistory';
|
import type { ChatHistoryItem } from './useChatHistory';
|
||||||
|
import { createScopedLogger } from '~/utils/logger';
|
||||||
|
|
||||||
const logger = createScopedLogger('ChatHistory');
|
const logger = createScopedLogger('ChatHistory');
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { useLoaderData, useNavigate } from '@remix-run/react';
|
import { useLoaderData, useNavigate } from '@remix-run/react';
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import { atom } from 'nanostores';
|
|
||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
|
import { atom } from 'nanostores';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
|
||||||
import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db';
|
import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db';
|
||||||
|
import { workbenchStore } from '~/lib/stores/workbench';
|
||||||
|
|
||||||
export interface ChatHistoryItem {
|
export interface ChatHistoryItem {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
|
import * as nodePath from 'node:path';
|
||||||
import { WebContainer } from '@webcontainer/api';
|
import { WebContainer } from '@webcontainer/api';
|
||||||
import { map, type MapStore } from 'nanostores';
|
import { map, type MapStore } from 'nanostores';
|
||||||
import * as nodePath from 'node:path';
|
import type { ActionCallbackData } from './message-parser';
|
||||||
import type { BoltAction } from '~/types/actions';
|
import type { BoltAction } from '~/types/actions';
|
||||||
import { createScopedLogger } from '~/utils/logger';
|
import { createScopedLogger } from '~/utils/logger';
|
||||||
import { unreachable } from '~/utils/unreachable';
|
import { unreachable } from '~/utils/unreachable';
|
||||||
import type { ActionCallbackData } from './message-parser';
|
|
||||||
|
|
||||||
const logger = createScopedLogger('ActionRunner');
|
const logger = createScopedLogger('ActionRunner');
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { atom, computed, map, type MapStore, type WritableAtom } from 'nanostores';
|
import { atom, computed, map, type MapStore, type WritableAtom } from 'nanostores';
|
||||||
import type { EditorDocument, ScrollPosition } from '~/components/editor/codemirror/CodeMirrorEditor';
|
|
||||||
import type { FileMap, FilesStore } from './files';
|
import type { FileMap, FilesStore } from './files';
|
||||||
|
import type { EditorDocument, ScrollPosition } from '~/components/editor/codemirror/CodeMirrorEditor';
|
||||||
|
|
||||||
export type EditorDocuments = Record<string, EditorDocument>;
|
export type EditorDocuments = Record<string, EditorDocument>;
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
import { Buffer } from 'node:buffer';
|
||||||
|
import * as nodePath from 'node:path';
|
||||||
import type { PathWatcherEvent, WebContainer } from '@webcontainer/api';
|
import type { PathWatcherEvent, WebContainer } from '@webcontainer/api';
|
||||||
import { getEncoding } from 'istextorbinary';
|
import { getEncoding } from 'istextorbinary';
|
||||||
import { map, type MapStore } from 'nanostores';
|
import { map, type MapStore } from 'nanostores';
|
||||||
import { Buffer } from 'node:buffer';
|
|
||||||
import * as nodePath from 'node:path';
|
|
||||||
import { bufferWatchEvents } from '~/utils/buffer';
|
import { bufferWatchEvents } from '~/utils/buffer';
|
||||||
import { WORK_DIR } from '~/utils/constants';
|
import { WORK_DIR } from '~/utils/constants';
|
||||||
import { computeFileModifications } from '~/utils/diff';
|
import { computeFileModifications } from '~/utils/diff';
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { atom, map, type MapStore, type ReadableAtom, type WritableAtom } from 'nanostores';
|
import { atom, map, type MapStore, type ReadableAtom, type WritableAtom } from 'nanostores';
|
||||||
|
import { EditorStore } from './editor';
|
||||||
|
import { FilesStore, type FileMap } from './files';
|
||||||
|
import { PreviewsStore } from './previews';
|
||||||
|
import { TerminalStore } from './terminal';
|
||||||
import type { EditorDocument, ScrollPosition } from '~/components/editor/codemirror/CodeMirrorEditor';
|
import type { EditorDocument, ScrollPosition } from '~/components/editor/codemirror/CodeMirrorEditor';
|
||||||
import { ActionRunner } from '~/lib/runtime/action-runner';
|
import { ActionRunner } from '~/lib/runtime/action-runner';
|
||||||
import type { ActionCallbackData, ArtifactCallbackData } from '~/lib/runtime/message-parser';
|
import type { ActionCallbackData, ArtifactCallbackData } from '~/lib/runtime/message-parser';
|
||||||
import { webcontainer } from '~/lib/webcontainer';
|
import { webcontainer } from '~/lib/webcontainer';
|
||||||
import type { ITerminal } from '~/types/terminal';
|
import type { ITerminal } from '~/types/terminal';
|
||||||
import { unreachable } from '~/utils/unreachable';
|
import { unreachable } from '~/utils/unreachable';
|
||||||
import { EditorStore } from './editor';
|
|
||||||
import { FilesStore, type FileMap } from './files';
|
|
||||||
import { PreviewsStore } from './previews';
|
|
||||||
import { TerminalStore } from './terminal';
|
|
||||||
|
|
||||||
export interface ArtifactState {
|
export interface ArtifactState {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
13
app/root.tsx
13
app/root.tsx
@ -2,14 +2,13 @@ import { useStore } from '@nanostores/react';
|
|||||||
import type { LinksFunction } from '@remix-run/cloudflare';
|
import type { LinksFunction } from '@remix-run/cloudflare';
|
||||||
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
|
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
|
||||||
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
|
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
|
||||||
import { themeStore } from './lib/stores/theme';
|
|
||||||
import { stripIndents } from './utils/stripIndent';
|
|
||||||
import { createHead } from 'remix-island';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
|
||||||
import globalStyles from './styles/index.scss?url';
|
|
||||||
import xtermStyles from '@xterm/xterm/css/xterm.css?url';
|
import xtermStyles from '@xterm/xterm/css/xterm.css?url';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
||||||
|
import { createHead } from 'remix-island';
|
||||||
|
import { themeStore } from './lib/stores/theme';
|
||||||
|
import globalStyles from './styles/index.scss?url';
|
||||||
|
import { stripIndents } from './utils/stripIndent';
|
||||||
|
|
||||||
import 'virtual:uno.css';
|
import 'virtual:uno.css';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { type ActionFunctionArgs } from '@remix-run/cloudflare';
|
import { type ActionFunctionArgs } from '@remix-run/cloudflare';
|
||||||
import { createDataStream, createDataStreamResponse } from 'ai';
|
|
||||||
import { streamText } from '~/lib/.server/llm/stream-text';
|
import { streamText } from '~/lib/.server/llm/stream-text';
|
||||||
import { stripIndents } from '~/utils/stripIndent';
|
import { stripIndents } from '~/utils/stripIndent';
|
||||||
|
|
||||||
@ -36,21 +35,23 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) {
|
|||||||
transform(chunk, controller) {
|
transform(chunk, controller) {
|
||||||
const text = decoder.decode(chunk);
|
const text = decoder.decode(chunk);
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.startsWith('0:')) {
|
if (line.startsWith('0:')) {
|
||||||
// Extract only the content after "0:"
|
// extract only the content after "0:"
|
||||||
const content = line.slice(2);
|
const content = line.slice(2);
|
||||||
controller.enqueue(encoder.encode(content));
|
controller.enqueue(encoder.encode(content));
|
||||||
}
|
}
|
||||||
// Ignore 'e:' and 'd:' lines as they contain metadata
|
|
||||||
|
// ignore 'e:' and 'd:' lines as they contain metadata
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const stream = result.toDataStream().pipeThrough(transformStream);
|
const stream = result.toDataStream().pipeThrough(transformStream);
|
||||||
|
|
||||||
return new Response(stream, {
|
return new Response(stream, {
|
||||||
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
|
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createTwoFilesPatch } from 'diff';
|
import { createTwoFilesPatch } from 'diff';
|
||||||
import type { FileMap } from '~/lib/stores/files';
|
|
||||||
import { MODIFICATIONS_TAG_NAME } from './constants';
|
import { MODIFICATIONS_TAG_NAME } from './constants';
|
||||||
|
import type { FileMap } from '~/lib/stores/files';
|
||||||
|
|
||||||
export const modificationsRegex = new RegExp(
|
export const modificationsRegex = new RegExp(
|
||||||
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
|
`^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ interface Logger {
|
|||||||
setLevel: (level: DebugLevel) => void;
|
setLevel: (level: DebugLevel) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV ? 'debug' : 'info';
|
let currentLevel: DebugLevel = (import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV) ? 'debug' : 'info';
|
||||||
|
|
||||||
const isWorker = 'HTMLRewriter' in globalThis;
|
const isWorker = 'HTMLRewriter' in globalThis;
|
||||||
const supportsColor = !isWorker;
|
const supportsColor = !isWorker;
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
|
import type { UnistNode, UnistParent } from 'node_modules/unist-util-visit/lib';
|
||||||
import rehypeRaw from 'rehype-raw';
|
import rehypeRaw from 'rehype-raw';
|
||||||
|
import rehypeSanitize, { defaultSchema, type Options as RehypeSanitizeOptions } from 'rehype-sanitize';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
import type { PluggableList, Plugin } from 'unified';
|
import type { PluggableList, Plugin } from 'unified';
|
||||||
import rehypeSanitize, { defaultSchema, type Options as RehypeSanitizeOptions } from 'rehype-sanitize';
|
|
||||||
import { SKIP, visit } from 'unist-util-visit';
|
import { SKIP, visit } from 'unist-util-visit';
|
||||||
import type { UnistNode, UnistParent } from 'node_modules/unist-util-visit/lib';
|
|
||||||
|
|
||||||
export const allowedHTMLElements = [
|
export const allowedHTMLElements = [
|
||||||
'a',
|
'a',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { WebContainer } from '@webcontainer/api';
|
import type { WebContainer } from '@webcontainer/api';
|
||||||
import type { ITerminal } from '~/types/terminal';
|
|
||||||
import { withResolvers } from './promises';
|
import { withResolvers } from './promises';
|
||||||
|
import type { ITerminal } from '~/types/terminal';
|
||||||
|
|
||||||
export async function newShellProcess(webcontainer: WebContainer, terminal: ITerminal) {
|
export async function newShellProcess(webcontainer: WebContainer, terminal: ITerminal) {
|
||||||
const args: string[] = [];
|
const args: string[] = [];
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { globSync } from 'fast-glob';
|
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
|
import { globSync } from 'fast-glob';
|
||||||
import { defineConfig, presetIcons, presetUno, transformerDirectives } from 'unocss';
|
import { defineConfig, presetIcons, presetUno, transformerDirectives } from 'unocss';
|
||||||
|
|
||||||
const iconPaths = globSync('./icons/*.svg');
|
const iconPaths = globSync('./icons/*.svg');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user