diff --git a/src-web/components/CreateWorkspaceDialog.tsx b/src-web/components/CreateWorkspaceDialog.tsx index f282801b..e1b4791e 100644 --- a/src-web/components/CreateWorkspaceDialog.tsx +++ b/src-web/components/CreateWorkspaceDialog.tsx @@ -1,10 +1,10 @@ import { useState } from 'react'; -import {createWorkspace} from "../lib/commands"; +import { createWorkspace } from '../lib/commands'; import { Button } from './core/Button'; import { PlainInput } from './core/PlainInput'; import { VStack } from './core/Stacks'; -import { MarkdownEditor } from './MarkdownEditor'; -import { SelectFile } from './SelectFile'; +import type { SyncToFilesystemSettingProps } from './SyncToFilesystemSetting'; +import { SyncToFilesystemSetting } from './SyncToFilesystemSetting'; interface Props { hide: () => void; @@ -12,8 +12,9 @@ interface Props { export function CreateWorkspaceDialog({ hide }: Props) { const [name, setName] = useState(''); - const [description, setDescription] = useState(''); - const [settingSyncDir, setSettingSyncDir] = useState(null); + const [settingSyncDir, setSettingSyncDir] = useState< + Parameters[0] + >({ value: null, enabled: false }); return ( { e.preventDefault(); - await createWorkspace.mutateAsync({ name, description, settingSyncDir }); + const { enabled, value } = settingSyncDir ?? {}; + if (enabled && !value) return; + await createWorkspace.mutateAsync({ name, settingSyncDir: value }); hide(); }} > - - -
- setSettingSyncDir(filePath)} - /> -
- + +
); } diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index 4dec3481..469c9d44 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -31,17 +31,18 @@ export function GlobalHooks() { useNotificationToast(); useActiveWorkspaceChangedToast(); - // Listen for toasts - useListenToTauriEvent('show_toast', (event) => { - showToast({ ...event.payload }); - }); - // Trigger workspace sync operation when workspace files change const activeWorkspace = useActiveWorkspace(); const { debouncedSync } = useSyncWorkspace(activeWorkspace, { debounceMillis: 1000 }); useListenToTauriEvent('upserted_model', debouncedSync); useWatchWorkspace(activeWorkspace, debouncedSync); + // Listen for toasts + useListenToTauriEvent('show_toast', (event) => { + showToast({ ...event.payload }); + }); + + // Listen for prompts useListenToTauriEvent<{ replyId: string; args: PromptTextRequest }>( 'show_prompt', async (event) => { diff --git a/src-web/components/MarkdownEditor.tsx b/src-web/components/MarkdownEditor.tsx index c277b5ad..4cc89de1 100644 --- a/src-web/components/MarkdownEditor.tsx +++ b/src-web/components/MarkdownEditor.tsx @@ -1,51 +1,51 @@ import classNames from 'classnames'; -import { useRef } from 'react'; +import { atom, useAtom } from 'jotai'; +import { useRef, useState } from 'react'; import Markdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; -import { useContainerSize } from '../hooks/useContainerQuery'; -import { useKeyValue } from '../hooks/useKeyValue'; +import { Button } from './core/Button'; import type { EditorProps } from './core/Editor/Editor'; import { Editor } from './core/Editor/Editor'; -import { IconButton } from './core/IconButton'; -import { SplitLayout } from './core/SplitLayout'; -import { VStack } from './core/Stacks'; +import { HStack, VStack } from './core/Stacks'; import { Prose } from './Prose'; +type ViewMode = 'edit' | 'preview'; + interface Props extends Pick { placeholder: string; className?: string; defaultValue: string; onChange: (value: string) => void; name: string; + defaultMode?: ViewMode; + doneButtonLabel?: string; } -export function MarkdownEditor({ className, defaultValue, onChange, name, ...editorProps }: Props) { +const viewModeAtom = atom>({}); + +export function MarkdownEditor({ + className, + defaultValue, + onChange, + name, + defaultMode = 'preview', + doneButtonLabel = 'Save', + ...editorProps +}: Props) { const containerRef = useRef(null); - - const { width } = useContainerSize(containerRef); - const wideEnoughForSplit = width > 600; - - const { set: setViewMode, value: rawViewMode } = useKeyValue<'edit' | 'preview' | 'both'>({ - namespace: 'global', - key: ['md_view', name], - fallback: 'edit', - }); - - if (rawViewMode == null) return null; - - let viewMode = rawViewMode; - if (rawViewMode === 'both' && !wideEnoughForSplit) { - viewMode = 'edit'; - } + const [rawViewMode, setViewMode] = useAtom(viewModeAtom); + const viewMode = rawViewMode[name] ?? defaultMode; + const [value, setValue] = useState(defaultValue); const editor = ( ); @@ -70,28 +70,12 @@ export function MarkdownEditor({ className, defaultValue, onChange, name, ...edi }, }} > - {defaultValue} + {value} ); - const contents = - viewMode === 'both' ? ( -
{editor}
} - secondSlot={({ style }) => ( -
- {preview} -
- )} - /> - ) : viewMode === 'preview' ? ( - preview - ) : ( - editor - ); + const contents = viewMode === 'preview' ? preview : editor; return (
- setViewMode('edit')} - /> - {wideEnoughForSplit && ( - setViewMode('both')} - /> + onClick={() => setViewMode((prev) => ({ ...prev, [name]: 'edit' }))} + > + Edit + + )} + {viewMode === 'edit' && ( + + + + )} - setViewMode('preview')} - />
); diff --git a/src-web/components/SelectFile.tsx b/src-web/components/SelectFile.tsx index cda485b0..2e21ec01 100644 --- a/src-web/components/SelectFile.tsx +++ b/src-web/components/SelectFile.tsx @@ -46,9 +46,9 @@ export function SelectFile({ const selectOrChange = (filePath ? 'Change ' : 'Select ') + itemLabel; return ( - +