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

172 lines
7.7 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, 31 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, 31 July 2026</p>
<h1>Session History</h1>
<p class="subtitle">File tree UX improvements, page editor toolbar, EditorConsole message system, console-panel tab.</p>
</header>
<section>
<h2>What we built</h2>
<div class="card">
<h3>File tree: open-state preservation on refresh</h3>
<p>
Before this fix, calling <code>refresh()</code> on <code>file-tree-panel</code>
collapsed all open directories. The fix records which <code>ftp-dir</code> elements
carry the <code>open</code> class (via <code>data-path</code> on their
<code>ftp-dir-label</code>) before rebuilding the HTML, then re-adds
<code>open</code> to the matching labels after the new tree renders.
</p>
</div>
<div class="card">
<h3>File tree: "Mark As Root Directory" moved to context menu</h3>
<p>
The double-click listener on <code>ftp-dir-label</code> was removed.
"As Root Directory" is now a context menu entry that appears when right-clicking
a directory, inside the existing <code>showItemMenu()</code> method.
<code>isDir</code> detection uses <code>targetPath.endsWith('/')</code>.
</p>
</div>
<div class="card">
<h3>File tree: "Open &gt;" submenu for alternate editors</h3>
<p>
When right-clicking a file that has a known editor, the context menu shows an
<em>Open &gt;</em> submenu listing the default editor plus all registered
alternatives. Selecting an entry calls <code>_openFileIn(path, editorTag)</code>,
which focuses an existing tab of that type or creates a new one.
</p>
<p style="margin-top:0.75rem">
Two static maps drive this:
<code>_panelTypeMap</code> (editorTag → panelType + label) and
<code>_editorAlternatives</code> (defaultEditorTag → alternativeEditorTags[]).
<code>_openFileDefault</code> (single click) focuses the file in any open editor
regardless of type; <code>_openFileIn</code> (context menu) focuses only in the
specified editor type.
</p>
</div>
<div class="card">
<h3>File tree: context menu label truncation</h3>
<p>
The context menu title now shows only the filename (not the full path), truncated
to a configurable <code>menuLabelMaxChars</code> (20). Names longer than the limit
are shown as <code>...last-20-chars</code>.
</p>
</div>
<div class="card">
<h3>Page editor: mode buttons moved into toolbar</h3>
<p>
The left sidebar (<code>.pep-sidebar</code>) and its wrapper (<code>.pep-main</code>)
were removed. The Blocks (⊞) and Areas (T) mode buttons were moved directly into
<code>.pep-toolbar</code>, separated from the left-side toolbar items by a
<code>.pep-toolbar-sep</code> spacer (<code>flex: 1</code>) that pushes them to
the right. <code>page-editor-panel</code> now uses <code>flex-direction: column</code>
with three direct children: toolbar, mode panel, iframe.
</p>
</div>
<div class="card">
<h3>EditorConsole — centralised message system</h3>
<p>
New singleton <code>EditorConsole</code> (<code>source/editor/EditorConsole.ts</code>)
holds a capped ring of 500 <code>ConsoleMessage</code> objects
(<code>text</code>, <code>type</code>: <code>'info'|'error'|'hint'</code>,
<code>timestamp</code>) and dispatches them via <code>onMessage: EventSlot</code>.
<code>showHover</code> / <code>hideHover</code> dispatch a secondary
<code>onHover: EventSlot&lt;string | null&gt;</code> for future tooltip use.
</p>
<p style="margin-top:0.75rem">
<code>editor-shell</code> subscribes to <code>onMessage</code> and shows incoming
messages in a new <code>.es-info</code> element in the header — fades in for 5 s
then fades out. Portrait mode hides the inline element and would show a fixed bottom
bar instead. <code>Editor.get().onFileTypeUnknown</code> is now handled here,
logging to <code>EditorConsole</code> rather than as an inline error in
<code>file-tree-panel</code>.
</p>
</div>
<div class="card">
<h3>console-panel — new editor tab</h3>
<p>
<code>console-panel</code> is a new tab that renders all messages from
<code>EditorConsole</code>. Custom elements: <code>conp-header</code>,
<code>conp-title</code>, <code>conp-list</code>, <code>conp-entry</code>,
<code>conp-time</code>, <code>conp-text</code>. On connect it pre-populates
from <code>EditorConsole.get().messages</code>, then appends new entries as they
arrive. Time format: <code>HH:MM:SS</code>. Entry classes (<code>conp-entry-error</code>,
<code>conp-entry-hint</code>) colour <code>conp-text</code> accordingly.
Implements <code>EditorPanelDefinition.type</code> via <code>__interfaces__</code>.
</p>
</div>
<div class="card">
<h3>Bug fix: <code>openDocumentIn</code> ignored the target panel type</h3>
<p>
<code>Editor.openDocumentIn(filePath, panelElement)</code> was re-resolving
<code>editorTag</code> from the <code>FileEditorRegistry</code>, which always
returned the <em>default</em> editor for the file type (e.g. <code>page-editor-panel</code>
for <code>.page</code> files). The <code>code-panel</code> listener checked
<code>if ('code-panel' !== e.editorTag) return</code> and bailed, so the file
appeared to open but showed no content.
</p>
<p style="margin-top:0.75rem">
Fix: derive <code>editorTag</code> from <code>panelElement.tagName.toLowerCase()</code>
directly, removing the registry lookup entirely from <code>openDocumentIn</code>.
The caller already decided which panel to target; the dispatch should respect that
decision.
</p>
</div>
</section>
<section>
<h2>Key decisions</h2>
<div class="card">
<p>
<strong><code>EditorConsole</code> as a standalone singleton, not part of
<code>Editor</code>.</strong>
Keeping it separate means any component (including future non-editor panels) can
import just <code>EditorConsole</code> without pulling in the full editor state.
<code>Editor.onFileTypeUnknown</code> is bridged to it in <code>editor-shell</code>,
the single component that knows about both.
</p>
</div>
<div class="card">
<p>
<strong>Two open-file methods instead of one.</strong>
<code>_openFileDefault</code> (single click) focuses the file in any editor;
<code>_openFileIn</code> (context menu "Open &gt;") focuses only in the specified
editor type and forces that type when creating a new tab. This makes the intent
explicit without adding a flag parameter.
</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>