feat(rgov): standalone n8n-style GovMod circuit canvas
Add <folk-gov-circuit> component with interactive SVG canvas pre-loaded with 3 demo governance circuits. 8 node types (signoff, threshold, knob, project, quadratic, conviction, multisig, sankey) with pan/zoom, node dragging, Bezier wiring, palette sidebar, and detail panel. Compatible with rspace canvas shape types for rapplet integration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5b4ab00a2d
commit
2e2fbae8bf
File diff suppressed because it is too large
Load Diff
|
|
@ -8,7 +8,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { resolve } from "path";
|
|
||||||
import { renderShell } from "../../server/shell";
|
import { renderShell } from "../../server/shell";
|
||||||
import { getModuleInfoList } from "../../shared/module";
|
import { getModuleInfoList } from "../../shared/module";
|
||||||
import type { RSpaceModule } from "../../shared/module";
|
import type { RSpaceModule } from "../../shared/module";
|
||||||
|
|
@ -17,53 +16,10 @@ import { addShapes, getDocumentData } from "../../server/community-store";
|
||||||
|
|
||||||
const routes = new Hono();
|
const routes = new Hono();
|
||||||
|
|
||||||
// ── Canvas content loader (same approach as rspace module) ──
|
// ── Module page — renders standalone GovMod circuit canvas ──
|
||||||
|
|
||||||
const DIST_DIR = resolve(import.meta.dir, "../../dist");
|
routes.get("/", (c) => {
|
||||||
let canvasCache: { body: string; styles: string; scripts: string } | null = null;
|
|
||||||
|
|
||||||
function extractCanvasContent(html: string) {
|
|
||||||
const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
||||||
const styleMatches = [...html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi)];
|
|
||||||
const scriptMatches = [...html.matchAll(/<script[^>]*>[\s\S]*?<\/script>/gi)];
|
|
||||||
return {
|
|
||||||
body: bodyMatch?.[1] || "",
|
|
||||||
styles: styleMatches.map(m => m[0]).join("\n"),
|
|
||||||
scripts: scriptMatches.map(m => m[0]).join("\n"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCanvasContent() {
|
|
||||||
if (canvasCache) return canvasCache;
|
|
||||||
|
|
||||||
const moduleFile = Bun.file(resolve(DIST_DIR, "canvas-module.html"));
|
|
||||||
if (await moduleFile.exists()) {
|
|
||||||
canvasCache = {
|
|
||||||
body: await moduleFile.text(),
|
|
||||||
styles: "",
|
|
||||||
scripts: `<script type="module" src="/canvas-module.js"></script>`,
|
|
||||||
};
|
|
||||||
return canvasCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullFile = Bun.file(resolve(DIST_DIR, "canvas.html"));
|
|
||||||
if (await fullFile.exists()) {
|
|
||||||
canvasCache = extractCanvasContent(await fullFile.text());
|
|
||||||
return canvasCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
body: `<div style="padding:2rem;text-align:center;color:#64748b;">Canvas loading...</div>`,
|
|
||||||
styles: "",
|
|
||||||
scripts: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Module page (within a space) — renders canvas directly ──
|
|
||||||
|
|
||||||
routes.get("/", async (c) => {
|
|
||||||
const space = c.req.param("space") || "demo";
|
const space = c.req.param("space") || "demo";
|
||||||
const canvas = await getCanvasContent();
|
|
||||||
|
|
||||||
return c.html(renderShell({
|
return c.html(renderShell({
|
||||||
title: `${space} — rGov | rSpace`,
|
title: `${space} — rGov | rSpace`,
|
||||||
|
|
@ -71,9 +27,8 @@ routes.get("/", async (c) => {
|
||||||
spaceSlug: space,
|
spaceSlug: space,
|
||||||
modules: getModuleInfoList(),
|
modules: getModuleInfoList(),
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
body: canvas.body,
|
body: `<folk-gov-circuit space="${space}" style="width:100%;height:100%;display:block;"></folk-gov-circuit>`,
|
||||||
styles: canvas.styles,
|
scripts: `<script type="module" src="/modules/rgov/folk-gov-circuit.js"></script>`,
|
||||||
scripts: canvas.scripts,
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1394,6 +1394,27 @@ export default defineConfig({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Build rGov circuit canvas component ──
|
||||||
|
mkdirSync(resolve(__dirname, "dist/modules/rgov"), { recursive: true });
|
||||||
|
await wasmBuild({
|
||||||
|
configFile: false,
|
||||||
|
root: resolve(__dirname, "modules/rgov/components"),
|
||||||
|
build: {
|
||||||
|
emptyOutDir: false,
|
||||||
|
outDir: resolve(__dirname, "dist/modules/rgov"),
|
||||||
|
lib: {
|
||||||
|
entry: resolve(__dirname, "modules/rgov/components/folk-gov-circuit.ts"),
|
||||||
|
formats: ["es"],
|
||||||
|
fileName: () => "folk-gov-circuit.js",
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
entryFileNames: "folk-gov-circuit.js",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// ── Generate content hashes for cache-busting ──
|
// ── Generate content hashes for cache-busting ──
|
||||||
const { readdirSync, readFileSync, writeFileSync, statSync: statSync2 } = await import("node:fs");
|
const { readdirSync, readFileSync, writeFileSync, statSync: statSync2 } = await import("node:fs");
|
||||||
const { createHash } = await import("node:crypto");
|
const { createHash } = await import("node:crypto");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue