diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index f2418e6..0c88982 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -1,15 +1,14 @@ import type { Message } from 'ai'; import React, { type RefCallback } from 'react'; 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 { IconButton } from '~/components/ui/IconButton'; import { Workbench } from '~/components/workbench/Workbench.client'; 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 { textareaRef?: React.RefObject | undefined; @@ -208,7 +207,12 @@ export const BaseChat = React.forwardRef( {})} + onRemove={ + onRemoveFile || + function noop() { + /* no-op */ + } + } /> )} diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index 8a69a61..6c16407 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -4,6 +4,7 @@ import { useChat } from 'ai/react'; import { useAnimate } from 'framer-motion'; import { memo, useEffect, useRef, useState } from 'react'; import { cssTransition, toast, ToastContainer } from 'react-toastify'; +import { BaseChat } from './BaseChat'; import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks'; import { useChatHistory } from '~/lib/persistence'; import { chatStore } from '~/lib/stores/chat'; @@ -11,7 +12,6 @@ import { workbenchStore } from '~/lib/stores/workbench'; import { fileModificationsToHTML } from '~/utils/diff'; import { cubicEasingFn } from '~/utils/easings'; import { createScopedLogger, renderLogger } from '~/utils/logger'; -import { BaseChat } from './BaseChat'; const toastAnimation = cssTransition({ enter: 'animated fadeInRight', @@ -151,9 +151,10 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp const handleFileUpload = (fileList: FileList) => { const newFiles = Array.from(fileList); setFiles((prevFiles) => [...prevFiles, ...newFiles]); - + newFiles.forEach((file) => { const reader = new FileReader(); + reader.onloadend = () => { setImageDataList((prev) => [...prev, reader.result as string]); }; diff --git a/app/components/chat/CodeBlock.tsx b/app/components/chat/CodeBlock.tsx index 329e4cd..3a3dd70 100644 --- a/app/components/chat/CodeBlock.tsx +++ b/app/components/chat/CodeBlock.tsx @@ -1,10 +1,9 @@ import { memo, useEffect, useState } from 'react'; import { bundledLanguages, codeToHtml, isSpecialLang, type BundledLanguage, type SpecialLanguage } from 'shiki'; +import styles from './CodeBlock.module.scss'; import { classNames } from '~/utils/classNames'; import { createScopedLogger } from '~/utils/logger'; -import styles from './CodeBlock.module.scss'; - const logger = createScopedLogger('CodeBlock'); interface CodeBlockProps { diff --git a/app/components/chat/FilePreview.tsx b/app/components/chat/FilePreview.tsx index a569c75..0500d03 100644 --- a/app/components/chat/FilePreview.tsx +++ b/app/components/chat/FilePreview.tsx @@ -32,4 +32,4 @@ const FilePreview: React.FC = ({ files, imageDataList, onRemov ); }; -export default FilePreview; \ No newline at end of file +export default FilePreview; diff --git a/app/components/chat/Markdown.tsx b/app/components/chat/Markdown.tsx index a91df43..4aa421b 100644 --- a/app/components/chat/Markdown.tsx +++ b/app/components/chat/Markdown.tsx @@ -1,12 +1,12 @@ import { memo, useMemo } from 'react'; import ReactMarkdown, { type Components } from 'react-markdown'; import type { BundledLanguage } from 'shiki'; -import { createScopedLogger } from '~/utils/logger'; -import { rehypePlugins, remarkPlugins, allowedHTMLElements } from '~/utils/markdown'; import { Artifact } from './Artifact'; import { CodeBlock } from './CodeBlock'; import styles from './Markdown.module.scss'; +import { createScopedLogger } from '~/utils/logger'; +import { rehypePlugins, remarkPlugins, allowedHTMLElements } from '~/utils/markdown'; const logger = createScopedLogger('MarkdownComponent'); diff --git a/app/components/chat/Messages.client.tsx b/app/components/chat/Messages.client.tsx index b3ef32d..7a59e9c 100644 --- a/app/components/chat/Messages.client.tsx +++ b/app/components/chat/Messages.client.tsx @@ -1,8 +1,8 @@ import type { Message } from 'ai'; import React from 'react'; -import { classNames } from '~/utils/classNames'; import { AssistantMessage } from './AssistantMessage'; import { UserMessage } from './UserMessage'; +import { classNames } from '~/utils/classNames'; interface MessagesProps { id?: string; diff --git a/app/components/chat/UserMessage.tsx b/app/components/chat/UserMessage.tsx index 1e32611..de154ce 100644 --- a/app/components/chat/UserMessage.tsx +++ b/app/components/chat/UserMessage.tsx @@ -1,5 +1,5 @@ -import { modificationsRegex } from '~/utils/diff'; import { Markdown } from './Markdown'; +import { modificationsRegex } from '~/utils/diff'; interface MessageContent { type: string; @@ -18,7 +18,11 @@ export function UserMessage({ content }: UserMessageProps) {
{content.map((item, index) => { if (item.type === 'text') { - return {sanitizeUserMessage(item.text || '')}; + return ( + + {sanitizeUserMessage(item.text || '')} + + ); } else if (item.type === 'image') { return (
@@ -26,6 +30,7 @@ export function UserMessage({ content }: UserMessageProps) {
); } + return null; })}
diff --git a/app/components/editor/codemirror/CodeMirrorEditor.tsx b/app/components/editor/codemirror/CodeMirrorEditor.tsx index 8e9f3a3..96e4201 100644 --- a/app/components/editor/codemirror/CodeMirrorEditor.tsx +++ b/app/components/editor/codemirror/CodeMirrorEditor.tsx @@ -17,14 +17,14 @@ import { type Tooltip, } from '@codemirror/view'; 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 { getTheme, reconfigureTheme } from './cm-theme'; import { indentKeyBinding } from './indent'; 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'); diff --git a/app/components/editor/codemirror/cm-theme.ts b/app/components/editor/codemirror/cm-theme.ts index 6f3f363..fedda83 100644 --- a/app/components/editor/codemirror/cm-theme.ts +++ b/app/components/editor/codemirror/cm-theme.ts @@ -1,8 +1,8 @@ import { Compartment, type Extension } from '@codemirror/state'; import { EditorView } from '@codemirror/view'; import { vscodeDark, vscodeLight } from '@uiw/codemirror-theme-vscode'; -import type { Theme } from '~/types/theme.js'; import type { EditorSettings } from './CodeMirrorEditor.js'; +import type { Theme } from '~/types/theme.js'; export const darkTheme = EditorView.theme({}, { dark: true }); export const themeSelection = new Compartment(); diff --git a/app/components/header/Header.tsx b/app/components/header/Header.tsx index 15cf4bf..5bbebae 100644 --- a/app/components/header/Header.tsx +++ b/app/components/header/Header.tsx @@ -1,9 +1,9 @@ import { useStore } from '@nanostores/react'; import { ClientOnly } from 'remix-utils/client-only'; -import { chatStore } from '~/lib/stores/chat'; -import { classNames } from '~/utils/classNames'; import { HeaderActionButtons } from './HeaderActionButtons.client'; import { ChatDescription } from '~/lib/persistence/ChatDescription.client'; +import { chatStore } from '~/lib/stores/chat'; +import { classNames } from '~/utils/classNames'; export function Header() { const chat = useStore(chatStore); diff --git a/app/components/header/HeaderActionButtons.client.tsx b/app/components/header/HeaderActionButtons.client.tsx index af9fc6d..506eec8 100644 --- a/app/components/header/HeaderActionButtons.client.tsx +++ b/app/components/header/HeaderActionButtons.client.tsx @@ -1,7 +1,7 @@ +import { useStore } from '@nanostores/react'; import { chatStore } from '~/lib/stores/chat'; import { workbenchStore } from '~/lib/stores/workbench'; import { classNames } from '~/utils/classNames'; -import { useStore } from '@nanostores/react'; interface HeaderActionButtonsProps {} diff --git a/app/components/sidebar/HistoryItem.tsx b/app/components/sidebar/HistoryItem.tsx index 60e642a..4bae4e3 100644 --- a/app/components/sidebar/HistoryItem.tsx +++ b/app/components/sidebar/HistoryItem.tsx @@ -22,6 +22,7 @@ export function HistoryItem({ item, onDelete, onRename, onExport }: HistoryItemP function mouseEnter() { setHovering(true); + if (timeout) { clearTimeout(timeout); } diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index eb4826c..786ed09 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -1,14 +1,14 @@ +import { saveAs } from 'file-saver'; import { motion, type Variants } from 'framer-motion'; import { useCallback, useEffect, useRef, useState } from 'react'; 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 { ThemeSwitch } from '~/components/ui/ThemeSwitch'; import { db, deleteById, getAll, chatId, type ChatHistoryItem, setMessages } from '~/lib/persistence'; import { cubicEasingFn } from '~/utils/easings'; import { logger } from '~/utils/logger'; -import { HistoryItem } from './HistoryItem'; -import { binDates } from './date-binning'; const menuVariants = { closed: { @@ -68,22 +68,26 @@ export function Menu() { } }, []); - const renameItem = useCallback((id: string, newDescription: string) => { - if (db) { - const item = list.find((item) => item.id === id); - if (item) { - setMessages(db, id, item.messages, item.urlId, newDescription) - .then(() => { - loadEntries(); - toast.success('Chat renamed successfully'); - }) - .catch((error) => { - toast.error('Failed to rename chat'); - logger.error(error); - }); + const renameItem = useCallback( + (id: string, newDescription: string) => { + if (db) { + const item = list.find((item) => item.id === id); + + if (item) { + setMessages(db, id, item.messages, item.urlId, newDescription) + .then(() => { + loadEntries(); + toast.success('Chat renamed successfully'); + }) + .catch((error) => { + toast.error('Failed to rename chat'); + logger.error(error); + }); + } } - } - }, [list]); + }, + [list], + ); const exportItem = useCallback((item: ChatHistoryItem) => { try { diff --git a/app/components/ui/Dialog.tsx b/app/components/ui/Dialog.tsx index a808c77..ad5e9f8 100644 --- a/app/components/ui/Dialog.tsx +++ b/app/components/ui/Dialog.tsx @@ -1,9 +1,9 @@ import * as RadixDialog from '@radix-ui/react-dialog'; import { motion, type Variants } from 'framer-motion'; import React, { memo, type ReactNode } from 'react'; +import { IconButton } from './IconButton'; import { classNames } from '~/utils/classNames'; import { cubicEasingFn } from '~/utils/easings'; -import { IconButton } from './IconButton'; export { Close as DialogClose, Root as DialogRoot } from '@radix-ui/react-dialog'; diff --git a/app/components/ui/IconButton.tsx b/app/components/ui/IconButton.tsx index 94cc6d4..9d58f58 100644 --- a/app/components/ui/IconButton.tsx +++ b/app/components/ui/IconButton.tsx @@ -64,15 +64,23 @@ export const IconButton = memo( function getIconSize(size: IconSize) { switch (size) { - case 'sm': + case 'sm': { return 'text-sm'; - case 'md': + } + case 'md': { return 'text-base'; - case 'lg': + } + case 'lg': { return 'text-lg'; - case 'xl': + } + case 'xl': { return 'text-xl'; - case 'xxl': + } + case 'xxl': { return 'text-2xl'; + } + default: { + return 'text-base'; + } } } diff --git a/app/components/ui/ThemeSwitch.tsx b/app/components/ui/ThemeSwitch.tsx index a46da2b..559e539 100644 --- a/app/components/ui/ThemeSwitch.tsx +++ b/app/components/ui/ThemeSwitch.tsx @@ -1,7 +1,7 @@ import { useStore } from '@nanostores/react'; import { memo, useEffect, useState } from 'react'; -import { themeStore, toggleTheme } from '~/lib/stores/theme'; import { IconButton } from './IconButton'; +import { themeStore, toggleTheme } from '~/lib/stores/theme'; interface ThemeSwitchProps { className?: string; diff --git a/app/components/workbench/EditorPanel.tsx b/app/components/workbench/EditorPanel.tsx index d1a265a..a6248e5 100644 --- a/app/components/workbench/EditorPanel.tsx +++ b/app/components/workbench/EditorPanel.tsx @@ -1,6 +1,9 @@ import { useStore } from '@nanostores/react'; import { memo, useEffect, useMemo, useRef, useState } from 'react'; 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 { CodeMirrorEditor, type EditorDocument, @@ -20,9 +23,6 @@ import { classNames } from '~/utils/classNames'; import { WORK_DIR } from '~/utils/constants'; import { renderLogger } from '~/utils/logger'; import { isMobile } from '~/utils/mobile'; -import { FileBreadcrumb } from './FileBreadcrumb'; -import { FileTree } from './FileTree'; -import { Terminal, type TerminalRef } from './terminal/Terminal'; interface EditorPanelProps { files?: FileMap; diff --git a/app/components/workbench/FileBreadcrumb.tsx b/app/components/workbench/FileBreadcrumb.tsx index 0ffbb2f..1f7434d 100644 --- a/app/components/workbench/FileBreadcrumb.tsx +++ b/app/components/workbench/FileBreadcrumb.tsx @@ -1,12 +1,12 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import { AnimatePresence, motion, type Variants } from 'framer-motion'; import { memo, useEffect, useRef, useState } from 'react'; +import FileTree from './FileTree'; import type { FileMap } from '~/lib/stores/files'; import { classNames } from '~/utils/classNames'; import { WORK_DIR } from '~/utils/constants'; import { cubicEasingFn } from '~/utils/easings'; import { renderLogger } from '~/utils/logger'; -import FileTree from './FileTree'; const WORK_DIR_REGEX = new RegExp(`^${WORK_DIR.split('/').slice(0, -1).join('/').replaceAll('/', '\\/')}/`); diff --git a/app/components/workbench/Preview.tsx b/app/components/workbench/Preview.tsx index 4c1877c..30a3bf8 100644 --- a/app/components/workbench/Preview.tsx +++ b/app/components/workbench/Preview.tsx @@ -1,8 +1,8 @@ import { useStore } from '@nanostores/react'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; +import { PortDropdown } from './PortDropdown'; import { IconButton } from '~/components/ui/IconButton'; import { workbenchStore } from '~/lib/stores/workbench'; -import { PortDropdown } from './PortDropdown'; export const Preview = memo(() => { const iframeRef = useRef(null); diff --git a/app/components/workbench/Workbench.client.tsx b/app/components/workbench/Workbench.client.tsx index 8d3200d..f775aa9 100644 --- a/app/components/workbench/Workbench.client.tsx +++ b/app/components/workbench/Workbench.client.tsx @@ -1,10 +1,12 @@ import { useStore } from '@nanostores/react'; +import { saveAs } from 'file-saver'; import { motion, type HTMLMotionProps, type Variants } from 'framer-motion'; +import JSZip from 'jszip'; import { computed } from 'nanostores'; import { memo, useCallback, useEffect } from 'react'; import { toast } from 'react-toastify'; -import JSZip from 'jszip'; -import { saveAs } from 'file-saver'; +import { EditorPanel } from './EditorPanel'; +import { Preview } from './Preview'; import { type OnChangeCallback as OnEditorChange, type OnScrollCallback as OnEditorScroll, @@ -16,8 +18,6 @@ import { workbenchStore, type WorkbenchViewType } from '~/lib/stores/workbench'; import { classNames } from '~/utils/classNames'; import { cubicEasingFn } from '~/utils/easings'; import { renderLogger } from '~/utils/logger'; -import { EditorPanel } from './EditorPanel'; -import { Preview } from './Preview'; interface WorkspaceProps { chatStarted?: boolean; @@ -155,10 +155,7 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
{selectedView === 'code' && ( <> - +
Download diff --git a/app/components/workbench/terminal/Terminal.tsx b/app/components/workbench/terminal/Terminal.tsx index b9b84dc..790aed5 100644 --- a/app/components/workbench/terminal/Terminal.tsx +++ b/app/components/workbench/terminal/Terminal.tsx @@ -2,9 +2,9 @@ import { FitAddon } from '@xterm/addon-fit'; import { WebLinksAddon } from '@xterm/addon-web-links'; import { Terminal as XTerm } from '@xterm/xterm'; import { forwardRef, memo, useEffect, useImperativeHandle, useRef } from 'react'; +import { getTerminalTheme } from './theme'; import type { Theme } from '~/lib/stores/theme'; import { createScopedLogger } from '~/utils/logger'; -import { getTerminalTheme } from './theme'; const logger = createScopedLogger('Terminal'); diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index d9febf5..eb999d6 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -1,8 +1,8 @@ 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 { getSystemPrompt } from './prompts'; +import { getAPIKey } from '~/lib/.server/llm/api-key'; +import { getAnthropicModel } from '~/lib/.server/llm/model'; interface ToolResult { toolCallId: string; diff --git a/app/lib/persistence/db.ts b/app/lib/persistence/db.ts index 7a952e3..9fc989f 100644 --- a/app/lib/persistence/db.ts +++ b/app/lib/persistence/db.ts @@ -1,6 +1,6 @@ import type { Message } from 'ai'; -import { createScopedLogger } from '~/utils/logger'; import type { ChatHistoryItem } from './useChatHistory'; +import { createScopedLogger } from '~/utils/logger'; const logger = createScopedLogger('ChatHistory'); diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index e562753..3afa401 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -1,10 +1,10 @@ import { useLoaderData, useNavigate } from '@remix-run/react'; -import { useState, useEffect } from 'react'; -import { atom } from 'nanostores'; import type { Message } from 'ai'; +import { atom } from 'nanostores'; +import { useState, useEffect } from 'react'; import { toast } from 'react-toastify'; -import { workbenchStore } from '~/lib/stores/workbench'; import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db'; +import { workbenchStore } from '~/lib/stores/workbench'; export interface ChatHistoryItem { id: string; diff --git a/app/lib/runtime/action-runner.ts b/app/lib/runtime/action-runner.ts index e2ea6a2..838c26e 100644 --- a/app/lib/runtime/action-runner.ts +++ b/app/lib/runtime/action-runner.ts @@ -1,10 +1,10 @@ +import * as nodePath from 'node:path'; import { WebContainer } from '@webcontainer/api'; 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 { createScopedLogger } from '~/utils/logger'; import { unreachable } from '~/utils/unreachable'; -import type { ActionCallbackData } from './message-parser'; const logger = createScopedLogger('ActionRunner'); diff --git a/app/lib/stores/editor.ts b/app/lib/stores/editor.ts index ff3b337..e063d9b 100644 --- a/app/lib/stores/editor.ts +++ b/app/lib/stores/editor.ts @@ -1,6 +1,6 @@ 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 { EditorDocument, ScrollPosition } from '~/components/editor/codemirror/CodeMirrorEditor'; export type EditorDocuments = Record; diff --git a/app/lib/stores/files.ts b/app/lib/stores/files.ts index 663ae58..aa2de74 100644 --- a/app/lib/stores/files.ts +++ b/app/lib/stores/files.ts @@ -1,8 +1,8 @@ +import { Buffer } from 'node:buffer'; +import * as nodePath from 'node:path'; import type { PathWatcherEvent, WebContainer } from '@webcontainer/api'; import { getEncoding } from 'istextorbinary'; import { map, type MapStore } from 'nanostores'; -import { Buffer } from 'node:buffer'; -import * as nodePath from 'node:path'; import { bufferWatchEvents } from '~/utils/buffer'; import { WORK_DIR } from '~/utils/constants'; import { computeFileModifications } from '~/utils/diff'; diff --git a/app/lib/stores/workbench.ts b/app/lib/stores/workbench.ts index 4040de1..cb48d2e 100644 --- a/app/lib/stores/workbench.ts +++ b/app/lib/stores/workbench.ts @@ -1,14 +1,14 @@ 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 { ActionRunner } from '~/lib/runtime/action-runner'; import type { ActionCallbackData, ArtifactCallbackData } from '~/lib/runtime/message-parser'; import { webcontainer } from '~/lib/webcontainer'; import type { ITerminal } from '~/types/terminal'; 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 { id: string; diff --git a/app/root.tsx b/app/root.tsx index 31eb387..468fc0a 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -2,14 +2,13 @@ import { useStore } from '@nanostores/react'; import type { LinksFunction } from '@remix-run/cloudflare'; import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'; 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 { 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'; diff --git a/app/routes/api.enhancer.ts b/app/routes/api.enhancer.ts index 3773487..850367a 100644 --- a/app/routes/api.enhancer.ts +++ b/app/routes/api.enhancer.ts @@ -1,5 +1,4 @@ import { type ActionFunctionArgs } from '@remix-run/cloudflare'; -import { createDataStream, createDataStreamResponse } from 'ai'; import { streamText } from '~/lib/.server/llm/stream-text'; import { stripIndents } from '~/utils/stripIndent'; @@ -36,21 +35,23 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) { transform(chunk, controller) { const text = decoder.decode(chunk); const lines = text.split('\n'); - + for (const line of lines) { if (line.startsWith('0:')) { - // Extract only the content after "0:" + // extract only the content after "0:" const content = line.slice(2); 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); + return new Response(stream, { - headers: { 'Content-Type': 'text/plain; charset=utf-8' } + headers: { 'Content-Type': 'text/plain; charset=utf-8' }, }); } catch (error) { console.log(error); diff --git a/app/utils/diff.ts b/app/utils/diff.ts index 66c0e15..8fc2e91 100644 --- a/app/utils/diff.ts +++ b/app/utils/diff.ts @@ -1,6 +1,6 @@ import { createTwoFilesPatch } from 'diff'; -import type { FileMap } from '~/lib/stores/files'; import { MODIFICATIONS_TAG_NAME } from './constants'; +import type { FileMap } from '~/lib/stores/files'; export const modificationsRegex = new RegExp( `^<${MODIFICATIONS_TAG_NAME}>[\\s\\S]*?<\\/${MODIFICATIONS_TAG_NAME}>\\s+`, diff --git a/app/utils/logger.ts b/app/utils/logger.ts index 1a5c932..9b2c31c 100644 --- a/app/utils/logger.ts +++ b/app/utils/logger.ts @@ -11,7 +11,7 @@ interface Logger { 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 supportsColor = !isWorker; diff --git a/app/utils/markdown.ts b/app/utils/markdown.ts index 4409b85..891c7b0 100644 --- a/app/utils/markdown.ts +++ b/app/utils/markdown.ts @@ -1,9 +1,9 @@ +import type { UnistNode, UnistParent } from 'node_modules/unist-util-visit/lib'; import rehypeRaw from 'rehype-raw'; +import rehypeSanitize, { defaultSchema, type Options as RehypeSanitizeOptions } from 'rehype-sanitize'; import remarkGfm from 'remark-gfm'; import type { PluggableList, Plugin } from 'unified'; -import rehypeSanitize, { defaultSchema, type Options as RehypeSanitizeOptions } from 'rehype-sanitize'; import { SKIP, visit } from 'unist-util-visit'; -import type { UnistNode, UnistParent } from 'node_modules/unist-util-visit/lib'; export const allowedHTMLElements = [ 'a', diff --git a/app/utils/shell.ts b/app/utils/shell.ts index 1c5c834..f5df106 100644 --- a/app/utils/shell.ts +++ b/app/utils/shell.ts @@ -1,6 +1,6 @@ import type { WebContainer } from '@webcontainer/api'; -import type { ITerminal } from '~/types/terminal'; import { withResolvers } from './promises'; +import type { ITerminal } from '~/types/terminal'; export async function newShellProcess(webcontainer: WebContainer, terminal: ITerminal) { const args: string[] = []; diff --git a/uno.config.ts b/uno.config.ts index 503e1af..7705d3e 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -1,6 +1,6 @@ -import { globSync } from 'fast-glob'; import fs from 'node:fs/promises'; import { basename } from 'node:path'; +import { globSync } from 'fast-glob'; import { defineConfig, presetIcons, presetUno, transformerDirectives } from 'unocss'; const iconPaths = globSync('./icons/*.svg');