depictio frontend, branding, users & groups, admin shell — audit for Spectio¶
Source: depictio checkout scratchpad/dep, v1.19.2 (commit 0d1aa0a, 2026-10-23). Paths are relative to the repository root. Inferences are marked [inf].
1. JS packages (pnpm workspace)¶
pnpm-workspace.yaml lists packages/* and depictio/viewer. The viewer pulls the shared packages as local paths (depictio/viewer/package.json: "depictio-react-core": "file:../../packages/depictio-react-core", same for depictio-components). Stack: React 18.3, Mantine 7.14+ (core, dates, hooks, notifications, code-highlight, carousel, tiptap, colors-generator), AG Grid 32, lucide-react, Iconify, Vite 5.
| Package | Version | Published | What it is |
|---|---|---|---|
packages/depictio-react-core |
0.1.0, "private": true |
no (npm) | Main shared library. main: src/index.ts; no build step, consumed as TypeScript source; 215 export statements in src/index.ts. Peers: Mantine, AG Grid, Plotly, react-grid-layout, genome-spy, 3dmol, depictio-components |
packages/depictio-components |
0.1.0, private | no | Small shared React component set (src/lib/index.ts, webpack build). Peers: Mantine core/hooks, Iconify, React 18 |
packages/tool-studio |
0.2.0, private | no | A separate Vite app (App.tsx, Monaco, Pyodide, zustand) for authoring catalog tools; depends on react-core |
packages/plotly-complexheatmap, packages/plotly-upset |
Python (hatchling pyproject.toml) |
pinned 0.1.0, PyPI publication pending (dev/design/local-server-profile.md §Risks 1) |
Python Plotly figure libraries, not JS |
What depictio-react-core contains, by area:
-
Theme and branding.
brandTheme.ts:buildDepictioTheme,depictioTheme,brandCssVariablesResolver,BrandingContext/useBranding,useBrandAccent(s),mergeBrandThemes,BRAND_PALETTES(brandPrimary/brandSecondary/brandTertiary) andLOGO_PALETTES(logoOrange,logoTeal, 10 shades each).brandColors.ts.components/branding/:BrandThemeForm,BrandThemePreview,BrandScope,useResolvedBrandTheme.
-
API client.
api.ts, 8,035 lines.API_BASE = '/depictio/api/v1'is hard-coded (l.22).authFetch(l.182) addsAuthorization: Bearer, refreshes the token on 401 and retries.- Typed functions for every endpoint: users, groups, tokens, branding, backups, monitoring, and so on.
-
Admin URL state.
adminUrlState.ts(ADMIN_TABS,MONITORING_PANES,adminUrl,parseAdminUrl) andlistingUrlState.ts. - Dashboard rendering.
components/holdsDashboardGrid,ComponentRenderer,FigureRenderer,TableRenderer,MultiQCRenderer,JBrowseRenderer,MapRenderer,ImageRenderer,StepFlow,SectionAccordion,TabTilesand others.components/chrome/holds per-card action buttons (Download, Export, Fullscreen, Inspect, Comments, Annotate).chrome/hasChromeButton,ChromeButtonGroup,chromeStyle. - Hooks and data utilities.
hooks/(useCollapseState,useInView,useRealtimeJournal, …),realtime.ts,telemetry.ts,fetchQueue.ts, filter and selection modules,runFolder*.ts. - Not in react-core. The app chrome is not here: AppShell, sidebar, header, glass shell and dock live in the viewer under
depictio/viewer/src/chrome/. So are auth pages, the current-user hook and all admin screens.
2. Branding and customisation¶
2.1 Model: depictio/models/models/branding.py¶
Branding has three layers, each one overriding the previous field by field (l.1–22). None means "inherit", so an untouched theme serialises to {}.
- Deployment defaults from
DEPICTIO_BRANDING_*environment variables (BrandingConfig,api/v1/configs/settings_models.py:2179). - Instance overrides, stored in the singleton
instance_settingsdocument and edited from /admin → Branding. - A per-dashboard override,
DashboardData.brand_theme.
merge_brand_themes() folds the layers. resolve_brand_theme() applies the final defaults (tint_mode="accent") and derives the figure colorway (8 colours) and the sequential scale (8 stops). This is done server-side so Python figures and React stay identical.
BrandTheme (l.311, extra="forbid"):
| Group | Fields |
|---|---|
| Identity | app_name (≤120; browser tab title and login greeting), logo_mode (inherit / none / custom), logo_url, logo_url_dark (falls back to logo_url) |
| Brand colours | primary, secondary, tertiary. Each is a hex value or a Mantine palette name, expanded to a 10-shade palette with the brand colour placed on shade 6, the shade a filled control paints with (l.64–68) |
| Status colours | success, warning, danger (override Mantine green / yellow / red) |
| Tint | tint_mode: accent (default) or full |
| Surfaces | surfaces_light and surfaces_dark (BrandSurfaces: app_bg, section_bg, nav_bg, heading) |
| Type and shape | font_family, headings_font_family (≤300), default_radius (xs–xl) |
| Figures | plots (BrandPlots: template, colorway[], sequential[]) |
| Extra | palettes: dict[str, list[str]] (named palettes) |
- Chrome style. A separate
InstanceLook.default_chrometakesglassorbase(l.43–56). It is instance-only, so a dashboard cannot change the app chrome, and each reader can still choose their own in Profile. - Presets.
BRAND_PRESETS(l.507) hasdepictio(default),trec,emblandocean. A preset only seeds the form; everything stays editable. - "Powered by Depictio". The badge shows exactly when the depictio wordmark is hidden, i.e. once
logo_modeiscustomornone(BrandingConfigdocstring).
2.2 Environment variables: BrandingConfig (settings_models.py:2179)¶
Three levels, the later one winning:
DEPICTIO_BRANDING_PRESET=trec;DEPICTIO_BRANDING_THEME='<JSON BrandTheme>', the only way to set nested blocks such as surfaces;- flat variables:
..._LOGO_URL,..._LOGO_URL_DARK,..._APP_NAME, and the remaining fields.
2.3 API (api/v1/endpoints/utils_endpoints/routes.py, under /depictio/api/v1/utils)¶
| Route | Line | Who | Purpose |
|---|---|---|---|
GET /utils/public-config |
171 | anyone | Bootstrap payload: branding (effective theme), Google Analytics, feedback link (read in viewer/src/main.tsx:354–370) |
GET /utils/branding/presets |
264 | anyone | Preset list for the panel |
POST /utils/branding/resolve |
290 | signed in | Resolve a draft theme (dashboard editors use it too) |
GET /utils/branding |
305 | admin | Panel state: overrides, env_defaults, effective, default_chrome |
PUT /utils/branding |
312 | admin | Replace the overrides; null fields fall back to env |
PUT /utils/branding/look |
323 | admin | Set default_chrome |
DELETE /utils/branding |
336 | admin | Reset all overrides |
POST /utils/branding/logo/{light\|dark} |
348 | admin | Logo upload, stored in branding_assets |
GET /utils/branding/logo/{variant} |
384 | anyone | Serve the logo |
Logo uploads accept PNG, JPEG and WebP, checked by magic bytes. SVG is not accepted [inf: SVG can carry scripts]. Maximum 2 MB (api/v1/services/branding.py:237–241,250). Logos are excluded from dashboard version snapshots (CLAUDE.md).
2.4 Admin Branding panel (depictio/viewer/src/admin/AdminBrandingPanel.tsx, 457 lines)¶
- Header (l.276–360): title "Instance branding" with the help text "Rebrand this deployment without touching its configuration… Cleared fields fall back to the deployment's
DEPICTIO_BRANDING_*defaults". Buttons: Presets menu, Import (JSON file), Export. - Chrome style: a
SegmentedControl(data-testid="branding-default-chrome") with Glass and Base and a one-line tagline for each. -
Body: a
Grid, 7/5 columns atmd.-
Left: logo upload slots "Logo" and "Logo (dark mode)" (l.396–405), each with an upload
FileButtonand a "Remove the override" icon. Below them sitsBrandThemeForm(packages/depictio-react-core/src/components/branding/BrandThemeForm.tsx, an Accordion; each item shows how many fields are set):identity(instance scope only): "Instance name";logo(instance only);colors"Brand colors": Primary / Secondary / Tertiary;status"Status colors": Success / Warning / Danger;surfaces: Page background, Cards & sections, Header & sidebar, Titles, each for light and dark;type"Typography & shape": Font stack, Heading font stack, Corner radius (Sharp xs, Small, Medium, Large, Round xl);figures: Plot template (Follow the UI theme, Plotly, Plotly white, Plotly dark, ggplot2, Seaborn, Simple white, Presentation), and Colorway as "From palette" or "Custom" with add/remove swatches.
-
Right: "Preview" (
BrandThemePreview.tsx), a mini page with badges, a section heading, and buttons in five variants (filled, light, outline, subtle, default).
-
-
Footer: Reset (
mdi:restore) and Save (l.439–447). On save,setBranding(state.effective)repaints the running UI (l.44). - Per-dashboard scope: the same
BrandThemeFormwithscopeset to the dashboard, without the identity and logo sections, opened from the dashboard appearance panel.
2.5 Runtime¶
- Fetching and caching (
depictio/viewer/src/branding.ts): branding arrives from/utils/public-configafter first paint and is cached in localStorage (readCachedBranding/cacheBranding) to avoid a flash. An external store (getBranding/setBranding/subscribeBranding) feeds the theme root.usePageTitle(page)sets the tab title to<app_name> — <page>. - Theme building (
brandTheme.ts:222):buildDepictioTheme()sets Mantine'sprimaryColor(falling back toblue),fontFamily,defaultRadius(mdby default) and heading fonts. - CSS variables (
brandTheme.ts:287):brandCssVariablesResolver()writes--mantine-color-body(app_bg),--mantine-color-defaultand--depictio-section-bg(section_bg),--depictio-nav-bg,--depictio-heading-color, and--depictio-brand-primary|secondary|tertiary. Secondary falls back totealand tertiary toorange. - Section colours use the logo palettes (
logoTealfor Projects,logoOrangefor Dashboards) unless the brand sets a secondary or tertiary (brandAccent).
2.6 Other customisation¶
-
Elsewhere in
public-configand settings:- Google Analytics (
GoogleAnalyticsConfig, l.2123); - an opt-in Feedback link (
FeedbackConfig, l.2140); saml_login_labelfor the SSO button text (l.546);password_login_disabled(l.463);- version check (
VersionCheckConfig, l.2073, with an admin toggle in Admin → Settings: "Check for new versions", interval, repository, current and latest version, last check, last error;AdminSettingsPanel.tsx:186–236); - telemetry and analytics configs.
- Google Analytics (
-
Not found: announcements or banners, custom login-page text beyond
app_nameand the SAML label, custom footer, custom homepage. The About page (viewer/src/about/AboutApp.tsx) is static;landing/LandingHintis a hint, not a configurable homepage. [inf: these are absent, based on a grep ofsettings_models.pyfor about / announce / footer / homepage.]
3. Users and groups¶
3.1 Admin → Users (depictio/viewer/src/admin/AdminUsersPanel.tsx, 341 lines)¶
-
Layout. An accordion list, one row per user, with no table columns.
- The row header shows a user icon and the email, plus badges: "You", "Inactive" (red), and "System admin" (primary colour, shield icon) or "User" (plain).
- The expanded row uses
Facts(admin/Facts.tsx): User ID, Registration date, Last login, Account status (badge), Verified (Yes/No), and "Password: Must be changed at next sign-in" when it applies. - In the Classic chrome there is also a "User status"
SegmentedControlwith Standard and System Admin (l.183–190), disabled for oneself.
-
Actions. "Delete user", disabled for oneself with the tooltip "You cannot delete your own account", with a confirmation modal (
DeleteUserModal.tsx).- "Create user" (
UserPlus) opensCreateUserModal.tsx: Email, Display name (optional), Initial password (pre-generated, 14 characters, with "Generate another" and "Copy"), a "Ask for a new password at first sign-in" switch and a "System admin" switch. - When password login is disabled, the modal says the person signs in with single sign-on (l.144).
- "Create user" (
-
Not found: filters, search, invitations, deactivate/reactivate, admin-triggered password reset, impersonation.
- API (
packages/depictio-react-core/src/api.ts):GET /auth/list(l.5964);DELETE /auth/delete/{id}(l.5971);POST /auth/admin/create_user(l.5992);POST /auth/turn_sysadmin/{id}/{True|False}(l.6009), a verb-style path.
3.2 Groups: one workspace, two scopes¶
- Two entry points, one component (
depictio/viewer/src/groups/GroupsWorkspace.tsx, 533 lines). Admin → Groups isAdminGroupsPanel.tsx→<GroupsWorkspace scope="admin" />, covering every group./groupsisGroupsApp.tsx, titled "My Groups", scopemine: the caller's groups, editable where the caller is a group admin, reached from the group badges on the Profile page. -
Master-detail (
Grid5/7 or 4/8). Screenshot:research/depictio-shots/user-groups_admin-workspace.png.- Left panel: a "Search groups…" box, a + Create button (admin scope), and an AG Grid with columns Group (sortable) and Members, plus a "N groups" footer.
- Right panel:
GroupDetailPanel.tsx, 894 lines.- Header: group icon, name, an "N members" badge, an SSO badge (grape) with the tooltip "Synced from SSO — manual changes may be overwritten at next login." (l.545–548, repeated as a notice at l.628–641), and Delete group.
- Editable fields: Name and Description, with Save.
- P.I. block. If
pending_pi_emailis set: "P.I. pending —becomes P.I. and group admin at their [first sign-in]", with a clear action. Otherwise a "Principal Investigator" email input ( pi@example.com) with a set button (l.648–712). - Members: a count and a "Filter members…" box. The AG Grid has columns Member (email plus a "You" badge), Name (
display_name), Group admin (checkbox), P.I. (a crown marks the current P.I.; others get a "Designate as P.I." action) and remove. - Below: a "Type to search users…" picker, or an email field, with + Add member.
-
Permission rules.
canManagecovers rename, add/remove and the group-admin toggle: admin scope, or the caller is a group admin.canEditPIis admin scope only, so P.I. designation is for system admins only, server-side as well (l.55–62).- Only the admin scope can delete, and the protected groups
adminanduserscan never be deleted (PROTECTED_GROUP_NAMES, l.42). CreateGroupModal.tsx: Group name ("A short, unique name"), Description (optional), Principal Investigator (optional).
-
API:
GET /groups/list(admin) andGET /groups/mine;GET,PATCHandDELETE /groups/{id}, plusPOST /groups;POSTandDELETE /groups/{id}/members/{uid};POST /groups/{id}/admins/{uid}/{True|False};POST /groups/{id}/pending-piandPOST /groups/{id}/pi/{uid};GET /projects/group_members_preview/{pid}/{gid}(api.ts l.5276–5432).
-
Screenshot
user-groups_pending-pi.pngshows the pending-P.I. state.
3.3 Profile (depictio/viewer/src/profile/ProfileApp.tsx, 734 lines)¶
- Avatar. Click the avatar to change it (
FileButton), and Remove. It may come from the identity provider ("picture from youraccount"). API: PUT/DELETE /auth/me/avatar. - Info rows (l.642–653): Email, Name, Title, Role (Administrator / User), Database ID, Registration date, Last login.
- Groups row (
GroupsInfoRow, l.162–190): one badge per group, a crown where the user is P.I. and a shield where they are admin, each linking to/groups?group=<id>. - Password:
PasswordForm.tsx, callingPOST /auth/edit_password. - The chrome preference (Glass / Base) and account preferences appear in the
server-info-account/account-*.pngscreenshots.
3.4 CLI agents (/cli-agents, depictio/viewer/src/cli-agents/CliAgentsPanel.tsx)¶
- Purpose. Subtitle: "Security configurations to access Depictio via the command line interface." Button Add configuration. Empty state: "No CLI configurations yet".
- Each configuration shows its name and "Expires:
", plus delete ( DeleteTokenModal, which asks the user to type "delete"). - Create (
CreateTokenModal, field Name) makes a long-lived token, thenDisplayTokenModalshows the generatedCLI.yamlto copy or download (screenshotcli-config-create.png). It is not available in public mode (l.132). - API:
GET /auth/list_tokens,POST /auth/me/tokens,DELETE /auth/me/tokens/{id},POST /auth/generate_agent_config. - Monitoring "Agents" pane:
GET /monitoring/agentsandPOST /monitoring/agents/{id}/trigger(api.ts l.6646–6663) list registered CLI agents or watchers and can trigger them. [inf: belongs to the ops audit.]
3.5 Project permissions (depictio/viewer/src/projects/detail/PermissionsApp.tsx, 1,539 lines; Glass layout l.842–1064)¶
- Visibility: a chip, the title "Public" or "Private", the text "Anyone signed in can view this project." or "Only listed members can view this project.", and a
Switch("Public project"). Turning it on opens a confirmation modal, "Change Project Visibility". -
Members: a header with a count and the hint "Owners manage members and visibility, editors change the project's content, viewers can only look."
- Each row has the email, an Owner / Editor / Viewer
SegmentedControland a remove button. - An add-by-email input at the bottom. Empty state: "No members yet. Add a teammate by email below."
- Each row has the email, an Owner / Editor / Viewer
-
Groups: same pattern. Each row has the group name, an Owner / Editor / Viewer
SegmentedControland remove, with a "Search groups" picker. Empty state: "No groups yet. Share this project with a group below."
Also: AccessOverview.tsx shows the effective access (a 403 for non-managers). The Classic layout uses AG Grids with Email | Owner | Editor | Viewer checkbox columns (l.660–729). API: POST /projects/update_project_permissions (l.5140).
4. Admin shell and chrome¶
- Two chromes.
InstanceLook.default_chromeselectsglass(the default) orbase("Classic"). Each reader can override it in Profile (chrome/variants/index.ts:writeChromePref,applyInstanceDefault). -
Glass page layout (
depictio/viewer/src/chrome/variants/glass/Shell.tsx, header comment):- a full-width top bar (logo │ breadcrumb … settings, theme, avatar);
- a sidebar labelled WORKSPACE with Dashboards, Projects, Administration and About, folding to an icon rail, with an "Online · vX" and "Single user mode" status at the bottom;
- a floating, movable dock for page actions.
- Below
sm: a bottom bar (Tabs, Filters, Search, More), compact top bar, bottom sheets.
-
AdminApp (
depictio/viewer/src/admin/AdminApp.tsx):-
Glass.
GlassPagewithsection="admin", the title "Administration" and the description "Users, content, branding and the health of this instance." Inside, a vertical, unstyledTabsacts as a settings-style section nav, 208 px wide and sticky, with 32 px gap to the content (chrome/variants/glass/pages/admin.css).-
Nav groups (l.72–106):
- People: Users, Groups;
- Content: Projects (teal), Dashboards (orange);
- Activity: Tasks, Ingestion, Watchers, Logs, Health, all "trusted";
- Instance: Branding, Backups (trusted), Maintenance, Settings.
-
Group labels are 11 px uppercase,
letter-spacing .06em. Tabs are 36 px high, radius 10, 13.5 px / weight 550. The active tab is--gl-accent-softat weight 650. Icons are lucide at stroke 1.6 (STROKE).
-
-
Base / Classic. A Mantine
AppShell(header 64, navbar 260) with one horizontal tab row: Users, Groups, Projects, Dashboards, Branding, Log & Task, Backups, Maintenance, Settings. - Behaviour.
- URL-routed as
/admin/<tab>and/admin/<pane>, with ingestion filters in the query string (react-coreadminUrlState). The last tab is remembered in localStorage (admin-active-tab). - Non-admins get a "Forbidden" page ("The administration page is only available to system administrators.").
- Monitoring and Backups are hidden in public and demo modes (
admin/access.ts:canSeeMonitoring,canSeeBackups). Single-user mode always shows them.
- URL-routed as
-
-
Visual style (screenshots
glass-chrome/admin-light.png,user-groups_admin-workspace.png):- pale blue-grey page background, white panels with a 1 px hairline and radius ~16–20, generous padding;
- the shield icon in a soft blue tile next to a large bold title;
- dense AG Grid tables with light rows and the selected row in pale blue;
- small pill badges ("You", "System Admin", "SSO", "N members"); soft-filled primary buttons (+ Create, Save, Delete group); red trash icons.
- In the older v1.11 Glass screenshot (
admin-light.png), the sections are a horizontal segmented pill bar instead of the left section nav.
-
Screenshots in
docs/images:glass-chrome/admin-light.png,glass-chrome/auth-login-light.png;user-groups_admin-workspace.png,user-groups_my-groups.png,user-groups_pending-pi.png;server-info-account/{admin-settings,account-settings,account-preferences,cli-config-create}.png;v0.12/react/admin_monitoring_{tasks,task_detail,logs,health,ingestion*,agents}_{light,dark}.png.- Copied to
research/depictio-shots/:admin-light.png,user-groups_admin-workspace.png,user-groups_pending-pi.png,admin-settings.png,cli-config-create.png,admin_monitoring_tasks_light.png. - No dedicated branding screenshot was found.
5. Reusable for Spectio¶
Depend on, or vendor, directly
-
depictio-react-corehas the theme engine (buildDepictioTheme,brandCssVariablesResolver,BrandingContext,mergeBrandThemes) and the branding editor (BrandThemeForm,BrandThemePreview).- Blocker 1: it is private, unpublished, unbuilt TypeScript source.
- Blocker 2:
api.tshard-codesAPI_BASE = '/depictio/api/v1'. - Blocker 3: it pulls heavy peers (Plotly, AG Grid, genome-spy, 3dmol).
- Proposal: depictio extracts
@depictio/theme(brandTheme.ts, brandColors.ts, components/branding) as a small published package with only Mantine as a peer. Spectio depends on it; until then, Spectio vendors those five files.
-
Python: depend on
depictio.models.models.branding(BrandTheme,merge_brand_themes,resolve_brand_theme, presets), or copy it. It is pure Pydantic. One shared model means one brand JSON can be imported or exported between the two apps (the panel already has Import / Export).
Port as patterns (copy, rename the API calls)
authFetch(bearer token, refresh on 401, retry).- URL-routed admin sections (
adminUrlState), plus remembering the last tab. - The groups master-detail workspace: one component for both admin and "my groups" scopes, with
canManage/canEditPIflags. Factskey–value rows, the confirmation modal that asks to type "delete", and the create-user modal with a generated password.
Mirror field for field
-
Branding.
- Spectio gets the same
BrandThemefields and the same three layers: envSPECTIO_BRANDING_*, the admin panel, and a per-workflow or per-project override in place of depictio's per-dashboard one [inf]. - When co-hosted, Spectio reads depictio's effective branding from
/depictio/api/v1/utils/public-configand only adds its own section colours. - Same rules: presets, Import / Export JSON, logo light and dark (PNG, JPEG, WebP, ≤2 MB, no SVG), "Powered by" badge logic.
- Spectio gets the same
-
Users. Spectio mirrors depictio's facts and its create form. It adds search, deactivate, and principal kinds (service accounts, runners, agents), which depictio lacks.
- Groups. Columns Member / Name / Group admin / P.I.; the pending-P.I.-by-email flow; the SSO badge with its tooltip; protected
adminandusersgroups; P.I. set by system admins only. - Project sharing. Visibility / Members / Groups sections with Owner / Editor / Viewer segmented controls and the same hint sentence. Spectio adds Launcher and Reviewer as capabilities on top of Editor, in an "advanced" row, so the three-role surface stays identical.
- Profile. Same rows (Email, Name, Title, Role, ID, Registration date, Last login), avatar, group badges with crown and shield, and password. "CLI configurations" becomes Spectio's Tokens section, with the same create → show-config-once flow.
- Admin navigation. Same groups and order:
- People: Users, Groups, plus Service accounts;
- Content: Projects, plus Workflows and Environments;
- Activity: Tasks, Logs, Health, plus Runs/Events;
- Instance: Branding, Backups, Maintenance, Settings, plus AI & agents, Webhooks, Plugins.
- The settings-style left section nav is 208 px, with 11 px uppercase group labels.
Conflicts with Spectio's current design
- Admin entry. depictio's admin is a full page inside its WORKSPACE sidebar ("Administration" item). Spectio currently has a "Settings" item with horizontal tabs. Proposal: rename it to "Administration" (admin only) and use depictio's left section nav inside the content area, keeping Spectio's fixed 276 px sidebar and full-width top bar, which match Glass's top bar and sidebar.
- Sidebar widths. depictio's Glass sidebar is about 210 px with Dashboards, Projects, Administration, About. Spectio's is 276 px with 7 items. When co-hosted, the app switcher must not reorder either app's items. [inf]
- No dock in Spectio. depictio's floating dock is dashboard-specific. Spectio keeps its page action bars.
- Role vocabulary. Spectio's five roles against depictio's three: show three in the sharing UI and the extra two as capabilities (see
api-and-data-model.md§13). - Settings to split out of "Settings". Spectio's current Settings tabs (General, Projects, Users & groups, Service accounts, Tokens, AI & agents, Webhooks, Notifications, Branding) should be redistributed into the four depictio groups. Personal tokens move to Profile, as depictio does with CLI agents.
- Icons. depictio's admin uses lucide at stroke 1.6, while Spectio's boards use hand-drawn 1.7-stroke paths. Close enough; align on 1.6 if Spectio vendors lucide.