feat(ui): TDS migration stage 1 — EJS layout, CSP, vendored design system, shared runtime

- Vendor web_template base.css/base.js (v1.2 bbec859) under public/web_template with sync script
- Add views/layout.ejs (fixed upstream EJS comment delimiters, mobile drawer, cmd palette,
  keys help, WS status, theme toggle) and lib/render.js two-step renderer
- Add page routes for /, /workers, /workflows, /executions, /quick, /scheduler (stub views)
- helmet with strict nonce CSP (script-src-attr 'none'), /csp-report, report-only toggle
- lib/pageauth.js: shared users upsert + HTML 401/403 for page routes
- PULSE_DEV_READONLY guard disabling background writers for local testing
- public/assets/app.js shared runtime (Pulse namespace, delegated actions, WS singleton,
  themed confirm modal) and app.css
- eslint globals for browser files; ignore vendored assets

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HamVMDrA8RqhyxmUHgiqRp
This commit is contained in:
2026-09-08 21:46:24 -04:00
co-authored by Claude Fable 5.1
parent 53b61249b0
commit cbb91a306d
24 changed files with 10750 additions and 38 deletions
+19
View File
@@ -0,0 +1,19 @@
<!-- COMMAND PALETTE -->
<div id="lt-cmd-overlay" class="lt-cmd-overlay" role="dialog" aria-modal="true" aria-label="Command palette" aria-hidden="true">
<div class="lt-cmd-palette" id="lt-cmd-palette">
<div class="lt-cmd-input-wrap">
<span class="lt-cmd-prompt">&gt;</span>
<input id="lt-cmd-input" class="lt-cmd-input" type="text"
placeholder="Search commands&hellip;" autocomplete="off"
spellcheck="false" aria-label="Search commands">
</div>
<div class="lt-cmd-results" id="lt-cmd-results">
<div class="lt-cmd-empty">Start typing to search&hellip;</div>
</div>
<div class="lt-cmd-footer">
<span><kbd>&#x2191;</kbd><kbd>&#x2193;</kbd> Navigate</span>
<span><kbd>Enter</kbd> Select</span>
<span><kbd>Esc</kbd> Close</span>
</div>
</div>
</div>
+23
View File
@@ -0,0 +1,23 @@
<!-- KEYBOARD SHORTCUTS MODAL -->
<div id="lt-keys-help" class="lt-modal-overlay" aria-hidden="true">
<div class="lt-modal" role="dialog" aria-modal="true" aria-labelledby="lt-keys-help-title">
<div class="lt-modal-header">
<span class="lt-modal-title" id="lt-keys-help-title">Keyboard Shortcuts</span>
<button type="button" class="lt-modal-close" data-modal-close aria-label="Close">&#x2715;</button>
</div>
<div class="lt-modal-body">
<table class="lt-table">
<thead><tr><th>Shortcut</th><th>Action</th></tr></thead>
<tbody>
<tr><td>Ctrl / &#x2318; + K</td><td>Command palette</td></tr>
<tr><td>R</td><td>Refresh data</td></tr>
<tr><td>?</td><td>Show this help</td></tr>
<tr><td>ESC</td><td>Close modal / drawer / palette</td></tr>
</tbody>
</table>
</div>
<div class="lt-modal-footer">
<button type="button" class="lt-btn" data-modal-close>Close</button>
</div>
</div>
</div>
+17
View File
@@ -0,0 +1,17 @@
<%#
Page header partial.
Locals: title (string), subtitle (optional string), actions (optional raw HTML).
Usage from a page view:
<%- include('../partials/page-header', { title: 'Workers', subtitle: '', actions: '' }) %>
%>
<div class="lt-page-header">
<div>
<h1 class="lt-page-title"><%= title %></h1>
<% if (typeof subtitle !== 'undefined' && subtitle) { %>
<p class="lt-page-subtitle"><%= subtitle %></p>
<% } %>
</div>
<% if (typeof actions !== 'undefined' && actions) { %>
<div class="lt-page-actions"><%- actions %></div>
<% } %>
</div>