@agent-native/core 0.26.9 → 0.28.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.
Files changed (225) hide show
  1. package/dist/agent/run-ownership.d.ts +12 -0
  2. package/dist/agent/run-ownership.d.ts.map +1 -0
  3. package/dist/agent/run-ownership.js +39 -0
  4. package/dist/agent/run-ownership.js.map +1 -0
  5. package/dist/client/AgentPanel.d.ts.map +1 -1
  6. package/dist/client/AgentPanel.js +1 -0
  7. package/dist/client/AgentPanel.js.map +1 -1
  8. package/dist/client/AssistantChat.d.ts.map +1 -1
  9. package/dist/client/AssistantChat.js +9 -6
  10. package/dist/client/AssistantChat.js.map +1 -1
  11. package/dist/client/db-admin/DataGrid.d.ts +42 -0
  12. package/dist/client/db-admin/DataGrid.d.ts.map +1 -0
  13. package/dist/client/db-admin/DataGrid.js +204 -0
  14. package/dist/client/db-admin/DataGrid.js.map +1 -0
  15. package/dist/client/db-admin/DbAdminPage.d.ts +2 -0
  16. package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -0
  17. package/dist/client/db-admin/DbAdminPage.js +72 -0
  18. package/dist/client/db-admin/DbAdminPage.js.map +1 -0
  19. package/dist/client/db-admin/DevDatabaseLink.d.ts +19 -0
  20. package/dist/client/db-admin/DevDatabaseLink.d.ts.map +1 -0
  21. package/dist/client/db-admin/DevDatabaseLink.js +25 -0
  22. package/dist/client/db-admin/DevDatabaseLink.js.map +1 -0
  23. package/dist/client/db-admin/EditableCell.d.ts +26 -0
  24. package/dist/client/db-admin/EditableCell.d.ts.map +1 -0
  25. package/dist/client/db-admin/EditableCell.js +150 -0
  26. package/dist/client/db-admin/EditableCell.js.map +1 -0
  27. package/dist/client/db-admin/FilterBar.d.ts +8 -0
  28. package/dist/client/db-admin/FilterBar.d.ts.map +1 -0
  29. package/dist/client/db-admin/FilterBar.js +68 -0
  30. package/dist/client/db-admin/FilterBar.js.map +1 -0
  31. package/dist/client/db-admin/ResultsGrid.d.ts +6 -0
  32. package/dist/client/db-admin/ResultsGrid.d.ts.map +1 -0
  33. package/dist/client/db-admin/ResultsGrid.js +41 -0
  34. package/dist/client/db-admin/ResultsGrid.js.map +1 -0
  35. package/dist/client/db-admin/RowSidePanel.d.ts +18 -0
  36. package/dist/client/db-admin/RowSidePanel.d.ts.map +1 -0
  37. package/dist/client/db-admin/RowSidePanel.js +104 -0
  38. package/dist/client/db-admin/RowSidePanel.js.map +1 -0
  39. package/dist/client/db-admin/SqlEditor.d.ts +8 -0
  40. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -0
  41. package/dist/client/db-admin/SqlEditor.js +350 -0
  42. package/dist/client/db-admin/SqlEditor.js.map +1 -0
  43. package/dist/client/db-admin/TableBrowser.d.ts +10 -0
  44. package/dist/client/db-admin/TableBrowser.d.ts.map +1 -0
  45. package/dist/client/db-admin/TableBrowser.js +61 -0
  46. package/dist/client/db-admin/TableBrowser.js.map +1 -0
  47. package/dist/client/db-admin/TableEditor.d.ts +9 -0
  48. package/dist/client/db-admin/TableEditor.d.ts.map +1 -0
  49. package/dist/client/db-admin/TableEditor.js +254 -0
  50. package/dist/client/db-admin/TableEditor.js.map +1 -0
  51. package/dist/client/db-admin/cell-format.d.ts +55 -0
  52. package/dist/client/db-admin/cell-format.d.ts.map +1 -0
  53. package/dist/client/db-admin/cell-format.js +223 -0
  54. package/dist/client/db-admin/cell-format.js.map +1 -0
  55. package/dist/client/db-admin/changeset.d.ts +74 -0
  56. package/dist/client/db-admin/changeset.d.ts.map +1 -0
  57. package/dist/client/db-admin/changeset.js +169 -0
  58. package/dist/client/db-admin/changeset.js.map +1 -0
  59. package/dist/client/db-admin/export-utils.d.ts +15 -0
  60. package/dist/client/db-admin/export-utils.d.ts.map +1 -0
  61. package/dist/client/db-admin/export-utils.js +62 -0
  62. package/dist/client/db-admin/export-utils.js.map +1 -0
  63. package/dist/client/db-admin/index.d.ts +7 -0
  64. package/dist/client/db-admin/index.d.ts.map +1 -0
  65. package/dist/client/db-admin/index.js +8 -0
  66. package/dist/client/db-admin/index.js.map +1 -0
  67. package/dist/client/db-admin/sql-storage.d.ts +35 -0
  68. package/dist/client/db-admin/sql-storage.d.ts.map +1 -0
  69. package/dist/client/db-admin/sql-storage.js +117 -0
  70. package/dist/client/db-admin/sql-storage.js.map +1 -0
  71. package/dist/client/db-admin/storage.d.ts +24 -0
  72. package/dist/client/db-admin/storage.d.ts.map +1 -0
  73. package/dist/client/db-admin/storage.js +50 -0
  74. package/dist/client/db-admin/storage.js.map +1 -0
  75. package/dist/client/db-admin/useAgentSync.d.ts +22 -0
  76. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -0
  77. package/dist/client/db-admin/useAgentSync.js +120 -0
  78. package/dist/client/db-admin/useAgentSync.js.map +1 -0
  79. package/dist/client/db-admin/useDbAdmin.d.ts +20 -0
  80. package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -0
  81. package/dist/client/db-admin/useDbAdmin.js +154 -0
  82. package/dist/client/db-admin/useDbAdmin.js.map +1 -0
  83. package/dist/client/index.d.ts +2 -1
  84. package/dist/client/index.d.ts.map +1 -1
  85. package/dist/client/index.js +2 -1
  86. package/dist/client/index.js.map +1 -1
  87. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  88. package/dist/client/settings/SettingsPanel.js +8 -4
  89. package/dist/client/settings/SettingsPanel.js.map +1 -1
  90. package/dist/client/use-dev-mode.d.ts +20 -2
  91. package/dist/client/use-dev-mode.d.ts.map +1 -1
  92. package/dist/client/use-dev-mode.js +49 -14
  93. package/dist/client/use-dev-mode.js.map +1 -1
  94. package/dist/credentials/index.d.ts.map +1 -1
  95. package/dist/credentials/index.js +25 -5
  96. package/dist/credentials/index.js.map +1 -1
  97. package/dist/db-admin/agent-tools.d.ts +15 -0
  98. package/dist/db-admin/agent-tools.d.ts.map +1 -0
  99. package/dist/db-admin/agent-tools.js +147 -0
  100. package/dist/db-admin/agent-tools.js.map +1 -0
  101. package/dist/db-admin/operations.d.ts +17 -0
  102. package/dist/db-admin/operations.d.ts.map +1 -0
  103. package/dist/db-admin/operations.js +541 -0
  104. package/dist/db-admin/operations.js.map +1 -0
  105. package/dist/db-admin/routes.d.ts +5 -0
  106. package/dist/db-admin/routes.d.ts.map +1 -0
  107. package/dist/db-admin/routes.js +134 -0
  108. package/dist/db-admin/routes.js.map +1 -0
  109. package/dist/db-admin/types.d.ts +85 -0
  110. package/dist/db-admin/types.d.ts.map +1 -0
  111. package/dist/db-admin/types.js +9 -0
  112. package/dist/db-admin/types.js.map +1 -0
  113. package/dist/extensions/url-safety.d.ts +20 -0
  114. package/dist/extensions/url-safety.d.ts.map +1 -1
  115. package/dist/extensions/url-safety.js +43 -0
  116. package/dist/extensions/url-safety.js.map +1 -1
  117. package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
  118. package/dist/file-upload/actions/upload-image.js +6 -1
  119. package/dist/file-upload/actions/upload-image.js.map +1 -1
  120. package/dist/integrations/adapters/email.d.ts.map +1 -1
  121. package/dist/integrations/adapters/email.js +112 -0
  122. package/dist/integrations/adapters/email.js.map +1 -1
  123. package/dist/integrations/types.d.ts +11 -0
  124. package/dist/integrations/types.d.ts.map +1 -1
  125. package/dist/integrations/types.js.map +1 -1
  126. package/dist/scripts/db/exec.d.ts.map +1 -1
  127. package/dist/scripts/db/exec.js +2 -1
  128. package/dist/scripts/db/exec.js.map +1 -1
  129. package/dist/scripts/db/index.d.ts.map +1 -1
  130. package/dist/scripts/db/index.js +1 -0
  131. package/dist/scripts/db/index.js.map +1 -1
  132. package/dist/scripts/db/migrate-encrypt-credentials.d.ts +28 -0
  133. package/dist/scripts/db/migrate-encrypt-credentials.d.ts.map +1 -0
  134. package/dist/scripts/db/migrate-encrypt-credentials.js +190 -0
  135. package/dist/scripts/db/migrate-encrypt-credentials.js.map +1 -0
  136. package/dist/scripts/db/query.d.ts.map +1 -1
  137. package/dist/scripts/db/query.js +2 -1
  138. package/dist/scripts/db/query.js.map +1 -1
  139. package/dist/scripts/db/safety.d.ts +1 -0
  140. package/dist/scripts/db/safety.d.ts.map +1 -1
  141. package/dist/scripts/db/safety.js +32 -0
  142. package/dist/scripts/db/safety.js.map +1 -1
  143. package/dist/scripts/db/scoping.d.ts.map +1 -1
  144. package/dist/scripts/db/scoping.js +11 -1
  145. package/dist/scripts/db/scoping.js.map +1 -1
  146. package/dist/secrets/crypto.d.ts +28 -0
  147. package/dist/secrets/crypto.d.ts.map +1 -0
  148. package/dist/secrets/crypto.js +81 -0
  149. package/dist/secrets/crypto.js.map +1 -0
  150. package/dist/secrets/storage.d.ts.map +1 -1
  151. package/dist/secrets/storage.js +3 -61
  152. package/dist/secrets/storage.js.map +1 -1
  153. package/dist/server/action-discovery.d.ts.map +1 -1
  154. package/dist/server/action-discovery.js +5 -2
  155. package/dist/server/action-discovery.js.map +1 -1
  156. package/dist/server/action-routes.d.ts.map +1 -1
  157. package/dist/server/action-routes.js +24 -7
  158. package/dist/server/action-routes.js.map +1 -1
  159. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  160. package/dist/server/agent-chat-plugin.js +49 -2
  161. package/dist/server/agent-chat-plugin.js.map +1 -1
  162. package/dist/server/auth.d.ts +1 -1
  163. package/dist/server/auth.d.ts.map +1 -1
  164. package/dist/server/auth.js.map +1 -1
  165. package/dist/server/better-auth-instance.js +3 -3
  166. package/dist/server/better-auth-instance.js.map +1 -1
  167. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  168. package/dist/server/core-routes-plugin.js +5 -0
  169. package/dist/server/core-routes-plugin.js.map +1 -1
  170. package/dist/server/csrf.d.ts.map +1 -1
  171. package/dist/server/csrf.js +9 -1
  172. package/dist/server/csrf.js.map +1 -1
  173. package/dist/server/design-token-utils.d.ts +8 -1
  174. package/dist/server/design-token-utils.d.ts.map +1 -1
  175. package/dist/server/design-token-utils.js +12 -4
  176. package/dist/server/design-token-utils.js.map +1 -1
  177. package/dist/templates/default/AGENTS.md +4 -4
  178. package/dist/templates/default/app/routes/database.tsx +13 -0
  179. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
  180. package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
  181. package/dist/vite/client.d.ts.map +1 -1
  182. package/dist/vite/client.js +4 -0
  183. package/dist/vite/client.js.map +1 -1
  184. package/docs/content/a2a-protocol.md +2 -2
  185. package/docs/content/actions.md +2 -54
  186. package/docs/content/agent-mentions.md +1 -1
  187. package/docs/content/agent-teams.md +1 -1
  188. package/docs/content/authentication.md +2 -2
  189. package/docs/content/cli-adapters.md +33 -17
  190. package/docs/content/client.md +11 -20
  191. package/docs/content/code-agents-ui.md +19 -6
  192. package/docs/content/context-awareness.md +36 -20
  193. package/docs/content/database.md +3 -3
  194. package/docs/content/deployment.md +8 -8
  195. package/docs/content/dispatch.md +1 -1
  196. package/docs/content/external-agents.md +5 -1
  197. package/docs/content/faq.md +1 -0
  198. package/docs/content/frames.md +116 -30
  199. package/docs/content/getting-started.md +15 -14
  200. package/docs/content/mcp-clients.md +1 -1
  201. package/docs/content/mcp-protocol.md +11 -88
  202. package/docs/content/messaging.md +1 -1
  203. package/docs/content/migration-workbench.md +13 -87
  204. package/docs/content/multi-app-workspace.md +2 -38
  205. package/docs/content/multi-tenancy.md +3 -26
  206. package/docs/content/onboarding.md +10 -3
  207. package/docs/content/recurring-jobs.md +2 -2
  208. package/docs/content/security.md +33 -1
  209. package/docs/content/server.md +1 -1
  210. package/docs/content/template-assets.md +9 -9
  211. package/docs/content/template-brain.md +114 -388
  212. package/docs/content/template-clips.md +42 -2
  213. package/docs/content/template-content.md +1 -1
  214. package/docs/content/template-design.md +27 -0
  215. package/docs/content/template-dispatch.md +3 -3
  216. package/docs/content/template-forms.md +6 -6
  217. package/docs/content/template-starter.md +2 -2
  218. package/docs/content/using-your-agent.md +56 -0
  219. package/docs/content/workspace-management.md +6 -6
  220. package/docs/content/workspace.md +28 -9
  221. package/package.json +10 -3
  222. package/src/templates/default/AGENTS.md +4 -4
  223. package/src/templates/default/app/routes/database.tsx +13 -0
  224. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
  225. package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
@@ -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":";AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EACL,UAAU,EACV,eAAe,EACf,aAAa,GAGd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,OAAO,EACP,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,OAAO,EACP,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AAyDpE,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,WAAW,GAAG,aAAa,CAAC;AAElC,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,EACJ,MAAM,EACN,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,GAChB,GAAG,KAAK,CAAC;IAEV,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,EAA6B,CAAC;QACjD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,WAAW;YAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC5D,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAEzB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAC;QAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1E,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAErB,MAAM,WAAW,GACf,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7D,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,IAAI,WAAW;YAAE,iBAAiB,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;;YACzC,iBAAiB,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAE3C,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,EAAU,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;;YAC7B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClB,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,EACD,CAAC,WAAW,EAAE,iBAAiB,CAAC,CACjC,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,OAAe,EAAE,EAAE;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO;YAAE,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;aACzD,IAAI,OAAO,CAAC,GAAG,KAAK,KAAK;YAC5B,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;;YAC9C,YAAY,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC,EACD,CAAC,IAAI,EAAE,YAAY,CAAC,CACrB,CAAC;IAEF,MAAM,OAAO,GAAG,OAAO,CAAuB,GAAG,EAAE;QACjD,MAAM,SAAS,GAAuB;YACpC,EAAE,EAAE,UAAU;YACd,IAAI,EAAE,EAAE;YACR,cAAc,EAAE,KAAK;YACrB,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,gBACE,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE;oBACV,IAAI,EAAE;wBAAE,EAAE,CAAC,aAAa,GAAG,CAAC,WAAW,IAAI,YAAY,CAAC;gBAC1D,CAAC,EACD,QAAQ,EAAE,SAAS,EACnB,SAAS,EAAC,2CAA2C,gBAC1C,iBAAiB,GAC5B,CACH;YACD,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CACjB,gBACE,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,EACzC,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,EAC1C,SAAS,EAAC,2CAA2C,gBAC1C,YAAY,GACvB,CACH;SACF,CAAC;QAEF,MAAM,QAAQ,GAAyB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAClE,EAAE,EAAE,GAAG,CAAC,IAAI;YACZ,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YACrC,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC;YACjD,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,KAAC,YAAY,IACX,MAAM,EAAE,GAAG,EACX,EAAE,EAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAC5B,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EACrD,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GACjC,CACH;YACD,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE;gBAC1B,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;gBACzB,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,QAAQ,GACZ,MAAM,EAAE,QAAQ,KAAK,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,KAAK,GAAG,CAAC,IAAI,CAAC;gBAChE,OAAO,CACL,eAAK,SAAS,EAAC,yCAAyC,aACtD,KAAC,YAAY,IACX,MAAM,EAAE,GAAG,EACX,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM,EAC1C,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,EAC7C,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,EAC1C,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAC1C,WAAW,EAAE,GAAG,EAAE,CAChB,cAAc,CAAC;gCACb,QAAQ,EAAE,GAAG,CAAC,KAAK;gCACnB,OAAO,EAAE,GAAG,CAAC,IAAI;gCACjB,OAAO,EAAE,IAAI;6BACd,CAAC,EAEJ,YAAY,EAAE,GAAG,EAAE,CACjB,cAAc,CAAC;gCACb,QAAQ,EAAE,GAAG,CAAC,KAAK;gCACnB,OAAO,EAAE,GAAG,CAAC,IAAI;gCACjB,OAAO,EAAE,KAAK;6BACf,CAAC,EAEJ,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;gCACd,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gCACxC,cAAc,CAAC;oCACb,QAAQ,EAAE,GAAG,CAAC,KAAK;oCACnB,OAAO,EAAE,GAAG,CAAC,IAAI;oCACjB,OAAO,EAAE,KAAK;iCACf,CAAC,CAAC;4BACL,CAAC,EACD,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,GACzD,EACD,EAAE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,CAC9C,iBACE,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,QAAQ,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,SAAS,EAAE,EAC5C,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gCACb,CAAC,CAAC,eAAe,EAAE,CAAC;gCACpB,eAAe,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;4BAC7B,CAAC,EACD,SAAS,EAAC,yHAAyH,YAEnI,KAAC,gBAAgB,IAAC,SAAS,EAAC,SAAS,GAAG,GACjC,CACV,IACG,CACP,CAAC;YACJ,CAAC;SACF,CAAC,CAAC,CAAC;QAEJ,MAAM,UAAU,GAAuB;YACrC,EAAE,EAAE,WAAW;YACf,IAAI,EAAE,EAAE;YACR,cAAc,EAAE,KAAK;YACrB,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI;YAClB,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CACjB,cAAK,SAAS,EAAC,yCAAyC,YACtD,iBACE,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,EAC5D,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAC3C,QAAQ,EAAE,CAAC,QAAQ,EACnB,SAAS,EAAE,EAAE,CACX,iFAAiF,EACjF,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,kBAAkB,CAC7C,YAEA,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CACxB,KAAC,eAAe,IAAC,SAAS,EAAC,aAAa,GAAG,CAC5C,CAAC,CAAC,CAAC,CACF,KAAC,SAAS,IAAC,SAAS,EAAC,aAAa,GAAG,CACtC,GACM,GACL,CACP;SACF,CAAC;QAEF,OAAO,CAAC,SAAS,EAAE,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC5C,uDAAuD;IACzD,CAAC,EAAE;QACD,MAAM,CAAC,OAAO;QACd,YAAY;QACZ,UAAU;QACV,YAAY;QACZ,IAAI;QACJ,MAAM;QACN,WAAW;QACX,WAAW;QACX,YAAY;QACZ,QAAQ;KACT,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,WAAW,CAC5B,CACE,GAAqC,EACrC,QAAgB,EAChB,OAAe,EACf,EAAE;QACF,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,OAAO,GAAG,QAAQ,CAAC;QACvB,IAAI,OAAO,GAAG,MAAM,CAAC;QACrB,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;aACjE,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;aACtD,IAAI,GAAG,KAAK,OAAO;YACtB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;aACrD,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3D,cAAc,CAAC;YACb,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC;YAC9B,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;IACL,CAAC,EACD,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAC9C,CAAC;IAEF,MAAM,WAAW,GAAsB,OAAO,CAAC,GAAG,EAAE;QAClD,MAAM,GAAG,GAAsB,EAAE,CAAC;QAClC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC9D,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,MAAM,KAAK,GAAG,aAAa,CAAC;QAC1B,IAAI,EAAE,IAAI;QACV,OAAO;QACP,eAAe,EAAE,eAAe,EAAE;QAClC,gBAAgB,EAAE,UAAU;QAC5B,KAAK,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;QACpC,oBAAoB,EAAE,CAAC,OAAO,EAAE,EAAE;YAChC,MAAM,IAAI,GACR,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACjE,oBAAoB,CAAC,IAA8B,CAAC,CAAC;QACvD,CAAC;QACD,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE;KACtB,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,CAAC,CAAsB,EAAE,EAAE;QAC/C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO;YAAE,OAAO;QACtC,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;YAC1B,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC/B,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtE,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACvD,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpE,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;aAAM,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;YAC5C,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,cAAc,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;IAExC,OAAO,CACL,KAAC,eAAe,IAAC,aAAa,EAAE,GAAG,YACjC,cACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAC,+BAA+B,EACzC,QAAQ,EAAE,CAAC,EACX,SAAS,EAAE,aAAa,YAExB,iBACE,SAAS,EAAC,0CAA0C,EACpD,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,aAE9C,gBAAO,SAAS,EAAC,mBAAmB,YACjC,KAAK,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CACnC,uBACG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC1B,cAEE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,EAClC,SAAS,EAAE,EAAE,CACX,sIAAsI,EACtI,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,UAAU,IAAI,kBAAkB,CACtD,aAEA,MAAM,CAAC,aAAa;wCACnB,CAAC,CAAC,IAAI;wCACN,CAAC,CAAC,UAAU,CACR,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAC9B,MAAM,CAAC,UAAU,EAAE,CACpB,EACJ,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAC/B,cACE,WAAW,EAAE,MAAM,CAAC,gBAAgB,EAAE,EACtC,YAAY,EAAE,MAAM,CAAC,gBAAgB,EAAE,EACvC,SAAS,EAAE,EAAE,CACX,yGAAyG,EACzG,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,SAAS,CAC3C,GACD,CACH,KAtBI,MAAM,CAAC,EAAE,CAuBX,CACN,CAAC,IA3BK,EAAE,CAAC,EAAE,CA4BT,CACN,CAAC,GACI,EACR,0BACG,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAChC,KAAC,YAAY,IACX,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EACtC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,GAC5B,CACH,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACtB,uBACE,aACE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAClC,SAAS,EAAC,8CAA8C,yBAGrD,GACF,CACN,CAAC,CAAC,CAAC,CACF,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpC,aAEE,SAAS,EAAE,EAAE,CACX,6BAA6B,EAC7B,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,kBAAkB,EACxC,GAAG,CAAC,QAAQ,CAAC,SAAS;gCACpB,0CAA0C,CAC7C,YAEA,GAAG,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnC,aAEE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,EACvC,SAAS,EAAE,EAAE,CACX,sDAAsD,EACtD,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,UAAU,IAAI,aAAa,CAC/C,YAEA,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,UAAU;oCAC9B,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC,CAAC,CAC/B,cAAK,SAAS,EAAC,yCAAyC,YACrD,UAAU,CACT,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAC1B,IAAI,CAAC,UAAU,EAAE,CAClB,GACG,CACP,CAAC,CAAC,CAAC,CACF,UAAU,CACR,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAC1B,IAAI,CAAC,UAAU,EAAE,CAClB,CACF,IApBI,IAAI,CAAC,EAAE,CAqBT,CACN,CAAC,IAhCG,GAAG,CAAC,EAAE,CAiCR,CACN,CAAC,CACH,GACK,IACF,GACJ,GACU,CACnB,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,GAAkB;IACtC,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IAChC,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IAChC,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,GAAG,CAAC;IACrC,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,EACpB,MAAM,EACN,EAAE,EACF,OAAO,EACP,MAAM,GAMP;IACC,OAAO,CACL,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAC,gDAAgD,aAEzD,MAAM,CAAC,EAAE,IAAI,CACZ,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,yBACE,KAAC,OAAO,IAAC,SAAS,EAAC,iCAAiC,GAAG,GAClD,GACQ,EACjB,KAAC,cAAc,8BAA6B,IACpC,CACX,EACA,EAAE,IAAI,CACL,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,yBACE,KAAC,gBAAgB,IAAC,SAAS,EAAC,kCAAkC,GAAG,GAC5D,GACQ,EACjB,MAAC,cAAc,0BACV,EAAE,CAAC,QAAQ,OAAG,EAAE,CAAC,SAAS,IACd,IACT,CACX,EACD,eAAM,SAAS,EAAC,sCAAsC,YACnD,MAAM,CAAC,IAAI,GACP,EACP,eAAM,SAAS,EAAC,sFAAsF,YACnG,MAAM,CAAC,IAAI,GACP,EACP,eAAM,SAAS,EAAC,kBAAkB,YAC/B,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CACnB,KAAC,WAAW,IAAC,SAAS,EAAC,SAAS,GAAG,CACpC,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CACvB,KAAC,aAAa,IAAC,SAAS,EAAC,SAAS,GAAG,CACtC,CAAC,CAAC,CAAC,IAAI,GACH,IACA,CACV,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,EACpB,WAAW,EACX,IAAI,GAIL;IACC,OAAO,CACL,4BACG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAC1C,uBACG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACjD,aAAY,SAAS,EAAC,0CAA0C,YAC9D,cAAK,SAAS,EAAC,0CAA0C,GAAG,IADrD,CAAC,CAEL,CACN,CAAC,IALK,CAAC,CAML,CACN,CAAC,GACD,CACJ,CAAC;AACJ,CAAC","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,2 @@
1
+ export declare function DbAdminPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=DbAdminPage.d.ts.map
@@ -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
+ * Code-mode database admin page — the shell that hosts the table browser, the
4
+ * table editor, and the SQL editor.
5
+ *
6
+ * Gated to Code mode: when the app cannot toggle into Code mode
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 { useCodeMode } 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 } = useCodeMode();
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
+ // ─── Code mode 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: "Code mode only" }), _jsx("p", { className: "mt-1.5 text-sm text-muted-foreground", children: "Database admin is available in Code 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 * Code-mode database admin page — the shell that hosts the table browser, the\n * table editor, and the SQL editor.\n *\n * Gated to Code mode: when the app cannot toggle into Code mode\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 { useCodeMode } 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 } = useCodeMode();\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 // ─── Code mode 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 Code mode only\n </h2>\n <p className=\"mt-1.5 text-sm text-muted-foreground\">\n Database admin is available in Code 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
+ * Code-mode-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 can toggle
11
+ * into Code mode (`useCodeMode().canToggle`). When it can't, 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 on the server, so this is purely a convenience affordance — never a
16
+ * 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 { useCodeMode } from "../use-dev-mode.js";
5
+ import { appPath } from "../api-path.js";
6
+ import { cn } from "../utils.js";
7
+ /**
8
+ * Code-mode-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 can toggle
12
+ * into Code mode (`useCodeMode().canToggle`). When it can't, 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 on the server, so this is purely a convenience affordance — never a
17
+ * security boundary.
18
+ */
19
+ export function DevDatabaseLink({ className, to = "/database", }) {
20
+ const { canToggle } = useCodeMode();
21
+ if (!canToggle)
22
+ return null;
23
+ return (_jsxs(Link, { to: appPath(to), title: "Database admin \u2014 Code mode 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: "code" })] }));
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,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,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,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,CACL,MAAC,IAAI,IACH,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,EACf,KAAK,EAAC,sCAAiC,EACvC,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,qBAEhG,IACF,CACR,CAAC;AACJ,CAAC","sourcesContent":["import { Link } from \"react-router\";\nimport { IconDatabase } from \"@tabler/icons-react\";\nimport { useCodeMode } 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 * Code-mode-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 can toggle\n * into Code mode (`useCodeMode().canToggle`). When it can't, 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 on the server, so this is purely a convenience affordance — never a\n * security boundary.\n */\nexport function DevDatabaseLink({\n className,\n to = \"/database\",\n}: DevDatabaseLinkProps) {\n const { canToggle } = useCodeMode();\n if (!canToggle) return null;\n return (\n <Link\n to={appPath(to)}\n title=\"Database admin — Code mode 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 code\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"}