Packages

Shared workspace packages for UI components, linting, TypeScript config, and feature management

The packages/ directory contains shared workspace packages. Some are private workspace-only packages (@workspace/*), others are published to npm under the @celestia-project org.

@celestia-project/ui

npm

A shadcn-style React component library built on Base UI and Tailwind CSS v4, with 150+ components.

Exports:

Import pathResolves to
@celestia-project/uiBarrel export of every component (tree-shaken)
@celestia-project/ui/globals.cssGlobal styles and CSS variables
@celestia-project/ui/primitive/*Primitive components (button, card, input, label, etc.)
@celestia-project/ui/composite/*Composite components (field, sidebar, data-table, form, etc.)
@celestia-project/ui/aiBarrel export of all AI chat & agent components
@celestia-project/ui/components/ai/*AI chat & agent components
@celestia-project/ui/hooks/*Shared React hooks
@celestia-project/ui/lib/*Utilities (cn helper)

Usage:

tsx
import { Button } from "@celestia-project/ui"import { Button } from "@celestia-project/ui/primitive/button"import { cn } from "@celestia-project/ui/lib/utils"

Key dependencies: @base-ui/react, class-variance-authority, tailwind-merge, next-themes, @phosphor-icons/react

Using it outside this monorepo

The package is published to npm and can be installed in any React 19 + Tailwind CSS v4 project:

bash
pnpm add @celestia-project/ui

Integration Option A: Quickstart (All-in-one)

Import the global stylesheet in your app entry point (app/layout.tsx or globals.css):

tsx
import "@celestia-project/ui/globals.css";

Integration Option B: Existing Tailwind v4 App (@source directive)

If your app already has an existing Tailwind v4 setup and custom theme variables, add the @source directive in your main CSS file so Tailwind scans the package without double-importing or overriding styles:

css
@import "tailwindcss";@source "../../node_modules/@celestia-project/ui";@source "../**";
@custom-variant dark (&:is(.dark *));

See the Components docs and the npm package for details.


@celestia-project/mobile

Native iOS and Android UI components built on @expo/ui — real SwiftUI on iOS and Jetpack Compose on Android, with no web-shim layer in between. The package reuses the same primitive / composite / layout taxonomy and the same design tokens as @celestia-project/ui, but it is not a port: mobile-only affordances (haptics, safe areas) have no web counterpart. For slide-up panels, use MobileBottomSheet on native and BottomSheet on web.

Publishing & Workspace Usage

@celestia-project/mobile is part of the npm publish set: scripts/publish.sh supports publishing @celestia-project/ui, @celestia-project/create, and @celestia-project/mobile. Inside the workspace, it is consumed from source through the workspace protocol ("@celestia-project/mobile": "workspace:*"). Publish it standalone with pnpm publish:mobile, or all packages together with pnpm publish:packages.

Exports:

Import pathResolves to
@celestia-project/mobileBarrel export of every component, hook and token (tree-shaken)
@celestia-project/mobile/tokensuseMobileTheme(), colour ramps, spacing scale, metrics
@celestia-project/mobile/hostMobileHost — theme + safe-area root
@celestia-project/mobile/primitiveAll 20 generic components (barrel)
@celestia-project/mobile/primitive/*One module — e.g. primitive/button
@celestia-project/mobile/compositeAll 13 opinionated assemblies (barrel)
@celestia-project/mobile/composite/*One module — e.g. composite/tab-bar
@celestia-project/mobile/layoutAll 10 shells and screens (barrel)
@celestia-project/mobile/layout/*One module — e.g. layout/sign-in-screen

Usage:

tsx
import { MobileButton, MobileScreen } from "@celestia-project/mobile"import { MobileButton } from "@celestia-project/mobile/primitive/button"import { useMobileTheme } from "@celestia-project/mobile/tokens"

Key dependencies: @expo/ui, expo-haptics, react-native-safe-area-context

The package is consumed from source — its exports map points straight at .ts / .tsx, so there is no build step and no dist/. The consuming app's bundler (Metro, via Expo) compiles it. Every colour comes from useMobileTheme(); nothing is hardcoded, which is what lets MobileHost switch themes at runtime.

See the Mobile (Expo) docs for setup, the full component inventory, and the design rules.


@celestia-project/create

npm

The interactive CLI that scaffolds a new Celestia Starter project. Lives in packages/cli.

Usage:

bash
npx @celestia-project/create my-appnpx @celestia-project/create my-app --template web --features auth,dashboardnpx @celestia-project/create my-desktop --template desktop

Bin registered: create-celestia

Publishing:

bash
# From the monorepo root — publishes both @celestia-project/ui and @celestia-project/createpnpm publish:packages
# Dry run first (previews tarball contents without uploading)pnpm publish:dry-run

See the CLI docs for the full feature flag reference and local testing guide.


@workspace/eslint-config

Shared ESLint flat-config presets for consistent linting across the monorepo.

Exports:

Import pathPurpose
@workspace/eslint-config/baseBase JS/TS rules (strict, turbo, prettier compat)
@workspace/eslint-config/next-jsNext.js-specific rules (adds @next/eslint-plugin-next)
@workspace/eslint-config/react-internalReact library rules (hooks, jsx)

Usage in a consumer eslint.config.js:

js
import { config } from "@workspace/eslint-config/next-js"
export default config

@workspace/typescript-config

Reusable tsconfig profiles that all workspace projects extend.

FileUse case
base.jsonDefault — strict, ES2022, NodeNext resolution
nextjs.jsonNext.js apps — adds JSX, bundler resolution, plugins
react-library.jsonReact component libraries — JSX + DOM libs

Usage in a consumer tsconfig.json:

json
{  "extends": "@workspace/typescript-config/nextjs.json"}

@workspace/feature-manager

A CLI and runtime registry that discovers, installs, and tracks features defined by feature.json manifests under the project's packages/feature-manager/features/ directory.

CLI commands (via root scripts):

bash
pnpm add-feature <name>       # Install (or --force reinstall/upgrade) a featurepnpm remove-feature <name>    # Uninstall a feature, restoring shared filespnpm list-features            # List available and installed featurespnpm verify-features [name]   # Check installed features for drift or breakagepnpm prompt-feature <name>    # Generate the AI verification prompt

Common flags: --dry-run (plan only, writes nothing), --force (reinstall/upgrade, or override removal safety checks), --strict (treat warnings as errors), --json (machine-readable report), --quiet.

How add works:

  1. Reads packages/feature-manager/features/<name>/feature.json (legacy v1 manifests using files are normalized)
  2. Validates it — malformed fields and paths that escape the repository are rejected before anything is written
  3. Verifies requires against the tracker (root features.json)
  4. Plans every action, then snapshots each path it will touch
  5. Copies copies entries into the workspace — apps/api, apps/web, packages/*, root files
  6. Inserts insertions snippets into managed marker regions
  7. Applies jsonAppends to JSON arrays (e.g. the docs sidebar meta.json)
  8. Merges dependencies / devDependencies into each target package's package.json, reporting any version conflict instead of silently replacing it
  9. Records the feature, its files (with content hashes), backups and dependency changes in the tracker, then prints next steps (pnpm install, env vars, postInstall commands)

If any step fails, every touched path is rolled back — a failed install can never leave a half-applied repository behind.

Shared files: when two features provide the same path (for example the CMS media dialog, provided by cms, media-r2 and unsplash), the newest install wins and the previous content is backed up under .feature-manager/backups/. Removing a feature restores the previous owner's version rather than deleting the file. Files you edited after installing are kept unless --force is given.

Feature manifest schema (feature.json):

json
{  "name": "blog",  "version": "1.0.0",  "description": "Full CRUD blog over the typed RPC client",  "requires": ["auth", "dashboard"],  "copies": [    { "from": "api/src/features/blog/routes.ts", "to": "apps/api/src/features/blog/routes.ts" }  ],  "insertions": [    { "file": "apps/api/src/app.ts", "marker": "routes", "snippet": "snippets/api-routes.ts" }  ],  "jsonAppends": [    { "file": "apps/web/content/docs/meta.json", "path": "pages", "value": "blog" }  ],  "dependencies": { "apps/api": { "drizzle-orm": "^0.45.2" } },  "env": { "apps/api": ["DATABASE_URL=postgresql://postgres:postgres@localhost:5432/celestia"] },  "postInstall": ["pnpm --filter @workspace/db db:push"],  "notes": "Optional post-install instructions"}

Managed marker regions

Insertions target empty sentinel regions that already exist in shared files. Installing wraps the snippet in a per-feature block so it can later be replaced (idempotent reinstall) or removed cleanly:

ts
// feature-manager:<marker>:begin// feature-manager:<marker>:<feature>:begin...snippet...// feature-manager:<marker>:<feature>:end// feature-manager:<marker>:end

Available regions:

MarkerFileWhat features insert
importsapps/api/src/app.tsRoute module imports
routesapps/api/src/app.ts.route(...) mounts
auth-importsapps/api/src/auth.tsBetter Auth server-plugin imports
auth-pluginsapps/api/src/auth.tsPlugin entries in the plugins array
tablespackages/db/src/schema.tsDrizzle tables + relations
user-fieldspackages/db/src/schema.tsAdditional columns on the user table
importsapps/web/components/dashboard/sidebar.tsxIcon imports
navapps/web/components/dashboard/sidebar.tsxSidebar navigation items
nav-visibilityapps/web/components/dashboard/sidebar.tsxPer-item visibility filter inside the nav map
client-importsapps/web/lib/auth-client.tsBetter Auth client-plugin imports
client-pluginsapps/web/lib/auth-client.tsClient-plugin entries in the plugins array
cardsapps/web/content/docs/index.mdxLanding page <Card> links
featuresREADME.md"Installed Features" bullets

Marker ids are scoped per file, so imports in app.ts and imports in sidebar.tsx don't collide. Comment syntax follows the file type (// for TS/TSX, <!-- --> for .md, {/* */} for .mdx). Files that cannot carry comments (e.g. meta.json) use jsonAppends instead.

Authoring snippets:

  • Snippets are inserted verbatim — include their leading indentation in the snippet file itself (only the sentinel comments are auto-indented)
  • A single trailing newline is stripped before insertion

Programmatic exports:

Import pathPurpose
@workspace/feature-managerEverything below, re-exported from one entry point
@workspace/feature-manager/markersMarker engine: insertIntoRegion, removeFromRegion, hasRegion, listRegionFeatures, jsonAppend, jsonRemove
@workspace/feature-manager/manifestloadManifest, normalizeManifest, validateManifest, discoverFeatures
@workspace/feature-manager/installerplanInstall, executeInstall, installFeature
@workspace/feature-manager/removerplanRemove, executeRemove, removeFeature
@workspace/feature-manager/verifierverify — integrity report for installed features
@workspace/feature-manager/typesFeature, FeatureManifest, FeatureCopy, FeatureInsertion, FeatureTracker types
@workspace/feature-manager/registryRuntime feature registry

All of these take an explicit repo root, so they can be driven from scripts and tests rather than only from the CLI.