Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
Wires the Discord-style demo UI to real user data. Users with 1+ meshes
now get situational awareness: who's online, what's in the queue, what
the broker saw recently — polling every 4s, all E2E encrypted.
Extraction pass:
- New `<MeshStream peers messages channelLabel footer>` renderer at
modules/marketing/home/mesh-stream.tsx — pure presentation, no
playback engine, no data fetching. Handles peer filter, hover-for-
ciphertext tooltip, animated message list.
- demo-dashboard.tsx refactored to use it: keeps the playback loop,
traffic-light chrome, and script-driven messages; passes everything
to MeshStream via props. ~120 LOC shorter.
Backend:
- new GET /api/my/meshes/:id/stream in packages/api (same authz gate
as /my/meshes/:id — owner OR non-revoked member). Returns:
- up to 20 live presences (disconnectedAt IS NULL), joined to
meshMember for displayName
- up to 50 most-recent message_queue envelopes with metadata only:
sender + displayName, targetSpec, priority, createdAt, deliveredAt,
byte size, and a 24-char ciphertext preview (this IS what the
broker sees — no plaintext anywhere in the response)
- up to 20 recent audit events
- getMyMeshStreamResponseSchema in schema/mesh-user.ts matches exactly.
Frontend:
- new LiveStreamPanel client component at modules/mesh/live-stream-panel.tsx
— react-query with refetchInterval: 4000ms, refetchIntervalInBackground
false. Maps presences + envelopes to MeshStream's Peer/Message shape,
classifies targetSpec into message type ("tag:*" → ask_mesh, "*" →
broadcast, else direct). Passes through the ciphertextPreview as the
hover content — no fake ciphertext in live view.
- new route /dashboard/meshes/[id]/live with server-side authz preflight
via /my/meshes/:id. Mounts LiveStreamPanel inside a dashboard page
shell with breadcrumb back to mesh detail.
- Mesh detail page gets a new "Live" pill button (clay-pulsing dot)
next to "Generate invite link" in the header.
- paths config gets dashboard.user.meshes.live(id).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
126 lines
3.3 KiB
TypeScript
126 lines
3.3 KiB
TypeScript
const ADMIN_PREFIX = "/admin";
|
|
const AUTH_PREFIX = "/auth";
|
|
const BLOG_PREFIX = "/blog";
|
|
const DASHBOARD_PREFIX = "/dashboard";
|
|
const LEGAL_PREFIX = "/legal";
|
|
|
|
const API_PREFIX = "/api";
|
|
|
|
// AI apps routes (no prefix - top-level routes)
|
|
const APPS_CHAT = "/chat";
|
|
const APPS_IMAGE = "/image";
|
|
const APPS_TTS = "/tts";
|
|
const APPS_PDF = "/pdf";
|
|
const APPS_AGENT = "/agent";
|
|
|
|
const DEMO_PREFIX = "/demo";
|
|
|
|
const pathsConfig = {
|
|
index: "/",
|
|
demo: {
|
|
index: DEMO_PREFIX,
|
|
scrollTest: `${DEMO_PREFIX}/scroll-test`,
|
|
},
|
|
apps: {
|
|
chat: {
|
|
index: APPS_CHAT,
|
|
chat: (id: string) => `${APPS_CHAT}/${id}`,
|
|
},
|
|
image: {
|
|
index: APPS_IMAGE,
|
|
history: `${APPS_IMAGE}/history`,
|
|
detail: (id: string) => `${APPS_IMAGE}/${id}`,
|
|
generation: (id: string) => `${APPS_IMAGE}/generation/${id}`,
|
|
},
|
|
tts: APPS_TTS,
|
|
pdf: {
|
|
index: APPS_PDF,
|
|
detail: (id: string) => `${APPS_PDF}/${id}`,
|
|
chat: (id: string) => `${APPS_PDF}/${id}`,
|
|
},
|
|
agent: APPS_AGENT,
|
|
},
|
|
admin: {
|
|
index: ADMIN_PREFIX,
|
|
users: {
|
|
index: `${ADMIN_PREFIX}/users`,
|
|
user: (id: string) => `${ADMIN_PREFIX}/users/${id}`,
|
|
},
|
|
organizations: {
|
|
index: `${ADMIN_PREFIX}/organizations`,
|
|
organization: (slug: string) => `${ADMIN_PREFIX}/organizations/${slug}`,
|
|
},
|
|
customers: {
|
|
index: `${ADMIN_PREFIX}/customers`,
|
|
customer: (id: string) => `${ADMIN_PREFIX}/customers/${id}`,
|
|
},
|
|
meshes: {
|
|
index: `${ADMIN_PREFIX}/meshes`,
|
|
mesh: (id: string) => `${ADMIN_PREFIX}/meshes/${id}`,
|
|
},
|
|
sessions: {
|
|
index: `${ADMIN_PREFIX}/sessions`,
|
|
},
|
|
invites: {
|
|
index: `${ADMIN_PREFIX}/invites`,
|
|
},
|
|
audit: {
|
|
index: `${ADMIN_PREFIX}/audit`,
|
|
},
|
|
},
|
|
marketing: {
|
|
pricing: "/pricing",
|
|
contact: "/contact",
|
|
blog: {
|
|
index: BLOG_PREFIX,
|
|
post: (slug: string) => `${BLOG_PREFIX}/${slug}`,
|
|
},
|
|
legal: (slug: string) => `${LEGAL_PREFIX}/${slug}`,
|
|
},
|
|
auth: {
|
|
login: `${AUTH_PREFIX}/login`,
|
|
register: `${AUTH_PREFIX}/register`,
|
|
join: `${AUTH_PREFIX}/join`,
|
|
forgotPassword: `${AUTH_PREFIX}/password/forgot`,
|
|
updatePassword: `${AUTH_PREFIX}/password/update`,
|
|
error: `${AUTH_PREFIX}/error`,
|
|
},
|
|
dashboard: {
|
|
user: {
|
|
index: DASHBOARD_PREFIX,
|
|
ai: `${DASHBOARD_PREFIX}/ai`,
|
|
vocabulary: `${DASHBOARD_PREFIX}/vocabulary`,
|
|
meshes: {
|
|
index: `${DASHBOARD_PREFIX}/meshes`,
|
|
new: `${DASHBOARD_PREFIX}/meshes/new`,
|
|
mesh: (id: string) => `${DASHBOARD_PREFIX}/meshes/${id}`,
|
|
invite: (id: string) => `${DASHBOARD_PREFIX}/meshes/${id}/invite`,
|
|
live: (id: string) => `${DASHBOARD_PREFIX}/meshes/${id}/live`,
|
|
},
|
|
invites: `${DASHBOARD_PREFIX}/invites`,
|
|
settings: {
|
|
index: `${DASHBOARD_PREFIX}/settings`,
|
|
security: `${DASHBOARD_PREFIX}/settings/security`,
|
|
billing: `${DASHBOARD_PREFIX}/settings/billing`,
|
|
},
|
|
},
|
|
organization: (slug: string) => ({
|
|
index: `${DASHBOARD_PREFIX}/${slug}`,
|
|
settings: {
|
|
index: `${DASHBOARD_PREFIX}/${slug}/settings`,
|
|
},
|
|
members: `${DASHBOARD_PREFIX}/${slug}/members`,
|
|
}),
|
|
},
|
|
} as const;
|
|
|
|
export {
|
|
pathsConfig,
|
|
DASHBOARD_PREFIX,
|
|
ADMIN_PREFIX,
|
|
BLOG_PREFIX,
|
|
AUTH_PREFIX,
|
|
API_PREFIX,
|
|
LEGAL_PREFIX,
|
|
};
|