+ {/* Document header (editable, first page) */}
+ {showHeader && (
+
{
const pageNumber = index + 1;
- // Each overlay sits at: header + N page-content-heights + previous overlays
+ // Each overlay sits at: first page header + document header + N page-content-heights + previous overlays
const topPosition =
HEADER_HEIGHT +
+ headerHeight +
PAGE_CONTENT_HEIGHT * pageNumber +
- index * OVERLAY_HEIGHT;
+ index * effectiveOverlayHeight;
return (
{/* Gap between pages */}
-
+
{/* Header of next page */}
+
+ {/* Document header (read-only copy for subsequent pages) */}
+ {showHeader && documentHeaderHtml && (
+
+ )}
);
})}
diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx
index 65cbd22..a2aa239 100644
--- a/src/components/Toolbar.tsx
+++ b/src/components/Toolbar.tsx
@@ -22,6 +22,7 @@ import {
Link,
ImageIcon,
MinusIcon,
+ PanelTop,
} from "lucide-react";
import ColorPicker from "./ColorPicker";
import LinkPopover from "./LinkPopover";
@@ -57,6 +58,8 @@ type PopoverName =
interface ToolbarProps {
editor: Editor;
+ isHeaderVisible: boolean;
+ onToggleHeader: () => void;
}
function ToolbarButton({
@@ -94,9 +97,17 @@ function ToolbarDivider() {
return
;
}
-function Toolbar({ editor }: ToolbarProps) {
+function Toolbar({ editor, isHeaderVisible, onToggleHeader }: ToolbarProps) {
const [activePopover, setActivePopover] = useState
(null);
+ // Check which node types are available in the active editor's schema
+ const hasHeading = !!editor.schema.nodes.heading;
+ const hasListItem = !!editor.schema.nodes.listItem;
+ const hasBulletList = !!editor.schema.nodes.bulletList;
+ const hasOrderedList = !!editor.schema.nodes.orderedList;
+ const hasHorizontalRule = !!editor.schema.nodes.horizontalRule;
+ const hasImage = !!editor.schema.nodes.image;
+
const togglePopover = useCallback(
(name: PopoverName) => {
setActivePopover((current) => (current === name ? null : name));
@@ -198,17 +209,18 @@ function Toolbar({ editor }: ToolbarProps) {
- {activePopover === "heading" && (
+ {activePopover === "heading" && hasHeading && (
editor.chain().focus().toggleBulletList().run()}
- isActive={editor.isActive("bulletList")}
+ isActive={hasBulletList && editor.isActive("bulletList")}
+ disabled={!hasBulletList}
>
editor.chain().focus().toggleOrderedList().run()}
- isActive={editor.isActive("orderedList")}
+ isActive={hasOrderedList && editor.isActive("orderedList")}
+ disabled={!hasOrderedList}
>
@@ -465,14 +479,14 @@ function Toolbar({ editor }: ToolbarProps) {
editor.chain().focus().liftListItem("listItem").run()}
- disabled={!editor.can().liftListItem("listItem")}
+ disabled={!hasListItem || !editor.can().liftListItem("listItem")}
>
editor.chain().focus().sinkListItem("listItem").run()}
- disabled={!editor.can().sinkListItem("listItem")}
+ disabled={!hasListItem || !editor.can().sinkListItem("listItem")}
>
@@ -501,10 +515,11 @@ function Toolbar({ editor }: ToolbarProps) {
togglePopover("image")}
+ disabled={!hasImage}
>
- {activePopover === "image" && (
+ {activePopover === "image" && hasImage && (
editor.chain().focus().setHorizontalRule().run()}
+ disabled={!hasHorizontalRule}
>
+
+
+
+ {/* Toggle Document Header */}
+
+
+
>
);
diff --git a/src/index.css b/src/index.css
index a8cdcc1..6c46d80 100644
--- a/src/index.css
+++ b/src/index.css
@@ -60,3 +60,20 @@
html {
margin: 0px;
}
+
+/* Document header editor styles */
+.document-header .tiptap {
+ outline: none;
+ font-family: Arial, sans-serif;
+ font-size: 9pt;
+ line-height: 1.4;
+ color: #555;
+}
+
+.document-header .tiptap p.is-editor-empty:first-child::before {
+ content: attr(data-placeholder);
+ float: left;
+ color: #adb5bd;
+ pointer-events: none;
+ height: 0;
+}