- React 19 + TypeScript
- Vite (build)
- Tailwind CSS v4 + shadcn/ui
- Zustand (state management)
- CodeMirror 6 (SQL editor)
- Glide Data Grid (tables)
- XYFlow (ERD diagrams)
src/
├── screens/ # Page components (MainScreen, WorkspaceScreen)
├── components/ # UI components (shadcn/ui + custom)
├── stores/ # Zustand state management
├── services/ # Backend communication
├── hooks/ # Custom React hooks
├── adapters/ # Frontend database adapters
├── types/ # TypeScript interfaces
└── utils/ # Helpers
Configured in vite.config.ts:
@/→src/@components/→src/components/@hooks/→src/hooks/@types/→src/types/@utils/→src/utils/@lib/→src/lib/
Each store has a single concern:
| Store | Purpose |
|---|---|
connectionStoreNew |
Active connections, favorites, recents |
workbenchStore |
Layout tree, tab metadata, drag-drop |
workspaceScreenStore |
Schema/table navigation, filtering |
crudStore |
Transaction state, pending CRUD |
dataInvalidationStore |
Event-driven cache invalidation |
panelStore |
Panel visibility and state |
tabStateStore |
Active tab tracking |
erdStore |
Entity relationship diagram data |
Pattern: Use Zustand for global state, React useState for local UI.
Functional components with named exports:
export const MyComponent = ({ prop }: Props) => {
// ...
};Conditional class merging with cn():
import { cn } from "@/lib/utils";
<div className={cn("base-class", isActive && "active-class")} />;Type-safe paradigm dispatch:
import { getAdapter, isSqlAdapter } from "@/adapters";
const adapter = getAdapter(connection);
if (isSqlAdapter(adapter)) {
// SQL-specific operations
}Available guards: isSqlAdapter(), isDocumentAdapter(), isKeyValueAdapter()
import { useDataInvalidationStore } from "@/stores/dataInvalidationStore";
// Subscribe to table changes
const unsubscribe = useDataInvalidationStore
.getState()
.subscribe("conn:db:schema:table", () => refetch());
// Trigger invalidation after CRUD
useDataInvalidationStore.getState().invalidateTable("conn:db:schema:table");Direct query (metadata, small results):
import { BackendAPI } from "@/services/backend";
const result = await BackendAPI.query(connectionId, sql);Streaming query (data grids, large results):
import { queryStreamClient } from "@/services/queryStreamClient";
await queryStreamClient.streamWithCallbacks(params, { onBatch });See Query Execution for details.