Added braille spinner to verify Query & Verify button; restructured verify results into a single card flow with OTS stamp/upgraded cards and a final summary card
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nostr_quantum_preparation",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "A migration strategy for bringing post-quantum security to Nostr without breaking the social graph, without requiring consensus on a single post-quantum algorithm, and without forcing existing users to abandon their identities.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
+183
-37
@@ -50,17 +50,55 @@
|
||||
const resultList = document.getElementById('pqResultList');
|
||||
const eventJsonWrap = document.getElementById('pqEventJsonWrap');
|
||||
const eventJsonEl = document.getElementById('pqEventJson');
|
||||
const otsBadge = document.getElementById('pqOtsBadge');
|
||||
const otsUpgradeWrap = document.getElementById('pqOtsUpgradeWrap');
|
||||
const otsInfo = document.getElementById('pqOtsInfo');
|
||||
const otsUpgradeBtn = document.getElementById('pqOtsUpgradeBtn');
|
||||
const otsRepublishBtn = document.getElementById('pqOtsRepublishBtn');
|
||||
const otsUpgradeStatus = document.getElementById('pqOtsUpgradeStatus');
|
||||
const summaryWrap = document.getElementById('pqSummaryWrap');
|
||||
const summaryCard = document.getElementById('pqSummaryCard');
|
||||
|
||||
// OTS cards are created dynamically and appended into the shared result
|
||||
// list so they flow as part of the same card list as the signature checks.
|
||||
let otsStampCard = null;
|
||||
let otsUpgradedCard = null;
|
||||
|
||||
let currentEvent = null;
|
||||
let currentOtsBytes = null;
|
||||
let currentExpectedAuthor = null;
|
||||
|
||||
/* Braille spinner: shown inside a button while its action is running
|
||||
(e.g. Query & Verify). Advances through the standard braille spinner
|
||||
frames every 80ms, matching the spinners on the preparation page. */
|
||||
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
||||
const buttonSpinnerIntervals = new Map();
|
||||
|
||||
function setButtonSpinner(btn) {
|
||||
if (!btn) return;
|
||||
clearButtonSpinner(btn);
|
||||
let span = btn.querySelector('.pq-btn-spinner');
|
||||
if (!span) {
|
||||
span = document.createElement('span');
|
||||
span.className = 'pq-btn-spinner';
|
||||
btn.insertBefore(span, btn.firstChild);
|
||||
}
|
||||
let frame = 0;
|
||||
span.textContent = SPINNER_FRAMES[0];
|
||||
buttonSpinnerIntervals.set(btn, setInterval(() => {
|
||||
frame = (frame + 1) % SPINNER_FRAMES.length;
|
||||
span.textContent = SPINNER_FRAMES[frame];
|
||||
}, 80));
|
||||
}
|
||||
|
||||
function clearButtonSpinner(btn) {
|
||||
if (!btn) return;
|
||||
if (buttonSpinnerIntervals.has(btn)) {
|
||||
clearInterval(buttonSpinnerIntervals.get(btn));
|
||||
buttonSpinnerIntervals.delete(btn);
|
||||
}
|
||||
const span = btn.querySelector('.pq-btn-spinner');
|
||||
if (span) span.remove();
|
||||
}
|
||||
|
||||
// F-H3: Build status DOM safely — type is internally controlled, message uses textContent
|
||||
function setStatus(element, type, message) {
|
||||
const div = document.createElement('div');
|
||||
@@ -77,13 +115,70 @@
|
||||
resultList.appendChild(item);
|
||||
}
|
||||
|
||||
// F-H3: Helper to set OTS badge (static HTML, safe) and info (textContent, safe)
|
||||
function setOtsBadge(className, text) {
|
||||
otsBadge.innerHTML = `<span class="pq-ots-badge ${className}"></span>`;
|
||||
otsBadge.querySelector('span').textContent = text;
|
||||
// OTS state tracked for the summary card.
|
||||
let otsStampState = { valid: null, label: '', detail: '' };
|
||||
let otsUpgradedState = { valid: null, label: '', detail: '' };
|
||||
|
||||
// Render an OTS card (stamp or upgraded). The card is created on demand
|
||||
// and appended to the shared result list so it flows with the signature
|
||||
// checks. `which` is 'stamp' or 'upgraded'; `state` is one of
|
||||
// 'valid' | 'invalid' | 'pending'.
|
||||
function setOtsCard(which, state, head, body) {
|
||||
const card = which === 'upgraded' ? otsUpgradedCard : otsStampCard;
|
||||
if (!card) {
|
||||
// Create the card and append it to the result list.
|
||||
const el = document.createElement('div');
|
||||
el.className = 'pq-result-item pq-result-detail';
|
||||
resultList.appendChild(el);
|
||||
if (which === 'upgraded') otsUpgradedCard = el; else otsStampCard = el;
|
||||
}
|
||||
const target = which === 'upgraded' ? otsUpgradedCard : otsStampCard;
|
||||
target.style.display = 'block';
|
||||
target.classList.remove('pq-result-valid', 'pq-result-invalid', 'pq-result-pending');
|
||||
if (state === 'valid') target.classList.add('pq-result-valid');
|
||||
else if (state === 'invalid') target.classList.add('pq-result-invalid');
|
||||
else if (state === 'pending') target.classList.add('pq-result-pending');
|
||||
// F-H3: build DOM safely — head and body use textContent
|
||||
target.innerHTML = '';
|
||||
const headEl = document.createElement('div');
|
||||
headEl.className = 'pq-result-head';
|
||||
headEl.textContent = head;
|
||||
const bodyEl = document.createElement('div');
|
||||
bodyEl.className = 'pq-result-body';
|
||||
bodyEl.textContent = body;
|
||||
target.appendChild(headEl);
|
||||
target.appendChild(bodyEl);
|
||||
}
|
||||
function setOtsInfo(text) {
|
||||
otsInfo.textContent = text;
|
||||
|
||||
function hideOtsCard(which) {
|
||||
const card = which === 'upgraded' ? otsUpgradedCard : otsStampCard;
|
||||
if (card) { card.remove(); }
|
||||
if (which === 'upgraded') otsUpgradedCard = null; else otsStampCard = null;
|
||||
}
|
||||
|
||||
// Render the final summary card based on signature validity and OTS state.
|
||||
function renderSummary(allValid) {
|
||||
const stampOk = otsStampState.valid === true;
|
||||
const upgradedOk = otsUpgradedState.valid === true;
|
||||
const anyOtsConfirmed = stampOk || upgradedOk;
|
||||
summaryWrap.style.display = 'block';
|
||||
summaryCard.classList.remove('pq-result-valid', 'pq-result-invalid', 'pq-result-pending');
|
||||
if (allValid && anyOtsConfirmed) {
|
||||
summaryCard.classList.add('pq-result-valid');
|
||||
const anchor = upgradedOk
|
||||
? (otsUpgradedState.label || 'the upgraded proof')
|
||||
: (otsStampState.label || 'the original proof');
|
||||
summaryCard.textContent = `All signatures are verified and valid, and the event is stamped on the Bitcoin blockchain (via ${anchor}).`;
|
||||
} else if (allValid && otsStampState.valid === 'pending') {
|
||||
summaryCard.classList.add('pq-result-pending');
|
||||
summaryCard.textContent = 'All signatures are verified and valid. The OpenTimestamps proof is pending — not yet stamped on the Bitcoin blockchain. You can upgrade it below.';
|
||||
} else if (allValid) {
|
||||
summaryCard.classList.add('pq-result-invalid');
|
||||
summaryCard.textContent = 'All signatures are verified and valid, but no Bitcoin timestamp attestation was found.';
|
||||
} else {
|
||||
summaryCard.classList.add('pq-result-invalid');
|
||||
summaryCard.textContent = 'Some checks failed verification. See the results above for details.';
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
@@ -174,9 +269,13 @@
|
||||
resultsWrap.style.display = 'none';
|
||||
eventJsonWrap.style.display = 'none';
|
||||
otsUpgradeWrap.style.display = 'none';
|
||||
otsBadge.innerHTML = '';
|
||||
hideOtsCard('stamp');
|
||||
hideOtsCard('upgraded');
|
||||
summaryWrap.style.display = 'none';
|
||||
otsStampState = { valid: null, label: '', detail: '' };
|
||||
otsUpgradedState = { valid: null, label: '', detail: '' };
|
||||
|
||||
// Display full proof carrier JSON
|
||||
// Display full proof carrier JSON at the top of the page
|
||||
eventJsonEl.textContent = JSON.stringify(event, null, 2);
|
||||
eventJsonWrap.style.display = 'block';
|
||||
|
||||
@@ -237,9 +336,10 @@
|
||||
}
|
||||
|
||||
// 3. Inspect OTS proof tag and verify it matches the sha256 tag.
|
||||
// First do a quick structural check for immediate UI feedback, then
|
||||
// run full cryptographic verification (async — fetches Bitcoin block
|
||||
// headers and validates the Merkle path).
|
||||
// The OTS result is rendered as a dedicated card (pqOtsStampCard)
|
||||
// alongside the signature results. If the proof is pending, an
|
||||
// upgrade card (pqOtsUpgradedCard) is reserved for a later upgraded
|
||||
// proof. A final summary card is rendered at the end.
|
||||
const otsTag = event.tags.find(t => t[0] === 'ots');
|
||||
const sha256Tag = event.tags.find(t => t[0] === 'sha256');
|
||||
if (otsTag && otsTag[1]) {
|
||||
@@ -254,17 +354,27 @@
|
||||
: `WARNING: sha256 tag (${sha256Tag[1].substring(0, 16)}...) does not match computed hash (${fullHash.substring(0, 16)}...)`)
|
||||
: 'No sha256 tag found';
|
||||
|
||||
// Quick structural display first
|
||||
otsUpgradeWrap.style.display = 'block';
|
||||
|
||||
if (!validFile) {
|
||||
setOtsBadge('pq-ots-badge-none', 'OTS: Invalid format');
|
||||
setOtsInfo(`OTS tag present but does not appear to be a valid detached .ots file (${currentOtsBytes.length} bytes). ${hashMatchNote}.`);
|
||||
otsUpgradeWrap.style.display = 'none';
|
||||
} else if (hasBitcoinAttestation) {
|
||||
setOtsBadge('pq-ots-badge-confirmed', 'OTS: Verifying...');
|
||||
setOtsInfo(`OpenTimestamps proof contains a Bitcoin attestation. Performing full cryptographic verification (fetching block header)... Proof size: ${currentOtsBytes.length} bytes. ${hashMatchNote}.`);
|
||||
otsUpgradeWrap.style.display = 'block';
|
||||
// Invalid format — no upgrade possible
|
||||
otsStampState = { valid: false, label: '', detail: '' };
|
||||
setOtsCard('stamp', 'invalid', 'OTS Stamp: Invalid format',
|
||||
`OTS tag present but does not appear to be a valid detached .ots file (${currentOtsBytes.length} bytes). ${hashMatchNote}.`);
|
||||
hideOtsCard('upgraded');
|
||||
otsUpgradeBtn.style.display = 'none';
|
||||
otsRepublishBtn.style.display = 'none';
|
||||
renderSummary(allValid);
|
||||
} else if (hasBitcoinAttestation) {
|
||||
// Contains a Bitcoin attestation — run full async verification.
|
||||
// Show a "verifying" stamp card first, then update it.
|
||||
otsStampState = { valid: null, label: '', detail: '' };
|
||||
setOtsCard('stamp', 'pending', 'OTS Stamp: Verifying...',
|
||||
`OpenTimestamps proof contains a Bitcoin attestation. Performing full cryptographic verification (fetching block header)... Proof size: ${currentOtsBytes.length} bytes. ${hashMatchNote}.`);
|
||||
hideOtsCard('upgraded');
|
||||
otsUpgradeBtn.style.display = 'none';
|
||||
otsRepublishBtn.style.display = 'none';
|
||||
renderSummary(allValid);
|
||||
|
||||
// Run full async verification: parse the proof, bind the target
|
||||
// digest to the sha256 tag, walk the Merkle path, and check the
|
||||
@@ -278,36 +388,56 @@
|
||||
: result.trustMode === 'single-explorer-checked'
|
||||
? 'single-explorer-checked (trusted API)'
|
||||
: result.trustMode || 'unknown';
|
||||
setOtsBadge('pq-ots-badge-confirmed', 'OTS: Verified (Bitcoin)');
|
||||
setOtsInfo(`OpenTimestamps proof verified. Bitcoin block ${att.height} (mined ${date} UTC). Merkle root matches. Trust mode: ${trustLabel} — block header is trusted from explorer API(s), not independently verified against PoW. Proof commits to the event hash. Proof size: ${currentOtsBytes.length} bytes. ${hashMatchNote}.`);
|
||||
otsStampState = {
|
||||
valid: true,
|
||||
label: `Bitcoin block ${att.height}`,
|
||||
detail: `OpenTimestamps proof verified. Bitcoin block ${att.height} (mined ${date} UTC). Merkle root matches. Trust mode: ${trustLabel} — block header is trusted from explorer API(s), not independently verified against PoW. Proof commits to the event hash. Proof size: ${currentOtsBytes.length} bytes. ${hashMatchNote}.`
|
||||
};
|
||||
setOtsCard('stamp', 'valid', 'OTS Stamp: Verified (Bitcoin)', otsStampState.detail);
|
||||
otsUpgradeBtn.style.display = 'none';
|
||||
renderSummary(allValid);
|
||||
} else {
|
||||
setOtsBadge('pq-ots-badge-none', 'OTS: Verification failed');
|
||||
const errDetail = result.errors.length > 0 ? ` Errors: ${result.errors.join('; ')}` : '';
|
||||
setOtsInfo(`OpenTimestamps proof could NOT be cryptographically verified. ${hashMatchNote}.${errDetail}`);
|
||||
otsStampState = { valid: false, label: '', detail: '' };
|
||||
setOtsCard('stamp', 'invalid', 'OTS Stamp: Verification failed',
|
||||
`OpenTimestamps proof could NOT be cryptographically verified. ${hashMatchNote}.${errDetail}`);
|
||||
otsUpgradeBtn.style.display = 'inline-block';
|
||||
renderSummary(allValid);
|
||||
}
|
||||
}).catch(err => {
|
||||
setOtsBadge('pq-ots-badge-none', 'OTS: Verification error');
|
||||
setOtsInfo(`OpenTimestamps verification error: ${err.message}. ${hashMatchNote}.`);
|
||||
otsStampState = { valid: false, label: '', detail: '' };
|
||||
setOtsCard('stamp', 'invalid', 'OTS Stamp: Verification error',
|
||||
`OpenTimestamps verification error: ${err.message}. ${hashMatchNote}.`);
|
||||
otsUpgradeBtn.style.display = 'inline-block';
|
||||
renderSummary(allValid);
|
||||
});
|
||||
} else {
|
||||
setOtsBadge('pq-ots-badge-pending', 'OTS: Pending');
|
||||
setOtsInfo(`OpenTimestamps proof is pending (no Bitcoin attestation yet). Proof size: ${currentOtsBytes.length} bytes. ${hashMatchNote}. You can try upgrading it below.`);
|
||||
otsUpgradeWrap.style.display = 'block';
|
||||
// Pending — no Bitcoin attestation yet. Reserve the upgraded card
|
||||
// for when the user upgrades the proof below.
|
||||
otsStampState = { valid: 'pending', label: '', detail: '' };
|
||||
setOtsCard('stamp', 'pending', 'OTS Stamp: Pending',
|
||||
`OpenTimestamps proof is pending (no Bitcoin attestation yet). Proof size: ${currentOtsBytes.length} bytes. ${hashMatchNote}. You can try upgrading it below.`);
|
||||
hideOtsCard('upgraded');
|
||||
otsUpgradeBtn.style.display = 'inline-block';
|
||||
otsRepublishBtn.style.display = 'none';
|
||||
renderSummary(allValid);
|
||||
}
|
||||
} catch (e) {
|
||||
setOtsBadge('pq-ots-badge-none', 'OTS: Error');
|
||||
setOtsInfo(`Failed to parse OTS proof: ${e.message}`);
|
||||
otsUpgradeWrap.style.display = 'none';
|
||||
otsStampState = { valid: false, label: '', detail: '' };
|
||||
setOtsCard('stamp', 'invalid', 'OTS Stamp: Error',
|
||||
`Failed to parse OTS proof: ${e.message}`);
|
||||
hideOtsCard('upgraded');
|
||||
otsUpgradeBtn.style.display = 'none';
|
||||
otsRepublishBtn.style.display = 'none';
|
||||
renderSummary(allValid);
|
||||
}
|
||||
} else {
|
||||
setOtsBadge('pq-ots-badge-none', 'OTS: None');
|
||||
setOtsInfo('No OpenTimestamps proof tag found in this event.');
|
||||
otsStampState = { valid: false, label: '', detail: '' };
|
||||
setOtsCard('stamp', 'invalid', 'OTS Stamp: None',
|
||||
'No OpenTimestamps proof tag found in this event.');
|
||||
hideOtsCard('upgraded');
|
||||
otsUpgradeWrap.style.display = 'none';
|
||||
renderSummary(allValid);
|
||||
}
|
||||
|
||||
resultsWrap.style.display = 'block';
|
||||
@@ -322,6 +452,7 @@
|
||||
|
||||
queryBtn.addEventListener('click', async () => {
|
||||
queryBtn.disabled = true;
|
||||
setButtonSpinner(queryBtn);
|
||||
setStatus(queryStatus, 'info', 'Querying relays...');
|
||||
|
||||
const pubkeyInput = document.getElementById('pqPubkeyInput').value.trim();
|
||||
@@ -331,6 +462,7 @@
|
||||
if (!pubkeyHex) {
|
||||
setStatus(queryStatus, 'error', 'Invalid pubkey. Enter a 64-char hex pubkey or an npub.');
|
||||
queryBtn.disabled = false;
|
||||
clearButtonSpinner(queryBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,6 +470,7 @@
|
||||
if (relayUrls.length === 0) {
|
||||
setStatus(queryStatus, 'error', 'Enter at least one relay URL.');
|
||||
queryBtn.disabled = false;
|
||||
clearButtonSpinner(queryBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -373,6 +506,7 @@
|
||||
if (allCandidates.length === 0) {
|
||||
setStatus(queryStatus, 'error', `No proof carrier events found for this pubkey on any of the ${relayUrls.length} relay(s)${lastError ? ' (last error: ' + lastError + ')' : ''}.`);
|
||||
queryBtn.disabled = false;
|
||||
clearButtonSpinner(queryBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -397,6 +531,7 @@
|
||||
if (!kind1Event) {
|
||||
setStatus(queryStatus, 'error', 'Could not parse kind 1 announcement from any candidate.');
|
||||
queryBtn.disabled = false;
|
||||
clearButtonSpinner(queryBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -427,6 +562,7 @@
|
||||
}
|
||||
|
||||
queryBtn.disabled = false;
|
||||
clearButtonSpinner(queryBtn);
|
||||
});
|
||||
|
||||
/* ================================================================
|
||||
@@ -523,10 +659,20 @@
|
||||
setStatus(otsUpgradeStatus, 'success', `Bitcoin attestation verified! Block ${att ? att.height : '?'} (mined ${date} UTC). Trust mode: ${trustLabel}. Proof upgraded (${currentOtsBytes.length} bytes). You can publish a new kind 9999 event carrying the confirmed proof below; it will reference the original via an upgrade_of tag.`);
|
||||
otsUpgradeBtn.style.display = 'none';
|
||||
otsRepublishBtn.style.display = 'inline-block';
|
||||
setOtsBadge('pq-ots-badge-confirmed', 'OTS: Verified (Bitcoin)');
|
||||
setOtsInfo(`OpenTimestamps proof verified. Bitcoin block ${att ? att.height : '?'}. Trust mode: ${trustLabel}. Proof size: ${currentOtsBytes.length} bytes.`);
|
||||
// Render the upgraded proof as a second OTS card and refresh summary.
|
||||
otsUpgradedState = {
|
||||
valid: true,
|
||||
label: `Bitcoin block ${att ? att.height : '?'}`,
|
||||
detail: `OpenTimestamps proof verified. Bitcoin block ${att ? att.height : '?'} (mined ${date} UTC). Trust mode: ${trustLabel}. Proof size: ${currentOtsBytes.length} bytes.`
|
||||
};
|
||||
setOtsCard('upgraded', 'valid', 'OTS Upgraded: Verified (Bitcoin)', otsUpgradedState.detail);
|
||||
renderSummary(true);
|
||||
} else {
|
||||
setStatus(otsUpgradeStatus, 'info', `Proof upgraded but still pending (no Bitcoin attestation yet). ${result.detail ? 'Detail: ' + result.detail : ''} Try again later.`);
|
||||
otsUpgradedState = { valid: 'pending', label: '', detail: '' };
|
||||
setOtsCard('upgraded', 'pending', 'OTS Upgraded: Still pending',
|
||||
`Proof upgraded but still pending (no Bitcoin attestation yet). ${result.detail ? 'Detail: ' + result.detail : ''} Try again later.`);
|
||||
renderSummary(true);
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus(otsUpgradeStatus, 'error', `Upgrade failed: ${error.message}`);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"VERSION": "v0.1.0",
|
||||
"VERSION_NUMBER": "0.1.0",
|
||||
"BUILD_DATE": "2026-07-28T10:01:19.383Z"
|
||||
"VERSION": "v0.1.1",
|
||||
"VERSION_NUMBER": "0.1.1",
|
||||
"BUILD_DATE": "2026-07-29T13:00:25.910Z"
|
||||
}
|
||||
|
||||
+59
-37
@@ -69,6 +69,22 @@
|
||||
.pq-button:hover { opacity: 0.7; }
|
||||
.pq-button:disabled { opacity: 0.3; cursor: not-allowed; }
|
||||
|
||||
/* Braille spinner shown inside a button while its action is running.
|
||||
Frames are advanced by JS (setButtonSpinner); this class just styles
|
||||
the inline spinner glyph. */
|
||||
.pq-btn-spinner {
|
||||
display: inline-block;
|
||||
color: var(--accent-color);
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
margin-right: 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Keep the spinner fully visible even while the button is dimmed
|
||||
(disabled) during a running action. */
|
||||
.pq-button:disabled .pq-btn-spinner { opacity: 1; }
|
||||
|
||||
.pq-button-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
@@ -164,6 +180,31 @@
|
||||
border-left: 4px solid #cc0000;
|
||||
}
|
||||
|
||||
.pq-result-pending {
|
||||
border-left: 4px solid #f0ad4e;
|
||||
}
|
||||
|
||||
/* Multi-line OTS / summary cards wrap their detail text. */
|
||||
.pq-result-item.pq-result-detail {
|
||||
display: block;
|
||||
line-height: 1.6;
|
||||
white-space: normal;
|
||||
}
|
||||
.pq-result-detail .pq-result-head {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.pq-result-detail .pq-result-body {
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.pq-summary-card {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 15px 18px;
|
||||
}
|
||||
|
||||
.pq-event-preview {
|
||||
background: var(--secondary-color);
|
||||
color: var(--primary-color);
|
||||
@@ -211,30 +252,6 @@
|
||||
|
||||
.pq-tab-panel { display: none; }
|
||||
.pq-tab-panel.pq-tab-panel-active { display: block; }
|
||||
|
||||
.pq-ots-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.pq-ots-badge-pending {
|
||||
background: #f0ad4e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pq-ots-badge-confirmed {
|
||||
background: #00aa00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pq-ots-badge-none {
|
||||
background: var(--muted-color);
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -314,24 +331,24 @@
|
||||
<div id="pqSignInStatus"></div>
|
||||
</div>
|
||||
|
||||
<!-- Shared results area -->
|
||||
<div id="pqResultsWrap" style="display: none;">
|
||||
<div class="pq-info-text" style="margin-top: 15px; margin-bottom: 5px;">
|
||||
<strong>Verification Results:</strong>
|
||||
<span id="pqOtsBadge"></span>
|
||||
</div>
|
||||
<div class="pq-result-list" id="pqResultList"></div>
|
||||
</div>
|
||||
|
||||
<!-- Full event JSON (shown at the top once an event is loaded) -->
|
||||
<div id="pqEventJsonWrap" style="display: none; margin-top: 15px;">
|
||||
<div class="pq-info-text" style="margin-bottom: 5px;"><strong>Full event JSON:</strong></div>
|
||||
<div class="pq-event-preview" id="pqEventJson"></div>
|
||||
</div>
|
||||
|
||||
<!-- OTS upgrade section -->
|
||||
<div id="pqOtsUpgradeWrap" style="display: none; margin-top: 15px;">
|
||||
<div class="pq-info-text" style="margin-bottom: 5px;"><strong>OpenTimestamps:</strong></div>
|
||||
<div id="pqOtsInfo" class="pq-status pq-status-info"></div>
|
||||
<!-- Shared results area: signature + PQ checks + OTS cards, all in
|
||||
one continuous card list. The OTS stamp/upgraded cards are
|
||||
appended into pqResultList by JS so the flow is unbroken. -->
|
||||
<div id="pqResultsWrap" style="display: none;">
|
||||
<div class="pq-info-text" style="margin-top: 15px; margin-bottom: 5px;">
|
||||
<strong>Verification Results:</strong>
|
||||
</div>
|
||||
<div class="pq-result-list" id="pqResultList"></div>
|
||||
</div>
|
||||
|
||||
<!-- OTS upgrade actions (cards live in pqResultList above) -->
|
||||
<div id="pqOtsUpgradeWrap" style="display: none; margin-top: 10px;">
|
||||
<div class="pq-button-row">
|
||||
<button class="pq-button" id="pqOtsUpgradeBtn">Upgrade OTS Proof</button>
|
||||
<button class="pq-button" id="pqOtsRepublishBtn" style="display:none;">Publish Upgraded Event</button>
|
||||
@@ -339,6 +356,11 @@
|
||||
<div id="pqOtsUpgradeStatus"></div>
|
||||
</div>
|
||||
|
||||
<!-- Summary card (shown once verification completes) -->
|
||||
<div id="pqSummaryWrap" style="display: none; margin-top: 15px;">
|
||||
<div class="pq-result-item pq-summary-card" id="pqSummaryCard"></div>
|
||||
</div>
|
||||
|
||||
<hr style="border: var(--border); margin: 25px 0 15px;" />
|
||||
|
||||
<div class="pq-info-text" style="color: var(--accent-color); font-size: 13px;">
|
||||
|
||||
Reference in New Issue
Block a user