chore(embedded): cut v0.0.22-beta
All checks were successful
Notify chart dev on beta cut / notify-chart-dev (push) Successful in 9s
All checks were successful
Notify chart dev on beta cut / notify-chart-dev (push) Successful in 9s
This commit is contained in:
parent
86d667309d
commit
b1ef81077e
7 changed files with 110 additions and 69 deletions
|
|
@ -2582,7 +2582,7 @@ td[data-field="trackingNumber"] {
|
|||
</svg>
|
||||
<div class="header-title-group">
|
||||
<span class="app-header__title">ZDDC Archive</span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102</span></span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673</span></span>
|
||||
</div>
|
||||
<button id="addDirectoryBtn" class="btn btn-primary">Use Local Directory</button>
|
||||
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh Data">⟳</button>
|
||||
|
|
|
|||
|
|
@ -1224,21 +1224,6 @@ body {
|
|||
|
||||
/* .hidden lives in shared/base.css; no per-tool override needed. */
|
||||
|
||||
/* Status bar — shows transient errors/info */
|
||||
.status-bar {
|
||||
padding: 0.4rem 1rem;
|
||||
background: var(--bg-secondary);
|
||||
border-top: 1px solid var(--border);
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
min-height: 1.6rem;
|
||||
line-height: 1.6rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-bar--error { color: #b00020; }
|
||||
.status-bar--info { color: var(--primary); }
|
||||
|
||||
/* Read-only banner for the YAML editor — surfaced by preview-yaml.js
|
||||
when the listing's `writable` bit was false. CodeMirror's readOnly
|
||||
mode has no built-in visual signal beyond the disabled caret, so a
|
||||
|
|
@ -1502,6 +1487,11 @@ body {
|
|||
|
||||
.preview-pane__body {
|
||||
flex: 1;
|
||||
min-height: 0; /* critical: lets the flex child shrink to fit
|
||||
the viewport instead of growing to its
|
||||
content's natural size (which clips the
|
||||
YAML editor's bottom when there are many
|
||||
lines, even with the editor's own scroll) */
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -1833,21 +1823,6 @@ body {
|
|||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ── Status bar ──────────────────────────────────────────────────────────── */
|
||||
|
||||
.status-bar {
|
||||
padding: 0.4rem 1rem;
|
||||
background: var(--bg-secondary);
|
||||
border-top: 1px solid var(--border);
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
min-height: 1.6rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-bar.is-error { color: var(--danger); }
|
||||
.status-bar.is-info { color: var(--text); }
|
||||
|
||||
/* ── Markdown plugin (right-pane internals when a .md is selected) ──────── */
|
||||
/* CSS-Grid shell mirroring mdedit's layout: sidebar on the LEFT
|
||||
(front matter top + TOC bottom), content on the RIGHT (informational
|
||||
|
|
@ -2369,7 +2344,7 @@ body {
|
|||
</svg>
|
||||
<div class="header-title-group">
|
||||
<span class="app-header__title">ZDDC Browse</span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 16:30:00 · 90a3102</span></span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673</span></span>
|
||||
</div>
|
||||
<button id="addDirectoryBtn" class="btn btn-primary">Use Local Directory</button>
|
||||
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh listing" aria-label="Refresh listing">⟳</button>
|
||||
|
|
@ -2445,8 +2420,6 @@ body {
|
|||
</div>
|
||||
</main>
|
||||
|
||||
<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">
|
||||
|
|
@ -10501,19 +10474,17 @@ var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Arr
|
|||
var REVIEW_OFFSET_DAYS = 7;
|
||||
var RESPONSE_OFFSET_DAYS = 14;
|
||||
|
||||
// Notifications go through the shared toast helper — there's no
|
||||
// persistent footer strip in browse anymore.
|
||||
function statusInfo(msg) {
|
||||
var el = document.getElementById('statusBar');
|
||||
if (!el) return;
|
||||
el.textContent = msg || '';
|
||||
el.classList.remove('status-bar--error');
|
||||
el.classList.add('status-bar--info');
|
||||
if (msg && window.zddc && typeof window.zddc.toast === 'function') {
|
||||
window.zddc.toast(msg, 'info');
|
||||
}
|
||||
}
|
||||
function statusError(msg) {
|
||||
var el = document.getElementById('statusBar');
|
||||
if (!el) return;
|
||||
el.textContent = msg || '';
|
||||
el.classList.remove('status-bar--info');
|
||||
el.classList.add('status-bar--error');
|
||||
if (msg && window.zddc && typeof window.zddc.toast === 'function') {
|
||||
window.zddc.toast(msg, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Compute today + N days as a YYYY-MM-DD string.
|
||||
|
|
@ -11563,18 +11534,21 @@ var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Arr
|
|||
// call time, not at IIFE-eval time.
|
||||
function previewMod() { return window.app.modules.preview; }
|
||||
|
||||
// Notifications route through the shared toast helper (shared/
|
||||
// toast.js) — there's no persistent footer strip in browse. Same
|
||||
// signatures as before so the 70+ existing call sites work
|
||||
// unchanged; statusClear is a no-op (toasts fade on their own and
|
||||
// single-toast policy guarantees only the latest is visible).
|
||||
function status(msg, kind) {
|
||||
var el = document.getElementById('statusBar');
|
||||
if (!el) return;
|
||||
el.textContent = msg || '';
|
||||
el.classList.remove('status-bar--error', 'status-bar--info');
|
||||
if (kind === 'error') el.classList.add('status-bar--error');
|
||||
if (kind === 'info') el.classList.add('status-bar--info');
|
||||
if (!msg) return;
|
||||
if (!window.zddc || typeof window.zddc.toast !== 'function') return;
|
||||
var level = kind === 'error' ? 'error' : 'info';
|
||||
window.zddc.toast(msg, level);
|
||||
}
|
||||
|
||||
function statusError(msg) { status(msg, 'error'); }
|
||||
function statusInfo(msg) { status(msg, 'info'); }
|
||||
function statusClear() { status('', null); }
|
||||
function statusClear() { /* no-op — toasts fade on their own */ }
|
||||
|
||||
async function pickLocalDir() {
|
||||
if (typeof window.showDirectoryPicker !== 'function') {
|
||||
|
|
|
|||
|
|
@ -1793,7 +1793,7 @@ body.is-elevated::after {
|
|||
</svg>
|
||||
<div class="header-title-group">
|
||||
<span class="app-header__title">ZDDC Classifier</span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102</span></span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673</span></span>
|
||||
</div>
|
||||
<button id="addDirectoryBtn" class="btn btn-primary">Use Local Directory</button>
|
||||
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory" aria-label="Refresh" style="font-size:1.1rem;">⟳</button>
|
||||
|
|
@ -7896,10 +7896,10 @@ X.B(E,Y);return E}return J}())
|
|||
const { file, index } = modifiedFiles[i];
|
||||
|
||||
try {
|
||||
// Add small delay between operations to prevent race conditions
|
||||
if (i > 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
}
|
||||
// No inter-operation delay: saveFile() is fully awaited and
|
||||
// each iteration renames a distinct file, so the saves are
|
||||
// already serialized. (The old 200ms sleep papered over an
|
||||
// earlier missing-await bug, since fixed.)
|
||||
|
||||
// Validate before saving
|
||||
const newFilename = computeNewFilename(file, index);
|
||||
|
|
@ -7942,8 +7942,8 @@ X.B(E,Y);return E}return J}())
|
|||
errorCount++;
|
||||
errors.push(`${zddc.joinExtension(file.originalFilename, file.extension)}: ${saveErr.message}`);
|
||||
|
||||
// Add delay after errors to let filesystem stabilize
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
// No post-error delay: each file is independent, so an
|
||||
// error on one doesn't require "settling" before the next.
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error processing file ${index}:`, err);
|
||||
|
|
|
|||
|
|
@ -1536,7 +1536,7 @@ body {
|
|||
</svg>
|
||||
<div class="header-title-group">
|
||||
<span class="app-header__title">ZDDC</span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102</span></span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
|
|
|
|||
|
|
@ -2635,7 +2635,7 @@ dialog.modal--narrow {
|
|||
</svg>
|
||||
<div class="header-title-group">
|
||||
<span class="app-header__title">ZDDC Transmittal</span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102</span></span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673</span></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;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Generated by build.sh — do not edit. One <app>=<build label> per line.
|
||||
archive=v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102
|
||||
transmittal=v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102
|
||||
classifier=v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102
|
||||
landing=v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102
|
||||
form=v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102
|
||||
tables=v0.0.22-beta · 2026-05-21 16:29:59 · 90a3102
|
||||
browse=v0.0.22-beta · 2026-05-21 16:30:00 · 90a3102
|
||||
archive=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
transmittal=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
classifier=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
landing=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
form=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
tables=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
browse=v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673
|
||||
|
|
|
|||
|
|
@ -1534,7 +1534,7 @@ body.is-elevated::after {
|
|||
</svg>
|
||||
<div class="header-title-group">
|
||||
<span class="app-header__title" id="table-title">ZDDC Table</span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-dev · 2026-05-21 18:21:55 · a6cb847-dirty</span></span>
|
||||
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.22-beta · 2026-05-21 22:10:16 · 86d6673</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
|
|
@ -4422,6 +4422,16 @@ body.is-elevated::after {
|
|||
|
||||
const propSchema = propertySchemaFor(col);
|
||||
|
||||
// Read-only cells (schema readOnly:true — e.g. the folder-bound
|
||||
// originator the server derives from the party folder, or
|
||||
// server-managed audit fields) can't be edited: any value the
|
||||
// user typed would be overwritten on write. Suppress edit entry
|
||||
// entirely; selection still works for keyboard navigation, same
|
||||
// as the $-prefixed synthesized columns above.
|
||||
if (propSchema && propSchema.readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Complex-type cells (nested object, generic array, oneOf)
|
||||
// can't be inline-edited cleanly — punt to the row's form
|
||||
// editor in a side panel / new page. Phase 2 ships the
|
||||
|
|
@ -5690,8 +5700,28 @@ body.is-elevated::after {
|
|||
const newEtag = (resp.headers.get('ETag') || '').replace(/"/g, '');
|
||||
row.yamlUrl = location;
|
||||
row.url = location ? location + '.html' : row.url;
|
||||
// Re-fetch the just-written row so server-derived fields
|
||||
// surface immediately: folder-bound originator, the composed
|
||||
// tracking number's components, and audit stamps. The local
|
||||
// `merged` lacks these (e.g. originator is read-only and
|
||||
// never typed). Fall back to merged if the GET fails.
|
||||
row.data = merged;
|
||||
row.etag = newEtag || null;
|
||||
if (location) {
|
||||
try {
|
||||
const back = await fetch(location, { credentials: 'same-origin' });
|
||||
if (back.ok) {
|
||||
const text = await back.text();
|
||||
if (text && text.trim() && window.jsyaml) {
|
||||
row.data = window.jsyaml.load(text) || merged;
|
||||
}
|
||||
const fetchedEtag = (back.headers.get('ETag') || '').replace(/"/g, '');
|
||||
if (fetchedEtag) row.etag = fetchedEtag;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[tables] post-create re-fetch failed; using local merge', e);
|
||||
}
|
||||
}
|
||||
if (!row.etag) row.etag = newEtag || null;
|
||||
row.isNew = false;
|
||||
// Move the drafts entry (was keyed on the synthetic id) to
|
||||
// the new url, then clear it (data has the merged values).
|
||||
|
|
@ -5731,6 +5761,20 @@ body.is-elevated::after {
|
|||
return { status: 'forbidden' };
|
||||
}
|
||||
|
||||
if (resp.status === 409) {
|
||||
// The composed tracking number collides with an existing
|
||||
// row (the server rejects duplicates). Surface it on the
|
||||
// sequence cell — the usual disambiguator — rather than the
|
||||
// generic errored state, so the user knows to bump a
|
||||
// component instead of retrying the same values.
|
||||
let msg = 'Duplicate tracking number — change a component (e.g. sequence).';
|
||||
try { const t = await resp.text(); if (t && t.trim()) msg = t.trim(); } catch (_) { /* ignore */ }
|
||||
clearCellInvalid(rowId);
|
||||
markCellInvalid(rowId, 'sequence', msg);
|
||||
setRowState(rowId, 'invalid');
|
||||
return { status: 'duplicate', message: msg };
|
||||
}
|
||||
|
||||
console.warn('[tables] createRow returned', resp.status);
|
||||
setRowState(rowId, 'errored');
|
||||
return { status: 'http-error', code: resp.status };
|
||||
|
|
@ -7320,6 +7364,29 @@ body.is-elevated::after {
|
|||
fs.appendChild(childWidget.el);
|
||||
}
|
||||
|
||||
// Cross-field mirror: a field with `ui:mirrorFrom: <sibling>`
|
||||
// shows the live value of that sibling. Used by the project-
|
||||
// rollup forms so the read-only `originator` reflects the
|
||||
// selected Package (party) — the party folder is the
|
||||
// originator's source of truth. Display-only: the server is
|
||||
// still authoritative via the cascade's folder_fields.
|
||||
for (let i = 0; i < ordered.length; i++) {
|
||||
const name = ordered[i];
|
||||
const mirrorFrom = ui && ui[name] && ui[name]['ui:mirrorFrom'];
|
||||
if (!mirrorFrom || !children[name] || !children[mirrorFrom]) {
|
||||
continue;
|
||||
}
|
||||
const targetInput = children[name].el.querySelector('input, select, textarea');
|
||||
const sourceInput = children[mirrorFrom].el.querySelector('input, select, textarea');
|
||||
if (!targetInput || !sourceInput) {
|
||||
continue;
|
||||
}
|
||||
const sync = function () { targetInput.value = sourceInput.value; };
|
||||
sourceInput.addEventListener('input', sync);
|
||||
sourceInput.addEventListener('change', sync);
|
||||
sync(); // initialize from any pre-filled party value
|
||||
}
|
||||
|
||||
return {
|
||||
el: fs,
|
||||
path: path,
|
||||
|
|
|
|||
Loading…
Reference in a new issue