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

282 lines
14 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Friday, 25 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, 25 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>