Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,21 @@
|
||||
# UI Inventory
|
||||
|
||||
This directory is the machine-readable starting point for UI ownership and future hotswap work.
|
||||
|
||||
Current scope:
|
||||
|
||||
- Identify stable shell, sidebar, navigation and primitive component names.
|
||||
- Record current file paths and backing libraries.
|
||||
- Mark adapter candidates for later `packages/ui` migration.
|
||||
|
||||
This inventory does not move components. App code should keep its current imports until a category-specific migration introduces real `@toolctrl/ui/*` adapter exports.
|
||||
|
||||
Files:
|
||||
|
||||
- `schema.json` declares the inventory shape and allowed categories/statuses.
|
||||
- `components.json` lists current shell, navigation and root UI primitive ownership.
|
||||
- `surfaces.schema.json` declares the stable UI surface/action/nav map shape.
|
||||
- `surfaces.json` maps stable `data-ui-id` values to source files and renderers.
|
||||
- `surface-map.md` is the human-readable companion for prompts and reviews.
|
||||
|
||||
The app test suite validates that inventory paths exist, lowercase `apps/web/app/components/ui/*.tsx` primitive files are represented here, and stable registry IDs are represented in the surface map.
|
||||
@@ -0,0 +1,507 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"notes": [
|
||||
"Read-only inventory for the first UI registry phase.",
|
||||
"Statuses describe current ownership, not desired end state.",
|
||||
"Headless UI entries are legacy candidates; Radix/shadcn remains the core default for now."
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"stableName": "ui.surface.app.sidebar",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "core"
|
||||
},
|
||||
{
|
||||
"stableName": "AppSidebar",
|
||||
"path": "apps/web/app/application/sidebar/AppSidebar.tsx",
|
||||
"category": "navigation",
|
||||
"library": "application-registry",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "ui.surface.admin.sidebar",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "core"
|
||||
},
|
||||
{
|
||||
"stableName": "AdminSidebar",
|
||||
"path": "apps/web/app/application/sidebar/AdminSidebar.tsx",
|
||||
"category": "navigation",
|
||||
"library": "application-registry",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "ui.surface.docs.sidebar",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "core"
|
||||
},
|
||||
{
|
||||
"stableName": "DocsSidebar",
|
||||
"path": "apps/web/app/application/sidebar/DocsSidebar.ts",
|
||||
"category": "navigation",
|
||||
"library": "application-registry",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "ShadcnSidebarLayout",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/radix",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "UiRegistry",
|
||||
"path": "apps/web/app/application/ui/uiRegistry.ts",
|
||||
"category": "layout",
|
||||
"library": "agent-registry",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "NavMain",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/nav-main.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "NavSecondary",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/nav-secondary.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "NavQuickLinks",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/nav-quick-links.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "NavActions",
|
||||
"path": "apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix/lucide/novu/custom",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "SidebarPrimitives",
|
||||
"path": "apps/web/app/components/ui/sidebar.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "Button",
|
||||
"path": "apps/web/app/components/ui/button.tsx",
|
||||
"category": "buttons",
|
||||
"library": "shadcn/radix",
|
||||
"status": "available"
|
||||
},
|
||||
{
|
||||
"stableName": "Dialog",
|
||||
"path": "apps/web/app/components/ui/dialog.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Modal",
|
||||
"path": "apps/web/app/components/ui/modals/Modal.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "ProfileButton",
|
||||
"path": "apps/web/app/components/layouts/buttons/ProfileButton.tsx",
|
||||
"category": "buttons",
|
||||
"library": "headlessui/custom",
|
||||
"status": "legacy"
|
||||
},
|
||||
{
|
||||
"stableName": "SearchButton",
|
||||
"path": "apps/web/app/components/layouts/buttons/SearchButton.tsx",
|
||||
"category": "buttons",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Accordion",
|
||||
"path": "apps/web/app/components/ui/accordion.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "AlertDialog",
|
||||
"path": "apps/web/app/components/ui/alert-dialog.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Alert",
|
||||
"path": "apps/web/app/components/ui/alert.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "AspectRatio",
|
||||
"path": "apps/web/app/components/ui/aspect-ratio.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Avatar",
|
||||
"path": "apps/web/app/components/ui/avatar.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Badge",
|
||||
"path": "apps/web/app/components/ui/badge.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Breadcrumb",
|
||||
"path": "apps/web/app/components/ui/breadcrumb.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Input",
|
||||
"path": "apps/web/app/components/ui/input.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "ButtonConfirm",
|
||||
"path": "apps/web/app/components/ui/button-confirm.tsx",
|
||||
"category": "buttons",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "ButtonGroup",
|
||||
"path": "apps/web/app/components/ui/button-group.tsx",
|
||||
"category": "buttons",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Calendar",
|
||||
"path": "apps/web/app/components/ui/calendar.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Card",
|
||||
"path": "apps/web/app/components/ui/card.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Carousel",
|
||||
"path": "apps/web/app/components/ui/carousel.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/embla",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Chart",
|
||||
"path": "apps/web/app/components/ui/chart.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/recharts",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Checkbox",
|
||||
"path": "apps/web/app/components/ui/checkbox.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Choicebox",
|
||||
"path": "apps/web/app/components/ui/choicebox.tsx",
|
||||
"category": "inputs",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Collapsible",
|
||||
"path": "apps/web/app/components/ui/collapsible.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Command",
|
||||
"path": "apps/web/app/components/ui/command.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/cmdk",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "ContextMenu",
|
||||
"path": "apps/web/app/components/ui/context-menu.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Drawer",
|
||||
"path": "apps/web/app/components/ui/drawer.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "shadcn/vaul",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "DropdownMenu",
|
||||
"path": "apps/web/app/components/ui/dropdown-menu.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Empty",
|
||||
"path": "apps/web/app/components/ui/empty.tsx",
|
||||
"category": "feedback",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Form",
|
||||
"path": "apps/web/app/components/ui/form.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix/react-hook-form",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "HoverCard",
|
||||
"path": "apps/web/app/components/ui/hover-card.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "InputGroup",
|
||||
"path": "apps/web/app/components/ui/input-group.tsx",
|
||||
"category": "inputs",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "InputOtp",
|
||||
"path": "apps/web/app/components/ui/input-otp.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/input-otp",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Label",
|
||||
"path": "apps/web/app/components/ui/label.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "MultiSelect",
|
||||
"path": "apps/web/app/components/ui/multi-select.tsx",
|
||||
"category": "inputs",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Pagination",
|
||||
"path": "apps/web/app/components/ui/pagination.tsx",
|
||||
"category": "navigation",
|
||||
"library": "shadcn/custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Popover",
|
||||
"path": "apps/web/app/components/ui/popover.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Progress",
|
||||
"path": "apps/web/app/components/ui/progress.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "RadioGroup",
|
||||
"path": "apps/web/app/components/ui/radio-group.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Resizable",
|
||||
"path": "apps/web/app/components/ui/resizable.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/react-resizable-panels",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "ScrollArea",
|
||||
"path": "apps/web/app/components/ui/scroll-area.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Select",
|
||||
"path": "apps/web/app/components/ui/select.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Separator",
|
||||
"path": "apps/web/app/components/ui/separator.tsx",
|
||||
"category": "layout",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Sheet",
|
||||
"path": "apps/web/app/components/ui/sheet.tsx",
|
||||
"category": "dialogs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Skeleton",
|
||||
"path": "apps/web/app/components/ui/skeleton.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Slider",
|
||||
"path": "apps/web/app/components/ui/slider.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Sonner",
|
||||
"path": "apps/web/app/components/ui/sonner.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/sonner",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "SplitButton",
|
||||
"path": "apps/web/app/components/ui/split-button.tsx",
|
||||
"category": "buttons",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Stepper",
|
||||
"path": "apps/web/app/components/ui/stepper.tsx",
|
||||
"category": "inputs",
|
||||
"library": "custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Switch",
|
||||
"path": "apps/web/app/components/ui/switch.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Table",
|
||||
"path": "apps/web/app/components/ui/table.tsx",
|
||||
"category": "tables",
|
||||
"library": "shadcn/custom",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Tabs",
|
||||
"path": "apps/web/app/components/ui/tabs.tsx",
|
||||
"category": "tables",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Textarea",
|
||||
"path": "apps/web/app/components/ui/textarea.tsx",
|
||||
"category": "inputs",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "ToastPrimitive",
|
||||
"path": "apps/web/app/components/ui/toast.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/radix",
|
||||
"status": "legacy"
|
||||
},
|
||||
{
|
||||
"stableName": "Toaster",
|
||||
"path": "apps/web/app/components/ui/toaster.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/radix",
|
||||
"status": "legacy"
|
||||
},
|
||||
{
|
||||
"stableName": "ToggleGroup",
|
||||
"path": "apps/web/app/components/ui/toggle-group.tsx",
|
||||
"category": "buttons",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Toggle",
|
||||
"path": "apps/web/app/components/ui/toggle.tsx",
|
||||
"category": "buttons",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Tooltip",
|
||||
"path": "apps/web/app/components/ui/tooltip.tsx",
|
||||
"category": "feedback",
|
||||
"library": "shadcn/radix",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Toast",
|
||||
"path": "apps/web/app/root.tsx",
|
||||
"category": "feedback",
|
||||
"library": "sonner",
|
||||
"status": "candidate-for-adapter"
|
||||
},
|
||||
{
|
||||
"stableName": "Lucide icons",
|
||||
"path": "apps/web/app/components/layouts/icons",
|
||||
"category": "feedback",
|
||||
"library": "lucide/custom",
|
||||
"status": "plugin"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
# UI Hot-Swap Status
|
||||
|
||||
## What's wired
|
||||
|
||||
The boilerplate ships three UI adapter packages under `packages/`:
|
||||
|
||||
| Package | Purpose | Current state |
|
||||
| --- | --- | --- |
|
||||
| `@toolctrl/ui-app` | App-shell registries (sidebar, breadcrumb, mobile shell, portals) | Skeleton + registry types, no concrete component bindings. |
|
||||
| `@toolctrl/ui-subframe` | Subframe (design-tool) component placeholder bridge | Placeholder factory only — `createSubframePlaceholder()` returns `{ status: "empty" }`. |
|
||||
| `@toolctrl/ui-mock` | Fixture-driven mock surface for visual/hot-swap testing | Has `createHotSwapDrillPage()` and is consumed by `scripts/verify-ui-hot-swap.ts`. |
|
||||
|
||||
`packages/foundation` and `packages/view-models` provide the contract these adapters compose against.
|
||||
|
||||
## What is NOT wired
|
||||
|
||||
- `@toolctrl/ui-subframe` is still a placeholder bridge — no real design-source binding.
|
||||
- Outside the activation reference route (see below), `apps/web/app/routes/**` does not render through any `@toolctrl/ui-*` registry — concrete components still live in `apps/web/app/components/`.
|
||||
- The majority of consumer surfaces (admin shell, tenant dashboard, marketing) have not been refactored to consume from `@toolctrl/ui-app`'s page registry.
|
||||
|
||||
## What IS wired (activation reference)
|
||||
|
||||
A minimal hot-swap activation now lives in `apps/web`:
|
||||
|
||||
- `apps/web/package.json` depends on `@toolctrl/ui-app`, `@toolctrl/ui-mock`, `@toolctrl/foundation`, `@toolctrl/view-models`.
|
||||
- `apps/web/app/components/ui/hot-swap/HotSwapPageRenderer.tsx` bridges the descriptor shape from `@toolctrl/ui-mock` to RR7 JSX using `createShellRegistries()` from `@toolctrl/ui-app`.
|
||||
- `apps/web/app/routes/hot-swap-demo.tsx` renders `createHotSwapDrillPage()` through the renderer. The route is gated to `NODE_ENV !== "production"` so production surface area is unchanged.
|
||||
- `apps/web/app/components/ui/hot-swap/HotSwapPageRenderer.test.tsx` asserts the descriptor → DOM mapping (sidebar, breadcrumbs, header, cards, activity).
|
||||
|
||||
This is the first activation milestone: the registry path is alive end-to-end against a real consumer route, but is still fixture-driven (no Subframe binding yet).
|
||||
|
||||
## Why this is intentional (Settings API Onboarding-era)
|
||||
|
||||
The adapter packages were introduced in the Phase-C boundary work to lock down what a **whitelabel-friendly** UI swap can look like without forcing a big-bang component rewrite. Boundaries are enforced by `scripts/verify-phase-c-boundaries.mjs` so any future activation cannot accidentally cross a runtime line.
|
||||
|
||||
Until an activation milestone explicitly enables Subframe (or another design source), the registries stay as a structural placeholder. The cost of registering them in `apps/web` without a real binding would be:
|
||||
|
||||
- pulling `@toolctrl/foundation` + `@toolctrl/view-models` into the web bundle for no consumer,
|
||||
- adding registry warm-up code paths to SSR + hot-swap tests with no real fixture to exercise,
|
||||
- noise in the hot-swap verifier (`verify:ui-hot-swap`) that would have to allowlist “empty registry” permanently.
|
||||
|
||||
## When to activate
|
||||
|
||||
Activate the hot-swap path when **at least one** is true:
|
||||
|
||||
- A real Subframe/Figma source is being integrated and `ui-subframe` becomes a non-empty bridge.
|
||||
- A second consumer app (admin shell, partner portal) needs to share `apps/web` components through a registry instead of importing relative paths.
|
||||
- A whitelabel deployment needs to swap the dashboard surface per-tenant without forking the codebase.
|
||||
|
||||
## Activation checklist
|
||||
|
||||
1. ✓ Add `@toolctrl/ui-app`, `@toolctrl/ui-mock`, `@toolctrl/foundation`, `@toolctrl/view-models` to `apps/web/package.json` `dependencies`. — done.
|
||||
2. ✓ Pick one low-risk route and refactor it to consume from `@toolctrl/ui-app`'s page registry. — done at `apps/web/app/routes/hot-swap-demo.tsx`, dev-only.
|
||||
3. ☐ Add `@toolctrl/ui-subframe` to `apps/web/package.json` once it becomes a non-empty bridge.
|
||||
4. ☐ Promote the mock fixture in `packages/ui-mock/src/index.ts` (`createHotSwapDrillPage`) to a non-mock descriptor source once a design tool is wired.
|
||||
5. ☐ Refactor a second real consumer surface (admin shell or tenant dashboard) through the renderer to prove cross-surface reuse.
|
||||
6. ☐ Update `docs/ui-inventory/surface-map.md` to claim the new surfaces as hot-swappable.
|
||||
|
||||
## Related verification
|
||||
|
||||
```sh
|
||||
pnpm run verify:phase-c-boundaries # boundaries that future activation must not cross
|
||||
pnpm run verify:ui-visual # current mock visual baseline
|
||||
pnpm run test:hot-swap # mock-driven hot-swap drill
|
||||
```
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://toolctrl.local/ui-inventory.schema.json",
|
||||
"title": "Toolctrl UI Inventory",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schemaVersion", "notes", "components"],
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"type": "integer",
|
||||
"const": 1
|
||||
},
|
||||
"notes": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"components": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/component" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"component": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["stableName", "path", "category", "library", "status"],
|
||||
"properties": {
|
||||
"stableName": { "type": "string", "minLength": 1 },
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": ["navigation", "layout", "buttons", "dialogs", "tables", "inputs", "feedback"]
|
||||
},
|
||||
"library": { "type": "string", "minLength": 1 },
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["core", "available", "legacy", "plugin", "candidate-for-adapter"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
# UI Surface Map
|
||||
|
||||
This file maps stable UI IDs to the source files an agent should inspect before changing a surface.
|
||||
|
||||
Machine-readable map: `docs/ui-inventory/surfaces.json`.
|
||||
|
||||
## Shell Surfaces
|
||||
|
||||
| UI ID | Source | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ui.surface.shell.header` | `apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx` | Authenticated shell header. |
|
||||
| `ui.surface.shell.breadcrumb` | `apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx` | Header breadcrumb container. |
|
||||
| `ui.surface.shell.breadcrumb.current` | `apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx` | Current page title. |
|
||||
| `ui.surface.shell.navActions` | `apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx` | Header right action area. |
|
||||
| `ui.surface.shell.main` | `apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx` | Authenticated page content. |
|
||||
|
||||
## Sidebar Surfaces
|
||||
|
||||
| UI ID | Source | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ui.surface.app.sidebar` | `apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx`, `apps/web/app/application/sidebar/AppSidebar.tsx` | Tenant app sidebar. |
|
||||
| `ui.surface.admin.sidebar` | `apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx`, `apps/web/app/application/sidebar/AdminSidebar.tsx` | Admin sidebar. |
|
||||
| `ui.surface.docs.sidebar` | `apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx`, `apps/web/app/application/sidebar/DocsSidebar.ts` | Docs sidebar. |
|
||||
|
||||
## Header Actions
|
||||
|
||||
| UI ID | Source | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ui.action.shell.sidebarTrigger` | `ShadcnSidebarLayout.tsx`, `apps/web/app/components/ui/sidebar.tsx` | Open/collapse sidebar. |
|
||||
| `ui.action.shell.search` | `nav-actions.tsx`, `SearchButton.tsx` | Open command palette/search. |
|
||||
| `ui.action.shell.aiAssistant` | `nav-actions.tsx` | Open AI assistant. |
|
||||
| `ui.action.shell.notifications` | `nav-actions.tsx` | Notifications. |
|
||||
| `ui.action.shell.onboarding` | `nav-actions.tsx` | Onboarding. |
|
||||
| `ui.action.shell.feedback` | `nav-actions.tsx` | Feedback dialog. |
|
||||
| `ui.action.shell.chatSupport` | `nav-actions.tsx` | External support chat. |
|
||||
| `ui.action.shell.more` | `nav-actions.tsx` | More menu. |
|
||||
| `ui.action.shell.profile` | `ProfileButton.tsx`, `nav-actions.tsx` | Profile action. |
|
||||
| `ui.action.shell.subscription` | `nav-actions.tsx` | Subscription action. |
|
||||
| `ui.action.shell.signOut` | `nav-actions.tsx` | Sign out. |
|
||||
|
||||
## Core Nav IDs
|
||||
|
||||
| UI ID | Source | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ui.nav.app.dashboard` | `AppSidebar.tsx` | Tenant dashboard. |
|
||||
| `ui.nav.app.settings` | `AppSidebar.tsx` | Tenant settings. |
|
||||
| `ui.nav.app.switchToAdmin` | `AppSidebar.tsx` | Tenant-to-admin switch. |
|
||||
| `ui.nav.admin.dashboard` | `AdminSidebar.tsx` | Admin dashboard. |
|
||||
| `ui.nav.admin.accounts` | `AdminSidebar.tsx` | Accounts and users. |
|
||||
| `ui.nav.admin.analytics` | `AdminSidebar.tsx` | Analytics. |
|
||||
| `ui.nav.admin.entities` | `AdminSidebar.tsx` | Entity builder/runtime. |
|
||||
| `ui.nav.admin.workflows` | `AdminSidebar.tsx` | Workflow plugin candidate. |
|
||||
| `ui.nav.admin.api` | `AdminSidebar.tsx` | API key area. |
|
||||
| `ui.nav.admin.events` | `AdminSidebar.tsx` | Events. |
|
||||
| `ui.nav.admin.auditTrails` | `AdminSidebar.tsx` | Audit trails. |
|
||||
| `ui.nav.admin.notifications` | `AdminSidebar.tsx` | Notifications. |
|
||||
| `ui.nav.admin.featureFlags` | `AdminSidebar.tsx` | Feature flags. |
|
||||
| `ui.nav.admin.navigation` | `AdminSidebar.tsx` | Navigation configuration. |
|
||||
| `ui.nav.admin.portals` | `AdminSidebar.tsx` | Portals plugin candidate. |
|
||||
| `ui.nav.admin.prompts` | `AdminSidebar.tsx` | Prompt builder plugin candidate. |
|
||||
| `ui.nav.admin.emailMarketing` | `AdminSidebar.tsx` | Email marketing plugin candidate. |
|
||||
| `ui.nav.admin.knowledgeBase` | `AdminSidebar.tsx` | Knowledge base plugin candidate. |
|
||||
| `ui.nav.admin.helpDesk` | `AdminSidebar.tsx` | Help desk plugin candidate. |
|
||||
| `ui.nav.admin.onboarding` | `AdminSidebar.tsx` | Onboarding. |
|
||||
| `ui.nav.admin.metrics` | `AdminSidebar.tsx` | Metrics. |
|
||||
| `ui.nav.admin.settings` | `AdminSidebar.tsx` | Admin settings. |
|
||||
| `ui.nav.admin.switchToApp` | `AdminSidebar.tsx` | Admin-to-app switch. |
|
||||
| `ui.nav.docs.introduction` | `DocsSidebar.ts` | Docs introduction. |
|
||||
|
||||
## Dynamic Patterns
|
||||
|
||||
| Pattern | Source | Notes |
|
||||
| --- | --- | --- |
|
||||
| `ui.nav.app.entity.{entityName}` | `AppSidebar.tsx` | Tenant entity nav entries. |
|
||||
| `ui.nav.app.entityGroup.{groupSlug}` | `AppSidebar.tsx` | Tenant entity group nav entries. |
|
||||
| `ui.nav.app.entityGroup.{groupSlug}.entity.{entityName}` | `AppSidebar.tsx` | Nested entity group entries. |
|
||||
| `ui.nav.app.section.{sectionName}` | `AppSidebar.tsx` | Tenant sidebar sections. |
|
||||
| `ui.nav.docs.route.{routeSlug}` | `DocsSidebar.ts` | Docs route entries. |
|
||||
|
||||
## Agent Guidance
|
||||
|
||||
When a prompt says "header right", "search", "sidebar", or "settings nav", resolve it to a UI ID first, then inspect the mapped source file. If no stable UI ID exists for a new surface, add it to `apps/web/app/application/ui/uiRegistry.ts`, render it as `data-ui-id`, update `surfaces.json`, and add a DOM test.
|
||||
@@ -0,0 +1,485 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"surfaces": [
|
||||
{
|
||||
"id": "ui.surface.shell",
|
||||
"kind": "surface",
|
||||
"label": "Authenticated shell",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "SidebarProvider",
|
||||
"notes": ["Root authenticated shell surface for app, admin and docs layouts."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.inset",
|
||||
"kind": "surface",
|
||||
"label": "Authenticated shell inset",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "SidebarInset",
|
||||
"notes": ["Inset area that contains the shell header and main content."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.app.sidebar",
|
||||
"kind": "surface",
|
||||
"label": "Tenant app sidebar",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx", "apps/web/app/application/sidebar/AppSidebar.tsx"],
|
||||
"renderer": "ShadcnAppSidebar",
|
||||
"notes": ["Use for prompts that target the tenant sidebar container."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.admin.sidebar",
|
||||
"kind": "surface",
|
||||
"label": "Admin sidebar",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx", "apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "ShadcnAppSidebar",
|
||||
"notes": ["Use for prompts that target the admin sidebar container."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.docs.sidebar",
|
||||
"kind": "surface",
|
||||
"label": "Docs sidebar",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx", "apps/web/app/application/sidebar/DocsSidebar.ts"],
|
||||
"renderer": "ShadcnAppSidebar",
|
||||
"notes": ["Use for prompts that target documentation navigation."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.header",
|
||||
"kind": "surface",
|
||||
"label": "Shell header",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx"],
|
||||
"renderer": "ShadcnSidebarLayout",
|
||||
"notes": ["Top authenticated shell bar containing sidebar trigger, breadcrumb and right actions."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.breadcrumb",
|
||||
"kind": "surface",
|
||||
"label": "Shell breadcrumb area",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx"],
|
||||
"renderer": "ShadcnSidebarLayout",
|
||||
"notes": ["Left header area after the sidebar trigger."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.breadcrumb.current",
|
||||
"kind": "surface",
|
||||
"label": "Current breadcrumb page",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx"],
|
||||
"renderer": "BreadcrumbPage",
|
||||
"notes": ["Current page title inside the shell breadcrumb."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.main",
|
||||
"kind": "surface",
|
||||
"label": "Shell main content",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx"],
|
||||
"renderer": "main",
|
||||
"notes": ["Authenticated page content host."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.navActions",
|
||||
"kind": "surface",
|
||||
"label": "Header right action area",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx", "apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Right side of header with search, notifications, AI, profile/more actions."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.aiAssistant.dialog",
|
||||
"kind": "surface",
|
||||
"label": "AI assistant dialog",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "DialogContent",
|
||||
"notes": ["Dialog surface for AI assistant chat."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.shell.feedback.dialog",
|
||||
"kind": "surface",
|
||||
"label": "Feedback dialog",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx", "apps/web/app/components/ui/modals/Modal.tsx"],
|
||||
"renderer": "Modal",
|
||||
"notes": ["Dialog surface for tenant feedback."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.commandPalette.portal",
|
||||
"kind": "surface",
|
||||
"label": "Command palette portal",
|
||||
"sourceFiles": ["apps/web/app/components/ui/commandPalettes/CommandPalette.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "KBarPortal",
|
||||
"notes": ["Portal host for global command palette overlays."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.commandPalette.search",
|
||||
"kind": "surface",
|
||||
"label": "Command palette search",
|
||||
"sourceFiles": ["apps/web/app/components/ui/commandPalettes/CommandPalette.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "KBarSearch",
|
||||
"notes": ["Search input inside the global command palette."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.commandPalette.results",
|
||||
"kind": "surface",
|
||||
"label": "Command palette results",
|
||||
"sourceFiles": ["apps/web/app/components/ui/commandPalettes/CommandPalette.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "KBarResults",
|
||||
"notes": ["Scrollable result list inside the global command palette."]
|
||||
},
|
||||
{
|
||||
"id": "ui.surface.commandPalette.row",
|
||||
"kind": "surface",
|
||||
"label": "Command palette row",
|
||||
"sourceFiles": ["apps/web/app/components/ui/commandPalettes/CommandPalette.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "CommandPaletteResult",
|
||||
"notes": ["Individual selectable command palette result row."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.sidebarTrigger",
|
||||
"kind": "action",
|
||||
"label": "Sidebar trigger",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx", "apps/web/app/components/ui/sidebar.tsx"],
|
||||
"renderer": "SidebarTrigger",
|
||||
"notes": ["Collapses or opens the shell sidebar."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.search",
|
||||
"kind": "action",
|
||||
"label": "Header search",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx", "apps/web/app/components/layouts/buttons/SearchButton.tsx"],
|
||||
"renderer": "NavActions/SearchButton",
|
||||
"notes": ["Opens command palette/search."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.aiAssistant",
|
||||
"kind": "action",
|
||||
"label": "AI assistant button",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Opens AI assistant dialog when available."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.aiAssistant.expand",
|
||||
"kind": "action",
|
||||
"label": "AI assistant expand",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Expands dialog chat into full chat route."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.aiAssistant.close",
|
||||
"kind": "action",
|
||||
"label": "AI assistant close",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Closes AI assistant dialog."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.notifications",
|
||||
"kind": "action",
|
||||
"label": "Notifications",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Novu-backed notifications trigger."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.onboarding",
|
||||
"kind": "action",
|
||||
"label": "Onboarding",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "OnboardingButton",
|
||||
"notes": ["Opens onboarding flow when enabled."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.feedback",
|
||||
"kind": "action",
|
||||
"label": "Feedback",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "AddFeedbackButton",
|
||||
"notes": ["Opens tenant feedback dialog."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.chatSupport",
|
||||
"kind": "action",
|
||||
"label": "Chat support",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Opens external support chat when configured."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.more",
|
||||
"kind": "action",
|
||||
"label": "More actions",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx", "apps/web/app/components/layouts/sidebars/shadcn/nav-quick-links.tsx"],
|
||||
"renderer": "PopoverTrigger/SidebarMenuButton",
|
||||
"notes": ["Header or quick-link overflow actions."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.profile",
|
||||
"kind": "action",
|
||||
"label": "Profile",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/buttons/ProfileButton.tsx", "apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "ProfileButton/NavActions",
|
||||
"notes": ["User profile or profile menu action."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.subscription",
|
||||
"kind": "action",
|
||||
"label": "Subscription",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Subscription CTA or menu action."]
|
||||
},
|
||||
{
|
||||
"id": "ui.action.shell.signOut",
|
||||
"kind": "action",
|
||||
"label": "Sign out",
|
||||
"sourceFiles": ["apps/web/app/components/layouts/sidebars/shadcn/nav-actions.tsx"],
|
||||
"renderer": "NavActions",
|
||||
"notes": ["Sign-out action in overflow menu."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.dashboard",
|
||||
"kind": "nav",
|
||||
"label": "Tenant dashboard nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant dashboard sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.notifications",
|
||||
"kind": "nav",
|
||||
"label": "Tenant notifications nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant notification center sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.crm",
|
||||
"kind": "nav",
|
||||
"label": "Tenant CRM nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant CRM sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.emailMarketing",
|
||||
"kind": "nav",
|
||||
"label": "Tenant email marketing nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant email marketing sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.api",
|
||||
"kind": "nav",
|
||||
"label": "Tenant API nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant API keys sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.workflowEngine",
|
||||
"kind": "nav",
|
||||
"label": "Tenant workflow engine nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant workflow engine sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.credits",
|
||||
"kind": "nav",
|
||||
"label": "Tenant credits nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx", "apps/web/app/application/ui/uiRegistry.ts"],
|
||||
"renderer": "AppSidebar/NavMain",
|
||||
"notes": ["Tenant credits sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.settings",
|
||||
"kind": "nav",
|
||||
"label": "Tenant settings nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx"],
|
||||
"renderer": "AppSidebar/NavSecondary",
|
||||
"notes": ["Tenant settings sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.app.switchToAdmin",
|
||||
"kind": "nav",
|
||||
"label": "Switch to admin nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AppSidebar.tsx"],
|
||||
"renderer": "AppSidebar/NavSecondary",
|
||||
"notes": ["Tenant sidebar admin switch item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.dashboard",
|
||||
"kind": "nav",
|
||||
"label": "Admin dashboard nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin dashboard sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.accounts",
|
||||
"kind": "nav",
|
||||
"label": "Admin accounts nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin accounts and users sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.analytics",
|
||||
"kind": "nav",
|
||||
"label": "Admin analytics nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin analytics sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.entities",
|
||||
"kind": "nav",
|
||||
"label": "Admin entities nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin entities sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.workflows",
|
||||
"kind": "nav",
|
||||
"label": "Admin workflows nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate workflow sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.api",
|
||||
"kind": "nav",
|
||||
"label": "Admin API nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin API keys sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.events",
|
||||
"kind": "nav",
|
||||
"label": "Admin events nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin events sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.auditTrails",
|
||||
"kind": "nav",
|
||||
"label": "Admin audit trails nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin audit trail sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.notifications",
|
||||
"kind": "nav",
|
||||
"label": "Admin notifications nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin notifications sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.featureFlags",
|
||||
"kind": "nav",
|
||||
"label": "Admin feature flags nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin feature flags sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.navigation",
|
||||
"kind": "nav",
|
||||
"label": "Admin navigation nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin navigation/sidebar configuration item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.portals",
|
||||
"kind": "nav",
|
||||
"label": "Admin portals nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate portals sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.prompts",
|
||||
"kind": "nav",
|
||||
"label": "Admin prompts nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate prompts sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.emailMarketing",
|
||||
"kind": "nav",
|
||||
"label": "Admin email marketing nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate email marketing sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.marketing",
|
||||
"kind": "nav",
|
||||
"label": "Admin marketing nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate marketing sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.knowledgeBase",
|
||||
"kind": "nav",
|
||||
"label": "Admin knowledge base nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate knowledge base sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.helpDesk",
|
||||
"kind": "nav",
|
||||
"label": "Admin help desk nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Disabled plugin-candidate help desk sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.onboarding",
|
||||
"kind": "nav",
|
||||
"label": "Admin onboarding nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin onboarding sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.metrics",
|
||||
"kind": "nav",
|
||||
"label": "Admin metrics nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin metrics sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.settings",
|
||||
"kind": "nav",
|
||||
"label": "Admin settings nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin settings sidebar item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.admin.switchToApp",
|
||||
"kind": "nav",
|
||||
"label": "Switch to app nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/AdminSidebar.tsx"],
|
||||
"renderer": "AdminSidebar/NavMain",
|
||||
"notes": ["Admin sidebar app switch item."]
|
||||
},
|
||||
{
|
||||
"id": "ui.nav.docs.introduction",
|
||||
"kind": "nav",
|
||||
"label": "Docs introduction nav",
|
||||
"sourceFiles": ["apps/web/app/application/sidebar/DocsSidebar.ts"],
|
||||
"renderer": "DocsSidebar/NavMain",
|
||||
"notes": ["Docs introduction navigation item."]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://toolctrl.local/ui-surfaces.schema.json",
|
||||
"title": "Toolctrl UI Surface Map",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schemaVersion", "surfaces"],
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"type": "integer",
|
||||
"const": 1
|
||||
},
|
||||
"surfaces": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/surface" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"surface": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "kind", "label", "sourceFiles", "renderer", "notes"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^ui\\.(surface|action|nav)\\."
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["surface", "action", "nav"]
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"sourceFiles": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"renderer": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"notes": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user