Thursday, 2 July 2026

Roject — Editor Build

Session summary: TypeScript conversion, UUID IDs, CLAUDE.md, and a full project editor with WYSIWYG HTML editing.

What we built

TypeScript Conversion

All server and frontend JavaScript converted to TypeScript. Server runs via ts-node using tsconfig.json. Frontend components compile from src/components/ to public/components/ via tsconfig.client.json with module: none — no bundler, each component is a self-contained global script. Interfaces added for all data shapes.

typescript ts-node tsconfig.client.json module: none

UUID IDs

All entity IDs (users, groups, projects, members) switched from integers to UUIDs via crypto.randomUUID(). This allows multiple server instances to generate IDs independently without collision, enabling data merging and parallel deployments without a central ID authority.

crypto.randomUUID() id: string no extra dependency

Project File Storage

Creating a project now creates a real directory structure on the server under storage/<project-id>/root/. A default index.html with a Hello World <page-content> element is generated automatically. A new /api/files route handles tree listing and file read/write with path traversal protection.

storage/ GET /api/files/:id/tree GET /api/files/:id/* PUT /api/files/:id/*

Editor Page

A full editor page (/editor.html) with a 3-panel layout: Left (file tree), Center (HTML editor), Right (empty, accepts dropped tabs). Panels are resizable via drag handles. Sections within a panel are arranged side by side and can be split using the ⊟ button on each tab container. Portrait mode shows one panel at a time, switched via a top bar.

<editor-shell> 3-panel layout resize handles portrait mode

Tab Container

A custom <tab-container> element manages tabs within each panel section. Tabs are draggable between containers using the HTML5 Drag and Drop API. A dirty dot (●) appears on tabs with unsaved changes. The ⊟ split button creates a new side-by-side section in the same panel.

<tab-container> drag and drop dirty indicator section split

HTML Editor Panel

A WYSIWYG editor that loads HTML documents in an iframe and makes the <page-content> element contenteditable. Changes are tracked via MutationObserver. Includes an undo/redo stack (up to 200 steps), Ctrl+S save, and a save button that highlights when the document is dirty.

<html-editor-panel> iframe WYSIWYG MutationObserver undo/redo Ctrl+S

File Tree Panel

A <file-tree-panel> component fetches and renders the project's root/ directory structure. HTML files are clickable and open in the HTML editor. Directories are collapsible. Non-HTML files are shown but not openable in this version.

<file-tree-panel> collapsible dirs HTML-only editing

Key Decisions

LayoutPanel sections are arranged horizontally

Sections within a panel sit side by side (not stacked), so the Right panel can hold an object graph next to an inspector. Each section can contain multiple TabContainers stacked vertically. Portrait mode shows one section at a time via a secondary tab row below the main panel switcher.

Fixed 3-column structure, flexible rows

The editor has three named panels (Left, Center, Right). The column count is fixed because portrait mode maps each column to one top-bar button — a clean 1-to-1 relationship. Within each column, sections and tab containers can be freely split and resized.

Tab drag uses HTML5 DnD, not pointer events

HTML5 Drag and Drop handles cross-container tab moves via application/editor-tab in dataTransfer. The source container exposes an extractTab() method and the target calls receiveTab(), preserving the panel's DOM element and state across moves.

EditorState on window, not imported

Since module: none forbids imports between frontend files, shared state lives on window.editorState, initialised synchronously in editor-shell before any child component connects. Other components wait via customElements.whenDefined() before accessing it.

Real-time collaboration deferred

The spec described multi-user selections but building WebSocket sync would have consumed the full session. The document/state model is designed for it — EditorState can emit events that a future sync layer hooks into.

Project Structure

server/storage.ts — project file storage helpers
server/routes/files.ts — file tree + read/write API
storage/ — project file directories (auto-created)
src/components/editor-shell/ — main editor layout + EditorState
src/components/tab-container/ — tabbed panel with drag-drop
src/components/file-tree-panel/ — project file browser
src/components/html-editor-panel/ — WYSIWYG iframe editor
public/editor.html — editor page entry point
CLAUDE.md — project conventions (UUID IDs, raw CSS, custom elements)

What is missing

Real-time multi-user collaboration and shared selections are not implemented. The file tree is read-only — creating, renaming, and deleting files from the editor is not yet supported. The Right panel is empty by default and requires manual tab dragging to populate. Portrait secondary section switching (when a panel has multiple side-by-side sections) is defined but not yet wired up as a secondary tab row.