rojects/workspace/history/2026/07-July/24-Friday/index.html

282 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Friday, 24 July 2026 — Roject</title>
<link rel="stylesheet" href="../../../../_assets_/styles.css">
<link rel="stylesheet" href="../../../../_assets_/nav.css">
</head>
<body>
<div class="page">
<header>
<p class="date">Friday, 24 July 2026</p>
<h1>Session History</h1>
<p class="subtitle">Page editor panel: structured .page format, block registry, rich-text toolbar, default theme.</p>
</header>
<section>
<h2>What we built</h2>
<div class="card">
<h3>Rename: <code>html-editor-panel</code><code>page-editor-panel</code></h3>
<p>
The old HTML editor panel is replaced by <code>page-editor-panel</code>
(<code>PageEditorPanel</code>), handling <code>.page</code> files instead of
<code>.html</code>/<code>.htm</code>. All references updated across:
<code>FileEditorRegistry.ts</code>, <code>editor.html</code>,
<code>editor-shell.ts</code>, <code>tab-container.ts</code>,
<code>file-tree-panel.ts</code>. The old folder was deleted.
<code>editor-shell</code> no longer auto-opens <code>index.html</code> on startup
— the panel shows an empty state until the user opens a <code>.page</code> file
from the file tree.
</p>
</div>
<div class="card">
<h3>Structured <code>.page</code> format</h3>
<p>
A <code>.page</code> file is a full HTML document whose <code>&lt;body&gt;</code>
must contain exactly one <code>&lt;page-header&gt;</code>, one
<code>&lt;page-root&gt;</code>, and one <code>&lt;page-footer&gt;</code> as direct
children, in that order.
<code>&lt;page-root&gt;</code> holds any number of <code>&lt;page-block&gt;</code>
elements; each block contains one or more <code>&lt;page-area&gt;</code> elements
for rich-text content.
</p>
<p style="margin-top:0.75rem">
Format validation is a placeholder function (<code>validatePageFormat</code>) that
always returns <code>true</code>, documented for future implementation. When real
validation is added, invalid files fall back to <code>code-panel</code>.
</p>
<p style="margin-top:0.75rem">
<strong>Auto-template for empty files:</strong> when a <code>.page</code> file is
opened with empty content, validation is bypassed and the standard template
(full-width block, default theme) is injected automatically. The document is marked
dirty — the user must save to persist the structure.
</p>
</div>
<div class="card">
<h3>Block registry</h3>
<p>
Available block templates are defined in a static table
(<code>PAGE_BLOCK_REGISTRY</code>) in <code>page-editor-panel.ts</code>. Each
entry has a <code>name</code>, optional CSS-sketch <code>preview</code> markup
(using <code>.pbp-*</code> helper classes), and an <code>html</code> snippet
appended to <code>&lt;page-root&gt;</code> when selected. Two standard blocks ship:
</p>
<ul style="line-height:1.9;margin-top:0.75rem">
<li><strong>Full Width</strong> — one <code>&lt;page-area&gt;</code> spanning the
full container width (<code>pep-block-full</code>).</li>
<li><strong>Two Columns</strong> — two equal <code>&lt;page-area&gt;</code> elements
side by side on landscape; stacked top-to-bottom on portrait via a CSS media
query (<code>pep-block-two-col</code>).</li>
</ul>
<p style="margin-top:0.75rem">
Block items without a <code>preview</code> entry show their name as a text label
inside the preview box.
</p>
</div>
<div class="card">
<h3>Two-mode sidebar</h3>
<p>
Two icon buttons on the left edge of the panel switch between editing modes:
</p>
<ul style="line-height:1.9;margin-top:0.75rem">
<li><strong>Blocks mode (⊞)</strong> — shows a horizontal scrollable list of block
templates. Each entry has a CSS layout sketch preview above its name. Clicking
a block appends it to <code>&lt;page-root&gt;</code> in the iframe and makes
its new <code>&lt;page-area&gt;</code> elements contenteditable immediately,
without a full re-render.</li>
<li><strong>Areas mode (T)</strong> — shows the rich-text formatting toolbar.
Buttons: <code>B</code> (bold), <code>I</code> (italic), <code>U</code>
(underline), then a separator, then <code>H1</code>, <code>H2</code>,
<code>H3</code>.</li>
</ul>
</div>
<div class="card">
<h3>Rich-text: <code>wrapSelection</code></h3>
<p>
Formatting is applied via <code>wrapSelection(doc, range, tagName, attributes?)</code>
— no <code>execCommand</code>. The helper uses <code>Range.extractContents()</code>
to pull the selected fragment out of the DOM, wraps it in the target element
(created in the iframe document), and re-inserts via <code>Range.insertNode()</code>.
The Range API automatically splits text nodes and element boundaries, so both
fully-contained nodes and boundary intersections are handled correctly.
Semantic tags are preferred: <code>&lt;b&gt;</code>, <code>&lt;i&gt;</code>,
<code>&lt;u&gt;</code>, <code>&lt;h1&gt;</code><code>&lt;h3&gt;</code>.
Adjacent identical elements are not merged after wrapping (future work, documented).
</p>
</div>
<div class="card">
<h3>iframe sandbox + editor CSS injection</h3>
<p>
The editor iframe carries <code>sandbox="allow-same-origin"</code>, blocking script
execution for user-authored <code>&lt;script&gt;</code> tags. The sandbox attribute
is documented as the single point to change if sandboxing needs adjustment.
</p>
<p style="margin-top:0.75rem">
Editor-side layout styles (<code>PEP_EDITOR_STYLES</code>) are injected into the
live iframe <code>&lt;head&gt;</code> post-load as
<code>&lt;style id="pep-editor-injected"&gt;</code>. Before saving, the element is
temporarily removed, <code>outerHTML</code> is captured, then the element is
re-appended — so the injected styles never reach disk. The MutationObserver watches
only <code>&lt;page-root&gt;</code>, so head mutations do not trigger change events.
</p>
</div>
<div class="card">
<h3>Default theme: <code>default-roject</code></h3>
<p>
All theme CSS rules are scoped to <code>[data-theme="default-roject"]</code>.
The <code>&lt;body&gt;</code> and <code>&lt;page-root&gt;</code> in the standard
template both carry this attribute so the theme applies to the full document and
to all content inside blocks.
</p>
<p style="margin-top:0.75rem">
The theme CSS (<code>DEFAULT_THEME_CSS</code>) is embedded as a
<code>&lt;style&gt;</code> block in the page <code>&lt;head&gt;</code> for new
files. Future plan: replace with a <code>&lt;link&gt;</code> to
<code>styles.rokojori.com</code> when themes are hosted there.
</p>
<p style="margin-top:0.75rem">Styles defined:</p>
<ul style="line-height:1.9;margin-top:0.5rem">
<li>Base: dark background (<code>#0f1117</code>), whitish copy (<code>#c8cce0</code>),
Barlow font via <code>@import</code> from <code>styles.rokojori.com</code>,
<code>1rem / 1.7</code> line-height.</li>
<li><code>h1</code>: blue (<code>#7c8cff</code>), 2.5rem, 900 italic, uppercase.</li>
<li><code>h2</code>: blue (<code>#7c8cff</code>), 1.6rem, 700.</li>
<li><code>h3</code>: muted blue (<code>#9ba4c7</code>), 1.2rem, 700.</li>
<li><code>b</code> / <code>strong</code>: bright white (<code>#e2e4ed</code>).</li>
</ul>
</div>
</section>
<section>
<h2>Tab container updates</h2>
<div class="card">
<h3>Split submenu + vertical split</h3>
<p>
The single <em>Split</em> menu entry is replaced by a <em>Split &gt;</em> submenu
with <em>↔ Horizontally</em> (adds a new section side by side in <code>.es-sections</code>)
and <em>↕ Vertically</em> (adds a second <code>tab-container</code> + horizontal
resize handle inside the same <code>.es-section</code>). The
<code>tab-container:split</code> event now carries a <code>direction</code> field.
<code>.es-section</code> was already <code>flex-direction: column</code>, so no CSS
changes were needed for vertical split.
</p>
</div>
<div class="card">
<h3>Close Container</h3>
<p>
A <em>Close Container</em> context menu entry fires <code>tab-container:close-container</code>,
handled by <code>editor-shell</code>: if the container is in a vertical split (multiple
<code>tab-container</code> elements in the section), only that container and its adjacent
handle are removed; otherwise the whole section and its adjacent <code>es-v-handle</code>
are removed. The entry is hidden when the container is the last one in its
<code>.es-panel</code> slot. If any tab has unsaved changes,
<code>showConfirmDialog</code> prompts with <em>Don't Close</em> /
<em>Close Without Saving</em> before dispatching the event.
</p>
</div>
<div class="card">
<h3>Middle-mouse tab close</h3>
<p>
Tabs now close on middle-mouse click: a <code>mousedown</code> listener with
<code>e.button === 1</code> calls <code>e.preventDefault()</code> (suppresses the
browser scroll cursor) and extracts the tab immediately.
</p>
</div>
<div class="card">
<h3>EditorPanel / FileEditorPanel interface system</h3>
<p>
New file <code>source/editor/editor-panel.ts</code> defines the panel contract.
<code>EditorPanel</code> requires <code>__interfaces__: string[]</code> and
<code>addContextMenuEntries()</code>. <code>FileEditorPanel extends EditorPanel</code>
adds <code>hasUnsavedChanges(): boolean</code>, replacing the old
<code>TabEntry.dirty</code> flag — <code>renderBar()</code> now queries the panel
live instead of caching a boolean.
Each interface has a companion Definition class with <code>static readonly type</code>;
<code>implementsInterface(el, Def)</code> is the single runtime check function.
All five panels (<code>page-editor-panel</code>, <code>code-panel</code>,
<code>file-tree-panel</code>, <code>rojo-settings-panel</code>,
<code>rojo-chat-panel</code>) updated with <code>__interfaces__</code>.
</p>
</div>
<div class="card">
<h3>Bug fixes</h3>
<p>
<strong>Section resize:</strong> <code>setupResizeHandler</code> previously observed
only the workspace element, so section redistribution never fired when a panel was
dragged (workspace size doesn't change on panel drag). Fixed by observing each
<code>.es-sections</code> element directly — when the panel narrows, its sections
container narrows too and the observer redistributes sections correctly.
</p>
<p style="margin-top:0.75rem">
<strong>Portrait → landscape:</strong> <code>showPortraitPanel</code> set inline
<code>display: none</code> on panels and handles, but returning to landscape only
removed the <code>portrait</code> class — the inline styles persisted and kept panels
hidden. Fixed by clearing <code>style.display</code> on all panels and
<code>.es-v-handle</code> elements when <code>apply(false)</code> runs.
</p>
</div>
</section>
<section>
<h2>Key decisions</h2>
<div class="card">
<p>
<strong>Placeholder validation instead of gating immediately.</strong>
The real format check (exactly one page-header / page-root / page-footer in body,
no other elements) is documented but not enforced yet — <code>validatePageFormat</code>
always returns <code>true</code>. This keeps the editor usable while the format
stabilises, without hiding the validation contract.
</p>
</div>
<div class="card">
<p>
<strong><code>wrapSelection</code> for headings, not block-level replacement.</strong>
H1/H2/H3 buttons wrap the selection the same way as B/I/U rather than replacing
the parent block element. This is consistent with the existing system and avoids
the complexity of block-level conversion for now. The limitation is documented.
</p>
</div>
<div class="card">
<p>
<strong>Theme attribute on both <code>&lt;body&gt;</code> and
<code>&lt;page-root&gt;</code>.</strong>
Putting <code>data-theme</code> on <code>&lt;body&gt;</code> lets the base styles
(background, font, colour) apply to the full document without a separate unscoped
<code>body { }</code> rule. <code>&lt;page-root&gt;</code> keeps its own attribute
for future per-block theme overrides.
</p>
</div>
</section>
<footer>
Roject &mdash; session history
</footer>
</div>
<script>var NAV_ROOT = '../../../../';</script>
<script src="../../../../_assets_/nav-data.js"></script>
<script src="../../../../_assets_/nav.js"></script>
</body>
</html>