history: add session entry for 25 July 2026

Page editor panel: structured .page format, block registry, rich-text
toolbar, default-roject theme, wrapSelection helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rokojori 2026-07-25 20:52:01 +02:00
parent 420c682408
commit 3e25a419ca
2 changed files with 206 additions and 0 deletions

View File

@ -51,6 +51,7 @@ var NAV_DATA = {
title: 'History',
path: 'history/index.html',
children: [
{ title: 'Friday, 25 July 2026', path: 'history/2026/07-July/25-Friday/index.html' },
{ title: 'Friday, 18 July 2026', path: 'history/2026/07-July/18-Friday/index.html' },
{ title: 'Wednesday, 16 July 2026', path: 'history/2026/07-July/16-Wednesday/index.html' },
{ title: 'Tuesday, 15 July 2026', path: 'history/2026/07-July/15-Tuesday/index.html' },

View File

@ -0,0 +1,205 @@
<!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>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>