// TipTap-backed narrative editor. // Renders the same prose surface as the static port, but typing actually works // and chips can be inserted via the slash menu (`/block`, `/property`, etc.). "use client"; import { useEditor, EditorContent } from "@tiptap/react"; import StarterKit from "@tiptap/starter-kit"; import { useEffect, useMemo } from "react"; import { ChipNode } from "./ChipNode"; import { SlashExtension } from "./SlashExtension"; import { ChipFocusContext } from "./FocusContext"; import { fixtureToDoc } from "./fixtureToDoc"; import type { Density } from "../socrates/SocratesDock"; import type { FixtureData } from "../../lib/fixtures/aristotle"; import type { MarkupStyle } from "./Chip"; interface TextCanvasProps { data: FixtureData; density: Density; markupStyle: MarkupStyle; focusBlockId: string | null; setFocusBlockId: (id: string | null) => void; } export function TextCanvas({ data, density, markupStyle, focusBlockId, setFocusBlockId }: TextCanvasProps) { const padY = density === "compact" ? 10 : 18; const padX = density === "compact" ? 22 : 36; const focusValue = useMemo( () => ({ focusBlockId, setFocusBlockId }), [focusBlockId, setFocusBlockId] ); const editor = useEditor({ immediatelyRender: false, extensions: [ StarterKit.configure({ heading: { levels: [1, 2] }, // Drop features we don't need yet codeBlock: false, blockquote: false, horizontalRule: false, bulletList: false, orderedList: false, listItem: false, strike: false, code: false, link: false, }), ChipNode, SlashExtension, ], content: fixtureToDoc(data), editorProps: { attributes: { class: "text-canvas tiptap", style: `padding: ${padY}px ${padX}px;`, }, }, }); // Push the markup style into the editor so the ChipView NodeView can read it. useEffect(() => { if (!editor) return; (editor.storage as unknown as Record).markupStyle = markupStyle; // Force a re-render of all chip node views so they pick up the new style. editor.view.dispatch(editor.state.tr.setMeta("force-update", true)); }, [editor, markupStyle]); if (!editor) { return (
loading editor…
); } return (
Σ Socrates · margin “Refuses to produce solutions” is a strong constraint. Have you decided what counts as a “solution” vs. a “scaffold”? This boundary will determine whether the refusal policy is enforceable.
); }