Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a180928
feat: add export entry points for npm package consumption
cevheri Mar 26, 2026
1ef919f
chore: update .gitignore to exclude files
cevheri Mar 26, 2026
dba370c
feat: add API adapter pattern for platform integration
cevheri Mar 26, 2026
032bcde
feat: configure package.json for npm package exports
cevheri Mar 26, 2026
f77ddd5
fix: remove unused imports and deprecated types
cevheri Mar 26, 2026
a6829a5
merge: add npm package exports and API adapter pattern
cevheri Mar 26, 2026
22c10b3
chore: rename package to @libredb/studio for npm publishing
cevheri Mar 26, 2026
47fb61a
feat: add npm publish workflow for @libredb/studio
cevheri Mar 26, 2026
b947cc7
chore: add .npmignore to publish only source code (572 → 205 files)
cevheri Mar 26, 2026
428b655
merge: add npm publish workflow and .npmignore
cevheri Mar 26, 2026
b9180a7
fix: use NPMJS_TOKEN secret name in npm publish workflow
cevheri Mar 26, 2026
c57a75c
chore: add index.js entry point and update main field for bundler com…
cevheri Mar 26, 2026
044894a
feat(workspace): add StudioWorkspace types and props interface
cevheri Mar 26, 2026
c7a7574
feat(workspace): add useConnectionAdapter hook with tests
cevheri Mar 26, 2026
25061f7
feat(workspace): add useQueryAdapter hook with tests
cevheri Mar 26, 2026
bf13193
feat(workspace): add StudioWorkspace composite component
cevheri Mar 26, 2026
5665a53
fix(workspace): stub QuerySafetyDialog AI analysis to prevent interna…
cevheri Mar 26, 2026
04cf6bc
feat(workspace): add workspace export entry point and build config
cevheri Mar 26, 2026
fb371cd
fix(workspace): remove unused QueryResult import
cevheri Mar 26, 2026
c879d1d
fix(workspace): add scoped dark theme CSS variables and remove Mobile…
cevheri Mar 26, 2026
b24a179
fix(workspace): inject scoped dark theme CSS via style tag for host a…
cevheri Mar 26, 2026
fc119df
feat: switch to Geist font and apply scoped font system in embedded w…
cevheri Mar 26, 2026
cdd9d24
fix: replace text-[12px] arbitrary values with text-xs for Tailwind v…
cevheri Mar 26, 2026
5b5ad95
refactor: replace hardcoded text-[Npx] with semantic font scale tokens
cevheri Mar 26, 2026
40c7fe9
fix: remove SQL Engine badge overlay from query editor
cevheri Mar 26, 2026
08a80e4
feat(workspace): implement StudioWorkspace export plan and design spe…
cevheri Mar 26, 2026
332379f
fix: update tests for semantic font tokens and removed SQL Engine badge
cevheri Mar 26, 2026
eec1a2e
chore: update CLAUDE.md with library build instructions and clean up …
cevheri Mar 26, 2026
191f80b
bump: version to 0.9.12
cevheri Mar 26, 2026
06e9475
fix(lint): ignore dist/ in eslint config and suppress require() in CJ…
cevheri Mar 26, 2026
f1e3053
tests: update QueryEditor and SnapshotTimeline tests for button label…
cevheri Mar 26, 2026
cf4bbde
fix(test): update OperationsTab badge variant assertion for Shadcn v2
cevheri Mar 26, 2026
286d28d
fix(test): update RootLayout font mock from Inter to Geist
cevheri Mar 26, 2026
fe40158
Merge pull request #51 from libredb/feat/studio-workspace-export
cevheri Mar 26, 2026
a74b53f
fix(ci): add bun install and build:lib to npm-publish workflow
cevheri Mar 26, 2026
1c17dbe
fix(tests): update PostgreSQL port in connection form tests to match …
cevheri Mar 26, 2026
26cb073
refactor: standardize font sizes across components
cevheri Mar 27, 2026
f36ebb6
refactor: update icon stroke widths across components
cevheri Mar 27, 2026
3f6cfa4
docs: enhance CLAUDE.md with critical platform integration rules and …
cevheri Mar 27, 2026
9ccdf5e
ci: add platform integration check workflow
cevheri Mar 27, 2026
91922bc
fix: replace shadcn Button with plain button in TableItem
cevheri Mar 27, 2026
a7a5dd6
fix(tests): update selectors for font and icon changes
cevheri Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/integration-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Platform Integration Check

on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read

jobs:
integration-rules:
name: Platform Integration Rules
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.9"

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Build library dist
run: bun run build:lib

- name: Check — No custom @theme text tokens
run: |
echo "Checking globals.css for custom text tokens..."
if grep -E '^\s*--text-[a-z]+:' src/app/globals.css | grep -v 'font-'; then
echo "::error::Custom text tokens found in @theme. tailwind-merge will silently strip these."
echo "Use standard Tailwind classes (text-xs, text-sm) or arbitrary values (text-[0.625rem]) instead."
exit 1
fi
echo "✓ No custom text tokens"

- name: Check — No custom text-* classes in components
run: |
echo "Checking for custom text token usage in components..."
CUSTOM=$(grep -rn '\btext-body\b\|\btext-data\b\|\btext-label\b\|\btext-title\b\|\btext-micro\b\|\btext-caption\b' \
src/components/ src/workspace/ --include="*.tsx" || true)
if [ -n "$CUSTOM" ]; then
echo "::error::Custom text tokens used in components. twMerge strips these silently."
echo "$CUSTOM"
exit 1
fi
echo "✓ No custom text token usage"

- name: Check — Lucide icons have strokeWidth
run: |
echo "Checking Lucide icons for strokeWidth prop..."
MISSING=$(grep -rn '<[A-Z][a-zA-Z]* className="w-' src/components/ src/workspace/ --include="*.tsx" \
| grep -v 'admin/' | grep -v 'ui/' \
| grep -v 'strokeWidth' \
| grep -v '<Button\|<Input\|<Label\|<Card\|<Dialog\|<Alert\|<Select\|<Textarea\|<Badge\|<Tooltip\|<Popover\|<Sheet\|<Tabs\|<Table\|<Drawer' \
| grep -v 'svg\|div\|span\|motion' || true)
if [ -n "$MISSING" ]; then
echo "::warning::Lucide icons without strokeWidth={1.5} found. These may render with inconsistent stroke weight."
echo "$MISSING" | head -20
fi
echo "✓ Lucide strokeWidth check complete"

- name: Check — No shadcn Button size=icon in sidebar/explorer
run: |
echo "Checking for shadcn Button in compact areas..."
BUTTONS=$(grep -rn 'size="icon"' \
src/components/sidebar/ src/components/schema-explorer/ --include="*.tsx" || true)
if [ -n "$BUTTONS" ]; then
echo "::error::shadcn Button size='icon' in sidebar/explorer. Use plain <button> to avoid platform CSS conflicts."
echo "$BUTTONS"
exit 1
fi
echo "✓ No Button size=icon in compact areas"

- name: Check — Dist chunks contain expected classes
run: |
echo "Checking dist for responsive class coverage..."
ALL_MD=$(grep -roh 'md:[a-z-]*' dist/*.mjs | sort -u)
WORKSPACE_MD=$(grep -roh 'md:[a-z-]*' dist/workspace.mjs | sort -u)
ONLY_IN_CHUNKS=$(comm -23 <(echo "$ALL_MD") <(echo "$WORKSPACE_MD"))
if [ -n "$ONLY_IN_CHUNKS" ]; then
echo "::notice::Responsive classes found only in chunks (not workspace.mjs):"
echo "$ONLY_IN_CHUNKS"
echo "Platform must scan chunk-*.mjs via @source directive."
fi
echo "✓ Dist chunk analysis complete"
111 changes: 111 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: NPM Publish

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g., 0.9.8). Leave empty to use package.json version.'
required: false
type: string
dry_run:
description: 'Dry run (do not actually publish)'
required: false
type: boolean
default: false

permissions:
contents: read

jobs:
validate:
name: Validate
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'NPM Publish' step
Uses Step
uses 'oven-sh/setup-bun' with ref 'v2', not a pinned commit hash
with:
bun-version: "1.3.9"

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Lint
run: bun run lint

- name: Typecheck
run: bun run typecheck

- name: Test
run: bun run test

- name: Build
run: bun run build
env:
NEXT_TELEMETRY_DISABLED: 1
JWT_SECRET: test-secret-for-ci-build-only-32ch
ADMIN_EMAIL: admin@libredb.org
ADMIN_PASSWORD: test-admin
USER_EMAIL: user@libredb.org
USER_PASSWORD: test-user

publish:
name: Publish to NPM
runs-on: ubuntu-latest
needs: validate
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'NPM Publish' step
Uses Step
uses 'oven-sh/setup-bun' with ref 'v2', not a pinned commit hash
with:
bun-version: "1.3.9"

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Build library (tsup)
run: bun run build:lib

- name: Set version (if provided)
if: inputs.version != ''
env:
INPUT_VERSION: ${{ inputs.version }}
run: npm version "$INPUT_VERSION" --no-git-tag-version

- name: Verify package
run: |
echo "Package name: $(node -p "require('./package.json').name")"
echo "Package version: $(node -p "require('./package.json').version")"

- name: Publish (dry run)
if: inputs.dry_run == true
run: npm publish --access public --dry-run
env:
NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }}

- name: Publish
if: inputs.dry_run != true
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }}

- name: Summary
run: |
VERSION=$(node -p "require('./package.json').version")
echo "## Published @libredb/studio@${VERSION}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- **Registry:** https://www.npmjs.com/package/@libredb/studio" >> "$GITHUB_STEP_SUMMARY"
echo "- **Install:** \`npm install @libredb/studio@${VERSION}\`" >> "$GITHUB_STEP_SUMMARY"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,6 @@ seed-connections.yaml

.playwright-mcp/*.png

npmjs-token

.npmrc
27 changes: 27 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Publish only source code needed for npm consumers
# Everything is ignored by default, then we allowlist

# Ignore everything
*

# Allow source exports
!src/exports/
!src/exports/**

# Allow library code (providers, types, utils)
!src/lib/
!src/lib/**

# Allow components
!src/components/
!src/components/**

# Allow hooks (used by components)
!src/hooks/
!src/hooks/**

# Allow package files
!package.json
!README.md
!LICENSE
!tsconfig.json
56 changes: 56 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

> **CRITICAL: This project is published as an npm package (`@libredb/studio`) and consumed by `libredb-platform`.**
> Every CSS class, Tailwind utility, icon prop, and component choice MUST follow the Platform Integration Rules below.
> Violations cause silent style/layout breakage that only appears when embedded in platform — not in standalone studio.

## Project Overview

LibreDB Studio is a web-based SQL IDE for cloud-native teams. It supports PostgreSQL, MySQL, SQLite, Oracle, SQL Server, MongoDB, Redis with AI-powered query assistance.

**Dual usage:** Studio runs both as a standalone Next.js app AND as an embedded npm package inside libredb-platform. The `build:lib` command (tsup) produces the package dist. Any UI change must be verified in both modes.

## Github
* Repository: https://github.com/libredb/libredb-studio
* Container Registry: https://github.com/libredb/libredb-studio/pkgs/container/libredb-studio
Expand Down Expand Up @@ -48,6 +54,11 @@ bun run test:e2e
# Coverage report
bun run test:coverage

# Library build (tsup) — exports @libredb/studio package for platform consumption
# IMPORTANT: After changing any component used by platform (workspace, providers, etc.),
# you MUST run this command. `bun run build` (Next.js) does NOT update the package dist.
bun run build:lib

# Docker development
docker-compose up -d

Expand All @@ -61,6 +72,51 @@ The project uses ESLint 9 for linting and `bun:test` for testing with `@testing-

> **Important**: Always use `bun run test` instead of bare `bun test`. Component tests require isolated execution groups (handled by `tests/run-components.sh`) to prevent `mock.module()` cross-contamination between test files.

## Platform Integration Rules (npm package @libredb/studio)

Studio is consumed by libredb-platform as an npm package via `build:lib` (tsup). These rules prevent silent style/layout breakage that only appears when embedded in platform.

### Tailwind CSS Rules

| Do | Don't | Why |
|----|-------|-----|
| `text-xs`, `text-sm` (standard) | `text-body`, `text-data` (custom @theme) | `tailwind-merge` strips custom tokens silently |
| `text-[0.625rem]` (arbitrary value) | `text-label` (custom @theme) | Arbitrary values are twMerge-safe |
| `font-medium`, `font-normal` | `font-bold` everywhere | Studio is compact IDE, lighter weights |
| `w-3 h-3`, `w-3.5 h-3.5` (icons) | `w-4 h-4` or larger | Studio icons smaller than platform |

**Never define custom text tokens in `@theme` block.** `tailwind-merge` (used in `cn()`) interprets `text-body` as a color utility, not font-size. When combined with `text-muted-foreground`, twMerge silently removes `text-body` → no font-size applied → browser default 16px. This bug is invisible in standalone studio (Tailwind generates the CSS) but breaks embedded mode.

### Lucide Icon Rules

Always pass `strokeWidth={1.5}` to every Lucide icon:
```tsx
<Lock strokeWidth={1.5} className="w-3 h-3" />
```
Lucide defaults to `strokeWidth=2` and emits `width="24" height="24"` HTML attributes. Custom DB icons use `strokeWidth=1.5` without HTML size attributes. Without this prop, Lucide icons appear thicker and potentially larger than custom icons.

### Component Rules

- **Small icon buttons:** Use plain `<button className="p-1 rounded ...">` instead of shadcn `<Button size="icon">`. Platform's Button CSS can override studio's size classes due to specificity.
- **Responsive classes:** `md:hidden`, `hidden md:block` etc. must work. If a component is in a tsup chunk, verify platform's `@source` scans that chunk.

### Platform-Side Requirements

Platform's `globals.css` must scan ALL studio dist files (tsup creates chunks):
```css
@source "../../node_modules/@libredb/studio/dist/workspace.mjs";
@source "../../node_modules/@libredb/studio/dist/chunk-*.mjs";
```
Without chunk scanning, responsive/utility classes in chunked components won't generate CSS.

### Verification Workflow

After any UI change in studio:
1. `bun run build:lib` — rebuild tsup dist
2. `cp -r dist/* ../libredb-platform/node_modules/@libredb/studio/dist/` — copy to platform
3. `rm -rf ../libredb-platform/.next` — clear platform cache (for CSS changes)
4. Restart platform dev server and verify at `localhost:3000/workspace`

## Architecture

### Tech Stack
Expand Down
Loading
Loading