Skip to content

Commit

Permalink
Document Editor page
Browse files Browse the repository at this point in the history
  • Loading branch information
csansoon committed Jul 23, 2024
1 parent 67e2613 commit 9339d63
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 34 deletions.
25 changes: 25 additions & 0 deletions apps/web/src/actions/documents/updateContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use server'

import { updateDocument } from '@latitude-data/core'
import { z } from 'zod'

import { withProject } from '../procedures'

export const updateDocumentContentAction = withProject
.createServerAction()
.input(
z.object({
documentUuid: z.string(),
commitId: z.number(),
content: z.string(),
}),
{ type: 'json' },
)
.handler(async ({ input }) => {
const result = await updateDocument({
commitId: input.commitId,
documentUuid: input.documentUuid,
content: input.content,
})
return result.unwrap()
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client'

import { Suspense, useCallback } from 'react'

import { Commit, DocumentVersion } from '@latitude-data/core'
import { DocumentEditor, useToast } from '@latitude-data/web-ui'
import { updateDocumentContentAction } from '$/actions/documents/updateContent'
import { useServerAction } from 'zsa-react'

export default function ClientDocumentEditor({
commit,
document,
}: {
commit: Commit
document: DocumentVersion
}) {
const updateDocumentAction = useServerAction(updateDocumentContentAction)
const { toast } = useToast()

const saveDocumentContent = useCallback(
async (content: string) => {
const [_, error] = await updateDocumentAction.execute({
projectId: commit.projectId,
documentUuid: document.documentUuid,
commitId: commit.id,
content,
})

if (error) {
toast({
title: 'Could not save document',
description: error.message,
variant: 'destructive',
})
}
},
[commit, document, updateDocumentAction, toast],
)

return (
<Suspense fallback={<div>Loading...</div>}>
<DocumentEditor
document={document.content}
saveDocumentContent={saveDocumentContent}
/>
</Suspense>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default async function DocumentLayout({
documentUuid={params.documentUuid}
documentPath={document.path}
/>
<div className='p-32'>{children}</div>
{children}
</DocumentDetailWrapper>
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
export default function DocumentPage({
import { findCommit, getDocumentByUuid } from '$/app/(private)/_data-access'

import ClientDocumentEditor from './_components/DocumentEditor'

export default async function DocumentPage({
params,
}: {
params: { projectId: string; commitUuid: string; documentUuid: string }
}) {
return <div>Documents {params.documentUuid}</div>
const commit = await findCommit({
projectId: Number(params.projectId),
uuid: params.commitUuid,
})
const document = await getDocumentByUuid({
documentUuid: params.documentUuid,
commitId: commit.id,
})
return <ClientDocumentEditor commit={commit} document={document} />
}
1 change: 1 addition & 0 deletions packages/web-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"react-dom": "18.3.0",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
"use-debounce": "^10.0.1",
"zod": "^3.23.8",
"zustand": "^4.5.4"
},
Expand Down
48 changes: 31 additions & 17 deletions packages/web-ui/src/ds/molecules/DocumentTextEditor/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function DocumentTextEditor({
value,
metadata,
onChange,
readOnlyMessage,
}: DocumentTextEditorProps) {
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null)
const monacoRef = useRef<Monaco | null>(null)
Expand Down Expand Up @@ -82,23 +83,36 @@ export function DocumentTextEditor({
}

return (
<div className='flex flex-col relative h-full w-full rounded-lg border border-border overflow-hidden'>
<Editor
height='100%'
width='100%'
theme='latitude'
language='document'
defaultValue={value}
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
onChange={handleValueChange}
options={{
lineNumbers: 'off',
minimap: {
enabled: false,
},
}}
/>
<div className='relative h-full rounded-lg border border-border overflow-hidden'>
<div className='absolute top-0 left-0 right-0 bottom-0'>
<Editor
height='100%'
width='100%'
theme='latitude'
language='document'
defaultValue={value}
beforeMount={handleEditorWillMount}
onMount={handleEditorDidMount}
onChange={handleValueChange}
options={{
lineDecorationsWidth: 0,
padding: {
top: 16,
bottom: 16,
},
lineNumbers: 'off',
minimap: {
enabled: false,
},
cursorSmoothCaretAnimation: 'on',
wordWrap: 'on',
readOnly: !!readOnlyMessage,
readOnlyMessage: readOnlyMessage
? { value: readOnlyMessage, supportHtml: true }
: undefined,
}}
/>
</div>
</div>
)
}
13 changes: 11 additions & 2 deletions packages/web-ui/src/ds/molecules/DocumentTextEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import React, { lazy } from 'react'
import React, { lazy, useEffect, useState } from 'react'

import type { ConversationMetadata } from '@latitude-data/compiler'

Expand All @@ -10,6 +10,7 @@ export type DocumentTextEditorProps = {
value: string
metadata?: ConversationMetadata
onChange?: (value: string) => void
readOnlyMessage?: string
}

const DocumentTextEditor = lazy(() =>
Expand All @@ -25,7 +26,15 @@ function EditorWrapper(props: DocumentTextEditorProps) {
// When imported, Monaco automatically tries to use the window object.
// Since this is not available when rendering on the server, we only
// render the fallback component for SSR.
if (typeof window === 'undefined') return <DocumentTextEditorFallback />
const [isClient, setIsClient] = useState(false)

useEffect(() => {
setIsClient(typeof window !== 'undefined')
}, [])

if (!isClient) {
return <DocumentTextEditorFallback />
}
return <DocumentTextEditor {...props} />
}

Expand Down
7 changes: 1 addition & 6 deletions packages/web-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,4 @@ export * from './ds/tokens'
export * from './ds/atoms'
export * from './ds/molecules'
export * from './providers'

export { default as DocumentSidebar } from './sections/Document/Sidebar'
export type { SidebarDocument } from './sections/Document/Sidebar/Files/useTree'

export { default as DocumentDetailWrapper } from './sections/Document/DetailWrapper'
export * from './sections/Document/Sidebar/Files'
export * from './sections'
31 changes: 25 additions & 6 deletions packages/web-ui/src/sections/Document/Editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
'use client'

import { ReactNode, Suspense, useEffect, useMemo, useState } from 'react'
import { ReactNode, Suspense, useCallback, useMemo, useState } from 'react'

import { ConversationMetadata, readMetadata } from '@latitude-data/compiler'
import { Badge, Input, Text } from '$ui/ds/atoms'
import {
DocumentTextEditor,
DocumentTextEditorFallback,
} from '$ui/ds/molecules'
import { useCurrentCommit } from '$ui/providers'
import { useDebouncedCallback } from 'use-debounce'

function Header({ title, children }: { title: string; children?: ReactNode }) {
return (
Expand All @@ -18,26 +20,43 @@ function Header({ title, children }: { title: string; children?: ReactNode }) {
)
}

export function DocumentEditor({ document }: { document: string }) {
export default function DocumentEditor({
document,
saveDocumentContent,
}: {
document: string
saveDocumentContent: (content: string) => void
}) {
const [value, setValue] = useState(document)
const [metadata, setMetadata] = useState<ConversationMetadata>()
useEffect(() => {

const { commit } = useCurrentCommit()

const debouncedSave = useDebouncedCallback(saveDocumentContent, 2_000)

const onChange = useCallback((value: string) => {
setValue(value)
readMetadata({ prompt: value }).then(setMetadata)
}, [value])
debouncedSave(value)
}, [])

const inputs = useMemo(() => {
if (!metadata) return []
return Array.from(metadata.parameters)
}, [metadata])

return (
<div className='flex flex-row w-full h-full gap-8'>
<div className='flex flex-row w-full h-full gap-8 p-6'>
<div className='flex flex-col flex-1 flex-grow flex-shrink gap-2 min-w-0'>
<Header title='Prompt editor' />
<Suspense fallback={<DocumentTextEditorFallback />}>
<DocumentTextEditor
value={value}
metadata={metadata}
onChange={setValue}
onChange={onChange}
readOnlyMessage={
commit.mergedAt ? 'Create a draft to edit documents' : undefined
}
/>
</Suspense>
</div>
Expand Down
7 changes: 7 additions & 0 deletions packages/web-ui/src/sections/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { default as DocumentSidebar } from './Document/Sidebar'
export type { SidebarDocument } from './Document/Sidebar/Files/useTree'

export { default as DocumentDetailWrapper } from './Document/DetailWrapper'
export * from './Document/Sidebar/Files'

export { default as DocumentEditor } from './Document/Editor'
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9339d63

Please sign in to comment.