chore(headers): standardize across all 7 tools
Bring every tool's header in line with archive's pattern: [logo] [title] [version] [Add Local Directory] [⟳] ............... [◐] [?] ------------- header-left --------------- ----- header-right - Changes per tool: * browse: rename "Select Directory" → "Add Local Directory"; add the red-non-stable wrap to the build label (was missing); add a help panel + bundle shared/help.js. * classifier: rename selectDirectoryBtn → addDirectoryBtn, refreshBtn → refreshHeaderBtn for consistency. Update all JS callers and welcome-screen copy to the new label. * mdedit: same id rename. Move the previously-in-pane refresh button into the header. Stop renaming the dir button to "Directory: <name>" once a folder is loaded — instead use the shared btn--subtle variant to de-emphasize while keeping the standard label. * transmittal: convert non-standard <div class="app-header"> with spacer/icons containers to <header class="app-header"> with the canonical header-left/header-right pair. Move the publish split- button into header-left (Transmittal-specific primary action). Remove dead .app-header__spacer/__icons/header-icon-btn CSS now that nothing references those classes. * landing, form: add help-btn + help-panel + bundle shared/help.js. Each panel is tool-specific (project picker docs for landing, schema-driven form docs for form). Cross-cutting: * shared/base.css: promote .btn--subtle from browse/css/tree.css so any tool with an online mode can de-emphasize Add Local Directory consistently. Verified all 7 tools in headless Chromium: header structure correct, build label red on non-stable cuts, help panel opens + closes via button + Esc.
This commit is contained in:
parent
a7faeed8fb
commit
22c142e45a
17 changed files with 277 additions and 101 deletions
|
|
@ -34,6 +34,7 @@ concat_files \
|
||||||
"../shared/zddc.js" \
|
"../shared/zddc.js" \
|
||||||
"../shared/zddc-filter.js" \
|
"../shared/zddc-filter.js" \
|
||||||
"../shared/theme.js" \
|
"../shared/theme.js" \
|
||||||
|
"../shared/help.js" \
|
||||||
"../shared/preview-lib.js" \
|
"../shared/preview-lib.js" \
|
||||||
"js/init.js" \
|
"js/init.js" \
|
||||||
"js/loader.js" \
|
"js/loader.js" \
|
||||||
|
|
@ -51,8 +52,13 @@ tool=browse
|
||||||
compute_build_label "$tool" "$@"
|
compute_build_label "$tool" "$@"
|
||||||
|
|
||||||
# Replace template placeholders with concatenated CSS/JS + label.
|
# Replace template placeholders with concatenated CSS/JS + label.
|
||||||
|
# Non-stable build labels (alpha/beta/dev-dirty) are wrapped in a red
|
||||||
|
# span — same convention as every other tool (compute_build_label
|
||||||
|
# sets $is_red=1 for non-stable cuts). Keeps the visual cue
|
||||||
|
# consistent across tool headers.
|
||||||
awk -v css_file="$css_temp" -v js_file="$js_temp" \
|
awk -v css_file="$css_temp" -v js_file="$js_temp" \
|
||||||
-v build_label="$build_label" -v favicon="$favicon_data_uri" '
|
-v build_label="$build_label" -v is_red="$is_red" \
|
||||||
|
-v favicon="$favicon_data_uri" '
|
||||||
/\{\{CSS_PLACEHOLDER\}\}/ {
|
/\{\{CSS_PLACEHOLDER\}\}/ {
|
||||||
while ((getline line < css_file) > 0) print line
|
while ((getline line < css_file) > 0) print line
|
||||||
close(css_file); next
|
close(css_file); next
|
||||||
|
|
@ -61,8 +67,15 @@ awk -v css_file="$css_temp" -v js_file="$js_temp" \
|
||||||
while ((getline line < js_file) > 0) print line
|
while ((getline line < js_file) > 0) print line
|
||||||
close(js_file); next
|
close(js_file); next
|
||||||
}
|
}
|
||||||
|
/\{\{BUILD_LABEL\}\}/ {
|
||||||
|
if (is_red == "1") {
|
||||||
|
gsub(/\{\{BUILD_LABEL\}\}/, "<span style=\"color:red;font-weight:bold\">" build_label "</span>")
|
||||||
|
} else {
|
||||||
|
gsub(/\{\{BUILD_LABEL\}\}/, build_label)
|
||||||
|
}
|
||||||
|
print; next
|
||||||
|
}
|
||||||
{
|
{
|
||||||
gsub(/\{\{BUILD_LABEL\}\}/, build_label)
|
|
||||||
gsub(/\{\{FAVICON\}\}/, favicon)
|
gsub(/\{\{FAVICON\}\}/, favicon)
|
||||||
print
|
print
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,22 +142,6 @@
|
||||||
outline-offset: -1px;
|
outline-offset: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subtle button variant — used for "Select Directory" when the page
|
|
||||||
is server-backed (the user usually doesn't need to switch to a
|
|
||||||
local folder; we keep the option visible but quiet). */
|
|
||||||
.btn.btn--subtle {
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-muted);
|
|
||||||
border-color: var(--border);
|
|
||||||
box-shadow: none;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.btn--subtle:hover {
|
|
||||||
color: var(--text);
|
|
||||||
background: var(--bg-hover, rgba(0,0,0,0.04));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table — folders + files in a tree */
|
/* Table — folders + files in a tree */
|
||||||
|
|
||||||
.browse-table {
|
.browse-table {
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@
|
||||||
<span class="app-header__title">ZDDC Browse</span>
|
<span class="app-header__title">ZDDC Browse</span>
|
||||||
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button id="addDirectoryBtn" class="btn btn-primary">Select Directory</button>
|
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
|
||||||
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh listing" aria-label="Refresh listing">⟳</button>
|
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh listing" aria-label="Refresh listing" style="font-size:1.1rem;">⟳</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Online</b> — when this page is served by zddc-server, the
|
<li><b>Online</b> — when this page is served by zddc-server, the
|
||||||
listing for the current directory loads automatically.</li>
|
listing for the current directory loads automatically.</li>
|
||||||
<li><b>Local</b> — click <i>Select Directory</i> to pick any folder
|
<li><b>Local</b> — click <i>Add Local Directory</i> to pick any folder
|
||||||
on your computer (Chromium-based browsers).</li>
|
on your computer (Chromium-based browsers).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Once loaded: click a folder to expand it, <b>shift-click</b>
|
<p>Once loaded: click a folder to expand it, <b>shift-click</b>
|
||||||
|
|
@ -103,6 +103,87 @@
|
||||||
|
|
||||||
<div id="statusBar" class="status-bar"></div>
|
<div id="statusBar" class="status-bar"></div>
|
||||||
|
|
||||||
|
<!-- Help Panel -->
|
||||||
|
<aside id="help-panel" class="help-panel" hidden aria-labelledby="help-panel-title">
|
||||||
|
<div class="help-panel__header">
|
||||||
|
<h2 id="help-panel-title" class="help-panel__title">Help — ZDDC Browse</h2>
|
||||||
|
<button type="button" class="help-panel__close" id="help-panel-close" aria-label="Close">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="help-panel__body">
|
||||||
|
<h3>What is Browse?</h3>
|
||||||
|
<p>Browse is a directory listing for ZDDC archives — and any directory. It works in two modes:</p>
|
||||||
|
<dl>
|
||||||
|
<dt>Online</dt>
|
||||||
|
<dd>When the page is served by zddc-server, the listing for the current
|
||||||
|
URL directory loads automatically. Breadcrumbs link to ancestor folders.</dd>
|
||||||
|
<dt>Local</dt>
|
||||||
|
<dd>Click <strong>Add Local Directory</strong> to pick any folder on your
|
||||||
|
computer. Local mode requires a Chromium-based browser (File System
|
||||||
|
Access API).</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Tree navigation</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>Click a folder</dt>
|
||||||
|
<dd>Toggle expand/collapse on that folder.</dd>
|
||||||
|
<dt>Shift-click a folder</dt>
|
||||||
|
<dd>Recursive expand or collapse — applies to the whole subtree.</dd>
|
||||||
|
<dt>Click a file</dt>
|
||||||
|
<dd>Open in the preview popup. Modifier-click (Ctrl/Cmd) or middle-click
|
||||||
|
opens in a new tab.</dd>
|
||||||
|
<dt>ZIP files</dt>
|
||||||
|
<dd>Behave as folders — click to inspect contents inline. JSZip is
|
||||||
|
bundled, so this works offline.</dd>
|
||||||
|
<dt>Column headers</dt>
|
||||||
|
<dd>Click to sort; click again to reverse.</dd>
|
||||||
|
<dt>Refresh</dt>
|
||||||
|
<dd>Re-fetches the current directory listing — works for both
|
||||||
|
local (re-enumerates the FS handle) and online (re-fetches the JSON).</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Filter rows</h3>
|
||||||
|
<p>Two filter rows live in the table header:</p>
|
||||||
|
<dl>
|
||||||
|
<dt>📄 file row</dt>
|
||||||
|
<dd>Filter by file name (left input) and/or extension (Type input).
|
||||||
|
File matches stay visible together with their ancestor folders, so
|
||||||
|
the path to each hit is always shown.</dd>
|
||||||
|
<dt>📁 folder row</dt>
|
||||||
|
<dd>Filter by folder name. Matching folders show with their entire
|
||||||
|
subtree. Combined with file filter: file must also be inside a
|
||||||
|
matching folder's subtree (intersection).</dd>
|
||||||
|
</dl>
|
||||||
|
<p>Filter syntax (shared across all ZDDC tools):</p>
|
||||||
|
<dl>
|
||||||
|
<dt><code>term</code></dt>
|
||||||
|
<dd>Contains "term" (case-insensitive)</dd>
|
||||||
|
<dt><code>!term</code></dt>
|
||||||
|
<dd>Does not contain</dd>
|
||||||
|
<dt><code>^term</code></dt>
|
||||||
|
<dd>Starts with</dd>
|
||||||
|
<dt><code>term$</code></dt>
|
||||||
|
<dd>Ends with</dd>
|
||||||
|
<dt><code>a b</code></dt>
|
||||||
|
<dd>Both (AND)</dd>
|
||||||
|
<dt><code>a | b</code></dt>
|
||||||
|
<dd>Either (OR)</dd>
|
||||||
|
<dt><code>el.*spc</code></dt>
|
||||||
|
<dd>Regex — any-char + any-sequence</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Header buttons</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>Add Local Directory</dt>
|
||||||
|
<dd>Pick a folder from your computer. Works in both modes; in online
|
||||||
|
mode it's de-emphasized but still available.</dd>
|
||||||
|
<dt>⟳ Refresh</dt>
|
||||||
|
<dd>Re-load the current directory listing.</dd>
|
||||||
|
<dt>◐ Theme</dt>
|
||||||
|
<dd>Cycle auto / light / dark.</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
{{JS_PLACEHOLDER}}
|
{{JS_PLACEHOLDER}}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
*/
|
*/
|
||||||
function showBrowserWarning() {
|
function showBrowserWarning() {
|
||||||
const warning = document.getElementById('browserWarning');
|
const warning = document.getElementById('browserWarning');
|
||||||
const selectBtn = document.getElementById('selectDirectoryBtn');
|
const selectBtn = document.getElementById('addDirectoryBtn');
|
||||||
if (warning) {
|
if (warning) {
|
||||||
warning.classList.remove('hidden');
|
warning.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
@ -80,8 +80,8 @@
|
||||||
mainApp: document.getElementById('mainApp'),
|
mainApp: document.getElementById('mainApp'),
|
||||||
|
|
||||||
// Header buttons
|
// Header buttons
|
||||||
selectDirectoryBtn: document.getElementById('selectDirectoryBtn'),
|
addDirectoryBtn: document.getElementById('addDirectoryBtn'),
|
||||||
refreshBtn: document.getElementById('refreshBtn'),
|
refreshHeaderBtn: document.getElementById('refreshHeaderBtn'),
|
||||||
saveAllBtn: document.getElementById('saveAllBtn'),
|
saveAllBtn: document.getElementById('saveAllBtn'),
|
||||||
cancelAllBtn: document.getElementById('cancelAllBtn'),
|
cancelAllBtn: document.getElementById('cancelAllBtn'),
|
||||||
exportHashesBtn: document.getElementById('exportHashesBtn'),
|
exportHashesBtn: document.getElementById('exportHashesBtn'),
|
||||||
|
|
@ -115,8 +115,8 @@
|
||||||
*/
|
*/
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
// Directory selection
|
// Directory selection
|
||||||
app.dom.selectDirectoryBtn.addEventListener('click', handleSelectDirectory);
|
app.dom.addDirectoryBtn.addEventListener('click', handleSelectDirectory);
|
||||||
app.dom.refreshBtn.addEventListener('click', handleRefresh);
|
app.dom.refreshHeaderBtn.addEventListener('click', handleRefresh);
|
||||||
|
|
||||||
// Drag and drop on welcome screen
|
// Drag and drop on welcome screen
|
||||||
setupWelcomeDragDrop();
|
setupWelcomeDragDrop();
|
||||||
|
|
@ -278,7 +278,7 @@
|
||||||
await app.modules.scanner.scanDirectory(dirHandle);
|
await app.modules.scanner.scanDirectory(dirHandle);
|
||||||
|
|
||||||
// Show refresh button now that a directory is loaded
|
// Show refresh button now that a directory is loaded
|
||||||
if (app.dom.refreshBtn) { app.dom.refreshBtn.classList.remove('hidden'); }
|
if (app.dom.refreshHeaderBtn) { app.dom.refreshHeaderBtn.classList.remove('hidden'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@
|
||||||
<span class="app-header__title">ZDDC Classifier</span>
|
<span class="app-header__title">ZDDC Classifier</span>
|
||||||
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button id="selectDirectoryBtn" class="btn btn-primary">Select Directory</button>
|
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
|
||||||
<button id="refreshBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory" aria-label="Refresh" style="font-size:1.1rem;">⟳</button>
|
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory" aria-label="Refresh" style="font-size:1.1rem;">⟳</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
|
|
@ -149,7 +149,7 @@
|
||||||
<li>Rename one file or all modified files at once</li>
|
<li>Rename one file or all modified files at once</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Click <strong>Select Directory</strong> to begin.</p>
|
<p>Click <strong>Add Local Directory</strong> to begin.</p>
|
||||||
|
|
||||||
<p class="note">This application works entirely in your browser. No data is transmitted to any server.</p>
|
<p class="note">This application works entirely in your browser. No data is transmitted to any server.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -168,7 +168,7 @@
|
||||||
|
|
||||||
<h3>Getting Started</h3>
|
<h3>Getting Started</h3>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Click <strong>Select Directory</strong> to open a folder containing files to rename.</li>
|
<li>Click <strong>Add Local Directory</strong> to open a folder containing files to rename.</li>
|
||||||
<li>The folder tree on the left shows all sub-folders. Click a folder to load its files.</li>
|
<li>The folder tree on the left shows all sub-folders. Click a folder to load its files.</li>
|
||||||
<li>Edit cells in the spreadsheet to set the new filename components.</li>
|
<li>Edit cells in the spreadsheet to set the new filename components.</li>
|
||||||
<li>Click <strong>Save All</strong> (or save individual rows) to rename the files on disk.</li>
|
<li>Click <strong>Save All</strong> (or save individual rows) to rename the files on disk.</li>
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ concat_files \
|
||||||
|
|
||||||
concat_files \
|
concat_files \
|
||||||
"../shared/theme.js" \
|
"../shared/theme.js" \
|
||||||
|
"../shared/help.js" \
|
||||||
"js/app.js" \
|
"js/app.js" \
|
||||||
"js/context.js" \
|
"js/context.js" \
|
||||||
"js/util.js" \
|
"js/util.js" \
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
|
<button id="help-btn" class="btn btn-secondary" title="Help" aria-label="Help">?</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -38,6 +39,51 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Help Panel -->
|
||||||
|
<aside id="help-panel" class="help-panel" hidden aria-labelledby="help-panel-title">
|
||||||
|
<div class="help-panel__header">
|
||||||
|
<h2 id="help-panel-title" class="help-panel__title">Help — ZDDC Form</h2>
|
||||||
|
<button type="button" class="help-panel__close" id="help-panel-close" aria-label="Close">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="help-panel__body">
|
||||||
|
<h3>What is this form?</h3>
|
||||||
|
<p>This is a schema-driven form rendered by zddc-server. Every
|
||||||
|
<code><name>.form.yaml</code> file in the archive becomes an
|
||||||
|
editable form at <code><path>/<name>.form.html</code>.
|
||||||
|
Submissions are saved as <code><name>/<id>.yaml</code>
|
||||||
|
files alongside the schema, and re-render with their data filled in
|
||||||
|
when revisited.</p>
|
||||||
|
|
||||||
|
<h3>Filling in the form</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>Required fields</dt>
|
||||||
|
<dd>Marked with an asterisk in their label. Submitting with a
|
||||||
|
required field empty re-renders the form with an inline error.</dd>
|
||||||
|
<dt>Validation</dt>
|
||||||
|
<dd>Server-side via JSON Schema 2020-12 (subset). Client-side
|
||||||
|
hints (<code>required</code>, <code>min</code>, <code>max</code>,
|
||||||
|
<code>pattern</code>) are added where the schema specifies them.</dd>
|
||||||
|
<dt>Submit</dt>
|
||||||
|
<dd>POSTs to the same URL the form was loaded from. On success the
|
||||||
|
browser navigates to the saved submission's URL. On failure the
|
||||||
|
form re-renders with errors inline at each invalid field.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Editing existing submissions</h3>
|
||||||
|
<p>Open the saved submission's URL — the form re-renders with its
|
||||||
|
current data and any errors. Submitting overwrites the same file.
|
||||||
|
History is in git via your normal commit cycle.</p>
|
||||||
|
|
||||||
|
<h3>Header buttons</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>◐ Theme</dt>
|
||||||
|
<dd>Cycle auto / light / dark.</dd>
|
||||||
|
<dt>? Help</dt>
|
||||||
|
<dd>This panel. Press <kbd>Esc</kbd> to close.</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Server injects the form context here on render. Shape:
|
Server injects the form context here on render. Shape:
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ concat_files \
|
||||||
"../shared/zddc.js" \
|
"../shared/zddc.js" \
|
||||||
"../shared/zddc-filter.js" \
|
"../shared/zddc-filter.js" \
|
||||||
"../shared/theme.js" \
|
"../shared/theme.js" \
|
||||||
|
"../shared/help.js" \
|
||||||
"js/landing.js" \
|
"js/landing.js" \
|
||||||
> "$js_raw"
|
> "$js_raw"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
|
<button id="help-btn" class="btn btn-secondary" title="Help" aria-label="Help">?</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -91,6 +92,52 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Help Panel -->
|
||||||
|
<aside id="help-panel" class="help-panel" hidden aria-labelledby="help-panel-title">
|
||||||
|
<div class="help-panel__header">
|
||||||
|
<h2 id="help-panel-title" class="help-panel__title">Help — ZDDC</h2>
|
||||||
|
<button type="button" class="help-panel__close" id="help-panel-close" aria-label="Close">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="help-panel__body">
|
||||||
|
<h3>What is this page?</h3>
|
||||||
|
<p>This is the ZDDC archive landing page — a project picker. It lists every
|
||||||
|
project (top-level directory) you have access to on this server, plus any
|
||||||
|
<strong>groups</strong> you've defined for opening multiple projects at once.</p>
|
||||||
|
|
||||||
|
<h3>Projects</h3>
|
||||||
|
<p>Click a project to open it. The project's archive view (list of folders +
|
||||||
|
files, with all the standard ZDDC tools available inside) loads in the same
|
||||||
|
tab. Use back/forward to navigate between projects and the picker.</p>
|
||||||
|
|
||||||
|
<h3>Groups</h3>
|
||||||
|
<p>A group bundles a set of projects you commonly open together. Click
|
||||||
|
<strong>+ New group</strong>, give it a name, click projects to include
|
||||||
|
them, then save. Opening a group opens all its projects in one go.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>Save group</dt>
|
||||||
|
<dd>Persist the selection as a named group on this server (visible to
|
||||||
|
other users with access to the same projects).</dd>
|
||||||
|
<dt>Open selected</dt>
|
||||||
|
<dd>Open the currently-checked projects without saving as a group.</dd>
|
||||||
|
<dt>Cancel</dt>
|
||||||
|
<dd>Exit select mode without saving.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h3>Access</h3>
|
||||||
|
<p>Projects and groups are filtered by your account's permissions.
|
||||||
|
If a URL references a project you don't have access to, a warning banner
|
||||||
|
appears and the inaccessible items are skipped silently.</p>
|
||||||
|
|
||||||
|
<h3>Header buttons</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>◐ Theme</dt>
|
||||||
|
<dd>Cycle auto / light / dark.</dd>
|
||||||
|
<dt>? Help</dt>
|
||||||
|
<dd>This panel. Press <kbd>Esc</kbd> to close.</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
{{JS_PLACEHOLDER}}
|
{{JS_PLACEHOLDER}}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ const SCRATCHPAD_WELCOME = [
|
||||||
'Use this **Scratchpad** for quick notes. Download it any time with the ⬇',
|
'Use this **Scratchpad** for quick notes. Download it any time with the ⬇',
|
||||||
'button on the Scratchpad row in the file list.',
|
'button on the Scratchpad row in the file list.',
|
||||||
'',
|
'',
|
||||||
'Click **Select Directory** above to open a folder of Markdown files,',
|
'Click **Add Local Directory** above to open a folder of Markdown files,',
|
||||||
'or just start typing here.',
|
'or just start typing here.',
|
||||||
'',
|
'',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
* Set up all event listeners for the application
|
* Set up all event listeners for the application
|
||||||
*/
|
*/
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
// Select directory button
|
// Add Local Directory button (was id="select-directory" / "refresh-directory")
|
||||||
const selectDirectoryBtn = document.getElementById('select-directory');
|
const selectDirectoryBtn = document.getElementById('addDirectoryBtn');
|
||||||
if (selectDirectoryBtn) {
|
if (selectDirectoryBtn) {
|
||||||
selectDirectoryBtn.addEventListener('click', openDirectory);
|
selectDirectoryBtn.addEventListener('click', openDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh directory button
|
// Refresh button (now in header, was in file-nav pane)
|
||||||
const refreshDirectoryBtn = document.getElementById('refresh-directory');
|
const refreshDirectoryBtn = document.getElementById('refreshHeaderBtn');
|
||||||
if (refreshDirectoryBtn) {
|
if (refreshDirectoryBtn) {
|
||||||
refreshDirectoryBtn.addEventListener('click', refreshDirectory);
|
refreshDirectoryBtn.addEventListener('click', refreshDirectory);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,12 +171,19 @@ async function openDirectory() {
|
||||||
* @param {string} directoryName - Name of the selected directory
|
* @param {string} directoryName - Name of the selected directory
|
||||||
*/
|
*/
|
||||||
function updateDirectoryStatus(directoryName) {
|
function updateDirectoryStatus(directoryName) {
|
||||||
const selectDirectoryBtn = document.getElementById('select-directory');
|
// Standardized header pattern (across all ZDDC tools): the button
|
||||||
|
// keeps the label "Add Local Directory"; de-emphasize it once a
|
||||||
|
// directory is loaded (the user can still click to pick another)
|
||||||
|
// by applying the shared btn--subtle variant. The directory name
|
||||||
|
// is shown in the file-nav pane, not on the button.
|
||||||
|
const selectDirectoryBtn = document.getElementById('addDirectoryBtn');
|
||||||
if (selectDirectoryBtn) {
|
if (selectDirectoryBtn) {
|
||||||
selectDirectoryBtn.textContent = `Directory: ${directoryName}`;
|
selectDirectoryBtn.classList.remove('btn-primary');
|
||||||
|
selectDirectoryBtn.classList.add('btn--subtle');
|
||||||
|
selectDirectoryBtn.title = `Loaded: ${directoryName} — click to switch`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshBtn = document.getElementById('refresh-directory');
|
const refreshBtn = document.getElementById('refreshHeaderBtn');
|
||||||
if (refreshBtn) {
|
if (refreshBtn) {
|
||||||
refreshBtn.classList.remove('hidden');
|
refreshBtn.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
@ -677,8 +684,8 @@ async function loadServerDirectory() {
|
||||||
|
|
||||||
// Only enter server-source mode if the host actually serves JSON directory
|
// Only enter server-source mode if the host actually serves JSON directory
|
||||||
// listings (zddc-server / Caddy). On a plain static host the probe fails
|
// listings (zddc-server / Caddy). On a plain static host the probe fails
|
||||||
// and we must leave "Select Directory" visible so the user can still load
|
// and we must leave "Add Local Directory" visible so the user can still
|
||||||
// local files.
|
// load local files.
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(baseUrl, { headers: { 'Accept': 'application/json' }, cache: 'no-cache' });
|
const resp = await fetch(baseUrl, { headers: { 'Accept': 'application/json' }, cache: 'no-cache' });
|
||||||
if (!resp.ok) return;
|
if (!resp.ok) return;
|
||||||
|
|
@ -703,12 +710,18 @@ async function loadServerDirectory() {
|
||||||
entries: {},
|
entries: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Surface refresh, hide write-only controls. "Select Directory" stays
|
// Surface refresh, hide write-only controls. "Add Local Directory"
|
||||||
// visible so the user can switch to a local folder at any time.
|
// stays visible (de-emphasized via btn--subtle) so the user can
|
||||||
const refreshBtn = document.getElementById('refresh-directory');
|
// switch to a local folder at any time.
|
||||||
|
const refreshBtn = document.getElementById('refreshHeaderBtn');
|
||||||
if (refreshBtn) refreshBtn.classList.remove('hidden');
|
if (refreshBtn) refreshBtn.classList.remove('hidden');
|
||||||
const newFileRootBtn = document.getElementById('new-file-root');
|
const newFileRootBtn = document.getElementById('new-file-root');
|
||||||
if (newFileRootBtn) newFileRootBtn.classList.add('hidden');
|
if (newFileRootBtn) newFileRootBtn.classList.add('hidden');
|
||||||
|
const addDirBtn = document.getElementById('addDirectoryBtn');
|
||||||
|
if (addDirBtn) {
|
||||||
|
addDirBtn.classList.remove('btn-primary');
|
||||||
|
addDirBtn.classList.add('btn--subtle');
|
||||||
|
}
|
||||||
|
|
||||||
const stats = await readServerDirectory(baseUrl, fileTree, 0);
|
const stats = await readServerDirectory(baseUrl, fileTree, 0);
|
||||||
renderFileTree();
|
renderFileTree();
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
* Initialize UI based on File System API availability
|
* Initialize UI based on File System API availability
|
||||||
*/
|
*/
|
||||||
function initializeApiAvailability() {
|
function initializeApiAvailability() {
|
||||||
const selectDirectoryBtn = document.getElementById('select-directory');
|
const selectDirectoryBtn = document.getElementById('addDirectoryBtn');
|
||||||
const welcomeHint = document.getElementById('welcome-hint');
|
const welcomeHint = document.getElementById('welcome-hint');
|
||||||
const welcomeFirefox = document.getElementById('welcome-firefox');
|
const welcomeFirefox = document.getElementById('welcome-firefox');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@
|
||||||
<span class="app-header__title">ZDDC Markdown</span>
|
<span class="app-header__title">ZDDC Markdown</span>
|
||||||
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
|
<button id="addDirectoryBtn" class="btn btn-primary" title="Add a local directory">Add Local Directory</button>
|
||||||
|
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh directory" aria-label="Refresh" style="font-size:1.1rem;">⟳</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
|
|
@ -45,7 +46,6 @@
|
||||||
<span>Files</span>
|
<span>Files</span>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<button id="new-file-root" class="btn btn-secondary btn-sm hidden" title="New file in root directory">+</button>
|
<button id="new-file-root" class="btn btn-secondary btn-sm hidden" title="New file in root directory">+</button>
|
||||||
<button id="refresh-directory" class="btn btn-secondary btn-sm hidden" title="Refresh directory">↻</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<div class="pane content-pane flex-1 relative flex flex-col bg-white dark:bg-gray-900 overflow-hidden" id="main-content">
|
<div class="pane content-pane flex-1 relative flex flex-col bg-white dark:bg-gray-900 overflow-hidden" id="main-content">
|
||||||
<div id="welcome-screen" class="welcome-screen hidden flex-col items-center justify-center h-full text-gray-500 dark:text-gray-400 text-center p-6">
|
<div id="welcome-screen" class="welcome-screen hidden flex-col items-center justify-center h-full text-gray-500 dark:text-gray-400 text-center p-6">
|
||||||
<p id="welcome-hint" class="text-sm">Click <strong>Scratchpad</strong> in the file list to start editing,<br>or <strong>Select Directory</strong> to work with files.</p>
|
<p id="welcome-hint" class="text-sm">Click <strong>Scratchpad</strong> in the file list to start editing,<br>or <strong>Add Local Directory</strong> to work with files.</p>
|
||||||
<p id="welcome-firefox" class="text-sm text-amber-600 hidden mt-2">Your browser doesn't support the File System API.<br>Use <strong>Scratchpad</strong> to edit markdown and download as a file.</p>
|
<p id="welcome-firefox" class="text-sm text-amber-600 hidden mt-2">Your browser doesn't support the File System API.<br>Use <strong>Scratchpad</strong> to edit markdown and download as a file.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@
|
||||||
|
|
||||||
<h3>Getting Started</h3>
|
<h3>Getting Started</h3>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Click <strong>Select Directory</strong> to open a folder. The file tree on the left will populate with all files in that folder.</li>
|
<li>Click <strong>Add Local Directory</strong> to open a folder. The file tree on the left will populate with all files in that folder.</li>
|
||||||
<li>Click any Markdown file (<code>.md</code>) in the tree to open it in the editor.</li>
|
<li>Click any Markdown file (<code>.md</code>) in the tree to open it in the editor.</li>
|
||||||
<li>Use the <strong>Scratchpad</strong> entry (always visible at the top of the tree) for temporary notes without saving to disk.</li>
|
<li>Use the <strong>Scratchpad</strong> entry (always visible at the top of the tree) for temporary notes without saving to disk.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,24 @@ a:hover {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Subdued / de-emphasized variant.
|
||||||
|
Used on the "Add Local Directory" button when a tool is operating
|
||||||
|
in server (online) mode — the local-dir affordance is still
|
||||||
|
available but visually quieter, since the typical user already
|
||||||
|
has the directory loaded from the server. */
|
||||||
|
.btn.btn--subtle {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-muted);
|
||||||
|
border-color: var(--border);
|
||||||
|
box-shadow: none;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn--subtle:not(:disabled):hover {
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-success {
|
.btn-success {
|
||||||
background: var(--success);
|
background: var(--success);
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
|
|
|
||||||
|
|
@ -372,37 +372,6 @@
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-header__spacer {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-header__icons {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-muted);
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.15s, background 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon-btn:hover {
|
|
||||||
color: var(--primary-hover);
|
|
||||||
background: var(--primary-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Fixed footer status bar at viewport bottom ───────── */
|
/* ── Fixed footer status bar at viewport bottom ───────── */
|
||||||
.page-footer {
|
.page-footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
|
|
@ -27,31 +27,34 @@ conventions at https://codeberg.org/VARASYS/ZDDC#file-naming-convention.
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="font-sans text-gray-900">
|
<body class="font-sans text-gray-900">
|
||||||
<div class="app-header print:hidden" data-no-disable="true">
|
<header class="app-header print:hidden" data-no-disable="true">
|
||||||
<div class="split-button" id="bottom-menu" hidden>
|
<div class="header-left">
|
||||||
<button id="bottom-toggle" type="button" class="btn btn-primary split-button__toggle" data-no-disable="true" aria-haspopup="true" aria-expanded="false">▾</button>
|
<svg class="app-header__logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" aria-hidden="true">
|
||||||
<button id="bottom-primary" type="button" class="btn btn-primary" data-no-disable="true">Publish</button>
|
<rect width="64" height="64" rx="12" fill="#1e3a5f"/>
|
||||||
<div class="dropdown-menu hidden" role="menu" id="bottom-dropdown"></div>
|
<g fill="#fff">
|
||||||
|
<rect x="14" y="18" width="36" height="7"/>
|
||||||
|
<polygon points="43,25 50,25 21,43 14,43"/>
|
||||||
|
<rect x="14" y="43" width="36" height="7"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<div class="header-title-group">
|
||||||
|
<span class="app-header__title">ZDDC Transmittal</span>
|
||||||
|
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
||||||
|
</div>
|
||||||
|
<span id="no-js-notice" class="text-gray-400 text-xs italic">JavaScript not available</span>
|
||||||
|
<!-- Publish split-button (Transmittal-specific primary action;
|
||||||
|
other tools have "Add Local Directory" here instead) -->
|
||||||
|
<div class="split-button" id="bottom-menu" hidden>
|
||||||
|
<button id="bottom-toggle" type="button" class="btn btn-primary split-button__toggle" data-no-disable="true" aria-haspopup="true" aria-expanded="false">▾</button>
|
||||||
|
<button id="bottom-primary" type="button" class="btn btn-primary" data-no-disable="true">Publish</button>
|
||||||
|
<div class="dropdown-menu hidden" role="menu" id="bottom-dropdown"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span id="no-js-notice" class="text-gray-400 text-xs italic">JavaScript not available</span>
|
<div class="header-right">
|
||||||
<svg class="app-header__logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" aria-hidden="true">
|
|
||||||
<rect width="64" height="64" rx="12" fill="#1e3a5f"/>
|
|
||||||
<g fill="#fff">
|
|
||||||
<rect x="14" y="18" width="36" height="7"/>
|
|
||||||
<polygon points="43,25 50,25 21,43 14,43"/>
|
|
||||||
<rect x="14" y="43" width="36" height="7"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<div class="header-title-group">
|
|
||||||
<span class="app-header__title">ZDDC Transmittal</span>
|
|
||||||
<span class="build-timestamp">{{BUILD_LABEL}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="app-header__spacer"></div>
|
|
||||||
<div class="app-header__icons">
|
|
||||||
<button type="button" id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
<button type="button" id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)">◐</button>
|
||||||
<button type="button" id="help-btn" class="btn btn-secondary" aria-label="Help" title="Help">?</button>
|
<button type="button" id="help-btn" class="btn btn-secondary" aria-label="Help" title="Help">?</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</header>
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<form id="transmittal-form">
|
<form id="transmittal-form">
|
||||||
<input type="hidden" id="mode" value="edit">
|
<input type="hidden" id="mode" value="edit">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue