feat(rdata): 3D force-directed Data Cloud as default view

Rewrite flat SVG radial layout into Canvas 2D with perspective-projected
3D force simulation. Three-tier node hierarchy (space/module/doc), cross-space
module links, shared-tag document connections, edge particles, depth fog,
collapse/expand clusters, orbit/zoom interaction. Default route now loads
Data Cloud instead of Content Tree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-04-15 13:21:40 -04:00
parent fe3e3621af
commit fd7a92b908
2 changed files with 799 additions and 347 deletions

File diff suppressed because it is too large Load Diff

View File

@ -241,7 +241,7 @@ routes.get("/api/content-tree", (c) => {
const DATA_TABS = [ const DATA_TABS = [
{ id: "tree", label: "Content Tree", icon: "🌳" }, { id: "tree", label: "Content Tree", icon: "🌳" },
{ id: "cloud", label: "Cloud", icon: "☁️" }, { id: "cloud", label: "Data Cloud", icon: "☁️" },
{ id: "analytics", label: "Analytics", icon: "📊" }, { id: "analytics", label: "Analytics", icon: "📊" },
] as const; ] as const;
@ -256,7 +256,7 @@ function renderDataPage(space: string, activeTab: string, isSubdomain: boolean)
const scripts = activeTab === "tree" const scripts = activeTab === "tree"
? `<script type="module" src="/modules/rdata/folk-content-tree.js?v=2"></script>` ? `<script type="module" src="/modules/rdata/folk-content-tree.js?v=2"></script>`
: activeTab === "cloud" : activeTab === "cloud"
? `<script type="module" src="/modules/rdata/folk-data-cloud.js?v=1"></script>` ? `<script type="module" src="/modules/rdata/folk-data-cloud.js?v=2"></script>`
: `<script type="module" src="/modules/rdata/folk-analytics-view.js"></script>`; : `<script type="module" src="/modules/rdata/folk-analytics-view.js"></script>`;
return renderShell({ return renderShell({
@ -274,7 +274,7 @@ function renderDataPage(space: string, activeTab: string, isSubdomain: boolean)
// ── Page routes ── // ── Page routes ──
routes.get("/", (c) => { routes.get("/", (c) => {
const space = c.req.param("space") || "demo"; const space = c.req.param("space") || "demo";
return c.html(renderDataPage(space, "tree", c.get("isSubdomain"))); return c.html(renderDataPage(space, "cloud", c.get("isSubdomain")));
}); });
routes.get("/:tabId", (c, next) => { routes.get("/:tabId", (c, next) => {
@ -320,7 +320,7 @@ export const dataModule: RSpaceModule = {
], ],
outputPaths: [ outputPaths: [
{ path: "tree", name: "Content Tree", icon: "🌳", description: "Hierarchical view of all CRDT documents" }, { path: "tree", name: "Content Tree", icon: "🌳", description: "Hierarchical view of all CRDT documents" },
{ path: "cloud", name: "Cloud", icon: "☁️", description: "Tag cloud visualization of content" }, { path: "cloud", name: "Data Cloud", icon: "☁️", description: "3D force-directed graph of all data objects" },
{ path: "analytics", name: "Analytics", icon: "📊", description: "Usage metrics and engagement data" }, { path: "analytics", name: "Analytics", icon: "📊", description: "Usage metrics and engagement data" },
], ],
acceptsFeeds: ["data", "economic"], acceptsFeeds: ["data", "economic"],