feat(spaces): Spaces as workbench card + canonical /spaces route

Extract member management from /spaces/members into a reusable
workbench-card ListView so users can drop the surface into any scene.

- lib/modules/spaces/ListView.svelte — hint + invite + members + pending
  invitations, all theme-token driven
- APP_ICONS.spaces icon (three-silhouette cluster, teal→indigo)
- MANA_APPS entry id=spaces (beta tier, shared-space management)
- registerApp({ id: 'spaces' }) so the card is scene-droppable
- /spaces/+page.svelte as the new canonical route wrapper
- /spaces/members/+page.svelte kept as legacy alias
- SpaceSwitcher menu now links to /spaces

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-21 18:53:03 +02:00
parent 5924f4fac3
commit 88eca8a759
7 changed files with 555 additions and 492 deletions

View file

@ -260,6 +260,13 @@ export const APP_ICONS = {
// while still reading as "chronological" in the AI Workbench family.
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="tl" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#f59e0b"/><stop offset="100%" style="stop-color:#ea580c"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#tl)"/><line x1="34" y1="22" x2="34" y2="78" stroke="white" stroke-width="3" stroke-linecap="round" opacity="0.55"/><circle cx="34" cy="30" r="5" fill="white"/><circle cx="34" cy="50" r="5" fill="white"/><circle cx="34" cy="70" r="5" fill="white"/><rect x="44" y="26" width="32" height="8" rx="2" fill="white" fill-opacity="0.95"/><rect x="44" y="46" width="26" height="8" rx="2" fill="white" fill-opacity="0.8"/><rect x="44" y="66" width="30" height="8" rx="2" fill="white" fill-opacity="0.9"/></svg>`
),
spaces: svgToDataUrl(
// Three people-silhouettes clustered in the tile — the Spaces primitive
// is about shared workspaces, so the icon emphasises "group". Teal→indigo
// gradient sits next to contacts (green) and chat (indigo) in the
// communication family without competing with either.
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="sp" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#14b8a6"/><stop offset="100%" style="stop-color:#6366f1"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#sp)"/><circle cx="30" cy="38" r="9" fill="white" fill-opacity="0.85"/><path d="M14 70c0-9 7-16 16-16s16 7 16 16v4H14v-4z" fill="white" fill-opacity="0.85"/><circle cx="70" cy="38" r="9" fill="white" fill-opacity="0.85"/><path d="M54 70c0-9 7-16 16-16s16 7 16 16v4H54v-4z" fill="white" fill-opacity="0.85"/><circle cx="50" cy="32" r="11" fill="white"/><path d="M30 76c0-11 9-20 20-20s20 9 20 20v4H30v-4z" fill="white"/></svg>`
),
} as const;
export type AppIconId = keyof typeof APP_ICONS;

View file

@ -1105,6 +1105,23 @@ export const MANA_APPS: ManaApp[] = [
status: 'beta',
requiredTier: 'beta',
},
{
id: 'spaces',
name: 'Spaces',
description: {
de: 'Geteilte Bereiche & Mitglieder',
en: 'Shared spaces & members',
},
longDescription: {
de: 'Verwalte den aktiven Space und seine Mitglieder — lade Personen per E-Mail ein, vergib Rollen (Admin/Mitglied) und widerrufe offene Einladungen. Personal-Spaces zeigen nur den Hinweis, dass sie bewusst nur für dich sind; geteilte Spaces (Familie, Team, Marke, Verein, Praxis) bekommen das volle Member-Management.',
en: 'Manage the active Space and its members — invite people by email, assign roles (admin/member) and revoke pending invitations. Personal spaces are single-user by design; shared spaces (family, team, brand, club, practice) get full member management.',
},
icon: APP_ICONS.spaces,
color: '#14b8a6',
comingSoon: false,
status: 'beta',
requiredTier: 'beta',
},
];
/**