@agent-native/core 0.26.9 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/run-ownership.d.ts +12 -0
- package/dist/agent/run-ownership.d.ts.map +1 -0
- package/dist/agent/run-ownership.js +39 -0
- package/dist/agent/run-ownership.js.map +1 -0
- package/dist/client/db-admin/DataGrid.d.ts +42 -0
- package/dist/client/db-admin/DataGrid.d.ts.map +1 -0
- package/dist/client/db-admin/DataGrid.js +204 -0
- package/dist/client/db-admin/DataGrid.js.map +1 -0
- package/dist/client/db-admin/DbAdminPage.d.ts +2 -0
- package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -0
- package/dist/client/db-admin/DbAdminPage.js +72 -0
- package/dist/client/db-admin/DbAdminPage.js.map +1 -0
- package/dist/client/db-admin/DevDatabaseLink.d.ts +19 -0
- package/dist/client/db-admin/DevDatabaseLink.d.ts.map +1 -0
- package/dist/client/db-admin/DevDatabaseLink.js +25 -0
- package/dist/client/db-admin/DevDatabaseLink.js.map +1 -0
- package/dist/client/db-admin/EditableCell.d.ts +26 -0
- package/dist/client/db-admin/EditableCell.d.ts.map +1 -0
- package/dist/client/db-admin/EditableCell.js +150 -0
- package/dist/client/db-admin/EditableCell.js.map +1 -0
- package/dist/client/db-admin/FilterBar.d.ts +8 -0
- package/dist/client/db-admin/FilterBar.d.ts.map +1 -0
- package/dist/client/db-admin/FilterBar.js +68 -0
- package/dist/client/db-admin/FilterBar.js.map +1 -0
- package/dist/client/db-admin/ResultsGrid.d.ts +6 -0
- package/dist/client/db-admin/ResultsGrid.d.ts.map +1 -0
- package/dist/client/db-admin/ResultsGrid.js +41 -0
- package/dist/client/db-admin/ResultsGrid.js.map +1 -0
- package/dist/client/db-admin/RowSidePanel.d.ts +18 -0
- package/dist/client/db-admin/RowSidePanel.d.ts.map +1 -0
- package/dist/client/db-admin/RowSidePanel.js +104 -0
- package/dist/client/db-admin/RowSidePanel.js.map +1 -0
- package/dist/client/db-admin/SqlEditor.d.ts +8 -0
- package/dist/client/db-admin/SqlEditor.d.ts.map +1 -0
- package/dist/client/db-admin/SqlEditor.js +350 -0
- package/dist/client/db-admin/SqlEditor.js.map +1 -0
- package/dist/client/db-admin/TableBrowser.d.ts +10 -0
- package/dist/client/db-admin/TableBrowser.d.ts.map +1 -0
- package/dist/client/db-admin/TableBrowser.js +61 -0
- package/dist/client/db-admin/TableBrowser.js.map +1 -0
- package/dist/client/db-admin/TableEditor.d.ts +9 -0
- package/dist/client/db-admin/TableEditor.d.ts.map +1 -0
- package/dist/client/db-admin/TableEditor.js +254 -0
- package/dist/client/db-admin/TableEditor.js.map +1 -0
- package/dist/client/db-admin/cell-format.d.ts +55 -0
- package/dist/client/db-admin/cell-format.d.ts.map +1 -0
- package/dist/client/db-admin/cell-format.js +223 -0
- package/dist/client/db-admin/cell-format.js.map +1 -0
- package/dist/client/db-admin/changeset.d.ts +74 -0
- package/dist/client/db-admin/changeset.d.ts.map +1 -0
- package/dist/client/db-admin/changeset.js +169 -0
- package/dist/client/db-admin/changeset.js.map +1 -0
- package/dist/client/db-admin/export-utils.d.ts +15 -0
- package/dist/client/db-admin/export-utils.d.ts.map +1 -0
- package/dist/client/db-admin/export-utils.js +62 -0
- package/dist/client/db-admin/export-utils.js.map +1 -0
- package/dist/client/db-admin/index.d.ts +7 -0
- package/dist/client/db-admin/index.d.ts.map +1 -0
- package/dist/client/db-admin/index.js +8 -0
- package/dist/client/db-admin/index.js.map +1 -0
- package/dist/client/db-admin/sql-storage.d.ts +35 -0
- package/dist/client/db-admin/sql-storage.d.ts.map +1 -0
- package/dist/client/db-admin/sql-storage.js +117 -0
- package/dist/client/db-admin/sql-storage.js.map +1 -0
- package/dist/client/db-admin/storage.d.ts +24 -0
- package/dist/client/db-admin/storage.d.ts.map +1 -0
- package/dist/client/db-admin/storage.js +50 -0
- package/dist/client/db-admin/storage.js.map +1 -0
- package/dist/client/db-admin/useAgentSync.d.ts +22 -0
- package/dist/client/db-admin/useAgentSync.d.ts.map +1 -0
- package/dist/client/db-admin/useAgentSync.js +120 -0
- package/dist/client/db-admin/useAgentSync.js.map +1 -0
- package/dist/client/db-admin/useDbAdmin.d.ts +20 -0
- package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -0
- package/dist/client/db-admin/useDbAdmin.js +154 -0
- package/dist/client/db-admin/useDbAdmin.js.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/credentials/index.d.ts.map +1 -1
- package/dist/credentials/index.js +25 -5
- package/dist/credentials/index.js.map +1 -1
- package/dist/db-admin/agent-tools.d.ts +15 -0
- package/dist/db-admin/agent-tools.d.ts.map +1 -0
- package/dist/db-admin/agent-tools.js +147 -0
- package/dist/db-admin/agent-tools.js.map +1 -0
- package/dist/db-admin/operations.d.ts +17 -0
- package/dist/db-admin/operations.d.ts.map +1 -0
- package/dist/db-admin/operations.js +541 -0
- package/dist/db-admin/operations.js.map +1 -0
- package/dist/db-admin/routes.d.ts +5 -0
- package/dist/db-admin/routes.d.ts.map +1 -0
- package/dist/db-admin/routes.js +134 -0
- package/dist/db-admin/routes.js.map +1 -0
- package/dist/db-admin/types.d.ts +85 -0
- package/dist/db-admin/types.d.ts.map +1 -0
- package/dist/db-admin/types.js +9 -0
- package/dist/db-admin/types.js.map +1 -0
- package/dist/extensions/url-safety.d.ts +20 -0
- package/dist/extensions/url-safety.d.ts.map +1 -1
- package/dist/extensions/url-safety.js +43 -0
- package/dist/extensions/url-safety.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
- package/dist/file-upload/actions/upload-image.js +6 -1
- package/dist/file-upload/actions/upload-image.js.map +1 -1
- package/dist/integrations/adapters/email.d.ts.map +1 -1
- package/dist/integrations/adapters/email.js +112 -0
- package/dist/integrations/adapters/email.js.map +1 -1
- package/dist/integrations/types.d.ts +11 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/scripts/db/exec.d.ts.map +1 -1
- package/dist/scripts/db/exec.js +2 -1
- package/dist/scripts/db/exec.js.map +1 -1
- package/dist/scripts/db/index.d.ts.map +1 -1
- package/dist/scripts/db/index.js +1 -0
- package/dist/scripts/db/index.js.map +1 -1
- package/dist/scripts/db/migrate-encrypt-credentials.d.ts +28 -0
- package/dist/scripts/db/migrate-encrypt-credentials.d.ts.map +1 -0
- package/dist/scripts/db/migrate-encrypt-credentials.js +190 -0
- package/dist/scripts/db/migrate-encrypt-credentials.js.map +1 -0
- package/dist/scripts/db/query.d.ts.map +1 -1
- package/dist/scripts/db/query.js +2 -1
- package/dist/scripts/db/query.js.map +1 -1
- package/dist/scripts/db/safety.d.ts +1 -0
- package/dist/scripts/db/safety.d.ts.map +1 -1
- package/dist/scripts/db/safety.js +32 -0
- package/dist/scripts/db/safety.js.map +1 -1
- package/dist/scripts/db/scoping.d.ts.map +1 -1
- package/dist/scripts/db/scoping.js +11 -1
- package/dist/scripts/db/scoping.js.map +1 -1
- package/dist/secrets/crypto.d.ts +28 -0
- package/dist/secrets/crypto.d.ts.map +1 -0
- package/dist/secrets/crypto.js +81 -0
- package/dist/secrets/crypto.js.map +1 -0
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +3 -61
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +5 -2
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +24 -7
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +39 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.js +3 -3
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +5 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +9 -1
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/design-token-utils.d.ts +8 -1
- package/dist/server/design-token-utils.d.ts.map +1 -1
- package/dist/server/design-token-utils.js +12 -4
- package/dist/server/design-token-utils.js.map +1 -1
- package/dist/templates/default/AGENTS.md +4 -4
- package/dist/templates/default/app/routes/database.tsx +13 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +4 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/a2a-protocol.md +2 -2
- package/docs/content/actions.md +2 -54
- package/docs/content/agent-mentions.md +1 -1
- package/docs/content/agent-teams.md +1 -1
- package/docs/content/authentication.md +2 -2
- package/docs/content/cli-adapters.md +33 -17
- package/docs/content/client.md +11 -20
- package/docs/content/code-agents-ui.md +19 -6
- package/docs/content/context-awareness.md +36 -20
- package/docs/content/database.md +3 -3
- package/docs/content/deployment.md +8 -8
- package/docs/content/dispatch.md +1 -1
- package/docs/content/external-agents.md +5 -1
- package/docs/content/faq.md +1 -0
- package/docs/content/frames.md +110 -30
- package/docs/content/getting-started.md +15 -14
- package/docs/content/mcp-clients.md +1 -1
- package/docs/content/mcp-protocol.md +11 -88
- package/docs/content/messaging.md +1 -1
- package/docs/content/migration-workbench.md +13 -87
- package/docs/content/multi-app-workspace.md +2 -38
- package/docs/content/multi-tenancy.md +3 -26
- package/docs/content/onboarding.md +10 -3
- package/docs/content/recurring-jobs.md +2 -2
- package/docs/content/security.md +33 -1
- package/docs/content/server.md +1 -1
- package/docs/content/template-assets.md +9 -9
- package/docs/content/template-brain.md +114 -388
- package/docs/content/template-clips.md +42 -2
- package/docs/content/template-content.md +1 -1
- package/docs/content/template-design.md +27 -0
- package/docs/content/template-dispatch.md +3 -3
- package/docs/content/template-forms.md +6 -6
- package/docs/content/template-starter.md +2 -2
- package/docs/content/using-your-agent.md +56 -0
- package/docs/content/workspace-management.md +6 -6
- package/docs/content/workspace.md +19 -0
- package/package.json +10 -3
- package/src/templates/default/AGENTS.md +4 -4
- package/src/templates/default/app/routes/database.tsx +13 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a run's owning thread id. Checks in-memory run state first (populated
|
|
3
|
+
* synchronously by startRun, so there is no race against the async SQL insert),
|
|
4
|
+
* then falls back to SQL for cross-isolate / post-reload lookups. Returns null
|
|
5
|
+
* when the run is unknown.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveRunThreadId(runId: string): Promise<string | null>;
|
|
8
|
+
/** True when `owner` owns the chat thread `threadId`. */
|
|
9
|
+
export declare function callerOwnsThread(owner: string, threadId: string | null | undefined): Promise<boolean>;
|
|
10
|
+
/** True when `owner` owns the thread that run `runId` belongs to. */
|
|
11
|
+
export declare function callerOwnsRun(owner: string, runId: string): Promise<boolean>;
|
|
12
|
+
//# sourceMappingURL=run-ownership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-ownership.d.ts","sourceRoot":"","sources":["../../src/agent/run-ownership.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB;AAED,yDAAyD;AACzD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,OAAO,CAAC,OAAO,CAAC,CAIlB;AAED,qEAAqE;AACrE,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAElB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership checks for the agent run HTTP routes (`/runs/:id/events`,
|
|
3
|
+
* `/runs/:id/abort`, `/runs/active`).
|
|
4
|
+
*
|
|
5
|
+
* `agent_runs` has no owner column — a run's ownership lives on its
|
|
6
|
+
* `chat_threads` row via `thread_id`. These helpers resolve that link and
|
|
7
|
+
* compare the thread's owner to the requesting user, so the run routes can
|
|
8
|
+
* reject cross-tenant access (any authenticated user who learns another
|
|
9
|
+
* tenant's runId/threadId must not be able to stream their live agent turn or
|
|
10
|
+
* abort their run).
|
|
11
|
+
*/
|
|
12
|
+
import { getRun } from "./run-manager.js";
|
|
13
|
+
import { getRunById } from "./run-store.js";
|
|
14
|
+
import { getThread } from "../chat-threads/store.js";
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a run's owning thread id. Checks in-memory run state first (populated
|
|
17
|
+
* synchronously by startRun, so there is no race against the async SQL insert),
|
|
18
|
+
* then falls back to SQL for cross-isolate / post-reload lookups. Returns null
|
|
19
|
+
* when the run is unknown.
|
|
20
|
+
*/
|
|
21
|
+
export async function resolveRunThreadId(runId) {
|
|
22
|
+
const memRun = getRun(runId);
|
|
23
|
+
if (memRun)
|
|
24
|
+
return memRun.threadId;
|
|
25
|
+
const row = await getRunById(runId);
|
|
26
|
+
return row?.threadId ?? null;
|
|
27
|
+
}
|
|
28
|
+
/** True when `owner` owns the chat thread `threadId`. */
|
|
29
|
+
export async function callerOwnsThread(owner, threadId) {
|
|
30
|
+
if (!threadId)
|
|
31
|
+
return false;
|
|
32
|
+
const thread = await getThread(threadId);
|
|
33
|
+
return !!thread && thread.ownerEmail === owner;
|
|
34
|
+
}
|
|
35
|
+
/** True when `owner` owns the thread that run `runId` belongs to. */
|
|
36
|
+
export async function callerOwnsRun(owner, runId) {
|
|
37
|
+
return callerOwnsThread(owner, await resolveRunThreadId(runId));
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=run-ownership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-ownership.js","sourceRoot":"","sources":["../../src/agent/run-ownership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa;IAEb,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,GAAG,EAAE,QAAQ,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAa,EACb,QAAmC;IAEnC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,KAAK,CAAC;AACjD,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAa,EACb,KAAa;IAEb,OAAO,gBAAgB,CAAC,KAAK,EAAE,MAAM,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;AAClE,CAAC","sourcesContent":["/**\n * Ownership checks for the agent run HTTP routes (`/runs/:id/events`,\n * `/runs/:id/abort`, `/runs/active`).\n *\n * `agent_runs` has no owner column — a run's ownership lives on its\n * `chat_threads` row via `thread_id`. These helpers resolve that link and\n * compare the thread's owner to the requesting user, so the run routes can\n * reject cross-tenant access (any authenticated user who learns another\n * tenant's runId/threadId must not be able to stream their live agent turn or\n * abort their run).\n */\nimport { getRun } from \"./run-manager.js\";\nimport { getRunById } from \"./run-store.js\";\nimport { getThread } from \"../chat-threads/store.js\";\n\n/**\n * Resolve a run's owning thread id. Checks in-memory run state first (populated\n * synchronously by startRun, so there is no race against the async SQL insert),\n * then falls back to SQL for cross-isolate / post-reload lookups. Returns null\n * when the run is unknown.\n */\nexport async function resolveRunThreadId(\n runId: string,\n): Promise<string | null> {\n const memRun = getRun(runId);\n if (memRun) return memRun.threadId;\n const row = await getRunById(runId);\n return row?.threadId ?? null;\n}\n\n/** True when `owner` owns the chat thread `threadId`. */\nexport async function callerOwnsThread(\n owner: string,\n threadId: string | null | undefined,\n): Promise<boolean> {\n if (!threadId) return false;\n const thread = await getThread(threadId);\n return !!thread && thread.ownerEmail === owner;\n}\n\n/** True when `owner` owns the thread that run `runId` belongs to. */\nexport async function callerOwnsRun(\n owner: string,\n runId: string,\n): Promise<boolean> {\n return callerOwnsThread(owner, await resolveRunThreadId(runId));\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DbAdminForeignKey, DbAdminSort, DbAdminTableSchema } from "../../db-admin/types.js";
|
|
2
|
+
/** A grid row pairs the displayed values with its stable pk string. */
|
|
3
|
+
export interface GridRow {
|
|
4
|
+
pk: string;
|
|
5
|
+
/** Values WITH staged edits already applied (for display). */
|
|
6
|
+
values: Record<string, unknown>;
|
|
7
|
+
isNew?: boolean;
|
|
8
|
+
isDeleted?: boolean;
|
|
9
|
+
/** Local id for new rows (so edits route to the right new-row). */
|
|
10
|
+
localId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Identifies the focused cell for keyboard nav. */
|
|
13
|
+
export interface ActiveCell {
|
|
14
|
+
rowIndex: number;
|
|
15
|
+
colName: string;
|
|
16
|
+
editing: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface DataGridProps {
|
|
19
|
+
schema: DbAdminTableSchema;
|
|
20
|
+
rows: GridRow[];
|
|
21
|
+
isLoading: boolean;
|
|
22
|
+
pageSize: number;
|
|
23
|
+
sort: DbAdminSort[];
|
|
24
|
+
onSortChange: (sort: DbAdminSort[]) => void;
|
|
25
|
+
selectedPks: Set<string>;
|
|
26
|
+
onSelectionChange: (pks: Set<string>) => void;
|
|
27
|
+
columnWidths: Record<string, number>;
|
|
28
|
+
onColumnWidthsChange: (widths: Record<string, number>) => void;
|
|
29
|
+
active: ActiveCell | null;
|
|
30
|
+
onActiveChange: (active: ActiveCell | null) => void;
|
|
31
|
+
/** Whether editing is permitted (table has a PK). */
|
|
32
|
+
editable: boolean;
|
|
33
|
+
/** Commit a staged cell edit. */
|
|
34
|
+
onCellCommit: (row: GridRow, col: string, value: unknown) => void;
|
|
35
|
+
/** Whether a given cell is dirty. */
|
|
36
|
+
isCellDirty: (row: GridRow, col: string) => boolean;
|
|
37
|
+
/** Toggle deletion staging for a single row. */
|
|
38
|
+
onToggleDelete: (row: GridRow) => void;
|
|
39
|
+
onNavigateToRow: (fk: DbAdminForeignKey, value: unknown) => void;
|
|
40
|
+
}
|
|
41
|
+
export declare function DataGrid(props: DataGridProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
//# sourceMappingURL=DataGrid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataGrid.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/DataGrid.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAEV,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAEjC,uEAAuE;AACvE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,YAAY,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IAE5C,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IAE9C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IAE/D,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAEpD,qDAAqD;IACrD,QAAQ,EAAE,OAAO,CAAC;IAElB,iCAAiC;IACjC,YAAY,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClE,qCAAqC;IACrC,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,gDAAgD;IAChD,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC,eAAe,EAAE,CAAC,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAClE;AAKD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAyX5C"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
3
|
+
import { flexRender, getCoreRowModel, useReactTable, } from "@tanstack/react-table";
|
|
4
|
+
import { IconKey, IconArrowUp, IconArrowDown, IconExternalLink, IconTrash, IconArrowBackUp, } from "@tabler/icons-react";
|
|
5
|
+
import { cn } from "../utils.js";
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
7
|
+
import { EditableCell } from "./EditableCell.js";
|
|
8
|
+
import { inferEditorKind } from "./cell-format.js";
|
|
9
|
+
const SELECT_COL = "__select__";
|
|
10
|
+
const ACTIONS_COL = "__actions__";
|
|
11
|
+
export function DataGrid(props) {
|
|
12
|
+
const { schema, rows, isLoading, pageSize, sort, onSortChange, selectedPks, onSelectionChange, columnWidths, onColumnWidthsChange, active, onActiveChange, editable, onCellCommit, isCellDirty, onToggleDelete, onNavigateToRow, } = props;
|
|
13
|
+
const containerRef = useRef(null);
|
|
14
|
+
const fkByColumn = useMemo(() => {
|
|
15
|
+
const map = new Map();
|
|
16
|
+
for (const fk of schema.foreignKeys)
|
|
17
|
+
map.set(fk.column, fk);
|
|
18
|
+
return map;
|
|
19
|
+
}, [schema.foreignKeys]);
|
|
20
|
+
const kindByColumn = useMemo(() => {
|
|
21
|
+
const map = new Map();
|
|
22
|
+
for (const col of schema.columns)
|
|
23
|
+
map.set(col.name, inferEditorKind(col));
|
|
24
|
+
return map;
|
|
25
|
+
}, [schema.columns]);
|
|
26
|
+
const allSelected = rows.length > 0 && rows.every((r) => selectedPks.has(r.pk));
|
|
27
|
+
const someSelected = rows.some((r) => selectedPks.has(r.pk));
|
|
28
|
+
const toggleAll = useCallback(() => {
|
|
29
|
+
if (allSelected)
|
|
30
|
+
onSelectionChange(new Set());
|
|
31
|
+
else
|
|
32
|
+
onSelectionChange(new Set(rows.map((r) => r.pk)));
|
|
33
|
+
}, [allSelected, rows, onSelectionChange]);
|
|
34
|
+
const toggleOne = useCallback((pk) => {
|
|
35
|
+
const next = new Set(selectedPks);
|
|
36
|
+
if (next.has(pk))
|
|
37
|
+
next.delete(pk);
|
|
38
|
+
else
|
|
39
|
+
next.add(pk);
|
|
40
|
+
onSelectionChange(next);
|
|
41
|
+
}, [selectedPks, onSelectionChange]);
|
|
42
|
+
const cycleSort = useCallback((colName) => {
|
|
43
|
+
const current = sort.find((s) => s.column === colName);
|
|
44
|
+
if (!current)
|
|
45
|
+
onSortChange([{ column: colName, dir: "asc" }]);
|
|
46
|
+
else if (current.dir === "asc")
|
|
47
|
+
onSortChange([{ column: colName, dir: "desc" }]);
|
|
48
|
+
else
|
|
49
|
+
onSortChange([]);
|
|
50
|
+
}, [sort, onSortChange]);
|
|
51
|
+
const columns = useMemo(() => {
|
|
52
|
+
const selectCol = {
|
|
53
|
+
id: SELECT_COL,
|
|
54
|
+
size: 40,
|
|
55
|
+
enableResizing: false,
|
|
56
|
+
header: () => (_jsx("input", { type: "checkbox", checked: allSelected, ref: (el) => {
|
|
57
|
+
if (el)
|
|
58
|
+
el.indeterminate = !allSelected && someSelected;
|
|
59
|
+
}, onChange: toggleAll, className: "h-3.5 w-3.5 cursor-pointer accent-primary", "aria-label": "Select all rows" })),
|
|
60
|
+
cell: ({ row }) => (_jsx("input", { type: "checkbox", checked: selectedPks.has(row.original.pk), onChange: () => toggleOne(row.original.pk), className: "h-3.5 w-3.5 cursor-pointer accent-primary", "aria-label": "Select row" })),
|
|
61
|
+
};
|
|
62
|
+
const dataCols = schema.columns.map((col) => ({
|
|
63
|
+
id: col.name,
|
|
64
|
+
accessorFn: (r) => r.values[col.name],
|
|
65
|
+
size: columnWidths[col.name] ?? defaultWidth(col),
|
|
66
|
+
minSize: 60,
|
|
67
|
+
header: () => (_jsx(ColumnHeader, { column: col, fk: fkByColumn.get(col.name), sortDir: sort.find((s) => s.column === col.name)?.dir, onSort: () => cycleSort(col.name) })),
|
|
68
|
+
cell: ({ row, getValue }) => {
|
|
69
|
+
const value = getValue();
|
|
70
|
+
const fk = fkByColumn.get(col.name);
|
|
71
|
+
const isActive = active?.rowIndex === row.index && active.colName === col.name;
|
|
72
|
+
return (_jsxs("div", { className: "group relative flex h-full items-center", children: [_jsx(EditableCell, { column: col, kind: kindByColumn.get(col.name) ?? "text", value: value, editable: editable && !row.original.isDeleted, dirty: isCellDirty(row.original, col.name), active: isActive, editing: isActive ? active.editing : false, onStartEdit: () => onActiveChange({
|
|
73
|
+
rowIndex: row.index,
|
|
74
|
+
colName: col.name,
|
|
75
|
+
editing: true,
|
|
76
|
+
}), onCancelEdit: () => onActiveChange({
|
|
77
|
+
rowIndex: row.index,
|
|
78
|
+
colName: col.name,
|
|
79
|
+
editing: false,
|
|
80
|
+
}), onCommit: (v) => {
|
|
81
|
+
onCellCommit(row.original, col.name, v);
|
|
82
|
+
onActiveChange({
|
|
83
|
+
rowIndex: row.index,
|
|
84
|
+
colName: col.name,
|
|
85
|
+
editing: false,
|
|
86
|
+
});
|
|
87
|
+
}, onNavigate: (dir) => moveActive(dir, row.index, col.name) }), fk && value !== null && value !== undefined && (_jsx("button", { type: "button", title: `Open ${fk.refTable}.${fk.refColumn}`, onClick: (e) => {
|
|
88
|
+
e.stopPropagation();
|
|
89
|
+
onNavigateToRow(fk, value);
|
|
90
|
+
}, className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground/50 opacity-0 hover:text-primary group-hover:opacity-100", children: _jsx(IconExternalLink, { className: "h-3 w-3" }) }))] }));
|
|
91
|
+
},
|
|
92
|
+
}));
|
|
93
|
+
const actionsCol = {
|
|
94
|
+
id: ACTIONS_COL,
|
|
95
|
+
size: 44,
|
|
96
|
+
enableResizing: false,
|
|
97
|
+
header: () => null,
|
|
98
|
+
cell: ({ row }) => (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx("button", { type: "button", title: row.original.isDeleted ? "Undo delete" : "Delete row", onClick: () => onToggleDelete(row.original), disabled: !editable, className: cn("rounded p-1 text-muted-foreground/50 hover:text-destructive disabled:opacity-30", row.original.isDeleted && "text-destructive"), children: row.original.isDeleted ? (_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" })) : (_jsx(IconTrash, { className: "h-3.5 w-3.5" })) }) })),
|
|
99
|
+
};
|
|
100
|
+
return [selectCol, ...dataCols, actionsCol];
|
|
101
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
|
+
}, [
|
|
103
|
+
schema.columns,
|
|
104
|
+
columnWidths,
|
|
105
|
+
fkByColumn,
|
|
106
|
+
kindByColumn,
|
|
107
|
+
sort,
|
|
108
|
+
active,
|
|
109
|
+
selectedPks,
|
|
110
|
+
allSelected,
|
|
111
|
+
someSelected,
|
|
112
|
+
editable,
|
|
113
|
+
]);
|
|
114
|
+
const moveActive = useCallback((dir, rowIndex, colName) => {
|
|
115
|
+
const dataColNames = schema.columns.map((c) => c.name);
|
|
116
|
+
const colIdx = dataColNames.indexOf(colName);
|
|
117
|
+
let nextRow = rowIndex;
|
|
118
|
+
let nextCol = colIdx;
|
|
119
|
+
if (dir === "down")
|
|
120
|
+
nextRow = Math.min(rows.length - 1, rowIndex + 1);
|
|
121
|
+
else if (dir === "up")
|
|
122
|
+
nextRow = Math.max(0, rowIndex - 1);
|
|
123
|
+
else if (dir === "right")
|
|
124
|
+
nextCol = Math.min(dataColNames.length - 1, colIdx + 1);
|
|
125
|
+
else if (dir === "left")
|
|
126
|
+
nextCol = Math.max(0, colIdx - 1);
|
|
127
|
+
onActiveChange({
|
|
128
|
+
rowIndex: nextRow,
|
|
129
|
+
colName: dataColNames[nextCol],
|
|
130
|
+
editing: false,
|
|
131
|
+
});
|
|
132
|
+
}, [schema.columns, rows.length, onActiveChange]);
|
|
133
|
+
const sizingState = useMemo(() => {
|
|
134
|
+
const out = {};
|
|
135
|
+
for (const [k, v] of Object.entries(columnWidths))
|
|
136
|
+
out[k] = v;
|
|
137
|
+
return out;
|
|
138
|
+
}, [columnWidths]);
|
|
139
|
+
const table = useReactTable({
|
|
140
|
+
data: rows,
|
|
141
|
+
columns,
|
|
142
|
+
getCoreRowModel: getCoreRowModel(),
|
|
143
|
+
columnResizeMode: "onChange",
|
|
144
|
+
state: { columnSizing: sizingState },
|
|
145
|
+
onColumnSizingChange: (updater) => {
|
|
146
|
+
const next = typeof updater === "function" ? updater(sizingState) : updater;
|
|
147
|
+
onColumnWidthsChange(next);
|
|
148
|
+
},
|
|
149
|
+
getRowId: (r) => r.pk,
|
|
150
|
+
});
|
|
151
|
+
const onGridKeyDown = (e) => {
|
|
152
|
+
if (!active || active.editing)
|
|
153
|
+
return;
|
|
154
|
+
const dataColNames = schema.columns.map((c) => c.name);
|
|
155
|
+
const colIdx = dataColNames.indexOf(active.colName);
|
|
156
|
+
if (e.key === "ArrowDown") {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
moveActive("down", active.rowIndex, active.colName);
|
|
159
|
+
}
|
|
160
|
+
else if (e.key === "ArrowUp") {
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
moveActive("up", active.rowIndex, active.colName);
|
|
163
|
+
}
|
|
164
|
+
else if (e.key === "ArrowRight" || (e.key === "Tab" && !e.shiftKey)) {
|
|
165
|
+
e.preventDefault();
|
|
166
|
+
moveActive("right", active.rowIndex, active.colName);
|
|
167
|
+
}
|
|
168
|
+
else if (e.key === "ArrowLeft" || (e.key === "Tab" && e.shiftKey)) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
moveActive("left", active.rowIndex, active.colName);
|
|
171
|
+
}
|
|
172
|
+
else if (e.key === "Enter" && colIdx >= 0) {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
onActiveChange({ ...active, editing: true });
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const totalWidth = table.getTotalSize();
|
|
178
|
+
return (_jsx(TooltipProvider, { delayDuration: 300, children: _jsx("div", { ref: containerRef, className: "relative flex-1 overflow-auto", tabIndex: 0, onKeyDown: onGridKeyDown, children: _jsxs("table", { className: "border-separate border-spacing-0 text-xs", style: { width: totalWidth, minWidth: "100%" }, children: [_jsx("thead", { className: "sticky top-0 z-20", children: table.getHeaderGroups().map((hg) => (_jsx("tr", { children: hg.headers.map((header) => (_jsxs("th", { style: { width: header.getSize() }, className: cn("relative h-9 border-b border-r border-border bg-muted/60 px-2 text-left align-middle font-medium text-muted-foreground backdrop-blur", header.column.id === SELECT_COL && "px-0 text-center"), children: [header.isPlaceholder
|
|
179
|
+
? null
|
|
180
|
+
: flexRender(header.column.columnDef.header, header.getContext()), header.column.getCanResize() && (_jsx("div", { onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), className: cn("absolute right-0 top-0 h-full w-1 cursor-col-resize select-none touch-none bg-transparent hover:bg-ring", header.column.getIsResizing() && "bg-ring") }))] }, header.id))) }, hg.id))) }), _jsx("tbody", { children: isLoading && rows.length === 0 ? (_jsx(SkeletonRows, { columnCount: schema.columns.length + 2, rows: Math.min(pageSize, 12) })) : rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: schema.columns.length + 2, className: "px-4 py-16 text-center text-muted-foreground", children: "No rows." }) })) : (table.getRowModel().rows.map((row) => (_jsx("tr", { className: cn("group/row hover:bg-muted/30", row.original.isNew && "bg-emerald-500/5", row.original.isDeleted &&
|
|
181
|
+
"bg-destructive/5 line-through opacity-60"), children: row.getVisibleCells().map((cell) => (_jsx("td", { style: { width: cell.column.getSize() }, className: cn("h-8 border-b border-r border-border p-0 align-middle", cell.column.id === SELECT_COL && "text-center"), children: cell.column.id === SELECT_COL ||
|
|
182
|
+
cell.column.id === ACTIONS_COL ? (_jsx("div", { className: "flex h-full items-center justify-center", children: flexRender(cell.column.columnDef.cell, cell.getContext()) })) : (flexRender(cell.column.columnDef.cell, cell.getContext())) }, cell.id))) }, row.id)))) })] }) }) }));
|
|
183
|
+
}
|
|
184
|
+
function defaultWidth(col) {
|
|
185
|
+
const kind = inferEditorKind(col);
|
|
186
|
+
if (kind === "boolean")
|
|
187
|
+
return 90;
|
|
188
|
+
if (kind === "uuid")
|
|
189
|
+
return 280;
|
|
190
|
+
if (kind === "json")
|
|
191
|
+
return 240;
|
|
192
|
+
if (kind === "timestamp")
|
|
193
|
+
return 180;
|
|
194
|
+
if (kind === "number")
|
|
195
|
+
return 110;
|
|
196
|
+
return 180;
|
|
197
|
+
}
|
|
198
|
+
function ColumnHeader({ column, fk, sortDir, onSort, }) {
|
|
199
|
+
return (_jsxs("button", { type: "button", onClick: onSort, className: "flex w-full items-center gap-1 overflow-hidden", children: [column.pk && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(IconKey, { className: "h-3 w-3 shrink-0 text-amber-500" }) }) }), _jsx(TooltipContent, { children: "Primary key" })] })), fk && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(IconExternalLink, { className: "h-3 w-3 shrink-0 text-primary/70" }) }) }), _jsxs(TooltipContent, { children: ["\u2192 ", fk.refTable, ".", fk.refColumn] })] })), _jsx("span", { className: "truncate font-medium text-foreground", children: column.name }), _jsx("span", { className: "rounded bg-background/60 px-1 font-mono text-[9px] font-normal text-muted-foreground", children: column.type }), _jsx("span", { className: "ml-auto shrink-0", children: sortDir === "asc" ? (_jsx(IconArrowUp, { className: "h-3 w-3" })) : sortDir === "desc" ? (_jsx(IconArrowDown, { className: "h-3 w-3" })) : null })] }));
|
|
200
|
+
}
|
|
201
|
+
function SkeletonRows({ columnCount, rows, }) {
|
|
202
|
+
return (_jsx(_Fragment, { children: Array.from({ length: rows }).map((_, r) => (_jsx("tr", { children: Array.from({ length: columnCount }).map((_, c) => (_jsx("td", { className: "h-8 border-b border-r border-border px-2", children: _jsx("div", { className: "h-3 w-3/4 animate-pulse rounded bg-muted" }) }, c))) }, r))) }));
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=DataGrid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataGrid.js","sourceRoot":"","sources":["../../../src/client/db-admin/DataGrid.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useRef } from \"react\";\nimport {\n flexRender,\n getCoreRowModel,\n useReactTable,\n type ColumnDef,\n type ColumnSizingState,\n} from \"@tanstack/react-table\";\nimport {\n IconKey,\n IconArrowUp,\n IconArrowDown,\n IconExternalLink,\n IconTrash,\n IconArrowBackUp,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../utils.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\nimport { EditableCell } from \"./EditableCell.js\";\nimport { inferEditorKind, type EditorKind } from \"./cell-format.js\";\nimport type {\n DbAdminColumn,\n DbAdminForeignKey,\n DbAdminSort,\n DbAdminTableSchema,\n} from \"../../db-admin/types.js\";\n\n/** A grid row pairs the displayed values with its stable pk string. */\nexport interface GridRow {\n pk: string;\n /** Values WITH staged edits already applied (for display). */\n values: Record<string, unknown>;\n isNew?: boolean;\n isDeleted?: boolean;\n /** Local id for new rows (so edits route to the right new-row). */\n localId?: string;\n}\n\n/** Identifies the focused cell for keyboard nav. */\nexport interface ActiveCell {\n rowIndex: number;\n colName: string;\n editing: boolean;\n}\n\nexport interface DataGridProps {\n schema: DbAdminTableSchema;\n rows: GridRow[];\n isLoading: boolean;\n pageSize: number;\n\n sort: DbAdminSort[];\n onSortChange: (sort: DbAdminSort[]) => void;\n\n selectedPks: Set<string>;\n onSelectionChange: (pks: Set<string>) => void;\n\n columnWidths: Record<string, number>;\n onColumnWidthsChange: (widths: Record<string, number>) => void;\n\n active: ActiveCell | null;\n onActiveChange: (active: ActiveCell | null) => void;\n\n /** Whether editing is permitted (table has a PK). */\n editable: boolean;\n\n /** Commit a staged cell edit. */\n onCellCommit: (row: GridRow, col: string, value: unknown) => void;\n /** Whether a given cell is dirty. */\n isCellDirty: (row: GridRow, col: string) => boolean;\n /** Toggle deletion staging for a single row. */\n onToggleDelete: (row: GridRow) => void;\n\n onNavigateToRow: (fk: DbAdminForeignKey, value: unknown) => void;\n}\n\nconst SELECT_COL = \"__select__\";\nconst ACTIONS_COL = \"__actions__\";\n\nexport function DataGrid(props: DataGridProps) {\n const {\n schema,\n rows,\n isLoading,\n pageSize,\n sort,\n onSortChange,\n selectedPks,\n onSelectionChange,\n columnWidths,\n onColumnWidthsChange,\n active,\n onActiveChange,\n editable,\n onCellCommit,\n isCellDirty,\n onToggleDelete,\n onNavigateToRow,\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n const fkByColumn = useMemo(() => {\n const map = new Map<string, DbAdminForeignKey>();\n for (const fk of schema.foreignKeys) map.set(fk.column, fk);\n return map;\n }, [schema.foreignKeys]);\n\n const kindByColumn = useMemo(() => {\n const map = new Map<string, EditorKind>();\n for (const col of schema.columns) map.set(col.name, inferEditorKind(col));\n return map;\n }, [schema.columns]);\n\n const allSelected =\n rows.length > 0 && rows.every((r) => selectedPks.has(r.pk));\n const someSelected = rows.some((r) => selectedPks.has(r.pk));\n\n const toggleAll = useCallback(() => {\n if (allSelected) onSelectionChange(new Set());\n else onSelectionChange(new Set(rows.map((r) => r.pk)));\n }, [allSelected, rows, onSelectionChange]);\n\n const toggleOne = useCallback(\n (pk: string) => {\n const next = new Set(selectedPks);\n if (next.has(pk)) next.delete(pk);\n else next.add(pk);\n onSelectionChange(next);\n },\n [selectedPks, onSelectionChange],\n );\n\n const cycleSort = useCallback(\n (colName: string) => {\n const current = sort.find((s) => s.column === colName);\n if (!current) onSortChange([{ column: colName, dir: \"asc\" }]);\n else if (current.dir === \"asc\")\n onSortChange([{ column: colName, dir: \"desc\" }]);\n else onSortChange([]);\n },\n [sort, onSortChange],\n );\n\n const columns = useMemo<ColumnDef<GridRow>[]>(() => {\n const selectCol: ColumnDef<GridRow> = {\n id: SELECT_COL,\n size: 40,\n enableResizing: false,\n header: () => (\n <input\n type=\"checkbox\"\n checked={allSelected}\n ref={(el) => {\n if (el) el.indeterminate = !allSelected && someSelected;\n }}\n onChange={toggleAll}\n className=\"h-3.5 w-3.5 cursor-pointer accent-primary\"\n aria-label=\"Select all rows\"\n />\n ),\n cell: ({ row }) => (\n <input\n type=\"checkbox\"\n checked={selectedPks.has(row.original.pk)}\n onChange={() => toggleOne(row.original.pk)}\n className=\"h-3.5 w-3.5 cursor-pointer accent-primary\"\n aria-label=\"Select row\"\n />\n ),\n };\n\n const dataCols: ColumnDef<GridRow>[] = schema.columns.map((col) => ({\n id: col.name,\n accessorFn: (r) => r.values[col.name],\n size: columnWidths[col.name] ?? defaultWidth(col),\n minSize: 60,\n header: () => (\n <ColumnHeader\n column={col}\n fk={fkByColumn.get(col.name)}\n sortDir={sort.find((s) => s.column === col.name)?.dir}\n onSort={() => cycleSort(col.name)}\n />\n ),\n cell: ({ row, getValue }) => {\n const value = getValue();\n const fk = fkByColumn.get(col.name);\n const isActive =\n active?.rowIndex === row.index && active.colName === col.name;\n return (\n <div className=\"group relative flex h-full items-center\">\n <EditableCell\n column={col}\n kind={kindByColumn.get(col.name) ?? \"text\"}\n value={value}\n editable={editable && !row.original.isDeleted}\n dirty={isCellDirty(row.original, col.name)}\n active={isActive}\n editing={isActive ? active.editing : false}\n onStartEdit={() =>\n onActiveChange({\n rowIndex: row.index,\n colName: col.name,\n editing: true,\n })\n }\n onCancelEdit={() =>\n onActiveChange({\n rowIndex: row.index,\n colName: col.name,\n editing: false,\n })\n }\n onCommit={(v) => {\n onCellCommit(row.original, col.name, v);\n onActiveChange({\n rowIndex: row.index,\n colName: col.name,\n editing: false,\n });\n }}\n onNavigate={(dir) => moveActive(dir, row.index, col.name)}\n />\n {fk && value !== null && value !== undefined && (\n <button\n type=\"button\"\n title={`Open ${fk.refTable}.${fk.refColumn}`}\n onClick={(e) => {\n e.stopPropagation();\n onNavigateToRow(fk, value);\n }}\n className=\"absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground/50 opacity-0 hover:text-primary group-hover:opacity-100\"\n >\n <IconExternalLink className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n );\n },\n }));\n\n const actionsCol: ColumnDef<GridRow> = {\n id: ACTIONS_COL,\n size: 44,\n enableResizing: false,\n header: () => null,\n cell: ({ row }) => (\n <div className=\"flex h-full items-center justify-center\">\n <button\n type=\"button\"\n title={row.original.isDeleted ? \"Undo delete\" : \"Delete row\"}\n onClick={() => onToggleDelete(row.original)}\n disabled={!editable}\n className={cn(\n \"rounded p-1 text-muted-foreground/50 hover:text-destructive disabled:opacity-30\",\n row.original.isDeleted && \"text-destructive\",\n )}\n >\n {row.original.isDeleted ? (\n <IconArrowBackUp className=\"h-3.5 w-3.5\" />\n ) : (\n <IconTrash className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n ),\n };\n\n return [selectCol, ...dataCols, actionsCol];\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n schema.columns,\n columnWidths,\n fkByColumn,\n kindByColumn,\n sort,\n active,\n selectedPks,\n allSelected,\n someSelected,\n editable,\n ]);\n\n const moveActive = useCallback(\n (\n dir: \"up\" | \"down\" | \"left\" | \"right\",\n rowIndex: number,\n colName: string,\n ) => {\n const dataColNames = schema.columns.map((c) => c.name);\n const colIdx = dataColNames.indexOf(colName);\n let nextRow = rowIndex;\n let nextCol = colIdx;\n if (dir === \"down\") nextRow = Math.min(rows.length - 1, rowIndex + 1);\n else if (dir === \"up\") nextRow = Math.max(0, rowIndex - 1);\n else if (dir === \"right\")\n nextCol = Math.min(dataColNames.length - 1, colIdx + 1);\n else if (dir === \"left\") nextCol = Math.max(0, colIdx - 1);\n onActiveChange({\n rowIndex: nextRow,\n colName: dataColNames[nextCol],\n editing: false,\n });\n },\n [schema.columns, rows.length, onActiveChange],\n );\n\n const sizingState: ColumnSizingState = useMemo(() => {\n const out: ColumnSizingState = {};\n for (const [k, v] of Object.entries(columnWidths)) out[k] = v;\n return out;\n }, [columnWidths]);\n\n const table = useReactTable({\n data: rows,\n columns,\n getCoreRowModel: getCoreRowModel(),\n columnResizeMode: \"onChange\",\n state: { columnSizing: sizingState },\n onColumnSizingChange: (updater) => {\n const next =\n typeof updater === \"function\" ? updater(sizingState) : updater;\n onColumnWidthsChange(next as Record<string, number>);\n },\n getRowId: (r) => r.pk,\n });\n\n const onGridKeyDown = (e: React.KeyboardEvent) => {\n if (!active || active.editing) return;\n const dataColNames = schema.columns.map((c) => c.name);\n const colIdx = dataColNames.indexOf(active.colName);\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n moveActive(\"down\", active.rowIndex, active.colName);\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n moveActive(\"up\", active.rowIndex, active.colName);\n } else if (e.key === \"ArrowRight\" || (e.key === \"Tab\" && !e.shiftKey)) {\n e.preventDefault();\n moveActive(\"right\", active.rowIndex, active.colName);\n } else if (e.key === \"ArrowLeft\" || (e.key === \"Tab\" && e.shiftKey)) {\n e.preventDefault();\n moveActive(\"left\", active.rowIndex, active.colName);\n } else if (e.key === \"Enter\" && colIdx >= 0) {\n e.preventDefault();\n onActiveChange({ ...active, editing: true });\n }\n };\n\n const totalWidth = table.getTotalSize();\n\n return (\n <TooltipProvider delayDuration={300}>\n <div\n ref={containerRef}\n className=\"relative flex-1 overflow-auto\"\n tabIndex={0}\n onKeyDown={onGridKeyDown}\n >\n <table\n className=\"border-separate border-spacing-0 text-xs\"\n style={{ width: totalWidth, minWidth: \"100%\" }}\n >\n <thead className=\"sticky top-0 z-20\">\n {table.getHeaderGroups().map((hg) => (\n <tr key={hg.id}>\n {hg.headers.map((header) => (\n <th\n key={header.id}\n style={{ width: header.getSize() }}\n className={cn(\n \"relative h-9 border-b border-r border-border bg-muted/60 px-2 text-left align-middle font-medium text-muted-foreground backdrop-blur\",\n header.column.id === SELECT_COL && \"px-0 text-center\",\n )}\n >\n {header.isPlaceholder\n ? null\n : flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n {header.column.getCanResize() && (\n <div\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n className={cn(\n \"absolute right-0 top-0 h-full w-1 cursor-col-resize select-none touch-none bg-transparent hover:bg-ring\",\n header.column.getIsResizing() && \"bg-ring\",\n )}\n />\n )}\n </th>\n ))}\n </tr>\n ))}\n </thead>\n <tbody>\n {isLoading && rows.length === 0 ? (\n <SkeletonRows\n columnCount={schema.columns.length + 2}\n rows={Math.min(pageSize, 12)}\n />\n ) : rows.length === 0 ? (\n <tr>\n <td\n colSpan={schema.columns.length + 2}\n className=\"px-4 py-16 text-center text-muted-foreground\"\n >\n No rows.\n </td>\n </tr>\n ) : (\n table.getRowModel().rows.map((row) => (\n <tr\n key={row.id}\n className={cn(\n \"group/row hover:bg-muted/30\",\n row.original.isNew && \"bg-emerald-500/5\",\n row.original.isDeleted &&\n \"bg-destructive/5 line-through opacity-60\",\n )}\n >\n {row.getVisibleCells().map((cell) => (\n <td\n key={cell.id}\n style={{ width: cell.column.getSize() }}\n className={cn(\n \"h-8 border-b border-r border-border p-0 align-middle\",\n cell.column.id === SELECT_COL && \"text-center\",\n )}\n >\n {cell.column.id === SELECT_COL ||\n cell.column.id === ACTIONS_COL ? (\n <div className=\"flex h-full items-center justify-center\">\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext(),\n )}\n </div>\n ) : (\n flexRender(\n cell.column.columnDef.cell,\n cell.getContext(),\n )\n )}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n </TooltipProvider>\n );\n}\n\nfunction defaultWidth(col: DbAdminColumn): number {\n const kind = inferEditorKind(col);\n if (kind === \"boolean\") return 90;\n if (kind === \"uuid\") return 280;\n if (kind === \"json\") return 240;\n if (kind === \"timestamp\") return 180;\n if (kind === \"number\") return 110;\n return 180;\n}\n\nfunction ColumnHeader({\n column,\n fk,\n sortDir,\n onSort,\n}: {\n column: DbAdminColumn;\n fk?: DbAdminForeignKey;\n sortDir?: \"asc\" | \"desc\";\n onSort: () => void;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onSort}\n className=\"flex w-full items-center gap-1 overflow-hidden\"\n >\n {column.pk && (\n <Tooltip>\n <TooltipTrigger asChild>\n <span>\n <IconKey className=\"h-3 w-3 shrink-0 text-amber-500\" />\n </span>\n </TooltipTrigger>\n <TooltipContent>Primary key</TooltipContent>\n </Tooltip>\n )}\n {fk && (\n <Tooltip>\n <TooltipTrigger asChild>\n <span>\n <IconExternalLink className=\"h-3 w-3 shrink-0 text-primary/70\" />\n </span>\n </TooltipTrigger>\n <TooltipContent>\n → {fk.refTable}.{fk.refColumn}\n </TooltipContent>\n </Tooltip>\n )}\n <span className=\"truncate font-medium text-foreground\">\n {column.name}\n </span>\n <span className=\"rounded bg-background/60 px-1 font-mono text-[9px] font-normal text-muted-foreground\">\n {column.type}\n </span>\n <span className=\"ml-auto shrink-0\">\n {sortDir === \"asc\" ? (\n <IconArrowUp className=\"h-3 w-3\" />\n ) : sortDir === \"desc\" ? (\n <IconArrowDown className=\"h-3 w-3\" />\n ) : null}\n </span>\n </button>\n );\n}\n\nfunction SkeletonRows({\n columnCount,\n rows,\n}: {\n columnCount: number;\n rows: number;\n}) {\n return (\n <>\n {Array.from({ length: rows }).map((_, r) => (\n <tr key={r}>\n {Array.from({ length: columnCount }).map((_, c) => (\n <td key={c} className=\"h-8 border-b border-r border-border px-2\">\n <div className=\"h-3 w-3/4 animate-pulse rounded bg-muted\" />\n </td>\n ))}\n </tr>\n ))}\n </>\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DbAdminPage.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/DbAdminPage.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,WAAW,4CAsH1B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Dev-mode database admin page — the shell that hosts the table browser, the
|
|
4
|
+
* table editor, and the SQL editor.
|
|
5
|
+
*
|
|
6
|
+
* Gated to development mode: when the app is not running in a dev environment
|
|
7
|
+
* (`canToggle` is false) we render a clean notice instead of the tool. The
|
|
8
|
+
* backend also enforces this with a 403, so this is purely a friendlier UX.
|
|
9
|
+
*/
|
|
10
|
+
import { useEffect, useMemo, useState } from "react";
|
|
11
|
+
import { IconDatabase, IconLoader2 } from "@tabler/icons-react";
|
|
12
|
+
import { cn } from "../utils.js";
|
|
13
|
+
import { useDevMode } from "../use-dev-mode.js";
|
|
14
|
+
import { useOverview } from "./useDbAdmin.js";
|
|
15
|
+
import { TableBrowser } from "./TableBrowser.js";
|
|
16
|
+
import { useDbAdminAgentSync, useNavigateConsumer } from "./useAgentSync.js";
|
|
17
|
+
import { TableEditor } from "./TableEditor.js";
|
|
18
|
+
import { SqlEditor } from "./SqlEditor.js";
|
|
19
|
+
const DIALECT_LABEL = {
|
|
20
|
+
postgres: "Postgres",
|
|
21
|
+
sqlite: "SQLite",
|
|
22
|
+
d1: "Cloudflare D1",
|
|
23
|
+
};
|
|
24
|
+
export function DbAdminPage() {
|
|
25
|
+
const { canToggle, isLoading: devLoading } = useDevMode();
|
|
26
|
+
const { data: overview, isLoading: overviewLoading } = useOverview();
|
|
27
|
+
const [selectedTable, setSelectedTable] = useState(null);
|
|
28
|
+
const [mode, setMode] = useState("table");
|
|
29
|
+
const [fkFilters, setFkFilters] = useState(undefined);
|
|
30
|
+
const tables = overview?.tables ?? [];
|
|
31
|
+
const dialect = overview?.dialect ?? "sqlite";
|
|
32
|
+
// Default selection to the first table once the overview loads.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (selectedTable === null && tables.length > 0) {
|
|
35
|
+
setSelectedTable(tables[0].name);
|
|
36
|
+
}
|
|
37
|
+
}, [selectedTable, tables]);
|
|
38
|
+
// Keep the agent's <current-screen> in sync, and let it drive navigation.
|
|
39
|
+
useDbAdminAgentSync({ table: selectedTable, mode });
|
|
40
|
+
useNavigateConsumer((table) => {
|
|
41
|
+
setSelectedTable(table);
|
|
42
|
+
setMode("table");
|
|
43
|
+
setFkFilters(undefined);
|
|
44
|
+
});
|
|
45
|
+
const tableNames = useMemo(() => tables.map((t) => t.name), [tables]);
|
|
46
|
+
// SqlEditor degrades gracefully without per-table columns; pass an empty map.
|
|
47
|
+
// (Table-name autocomplete still works; column autocomplete fills in lazily.)
|
|
48
|
+
const columnsByTable = useMemo(() => ({}), []);
|
|
49
|
+
// ─── Dev gate ──────────────────────────────────────────────────────────
|
|
50
|
+
if (!devLoading && !canToggle) {
|
|
51
|
+
return (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-background p-6", children: _jsxs("div", { className: "flex max-w-md flex-col items-center rounded-lg border bg-card p-8 text-center shadow-sm", children: [_jsx("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: _jsx(IconDatabase, { className: "h-6 w-6 text-muted-foreground", stroke: 1.75 }) }), _jsx("h2", { className: "text-base font-semibold text-foreground", children: "Development mode only" }), _jsx("p", { className: "mt-1.5 text-sm text-muted-foreground", children: "Database admin is available in development mode only." })] }) }));
|
|
52
|
+
}
|
|
53
|
+
const showInitialLoading = (devLoading || overviewLoading) && !overview;
|
|
54
|
+
return (_jsxs("div", { className: "flex h-full w-full flex-col bg-background text-foreground", children: [_jsxs("header", { className: "flex h-12 shrink-0 items-center gap-3 border-b px-4", children: [_jsx(IconDatabase, { className: "h-5 w-5 text-muted-foreground", stroke: 1.75 }), _jsx("span", { className: "text-sm font-semibold", children: "Database" }), _jsx("span", { className: "inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground", children: DIALECT_LABEL[dialect] ?? dialect }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [tables.length, " ", tables.length === 1 ? "table" : "tables"] })] }), _jsxs("div", { className: "flex min-h-0 flex-1", children: [_jsx("aside", { className: "w-[260px] shrink-0 border-r", children: showInitialLoading ? (_jsx(SidebarSkeleton, {})) : (_jsx(TableBrowser, { tables: tables, selected: selectedTable, onSelect: (t) => {
|
|
55
|
+
setSelectedTable(t);
|
|
56
|
+
setFkFilters(undefined);
|
|
57
|
+
}, mode: mode, onModeChange: setMode })) }), _jsx("main", { className: "min-w-0 flex-1 overflow-hidden", children: showInitialLoading ? (_jsx(MainLoading, {})) : mode === "sql" ? (_jsx(SqlEditor, { dialect: dialect, tableNames: tableNames, columnsByTable: columnsByTable })) : selectedTable ? (_jsx(TableEditor, { table: selectedTable, dialect: dialect, initialFilters: fkFilters, onNavigateToRow: (t, filters) => {
|
|
58
|
+
setSelectedTable(t);
|
|
59
|
+
setMode("table");
|
|
60
|
+
setFkFilters(filters);
|
|
61
|
+
} }, selectedTable)) : (_jsx(NoTableSelected, {})) })] })] }));
|
|
62
|
+
}
|
|
63
|
+
function SidebarSkeleton() {
|
|
64
|
+
return (_jsxs("div", { className: "flex h-full flex-col bg-card p-2", children: [_jsx("div", { className: "mb-2 h-9 animate-pulse rounded-md bg-muted" }), _jsx("div", { className: "mb-3 h-9 animate-pulse rounded-md bg-muted" }), _jsx("div", { className: "space-y-1.5", children: Array.from({ length: 8 }).map((_, i) => (_jsx("div", { className: "h-8 animate-pulse rounded-md bg-muted", style: { opacity: 1 - i * 0.08 } }, i))) })] }));
|
|
65
|
+
}
|
|
66
|
+
function MainLoading() {
|
|
67
|
+
return (_jsx("div", { className: "flex h-full w-full items-center justify-center", children: _jsx(IconLoader2, { className: cn("h-5 w-5 animate-spin text-muted-foreground") }) }));
|
|
68
|
+
}
|
|
69
|
+
function NoTableSelected() {
|
|
70
|
+
return (_jsx("div", { className: "flex h-full w-full items-center justify-center p-6 text-center", children: _jsxs("div", { className: "flex flex-col items-center", children: [_jsx(IconDatabase, { className: "mb-3 h-8 w-8 text-muted-foreground/50", stroke: 1.5 }), _jsx("p", { className: "text-sm font-medium text-foreground", children: "No table selected" }), _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Pick a table from the sidebar to browse and edit its rows." })] }) }));
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=DbAdminPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DbAdminPage.js","sourceRoot":"","sources":["../../../src/client/db-admin/DbAdminPage.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Dev-mode database admin page — the shell that hosts the table browser, the\n * table editor, and the SQL editor.\n *\n * Gated to development mode: when the app is not running in a dev environment\n * (`canToggle` is false) we render a clean notice instead of the tool. The\n * backend also enforces this with a 403, so this is purely a friendlier UX.\n */\nimport { useEffect, useMemo, useState } from \"react\";\nimport { IconDatabase, IconLoader2 } from \"@tabler/icons-react\";\nimport { cn } from \"../utils.js\";\nimport { useDevMode } from \"../use-dev-mode.js\";\nimport type { DbAdminFilter, DbAdminColumn } from \"../../db-admin/types.js\";\nimport { useOverview } from \"./useDbAdmin.js\";\nimport { TableBrowser } from \"./TableBrowser.js\";\nimport { useDbAdminAgentSync, useNavigateConsumer } from \"./useAgentSync.js\";\nimport { TableEditor } from \"./TableEditor.js\";\nimport { SqlEditor } from \"./SqlEditor.js\";\n\nconst DIALECT_LABEL: Record<string, string> = {\n postgres: \"Postgres\",\n sqlite: \"SQLite\",\n d1: \"Cloudflare D1\",\n};\n\nexport function DbAdminPage() {\n const { canToggle, isLoading: devLoading } = useDevMode();\n const { data: overview, isLoading: overviewLoading } = useOverview();\n\n const [selectedTable, setSelectedTable] = useState<string | null>(null);\n const [mode, setMode] = useState<\"table\" | \"sql\">(\"table\");\n const [fkFilters, setFkFilters] = useState<DbAdminFilter[] | undefined>(\n undefined,\n );\n\n const tables = overview?.tables ?? [];\n const dialect = overview?.dialect ?? \"sqlite\";\n\n // Default selection to the first table once the overview loads.\n useEffect(() => {\n if (selectedTable === null && tables.length > 0) {\n setSelectedTable(tables[0].name);\n }\n }, [selectedTable, tables]);\n\n // Keep the agent's <current-screen> in sync, and let it drive navigation.\n useDbAdminAgentSync({ table: selectedTable, mode });\n useNavigateConsumer((table) => {\n setSelectedTable(table);\n setMode(\"table\");\n setFkFilters(undefined);\n });\n\n const tableNames = useMemo(() => tables.map((t) => t.name), [tables]);\n // SqlEditor degrades gracefully without per-table columns; pass an empty map.\n // (Table-name autocomplete still works; column autocomplete fills in lazily.)\n const columnsByTable = useMemo<Record<string, string[]>>(() => ({}), []);\n\n // ─── Dev gate ──────────────────────────────────────────────────────────\n if (!devLoading && !canToggle) {\n return (\n <div className=\"flex h-full w-full items-center justify-center bg-background p-6\">\n <div className=\"flex max-w-md flex-col items-center rounded-lg border bg-card p-8 text-center shadow-sm\">\n <div className=\"mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted\">\n <IconDatabase\n className=\"h-6 w-6 text-muted-foreground\"\n stroke={1.75}\n />\n </div>\n <h2 className=\"text-base font-semibold text-foreground\">\n Development mode only\n </h2>\n <p className=\"mt-1.5 text-sm text-muted-foreground\">\n Database admin is available in development mode only.\n </p>\n </div>\n </div>\n );\n }\n\n const showInitialLoading = (devLoading || overviewLoading) && !overview;\n\n return (\n <div className=\"flex h-full w-full flex-col bg-background text-foreground\">\n {/* Header */}\n <header className=\"flex h-12 shrink-0 items-center gap-3 border-b px-4\">\n <IconDatabase className=\"h-5 w-5 text-muted-foreground\" stroke={1.75} />\n <span className=\"text-sm font-semibold\">Database</span>\n <span className=\"inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground\">\n {DIALECT_LABEL[dialect] ?? dialect}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {tables.length} {tables.length === 1 ? \"table\" : \"tables\"}\n </span>\n </header>\n\n {/* Body: fixed sidebar + flexible main */}\n <div className=\"flex min-h-0 flex-1\">\n <aside className=\"w-[260px] shrink-0 border-r\">\n {showInitialLoading ? (\n <SidebarSkeleton />\n ) : (\n <TableBrowser\n tables={tables}\n selected={selectedTable}\n onSelect={(t) => {\n setSelectedTable(t);\n setFkFilters(undefined);\n }}\n mode={mode}\n onModeChange={setMode}\n />\n )}\n </aside>\n\n <main className=\"min-w-0 flex-1 overflow-hidden\">\n {showInitialLoading ? (\n <MainLoading />\n ) : mode === \"sql\" ? (\n <SqlEditor\n dialect={dialect}\n tableNames={tableNames}\n columnsByTable={columnsByTable}\n />\n ) : selectedTable ? (\n <TableEditor\n key={selectedTable}\n table={selectedTable}\n dialect={dialect}\n initialFilters={fkFilters}\n onNavigateToRow={(t, filters) => {\n setSelectedTable(t);\n setMode(\"table\");\n setFkFilters(filters);\n }}\n />\n ) : (\n <NoTableSelected />\n )}\n </main>\n </div>\n </div>\n );\n}\n\nfunction SidebarSkeleton() {\n return (\n <div className=\"flex h-full flex-col bg-card p-2\">\n <div className=\"mb-2 h-9 animate-pulse rounded-md bg-muted\" />\n <div className=\"mb-3 h-9 animate-pulse rounded-md bg-muted\" />\n <div className=\"space-y-1.5\">\n {Array.from({ length: 8 }).map((_, i) => (\n <div\n key={i}\n className=\"h-8 animate-pulse rounded-md bg-muted\"\n style={{ opacity: 1 - i * 0.08 }}\n />\n ))}\n </div>\n </div>\n );\n}\n\nfunction MainLoading() {\n return (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconLoader2\n className={cn(\"h-5 w-5 animate-spin text-muted-foreground\")}\n />\n </div>\n );\n}\n\nfunction NoTableSelected() {\n return (\n <div className=\"flex h-full w-full items-center justify-center p-6 text-center\">\n <div className=\"flex flex-col items-center\">\n <IconDatabase\n className=\"mb-3 h-8 w-8 text-muted-foreground/50\"\n stroke={1.5}\n />\n <p className=\"text-sm font-medium text-foreground\">No table selected</p>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n Pick a table from the sidebar to browse and edit its rows.\n </p>\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DevDatabaseLinkProps {
|
|
2
|
+
className?: string;
|
|
3
|
+
/** Route path for the DB admin page. Defaults to `/database`. */
|
|
4
|
+
to?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Development-only entry point to the database admin.
|
|
8
|
+
*
|
|
9
|
+
* Renders a compact footer link (designed to sit next to `FeedbackButton` /
|
|
10
|
+
* `OrgSwitcher` in a template's sidebar footer) ONLY when the app is running in
|
|
11
|
+
* a development environment (`useDevMode().canToggle`). In production it renders
|
|
12
|
+
* nothing, so it is safe to drop into every template's chrome unconditionally.
|
|
13
|
+
*
|
|
14
|
+
* The page it links to (`/database`) and its backing routes are independently
|
|
15
|
+
* gated to `NODE_ENV === "development"` on the server, so this is purely a
|
|
16
|
+
* convenience affordance — never a security boundary.
|
|
17
|
+
*/
|
|
18
|
+
export declare function DevDatabaseLink({ className, to, }: DevDatabaseLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=DevDatabaseLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevDatabaseLink.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/DevDatabaseLink.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,EAAgB,GACjB,EAAE,oBAAoB,2CAmBtB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Link } from "react-router";
|
|
3
|
+
import { IconDatabase } from "@tabler/icons-react";
|
|
4
|
+
import { useDevMode } from "../use-dev-mode.js";
|
|
5
|
+
import { appPath } from "../api-path.js";
|
|
6
|
+
import { cn } from "../utils.js";
|
|
7
|
+
/**
|
|
8
|
+
* Development-only entry point to the database admin.
|
|
9
|
+
*
|
|
10
|
+
* Renders a compact footer link (designed to sit next to `FeedbackButton` /
|
|
11
|
+
* `OrgSwitcher` in a template's sidebar footer) ONLY when the app is running in
|
|
12
|
+
* a development environment (`useDevMode().canToggle`). In production it renders
|
|
13
|
+
* nothing, so it is safe to drop into every template's chrome unconditionally.
|
|
14
|
+
*
|
|
15
|
+
* The page it links to (`/database`) and its backing routes are independently
|
|
16
|
+
* gated to `NODE_ENV === "development"` on the server, so this is purely a
|
|
17
|
+
* convenience affordance — never a security boundary.
|
|
18
|
+
*/
|
|
19
|
+
export function DevDatabaseLink({ className, to = "/database", }) {
|
|
20
|
+
const { canToggle } = useDevMode();
|
|
21
|
+
if (!canToggle)
|
|
22
|
+
return null;
|
|
23
|
+
return (_jsxs(Link, { to: appPath(to), title: "Database admin \u2014 development only", className: cn("flex w-full items-center gap-2 rounded-md border border-border/50 px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring", className), children: [_jsx(IconDatabase, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "flex-1 truncate text-left", children: "Database" }), _jsx("span", { className: "rounded bg-muted px-1 py-0.5 text-[9px] uppercase tracking-wide text-muted-foreground", children: "dev" })] }));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=DevDatabaseLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevDatabaseLink.js","sourceRoot":"","sources":["../../../src/client/db-admin/DevDatabaseLink.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAQjC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,SAAS,EACT,EAAE,GAAG,WAAW,GACK;IACrB,MAAM,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,CAAC;IACnC,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,CACL,MAAC,IAAI,IACH,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,EACf,KAAK,EAAC,wCAAmC,EACzC,SAAS,EAAE,EAAE,CACX,oOAAoO,EACpO,SAAS,CACV,aAED,KAAC,YAAY,IAAC,SAAS,EAAC,sBAAsB,GAAG,EACjD,eAAM,SAAS,EAAC,2BAA2B,yBAAgB,EAC3D,eAAM,SAAS,EAAC,uFAAuF,oBAEhG,IACF,CACR,CAAC;AACJ,CAAC","sourcesContent":["import { Link } from \"react-router\";\nimport { IconDatabase } from \"@tabler/icons-react\";\nimport { useDevMode } from \"../use-dev-mode.js\";\nimport { appPath } from \"../api-path.js\";\nimport { cn } from \"../utils.js\";\n\nexport interface DevDatabaseLinkProps {\n className?: string;\n /** Route path for the DB admin page. Defaults to `/database`. */\n to?: string;\n}\n\n/**\n * Development-only entry point to the database admin.\n *\n * Renders a compact footer link (designed to sit next to `FeedbackButton` /\n * `OrgSwitcher` in a template's sidebar footer) ONLY when the app is running in\n * a development environment (`useDevMode().canToggle`). In production it renders\n * nothing, so it is safe to drop into every template's chrome unconditionally.\n *\n * The page it links to (`/database`) and its backing routes are independently\n * gated to `NODE_ENV === \"development\"` on the server, so this is purely a\n * convenience affordance — never a security boundary.\n */\nexport function DevDatabaseLink({\n className,\n to = \"/database\",\n}: DevDatabaseLinkProps) {\n const { canToggle } = useDevMode();\n if (!canToggle) return null;\n return (\n <Link\n to={appPath(to)}\n title=\"Database admin — development only\"\n className={cn(\n \"flex w-full items-center gap-2 rounded-md border border-border/50 px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n >\n <IconDatabase className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"flex-1 truncate text-left\">Database</span>\n <span className=\"rounded bg-muted px-1 py-0.5 text-[9px] uppercase tracking-wide text-muted-foreground\">\n dev\n </span>\n </Link>\n );\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type EditorKind } from "./cell-format.js";
|
|
2
|
+
import type { DbAdminColumn } from "../../db-admin/types.js";
|
|
3
|
+
export interface EditableCellProps {
|
|
4
|
+
column: DbAdminColumn;
|
|
5
|
+
kind: EditorKind;
|
|
6
|
+
value: unknown;
|
|
7
|
+
/** Whether this cell holds a staged (uncommitted) edit. */
|
|
8
|
+
dirty?: boolean;
|
|
9
|
+
/** Whether editing is allowed (false when the table has no PK). */
|
|
10
|
+
editable?: boolean;
|
|
11
|
+
/** Whether this cell is the keyboard-focused/active cell in the grid. */
|
|
12
|
+
active?: boolean;
|
|
13
|
+
/** True if the editor should open immediately (e.g. typing began). */
|
|
14
|
+
editing?: boolean;
|
|
15
|
+
/** Commit a new value into the changeset. */
|
|
16
|
+
onCommit: (value: unknown) => void;
|
|
17
|
+
/** Request entering edit mode. */
|
|
18
|
+
onStartEdit?: () => void;
|
|
19
|
+
/** Request leaving edit mode without committing. */
|
|
20
|
+
onCancelEdit?: () => void;
|
|
21
|
+
/** Move focus after Enter ("down") or Tab ("right"). */
|
|
22
|
+
onNavigate?: (dir: "up" | "down" | "left" | "right") => void;
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function EditableCell({ column, kind, value, dirty, editable, active, editing, onCommit, onStartEdit, onCancelEdit, onNavigate, className, }: EditableCellProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=EditableCell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditableCell.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/EditableCell.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,UAAU,EAQhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wDAAwD;IACxD,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,IAAI,EACJ,KAAK,EACL,KAAK,EACL,QAAe,EACf,MAAM,EACN,OAAO,EACP,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,UAAU,EACV,SAAS,GACV,EAAE,iBAAiB,2CA4FnB"}
|