From eb6207d7bb7c430fedff0b462c3dc26471d12f21 Mon Sep 17 00:00:00 2001 From: m5x5 Date: Wed, 20 May 2026 00:49:22 +0200 Subject: [PATCH 01/24] feat(viewer): spa rewrite with drafts, flags, submissions, and vercel deploy - real subroutes via tiny SPA router and dev-server.js fallback - avatar menu houses login (uvdsl OIDC), drafts, edit requests, submissions, flags, filters - card grid view of every section with title/description/clickable domain icons - /drafts: save records locally, edit, publish later - /edits: log of locally-published changes - /submissions: LDP listing of central pod new-data folder with edit/new diff - /flags: per-record flags (invalid link, archived, etc.) - solid-ui-button (vendored) for header actions - lucide icons via npm - vercel deploy: scripts/build.js produces dist/ with vercel.json SPA rewrites Co-Authored-By: Claude Opus 4.7 --- .gitignore | 2 + dev-server.js | 81 + index.html | 165 +- package-lock.json | 10149 +++++++++++------------------ package.json | 6 +- scripts/build.js | 85 + vercel.json | 27 + viewer/auth.js | 332 + viewer/drafts.js | 55 + viewer/editRequests.js | 52 + viewer/flags.js | 65 + viewer/form.js | 94 +- viewer/forms.css | 24 +- viewer/icons.js | 17 + viewer/makeTOC.js | 102 +- viewer/notify.js | 242 + viewer/overlay.js | 81 + viewer/page-content.js | 18 +- viewer/pages.js | 10 +- viewer/showRecord.js | 74 +- viewer/solid-ui.js | 11 + viewer/submissionLog.js | 111 + viewer/utils.js | 126 +- viewer/vendor/solid-ui/Button.js | 325 + viewer/viewer.css | 1042 ++- viewer/viewer.js | 1184 +++- 26 files changed, 7941 insertions(+), 6539 deletions(-) create mode 100644 dev-server.js create mode 100644 scripts/build.js create mode 100644 vercel.json create mode 100644 viewer/auth.js create mode 100644 viewer/drafts.js create mode 100644 viewer/editRequests.js create mode 100644 viewer/flags.js create mode 100644 viewer/icons.js create mode 100644 viewer/notify.js create mode 100644 viewer/overlay.js create mode 100644 viewer/solid-ui.js create mode 100644 viewer/submissionLog.js create mode 100644 viewer/vendor/solid-ui/Button.js diff --git a/.gitignore b/.gitignore index 32decd8..3c06184 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ catalog-shacl.ttl /node_modules /test/tmp /.idea + +dist/ diff --git a/dev-server.js b/dev-server.js new file mode 100644 index 0000000..8d958ac --- /dev/null +++ b/dev-server.js @@ -0,0 +1,81 @@ +#!/usr/bin/env node +// Minimal static dev server with SPA fallback. +// Serves files from the project root; for any GET that does not resolve +// to an existing file (e.g. /apps, /learning), responds with index.html. + +import http from 'node:http'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.dirname(fileURLToPath(import.meta.url)); +const port = Number(process.env.PORT || 8765); + +const MIME = { + '.html': 'text/html; charset=utf-8', + '.js': 'application/javascript; charset=utf-8', + '.mjs': 'application/javascript; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.svg': 'image/svg+xml', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.ico': 'image/x-icon', + '.ttl': 'text/turtle; charset=utf-8', + '.shce': 'text/plain; charset=utf-8', + '.txt': 'text/plain; charset=utf-8', + '.map': 'application/json; charset=utf-8', + '.woff': 'font/woff', + '.woff2':'font/woff2', +}; + +function safeJoin(base, p){ + const target = path.normalize(path.join(base, p)); + if(!target.startsWith(base)) return null; + return target; +} + +function send(res, status, body, type='text/plain; charset=utf-8'){ + res.writeHead(status, { 'Content-Type': type, 'Cache-Control': 'no-cache' }); + res.end(body); +} + +const server = http.createServer((req, res) => { + try { + const url = new URL(req.url, 'http://localhost'); + const decoded = decodeURIComponent(url.pathname); + const target = safeJoin(root, decoded); + if(!target){ return send(res, 400, 'Bad path'); } + + fs.stat(target, (err, stat) => { + if(!err && stat.isFile()){ + const ext = path.extname(target).toLowerCase(); + res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream', 'Cache-Control': 'no-cache' }); + fs.createReadStream(target).pipe(res); + return; + } + // SPA fallback: serve index.html ONLY for top-level app routes (no `.` + // in the last segment, and not under /node_modules or /viewer where a + // missing file is a real 404). This avoids masking bad ESM imports. + const last = decoded.split('/').pop() || ''; + const isAssetPath = decoded.startsWith('/node_modules/') || + decoded.startsWith('/viewer/') || + decoded.startsWith('/assets/'); + if(!last.includes('.') && !isAssetPath){ + const idx = path.join(root, 'index.html'); + res.writeHead(200, { 'Content-Type': MIME['.html'], 'Cache-Control': 'no-cache' }); + fs.createReadStream(idx).pipe(res); + return; + } + send(res, 404, 'Not found: ' + decoded); + }); + } catch(e){ + send(res, 500, String(e)); + } +}); + +server.listen(port, () => { + console.log(`Solid Resources Catalog dev server: http://localhost:${port}/`); +}); diff --git a/index.html b/index.html index 9410acf..6b9d332 100644 --- a/index.html +++ b/index.html @@ -4,29 +4,159 @@ Solid Resources Catalog - + + + + + - - + + +
-
- - - Solid Resources Catalog - - - - - + + +
+ + + + + + + + +
@@ -45,7 +175,6 @@