From d1fdfb140cc440664f77a24886172f9976a05a31 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Tue, 28 Apr 2026 19:12:52 +0100
Subject: feat: revert broken directive PRs, re-implement Lexical document
 orchestrator (#98)

* feat: soryu-co/soryu - makima: Revert broken directive PRs and verify clean build

* feat: soryu-co/soryu - makima: Re-implement frontend: Lexical document editor with feature flag and base components

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Add contract blocks, expandable log rows, and interaction controls

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: End-to-end build verification and integration polish
---
 .../src/components/document/DocumentEditor.tsx     | 236 ---------------------
 1 file changed, 236 deletions(-)
 delete mode 100644 frontend/src/components/document/DocumentEditor.tsx

(limited to 'frontend/src/components/document/DocumentEditor.tsx')

diff --git a/frontend/src/components/document/DocumentEditor.tsx b/frontend/src/components/document/DocumentEditor.tsx
deleted file mode 100644
index 2ef37fe..0000000
--- a/frontend/src/components/document/DocumentEditor.tsx
+++ /dev/null
@@ -1,236 +0,0 @@
-import { useCallback, useRef, useState } from 'react';
-import { LexicalComposer } from '@lexical/react/LexicalComposer';
-import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
-import { ContentEditable } from '@lexical/react/LexicalContentEditable';
-import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
-import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
-import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
-import { HeadingNode } from '@lexical/rich-text';
-import { ListNode, ListItemNode } from '@lexical/list';
-import { LinkNode } from '@lexical/link';
-import {
-  $getRoot,
-  $createParagraphNode,
-  $createTextNode,
-  type EditorState,
-  type LexicalEditor,
-} from 'lexical';
-import { $createHeadingNode } from '@lexical/rich-text';
-
-import { StepsDiagramNode, $isStepsDiagramNode, $createStepsDiagramNode } from './nodes/StepsDiagramNode';
-import { ContractBlockNode, $isContractBlockNode } from './nodes/ContractBlockNode';
-import editorTheme from './EditorTheme';
-import AutoSavePlugin from './AutoSavePlugin';
-import ContextMenu, { type ContextMenuAction } from './ContextMenu';
-import './DocumentEditor.css';
-import './nodes/StepsDiagram.css';
-import './nodes/ContractBlock.css';
-
-interface DocumentEditorProps {
-  directiveId: string;
-  title: string;
-  goal: string;
-  status: string;
-  prBranch?: string | null;
-  onGoalChange?: (newGoal: string) => void;
-  onTitleChange?: (newTitle: string) => void;
-  onCleanup?: () => void;
-  onCreatePr?: () => void;
-  onPlanOrders?: () => void;
-  onTogglePause?: () => void;
-  readOnly?: boolean;
-}
-
-function buildInitialEditorState(directiveId: string, title: string, goal: string) {
-  return () => {
-    const root = $getRoot();
-
-    // Title as H1
-    const heading = $createHeadingNode('h1');
-    heading.append($createTextNode(title));
-    root.append(heading);
-
-    // Goal as paragraph(s), split by newlines
-    const lines = goal.split('\n');
-    for (const line of lines) {
-      const paragraph = $createParagraphNode();
-      if (line.trim()) {
-        paragraph.append($createTextNode(line));
-      }
-      root.append(paragraph);
-    }
-
-    // Insert steps diagram node after the goal content
-    const stepsNode = $createStepsDiagramNode(directiveId);
-    root.append(stepsNode);
-
-    // Add a trailing paragraph so the user can type below the diagram
-    const trailingParagraph = $createParagraphNode();
-    root.append(trailingParagraph);
-  };
-}
-
-function onError(error: Error) {
-  console.error('[DocumentEditor] Lexical error:', error);
-}
-
-export default function DocumentEditor({
-  directiveId,
-  title,
-  goal,
-  status,
-  prBranch,
-  onGoalChange,
-  onTitleChange,
-  onCleanup,
-  onCreatePr,
-  onPlanOrders,
-  onTogglePause,
-  readOnly = false,
-}: DocumentEditorProps) {
-  const editorRef = useRef<LexicalEditor | null>(null);
-  const latestGoalRef = useRef(goal);
-  const latestTitleRef = useRef(title);
-
-  // Context menu state
-  const [ctxMenu, setCtxMenu] = useState<{ x: number; y: number } | null>(null);
-
-  const initialConfig = {
-    namespace: `DocumentEditor-${directiveId}`,
-    theme: editorTheme,
-    editorState: buildInitialEditorState(directiveId, title, goal),
-    nodes: [HeadingNode, ListNode, ListItemNode, LinkNode, StepsDiagramNode, ContractBlockNode],
-    onError,
-    editable: !readOnly,
-  };
-
-  const handleChange = useCallback(
-    (_editorState: EditorState, editor: LexicalEditor) => {
-      editorRef.current = editor;
-
-      editor.getEditorState().read(() => {
-        const root = $getRoot();
-        const children = root.getChildren();
-
-        let newTitle = '';
-        const goalLines: string[] = [];
-
-        for (let i = 0; i < children.length; i++) {
-          const child = children[i];
-          // Skip decorator nodes (steps diagram, contract blocks) when extracting text
-          if ($isStepsDiagramNode(child)) continue;
-          if ($isContractBlockNode(child)) continue;
-
-          const text = child.getTextContent();
-
-          if (i === 0 && child.getType() === 'heading') {
-            newTitle = text;
-          } else {
-            goalLines.push(text);
-          }
-        }
-
-        const newGoal = goalLines.join('\n');
-
-        if (newTitle !== latestTitleRef.current) {
-          latestTitleRef.current = newTitle;
-          onTitleChange?.(newTitle);
-        }
-
-        if (newGoal !== latestGoalRef.current) {
-          latestGoalRef.current = newGoal;
-          onGoalChange?.(newGoal);
-        }
-      });
-    },
-    [onGoalChange, onTitleChange]
-  );
-
-  const getContent = useCallback(() => {
-    return latestGoalRef.current;
-  }, []);
-
-  const handleAutoSave = useCallback(
-    (content: string) => {
-      onGoalChange?.(content);
-    },
-    [onGoalChange]
-  );
-
-  // Context menu handler
-  const handleContextMenu = useCallback(
-    (e: React.MouseEvent) => {
-      e.preventDefault();
-      setCtxMenu({ x: e.clientX, y: e.clientY });
-    },
-    []
-  );
-
-  const closeCtxMenu = useCallback(() => setCtxMenu(null), []);
-
-  const isPaused = status === 'paused';
-  const isIdle = status === 'idle';
-
-  const ctxActions: ContextMenuAction[] = [
-    {
-      label: 'Clean Up',
-      icon: '\uD83E\uDDF9', // broom
-      disabled: !isIdle,
-      onClick: () => onCleanup?.(),
-    },
-    {
-      label: 'Update PR',
-      icon: '\uD83D\uDD00', // shuffle
-      disabled: !prBranch,
-      onClick: () => onCreatePr?.(),
-    },
-    {
-      label: 'Plan Orders',
-      icon: '\uD83D\uDCCB', // clipboard
-      onClick: () => onPlanOrders?.(),
-    },
-    {
-      label: isPaused ? 'Resume Directive' : 'Pause Directive',
-      icon: isPaused ? '\u25B6' : '\u23F8',
-      onClick: () => onTogglePause?.(),
-    },
-  ];
-
-  return (
-    <div className="document-editor-container" onContextMenu={handleContextMenu}>
-      <LexicalComposer initialConfig={initialConfig}>
-        <div className="doc-editor-input">
-          <RichTextPlugin
-            contentEditable={
-              <ContentEditable className="doc-editor-content-editable doc-editor-root" />
-            }
-            placeholder={
-              <div className="doc-editor-placeholder">Start writing...</div>
-            }
-            ErrorBoundary={LexicalErrorBoundary}
-          />
-        </div>
-        <HistoryPlugin />
-        <OnChangePlugin onChange={handleChange} />
-        {!readOnly && (
-          <AutoSavePlugin
-            onAutoSave={handleAutoSave}
-            getContent={getContent}
-            enabled={!readOnly}
-          />
-        )}
-      </LexicalComposer>
-
-      {ctxMenu && (
-        <ContextMenu
-          x={ctxMenu.x}
-          y={ctxMenu.y}
-          actions={ctxActions}
-          dividerAfter={[2]}
-          onClose={closeCtxMenu}
-        />
-      )}
-    </div>
-  );
-}
-
-- 
cgit v1.2.3

