@rebasepro/studio 0.17.3 → 0.18.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 (81) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +12 -4
  3. package/dist/{ApiKeysView-DiCurTEU.js → ApiKeysView-EIvegrUk.js} +54 -39
  4. package/dist/ApiKeysView-EIvegrUk.js.map +1 -0
  5. package/dist/{BackupsView-DNS6LdVg.js → BackupsView-DVkKzaPR.js} +29 -31
  6. package/dist/BackupsView-DVkKzaPR.js.map +1 -0
  7. package/dist/{BranchesView-CM3B-ER3.js → BranchesView-Df8hQ8wE.js} +85 -24
  8. package/dist/BranchesView-Df8hQ8wE.js.map +1 -0
  9. package/dist/{CronJobsView-BHtJZeJZ.js → CronJobsView-CLOGjb-L.js} +32 -27
  10. package/dist/CronJobsView-CLOGjb-L.js.map +1 -0
  11. package/dist/{RLSEditor-CBX4uD66.js → RLSEditor-Sa1D34ny.js} +724 -615
  12. package/dist/RLSEditor-Sa1D34ny.js.map +1 -0
  13. package/dist/{SQLEditor-ByecwX8B.js → SQLEditor-BfNA8xM3.js} +9 -4
  14. package/dist/{SQLEditor-ByecwX8B.js.map → SQLEditor-BfNA8xM3.js.map} +1 -1
  15. package/dist/{SchemaVisualizer-D6tMZgeC.js → SchemaVisualizer-DTDMYL6o.js} +84 -110
  16. package/dist/SchemaVisualizer-DTDMYL6o.js.map +1 -0
  17. package/dist/{StorageView-BGDeC5GI.js → StorageView-Ed9Q_VlB.js} +5 -32
  18. package/dist/StorageView-Ed9Q_VlB.js.map +1 -0
  19. package/dist/components/RLSEditor/saveRules.d.ts +38 -0
  20. package/dist/components/RebaseStudio.d.ts +1 -1
  21. package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +5 -2
  22. package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +0 -4
  23. package/dist/components/load-failure-view.d.ts +17 -0
  24. package/dist/components/load-failure.d.ts +47 -0
  25. package/dist/index.es.js +56 -36
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/load-failure-C6OMJSGC.js +31 -0
  28. package/dist/load-failure-C6OMJSGC.js.map +1 -0
  29. package/dist/load-failure-view-CBTB_h-h.js +48 -0
  30. package/dist/load-failure-view-CBTB_h-h.js.map +1 -0
  31. package/package.json +36 -21
  32. package/dist/ApiKeysView-DiCurTEU.js.map +0 -1
  33. package/dist/BackupsView-DNS6LdVg.js.map +0 -1
  34. package/dist/BranchesView-CM3B-ER3.js.map +0 -1
  35. package/dist/CronJobsView-BHtJZeJZ.js.map +0 -1
  36. package/dist/RLSEditor-CBX4uD66.js.map +0 -1
  37. package/dist/SchemaVisualizer-D6tMZgeC.js.map +0 -1
  38. package/dist/StorageView-BGDeC5GI.js.map +0 -1
  39. package/dist/components/StorageView/storage-failure.d.ts +0 -34
  40. package/src/components/ApiExplorer/ApiExplorer.tsx +0 -299
  41. package/src/components/ApiExplorer/EndpointDetail.tsx +0 -279
  42. package/src/components/ApiExplorer/TryItPanel.tsx +0 -499
  43. package/src/components/ApiExplorer/parseSpec.ts +0 -104
  44. package/src/components/ApiExplorer/types.ts +0 -84
  45. package/src/components/ApiKeys/ApiKeysView.tsx +0 -479
  46. package/src/components/ApiKeys/CreateApiKeyDialog.tsx +0 -711
  47. package/src/components/ApiKeys/permissions.ts +0 -145
  48. package/src/components/AuthSimulationSelector.tsx +0 -72
  49. package/src/components/Backups/BackupsView.tsx +0 -185
  50. package/src/components/Branches/BranchesView.tsx +0 -386
  51. package/src/components/CronJobs/CronJobsView.tsx +0 -413
  52. package/src/components/JSEditor/JSEditor.tsx +0 -1100
  53. package/src/components/JSEditor/JSEditorSidebar.tsx +0 -350
  54. package/src/components/JSEditor/JSMonacoEditor.tsx +0 -410
  55. package/src/components/LogsExplorer/LogsExplorer.tsx +0 -273
  56. package/src/components/LogsExplorer/useLogTail.ts +0 -356
  57. package/src/components/RLSEditor/PolicyEditor.tsx +0 -399
  58. package/src/components/RLSEditor/RLSEditor.tsx +0 -1128
  59. package/src/components/RLSEditor/index.ts +0 -1
  60. package/src/components/RLSEditor/policy-presets.ts +0 -176
  61. package/src/components/RLSEditor/saveSecurityRules.ts +0 -48
  62. package/src/components/RebaseStudio.tsx +0 -174
  63. package/src/components/SQLEditor/ExplainVisualizer.tsx +0 -128
  64. package/src/components/SQLEditor/MonacoEditor.tsx +0 -203
  65. package/src/components/SQLEditor/SQLEditor.tsx +0 -1518
  66. package/src/components/SQLEditor/SQLEditorSidebar.tsx +0 -173
  67. package/src/components/SQLEditor/SchemaBrowser.tsx +0 -168
  68. package/src/components/SQLEditor/sql_editor_types.ts +0 -18
  69. package/src/components/SchemaVisualizer/RelationEdge.tsx +0 -102
  70. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +0 -710
  71. package/src/components/SchemaVisualizer/TableNode.tsx +0 -257
  72. package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +0 -140
  73. package/src/components/SchemaVisualizer/useLiveRls.ts +0 -86
  74. package/src/components/SchemaVisualizer/useSchemaGraph.ts +0 -426
  75. package/src/components/StorageView/StorageView.tsx +0 -1588
  76. package/src/components/StorageView/storage-failure.ts +0 -63
  77. package/src/components/StudioHomePage.tsx +0 -430
  78. package/src/index.ts +0 -31
  79. package/src/utils/parseSpec.test.ts +0 -309
  80. package/src/utils/sql_utils.test.ts +0 -265
  81. package/src/utils/sql_utils.ts +0 -291
package/LICENSE CHANGED
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
package/README.md CHANGED
@@ -8,6 +8,10 @@ Developer tools layer for Rebase — provides 9 lazy-loaded tools (SQL Console,
8
8
  pnpm add @rebasepro/studio
9
9
  ```
10
10
 
11
+ ESM-only: `"type": "module"` with no CommonJS build, so it is loaded with
12
+ `import`. `require()` of it resolves only on Node 22.12+, which supports
13
+ `require(esm)`.
14
+
11
15
  ### Peer Dependencies
12
16
 
13
17
  - `react` >= 19.0.0
@@ -52,11 +56,15 @@ All 9 tools are enabled by default. The `schema` tool (collection editor) is aut
52
56
  | `BreadcrumbEntry` | Type | Single breadcrumb item |
53
57
  | `BreadcrumbsController` | Type | Breadcrumb controller interface |
54
58
 
55
- Individual tools (e.g. `SQLEditor`, `SchemaVisualizer`) are **not** re-exported from the barrel to preserve code splitting. Use deep imports if needed:
59
+ Individual tools (e.g. `SQLEditor`, `SchemaVisualizer`) are **not** exported
60
+ not from the barrel, and not by path either. `@rebasepro/studio` exports `.`
61
+ and nothing else, so a deep import fails at resolution with
62
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`.
56
63
 
57
- ```typescript
58
- import { SQLEditor } from "@rebasepro/studio/components/SQLEditor/SQLEditor";
59
- ```
64
+ That is the point: `RebaseStudio` registers a route per tool and fetches its
65
+ chunk when the route is opened, and an importable `SQLEditor` would put Monaco
66
+ in the initial bundle of anyone who reached for it. To add a tool of your own
67
+ beside them, pass an `AppView` to `<RebaseStudio devViews>`.
60
68
 
61
69
  ## Quick Start
62
70
 
@@ -1,4 +1,6 @@
1
- import { useApiBase, useApiConfig, useRebaseClient, useSnackbarController, useStudioCollectionRegistry } from "@rebasepro/app";
1
+ import { t as classifyLoadFailure } from "./load-failure-C6OMJSGC.js";
2
+ import { t as LoadFailureView } from "./load-failure-view-CBTB_h-h.js";
3
+ import { useApiBase, useApiConfig, useRebaseClient, useSnackbarController, useStudioCollectionRegistry, useTranslation } from "@rebasepro/app";
2
4
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
5
  import { AlertCircleIcon, AlertTriangleIcon, BooleanSwitchWithLabel, Button, CheckCircleIcon, ChevronsUpDownIcon, Chip, CircularProgress, CopyIcon, DatabaseIcon, Dialog, DialogActions, DialogContent, DialogTitle, FolderIcon, FunctionSquareIcon, GlobeIcon, IconButton, KeyRoundIcon, PlusIcon, RefreshCwIcon, Select, SelectGroup, SelectItem, ShieldIcon, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
4
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -670,21 +672,22 @@ function isExpired(key) {
670
672
  }
671
673
  function keyStatus(key) {
672
674
  if (key.revoked_at) return {
673
- label: "Revoked",
675
+ kind: "revoked",
674
676
  color: "text-red-500"
675
677
  };
676
678
  if (isExpired(key)) return {
677
- label: "Expired",
679
+ kind: "expired",
678
680
  color: "text-amber-500"
679
681
  };
680
682
  return {
681
- label: "Active",
683
+ kind: "active",
682
684
  color: "text-emerald-500"
683
685
  };
684
686
  }
685
687
  function ApiKeysView() {
686
688
  const client = useRebaseClient();
687
689
  const snackbar = useSnackbarController();
690
+ const { t } = useTranslation();
688
691
  const [keys, setKeys] = useState([]);
689
692
  const [loading, setLoading] = useState(true);
690
693
  const [selectedId, setSelectedId] = useState(null);
@@ -692,6 +695,8 @@ function ApiKeysView() {
692
695
  const [showSecret, setShowSecret] = useState(null);
693
696
  const [revoking, setRevoking] = useState(null);
694
697
  const [confirmRevoke, setConfirmRevoke] = useState(null);
698
+ /** Why the key listing failed, classified — see `load-failure.ts`. */
699
+ const [failure, setFailure] = useState(null);
695
700
  const clientRef = useRef(client);
696
701
  clientRef.current = client;
697
702
  const snackbarRef = useRef(snackbar);
@@ -705,11 +710,9 @@ function ApiKeysView() {
705
710
  try {
706
711
  const res = await c.apiKeys.listKeys();
707
712
  setKeys(res.keys);
713
+ setFailure(null);
708
714
  } catch (e) {
709
- snackbarRef.current.open({
710
- type: "error",
711
- message: e instanceof Error ? e.message : String(e)
712
- });
715
+ setFailure(classifyLoadFailure(e));
713
716
  } finally {
714
717
  setLoading(false);
715
718
  }
@@ -767,7 +770,7 @@ function ApiKeysView() {
767
770
  /* @__PURE__ */ jsx(Typography, {
768
771
  variant: "subtitle2",
769
772
  className: "font-semibold",
770
- children: "API Keys"
773
+ children: t("studio_tool_api_keys")
771
774
  }),
772
775
  /* @__PURE__ */ jsx(Chip, {
773
776
  size: "smallest",
@@ -793,7 +796,14 @@ function ApiKeysView() {
793
796
  }), /* @__PURE__ */ jsxs("div", {
794
797
  className: "flex-1 overflow-y-auto p-2 space-y-1",
795
798
  children: [
796
- activeKeys.length === 0 && inactiveKeys.length === 0 && /* @__PURE__ */ jsxs("div", {
799
+ failure && /* @__PURE__ */ jsx(LoadFailureView, {
800
+ failure,
801
+ title: t("studio_api_keys_read_failed"),
802
+ deniedTitle: t("studio_api_keys_denied_title"),
803
+ deniedHint: t("studio_api_keys_denied_hint"),
804
+ onRetry: loadKeys
805
+ }),
806
+ !failure && activeKeys.length === 0 && inactiveKeys.length === 0 && /* @__PURE__ */ jsxs("div", {
797
807
  className: "flex flex-col items-center justify-center h-full gap-3 text-center p-6",
798
808
  children: [
799
809
  /* @__PURE__ */ jsx(KeyRoundIcon, {
@@ -803,12 +813,12 @@ function ApiKeysView() {
803
813
  /* @__PURE__ */ jsx(Typography, {
804
814
  variant: "body2",
805
815
  color: "secondary",
806
- children: "No API keys yet"
816
+ children: t("studio_api_keys_empty_title")
807
817
  }),
808
818
  /* @__PURE__ */ jsx(Typography, {
809
819
  variant: "caption",
810
820
  color: "disabled",
811
- children: "Create a key to enable machine-to-machine authentication"
821
+ children: t("studio_api_keys_empty_hint")
812
822
  })
813
823
  ]
814
824
  }),
@@ -819,11 +829,11 @@ function ApiKeysView() {
819
829
  }, key.id)),
820
830
  inactiveKeys.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
821
831
  className: "px-2 pt-3 pb-1",
822
- children: /* @__PURE__ */ jsx(Typography, {
832
+ children: /* @__PURE__ */ jsxs(Typography, {
823
833
  variant: "caption",
824
834
  color: "disabled",
825
835
  className: "text-[10px] uppercase tracking-wider font-medium",
826
- children: "Revoked / Expired"
836
+ children: [t("studio_api_keys_revoke"), "d / Expired"]
827
837
  })
828
838
  }), inactiveKeys.map((key) => /* @__PURE__ */ jsx(KeyListItem, {
829
839
  apiKey: key,
@@ -839,7 +849,7 @@ function ApiKeysView() {
839
849
  children: /* @__PURE__ */ jsx(Typography, {
840
850
  variant: "body2",
841
851
  color: "disabled",
842
- children: "Select an API key to view details"
852
+ children: t("studio_api_keys_select_hint")
843
853
  })
844
854
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [
845
855
  /* @__PURE__ */ jsxs("div", {
@@ -874,7 +884,7 @@ function ApiKeysView() {
874
884
  onClick: () => setConfirmRevoke(selectedKey),
875
885
  disabled: revoking === selectedKey.id,
876
886
  startIcon: revoking === selectedKey.id ? /* @__PURE__ */ jsx(CircularProgress, { size: "smallest" }) : /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.smallest }),
877
- children: "Revoke"
887
+ children: t("studio_api_keys_revoke")
878
888
  })
879
889
  })]
880
890
  }),
@@ -884,37 +894,37 @@ function ApiKeysView() {
884
894
  className: "grid grid-cols-2 md:grid-cols-4 gap-3",
885
895
  children: [
886
896
  /* @__PURE__ */ jsx(StatCard, {
887
- label: "Status",
888
- value: keyStatus(selectedKey).label,
897
+ label: t("studio_api_keys_stat_status"),
898
+ value: t(`studio_api_keys_status_${keyStatus(selectedKey).kind}`),
889
899
  className: keyStatus(selectedKey).color
890
900
  }),
891
901
  /* @__PURE__ */ jsx(StatCard, {
892
- label: "Created",
902
+ label: t("created"),
893
903
  value: formatRelative(selectedKey.created_at)
894
904
  }),
895
905
  /* @__PURE__ */ jsx(StatCard, {
896
- label: "Last Used",
906
+ label: t("studio_api_keys_stat_last_used"),
897
907
  value: formatRelative(selectedKey.last_used_at)
898
908
  }),
899
909
  /* @__PURE__ */ jsx(StatCard, {
900
- label: "Expires",
901
- value: selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : "Never"
910
+ label: t("studio_api_keys_stat_expires"),
911
+ value: selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : t("studio_api_keys_never")
902
912
  })
903
913
  ]
904
914
  }), /* @__PURE__ */ jsxs("div", {
905
915
  className: "grid grid-cols-2 md:grid-cols-3 gap-3 mt-3",
906
916
  children: [
907
917
  /* @__PURE__ */ jsx(StatCard, {
908
- label: "Role",
909
- value: selectedKey.admin ? "Admin" : "Service",
918
+ label: t("role"),
919
+ value: selectedKey.admin ? t("admin") : t("studio_api_keys_role_service"),
910
920
  className: selectedKey.admin ? "text-amber-600 dark:text-amber-400" : void 0
911
921
  }),
912
922
  /* @__PURE__ */ jsx(StatCard, {
913
- label: "Rate Limit",
914
- value: selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : "Default (1000/15min)"
923
+ label: t("studio_api_keys_stat_rate_limit"),
924
+ value: selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : t("studio_api_keys_rate_limit_default")
915
925
  }),
916
926
  /* @__PURE__ */ jsx(StatCard, {
917
- label: "Created By",
927
+ label: t("studio_api_keys_stat_created_by"),
918
928
  value: selectedKey.created_by,
919
929
  mono: true
920
930
  })
@@ -926,7 +936,7 @@ function ApiKeysView() {
926
936
  children: [/* @__PURE__ */ jsx(Typography, {
927
937
  variant: "subtitle2",
928
938
  className: "font-semibold text-[13px]",
929
- children: "Permissions"
939
+ children: t("studio_api_keys_permissions")
930
940
  }), /* @__PURE__ */ jsx(Chip, {
931
941
  size: "smallest",
932
942
  className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
@@ -960,7 +970,7 @@ function ApiKeysView() {
960
970
  }), selectedKey.permissions.length === 0 ? /* @__PURE__ */ jsx(Typography, {
961
971
  variant: "body2",
962
972
  color: "disabled",
963
- children: "No permissions configured"
973
+ children: t("studio_api_keys_no_permissions")
964
974
  }) : /* @__PURE__ */ jsx("div", {
965
975
  className: "space-y-2",
966
976
  children: selectedKey.permissions.map((perm, idx) => /* @__PURE__ */ jsxs("div", {
@@ -999,7 +1009,7 @@ function ApiKeysView() {
999
1009
  children: [
1000
1010
  /* @__PURE__ */ jsx(DialogTitle, {
1001
1011
  hidden: true,
1002
- children: "Revoke Confirmation"
1012
+ children: t("studio_api_keys_revoke_confirmation")
1003
1013
  }),
1004
1014
  /* @__PURE__ */ jsxs(DialogContent, { children: [/* @__PURE__ */ jsxs(Typography, {
1005
1015
  variant: "subtitle1",
@@ -1018,7 +1028,7 @@ function ApiKeysView() {
1018
1028
  variant: "text",
1019
1029
  onClick: () => setConfirmRevoke(null),
1020
1030
  disabled: revoking !== null,
1021
- children: "Cancel"
1031
+ children: t("cancel")
1022
1032
  }), /* @__PURE__ */ jsx(Button, {
1023
1033
  color: "error",
1024
1034
  disabled: revoking !== null,
@@ -1065,7 +1075,7 @@ function KeyListItem({ apiKey, selected, onClick }) {
1065
1075
  onClick,
1066
1076
  className: cls("flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all", selected ? "bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30" : "hover:bg-surface-100 dark:hover:bg-surface-950"),
1067
1077
  children: [
1068
- /* @__PURE__ */ jsx("div", { className: cls("w-2 h-2 rounded-full shrink-0", status.label === "Active" ? "bg-emerald-400" : status.label === "Expired" ? "bg-amber-400" : "bg-red-400") }),
1078
+ /* @__PURE__ */ jsx("div", { className: cls("w-2 h-2 rounded-full shrink-0", status.kind === "active" ? "bg-emerald-400" : status.kind === "expired" ? "bg-amber-400" : "bg-red-400") }),
1069
1079
  /* @__PURE__ */ jsxs("div", {
1070
1080
  className: "flex-1 min-w-0",
1071
1081
  children: [/* @__PURE__ */ jsxs("div", {
@@ -1110,6 +1120,7 @@ function StatCard({ label, value, mono, className }) {
1110
1120
  });
1111
1121
  }
1112
1122
  function SecretDisplayDialog({ keyWithSecret, onClose }) {
1123
+ const { t } = useTranslation();
1113
1124
  const snackbar = useSnackbarController();
1114
1125
  const [copied, setCopied] = useState(false);
1115
1126
  const handleCopy = async () => {
@@ -1124,7 +1135,7 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1124
1135
  } catch {
1125
1136
  snackbar.open({
1126
1137
  type: "error",
1127
- message: "Failed to copy"
1138
+ message: t("studio_api_keys_copy_failed")
1128
1139
  });
1129
1140
  }
1130
1141
  };
@@ -1167,7 +1178,7 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1167
1178
  className: "flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all",
1168
1179
  children: keyWithSecret.key
1169
1180
  }), /* @__PURE__ */ jsx(Tooltip, {
1170
- title: copied ? "Copied!" : "Copy",
1181
+ title: copied ? t("copied") : t("copy"),
1171
1182
  children: /* @__PURE__ */ jsx(IconButton, {
1172
1183
  size: "small",
1173
1184
  onClick: handleCopy,
@@ -1185,7 +1196,7 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1185
1196
  variant: "caption",
1186
1197
  color: "secondary",
1187
1198
  children: [
1188
- /* @__PURE__ */ jsx("strong", { children: "Name:" }),
1199
+ /* @__PURE__ */ jsx("strong", { children: t("studio_api_keys_name_label") }),
1189
1200
  " ",
1190
1201
  keyWithSecret.name
1191
1202
  ]
@@ -1194,7 +1205,7 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1194
1205
  variant: "caption",
1195
1206
  color: "secondary",
1196
1207
  children: [
1197
- /* @__PURE__ */ jsx("strong", { children: "Access:" }),
1208
+ /* @__PURE__ */ jsx("strong", { children: t("studio_api_keys_access_label") }),
1198
1209
  " ",
1199
1210
  permissionSummary(keyWithSecret.permissions)
1200
1211
  ]
@@ -1205,7 +1216,11 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1205
1216
  children: [/* @__PURE__ */ jsx(ShieldIcon, {
1206
1217
  size: iconSize.smallest,
1207
1218
  className: "shrink-0"
1208
- }), /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("strong", { children: "Admin role granted" }), " — the admin routes and the default_admin policies"] })]
1219
+ }), /* @__PURE__ */ jsxs("span", { children: [
1220
+ /* @__PURE__ */ jsx("strong", { children: t("studio_api_keys_admin_granted") }),
1221
+ " — ",
1222
+ t("studio_api_keys_admin_granted_hint")
1223
+ ] })]
1209
1224
  })
1210
1225
  ]
1211
1226
  })
@@ -1213,7 +1228,7 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1213
1228
  /* @__PURE__ */ jsx(DialogActions, { children: /* @__PURE__ */ jsx(Button, {
1214
1229
  color: "primary",
1215
1230
  onClick: onClose,
1216
- children: "Done"
1231
+ children: t("studio_api_keys_done")
1217
1232
  }) })
1218
1233
  ]
1219
1234
  });
@@ -1221,4 +1236,4 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
1221
1236
  //#endregion
1222
1237
  export { ApiKeysView };
1223
1238
 
1224
- //# sourceMappingURL=ApiKeysView-DiCurTEU.js.map
1239
+ //# sourceMappingURL=ApiKeysView-EIvegrUk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiKeysView-EIvegrUk.js","names":[],"sources":["../src/components/ApiKeys/permissions.ts","../src/components/ApiKeys/CreateApiKeyDialog.tsx","../src/components/ApiKeys/ApiKeysView.tsx"],"sourcesContent":["/**\n * The vocabulary of an API key's permission entries.\n *\n * A permission entry is `{ collection, operations }`, but `collection` is not\n * only a collection: the same field addresses three namespaces, and the guard\n * that enforces them lives in `@rebasepro/server`\n * (`auth/api-keys/api-key-permission-guard.ts`):\n *\n * - `\"*\"` — every collection, every custom function, and storage\n * - `\"storage\"` — the storage routes\n * - `\"functions\"` — every custom function, including the function index\n * - `\"functions/<name>\"`— one named function\n * - anything else — the collection with that slug\n *\n * The UI used to label the field \"Collection slug or *\", which made two of\n * those namespaces undiscoverable and made `\"*\"` read as \"all collections\"\n * when it also hands over storage and every function. This module is the one\n * place that knows the mapping, so the picker, the row description, the grant\n * summary and the detail panel cannot drift from each other.\n *\n * @module\n */\n\nimport type { ApiKeyPermission } from \"@rebasepro/types\";\n\n/** Grants every collection, every function, and storage. */\nexport const RESOURCE_EVERYTHING = \"*\";\n/** Grants the storage routes. */\nexport const RESOURCE_STORAGE = \"storage\";\n/** Grants every custom function. */\nexport const RESOURCE_ALL_FUNCTIONS = \"functions\";\n/** Prefix addressing a single custom function. */\nexport const FUNCTION_PREFIX = \"functions/\";\n\nexport type ResourceKind =\n | \"everything\"\n | \"storage\"\n | \"all-functions\"\n | \"function\"\n | \"collection\";\n\nexport interface ParsedResource {\n kind: ResourceKind;\n /** The function name for `\"function\"`, the slug for `\"collection\"`. */\n name: string;\n}\n\n/** Classify a raw `collection` field into the namespace it addresses. */\nexport function parseResource(collection: string): ParsedResource {\n const value = collection.trim();\n if (value === RESOURCE_EVERYTHING) return { kind: \"everything\", name: \"\" };\n if (value === RESOURCE_STORAGE) return { kind: \"storage\", name: \"\" };\n if (value === RESOURCE_ALL_FUNCTIONS) return { kind: \"all-functions\", name: \"\" };\n if (value.startsWith(FUNCTION_PREFIX)) {\n return { kind: \"function\", name: value.slice(FUNCTION_PREFIX.length) };\n }\n return { kind: \"collection\", name: value };\n}\n\n/**\n * Short label for a resource — what a picker or a chip shows.\n *\n * Deliberately not the raw value: `\"*\"` alone is the thing nobody could read.\n */\nexport function resourceLabel(collection: string): string {\n const { kind, name } = parseResource(collection);\n switch (kind) {\n case \"everything\": return \"Everything\";\n case \"storage\": return \"Storage\";\n case \"all-functions\": return \"All functions\";\n case \"function\": return name ? `${name}()` : \"Function\";\n case \"collection\": return name || \"—\";\n }\n}\n\n/**\n * The resource as a sentence fragment, for \"this key can read <fragment>\".\n *\n * `\"everything\"` spells out all three namespaces, because that is exactly the\n * fact the old `*` input hid.\n */\nexport function resourcePhrase(collection: string): string {\n const { kind, name } = parseResource(collection);\n switch (kind) {\n case \"everything\": return \"every collection, every custom function and storage\";\n case \"storage\": return \"storage\";\n case \"all-functions\": return \"every custom function\";\n case \"function\": return name ? `the ${name} function` : \"one function\";\n case \"collection\": return name ? `the ${name} collection` : \"an unnamed collection\";\n }\n}\n\n/** How each operation reads for a given namespace, so the sentence stays true. */\nfunction operationPhrase(kind: ResourceKind, operation: string): string {\n if (kind === \"all-functions\" || kind === \"function\") {\n // Functions are invoked, not written to; the HTTP method still picks\n // the operation, so a POST-only function needs `write`.\n return operation === \"read\" ? \"call (GET)\"\n : operation === \"write\" ? \"call (POST, PUT, PATCH)\"\n : \"call (DELETE)\";\n }\n if (kind === \"storage\") {\n return operation === \"read\" ? \"download from\"\n : operation === \"write\" ? \"upload to\"\n : \"delete from\";\n }\n return operation;\n}\n\n/** Join with an Oxford-less \"and\", the way the rest of the panel reads. */\nfunction joinPhrases(parts: string[]): string {\n if (parts.length <= 1) return parts[0] ?? \"\";\n return `${parts.slice(0, -1).join(\", \")} and ${parts[parts.length - 1]}`;\n}\n\n/**\n * One plain sentence per permission entry: what the key will actually be able\n * to do. An entry with no operations selected grants nothing and says so,\n * rather than being silently dropped at submit time.\n */\nexport function grantSentence(perm: ApiKeyPermission): string {\n const { kind } = parseResource(perm.collection);\n const target = resourcePhrase(perm.collection);\n if (perm.operations.length === 0) return `No access to ${target}`;\n const verbs = joinPhrases(perm.operations.map(op => operationPhrase(kind, op)));\n const verb = verbs.charAt(0).toUpperCase() + verbs.slice(1);\n return `${verb} ${target}`;\n}\n\n/**\n * Dense one-line summary for list rows and the created-key confirmation.\n *\n * The wildcard wins over everything else in the array, because the guard\n * returns on the first match — a key holding `*` is a full-access key no\n * matter what else is listed beside it.\n */\nexport function permissionSummary(perms: ApiKeyPermission[]): string {\n if (perms.length === 0) return \"No permissions\";\n const wildcard = perms.find(p => p.collection === RESOURCE_EVERYTHING);\n if (wildcard) return `Everything (${wildcard.operations.join(\", \")})`;\n if (perms.length === 1) {\n return `${resourceLabel(perms[0].collection)} (${perms[0].operations.join(\", \")})`;\n }\n return `${perms.length} resources`;\n}\n","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n AlertTriangleIcon,\n BooleanSwitchWithLabel,\n Button,\n ChevronsUpDownIcon,\n CircularProgress,\n cls,\n DatabaseIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FolderIcon,\n FunctionSquareIcon,\n GlobeIcon,\n iconSize,\n IconButton,\n PlusIcon,\n Select,\n SelectGroup,\n SelectItem,\n ShieldIcon,\n TextField,\n Tooltip,\n Trash2Icon,\n Typography\n} from \"@rebasepro/ui\";\nimport {\n useApiBase,\n useApiConfig,\n useRebaseClient,\n useSnackbarController,\n useStudioCollectionRegistry\n} from \"@rebasepro/app\";\nimport type { ApiKeyPermission, ApiKeyWithSecret, RebaseClient } from \"@rebasepro/types\";\n\nimport {\n FUNCTION_PREFIX,\n grantSentence,\n parseResource,\n RESOURCE_ALL_FUNCTIONS,\n RESOURCE_EVERYTHING,\n RESOURCE_STORAGE,\n type ResourceKind\n} from \"./permissions\";\n\n/* ═══════════════════════════════════════════════════════════════\n Row model\n ═══════════════════════════════════════════════════════════════ */\n\ninterface PermissionRow {\n /** The raw wire value for `ApiKeyPermission.collection`. */\n resource: string;\n read: boolean;\n write: boolean;\n delete: boolean;\n /**\n * The row edits its resource as free text instead of through the picker.\n * Tracked separately from the value: a slug the panel has not registered\n * is still a legitimate grant, and without this flag the row would snap\n * back to the picker on the next render as soon as it was typed.\n */\n freeText: boolean;\n}\n\nconst OPERATIONS = [\"read\", \"write\", \"delete\"] as const;\ntype Operation = (typeof OPERATIONS)[number];\n\n/** What each operation actually permits, per namespace, shown on the toggle. */\nconst OPERATION_HINT: Record<ResourceKind, Record<Operation, string>> = {\n everything: {\n read: \"GET on every resource\",\n write: \"POST, PUT and PATCH on every resource\",\n delete: \"DELETE on every resource\"\n },\n collection: {\n read: \"List and get snapshots\",\n write: \"Create and update snapshots\",\n delete: \"Delete snapshots\"\n },\n storage: {\n read: \"List and download files\",\n write: \"Upload files and create folders\",\n delete: \"Delete files\"\n },\n \"all-functions\": {\n read: \"Call any function over GET\",\n write: \"Call any function over POST, PUT or PATCH\",\n delete: \"Call any function over DELETE\"\n },\n function: {\n read: \"Call it over GET\",\n write: \"Call it over POST, PUT or PATCH\",\n delete: \"Call it over DELETE\"\n }\n};\n\n/**\n * Picker value that switches a row to free text.\n *\n * Never reaches the wire — choosing it sets `freeText` and clears the resource\n * — so it only has to be a value no collection slug would take.\n */\nconst SENTINEL_FREE_TEXT = \"__custom__\";\n\nconst rowToPermission = (row: PermissionRow): ApiKeyPermission => ({\n collection: row.resource.trim(),\n operations: OPERATIONS.filter(op => row[op])\n});\n\nconst rowGrantsNothing = (row: PermissionRow): boolean => {\n const perm = rowToPermission(row);\n return !perm.collection || perm.operations.length === 0;\n};\n\nfunction ResourceIcon({ kind, className }: { kind: ResourceKind; className?: string }) {\n const Component = kind === \"everything\" ? GlobeIcon\n : kind === \"storage\" ? FolderIcon\n : kind === \"all-functions\" || kind === \"function\" ? FunctionSquareIcon\n : DatabaseIcon;\n return <Component size={iconSize.smallest} className={className}/>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Operation toggles\n\n Three states carried by one control each: off is neutral, on is tinted with\n the operation's own semantic colour. The previous version coloured the\n labels whether or not they were checked, so a read-only key still showed a\n red \"delete\" and the row read as more permissive than it was.\n ═══════════════════════════════════════════════════════════════ */\n\nconst OPERATION_STYLES: Record<Operation, { on: string; dot: string }> = {\n read: {\n on: \"bg-emerald-500/12 text-emerald-700 dark:text-emerald-300 ring-emerald-500/40\",\n dot: \"bg-emerald-500\"\n },\n write: {\n on: \"bg-blue-500/12 text-blue-700 dark:text-blue-300 ring-blue-500/40\",\n dot: \"bg-blue-500\"\n },\n delete: {\n on: \"bg-rose-500/12 text-rose-700 dark:text-rose-300 ring-rose-500/40\",\n dot: \"bg-rose-500\"\n }\n};\n\nfunction OperationToggles({\n row,\n onToggle\n }: {\n row: PermissionRow;\n onToggle: (operation: Operation, value: boolean) => void;\n}) {\n const kind = parseResource(row.resource).kind;\n return (\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Allowed operations\">\n {OPERATIONS.map(op => {\n const active = row[op];\n const styles = OPERATION_STYLES[op];\n return (\n <Tooltip key={op} title={OPERATION_HINT[kind][op]} delayDuration={400}>\n <button\n type=\"button\"\n aria-pressed={active}\n onClick={() => onToggle(op, !active)}\n className={cls(\n \"flex items-center gap-1.5 h-7 pl-2 pr-2.5 rounded-md text-2xs font-medium\",\n \"ring-1 transition-colors duration-150 cursor-pointer\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\",\n active\n ? styles.on\n : \"bg-transparent ring-transparent text-surface-500 dark:text-surface-400 hover:bg-surface-accent-100 dark:hover:bg-surface-800\"\n )}\n >\n <span className={cls(\n \"w-1.5 h-1.5 rounded-full transition-colors duration-150\",\n active ? styles.dot : \"bg-surface-300 dark:bg-surface-600\"\n )}/>\n {op}\n </button>\n </Tooltip>\n );\n })}\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Section heading\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SectionLabel({ children, hint }: { children: React.ReactNode; hint?: React.ReactNode }) {\n return (\n <div className=\"flex items-baseline gap-2 mb-2\">\n <Typography\n variant=\"label\"\n className=\"text-2xs uppercase tracking-wider font-semibold text-surface-600 dark:text-surface-300\"\n gutterBottom={false}\n >\n {children}\n </Typography>\n {hint && (\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-2xs\" gutterBottom={false}>\n {hint}\n </Typography>\n )}\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Create API Key Dialog\n ═══════════════════════════════════════════════════════════════ */\n\nexport function CreateApiKeyDialog({\n onClose,\n onCreated\n }: {\n onClose: () => void;\n onCreated: (key: ApiKeyWithSecret) => void;\n}) {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const collectionRegistry = useStudioCollectionRegistry();\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n\n const [name, setName] = useState(\"\");\n const [rows, setRows] = useState<PermissionRow[]>([\n { resource: RESOURCE_EVERYTHING, read: true, write: false, delete: false, freeText: false }\n ]);\n const [admin, setAdmin] = useState(false);\n const [rateLimit, setRateLimit] = useState(\"\");\n const [expiresIn, setExpiresIn] = useState(\"never\");\n const [creating, setCreating] = useState(false);\n\n /* The collections the panel already knows — the picker's main list. */\n const collections = useMemo(\n () => (collectionRegistry?.collections ?? [])\n .map(col => ({ slug: col.slug, name: col.name }))\n .filter(col => !!col.slug)\n .sort((a, b) => a.slug.localeCompare(b.slug)),\n [collectionRegistry?.collections]\n );\n\n /**\n * Deployed functions, so a single-function grant can be picked instead of\n * spelled `functions/<name>` from memory. Best-effort: this is an ordinary\n * API route, and a backend that serves no functions — or refuses the\n * request — just leaves the free-text escape to cover it.\n */\n const [functionNames, setFunctionNames] = useState<string[]>([]);\n useEffect(() => {\n if (!apiBase) return;\n let cancelled = false;\n (async () => {\n try {\n const token = await apiConfig?.getAuthToken?.();\n const res = await fetch(`${apiBase}/functions`, {\n headers: token ? { Authorization: `Bearer ${token}` } : undefined\n });\n if (!res.ok) return;\n const body = await res.json() as { functions?: { name?: string }[] };\n if (cancelled) return;\n setFunctionNames((body.functions ?? [])\n .map(fn => fn.name)\n .filter((fnName): fnName is string => !!fnName));\n } catch {\n /* No listing available; the picker falls back to free text. */\n }\n })();\n return () => { cancelled = true; };\n }, [apiBase, apiConfig]);\n\n const updateRow = useCallback((idx: number, patch: Partial<PermissionRow>) => {\n setRows(current => current.map((row, i) => i === idx ? { ...row, ...patch } : row));\n }, []);\n\n const addRow = () => setRows(current => [\n ...current,\n {\n resource: collections[0]?.slug ?? \"\",\n read: true,\n write: false,\n delete: false,\n freeText: collections.length === 0\n }\n ]);\n\n const removeRow = (idx: number) => setRows(current => current.filter((_, i) => i !== idx));\n\n /* What would actually be sent. A row with no operations grants nothing;\n it used to be dropped at submit time with no indication it had been. */\n const effectivePermissions = useMemo(\n () => rows.map(rowToPermission).filter(perm => perm.collection && perm.operations.length > 0),\n [rows]\n );\n\n const droppedRows = rows.filter(rowGrantsNothing).length;\n const hasWildcard = rows.some(row => row.resource === RESOURCE_EVERYTHING && OPERATIONS.some(op => row[op]));\n const canSubmit = !!name.trim() && effectivePermissions.length > 0 && !creating;\n\n const handleCreate = async () => {\n if (!client?.apiKeys || !canSubmit) return;\n\n let expires_at: string | null = null;\n if (expiresIn === \"7d\") expires_at = new Date(Date.now() + 7 * 86400000).toISOString();\n else if (expiresIn === \"30d\") expires_at = new Date(Date.now() + 30 * 86400000).toISOString();\n else if (expiresIn === \"90d\") expires_at = new Date(Date.now() + 90 * 86400000).toISOString();\n else if (expiresIn === \"1y\") expires_at = new Date(Date.now() + 365 * 86400000).toISOString();\n\n setCreating(true);\n try {\n const res = await client.apiKeys.createKey({\n name: name.trim(),\n permissions: effectivePermissions,\n admin,\n rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,\n expires_at\n });\n onCreated(res.key);\n } catch (e: unknown) {\n snackbar.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally {\n setCreating(false);\n }\n };\n\n const submitBlockedReason = !name.trim()\n ? \"Name the key first\"\n : effectivePermissions.length === 0\n ? \"Grant at least one operation on one resource\"\n : \"\";\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open && !creating) onClose(); }} maxWidth=\"2xl\">\n\n <DialogTitle variant=\"subtitle1\" gutterBottom={false} className=\"font-semibold\">\n Create API key\n </DialogTitle>\n\n <DialogContent includeMargin={false} className=\"px-8 pt-2 pb-4 flex flex-col gap-6\">\n\n <Typography variant=\"body2\" color=\"secondary\" gutterBottom={false} className=\"text-[13px] max-w-[62ch]\">\n A credential for scripts, cron jobs and agents. It authenticates as itself rather\n than as a user, and reaches only what you grant here.\n </Typography>\n\n <TextField\n label=\"Name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Analytics pipeline\"\n size=\"small\"\n autoFocus\n />\n\n {/* ── Access ── */}\n <div>\n <SectionLabel hint=\"What the key may call\">Access</SectionLabel>\n\n <div className={cls(\"rounded-lg border overflow-hidden\", defaultBorderMixin)}>\n {rows.map((row, idx) => {\n const parsed = parseResource(row.resource);\n return (\n <div\n key={idx}\n role=\"group\"\n aria-label={`Resource ${idx + 1}`}\n className={cls(\n \"flex flex-wrap items-center gap-x-3 gap-y-2 px-3 py-2.5\",\n idx > 0 && cls(\"border-t\", defaultBorderMixin)\n )}\n >\n <div className=\"flex items-center gap-2 flex-1 min-w-[15rem]\">\n <ResourceIcon\n kind={parsed.kind}\n className={cls(\n \"shrink-0\",\n parsed.kind === \"everything\"\n ? \"text-amber-600 dark:text-amber-400\"\n : \"text-surface-500 dark:text-surface-400\"\n )}\n />\n {row.freeText\n ? (\n <TextField\n size=\"small\"\n aria-label={`Resource ${idx + 1} name`}\n value={row.resource}\n onChange={(e) => updateRow(idx, { resource: e.target.value })}\n placeholder=\"collection slug, or functions/<name>\"\n className=\"flex-1\"\n endAdornment={\n <Tooltip title=\"Back to the list\">\n <IconButton\n size=\"smallest\"\n aria-label=\"Pick from the list instead\"\n onClick={() => updateRow(idx, {\n resource: collections[0]?.slug ?? RESOURCE_EVERYTHING,\n freeText: false\n })}\n >\n <ChevronsUpDownIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n }\n />\n )\n : (\n <Select\n size=\"small\"\n fullWidth\n className=\"flex-1\"\n aria-label={`Resource ${idx + 1}`}\n value={row.resource}\n position=\"popper\"\n onValueChange={(value) => {\n if (value === SENTINEL_FREE_TEXT) {\n updateRow(idx, { freeText: true, resource: \"\" });\n } else {\n updateRow(idx, { resource: value, freeText: false });\n }\n }}\n renderValue={(value) => (\n <span className=\"truncate\">\n {value === RESOURCE_EVERYTHING ? \"Everything\"\n : value === RESOURCE_STORAGE ? \"Storage\"\n : value === RESOURCE_ALL_FUNCTIONS ? \"All functions\"\n : String(value).startsWith(FUNCTION_PREFIX)\n ? `${String(value).slice(FUNCTION_PREFIX.length)}()`\n : String(value)}\n </span>\n )}\n >\n <SelectItem value={RESOURCE_EVERYTHING}>\n <div className=\"flex flex-col text-left\">\n <span>Everything</span>\n <span className=\"text-2xs text-text-secondary dark:text-text-secondary-dark\">\n Every collection, every function and storage\n </span>\n </div>\n </SelectItem>\n\n {collections.length > 0 && (\n <SelectGroup label=\"Collections\">\n {collections.map(col => (\n <SelectItem key={col.slug} value={col.slug}>\n <div className=\"flex flex-col text-left\">\n <span>{col.slug}</span>\n {col.name && col.name !== col.slug && (\n <span className=\"text-2xs text-text-secondary dark:text-text-secondary-dark\">\n {col.name}\n </span>\n )}\n </div>\n </SelectItem>\n ))}\n </SelectGroup>\n )}\n\n <SelectGroup label=\"Functions\">\n <SelectItem value={RESOURCE_ALL_FUNCTIONS}>All functions</SelectItem>\n {functionNames.map(fnName => (\n <SelectItem key={fnName} value={`${FUNCTION_PREFIX}${fnName}`}>\n {fnName}()\n </SelectItem>\n ))}\n </SelectGroup>\n\n <SelectGroup label=\"Storage\">\n <SelectItem value={RESOURCE_STORAGE}>Storage</SelectItem>\n </SelectGroup>\n\n <SelectGroup label=\"Other\">\n <SelectItem value={SENTINEL_FREE_TEXT}>Type a name…</SelectItem>\n </SelectGroup>\n </Select>\n )}\n </div>\n\n <OperationToggles\n row={row}\n onToggle={(operation, value) => updateRow(idx, { [operation]: value })}\n />\n\n <Tooltip title={rows.length > 1 ? \"Remove\" : \"At least one resource is required\"}>\n <span>\n <IconButton\n size=\"small\"\n disabled={rows.length === 1}\n onClick={() => removeRow(idx)}\n aria-label={`Remove resource ${idx + 1}`}\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </span>\n </Tooltip>\n </div>\n );\n })}\n </div>\n\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={addRow}\n className=\"mt-1.5\"\n startIcon={<PlusIcon size={iconSize.smallest}/>}\n >\n Add resource\n </Button>\n\n {/* Plain-language read-back of the grant being built. */}\n <div className={cls(\n \"mt-3 rounded-lg border px-3 py-2.5 bg-surface-accent-50 dark:bg-surface-900\",\n defaultBorderMixin\n )}>\n <Typography\n variant=\"label\"\n gutterBottom={false}\n className=\"text-2xs uppercase tracking-wider font-semibold text-surface-600 dark:text-surface-300\"\n >\n This key will be able to\n </Typography>\n {effectivePermissions.length === 0\n ? (\n <Typography\n variant=\"body2\"\n gutterBottom={false}\n className=\"mt-1.5 text-[13px] text-surface-500 dark:text-surface-400\"\n >\n Nothing yet — pick a resource and at least one operation.\n </Typography>\n )\n : (\n <ul className=\"mt-1.5 space-y-1\">\n {effectivePermissions.map((perm, idx) => (\n <li key={idx} className=\"flex items-start gap-2\">\n <ResourceIcon\n kind={parseResource(perm.collection).kind}\n className=\"mt-[3px] shrink-0 text-surface-500 dark:text-surface-400\"\n />\n <Typography variant=\"body2\" gutterBottom={false} className=\"text-[13px] leading-snug\">\n {grantSentence(perm)}\n </Typography>\n </li>\n ))}\n {admin && (\n <li className=\"flex items-start gap-2\">\n <ShieldIcon size={iconSize.smallest} className=\"mt-[3px] shrink-0 text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"body2\" gutterBottom={false} className=\"text-[13px] leading-snug\">\n Reach every admin route — users, roles, cron, backups, logs — and read\n through the <span className=\"font-mono text-2xs\">default_admin</span> policies\n </Typography>\n </li>\n )}\n </ul>\n )}\n {droppedRows > 0 && effectivePermissions.length > 0 && (\n <Typography\n variant=\"caption\"\n gutterBottom={false}\n className=\"block mt-2 text-2xs text-surface-500 dark:text-surface-400\"\n >\n {droppedRows === 1\n ? \"1 row grants nothing and will not be saved.\"\n : `${droppedRows} rows grant nothing and will not be saved.`}\n </Typography>\n )}\n </div>\n\n {hasWildcard && (\n <div className=\"flex items-start gap-2 mt-2 px-1\">\n <AlertTriangleIcon size={iconSize.smallest} className=\"mt-[3px] shrink-0 text-amber-600 dark:text-amber-400\"/>\n <Typography\n variant=\"caption\"\n gutterBottom={false}\n className=\"text-2xs text-amber-700 dark:text-amber-300 leading-snug max-w-[70ch]\"\n >\n <span className=\"font-semibold\">Everything</span> is the widest grant there is. It also\n covers collections and functions you add later, without this key being edited again.\n </Typography>\n </div>\n )}\n </div>\n\n {/* ── Admin role ── */}\n <div>\n <SectionLabel hint=\"Off for almost every key\">Admin role</SectionLabel>\n <div className={cls(\n \"rounded-lg border px-3 py-2.5 transition-colors duration-150\",\n admin ? \"border-amber-500/40 bg-amber-500/[0.06]\" : defaultBorderMixin\n )}>\n <BooleanSwitchWithLabel\n size=\"small\"\n position=\"start\"\n invisible\n value={admin}\n onValueChange={setAdmin}\n label={\n <div className=\"flex items-center gap-2\">\n <ShieldIcon\n size={iconSize.smallest}\n className={admin\n ? \"text-amber-600 dark:text-amber-400\"\n : \"text-surface-500 dark:text-surface-400\"}\n />\n <span className=\"text-[13px]\">Grant the admin role</span>\n </div>\n }\n />\n <Typography\n variant=\"caption\"\n color=\"secondary\"\n gutterBottom={false}\n className=\"block mt-1.5 text-2xs leading-snug max-w-[70ch]\"\n >\n {admin\n ? \"The key passes the admin-gated routes and reads through the default_admin RLS policies — far wider than the resources above. It still cannot manage API keys.\"\n : \"Without it the key carries only the service role, and RLS grants it nothing unless a collection policy names that role.\"}\n </Typography>\n </div>\n </div>\n\n {/* ── Limits ── */}\n <div>\n <SectionLabel>Limits</SectionLabel>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3 items-end\">\n {/* Each control gets its own cell: `Select.Root` renders no DOM\n node, so an unwrapped Select puts its label and its control\n into two separate grid items. */}\n <div>\n <Select\n label=\"Expires\"\n value={expiresIn}\n onValueChange={setExpiresIn}\n size=\"small\"\n fullWidth\n position=\"popper\"\n renderValue={(v) =>\n v === \"never\" ? \"Never\"\n : v === \"7d\" ? \"In 7 days\"\n : v === \"30d\" ? \"In 30 days\"\n : v === \"90d\" ? \"In 90 days\" : \"In 1 year\"\n }\n >\n <SelectItem value=\"never\">Never</SelectItem>\n <SelectItem value=\"7d\">In 7 days</SelectItem>\n <SelectItem value=\"30d\">In 30 days</SelectItem>\n <SelectItem value=\"90d\">In 90 days</SelectItem>\n <SelectItem value=\"1y\">In 1 year</SelectItem>\n </Select>\n </div>\n <div>\n {/* Labelled above rather than floating inside, so it lines\n up with the Select beside it — a Select can only label\n above, and the two idioms in one row do not align. */}\n <label\n htmlFor=\"api-key-rate-limit\"\n className=\"block text-sm font-medium ml-3.5 mb-1 text-text-secondary dark:text-text-secondary-dark\"\n >\n Rate limit\n </label>\n <TextField\n id=\"api-key-rate-limit\"\n value={rateLimit}\n onChange={(e) => setRateLimit(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"1000\"\n size=\"small\"\n endAdornment={\n <span className=\"text-2xs text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\">\n / 15 min\n </span>\n }\n />\n </div>\n </div>\n <Typography\n variant=\"caption\"\n color=\"secondary\"\n gutterBottom={false}\n className=\"block mt-1.5 text-2xs\"\n >\n Leave the rate limit empty for the server default of 1000 requests per 15-minute window.\n </Typography>\n </div>\n\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={onClose} disabled={creating}>Cancel</Button>\n <Tooltip title={submitBlockedReason}>\n <span>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={!canSubmit}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : undefined}\n >\n {creating ? \"Creating…\" : \"Create key\"}\n </Button>\n </span>\n </Tooltip>\n </DialogActions>\n </Dialog>\n );\n}\n","\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport {\n Button,\n Card,\n Chip,\n CircularProgress,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n KeyRoundIcon,\n RefreshCwIcon,\n ShieldIcon,\n Tooltip,\n Typography,\n CopyIcon,\n PlusIcon as AddIcon,\n Trash2Icon as DeleteIcon,\n AlertCircleIcon,\n CheckCircleIcon\n} from \"@rebasepro/ui\";\nimport { useRebaseClient, useSnackbarController, useTranslation } from \"@rebasepro/app\";\nimport type { ApiKeyMasked, ApiKeyWithSecret, RebaseClient } from \"@rebasepro/types\";\n\nimport { CreateApiKeyDialog } from \"./CreateApiKeyDialog\";\nimport { permissionSummary, resourceLabel, resourcePhrase } from \"./permissions\";\nimport { classifyLoadFailure, type LoadFailure } from \"../load-failure\";\nimport { LoadFailureView } from \"../load-failure-view\";\n\n/* ═══════════════════════════════════════════════════════════════\n Helpers\n\n The row types come from `@rebasepro/types`: this view used to declare its\n own copies, and they had already drifted — neither carried `admin`, so the\n panel could not tell an admin key from a scoped one.\n ═══════════════════════════════════════════════════════════════ */\n\nfunction formatRelative(iso: string | null | undefined): string {\n if (!iso) return \"—\";\n const d = new Date(iso);\n const now = Date.now();\n const diff = d.getTime() - now;\n const abs = Math.abs(diff);\n if (abs < 60000) return diff > 0 ? \"in <1m\" : \"<1m ago\";\n if (abs < 3600000) { const m = Math.round(abs / 60000); return diff > 0 ? `in ${m}m` : `${m}m ago`; }\n if (abs < 86400000) { const h = Math.round(abs / 3600000); return diff > 0 ? `in ${h}h` : `${h}h ago`; }\n return d.toLocaleDateString();\n}\n\nfunction isExpired(key: ApiKeyMasked): boolean {\n return !!(key.expires_at && new Date(key.expires_at) < new Date());\n}\n\ntype KeyStatusKind = \"active\" | \"expired\" | \"revoked\";\n\nfunction keyStatus(key: ApiKeyMasked): { kind: KeyStatusKind; color: string } {\n if (key.revoked_at) return { kind: \"revoked\", color: \"text-red-500\" };\n if (isExpired(key)) return { kind: \"expired\", color: \"text-amber-500\" };\n return { kind: \"active\", color: \"text-emerald-500\" };\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Main Component\n ═══════════════════════════════════════════════════════════════ */\n\nexport function ApiKeysView() {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const { t } = useTranslation();\n const [keys, setKeys] = useState<ApiKeyMasked[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [showCreate, setShowCreate] = useState(false);\n const [showSecret, setShowSecret] = useState<ApiKeyWithSecret | null>(null);\n const [revoking, setRevoking] = useState<string | null>(null);\n const [confirmRevoke, setConfirmRevoke] = useState<ApiKeyMasked | null>(null);\n /** Why the key listing failed, classified — see `load-failure.ts`. */\n const [failure, setFailure] = useState<LoadFailure | null>(null);\n\n const clientRef = useRef(client);\n clientRef.current = client;\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n const loadKeys = useCallback(async () => {\n const c = clientRef.current;\n if (!c?.apiKeys) { setLoading(false); return; }\n try {\n const res = await c.apiKeys.listKeys();\n setKeys(res.keys);\n setFailure(null);\n } catch (e: unknown) {\n // \"No API keys yet\" is a claim about the project. A refused listing\n // is a claim about the caller, and only one of the two is an\n // invitation to create a key.\n setFailure(classifyLoadFailure(e));\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => { loadKeys(); }, [loadKeys]);\n\n const handleRevoke = async (id: string) => {\n const c = clientRef.current;\n if (!c?.apiKeys) return;\n setRevoking(id);\n try {\n await c.apiKeys.revokeKey(id);\n snackbarRef.current.open({ type: \"success\", message: \"API key revoked\" });\n await loadKeys();\n if (selectedId === id) setSelectedId(null);\n } catch (e: unknown) {\n snackbarRef.current.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally { setRevoking(null); }\n };\n\n const handleCreated = (keyWithSecret: ApiKeyWithSecret) => {\n setShowCreate(false);\n setShowSecret(keyWithSecret);\n loadKeys();\n };\n\n const selectedKey = keys.find(k => k.id === selectedId);\n const activeKeys = keys.filter(k => !k.revoked_at && !isExpired(k));\n const inactiveKeys = keys.filter(k => k.revoked_at || isExpired(k));\n\n if (loading) return <div className=\"flex items-center justify-center h-full\"><CircularProgress/></div>;\n\n return (\n <>\n <div className=\"flex h-full w-full overflow-hidden bg-white dark:bg-surface-950\">\n {/* ── Key List ── */}\n <div className={cls(\"flex flex-col w-[340px] min-w-[280px] border-r h-full\", defaultBorderMixin)}>\n <div className={cls(\"flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2\">\n <KeyRoundIcon size={iconSize.smallest} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">{t(\"studio_tool_api_keys\")}</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">{activeKeys.length}</Chip>\n </div>\n <div className=\"flex items-center gap-1\">\n <IconButton size=\"small\" onClick={loadKeys} title=\"Refresh\"><RefreshCwIcon size={iconSize.smallest}/></IconButton>\n <Button size=\"small\" color=\"primary\" onClick={() => setShowCreate(true)} startIcon={<AddIcon size={iconSize.smallest}/>}>\n New\n </Button>\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {failure && (\n <LoadFailureView\n failure={failure}\n title={t(\"studio_api_keys_read_failed\")}\n deniedTitle={t(\"studio_api_keys_denied_title\")}\n deniedHint={t(\"studio_api_keys_denied_hint\")}\n onRetry={loadKeys}\n />\n )}\n {!failure && activeKeys.length === 0 && inactiveKeys.length === 0 && (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-center p-6\">\n <KeyRoundIcon size={iconSize.medium} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"body2\" color=\"secondary\">{t(\"studio_api_keys_empty_title\")}</Typography>\n <Typography variant=\"caption\" color=\"disabled\">{t(\"studio_api_keys_empty_hint\")}</Typography>\n </div>\n )}\n {activeKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n {inactiveKeys.length > 0 && (\n <>\n <div className=\"px-2 pt-3 pb-1\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px] uppercase tracking-wider font-medium\">{t(\"studio_api_keys_revoke\")}d / Expired</Typography>\n </div>\n {inactiveKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n </>\n )}\n </div>\n </div>\n\n {/* ── Detail Panel ── */}\n <div className=\"flex-1 flex flex-col min-w-0 h-full overflow-hidden\">\n {!selectedKey ? (\n <div className=\"flex items-center justify-center h-full\">\n <Typography variant=\"body2\" color=\"disabled\">{t(\"studio_api_keys_select_hint\")}</Typography>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-3 min-w-0\">\n <KeyRoundIcon size={iconSize.small} className=\"text-primary shrink-0\"/>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selectedKey.name}</Typography>\n {selectedKey.admin && <AdminChip/>}\n </div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"font-mono text-[11px]\">{selectedKey.key_prefix}•••</Typography>\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n {!selectedKey.revoked_at && (\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n onClick={() => setConfirmRevoke(selectedKey)}\n disabled={revoking === selectedKey.id}\n startIcon={revoking === selectedKey.id ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n >\n {t(\"studio_api_keys_revoke\")}\n </Button>\n )}\n </div>\n </div>\n\n {/* Stats */}\n <div className=\"px-5 py-4 bg-surface-50 dark:bg-surface-900/50\">\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3\">\n <StatCard label={t(\"studio_api_keys_stat_status\")} value={t(`studio_api_keys_status_${keyStatus(selectedKey).kind}`)} className={keyStatus(selectedKey).color}/>\n <StatCard label={t(\"created\")} value={formatRelative(selectedKey.created_at)}/>\n <StatCard label={t(\"studio_api_keys_stat_last_used\")} value={formatRelative(selectedKey.last_used_at)}/>\n <StatCard label={t(\"studio_api_keys_stat_expires\")} value={selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : t(\"studio_api_keys_never\")}/>\n </div>\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-3 mt-3\">\n <StatCard\n label={t(\"role\")}\n value={selectedKey.admin ? t(\"admin\") : t(\"studio_api_keys_role_service\")}\n className={selectedKey.admin ? \"text-amber-600 dark:text-amber-400\" : undefined}\n />\n <StatCard label={t(\"studio_api_keys_stat_rate_limit\")} value={selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : t(\"studio_api_keys_rate_limit_default\")}/>\n <StatCard label={t(\"studio_api_keys_stat_created_by\")} value={selectedKey.created_by} mono/>\n </div>\n </div>\n\n {/* Permissions */}\n <div className={cls(\"flex items-center gap-2 px-5 py-2 border-y bg-white dark:bg-surface-950\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"font-semibold text-[13px]\">{t(\"studio_api_keys_permissions\")}</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">\n {selectedKey.permissions.length}\n </Chip>\n </div>\n <div className=\"flex-1 overflow-y-auto px-5 py-3\">\n {selectedKey.admin && (\n <div className=\"flex items-start gap-2 mb-3 px-3 py-2 rounded-lg border border-amber-500/40 bg-amber-500/[0.06]\">\n <ShieldIcon size={iconSize.smallest} className=\"mt-[3px] shrink-0 text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"caption\" className=\"text-[12px] leading-snug text-amber-700 dark:text-amber-300\">\n This key holds the <span className=\"font-semibold\">admin role</span>: it also passes the\n admin-gated routes — users, roles, cron, backups, logs — and reads through the\n <span className=\"font-mono\"> default_admin</span> RLS policies, beyond the resources listed here.\n </Typography>\n </div>\n )}\n {selectedKey.permissions.length === 0 ? (\n <Typography variant=\"body2\" color=\"disabled\">{t(\"studio_api_keys_no_permissions\")}</Typography>\n ) : (\n <div className=\"space-y-2\">\n {selectedKey.permissions.map((perm, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-3 px-3 py-2 rounded-lg border\", defaultBorderMixin)}>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {resourceLabel(perm.collection)}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[11px]\">\n {resourcePhrase(perm.collection)}\n </Typography>\n </div>\n <div className=\"flex items-center gap-1 shrink-0\">\n {perm.operations.map(op => (\n <Chip key={op} size=\"smallest\" className={cls(\n op === \"read\" && \"bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300\",\n op === \"write\" && \"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300\",\n op === \"delete\" && \"bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300\"\n )}>{op}</Chip>\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n\n {/* Revoke Confirmation Dialog */}\n <Dialog\n open={confirmRevoke !== null}\n onOpenChange={(open) => {\n if (!open && !revoking) setConfirmRevoke(null);\n }}\n >\n <DialogTitle hidden>{t(\"studio_api_keys_revoke_confirmation\")}</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n Revoke &quot;{confirmRevoke?.name}&quot;?\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n Requests authenticated with this key will stop working immediately. This action cannot be undone.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => setConfirmRevoke(null)}\n disabled={revoking !== null}\n >\n {t(\"cancel\")}\n </Button>\n <Button\n color=\"error\"\n disabled={revoking !== null}\n startIcon={revoking !== null ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n onClick={async () => {\n if (!confirmRevoke) return;\n await handleRevoke(confirmRevoke.id);\n setConfirmRevoke(null);\n }}\n >\n Revoke\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Create Dialog */}\n {showCreate && (\n <CreateApiKeyDialog\n onClose={() => setShowCreate(false)}\n onCreated={handleCreated}\n />\n )}\n\n {/* Secret Display Dialog */}\n {showSecret && (\n <SecretDisplayDialog\n keyWithSecret={showSecret}\n onClose={() => setShowSecret(null)}\n />\n )}\n </>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n List item\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Marks a key that carries the admin role.\n *\n * Not cosmetic: an admin key reaches the admin routes and the `default_admin`\n * RLS policies, and without this it is indistinguishable in the list from a\n * read-only one.\n */\nfunction AdminChip() {\n return (\n <Tooltip title=\"Carries the admin role: the admin-gated routes and the default_admin RLS policies\">\n <Chip\n size=\"smallest\"\n className=\"shrink-0 bg-amber-500/12 dark:bg-amber-500/12 text-amber-700 dark:text-amber-300 border-amber-500/30 dark:border-amber-500/30\"\n >\n <ShieldIcon size={10}/>\n admin\n </Chip>\n </Tooltip>\n );\n}\n\nfunction KeyListItem({ apiKey, selected, onClick }: { apiKey: ApiKeyMasked; selected: boolean; onClick: () => void }) {\n const status = keyStatus(apiKey);\n return (\n <div\n onClick={onClick}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selected\n ? \"bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-950\"\n )}\n >\n <div className={cls(\"w-2 h-2 rounded-full shrink-0\",\n status.kind === \"active\" ? \"bg-emerald-400\" :\n status.kind === \"expired\" ? \"bg-amber-400\" : \"bg-red-400\"\n )}/>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-1.5 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{apiKey.name}</Typography>\n {apiKey.admin && <AdminChip/>}\n </div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px] font-mono\">{apiKey.key_prefix}•••</Typography>\n </div>\n <div className=\"shrink-0\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px]\">{permissionSummary(apiKey.permissions)}</Typography>\n </div>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Stat card\n ═══════════════════════════════════════════════════════════════ */\n\nfunction StatCard({ label, value, mono, className }: { label: string; value: string; mono?: boolean; className?: string }) {\n return (\n <div className={cls(\"px-3 py-2 rounded-lg border bg-white dark:bg-surface-900\", defaultBorderMixin)}>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[10px] uppercase tracking-wider font-medium\">{label}</Typography>\n <Typography variant=\"body2\" className={cls(\n \"mt-0.5 font-semibold text-[13px]\",\n mono && \"font-mono\",\n className\n )}>{value}</Typography>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Secret Display Dialog — shown exactly once after creation\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SecretDisplayDialog({ keyWithSecret, onClose }: { keyWithSecret: ApiKeyWithSecret; onClose: () => void }) {\n const { t } = useTranslation();\n const snackbar = useSnackbarController();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(keyWithSecret.key);\n setCopied(true);\n snackbar.open({ type: \"success\", message: \"API key copied to clipboard\" });\n setTimeout(() => setCopied(false), 2000);\n } catch {\n snackbar.open({ type: \"error\", message: t(\"studio_api_keys_copy_failed\") });\n }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"md\">\n <DialogTitle>\n <div className=\"flex items-center gap-2\">\n <CheckCircleIcon size={iconSize.small} className=\"text-emerald-500\"/>\n API Key Created\n </div>\n </DialogTitle>\n <DialogContent>\n <div className=\"p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4\">\n <div className=\"flex items-center gap-2 mb-1\">\n <AlertCircleIcon size={iconSize.smallest} className=\"text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"caption\" className=\"font-semibold text-amber-700 dark:text-amber-400\">\n Copy your key now — it won&apos;t be shown again\n </Typography>\n </div>\n <Typography variant=\"caption\" className=\"text-amber-600 dark:text-amber-300\">\n This is the only time the full API key will be displayed. Store it securely.\n </Typography>\n </div>\n\n <div className={cls(\"flex items-center gap-2 p-3 rounded-lg border bg-surface-50 dark:bg-surface-900\", defaultBorderMixin)}>\n <code className=\"flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all\">\n {keyWithSecret.key}\n </code>\n <Tooltip title={copied ? t(\"copied\") : t(\"copy\")}>\n <IconButton size=\"small\" onClick={handleCopy}>\n {copied\n ? <CheckCircleIcon size={iconSize.smallest} className=\"text-emerald-500\"/>\n : <CopyIcon size={iconSize.smallest}/>\n }\n </IconButton>\n </Tooltip>\n </div>\n\n <div className=\"mt-4 space-y-1\">\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>{t(\"studio_api_keys_name_label\")}</strong> {keyWithSecret.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>{t(\"studio_api_keys_access_label\")}</strong> {permissionSummary(keyWithSecret.permissions)}\n </Typography>\n {keyWithSecret.admin && (\n <Typography variant=\"caption\" className=\"flex items-center gap-1.5 text-amber-700 dark:text-amber-300\">\n <ShieldIcon size={iconSize.smallest} className=\"shrink-0\"/>\n <span><strong>{t(\"studio_api_keys_admin_granted\")}</strong> — {t(\"studio_api_keys_admin_granted_hint\")}</span>\n </Typography>\n )}\n </div>\n </DialogContent>\n <DialogActions>\n <Button color=\"primary\" onClick={onClose}>{t(\"studio_api_keys_done\")}</Button>\n </DialogActions>\n </Dialog>\n );\n}\n"],"mappings":"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"}