diff --git a/docker-compose.standalone.yml b/docker-compose.standalone.yml
index 20e59b8..758d366 100644
--- a/docker-compose.standalone.yml
+++ b/docker-compose.standalone.yml
@@ -109,11 +109,11 @@ services:
traefik.http.routers.rchoices-sa.entrypoints: web
traefik.http.services.rchoices-sa.loadbalancer.server.port: "3000"
- # ── rFunds ──
- rfunds-standalone:
+ # ── rFlows ──
+ rflows-standalone:
<<: *standalone-base
- container_name: rfunds-standalone
- command: ["bun", "run", "modules/rfunds/standalone.ts"]
+ container_name: rflows-standalone
+ command: ["bun", "run", "modules/rflows/standalone.ts"]
environment:
<<: *base-env
FLOW_SERVICE_URL: http://payment-flow:3010
@@ -125,9 +125,9 @@ services:
- payment-network
labels:
<<: *traefik-enabled
- traefik.http.routers.rfunds-sa.rule: Host(`rfunds.online`)
- traefik.http.routers.rfunds-sa.entrypoints: web
- traefik.http.services.rfunds-sa.loadbalancer.server.port: "3000"
+ traefik.http.routers.rflows-sa.rule: Host(`rflows.online`)
+ traefik.http.routers.rflows-sa.entrypoints: web
+ traefik.http.services.rflows-sa.loadbalancer.server.port: "3000"
# ── rFiles ──
rfiles-standalone:
diff --git a/docker-compose.yml b/docker-compose.yml
index 0d9b38a..e3d6a08 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -75,10 +75,10 @@ services:
- "traefik.http.routers.rspace-rchoices.entrypoints=web"
- "traefik.http.routers.rspace-rchoices.priority=120"
- "traefik.http.routers.rspace-rchoices.service=rspace-online"
- - "traefik.http.routers.rspace-rfunds.rule=Host(`rfunds.online`) || HostRegexp(`{sub:[a-z0-9-]+}.rfunds.online`)"
- - "traefik.http.routers.rspace-rfunds.entrypoints=web"
- - "traefik.http.routers.rspace-rfunds.priority=120"
- - "traefik.http.routers.rspace-rfunds.service=rspace-online"
+ - "traefik.http.routers.rspace-rflows.rule=Host(`rflows.online`) || HostRegexp(`{sub:[a-z0-9-]+}.rflows.online`)"
+ - "traefik.http.routers.rspace-rflows.entrypoints=web"
+ - "traefik.http.routers.rspace-rflows.priority=120"
+ - "traefik.http.routers.rspace-rflows.service=rspace-online"
- "traefik.http.routers.rspace-rforum.rule=Host(`rforum.online`) || HostRegexp(`{sub:[a-z0-9-]+}.rforum.online`)"
- "traefik.http.routers.rspace-rforum.entrypoints=web"
- "traefik.http.routers.rspace-rforum.priority=120"
diff --git a/lib/folk-rapp.ts b/lib/folk-rapp.ts
index 77fd71a..bbd00e6 100644
--- a/lib/folk-rapp.ts
+++ b/lib/folk-rapp.ts
@@ -26,7 +26,7 @@ const MODULE_META: Record Widge
};
},
},
- rfunds: {
+ rflows: {
path: "/api/flows",
transform: (data) => {
const flows = Array.isArray(data) ? data : [];
diff --git a/modules/rcal/mod.ts b/modules/rcal/mod.ts
index 0bd0055..a7c8285 100644
--- a/modules/rcal/mod.ts
+++ b/modules/rcal/mod.ts
@@ -191,11 +191,11 @@ function seedDemoIfEmpty(space: string) {
sourceId: sprintsId, allDay: true,
},
{
- title: "rFunds Budget Review",
+ title: "rFlows Budget Review",
desc: "Quarterly review of treasury flows, enoughness thresholds, and overflow routing.",
start: daysFromNow(6, 15, 0), end: daysFromNow(6, 17, 0),
sourceId: communityId, isVirtual: true,
- virtualUrl: "https://meet.jit.si/rfunds-review", virtualPlatform: "Jitsi",
+ virtualUrl: "https://meet.jit.si/rflows-review", virtualPlatform: "Jitsi",
},
{
title: "Cosmolocal Design Sprint",
diff --git a/modules/rcart/landing.ts b/modules/rcart/landing.ts
index 8242ae8..f669534 100644
--- a/modules/rcart/landing.ts
+++ b/modules/rcart/landing.ts
@@ -41,7 +41,7 @@ export function renderLanding(): string {
💰
Revenue Splits
-
Creator, community, and provider shares calculated automatically via rFunds. Transparent by default.
+
Creator, community, and provider shares calculated automatically via rFlows. Transparent by default.
@@ -82,7 +82,7 @@ export function renderLanding(): string {
Provider matching — automatic routing by capability, location, and cost
- Revenue splits — creator, community, and provider shares via rFunds
+ Revenue splits — creator, community, and provider shares via rFlows
Order tracking — real-time status from accepted to delivered
Volume pricing — automatic tier detection from pooled orders
diff --git a/modules/rdata/components/folk-analytics-view.ts b/modules/rdata/components/folk-analytics-view.ts
index c4afc76..723da48 100644
--- a/modules/rdata/components/folk-analytics-view.ts
+++ b/modules/rdata/components/folk-analytics-view.ts
@@ -29,7 +29,7 @@ class FolkAnalyticsView extends HTMLElement {
cookiesSet: 0,
scriptSize: "~2KB",
selfHosted: true,
- apps: ["rSpace", "rBooks", "rCart", "rFunds", "rVote", "rWork", "rCal", "rTrips", "rPubs", "rForum", "rInbox", "rMaps", "rTube", "rChoices", "rWallet", "rData", "rNotes"],
+ apps: ["rSpace", "rBooks", "rCart", "rFlows", "rVote", "rWork", "rCal", "rTrips", "rPubs", "rForum", "rInbox", "rMaps", "rTube", "rChoices", "rWallet", "rData", "rNotes"],
dashboardUrl: "https://analytics.rspace.online",
};
this.render();
diff --git a/modules/rdata/mod.ts b/modules/rdata/mod.ts
index 3595fdd..da12662 100644
--- a/modules/rdata/mod.ts
+++ b/modules/rdata/mod.ts
@@ -17,7 +17,7 @@ const UMAMI_URL = process.env.UMAMI_URL || "https://analytics.rspace.online";
const UMAMI_WEBSITE_ID = process.env.UMAMI_WEBSITE_ID || "292f6ac6-79f8-497b-ba6a-7a51e3b87b9f";
const TRACKED_APPS = [
- "rSpace", "rNotes", "rVote", "rFunds", "rCart", "rWallet",
+ "rSpace", "rNotes", "rVote", "rFlows", "rCart", "rWallet",
"rPubs", "rChoices", "rCal", "rForum", "rInbox", "rFiles",
"rTrips", "rTube", "rWork", "rNetwork", "rData",
];
diff --git a/modules/rflows/components/flows-demo.ts b/modules/rflows/components/flows-demo.ts
index 466c25a..e6459a3 100644
--- a/modules/rflows/components/flows-demo.ts
+++ b/modules/rflows/components/flows-demo.ts
@@ -1,5 +1,5 @@
/**
- * rFunds demo — client-side WebSocket controller.
+ * rFlows demo — client-side WebSocket controller.
*
* Connects via DemoSync, extracts expenses and budget from shapes,
* renders/updates budget overview, expense list, balances, settlements,
diff --git a/modules/rflows/components/flows.css b/modules/rflows/components/flows.css
index 1c6db8d..7a90e06 100644
--- a/modules/rflows/components/flows.css
+++ b/modules/rflows/components/flows.css
@@ -1,178 +1,191 @@
-/* ── Funds module theme ───────────────────────────────── */
+/* ── Flows module theme ───────────────────────────────── */
+
+/* ── Base ────────────────────────────────────────────── */
+.flows-landing, .flows-detail {
+ font-family: system-ui, -apple-system, sans-serif;
+}
+
+/* Thin scrollbars (rApp convention) */
+.flows-detail ::-webkit-scrollbar,
+.flows-landing ::-webkit-scrollbar { width: 6px; height: 6px; }
+.flows-detail ::-webkit-scrollbar-track,
+.flows-landing ::-webkit-scrollbar-track { background: transparent; }
+.flows-detail ::-webkit-scrollbar-thumb,
+.flows-landing ::-webkit-scrollbar-thumb { background: #475569; border-radius: 3px; }
/* ── Shared utility classes ──────────────────────────── */
-.funds-loading { text-align: center; color: #64748b; padding: 48px 16px; font-size: 14px; }
-.funds-error { text-align: center; color: #ef4444; padding: 20px 16px; font-size: 14px; }
+.flows-loading { text-align: center; color: #64748b; padding: 48px 16px; font-size: 14px; }
+.flows-error { text-align: center; color: #ef4444; padding: 20px 16px; font-size: 14px; }
/* ── Landing page ────────────────────────────────────── */
-.funds-landing { max-width: 960px; margin: 0 auto; padding: 24px 20px 64px; }
+.flows-landing { max-width: 960px; margin: 0 auto; padding: 24px 20px 64px; }
/* Features grid */
-.funds-features { margin-bottom: 48px; }
-.funds-features__grid {
+.flows-features { margin-bottom: 48px; }
+.flows-features__grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 12px;
}
-.funds-features__card {
- background: #1e293b; border: 1px solid #334155; border-radius: 10px; padding: 20px;
+.flows-features__card {
+ background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 20px;
transition: border-color 0.2s;
}
-.funds-features__card:hover { border-color: #475569; }
-.funds-features__icon { font-size: 24px; margin-bottom: 8px; }
-.funds-features__card h3 { font-size: 14px; font-weight: 600; color: #e2e8f0; margin: 0 0 6px; }
-.funds-features__card p { font-size: 12px; color: #94a3b8; line-height: 1.6; margin: 0; }
+.flows-features__card:hover { border-color: #475569; }
+.flows-features__icon { font-size: 24px; margin-bottom: 8px; }
+.flows-features__card h3 { font-size: 14px; font-weight: 600; color: #e2e8f0; margin: 0 0 6px; }
+.flows-features__card p { font-size: 12px; color: #94a3b8; line-height: 1.6; margin: 0; }
/* Flow list */
-.funds-flows { margin-bottom: 48px; }
-.funds-flows__header { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 16px; gap: 12px; flex-wrap: wrap; }
-.funds-flows__heading { font-size: 18px; font-weight: 600; color: #e2e8f0; margin: 0; }
-.funds-flows__user { font-size: 12px; color: #64748b; }
-.funds-flows__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
-.funds-flows__empty {
+.flows-flows { margin-bottom: 48px; }
+.flows-flows__header { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 16px; gap: 12px; flex-wrap: wrap; }
+.flows-flows__heading { font-size: 18px; font-weight: 600; color: #e2e8f0; margin: 0; }
+.flows-flows__user { font-size: 12px; color: #64748b; }
+.flows-flows__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
+.flows-flows__empty {
text-align: center; color: #64748b; padding: 32px 16px; font-size: 14px;
- background: #1e293b; border: 1px solid #334155; border-radius: 10px;
+ background: #1e293b; border: 1px solid #334155; border-radius: 8px;
}
-.funds-flows__empty a { color: #6366f1; text-decoration: none; }
-.funds-flows__empty a:hover { text-decoration: underline; }
+.flows-flows__empty a { color: #6366f1; text-decoration: none; }
+.flows-flows__empty a:hover { text-decoration: underline; }
-.funds-flow-card {
+.flows-flow-card {
display: block; text-decoration: none;
- background: #1e293b; border: 1px solid #334155; border-radius: 10px;
+ background: #1e293b; border: 1px solid #334155; border-radius: 8px;
padding: 16px; cursor: pointer; transition: border-color 0.2s, transform 0.15s;
}
-.funds-flow-card:hover { border-color: #6366f1; transform: translateY(-1px); }
-.funds-flow-card__name { font-size: 15px; font-weight: 600; color: #e2e8f0; margin-bottom: 4px; }
-.funds-flow-card__value { font-size: 20px; font-weight: 700; color: #0ea5e9; margin-bottom: 4px; }
-.funds-flow-card__meta { font-size: 12px; color: #64748b; }
+.flows-flow-card:hover { border-color: #6366f1; transform: translateY(-1px); }
+.flows-flow-card__name { font-size: 15px; font-weight: 600; color: #e2e8f0; margin-bottom: 4px; }
+.flows-flow-card__value { font-size: 20px; font-weight: 700; color: #0ea5e9; margin-bottom: 4px; }
+.flows-flow-card__meta { font-size: 12px; color: #64748b; }
/* About / how-it-works section */
-.funds-about { margin-bottom: 48px; }
-.funds-about__heading { font-size: 18px; font-weight: 600; color: #e2e8f0; margin: 0 0 20px; }
+.flows-about { margin-bottom: 48px; }
+.flows-about__heading { font-size: 18px; font-weight: 600; color: #e2e8f0; margin: 0 0 20px; }
/* Steps layout (replaces the old card grid for "how it works") */
-.funds-about__steps { display: flex; flex-direction: column; gap: 16px; }
-.funds-about__step {
+.flows-about__steps { display: flex; flex-direction: column; gap: 16px; }
+.flows-about__step {
display: flex; gap: 16px; align-items: flex-start;
- background: #1e293b; border: 1px solid #334155; border-radius: 10px; padding: 20px;
+ background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 20px;
}
-.funds-about__step-num {
+.flows-about__step-num {
width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
background: #4f46e5; color: #fff; font-weight: 700; font-size: 14px;
display: flex; align-items: center; justify-content: center;
}
-.funds-about__step h3 { font-size: 14px; font-weight: 600; color: #e2e8f0; margin: 0 0 4px; }
-.funds-about__step p { font-size: 13px; color: #94a3b8; line-height: 1.6; margin: 0; }
+.flows-about__step h3 { font-size: 14px; font-weight: 600; color: #e2e8f0; margin: 0 0 4px; }
+.flows-about__step p { font-size: 13px; color: #94a3b8; line-height: 1.6; margin: 0; }
/* Legacy about grid (kept for compat) */
-.funds-about__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
-.funds-about__card {
- background: #1e293b; border: 1px solid #334155; border-radius: 10px; padding: 20px;
+.flows-about__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
+.flows-about__card {
+ background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 20px;
}
-.funds-about__icon { font-size: 28px; margin-bottom: 8px; }
-.funds-about__card h3 { font-size: 15px; font-weight: 600; color: #e2e8f0; margin: 0 0 8px; }
-.funds-about__card p { font-size: 13px; color: #94a3b8; line-height: 1.6; margin: 0; }
+.flows-about__icon { font-size: 28px; margin-bottom: 8px; }
+.flows-about__card h3 { font-size: 15px; font-weight: 600; color: #e2e8f0; margin: 0 0 8px; }
+.flows-about__card p { font-size: 13px; color: #94a3b8; line-height: 1.6; margin: 0; }
/* ── Detail view ─────────────────────────────────────── */
-.funds-detail { max-width: 1100px; margin: 0 auto; padding: 16px 20px 64px; }
+.flows-detail { max-width: 1100px; margin: 0 auto; padding: 16px 20px 64px; }
/* ── Tabs ────────────────────────────────────────────── */
-.funds-tabs {
+.flows-tabs {
display: flex; gap: 4px; border-bottom: 1px solid #1e293b; margin-bottom: 20px;
}
-.funds-tab {
+.flows-tab {
padding: 8px 18px; border: none; border-bottom: 2px solid transparent;
background: transparent; color: #64748b; font-size: 13px; font-weight: 500;
cursor: pointer; transition: color 0.2s, border-color 0.2s;
}
-.funds-tab:hover { color: #e2e8f0; }
-.funds-tab--active { color: #e2e8f0; border-bottom-color: #6366f1; }
+.flows-tab:hover { color: #e2e8f0; }
+.flows-tab--active { color: #e2e8f0; border-bottom-color: #6366f1; }
-.funds-tab-content { min-height: 300px; }
+.flows-tab-content { min-height: 300px; }
/* ── Table tab — card grid ───────────────────────────── */
-.funds-table { }
-.funds-section { margin-bottom: 28px; }
-.funds-section__title { font-size: 14px; font-weight: 600; color: #94a3b8; margin: 0 0 12px; text-transform: uppercase; letter-spacing: 0.05em; }
-.funds-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
+.flows-table { }
+.flows-section { margin-bottom: 28px; }
+.flows-section__title { font-size: 14px; font-weight: 600; color: #94a3b8; margin: 0 0 12px; text-transform: uppercase; letter-spacing: 0.05em; }
+.flows-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
-.funds-card {
- background: #1e293b; border: 1px solid #334155; border-radius: 10px; padding: 16px;
+.flows-card {
+ background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 16px;
}
-.funds-card__header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
-.funds-card__icon { font-size: 18px; }
-.funds-card__label { font-size: 14px; font-weight: 600; color: #e2e8f0; flex: 1; }
-.funds-card__type { font-size: 11px; color: #64748b; text-transform: uppercase; }
-.funds-card__status { font-size: 11px; font-weight: 600; text-transform: capitalize; }
-.funds-card__desc { font-size: 12px; color: #94a3b8; margin-bottom: 10px; line-height: 1.5; }
+.flows-card__header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
+.flows-card__icon { font-size: 18px; }
+.flows-card__label { font-size: 14px; font-weight: 600; color: #e2e8f0; flex: 1; }
+.flows-card__type { font-size: 11px; color: #64748b; text-transform: uppercase; }
+.flows-card__status { font-size: 11px; font-weight: 600; text-transform: capitalize; }
+.flows-card__desc { font-size: 12px; color: #94a3b8; margin-bottom: 10px; line-height: 1.5; }
-.funds-card__stat { margin-bottom: 10px; }
-.funds-card__stat-value { font-size: 18px; font-weight: 700; color: #e2e8f0; }
-.funds-card__stat-label { font-size: 12px; color: #64748b; margin-left: 4px; }
-.funds-card__stats { display: flex; justify-content: space-between; margin-bottom: 8px; }
+.flows-card__stat { margin-bottom: 10px; }
+.flows-card__stat-value { font-size: 18px; font-weight: 700; color: #e2e8f0; }
+.flows-card__stat-label { font-size: 12px; color: #64748b; margin-left: 4px; }
+.flows-card__stats { display: flex; justify-content: space-between; margin-bottom: 8px; }
/* Progress bar */
-.funds-card__bar-container {
+.flows-card__bar-container {
position: relative; height: 6px; background: #334155; border-radius: 3px;
margin-bottom: 10px; overflow: visible;
}
-.funds-card__bar {
+.flows-card__bar {
height: 100%; border-radius: 3px; background: #0ea5e9;
transition: width 0.3s ease;
}
-.funds-card__bar--outcome { opacity: 0.8; }
-.funds-card__bar-threshold {
+.flows-card__bar--outcome { opacity: 0.8; }
+.flows-card__bar-threshold {
position: absolute; top: -3px; width: 2px; height: 12px;
background: #fbbf24; border-radius: 1px;
}
-.funds-card__thresholds {
+.flows-card__thresholds {
display: flex; gap: 12px; font-size: 11px; color: #64748b; margin-bottom: 8px;
}
/* Allocation lists */
-.funds-card__allocs { margin-top: 8px; padding-top: 8px; border-top: 1px solid #334155; }
-.funds-card__alloc-title { font-size: 11px; color: #64748b; font-weight: 600; margin-bottom: 4px; text-transform: uppercase; }
-.funds-card__alloc { font-size: 12px; color: #94a3b8; display: flex; align-items: center; gap: 6px; margin: 2px 0; }
-.funds-card__alloc-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
+.flows-card__allocs { margin-top: 8px; padding-top: 8px; border-top: 1px solid #334155; }
+.flows-card__alloc-title { font-size: 11px; color: #64748b; font-weight: 600; margin-bottom: 4px; text-transform: uppercase; }
+.flows-card__alloc { font-size: 12px; color: #94a3b8; display: flex; align-items: center; gap: 6px; margin: 2px 0; }
+.flows-card__alloc-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
/* Status colors */
-.funds-status--abundant { color: #fbbf24; }
-.funds-status--sufficient { color: #10b981; }
-.funds-status--seeking { color: #0ea5e9; }
-.funds-status--critical { color: #ef4444; }
+.flows-status--abundant { color: #fbbf24; }
+.flows-status--sufficient { color: #10b981; }
+.flows-status--seeking { color: #0ea5e9; }
+.flows-status--critical { color: #ef4444; }
/* ── Interactive canvas (Diagram tab) ───────────────── */
-.funds-canvas-container {
+.flows-canvas-container {
position: relative; height: 70vh; min-height: 400px;
background: #0f172a; border-radius: 12px; border: 1px solid #334155;
- overflow: hidden; user-select: none;
+ overflow: hidden; user-select: none; touch-action: none;
}
-.funds-canvas-svg {
+.flows-canvas-svg {
width: 100%; height: 100%; display: block;
cursor: grab;
}
-.funds-canvas-svg.panning { cursor: grabbing; }
-.funds-canvas-svg.dragging { cursor: move; }
+.flows-canvas-svg.panning { cursor: grabbing; }
+.flows-canvas-svg.dragging { cursor: move; }
/* Toolbar — top-right overlay */
-.funds-canvas-toolbar {
+.flows-canvas-toolbar {
position: absolute; top: 10px; right: 10px; z-index: 10;
display: flex; gap: 4px; flex-wrap: wrap; align-items: center;
}
-.funds-canvas-btn {
+.flows-canvas-btn {
padding: 5px 10px; border: 1px solid #475569; border-radius: 6px;
background: #1e293b; color: #e2e8f0; font-size: 11px; font-weight: 500;
cursor: pointer; white-space: nowrap; transition: background 0.15s, border-color 0.15s;
}
-.funds-canvas-btn:hover { background: #334155; border-color: #64748b; }
-.funds-canvas-btn--source { border-color: #10b981; color: #6ee7b7; }
-.funds-canvas-btn--source:hover { background: #064e3b; }
-.funds-canvas-btn--funnel { border-color: #3b82f6; color: #93c5fd; }
-.funds-canvas-btn--funnel:hover { background: #1e3a5f; }
-.funds-canvas-btn--outcome { border-color: #ec4899; color: #f9a8d4; }
-.funds-canvas-btn--outcome:hover { background: #4a1942; }
-.funds-canvas-btn--active { background: #4f46e5; border-color: #6366f1; color: #fff; }
-.funds-canvas-sep {
+.flows-canvas-btn:hover { background: #334155; border-color: #64748b; }
+.flows-canvas-btn--source { border-color: #10b981; color: #6ee7b7; }
+.flows-canvas-btn--source:hover { background: #064e3b; }
+.flows-canvas-btn--funnel { border-color: #3b82f6; color: #93c5fd; }
+.flows-canvas-btn--funnel:hover { background: #1e3a5f; }
+.flows-canvas-btn--outcome { border-color: #ec4899; color: #f9a8d4; }
+.flows-canvas-btn--outcome:hover { background: #4a1942; }
+.flows-canvas-btn--active { background: #4f46e5; border-color: #6366f1; color: #fff; }
+.flows-canvas-sep {
width: 1px; height: 20px; background: #334155; margin: 0 4px;
}
@@ -183,14 +196,14 @@
.node-glow { filter: drop-shadow(0 0 6px rgba(251,191,36,0.5)); }
/* Editor panel — right side slide-in */
-.funds-editor-panel {
+.flows-editor-panel {
position: absolute; top: 0; right: 0; bottom: 0; width: 320px; z-index: 20;
background: #1e293b; border-left: 1px solid #334155;
transform: translateX(100%); transition: transform 0.25s ease;
overflow-y: auto; padding: 16px;
display: flex; flex-direction: column; gap: 12px;
}
-.funds-editor-panel.open { transform: translateX(0); }
+.flows-editor-panel.open { transform: translateX(0); }
.editor-header {
display: flex; align-items: center; justify-content: space-between;
@@ -243,59 +256,59 @@
.edge-pct { color: #e2e8f0; font-weight: 600; min-width: 30px; text-align: center; }
/* Legend — bottom-left */
-.funds-canvas-legend {
+.flows-canvas-legend {
position: absolute; bottom: 10px; left: 10px; z-index: 10;
display: flex; flex-wrap: wrap; gap: 12px;
font-size: 11px; color: #94a3b8; background: rgba(15,23,42,0.85);
padding: 6px 10px; border-radius: 8px;
}
-.funds-canvas-legend-item { display: flex; align-items: center; gap: 4px; }
-.funds-canvas-legend-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
+.flows-canvas-legend-item { display: flex; align-items: center; gap: 4px; }
+.flows-canvas-legend-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
/* Zoom controls — bottom-right */
-.funds-canvas-zoom {
+.flows-canvas-zoom {
position: absolute; bottom: 10px; right: 10px; z-index: 10;
display: flex; gap: 4px;
}
/* Sufficiency badge — top-left */
-.funds-canvas-badge {
+.flows-canvas-badge {
position: absolute; top: 10px; left: 10px; z-index: 10;
- background: rgba(15,23,42,0.85); border-radius: 10px; padding: 8px 14px;
+ background: rgba(15,23,42,0.85); border-radius: 8px; padding: 8px 14px;
display: flex; align-items: center; gap: 8px;
}
-.funds-canvas-badge__score { font-size: 20px; font-weight: 700; }
-.funds-canvas-badge__label { font-size: 10px; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; }
+.flows-canvas-badge__score { font-size: 20px; font-weight: 700; }
+.flows-canvas-badge__label { font-size: 10px; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; }
/* Legacy diagram (kept for compat) */
-.funds-diagram { overflow-x: auto; }
-.funds-diagram svg { display: block; margin: 0 auto; }
-.funds-diagram__legend {
+.flows-diagram { overflow-x: auto; }
+.flows-diagram svg { display: block; margin: 0 auto; }
+.flows-diagram__legend {
display: flex; flex-wrap: wrap; gap: 16px; justify-content: center;
margin-top: 12px; font-size: 12px; color: #94a3b8;
}
-.funds-diagram__legend-item { display: flex; align-items: center; gap: 5px; }
-.funds-diagram__dot { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
+.flows-diagram__legend-item { display: flex; align-items: center; gap: 5px; }
+.flows-diagram__dot { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
/* ── River tab ───────────────────────────────────────── */
-.funds-river-container { min-height: 500px; }
+.flows-river-container { min-height: 500px; }
/* ── Transactions tab ────────────────────────────────── */
-.funds-tx-list { display: flex; flex-direction: column; gap: 4px; }
-.funds-tx-empty { text-align: center; color: #64748b; padding: 48px 16px; font-size: 14px; }
+.flows-tx-list { display: flex; flex-direction: column; gap: 4px; }
+.flows-tx-empty { text-align: center; color: #64748b; padding: 48px 16px; font-size: 14px; }
-.funds-tx {
+.flows-tx {
display: flex; align-items: center; gap: 12px; padding: 12px 16px;
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
}
-.funds-tx__icon { font-size: 16px; flex-shrink: 0; }
-.funds-tx__body { flex: 1; min-width: 0; }
-.funds-tx__desc { font-size: 13px; color: #e2e8f0; font-weight: 500; }
-.funds-tx__meta { font-size: 11px; color: #64748b; margin-top: 2px; }
-.funds-tx__amount { font-size: 14px; font-weight: 600; white-space: nowrap; }
-.funds-tx__amount--positive { color: #10b981; }
-.funds-tx__amount--negative { color: #ef4444; }
-.funds-tx__time { font-size: 11px; color: #64748b; white-space: nowrap; }
+.flows-tx__icon { font-size: 16px; flex-shrink: 0; }
+.flows-tx__body { flex: 1; min-width: 0; }
+.flows-tx__desc { font-size: 13px; color: #e2e8f0; font-weight: 500; }
+.flows-tx__meta { font-size: 11px; color: #64748b; margin-top: 2px; }
+.flows-tx__amount { font-size: 14px; font-weight: 600; white-space: nowrap; }
+.flows-tx__amount--positive { color: #10b981; }
+.flows-tx__amount--negative { color: #ef4444; }
+.flows-tx__time { font-size: 11px; color: #64748b; white-space: nowrap; }
/* ── Port & wiring ──────────────────────────────────── */
.port-group { pointer-events: all; }
@@ -312,7 +325,7 @@
stroke-linecap: round; animation: wire-dash 0.6s linear infinite;
}
-.funds-canvas-svg.wiring { cursor: crosshair; }
+.flows-canvas-svg.wiring { cursor: crosshair; }
@keyframes port-glow {
0%, 100% { filter: drop-shadow(0 0 4px currentColor); }
@@ -342,40 +355,40 @@
.satisfaction-bar-fill { transition: width 0.3s ease; }
/* ── Node detail modals ──────────────────────────────── */
-.funds-modal-backdrop {
+.flows-modal-backdrop {
position: fixed; inset: 0; z-index: 50;
background: rgba(0,0,0,0.6); display: flex;
align-items: center; justify-content: center;
animation: modalFadeIn 0.15s ease-out;
}
@keyframes modalFadeIn { from { opacity: 0; } to { opacity: 1; } }
-.funds-modal {
+.flows-modal {
background: #1e293b; border-radius: 16px; padding: 24px;
width: 440px; max-height: 85vh; overflow-y: auto;
border: 1px solid #334155; box-shadow: 0 20px 60px rgba(0,0,0,0.5);
animation: modalSlideIn 0.2s ease-out;
}
@keyframes modalSlideIn { from { transform: translateY(12px); opacity: 0; } to { transform: none; opacity: 1; } }
-.funds-modal::-webkit-scrollbar { width: 6px; }
-.funds-modal::-webkit-scrollbar-track { background: transparent; }
-.funds-modal::-webkit-scrollbar-thumb { background: #475569; border-radius: 3px; }
-.funds-modal__header {
+.flows-modal::-webkit-scrollbar { width: 6px; }
+.flows-modal::-webkit-scrollbar-track { background: transparent; }
+.flows-modal::-webkit-scrollbar-thumb { background: #475569; border-radius: 3px; }
+.flows-modal__header {
display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;
}
-.funds-modal__close {
+.flows-modal__close {
background: none; border: none; color: #94a3b8; font-size: 24px; cursor: pointer;
padding: 2px 8px; border-radius: 4px; transition: color 0.15s;
}
-.funds-modal__close:hover { color: #e2e8f0; }
-.funds-modal__progress-bar {
+.flows-modal__close:hover { color: #e2e8f0; }
+.flows-modal__progress-bar {
height: 8px; background: #334155; border-radius: 4px; overflow: hidden; margin-top: 8px;
}
-.funds-modal__progress-fill { height: 100%; border-radius: 4px; transition: width 0.3s; }
+.flows-modal__progress-fill { height: 100%; border-radius: 4px; transition: width 0.3s; }
/* Phase accordion */
.phase-tier-bar { display: flex; gap: 1px; height: 8px; border-radius: 4px; overflow: hidden; margin-bottom: 16px; }
.phase-tier-segment { flex: 1; transition: background 0.3s; }
-.phase-card { border: 1px solid #334155; border-radius: 10px; overflow: hidden; margin-bottom: 8px; }
+.phase-card { border: 1px solid #334155; border-radius: 8px; overflow: hidden; margin-bottom: 8px; }
.phase-card--locked { opacity: 0.5; }
.phase-header {
padding: 10px 14px; cursor: pointer; display: flex; align-items: center; gap: 8px;
@@ -399,7 +412,7 @@
.source-type-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 16px; }
.source-type-btn {
display: flex; flex-direction: column; align-items: center; gap: 6px;
- padding: 14px 8px; border-radius: 10px; border: 2px solid #334155;
+ padding: 14px 8px; border-radius: 8px; border: 2px solid #334155;
background: #0f172a; color: #94a3b8; cursor: pointer; transition: all 0.15s;
font-size: 12px; font-weight: 500;
}
@@ -407,14 +420,14 @@
.source-type-btn--active { border-color: #10b981; background: #064e3b; color: #6ee7b7; }
/* Node hover tooltip */
-.funds-node-tooltip {
+.flows-node-tooltip {
position: absolute; z-index: 30; pointer-events: none;
background: rgba(15,23,42,0.95); border: 1px solid #475569; border-radius: 8px;
padding: 8px 12px; font-size: 12px; color: #e2e8f0;
box-shadow: 0 4px 12px rgba(0,0,0,0.4); white-space: nowrap;
}
-.funds-node-tooltip__label { font-weight: 600; margin-bottom: 2px; }
-.funds-node-tooltip__stat { color: #94a3b8; font-size: 11px; }
+.flows-node-tooltip__label { font-weight: 600; margin-bottom: 2px; }
+.flows-node-tooltip__stat { color: #94a3b8; font-size: 11px; }
/* Sufficiency glow on funnel status text */
@keyframes sufficiencyPulse {
@@ -425,14 +438,17 @@
/* ── Mobile responsive ──────────────────────────────── */
@media (max-width: 768px) {
- .funds-diagram { overflow-x: auto; -webkit-overflow-scrolling: touch; }
- .funds-flows__grid { grid-template-columns: 1fr; }
- .funds-features__grid { grid-template-columns: 1fr; }
- .funds-cards { grid-template-columns: 1fr; }
- .funds-tabs { flex-wrap: wrap; }
- .funds-canvas-container { height: 50vh; min-height: 300px; }
- .funds-canvas-toolbar { flex-wrap: wrap; gap: 3px; top: 6px; right: 6px; }
- .funds-canvas-btn { padding: 4px 7px; font-size: 10px; }
- .funds-editor-panel { width: 100%; }
- .funds-canvas-legend { font-size: 10px; gap: 8px; }
+ .flows-diagram { overflow-x: auto; -webkit-overflow-scrolling: touch; }
+ .flows-flows__grid { grid-template-columns: 1fr; }
+ .flows-features__grid { grid-template-columns: 1fr; }
+ .flows-cards { grid-template-columns: 1fr; }
+ .flows-tabs { flex-wrap: wrap; }
+ .flows-tab { min-height: 44px; min-width: 44px; display: flex; align-items: center; justify-content: center; }
+ .flows-canvas-container { height: 50vh; min-height: 300px; }
+ .flows-canvas-toolbar { flex-wrap: wrap; gap: 3px; top: 6px; right: 6px; }
+ .flows-canvas-btn { padding: 6px 10px; font-size: 11px; min-height: 44px; min-width: 44px; }
+ .flows-editor-panel { width: 100%; }
+ .flows-canvas-legend { font-size: 10px; gap: 8px; }
+ .flows-landing { padding: 16px 12px 48px; }
+ .flows-detail { padding: 12px 12px 48px; }
}
diff --git a/modules/rflows/components/folk-flow-river.ts b/modules/rflows/components/folk-flow-river.ts
index 0f0fc60..dd086c7 100644
--- a/modules/rflows/components/folk-flow-river.ts
+++ b/modules/rflows/components/folk-flow-river.ts
@@ -1,7 +1,7 @@
/**
- * — animated SVG sankey river visualization.
+ * — animated SVG sankey river visualization.
* Pure renderer: receives nodes via setNodes() or falls back to demo data.
- * Parent component (folk-funds-app) handles data fetching and mapping.
+ * Parent component (folk-flows-app) handles data fetching and mapping.
*/
import type { FlowNode, FunnelNodeData, OutcomeNodeData, SourceNodeData, SufficiencyState } from "../lib/types";
@@ -382,7 +382,7 @@ function esc(s: string): string {
// ─── Web Component ──────────────────────────────────────
-class FolkBudgetRiver extends HTMLElement {
+class FolkFlowRiver extends HTMLElement {
private shadow: ShadowRoot;
private nodes: FlowNode[] = [];
private simulating = false;
@@ -521,4 +521,4 @@ class FolkBudgetRiver extends HTMLElement {
}
}
-customElements.define("folk-budget-river", FolkBudgetRiver);
+customElements.define("folk-flow-river", FolkFlowRiver);
diff --git a/modules/rflows/components/folk-flows-app.ts b/modules/rflows/components/folk-flows-app.ts
index 2584f7d..0be2cc3 100644
--- a/modules/rflows/components/folk-flows-app.ts
+++ b/modules/rflows/components/folk-flows-app.ts
@@ -1,5 +1,5 @@
/**
- * — main rFunds application component.
+ * — main rFlows application component.
*
* Views:
* "landing" — TBFF info hero + flow list cards
@@ -56,7 +56,7 @@ function isAuthenticated(): boolean { return getSession() !== null; }
function getAccessToken(): string | null { return getSession()?.accessToken ?? null; }
function getUsername(): string | null { return getSession()?.claims?.username ?? null; }
-class FolkFundsApp extends HTMLElement {
+class FolkFlowsApp extends HTMLElement {
private shadow: ShadowRoot;
private space = "";
private view: View = "landing";
@@ -101,6 +101,11 @@ class FolkFundsApp extends HTMLElement {
private wiringPointerX = 0;
private wiringPointerY = 0;
+ // Touch gesture state (two-finger pinch-to-zoom & pan)
+ private isTouchPanning = false;
+ private lastTouchCenter: { x: number; y: number } | null = null;
+ private lastTouchDist: number | null = null;
+
// Bound handlers for cleanup
private _boundKeyDown: ((e: KeyboardEvent) => void) | null = null;
private _boundPointerMove: ((e: PointerEvent) => void) | null = null;
@@ -132,8 +137,8 @@ class FolkFundsApp extends HTMLElement {
private getApiBase(): string {
const path = window.location.pathname;
- // Subdomain: /rfunds/... or Direct: /{space}/rfunds/...
- const match = path.match(/^(\/[^/]+)?\/rfunds/);
+ // Subdomain: /rflows/... or Direct: /{space}/rflows/...
+ const match = path.match(/^(\/[^/]+)?\/rflows/);
return match ? `${match[0]}` : "";
}
@@ -195,9 +200,9 @@ class FolkFundsApp extends HTMLElement {
}
private getCssPath(): string {
- // In rSpace: /modules/rfunds/funds.css | Standalone: /modules/rfunds/funds.css
- // The shell always serves from /modules/rfunds/ in both modes
- return "/modules/rfunds/funds.css";
+ // In rSpace: /modules/rflows/flows.css | Standalone: /modules/rflows/flows.css
+ // The shell always serves from /modules/rflows/ in both modes
+ return "/modules/rflows/flows.css";
}
private render() {
@@ -207,8 +212,8 @@ class FolkFundsApp extends HTMLElement {
*, *::before, *::after { box-sizing: border-box; }
- ${this.error ? `${this.esc(this.error)}
` : ""}
- ${this.loading && this.view === "landing" ? 'Loading...
' : ""}
+ ${this.error ? `${this.esc(this.error)}
` : ""}
+ ${this.loading && this.view === "landing" ? 'Loading...
' : ""}
${this.renderView()}
`;
this.attachListeners();
@@ -222,12 +227,12 @@ class FolkFundsApp extends HTMLElement {
// ─── Landing page ──────────────────────────────────────
private renderLanding(): string {
- const demoUrl = this.getApiBase() ? `${this.getApiBase()}/demo` : "/rfunds/demo";
+ const demoUrl = this.getApiBase() ? `${this.getApiBase()}/demo` : "/rflows/demo";
const authed = isAuthenticated();
const username = getUsername();
return `
-
+
Flows
@@ -239,53 +244,53 @@ class FolkFundsApp extends HTMLElement {
-
+
Design transparent resource flows with sufficiency-based cascading.
Funnels fill to their threshold, then overflow routes surplus to the next layer —
ensuring every level has enough before abundance cascades forward.
-
-
-
-
💰
+
+
+
+
💰
Sources
Revenue streams split across funnels by configurable allocation percentages.
-
-
🏛
+
+
🏛
Funnels
Budget buckets with min/max thresholds and sufficiency-based overflow cascading.
-
-
🎯
+
+
🎯
Outcomes
Funding targets that receive spending allocations. Track progress toward each goal.
-
-
🌊
+
+
🌊
River View
Animated sankey diagram showing live fund flows through your entire system.
-
-
✨
+
+
✨
Enoughness
System-wide sufficiency scoring. Golden glow when funnels reach their threshold.
-
-