ZDDC — Zero Day Document Control. A file-naming convention plus five single-file HTML tools (archive, transmittal, classifier, mdedit, landing) and an optional Go HTTP server (zddc-server) with ACL and a virtual archive index. Self-contained, offline-capable, dependency-free. See README.md for an overview, AGENTS.md and ARCHITECTURE.md for the build/release/architecture detail, bootstrap/README.md for the two-level deployment install pattern, and zddc/README.md for the HTTP server.
106 lines
3.5 KiB
JavaScript
106 lines
3.5 KiB
JavaScript
(function (app) {
|
|
'use strict';
|
|
|
|
const dom = app.dom;
|
|
const util = app.util;
|
|
const json = app.json;
|
|
|
|
let debounceTimer = null;
|
|
const DEBOUNCE_MS = 300; // 300ms debounce
|
|
|
|
async function updateLiveDigest() {
|
|
var digestDisplay = dom.qs('#digest-display');
|
|
if (!digestDisplay) { return; }
|
|
|
|
// Published — let renderSignaturesList handle the display
|
|
var data = json.parse();
|
|
var envelope = (data && data.envelope) || {};
|
|
if (envelope.digest) { return; }
|
|
|
|
// Only compute live digest in edit mode
|
|
if (app.state && app.state.mode !== 'edit') { return; }
|
|
|
|
try {
|
|
// Sync form-field values to JSON only after the app has fully
|
|
// initialised (i.e. loadFromJson has run and app.data.files is
|
|
// populated). Calling syncUiToJson before that would overwrite
|
|
// the saved-draft JSON with the empty in-memory state.
|
|
if (app._initialized && app.modules.files && app.modules.files.syncUiToJson) {
|
|
app.modules.files.syncUiToJson();
|
|
// Re-read after sync so the digest reflects the updated payload.
|
|
data = json.parse();
|
|
}
|
|
var payload = (data && data.payload) || {};
|
|
var payloadStr = util.canonicalStringify(payload);
|
|
var digest = await util.hashString(payloadStr);
|
|
var now = new Date().toLocaleString();
|
|
|
|
// Render draft verify-card
|
|
digestDisplay.innerHTML = '';
|
|
var card = document.createElement('div');
|
|
card.className = 'verify-card verify-card--draft';
|
|
|
|
var status = document.createElement('div');
|
|
status.className = 'verify-card__status verify-card__status--draft';
|
|
status.textContent = 'DRAFT';
|
|
card.appendChild(status);
|
|
|
|
var detail = document.createElement('div');
|
|
detail.className = 'verify-card__detail';
|
|
detail.innerHTML = 'Digest (SHA-256): <code>' + digest + '</code>';
|
|
card.appendChild(detail);
|
|
|
|
var time = document.createElement('div');
|
|
time.className = 'verify-card__detail';
|
|
time.textContent = 'Live \u2014 ' + now;
|
|
card.appendChild(time);
|
|
|
|
digestDisplay.appendChild(card);
|
|
} catch (err) {
|
|
console.error('[live-digest] Failed to calculate digest:', err);
|
|
}
|
|
}
|
|
|
|
function scheduleDigestUpdate() {
|
|
if (debounceTimer) {
|
|
clearTimeout(debounceTimer);
|
|
}
|
|
debounceTimer = setTimeout(updateLiveDigest, DEBOUNCE_MS);
|
|
}
|
|
|
|
function bindFormChanges() {
|
|
const form = dom.qs('#transmittal-form');
|
|
if (!form) {
|
|
return;
|
|
}
|
|
|
|
// Listen to all input changes
|
|
form.addEventListener('input', scheduleDigestUpdate);
|
|
form.addEventListener('change', scheduleDigestUpdate);
|
|
}
|
|
|
|
function subscribeToStateChanges() {
|
|
// Subscribe to reactive state changes
|
|
if (app.state && app.state.subscribe) {
|
|
app.state.subscribe(function(property, newValue, oldValue) {
|
|
scheduleDigestUpdate();
|
|
});
|
|
}
|
|
}
|
|
|
|
app.onDirty(function () {
|
|
scheduleDigestUpdate();
|
|
});
|
|
|
|
app.modules.liveDigest = {
|
|
update: updateLiveDigest,
|
|
schedule: scheduleDigestUpdate
|
|
};
|
|
|
|
app.registerInit(function () {
|
|
bindFormChanges();
|
|
subscribeToStateChanges();
|
|
// Calculate initial digest
|
|
updateLiveDigest();
|
|
});
|
|
})(window.transmittalApp);
|