From 11483a0b5dafa501f6962186931ece2f4b02a416 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Wed, 29 Jul 2026 13:58:23 +0700 Subject: [PATCH 1/4] feat(registry): add dashboard source blocks --- .github/workflows/ci.yml | 6 + AGENTS.md | 14 +- CLAUDE.md | 12 +- README.md | 4 +- apps/blocks/package.json | 1 + apps/blocks/registry.json | 76 +- .../src/app/blocks/command-palette/page.tsx | 138 +++ apps/blocks/src/app/blocks/org-chart/page.tsx | 31 + .../src/app/blocks/org-chart/preview/page.tsx | 18 + apps/blocks/src/app/blocks/page.tsx | 54 +- .../src/app/blocks/schema-builder/page.tsx | 23 + apps/blocks/src/app/blocks/sheets/page.tsx | 23 + .../src/app/blocks/storage-browser/page.tsx | 31 + .../blocks/storage-browser/preview/page.tsx | 18 + apps/blocks/src/app/sitemap.test.ts | 16 +- apps/blocks/src/app/sitemap.ts | 7 + .../background/background-task-stack.tsx | 52 + .../background/inline-task-bar.tsx | 153 +++ .../command-palette/background/task-card.tsx | 89 ++ .../command-palette/background/task-icons.tsx | 60 + .../command-palette/command-palette.tsx | 291 +++++ .../blocks/command-palette/kbd-shortcut.tsx | 30 + .../multi-step/multi-step-view.test.tsx | 117 ++ .../multi-step/multi-step-view.tsx | 172 +++ .../multi-step/step-indicator.tsx | 45 + .../application-block-docs-page.tsx | 200 ++++ .../application-block-showcase-canvas.tsx | 427 +++++++ .../application-block-showcase-preview.tsx | 370 ++++++ .../command-palette-demo.tsx | 140 +++ .../components/site/registry-shell.test.tsx | 2 + .../src/components/site/registry-shell.tsx | 2 +- .../src/components/site/site-sidebar.tsx | 43 +- .../src/components/site/site-topbar.test.tsx | 41 +- .../src/components/site/site-topbar.tsx | 45 + .../source-block-docs-page.tsx | 240 ++++ apps/blocks/src/lib/application-blocks.ts | 292 +++++ apps/blocks/src/lib/source-blocks.ts | 189 +++ apps/registry/REGISTRY.md | 55 +- .../registry/scripts/build-source-registry.ts | 163 +++ apps/registry/scripts/build.ts | 14 +- apps/registry/scripts/compiler.test.ts | 31 +- apps/registry/scripts/compiler.ts | 43 +- apps/registry/scripts/smoke-install.ts | 111 +- docs/RELEASING.md | 24 +- package.json | 2 +- packages/command-palette/CHANGELOG.md | 7 + packages/command-palette/LICENSE | 21 + packages/command-palette/README.md | 62 + packages/command-palette/docs/SPEC.md | 115 ++ packages/command-palette/package.json | 71 ++ .../src/__tests__/background-tasks.test.ts | 363 ++++++ .../src/__tests__/execution.test.ts | 163 +++ .../src/__tests__/global-shortcuts.test.ts | 207 ++++ .../src/__tests__/keybinding.test.ts | 277 +++++ .../src/__tests__/multi-step-builder.test.ts | 135 +++ .../src/__tests__/multi-step-reducer.test.ts | 439 +++++++ .../src/__tests__/registry.test.ts | 179 +++ .../command-palette/src/background/index.ts | 10 + .../command-palette/src/background/types.ts | 27 + .../src/background/use-background-tasks.ts | 204 ++++ packages/command-palette/src/hooks.ts | 90 ++ packages/command-palette/src/index.ts | 54 + packages/command-palette/src/keybinding.ts | 89 ++ .../src/machine/use-machine.ts | 115 ++ .../command-palette/src/multi-step/builder.ts | 88 ++ .../command-palette/src/multi-step/index.ts | 30 + .../command-palette/src/multi-step/machine.ts | 258 ++++ .../command-palette/src/multi-step/reducer.ts | 176 +++ .../command-palette/src/multi-step/state.ts | 41 + .../command-palette/src/multi-step/types.ts | 64 + .../multi-step/use-multi-step-execution.ts | 97 ++ packages/command-palette/src/registry.ts | 73 ++ packages/command-palette/src/types.ts | 89 ++ .../src/use-global-shortcuts.ts | 48 + packages/command-palette/tsconfig.json | 23 + packages/command-palette/tsup.config.ts | 15 + packages/command-palette/vitest.config.ts | 7 + packages/data/package.json | 3 +- .../data/src/__tests__/error-handler.test.ts | 475 ++------ packages/data/src/error-handler.ts | 736 ++---------- packages/data/src/index.ts | 28 +- packages/schema-builder/README.md | 16 +- packages/schema-builder/package.json | 4 +- packages/schema-builder/registry.json | 1050 ++++++++--------- .../schema-builder/scripts/build-registry.ts | 140 ++- .../__tests__/schema-builder-render.test.tsx | 28 +- .../schema-builder-selectors.test.tsx | 56 +- .../src/compat/modules-client.ts | 11 - .../src/components/schema-builder.tsx | 37 +- packages/schema-builder/src/core/context.tsx | 77 +- packages/schema-builder/src/index.ts | 1 + .../schemas/schema-state-display.tsx | 3 +- .../components/shared/base-state-display.tsx | 14 +- .../lib/data/error-handler.ts | 649 ---------- .../schema-builder-core/lib/data/index.ts | 10 +- .../lib/gql/auth-error-handler.tsx | 10 +- .../lib/gql/hooks/schema-builder/index.ts | 1 + .../use-accessible-databases.ts | 5 +- .../use-database-constraints.ts | 5 +- .../use-schema-builder-selectors.ts | 15 +- .../schema-builder/schema-builder-block.tsx | 32 - packages/schema-builder/src/types.ts | 2 + packages/sheets/.gitignore | 6 +- packages/sheets/README.md | 39 +- packages/sheets/docs/FEATURES.md | 2 +- packages/sheets/docs/HOST_INTEGRATION.md | 2 +- .../sheets/docs/SHEETS_IMPROVEMENT_ROADMAP.md | 12 +- packages/sheets/package.json | 6 +- packages/sheets/registry.json | 982 +++++++++++++++ packages/sheets/scripts/build-registry.ts | 43 + .../src/adapter/postgraphile-adapter.ts | 4 +- .../src/cell-model/views/badges-view.tsx | 2 +- .../src/cell-model/views/boolean-view.tsx | 2 +- .../src/cell-model/views/datetime-view.tsx | 2 +- .../cell-model/views/draft-action-view.tsx | 2 +- .../src/cell-model/views/geometry-view.tsx | 2 +- .../src/cell-model/views/image-view.tsx | 2 +- .../src/cell-model/views/interval-view.tsx | 2 +- .../src/cell-model/views/loading-view.tsx | 2 +- .../src/cell-model/views/number-view.tsx | 2 +- .../src/cell-model/views/relation-view.tsx | 2 +- .../sheets/src/cell-model/views/text-view.tsx | 2 +- .../sheets/src/context/sheets-execute.test.ts | 21 +- packages/sheets/src/context/sheets-execute.ts | 10 +- .../src/grid-dom/overlay/overlay-manager.tsx | 2 +- .../sheets/src/grid-dom/sheets-cell-host.tsx | 2 +- .../__tests__/sheets-error-gating.test.tsx | 2 +- .../sheets/src/grid/editors/image-editor.tsx | 2 +- .../src/grid/editors/tsvector-editor.tsx | 2 +- .../sheets/src/grid/hooks/use-cell-editing.ts | 2 +- .../sheets/src/grid/hooks/use-load-grid.ts | 6 +- packages/sheets/src/grid/sheets.tsx | 26 +- packages/sheets/src/grid/use-sheets.ts | 2 +- packages/sheets/src/hooks/query-keys.test.ts | 29 + packages/sheets/src/hooks/query-keys.ts | 13 + .../src/hooks/use-sheets-infinite-table.ts | 16 +- packages/sheets/src/hooks/use-sheets-table.ts | 9 +- .../src/hooks/use-sheets-upload.test.ts | 18 +- .../sheets/src/hooks/use-sheets-upload.ts | 4 +- packages/sheets/src/index.ts | 8 +- packages/sheets/src/store/draft-rows-slice.ts | 8 +- packages/sheets/src/table/V9_API_NOTES.md | 8 +- packages/sheets/src/table/features.ts | 2 +- packages/sheets/src/table/grid-viewport.tsx | 6 +- .../src/table/use-sheets-table-instance.ts | 8 +- .../sheets/src/testing/graphql-schema-mock.ts | 2 +- packages/sheets/src/utils/map-picker.tsx | 2 +- packages/ui/package.json | 2 +- packages/ui/registry.json | 201 +++- packages/ui/scripts/build-registry.ts | 14 + packages/ui/src/components/org-chart/index.ts | 2 + .../ui/src/components/org-chart/layout.ts | 116 ++ .../org-chart/org-chart-context.tsx | 77 ++ .../components/org-chart/org-chart-edge.tsx | 23 + .../components/org-chart/org-chart-empty.tsx | 27 + .../components/org-chart/org-chart-node.tsx | 109 ++ .../components/org-chart/org-chart-utils.ts | 8 + .../ui/src/components/org-chart/org-chart.tsx | 339 ++++++ .../components/org-chart/org-chart.types.ts | 21 + .../storage/bucket-config-sheet.tsx | 365 ++++++ .../ui/src/components/storage/bucket-rail.tsx | 93 ++ .../src/components/storage/file-type-icon.tsx | 73 ++ packages/ui/src/components/storage/index.ts | 43 + .../storage/object-detail-sheet.tsx | 250 ++++ .../src/components/storage/object-table.tsx | 270 +++++ .../src/components/storage/object-toolbar.tsx | 142 +++ .../components/storage/storage-breadcrumb.tsx | 82 ++ .../components/storage/storage-browser.tsx | 198 ++++ .../storage/storage-empty-state.tsx | 94 ++ packages/ui/src/components/storage/types.ts | 58 + .../components/storage/upload-dropzone.tsx | 178 +++ packages/ui/src/components/storage/utils.ts | 91 ++ .../components/storage/visibility-badge.tsx | 61 + packages/ui/src/lib/use-mobile.ts | 59 +- pnpm-lock.yaml | 66 +- scripts/check-packed-packages.ts | 15 +- scripts/check-registry-contract.ts | 22 +- scripts/check-runtime-contract.ts | 7 + scripts/pack-local.ts | 1 + scripts/serve-local-registry.ts | 1 + 180 files changed, 12724 insertions(+), 2672 deletions(-) create mode 100644 apps/blocks/src/app/blocks/command-palette/page.tsx create mode 100644 apps/blocks/src/app/blocks/org-chart/page.tsx create mode 100644 apps/blocks/src/app/blocks/org-chart/preview/page.tsx create mode 100644 apps/blocks/src/app/blocks/schema-builder/page.tsx create mode 100644 apps/blocks/src/app/blocks/sheets/page.tsx create mode 100644 apps/blocks/src/app/blocks/storage-browser/page.tsx create mode 100644 apps/blocks/src/app/blocks/storage-browser/preview/page.tsx create mode 100644 apps/blocks/src/blocks/command-palette/background/background-task-stack.tsx create mode 100644 apps/blocks/src/blocks/command-palette/background/inline-task-bar.tsx create mode 100644 apps/blocks/src/blocks/command-palette/background/task-card.tsx create mode 100644 apps/blocks/src/blocks/command-palette/background/task-icons.tsx create mode 100644 apps/blocks/src/blocks/command-palette/command-palette.tsx create mode 100644 apps/blocks/src/blocks/command-palette/kbd-shortcut.tsx create mode 100644 apps/blocks/src/blocks/command-palette/multi-step/multi-step-view.test.tsx create mode 100644 apps/blocks/src/blocks/command-palette/multi-step/multi-step-view.tsx create mode 100644 apps/blocks/src/blocks/command-palette/multi-step/step-indicator.tsx create mode 100644 apps/blocks/src/components/application-block-showcase/application-block-docs-page.tsx create mode 100644 apps/blocks/src/components/application-block-showcase/application-block-showcase-canvas.tsx create mode 100644 apps/blocks/src/components/application-block-showcase/application-block-showcase-preview.tsx create mode 100644 apps/blocks/src/components/command-palette-showcase/command-palette-demo.tsx create mode 100644 apps/blocks/src/components/source-block-showcase/source-block-docs-page.tsx create mode 100644 apps/blocks/src/lib/application-blocks.ts create mode 100644 apps/blocks/src/lib/source-blocks.ts create mode 100644 apps/registry/scripts/build-source-registry.ts create mode 100644 packages/command-palette/CHANGELOG.md create mode 100644 packages/command-palette/LICENSE create mode 100644 packages/command-palette/README.md create mode 100644 packages/command-palette/docs/SPEC.md create mode 100644 packages/command-palette/package.json create mode 100644 packages/command-palette/src/__tests__/background-tasks.test.ts create mode 100644 packages/command-palette/src/__tests__/execution.test.ts create mode 100644 packages/command-palette/src/__tests__/global-shortcuts.test.ts create mode 100644 packages/command-palette/src/__tests__/keybinding.test.ts create mode 100644 packages/command-palette/src/__tests__/multi-step-builder.test.ts create mode 100644 packages/command-palette/src/__tests__/multi-step-reducer.test.ts create mode 100644 packages/command-palette/src/__tests__/registry.test.ts create mode 100644 packages/command-palette/src/background/index.ts create mode 100644 packages/command-palette/src/background/types.ts create mode 100644 packages/command-palette/src/background/use-background-tasks.ts create mode 100644 packages/command-palette/src/hooks.ts create mode 100644 packages/command-palette/src/index.ts create mode 100644 packages/command-palette/src/keybinding.ts create mode 100644 packages/command-palette/src/machine/use-machine.ts create mode 100644 packages/command-palette/src/multi-step/builder.ts create mode 100644 packages/command-palette/src/multi-step/index.ts create mode 100644 packages/command-palette/src/multi-step/machine.ts create mode 100644 packages/command-palette/src/multi-step/reducer.ts create mode 100644 packages/command-palette/src/multi-step/state.ts create mode 100644 packages/command-palette/src/multi-step/types.ts create mode 100644 packages/command-palette/src/multi-step/use-multi-step-execution.ts create mode 100644 packages/command-palette/src/registry.ts create mode 100644 packages/command-palette/src/types.ts create mode 100644 packages/command-palette/src/use-global-shortcuts.ts create mode 100644 packages/command-palette/tsconfig.json create mode 100644 packages/command-palette/tsup.config.ts create mode 100644 packages/command-palette/vitest.config.ts delete mode 100644 packages/schema-builder/src/compat/modules-client.ts delete mode 100644 packages/schema-builder/src/schema/schema-builder-core/lib/data/error-handler.ts delete mode 100644 packages/schema-builder/src/schema/schema-builder/schema-builder-block.tsx create mode 100644 packages/sheets/registry.json create mode 100644 packages/sheets/scripts/build-registry.ts create mode 100644 packages/sheets/src/hooks/query-keys.test.ts create mode 100644 packages/ui/src/components/org-chart/index.ts create mode 100644 packages/ui/src/components/org-chart/layout.ts create mode 100644 packages/ui/src/components/org-chart/org-chart-context.tsx create mode 100644 packages/ui/src/components/org-chart/org-chart-edge.tsx create mode 100644 packages/ui/src/components/org-chart/org-chart-empty.tsx create mode 100644 packages/ui/src/components/org-chart/org-chart-node.tsx create mode 100644 packages/ui/src/components/org-chart/org-chart-utils.ts create mode 100644 packages/ui/src/components/org-chart/org-chart.tsx create mode 100644 packages/ui/src/components/org-chart/org-chart.types.ts create mode 100644 packages/ui/src/components/storage/bucket-config-sheet.tsx create mode 100644 packages/ui/src/components/storage/bucket-rail.tsx create mode 100644 packages/ui/src/components/storage/file-type-icon.tsx create mode 100644 packages/ui/src/components/storage/index.ts create mode 100644 packages/ui/src/components/storage/object-detail-sheet.tsx create mode 100644 packages/ui/src/components/storage/object-table.tsx create mode 100644 packages/ui/src/components/storage/object-toolbar.tsx create mode 100644 packages/ui/src/components/storage/storage-breadcrumb.tsx create mode 100644 packages/ui/src/components/storage/storage-browser.tsx create mode 100644 packages/ui/src/components/storage/storage-empty-state.tsx create mode 100644 packages/ui/src/components/storage/types.ts create mode 100644 packages/ui/src/components/storage/upload-dropzone.tsx create mode 100644 packages/ui/src/components/storage/utils.ts create mode 100644 packages/ui/src/components/storage/visibility-badge.tsx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 969b7c6..09a102d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: - '@constructive-io/ui@*' - '@constructive-io/data@*' - '@constructive-io/sheets@*' + - '@constructive-io/command-palette@*' - '@constructive-io/schema-builder@*' pull_request: branches: [main] @@ -44,6 +45,11 @@ jobs: - name: Build shadcn registry run: pnpm build:registry + - name: Verify migration registry installs + env: + SMOKE_CASE: org-chart,storage-browser,sheets,schema-builder,command-palette + run: pnpm --filter @constructive-io/registry smoke:install + packages: runs-on: ubuntu-latest timeout-minutes: 30 diff --git a/AGENTS.md b/AGENTS.md index 199bc89..a6cae99 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,17 +2,19 @@ This public monorepo owns the Constructive Blocks documentation, the `@constructive` shadcn registry, and the `@constructive-io/ui`, -`@constructive-io/data`, and `@constructive-io/sheets` packages. The local -schema-builder package remains in the workspace but is not part of the public -registry. +`@constructive-io/data`, `@constructive-io/sheets`, +`@constructive-io/command-palette`, and `@constructive-io/schema-builder` +packages. Schema Builder is also distributed as editable source through the +public registry and binds to each host through an explicit adapter. ## Invariants - Canonical public source lives in `apps/blocks`, `packages/ui`, - `packages/data`, and `packages/sheets`; do not edit generated registry output. + `packages/data`, `packages/sheets`, `packages/command-palette`, and + `packages/schema-builder`; do not edit generated registry output. - Keep the registry collision-free and `@constructive`-namespaced. -- Preserve the seven feature-pack manifests and four experimental preset - profiles installed under `.constructive/feature-packs`. +- Preserve the reviewed feature-pack manifests and preset profiles installed + under `.constructive/feature-packs`. - Never auto-discover or mutate sibling repositories from flow tooling. - Keep normal CI independent of generated SDKs and live endpoints. - Never add automated npm publishing; publish verified tarballs manually. diff --git a/CLAUDE.md b/CLAUDE.md index 2088166..3f10618 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,14 +2,16 @@ This public monorepo owns the Constructive Blocks documentation, the `@constructive` shadcn registry, and the `@constructive-io/ui`, -`@constructive-io/data`, and `@constructive-io/sheets` packages. The local -schema-builder package remains available for development but is delisted from -the public registry. +`@constructive-io/data`, `@constructive-io/sheets`, +`@constructive-io/command-palette`, and `@constructive-io/schema-builder` +packages. Schema Builder is also distributed as editable source through the +public registry and binds to each host through an explicit adapter. ## Invariants -- `apps/blocks`, `packages/ui`, `packages/data`, and `packages/sheets` are - canonical public source trees; generated registry output is never edited. +- `apps/blocks`, `packages/ui`, `packages/data`, `packages/sheets`, + `packages/command-palette`, and `packages/schema-builder` are canonical public + source trees; generated registry output is never edited. - The combined registry must remain collision-free and every install command must use the `@constructive` namespace. - Feature-pack and preset manifests installed under diff --git a/README.md b/README.md index 6385524..09ce06d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ component documentation, and published React packages. - `packages/ui` — the `@constructive-io/ui` npm package and UI registry source. - `packages/data` — runtime GraphQL generation and the strict July 2026 `_meta` contract. - `packages/sheets` — the metadata-driven application CRUD grid. -- `packages/schema-builder` — retained platform/operator tooling that is delisted from the public registry. +- `packages/command-palette` — the headless command registry, shortcuts, workflows, and background-task engine. +- `packages/schema-builder` — the source-installable Schema Builder, its host-adapter contract, and its npm package. The documentation site is published at . Registry JSON is served from @@ -43,6 +44,7 @@ Then choose the smallest surface that owns the workflow you need: | Backend-aligned official composition | `preset-auth-hardened`, `preset-b2b-storage`, or `preset-full` | | Custom Console Kit composition | `console-kit-core` plus selected `console-module-*` items | | Provider-neutral view with host-owned data and actions | `feature-pack-*` | +| Application command center with editable presentation | `command-palette` | | Reusable primitive | npm subpath or namespaced primitive registry item | ```bash diff --git a/apps/blocks/package.json b/apps/blocks/package.json index 607657b..2bc64a5 100644 --- a/apps/blocks/package.json +++ b/apps/blocks/package.json @@ -35,6 +35,7 @@ "@ai-sdk/openai-compatible": "^3.0.9", "@ai-sdk/react": "^4.0.27", "@base-ui/react": "^1.0.0", + "@constructive-io/command-palette": "workspace:*", "@constructive-io/data": "workspace:*", "@constructive-io/graphql-types": "^3.4.3", "@constructive-io/sheets": "workspace:*", diff --git a/apps/blocks/registry.json b/apps/blocks/registry.json index dc63f35..c3a6225 100644 --- a/apps/blocks/registry.json +++ b/apps/blocks/registry.json @@ -258,6 +258,71 @@ } ] }, + { + "name": "command-palette", + "type": "registry:block", + "title": "Command Palette", + "description": "Framework-neutral command discovery with page-scoped registration, structured shortcuts, multi-step flows, and cancellable background tasks.", + "categories": [ + "blocks", + "application-shell" + ], + "docs": "## Usage\n\nCreate one registry for the application, adapt navigation at the host boundary, and render the palette near the root layout. Pages can register commands while mounted with `usePageCommands`.\n\n```tsx\nimport { createCommandRegistry, kbd } from '@constructive-io/command-palette';\nimport { CommandPalette } from '@/blocks/command-palette/command-palette';\n\nconst registry = createCommandRegistry({\n groups: [{ id: 'navigation', label: 'Navigation', priority: 1 }],\n commands: [{\n id: 'settings',\n label: 'Open settings',\n type: 'navigation',\n group: 'navigation',\n href: '/settings',\n shortcut: kbd(',', 'mod')\n }]\n});\n\n router.push(href)} />;\n```\n\nThe installed block owns presentation and keyboard interaction. The headless package owns command registration, execution, multi-step state, and background-task lifecycle. Route authorization, action permissions, errors, and business workflows remain the host application's responsibility.", + "dependencies": [ + "@constructive-io/command-palette@^0.3.0", + "lucide-react", + "motion" + ], + "registryDependencies": [ + "badge", + "button", + "cn", + "command", + "separator" + ], + "files": [ + { + "path": "registry/constructive/blocks/command-palette/command-palette.tsx", + "target": "src/blocks/command-palette/command-palette.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/kbd-shortcut.tsx", + "target": "src/blocks/command-palette/kbd-shortcut.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/multi-step/multi-step-view.tsx", + "target": "src/blocks/command-palette/multi-step/multi-step-view.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/multi-step/step-indicator.tsx", + "target": "src/blocks/command-palette/multi-step/step-indicator.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/background/background-task-stack.tsx", + "target": "src/blocks/command-palette/background/background-task-stack.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/background/inline-task-bar.tsx", + "target": "src/blocks/command-palette/background/inline-task-bar.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/background/task-card.tsx", + "target": "src/blocks/command-palette/background/task-card.tsx", + "type": "registry:component" + }, + { + "path": "registry/constructive/blocks/command-palette/background/task-icons.tsx", + "target": "src/blocks/command-palette/background/task-icons.tsx", + "type": "registry:component" + } + ] + }, { "name": "pack-foundation", "type": "registry:lib", @@ -371,7 +436,7 @@ "description": "The leaf-independent Console Kit shell, runtime, discovery, and single modular Zustand store.", "docs": "`console-kit-core` installed the shell, runtime, semantic routing, callback boundary, and one per-instance modular Zustand store. Core intentionally includes no feature view, so add selected `console-module-*` items.\n\nDegraded states: explicit endpoint, current `_meta`, introspection, capability, and adapter evidence fail closed independently; installation never grants authority.\n\nGuide: https://constructive-io.github.io/blocks/blocks/console-kit/", "dependencies": [ - "@constructive-io/data", + "@constructive-io/data@^0.4.0", "@tanstack/react-query", "graphql", "lucide-react", @@ -501,15 +566,12 @@ "description": "A current-_meta-only application data explorer with application-table filtering and spreadsheet CRUD.", "docs": "`feature-pack-data` installed a provider-neutral view and `.constructive/feature-packs/data.json`. Import from `@/blocks/feature-packs/data/data-feature-pack`. The host must supply the view resource, policy, and actions; add `console-module-data` when Console Kit should own discovery and Constructive integration.\n\nDegraded states: The host owns Sheets state and endpoint binding; incompatible metadata stays explicit, and PostgreSQL privileges and RLS decide every query and mutation.\n\nGuide: https://constructive-io.github.io/blocks/blocks/features/data/", "dependencies": [ - "@constructive-io/data", - "@constructive-io/sheets", + "@constructive-io/data@^0.4.0", "lucide-react" ], - "css": { - "@import '@constructive-io/sheets/styles.css'": {} - }, "registryDependencies": [ - "pack-foundation" + "pack-foundation", + "sheets" ], "files": [ { diff --git a/apps/blocks/src/app/blocks/command-palette/page.tsx b/apps/blocks/src/app/blocks/command-palette/page.tsx new file mode 100644 index 0000000..e5957c9 --- /dev/null +++ b/apps/blocks/src/app/blocks/command-palette/page.tsx @@ -0,0 +1,138 @@ +import type { Metadata } from 'next'; + +import { CommandPaletteDemo } from '@/components/command-palette-showcase/command-palette-demo'; +import { CodeBlock } from '@/components/docs/code-block'; +import { DocSection } from '@/components/docs/doc-section'; +import { registryAdd } from '@/lib/install-mode'; +import { OG_IMAGE, withBase } from '@/lib/site'; + +const TITLE = 'Command Palette'; +const DESCRIPTION = + 'An installable command center with page-scoped registration, structured shortcuts, multi-step flows, and cancellable background tasks.'; + +const BASIC_EXAMPLE = `'use client'; + +import { + createCommandRegistry, + kbd +} from '@constructive-io/command-palette'; +import { useRouter } from 'next/navigation'; +import { CommandPalette } from '@/blocks/command-palette/command-palette'; + +const registry = createCommandRegistry({ + groups: [{ id: 'navigation', label: 'Navigation', priority: 1 }], + commands: [{ + id: 'settings', + label: 'Open settings', + type: 'navigation', + group: 'navigation', + href: '/settings', + shortcut: kbd(',', 'mod') + }] +}); + +export function ApplicationCommands() { + const router = useRouter(); + return ( + router.push(href)} + /> + ); +}`; + +const PAGE_COMMANDS_EXAMPLE = `const pageCommands = useMemo(() => [{ + id: 'create-record', + label: 'Create record', + type: 'action' as const, + group: 'actions', + onSelect: openCreateRecord +}], [openCreateRecord]); + +usePageCommands(registry, pageCommands);`; + +export default function CommandPalettePage() { + return ( +
+
+

Application block

+

+ {TITLE} +

+

+ {DESCRIPTION} +

+
+ + + + + {registryAdd('command-palette')} + + + + + {BASIC_EXAMPLE} + + + + + + {PAGE_COMMANDS_EXAMPLE} + + + + +
+ {[ + { + title: 'Headless engine', + body: 'Registry snapshots, shortcuts, execution, multi-step state, and background-task lifecycle.' + }, + { + title: 'Installed block', + body: 'Command dialog, search results, shortcut hints, step navigation, and task feedback.' + }, + { + title: 'Host application', + body: 'Routes, policies, business actions, mutations, errors, and telemetry.' + } + ].map((item) => ( +
+

{item.title}

+

{item.body}

+
+ ))} +
+
+
+ ); +} + +export const metadata: Metadata = { + title: TITLE, + description: DESCRIPTION, + alternates: { canonical: withBase('/blocks/command-palette') }, + openGraph: { + title: TITLE, + description: DESCRIPTION, + url: withBase('/blocks/command-palette'), + images: [OG_IMAGE] + } +}; diff --git a/apps/blocks/src/app/blocks/org-chart/page.tsx b/apps/blocks/src/app/blocks/org-chart/page.tsx new file mode 100644 index 0000000..ec1978e --- /dev/null +++ b/apps/blocks/src/app/blocks/org-chart/page.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from 'next'; + +import { ApplicationBlockDocsPage } from '@/components/application-block-showcase/application-block-docs-page'; +import { + APPLICATION_BLOCKS, + getApplicationBlock, +} from '@/lib/application-blocks'; +import { OG_IMAGE, withBase } from '@/lib/site'; + +const block = getApplicationBlock('org-chart')!; + +export default function OrgChartPage() { + return ( + + ); +} + +export const metadata: Metadata = { + title: block.title, + description: block.description, + alternates: { canonical: withBase('/blocks/org-chart') }, + openGraph: { + title: block.title, + description: block.description, + url: withBase('/blocks/org-chart'), + images: [OG_IMAGE], + }, +}; diff --git a/apps/blocks/src/app/blocks/org-chart/preview/page.tsx b/apps/blocks/src/app/blocks/org-chart/preview/page.tsx new file mode 100644 index 0000000..7b65866 --- /dev/null +++ b/apps/blocks/src/app/blocks/org-chart/preview/page.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next'; + +import { ApplicationBlockShowcaseCanvas } from '@/components/application-block-showcase/application-block-showcase-canvas'; +import { withBase } from '@/lib/site'; + +export default function OrgChartPreviewPage() { + return ( + <> +

Org Chart live preview

+ + + ); +} + +export const metadata: Metadata = { + alternates: { canonical: withBase('/blocks/org-chart') }, + robots: { follow: false, index: false }, +}; diff --git a/apps/blocks/src/app/blocks/page.tsx b/apps/blocks/src/app/blocks/page.tsx index 40bd49b..a3ff93f 100644 --- a/apps/blocks/src/app/blocks/page.tsx +++ b/apps/blocks/src/app/blocks/page.tsx @@ -2,13 +2,46 @@ import type { Metadata } from 'next'; import Link from 'next/link'; import { InstallToggle } from '@/components/docs/install-toggle'; +import { APPLICATION_BLOCKS } from '@/lib/application-blocks'; import { BASE_PRIMITIVES } from '@/lib/base-primitives'; import { packageCommands, registryCommands } from '@/lib/install-mode'; import { OG_IMAGE, withBase } from '@/lib/site'; +import { SOURCE_BLOCKS } from '@/lib/source-blocks'; const TITLE = 'Setup'; const DESCRIPTION = 'Choose npm package distribution or source installation through the shadcn CLI.'; +const APPLICATION_CATALOG = [ + { + href: '/blocks/features', + title: 'Feature packs', + description: + 'Data, authentication, users, organizations, storage, billing, and notifications.', + }, + ...APPLICATION_BLOCKS.map((block) => ({ + href: `/blocks/${block.name}`, + title: block.title, + description: block.description, + })), + ...SOURCE_BLOCKS.map((block) => ({ + href: `/blocks/${block.name}`, + title: block.title, + description: block.description, + })), + { + href: '/blocks/command-palette', + title: 'Command Palette', + description: + 'Search, page-scoped commands, structured shortcuts, guided flows, and background tasks.', + }, + { + href: '/blocks/console-kit', + title: 'Console Kit for Next.js', + description: + 'A full-page console driven by injected endpoints, session state, adapters, and versioned _meta.', + }, +] as const; + export default function SetupPage() { return (
@@ -41,26 +74,13 @@ export default function SetupPage() { Application blocks

- Start with a capability-aligned feature pack or install the full - Next.js console with its route-neutral app shell and dynamic data - explorer. + Start with a capability-aligned feature pack, add a composed + workflow block, or install the full Next.js console with its + route-neutral app shell and dynamic data explorer.

    - {[ - { - href: '/blocks/features', - title: 'Feature packs', - description: - 'Data, authentication, users, organizations, storage, billing, and notifications.' - }, - { - href: '/blocks/console-kit', - title: 'Console Kit for Next.js', - description: - 'A full-page console driven by injected endpoints, session state, adapters, and versioned _meta.' - } - ].map((item) => ( + {APPLICATION_CATALOG.map((item) => (
  • ; +} + +export const metadata: Metadata = { + title: block.title, + description: block.description, + alternates: { canonical: withBase('/blocks/schema-builder') }, + openGraph: { + title: block.title, + description: block.description, + url: withBase('/blocks/schema-builder'), + images: [OG_IMAGE], + }, +}; diff --git a/apps/blocks/src/app/blocks/sheets/page.tsx b/apps/blocks/src/app/blocks/sheets/page.tsx new file mode 100644 index 0000000..de9252f --- /dev/null +++ b/apps/blocks/src/app/blocks/sheets/page.tsx @@ -0,0 +1,23 @@ +import type { Metadata } from 'next'; + +import { SourceBlockDocsPage } from '@/components/source-block-showcase/source-block-docs-page'; +import { OG_IMAGE, withBase } from '@/lib/site'; +import { getSourceBlock } from '@/lib/source-blocks'; + +const block = getSourceBlock('sheets')!; + +export default function SheetsPage() { + return ; +} + +export const metadata: Metadata = { + title: block.title, + description: block.description, + alternates: { canonical: withBase('/blocks/sheets') }, + openGraph: { + title: block.title, + description: block.description, + url: withBase('/blocks/sheets'), + images: [OG_IMAGE], + }, +}; diff --git a/apps/blocks/src/app/blocks/storage-browser/page.tsx b/apps/blocks/src/app/blocks/storage-browser/page.tsx new file mode 100644 index 0000000..d07a574 --- /dev/null +++ b/apps/blocks/src/app/blocks/storage-browser/page.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from 'next'; + +import { ApplicationBlockDocsPage } from '@/components/application-block-showcase/application-block-docs-page'; +import { + APPLICATION_BLOCKS, + getApplicationBlock, +} from '@/lib/application-blocks'; +import { OG_IMAGE, withBase } from '@/lib/site'; + +const block = getApplicationBlock('storage-browser')!; + +export default function StorageBrowserPage() { + return ( + + ); +} + +export const metadata: Metadata = { + title: block.title, + description: block.description, + alternates: { canonical: withBase('/blocks/storage-browser') }, + openGraph: { + title: block.title, + description: block.description, + url: withBase('/blocks/storage-browser'), + images: [OG_IMAGE], + }, +}; diff --git a/apps/blocks/src/app/blocks/storage-browser/preview/page.tsx b/apps/blocks/src/app/blocks/storage-browser/preview/page.tsx new file mode 100644 index 0000000..8240806 --- /dev/null +++ b/apps/blocks/src/app/blocks/storage-browser/preview/page.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next'; + +import { ApplicationBlockShowcaseCanvas } from '@/components/application-block-showcase/application-block-showcase-canvas'; +import { withBase } from '@/lib/site'; + +export default function StorageBrowserPreviewPage() { + return ( + <> +

    Storage Browser live preview

    + + + ); +} + +export const metadata: Metadata = { + alternates: { canonical: withBase('/blocks/storage-browser') }, + robots: { follow: false, index: false }, +}; diff --git a/apps/blocks/src/app/sitemap.test.ts b/apps/blocks/src/app/sitemap.test.ts index a4a9ca1..35c4347 100644 --- a/apps/blocks/src/app/sitemap.test.ts +++ b/apps/blocks/src/app/sitemap.test.ts @@ -1,22 +1,34 @@ import { describe, expect, it } from 'vitest'; +import { APPLICATION_BLOCKS } from '@/lib/application-blocks'; import { BASE_PRIMITIVES } from '@/lib/base-primitives'; import { BILLING_BLOCKS } from '@/lib/billing-blocks'; import { FEATURE_PACK_DOCS } from '@/lib/feature-packs'; +import { SOURCE_BLOCKS } from '@/lib/source-blocks'; import sitemap from './sitemap'; describe('sitemap', () => { - it('contains foundations, application docs, seven feature packs, 29 primitives, and the complete billing catalog', () => { + it('contains foundations, application blocks, seven feature packs, 29 primitives, and the complete billing catalog', () => { const entries = sitemap(); expect(BASE_PRIMITIVES).toHaveLength(29); - expect(entries).toHaveLength(BASE_PRIMITIVES.length + FEATURE_PACK_DOCS.length + BILLING_BLOCKS.length + 6); + expect(entries).toHaveLength( + BASE_PRIMITIVES.length + + FEATURE_PACK_DOCS.length + + BILLING_BLOCKS.length + + APPLICATION_BLOCKS.length + + SOURCE_BLOCKS.length + + 7, + ); expect(entries.map(({ url }) => url)).toEqual([ 'http://localhost:3005/', 'http://localhost:3005/blocks', 'http://localhost:3005/blocks/styling', 'http://localhost:3005/blocks/features', ...FEATURE_PACK_DOCS.map(({ id }) => `http://localhost:3005/blocks/features/${id}`), + 'http://localhost:3005/blocks/command-palette', + ...SOURCE_BLOCKS.map(({ name }) => `http://localhost:3005/blocks/${name}`), + ...APPLICATION_BLOCKS.map(({ name }) => `http://localhost:3005/blocks/${name}`), 'http://localhost:3005/blocks/console-kit', ...BASE_PRIMITIVES.map(({ name }) => `http://localhost:3005/blocks/ui/${name}`), 'http://localhost:3005/blocks/billing', diff --git a/apps/blocks/src/app/sitemap.ts b/apps/blocks/src/app/sitemap.ts index c918951..21bcb72 100644 --- a/apps/blocks/src/app/sitemap.ts +++ b/apps/blocks/src/app/sitemap.ts @@ -1,9 +1,11 @@ import type { MetadataRoute } from 'next'; +import { APPLICATION_BLOCKS } from '@/lib/application-blocks'; import { BASE_PRIMITIVES } from '@/lib/base-primitives'; import { BILLING_BLOCKS } from '@/lib/billing-blocks'; import { FEATURE_PACK_DOCS } from '@/lib/feature-packs'; import { BASE_PATH, SITE_ORIGIN, withBase } from '@/lib/site'; +import { SOURCE_BLOCKS } from '@/lib/source-blocks'; export const dynamic = 'force-static'; @@ -14,6 +16,9 @@ export default function sitemap(): MetadataRoute.Sitemap { '/blocks/styling', '/blocks/features', ...FEATURE_PACK_DOCS.map(({ id }) => `/blocks/features/${id}`), + '/blocks/command-palette', + ...SOURCE_BLOCKS.map(({ name }) => `/blocks/${name}`), + ...APPLICATION_BLOCKS.map(({ name }) => `/blocks/${name}`), '/blocks/console-kit', ...BASE_PRIMITIVES.map(({ name }) => `/blocks/ui/${name}`), '/blocks/billing', @@ -30,6 +35,8 @@ export default function sitemap(): MetadataRoute.Sitemap { : path === '/blocks' || path === '/blocks/styling' || path === '/blocks/features' || + path === '/blocks/command-palette' || + SOURCE_BLOCKS.some(({ name }) => path === `/blocks/${name}`) || path === '/blocks/console-kit' || path === '/blocks/billing' ? 0.9 diff --git a/apps/blocks/src/blocks/command-palette/background/background-task-stack.tsx b/apps/blocks/src/blocks/command-palette/background/background-task-stack.tsx new file mode 100644 index 0000000..930b82e --- /dev/null +++ b/apps/blocks/src/blocks/command-palette/background/background-task-stack.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { AnimatePresence } from 'motion/react'; +import { TaskCard } from './task-card'; +import type { BackgroundTask } from '@constructive-io/command-palette'; + +const MAX_VISIBLE = 5; + +export interface BackgroundTaskStackProps { + tasks: BackgroundTask[]; + onCancel: (taskId: string) => void; + onDismiss: (taskId: string) => void; + /** Override default positioning */ + className?: string; +} + +export function BackgroundTaskStack({ + tasks, + onCancel, + onDismiss, + className, +}: BackgroundTaskStackProps) { + if (tasks.length === 0) return null; + + const visible = tasks.slice(0, MAX_VISIBLE); + const overflow = tasks.length - MAX_VISIBLE; + + return ( +
    + {overflow > 0 && ( +
    + +{overflow} more task{overflow > 1 ? 's' : ''} +
    + )} + + {visible.map((task) => ( + + ))} + +
    + ); +} diff --git a/apps/blocks/src/blocks/command-palette/background/inline-task-bar.tsx b/apps/blocks/src/blocks/command-palette/background/inline-task-bar.tsx new file mode 100644 index 0000000..1eabf52 --- /dev/null +++ b/apps/blocks/src/blocks/command-palette/background/inline-task-bar.tsx @@ -0,0 +1,153 @@ +'use client'; + +import { useState } from 'react'; +import { AnimatePresence, motion } from 'motion/react'; +import type { BackgroundTask } from '@constructive-io/command-palette'; +import { ChevronDownIcon } from 'lucide-react'; +import { Badge } from '@constructive-io/ui/badge'; +import { Button } from '@constructive-io/ui/button'; +import { cn } from '@/lib/utils'; +import { StatusIcon, ElapsedTime } from './task-icons'; + +export interface InlineTaskBarProps { + tasks: BackgroundTask[]; + onCancel: (taskId: string) => void; + onDismiss: (taskId: string) => void; +} + +const MAX_INLINE = 3; + +function InlineTaskRow({ + task, + onCancel, + onDismiss, +}: { + task: BackgroundTask; + onCancel: (id: string) => void; + onDismiss: (id: string) => void; +}) { + const [expanded, setExpanded] = useState(false); + const isRunning = task.status === 'running'; + + return ( + +
    + + +
    + + {/* Expanded error / details */} + + {expanded && ( + +
    +
    + Command: {task.commandId} +
    +
    Started: {new Date(task.startedAt).toLocaleTimeString()}
    + {task.completedAt && ( +
    Completed: {new Date(task.completedAt).toLocaleTimeString()}
    + )} + {task.error && ( +
    {task.error.message}
    + )} +
    +
    + )} +
    +
    + ); +} + +export function InlineTaskBar({ tasks, onCancel, onDismiss }: InlineTaskBarProps) { + if (tasks.length === 0) return null; + + const visible = tasks.slice(0, MAX_INLINE); + const overflow = tasks.length - MAX_INLINE; + + const runningCount = tasks.filter((t) => t.status === 'running').length; + const errorCount = tasks.filter((t) => t.status === 'error').length; + + return ( +
    + {/* Summary header */} +
    + + Tasks + + {runningCount > 0 && ( + + + {runningCount} + + )} + {errorCount > 0 && ( + + {errorCount} failed + + )} + {tasks.length > 0 && ( + + {tasks.length} total + + )} +
    + + {/* Task rows */} +
    + + {visible.map((task) => ( + + ))} + +
    + + {overflow > 0 && ( +
    + +{overflow} more task{overflow > 1 ? 's' : ''} +
    + )} +
    + ); +} diff --git a/apps/blocks/src/blocks/command-palette/background/task-card.tsx b/apps/blocks/src/blocks/command-palette/background/task-card.tsx new file mode 100644 index 0000000..545b593 --- /dev/null +++ b/apps/blocks/src/blocks/command-palette/background/task-card.tsx @@ -0,0 +1,89 @@ +'use client'; + +import { useState } from 'react'; +import { motion } from 'motion/react'; +import type { BackgroundTask } from '@constructive-io/command-palette'; +import { cn } from '@/lib/utils'; +import { Button } from '@constructive-io/ui/button'; +import { Separator } from '@constructive-io/ui/separator'; +import { StatusIcon, ElapsedTime } from './task-icons'; + +export interface TaskCardProps { + task: BackgroundTask; + onCancel: (taskId: string) => void; + onDismiss: (taskId: string) => void; +} + +const borderByStatus: Record = { + running: 'border-info/30', + success: 'border-success/30', + error: 'border-destructive/30', + cancelled: 'border-border' +}; + +export function TaskCard({ task, onCancel, onDismiss }: TaskCardProps) { + const [expanded, setExpanded] = useState(false); + const isRunning = task.status === 'running'; + + return ( + +
    + + +
    + + {/* Expanded details */} + {expanded && ( + + +
    +
    Command: {task.commandId}
    +
    Started: {new Date(task.startedAt).toLocaleTimeString()}
    + {task.completedAt && ( +
    + Completed: {new Date(task.completedAt).toLocaleTimeString()} +
    + )} + {task.error && ( +
    {task.error.message}
    + )} +
    +
    + )} +
    + ); +} diff --git a/apps/blocks/src/blocks/command-palette/background/task-icons.tsx b/apps/blocks/src/blocks/command-palette/background/task-icons.tsx new file mode 100644 index 0000000..889f021 --- /dev/null +++ b/apps/blocks/src/blocks/command-palette/background/task-icons.tsx @@ -0,0 +1,60 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import type { BackgroundTaskStatus } from '@constructive-io/command-palette'; +import { + BanIcon, + CircleCheckIcon, + CircleXIcon, + LoaderCircleIcon, + type LucideIcon +} from 'lucide-react'; +import { cn } from '@/lib/utils'; + +const statusPresentation: Record< + BackgroundTaskStatus, + { icon: LucideIcon; className: string } +> = { + running: { icon: LoaderCircleIcon, className: 'animate-spin text-info' }, + success: { icon: CircleCheckIcon, className: 'text-success' }, + error: { icon: CircleXIcon, className: 'text-destructive' }, + cancelled: { icon: BanIcon, className: 'text-muted-foreground' } +}; + +export function StatusIcon({ + status, + className +}: { + status: BackgroundTaskStatus; + className?: string; +}) { + const presentation = statusPresentation[status]; + const Icon = presentation.icon; + return ( +