Thursday, 2 July 2026
Session summary: TypeScript conversion, UUID IDs, CLAUDE.md, and a full project editor with WYSIWYG HTML editing.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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)
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.