From f4490a8dd8356a3c07f2ac4d46a1fb5c0dd1b5fd Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 29 Apr 2026 16:01:11 -0400 Subject: [PATCH 1/9] feat: initial basic dashproof-lite --- .../dashproof-lab/dashproof-lite.html | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 example-apps/dashproof-lab/dashproof-lite.html diff --git a/example-apps/dashproof-lab/dashproof-lite.html b/example-apps/dashproof-lab/dashproof-lite.html new file mode 100644 index 0000000..af06c70 --- /dev/null +++ b/example-apps/dashproof-lab/dashproof-lite.html @@ -0,0 +1,219 @@ + + + + + DashProof Lite + + + +

DashProof Lite

+

Read-only verification against Dash Platform testnet.

+
Connecting…
+ +
+

Verify a file

+

Hashes the file locally with SHA-256, then looks up the digest on-chain. The file never leaves your browser.

+ +
+

+  
+ +
+

History by chainId

+

List all anchors recorded under a given chainId bucket.

+ +
+ +
+

+  
+ +

+ Contract: +

+ + + + From 9eb6e6a36742c3d696c0729048f04e054d5675e3 Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 29 Apr 2026 16:16:51 -0400 Subject: [PATCH 2/9] feat: restyle dashproof-lite with sidebar shell and anchor cards Mirror the React app's sidebar layout (logo, nav, connection dot) and render verify/history results as structured anchor cards instead of raw JSON, keeping the file fully self-contained. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dashproof-lab/dashproof-lite.html | 452 +++++++++++++++--- 1 file changed, 384 insertions(+), 68 deletions(-) diff --git a/example-apps/dashproof-lab/dashproof-lite.html b/example-apps/dashproof-lab/dashproof-lite.html index af06c70..89af5fe 100644 --- a/example-apps/dashproof-lab/dashproof-lite.html +++ b/example-apps/dashproof-lab/dashproof-lite.html @@ -2,51 +2,285 @@ + DashProof Lite + + + -

DashProof Lite

-

Read-only verification against Dash Platform testnet.

-
Connecting…
- -
-

Verify a file

-

Hashes the file locally with SHA-256, then looks up the digest on-chain. The file never leaves your browser.

- -
-

-  
- -
-

History by chainId

-

List all anchors recorded under a given chainId bucket.

- -
- -
-

-  
- -

- Contract: -

+
+ + +
+
+
+ +
+

Pick a file

+

Drop in any file you previously anchored. We compute SHA-256 in the browser and look it up by entryHash.

+ File + +
+
+
+
+ +
+ +
+

Look up by chainId

+

Try demo-proof-fixture-01 for the bundled example fixture.

+ chainId +
+ + +
+
+
+
+ +
+ Contract: +
+ + +
+
+
From 4bd00544c7c63eb2d89ab58e3585b68c744cd696 Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 29 Apr 2026 16:43:08 -0400 Subject: [PATCH 4/9] refactor: group SDK calls and render results as field-list cards Reorder dashproof-lite.html so the Dash Platform SDK surface (connect + queries) is grouped at the top of the script, and replace the raw JSON output with simple per-anchor cards (title row + definition list). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dashproof-lab/dashproof-lite.html | 279 ++++++++++-------- 1 file changed, 161 insertions(+), 118 deletions(-) diff --git a/example-apps/dashproof-lab/dashproof-lite.html b/example-apps/dashproof-lab/dashproof-lite.html index dfc9411..a344d43 100644 --- a/example-apps/dashproof-lab/dashproof-lite.html +++ b/example-apps/dashproof-lab/dashproof-lite.html @@ -47,14 +47,17 @@ input[type="text"] { width: 100%; padding: 0.4rem; box-sizing: border-box; font-family: inherit; } button.action { padding: 0.4rem 0.9rem; cursor: pointer; } button:disabled { cursor: not-allowed; opacity: 0.6; } - pre { background: #f5f5f5; padding: 0.75rem; border-radius: 4px; overflow-x: auto; font-size: 0.85rem; white-space: pre-wrap; word-break: break-all; } .hash { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; } - .ok { color: #1a7f37; } - .miss { color: #b54708; } - .err { color: #b42318; } - #status { font-size: 0.85rem; padding: 0.3rem 0.5rem; background: #fff3cd; border-radius: 4px; display: inline-block; margin-top: 1rem; } - #status.connected { background: #d1f7d6; } - #status.failed { background: #fde2e1; } + .anchor { border: 1px solid #ddd; border-radius: 6px; margin: 0.5rem 0; background: #fafafa; padding: 0.5rem 0.75rem; } + .anchor .title { font-weight: 500; margin-bottom: 0.4rem; padding-bottom: 0.4rem; border-bottom: 1px solid #eee; } + .anchor dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 0.75rem; font-size: 0.85rem; } + .anchor dt { color: #666; text-transform: uppercase; font-size: 0.7rem; letter-spacing: 0.05em; padding-top: 0.15rem; } + .anchor dd { margin: 0; word-break: break-all; font-size: 0.85rem; } + .summary-line { font-size: 0.8rem; color: #666; margin: 0.5rem 0 0.25rem; } + .status-line { padding: 0.5rem 0.75rem; border: 1px solid #ddd; border-radius: 4px; background: #f5f5f5; font-size: 0.85rem; margin-top: 0.5rem; } + .status-line.miss { border-color: #f0c987; background: #fff8e1; } + .status-line.err { border-color: #e8a09a; background: #fdecea; color: #b42318; } + #status { font-size: 0.85rem; color: #666; margin-top: 1rem; } @media (max-width: 600px) { .shell { grid-template-columns: 1fr; } aside { border-right: none; border-bottom: 1px solid #ddd; } @@ -80,7 +83,7 @@

Verify a file

Hashes the file locally with SHA-256, then looks up the digest on-chain. The file never leaves your browser.

-

+        
@@ -90,7 +93,7 @@

History by chainId

-

+        

@@ -100,6 +103,14 @@

History by chainId

+ + From 7dd9e6a35380a65310cad855ea2becc160bf3355 Mon Sep 17 00:00:00 2001 From: thephez Date: Thu, 30 Apr 2026 10:27:32 -0400 Subject: [PATCH 8/9] test: add Playwright e2e for dashproof-lite and exclude lite pages from Prettier Cover the single-file companion's read-only flows (connect, verify match/miss, history hit/miss, tab switching) against live testnet via the existing dashproof-lab Playwright config. Also pin the hand-tuned dashproof-lite and dashmint-lite HTML files in their respective .prettierignore so npm run format stops doubling their line count. Co-Authored-By: Claude Opus 4.7 (1M context) --- example-apps/dashmint-lab/.prettierignore | 1 + example-apps/dashproof-lab/.prettierignore | 1 + example-apps/dashproof-lab/e2e/lite.spec.ts | 152 ++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 example-apps/dashproof-lab/e2e/lite.spec.ts diff --git a/example-apps/dashmint-lab/.prettierignore b/example-apps/dashmint-lab/.prettierignore index 007ea8a..7f89e0c 100644 --- a/example-apps/dashmint-lab/.prettierignore +++ b/example-apps/dashmint-lab/.prettierignore @@ -1,3 +1,4 @@ dist node_modules coverage +public/dashmint-lite.html diff --git a/example-apps/dashproof-lab/.prettierignore b/example-apps/dashproof-lab/.prettierignore index 007ea8a..6f1b734 100644 --- a/example-apps/dashproof-lab/.prettierignore +++ b/example-apps/dashproof-lab/.prettierignore @@ -1,3 +1,4 @@ dist node_modules coverage +public/dashproof-lite.html diff --git a/example-apps/dashproof-lab/e2e/lite.spec.ts b/example-apps/dashproof-lab/e2e/lite.spec.ts new file mode 100644 index 0000000..d9f9f0a --- /dev/null +++ b/example-apps/dashproof-lab/e2e/lite.spec.ts @@ -0,0 +1,152 @@ +import { test, expect } from "./fixtures"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +// Vite serves files under public/ at the URL root, so the lite page lives at +// /dashproof-lite.html during dev and at //dashproof-lite.html after a +// production build. These specs use the dev server (configured in +// playwright.config.ts) so the path is just /dashproof-lite.html. +const LITE_URL = "/dashproof-lite.html"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +function loadFixtureFile(filename: string) { + const path = resolve(__dirname, "../public/example-files", filename); + return { + name: filename, + mimeType: filename.endsWith(".json") + ? "application/json" + : filename.endsWith(".csv") + ? "text/csv" + : "text/plain", + buffer: readFileSync(path), + }; +} + +test.describe("dashproof-lite (single-file companion)", () => { + test("connects to testnet and enables inputs on load", async ({ page }) => { + await page.goto(LITE_URL); + await expect(page.locator("#status")).toHaveText(/Connected to testnet/, { + timeout: 30_000, + }); + await expect(page.locator("#verify-file")).toBeEnabled(); + await expect(page.locator("#chain-id")).toBeEnabled(); + await expect(page.locator("#chain-btn")).toBeEnabled(); + }); + + test("verify match: shows anchor card for a known fixture", async ({ + page, + }) => { + await page.goto(LITE_URL); + await expect(page.locator("#status")).toHaveText(/Connected to testnet/, { + timeout: 30_000, + }); + + const fixture = loadFixtureFile("proof-fixture-01.txt"); + await page.locator("#verify-file").setInputFiles({ + name: fixture.name, + mimeType: fixture.mimeType, + buffer: fixture.buffer, + }); + + // Hash echo above the result confirms SHA-256 ran client-side. + await expect(page.locator("#verify-hash")).toContainText( + "02e4e7cd6b6c73ec895e82d5e59065f30ffbb70f03fdd7d2a575ffd0c333d414", + { timeout: 30_000 }, + ); + + // Anchor card lists the chainId and the decoded SHA-256 hex. + const card = page.locator("#verify-result .anchor"); + await expect(card).toBeVisible({ timeout: 60_000 }); + await expect(card).toContainText("demo-proof-fixture-01"); + await expect(card).toContainText( + "02e4e7cd6b6c73ec895e82d5e59065f30ffbb70f03fdd7d2a575ffd0c333d414", + ); + }); + + test("verify miss: shows not-found banner for a random file", async ({ + page, + randomFilePayload, + }) => { + await page.goto(LITE_URL); + await expect(page.locator("#status")).toHaveText(/Connected to testnet/, { + timeout: 30_000, + }); + + await page.locator("#verify-file").setInputFiles({ + name: randomFilePayload.name, + mimeType: randomFilePayload.mimeType, + buffer: randomFilePayload.buffer, + }); + + await expect( + page.locator("#verify-result .status-line.miss"), + ).toContainText("No anchor found", { timeout: 60_000 }); + }); + + test("history: lists anchors for a known chainId", async ({ page }) => { + await page.goto(LITE_URL); + await expect(page.locator("#status")).toHaveText(/Connected to testnet/, { + timeout: 30_000, + }); + + // Switch to History tab. + await page.locator('.nav-btn[data-tab="history"]').click(); + await expect(page.locator("#panel-history")).toHaveClass(/active/); + + await page.locator("#chain-id").fill("demo-proof-fixture-01"); + await page.locator("#chain-btn").click(); + + await expect(page.locator("#chain-result .summary-line")).toContainText( + /\d+ anchor\(s\) found/, + { timeout: 60_000 }, + ); + const cards = page.locator("#chain-result .anchor"); + await expect(cards.first()).toBeVisible(); + await expect(cards.first()).toContainText("demo-proof-fixture-01"); + }); + + test("history miss: shows not-found banner for an unknown chainId", async ({ + page, + }) => { + await page.goto(LITE_URL); + await expect(page.locator("#status")).toHaveText(/Connected to testnet/, { + timeout: 30_000, + }); + + await page.locator('.nav-btn[data-tab="history"]').click(); + await page + .locator("#chain-id") + .fill( + `bogus-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, + ); + await page.locator("#chain-btn").click(); + + await expect(page.locator("#chain-result .status-line.miss")).toContainText( + "No anchors found", + { timeout: 60_000 }, + ); + }); + + test("tab switching toggles panel visibility without reload", async ({ + page, + }) => { + await page.goto(LITE_URL); + await expect(page.locator("#status")).toHaveText(/Connected to testnet/, { + timeout: 30_000, + }); + + // Verify is active on load. + await expect(page.locator("#panel-verify")).toHaveClass(/active/); + await expect(page.locator("#panel-history")).not.toHaveClass(/active/); + + await page.locator('.nav-btn[data-tab="history"]').click(); + await expect(page.locator("#panel-history")).toHaveClass(/active/); + await expect(page.locator("#panel-verify")).not.toHaveClass(/active/); + + await page.locator('.nav-btn[data-tab="verify"]').click(); + await expect(page.locator("#panel-verify")).toHaveClass(/active/); + await expect(page.locator("#panel-history")).not.toHaveClass(/active/); + }); +}); From 97c1eeb99cb6166e87f967187470ba3f7d431d3c Mon Sep 17 00:00:00 2001 From: thephez Date: Thu, 30 Apr 2026 10:50:24 -0400 Subject: [PATCH 9/9] chore: pin evo-sdk version in lite pages to match React apps Both lite pages were importing @dashevo/evo-sdk via the bare esm.sh specifier, silently following the npm latest tag. Pin to 3.1.0-dev.1 so they track the same SDK version each app's package.json depends on. Co-Authored-By: Claude Opus 4.7 (1M context) --- example-apps/dashmint-lab/public/dashmint-lite.html | 7 ++++--- example-apps/dashproof-lab/public/dashproof-lite.html | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/example-apps/dashmint-lab/public/dashmint-lite.html b/example-apps/dashmint-lab/public/dashmint-lite.html index 7f47d19..41de9f9 100644 --- a/example-apps/dashmint-lab/public/dashmint-lite.html +++ b/example-apps/dashmint-lab/public/dashmint-lite.html @@ -116,9 +116,10 @@

Browse cards

// ============================================================================ // Pull the Evo SDK from esm.sh — no bundler needed. esm.sh resolves the npm - // package and serves it as a browser-native ES module. Pin a specific - // version (e.g. '@dashevo/evo-sdk@3.1.0-dev.1') for reproducibility. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk'; + // package and serves it as a browser-native ES module. Pinned to the same + // version the React app at ../package.json depends on so both UIs behave + // identically against the same testnet contract. + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@3.1.0-dev.1'; // The "card" data contract is already published on testnet by the React app. // Anyone querying with the same contract id hits the same documents. diff --git a/example-apps/dashproof-lab/public/dashproof-lite.html b/example-apps/dashproof-lab/public/dashproof-lite.html index 9330ff8..ef53352 100644 --- a/example-apps/dashproof-lab/public/dashproof-lite.html +++ b/example-apps/dashproof-lab/public/dashproof-lite.html @@ -117,9 +117,10 @@

History by chainId

// ============================================================================ // Pull the Evo SDK from esm.sh — no bundler needed. esm.sh resolves the npm - // package and serves it as a browser-native ES module. Pin a specific - // version (e.g. '@dashevo/evo-sdk@3.1.0-dev.1') for reproducibility. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk'; + // package and serves it as a browser-native ES module. Pinned to the same + // version the React app at ../package.json depends on so both UIs behave + // identically against the same testnet contract. + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@3.1.0-dev.1'; // The "anchor" data contract is already published on testnet by the React app. // Anyone querying with the same contract id hits the same documents.