-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
basic editor #1
base: main
Are you sure you want to change the base?
basic editor #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,40 @@ | ||
'use client'; | ||
|
||
import { useState } from 'react'; | ||
import { useEffect, useState } from 'react'; | ||
import { | ||
EditorBubble, | ||
EditorContent, | ||
EditorRoot | ||
EditorRoot, | ||
type JSONContent, | ||
} from "novel"; | ||
import { defaultEditorContent } from '@/lib/content'; | ||
import { defaultExtensions } from '@/lib/extensions'; | ||
import { Input } from '@/components/ui/input'; | ||
import { Button } from '@/components/ui/button'; | ||
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; | ||
import { | ||
Sheet, | ||
SheetContent, | ||
SheetHeader, | ||
SheetTitle, | ||
SheetTrigger, | ||
} from '@/components/ui/sheet'; | ||
import { Menu } from 'lucide-react'; | ||
|
||
export default function Home() { | ||
const [repoUrl, setRepoUrl] = useState(''); | ||
const [isSidebarOpen, setIsSidebarOpen] = useState(false); | ||
|
||
const [initialContent, setInitialContent] = useState<null | JSONContent>(null); | ||
|
||
const extensions = [...defaultExtensions]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Extensions array recreated on every render. Move outside component or use useMemo |
||
|
||
useEffect(() => { | ||
const content = window.localStorage.getItem("novel-content"); | ||
if (content) setInitialContent(JSON.parse(content)); | ||
else setInitialContent(defaultEditorContent); | ||
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: JSON.parse() could throw if content is malformed. Wrap in try/catch to prevent runtime errors. |
||
}, []); | ||
|
||
if (!initialContent) return null; | ||
|
||
return ( | ||
<div className="flex flex-col h-screen"> | ||
{/* Navbar */} | ||
|
@@ -27,11 +47,10 @@ export default function Home() { | |
</Button> | ||
</SheetTrigger> | ||
<SheetContent side="left" className="w-64"> | ||
<SheetHeader> | ||
<SheetTitle>Repository History</SheetTitle> | ||
</SheetHeader> | ||
{/* Sidebar content */} | ||
<div className="flex flex-col gap-4 mt-8"> | ||
<h2 className="text-lg font-semibold">Repository History</h2> | ||
{/* Add repository history here */} | ||
</div> | ||
</SheetContent> | ||
</Sheet> | ||
<div className="flex items-center gap-2 flex-1 max-w-xl"> | ||
|
@@ -50,10 +69,10 @@ export default function Home() { | |
<main className="flex-1 p-4"> | ||
<EditorRoot> | ||
<EditorContent | ||
initialContent={[]} | ||
initialContent={initialContent} | ||
extensions={extensions} | ||
className="min-h-[500px] border rounded-lg" | ||
/> | ||
Comment on lines
71
to
75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: No save handler implemented - content changes won't persist to localStorage |
||
<EditorBubble /> | ||
</EditorRoot> | ||
</main> | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export const defaultEditorContent = { | ||
type: "doc", | ||
content: [ | ||
{ | ||
type: "paragraph", | ||
content: [ | ||
{ | ||
type: "text", | ||
text: "Text", | ||
}, | ||
], | ||
}, | ||
], | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* From https://novel.sh/docs/guides/tailwind/extensions */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Attribution comment should be removed since this is now part of the project's source code |
||
|
||
import { | ||
TiptapImage, | ||
TiptapLink, | ||
UpdatedImage, | ||
TaskList, | ||
TaskItem, | ||
HorizontalRule, | ||
StarterKit, | ||
Placeholder, | ||
} from "novel/extensions"; | ||
|
||
import { cx } from "class-variance-authority"; | ||
|
||
// TODO I am using cx here to get tailwind autocomplete working, idk if someone else can write a regex to just capture the class key in objects | ||
|
||
// You can overwrite the placeholder with your own configuration | ||
const placeholder = Placeholder; | ||
const tiptapLink = TiptapLink.configure({ | ||
HTMLAttributes: { | ||
class: cx( | ||
"text-muted-foreground underline underline-offset-[3px] hover:text-primary transition-colors cursor-pointer", | ||
), | ||
}, | ||
}); | ||
|
||
const taskList = TaskList.configure({ | ||
HTMLAttributes: { | ||
class: cx("not-prose pl-2"), | ||
}, | ||
}); | ||
const taskItem = TaskItem.configure({ | ||
HTMLAttributes: { | ||
class: cx("flex items-start my-4"), | ||
}, | ||
nested: true, | ||
}); | ||
|
||
const horizontalRule = HorizontalRule.configure({ | ||
HTMLAttributes: { | ||
class: cx("mt-4 mb-6 border-t border-muted-foreground"), | ||
}, | ||
}); | ||
|
||
const starterKit = StarterKit.configure({ | ||
bulletList: { | ||
HTMLAttributes: { | ||
class: cx("list-disc list-outside leading-3 -mt-2"), | ||
}, | ||
}, | ||
orderedList: { | ||
HTMLAttributes: { | ||
class: cx("list-decimal list-outside leading-3 -mt-2"), | ||
}, | ||
}, | ||
listItem: { | ||
HTMLAttributes: { | ||
class: cx("leading-normal -mb-2"), | ||
}, | ||
}, | ||
blockquote: { | ||
HTMLAttributes: { | ||
class: cx("border-l-4 border-primary"), | ||
}, | ||
}, | ||
codeBlock: { | ||
HTMLAttributes: { | ||
class: cx("rounded-sm bg-muted border p-5 font-mono font-medium"), | ||
}, | ||
}, | ||
code: { | ||
HTMLAttributes: { | ||
class: cx("rounded-md bg-muted px-1.5 py-1 font-mono font-medium"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. syntax: Extra space in class string between bg-muted and px-1.5 |
||
spellcheck: "false", | ||
}, | ||
}, | ||
horizontalRule: false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: horizontalRule disabled here but added back as separate extension - consider using one approach for consistency |
||
dropcursor: { | ||
color: "#DBEAFE", | ||
width: 4, | ||
}, | ||
gapcursor: false, | ||
}); | ||
|
||
export const defaultExtensions = [ | ||
starterKit, | ||
placeholder, | ||
tiptapLink, | ||
TiptapImage, | ||
UpdatedImage, | ||
taskList, | ||
taskItem, | ||
horizontalRule, | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: React v19 is not yet officially released. This could cause stability issues.