Skip to content
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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"clsx": "^2.1.1",
"lucide-react": "^0.469.0",
"next": "15.1.2",
"novel": "^0.5.0",
"novel": "0.5.0",
"react": "^19.0.0",
Copy link

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.

"react-dom": "^19.0.0",
"tailwind-merge": "^2.6.0",
Expand All @@ -33,6 +33,6 @@
"eslint-config-next": "15.1.2",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
"typescript": "^5.7.2"
}
}
39 changes: 29 additions & 10 deletions src/app/page.tsx
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];
Copy link

Choose a reason for hiding this comment

The 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
Copy link

Choose a reason for hiding this comment

The 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 */}
Expand All @@ -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">
Expand All @@ -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
Copy link

Choose a reason for hiding this comment

The 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>
Expand Down
14 changes: 14 additions & 0 deletions src/lib/content.ts
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",
},
],
},
],
};
95 changes: 95 additions & 0 deletions src/lib/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* From https://novel.sh/docs/guides/tailwind/extensions */
Copy link

Choose a reason for hiding this comment

The 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"),
Copy link

Choose a reason for hiding this comment

The 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,
Copy link

Choose a reason for hiding this comment

The 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,
];