@elizaos/ui 2.0.3-beta.4 → 2.0.3-beta.6

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 (83) hide show
  1. package/app-navigate-view.d.ts.map +1 -1
  2. package/app-navigate-view.js +1 -1
  3. package/cloud/api-keys/ApiKeysView.d.ts +6 -3
  4. package/cloud/api-keys/ApiKeysView.d.ts.map +1 -1
  5. package/cloud/api-keys/ApiKeysView.js +21 -29
  6. package/cloud/api-keys/copy-api-key.d.ts +1 -8
  7. package/cloud/api-keys/copy-api-key.d.ts.map +1 -1
  8. package/cloud/api-keys/copy-api-key.js +1 -10
  9. package/cloud/api-keys/index.d.ts +1 -1
  10. package/cloud/api-keys/index.d.ts.map +1 -1
  11. package/cloud/api-keys/index.js +1 -1
  12. package/cloud/shell/CloudRouterShell.d.ts +12 -0
  13. package/cloud/shell/CloudRouterShell.d.ts.map +1 -1
  14. package/cloud/shell/CloudRouterShell.js +38 -2
  15. package/cloud-ui/components/data-list/api-keys-table.d.ts +1 -2
  16. package/cloud-ui/components/data-list/api-keys-table.d.ts.map +1 -1
  17. package/cloud-ui/components/data-list/api-keys-table.js +3 -9
  18. package/cloud-ui/components/docs/api-route-explorer-client.d.ts.map +1 -1
  19. package/cloud-ui/components/docs/api-route-explorer-client.js +2 -1
  20. package/cloud-ui/components/layout/dashboard-route-page.d.ts.map +1 -1
  21. package/cloud-ui/components/layout/dashboard-route-page.js +3 -1
  22. package/components/config-ui/config-field.js +1 -1
  23. package/components/pages/Springboard.d.ts.map +1 -1
  24. package/components/pages/Springboard.js +34 -45
  25. package/components/pages/SpringboardSurface.d.ts.map +1 -1
  26. package/components/pages/SpringboardSurface.js +116 -6
  27. package/components/shell/ContinuousChatOverlay.d.ts.map +1 -1
  28. package/components/shell/ContinuousChatOverlay.js +207 -146
  29. package/components/shell/HomeSpringboardSurface.d.ts.map +1 -1
  30. package/components/shell/HomeSpringboardSurface.js +31 -76
  31. package/components/shell/use-pull-gesture.d.ts +6 -0
  32. package/components/shell/use-pull-gesture.d.ts.map +1 -1
  33. package/components/shell/use-pull-gesture.js +66 -27
  34. package/components/shell/useShellController.d.ts.map +1 -1
  35. package/components/shell/useShellController.js +13 -3
  36. package/components/views/ViewTileImage.d.ts +8 -8
  37. package/components/views/ViewTileImage.d.ts.map +1 -1
  38. package/components/views/ViewTileImage.js +38 -9
  39. package/config/boot-config-store.js +1 -1
  40. package/hooks/useHorizontalPager.d.ts +33 -0
  41. package/hooks/useHorizontalPager.d.ts.map +1 -0
  42. package/hooks/useHorizontalPager.js +226 -0
  43. package/hooks/useLayoutShiftMonitor.d.ts +2 -0
  44. package/hooks/useLayoutShiftMonitor.d.ts.map +1 -1
  45. package/hooks/useLayoutShiftMonitor.js +23 -0
  46. package/hooks/view-catalog.d.ts +3 -4
  47. package/hooks/view-catalog.d.ts.map +1 -1
  48. package/hooks/view-catalog.js +4 -6
  49. package/package.json +10 -10
  50. package/state/startup-telemetry.d.ts +6 -0
  51. package/state/startup-telemetry.d.ts.map +1 -1
  52. package/state/startup-telemetry.js +23 -2
  53. package/styles/base.css +6 -6
  54. package/styles/brand-gold.css +6 -5
  55. package/testing/layout-stability.d.ts +11 -3
  56. package/testing/layout-stability.d.ts.map +1 -1
  57. package/testing/layout-stability.js +29 -4
  58. package/testing/run-animation-frames-immediately.d.ts +2 -0
  59. package/testing/run-animation-frames-immediately.d.ts.map +1 -0
  60. package/testing/run-animation-frames-immediately.js +8 -0
  61. package/themes/presets.js +14 -14
  62. package/utils/cloud-agent-base.d.ts.map +1 -1
  63. package/utils/cloud-agent-base.js +6 -0
  64. package/widgets/registry.d.ts.map +1 -1
  65. package/widgets/registry.js +7 -0
  66. package/components/pages/automation-conversations.d.ts +0 -16
  67. package/components/pages/automation-conversations.d.ts.map +0 -1
  68. package/components/pages/automation-conversations.js +0 -204
  69. package/components/pages/automations-missing-creds.d.ts +0 -25
  70. package/components/pages/automations-missing-creds.d.ts.map +0 -1
  71. package/components/pages/automations-missing-creds.js +0 -39
  72. package/components/settings/desktop-workspace-audit-config.d.ts +0 -3
  73. package/components/settings/desktop-workspace-audit-config.d.ts.map +0 -1
  74. package/components/settings/desktop-workspace-audit-config.js +0 -171
  75. package/components/shell/first-run-theme.d.ts +0 -6
  76. package/components/shell/first-run-theme.d.ts.map +0 -1
  77. package/components/shell/first-run-theme.js +0 -31
  78. package/genui/use-json-render-message.d.ts +0 -20
  79. package/genui/use-json-render-message.d.ts.map +0 -1
  80. package/genui/use-json-render-message.js +0 -38
  81. package/state/first-run-restart.d.ts +0 -8
  82. package/state/first-run-restart.d.ts.map +0 -1
  83. package/state/first-run-restart.js +0 -8
@@ -1 +1 @@
1
- {"version":3,"file":"app-navigate-view.d.ts","sourceRoot":"","sources":["../../../../src/app-navigate-view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-navigate-view.d.ts","sourceRoot":"","sources":["../../../../src/app-navigate-view.ts"],"names":[],"mappings":"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"}
@@ -129,7 +129,7 @@ export function createNavigateViewHandler({ availableViewsForDesktopTabs, closeD
129
129
  recordRecentViewId(entry.id);
130
130
  openDesktopTab(entry, { pinned: false });
131
131
  }
132
- const primaryViewId = viewIds[0] ?? detail.viewId ?? null;
132
+ const primaryViewId = resolvedViewIds[0] ?? viewIds[0] ?? detail.viewId ?? null;
133
133
  if (primaryViewId)
134
134
  setActiveDesktopTabId(primaryViewId);
135
135
  setViewLayout?.({
@@ -13,9 +13,12 @@
13
13
  * - Mutations use the shared cloud `apiFetch` client (auth injection +
14
14
  * structured `ApiError`) and invalidate the react-query key, replacing the
15
15
  * raw `fetch` + manual `response.ok` plumbing.
16
- * - The dead "copy stored key" action (which imported a non-existent
17
- * `@/lib/client/api-keys` module and could never fetch a hashed secret) now
18
- * copies the public key prefix the only identifier visible after creation.
16
+ * - There is no row-level "copy key" action: the full secret is only ever
17
+ * shown once, in the post-create reveal dialog (copyable via `handleCopyKey`).
18
+ * A stored key exposes only its public prefix, so copying it is pointless.
19
+ * - Delete optimistically removes the row from the `["api-keys"]` cache because
20
+ * Hyperdrive caches the list GET, so the post-delete refetch can briefly
21
+ * serve the deleted key; the invalidate reconciles once the cache expires.
19
22
  */
20
23
  import { type ApiKeyDisplay, type ApiKeysSummaryData } from "../../cloud-ui/components/data-list/index.js";
21
24
  interface ApiKeysViewProps {
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeysView.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/api-keys/ApiKeysView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,kBAAkB,EAExB,MAAM,qCAAqC,CAAC;AAkC7C,UAAU,gBAAgB;IACxB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAoCD,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAihB9D"}
1
+ {"version":3,"file":"ApiKeysView.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/api-keys/ApiKeysView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,kBAAkB,EAExB,MAAM,qCAAqC,CAAC;AAmC7C,UAAU,gBAAgB;IACxB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAoCD,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAkgB9D"}
@@ -14,9 +14,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  * - Mutations use the shared cloud `apiFetch` client (auth injection +
15
15
  * structured `ApiError`) and invalidate the react-query key, replacing the
16
16
  * raw `fetch` + manual `response.ok` plumbing.
17
- * - The dead "copy stored key" action (which imported a non-existent
18
- * `@/lib/client/api-keys` module and could never fetch a hashed secret) now
19
- * copies the public key prefix the only identifier visible after creation.
17
+ * - There is no row-level "copy key" action: the full secret is only ever
18
+ * shown once, in the post-create reveal dialog (copyable via `handleCopyKey`).
19
+ * A stored key exposes only its public prefix, so copying it is pointless.
20
+ * - Delete optimistically removes the row from the `["api-keys"]` cache because
21
+ * Hyperdrive caches the list GET, so the post-delete refetch can briefly
22
+ * serve the deleted key; the invalidate reconciles once the cache expires.
20
23
  */
21
24
  import { useQueryClient } from "@tanstack/react-query";
22
25
  import { Copy, Plus } from "lucide-react";
@@ -34,7 +37,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "
34
37
  import { Textarea } from "../../components/ui/textarea.js";
35
38
  import { ApiError, apiFetch } from "../lib/api-client.js";
36
39
  import { useCloudT } from "../shell/CloudI18nProvider.js";
37
- import { copyApiKeyPrefix, copyApiKeyToClipboard } from "./copy-api-key.js";
40
+ import { copyApiKeyToClipboard } from "./copy-api-key.js";
38
41
  const rateLimitPresets = [
39
42
  {
40
43
  value: "standard",
@@ -133,29 +136,6 @@ export function ApiKeysView({ keys, summary }) {
133
136
  }), { description: errorMessage(error) });
134
137
  }
135
138
  };
136
- // The full secret is hashed + KMS-encrypted at rest and only returned once
137
- // (on create / regenerate). For a stored key the only copyable identifier is
138
- // its public prefix.
139
- const handleCopyStoredKey = async (id) => {
140
- const key = keys.find((k) => k.id === id);
141
- if (!key)
142
- return;
143
- try {
144
- await copyApiKeyPrefix(key.keyPrefix);
145
- toast.success(t("cloud.apiKeys.prefixCopied", {
146
- defaultValue: "Key prefix copied",
147
- }), {
148
- description: t("cloud.apiKeys.prefixCopiedDesc", {
149
- defaultValue: "The full key is only shown once, when it is created. Copied the public prefix instead.",
150
- }),
151
- });
152
- }
153
- catch (error) {
154
- toast.error(t("cloud.apiKeys.copyFailed", {
155
- defaultValue: "Failed to copy API key",
156
- }), { description: errorMessage(error) });
157
- }
158
- };
159
139
  const handleDisableKey = (id) => {
160
140
  const key = keys.find((k) => k.id === id);
161
141
  const isCurrentlyActive = key?.status === "active";
@@ -235,12 +215,24 @@ export function ApiKeysView({ keys, summary }) {
235
215
  else if (type === "delete") {
236
216
  try {
237
217
  await apiFetch(`/api/v1/api-keys/${id}`, { method: "DELETE" });
218
+ // Hyperdrive caches the list GET, so an immediate refetch can still
219
+ // serve the just-deleted row. Optimistically drop it from every cached
220
+ // `["api-keys", ...]` query (the key is partitioned per user, so match
221
+ // on the prefix) so it disappears immediately.
222
+ queryClient.setQueriesData({ queryKey: ["api-keys"] }, (existing) => existing?.filter((key) => key.id !== id));
238
223
  toast.success(t("cloud.apiKeys.deleted", { defaultValue: "API key deleted" }), {
239
224
  description: t("cloud.apiKeys.deletedDesc", {
240
225
  defaultValue: "The API key has been permanently deleted.",
241
226
  }),
242
227
  });
243
- refreshApiKeys();
228
+ // Mark the list stale WITHOUT an immediate refetch: refetching now would
229
+ // re-read the Hyperdrive-cached GET and clobber the optimistic removal
230
+ // above, making the deleted row reappear. It reconciles on the next
231
+ // natural refetch (mount/focus), by which point the cache has rolled over.
232
+ void queryClient.invalidateQueries({
233
+ queryKey: ["api-keys"],
234
+ refetchType: "none",
235
+ });
244
236
  }
245
237
  catch (error) {
246
238
  toast.error(t("cloud.apiKeys.deleteFailed", {
@@ -301,7 +293,7 @@ export function ApiKeysView({ keys, summary }) {
301
293
  defaultValue: "API key created successfully",
302
294
  }) }), _jsx(DialogDescription, { children: t("cloud.apiKeys.createdDialogDesc", {
303
295
  defaultValue: "Make sure to copy your API key now. You won't be able to see it again!",
304
- }) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-2", children: [_jsx("p", { className: "text-xs font-medium text-white/70 uppercase tracking-wide", children: t("cloud.apiKeys.keyName", { defaultValue: "Key name" }) }), _jsx("div", { className: "font-mono text-sm font-semibold text-white", children: createdKey.name })] }), _jsxs("div", { className: "grid gap-2", children: [_jsx("p", { className: "text-xs font-medium text-white/70 uppercase tracking-wide", children: t("cloud.apiKeys.apiKeyLabel", { defaultValue: "API Key" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: createdKey.plainKey, readOnly: true, className: "font-mono text-sm rounded-sm border-white/10 bg-black/40 text-white" }), _jsx(BrandButton, { variant: "outline", onClick: () => void handleCopyKey(createdKey.plainKey), children: _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(DialogFooter, { children: _jsx(BrandButton, { variant: "primary", onClick: () => setCreatedKey(null), children: t("cloud.apiKeys.done", { defaultValue: "Done" }) }) })] }) })), _jsx("div", { className: "space-y-6", children: hasKeys ? (_jsx(ApiKeysTable, { keys: keys, onCopyKey: (id) => void handleCopyStoredKey(id), onDisableKey: handleDisableKey, onDeleteKey: handleDeleteKey, onRegenerateKey: handleRegenerateKey })) : (_jsx(ApiKeyEmptyState, { onCreateKey: () => setCreateDialogOpen(true) })) }), _jsx(AlertDialog, { open: pendingAction !== null, onOpenChange: (open) => !open && setPendingAction(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: pendingAction?.title }), _jsx(AlertDialogDescription, { children: pendingAction?.description })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: t("cloud.apiKeys.cancel", { defaultValue: "Cancel" }) }), _jsx(AlertDialogAction, { onClick: () => void handleConfirmAction(), className: pendingAction?.type === "delete"
296
+ }) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-2", children: [_jsx("p", { className: "text-xs font-medium text-white/70 uppercase tracking-wide", children: t("cloud.apiKeys.keyName", { defaultValue: "Key name" }) }), _jsx("div", { className: "font-mono text-sm font-semibold text-white", children: createdKey.name })] }), _jsxs("div", { className: "grid gap-2", children: [_jsx("p", { className: "text-xs font-medium text-white/70 uppercase tracking-wide", children: t("cloud.apiKeys.apiKeyLabel", { defaultValue: "API Key" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: createdKey.plainKey, readOnly: true, className: "font-mono text-sm rounded-sm border-white/10 bg-black/40 text-white" }), _jsx(BrandButton, { variant: "outline", onClick: () => void handleCopyKey(createdKey.plainKey), children: _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(DialogFooter, { children: _jsx(BrandButton, { variant: "primary", onClick: () => setCreatedKey(null), children: t("cloud.apiKeys.done", { defaultValue: "Done" }) }) })] }) })), _jsx("div", { className: "space-y-6", children: hasKeys ? (_jsx(ApiKeysTable, { keys: keys, onDisableKey: handleDisableKey, onDeleteKey: handleDeleteKey, onRegenerateKey: handleRegenerateKey })) : (_jsx(ApiKeyEmptyState, { onCreateKey: () => setCreateDialogOpen(true) })) }), _jsx(AlertDialog, { open: pendingAction !== null, onOpenChange: (open) => !open && setPendingAction(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: pendingAction?.title }), _jsx(AlertDialogDescription, { children: pendingAction?.description })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: t("cloud.apiKeys.cancel", { defaultValue: "Cancel" }) }), _jsx(AlertDialogAction, { onClick: () => void handleConfirmAction(), className: pendingAction?.type === "delete"
305
297
  ? "bg-red-600 hover:bg-red-700"
306
298
  : "", children: t("cloud.apiKeys.confirm", { defaultValue: "Confirm" }) })] })] }) })] }));
307
299
  }
@@ -12,20 +12,13 @@
12
12
  * endpoint that reveals the secret of an existing key, so "copy the stored
13
13
  * secret" is impossible by design.
14
14
  *
15
- * So this exposes exactly two operations:
15
+ * So this exposes a single operation:
16
16
  * - {@link copyApiKeyToClipboard} — copy a one-time plaintext key (the value
17
17
  * shown in the post-create reveal dialog).
18
- * - {@link copyApiKeyPrefix} — copy the public key prefix (the only visible
19
- * identifier for a stored key) for the row-level "Copy key" action.
20
18
  */
21
19
  /**
22
20
  * Copy a full plaintext API key (only available in the one-time reveal dialog).
23
21
  * Throws if the clipboard is unavailable so the caller can surface an error.
24
22
  */
25
23
  export declare function copyApiKeyToClipboard(plainKey: string): Promise<void>;
26
- /**
27
- * Copy the public key prefix for a stored key. The full secret cannot be
28
- * retrieved after creation, so the prefix is the only copyable identifier.
29
- */
30
- export declare function copyApiKeyPrefix(keyPrefix: string): Promise<void>;
31
24
  //# sourceMappingURL=copy-api-key.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy-api-key.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/api-keys/copy-api-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE"}
1
+ {"version":3,"file":"copy-api-key.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/api-keys/copy-api-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E"}
@@ -12,11 +12,9 @@
12
12
  * endpoint that reveals the secret of an existing key, so "copy the stored
13
13
  * secret" is impossible by design.
14
14
  *
15
- * So this exposes exactly two operations:
15
+ * So this exposes a single operation:
16
16
  * - {@link copyApiKeyToClipboard} — copy a one-time plaintext key (the value
17
17
  * shown in the post-create reveal dialog).
18
- * - {@link copyApiKeyPrefix} — copy the public key prefix (the only visible
19
- * identifier for a stored key) for the row-level "Copy key" action.
20
18
  */
21
19
  import { copyTextToClipboard } from "../../utils/clipboard.js";
22
20
  /**
@@ -26,10 +24,3 @@ import { copyTextToClipboard } from "../../utils/clipboard.js";
26
24
  export async function copyApiKeyToClipboard(plainKey) {
27
25
  await copyTextToClipboard(plainKey);
28
26
  }
29
- /**
30
- * Copy the public key prefix for a stored key. The full secret cannot be
31
- * retrieved after creation, so the prefix is the only copyable identifier.
32
- */
33
- export async function copyApiKeyPrefix(keyPrefix) {
34
- await copyTextToClipboard(keyPrefix);
35
- }
@@ -22,7 +22,7 @@ import { type CloudRouteDef } from "../shell/cloud-route-registry.js";
22
22
  export { ApiKeysSurface, default as ApiKeysRoute } from "./ApiKeysRoute.js";
23
23
  export { ApiKeysSection } from "./ApiKeysSection.js";
24
24
  export { ApiKeysView } from "./ApiKeysView.js";
25
- export { copyApiKeyPrefix, copyApiKeyToClipboard } from "./copy-api-key.js";
25
+ export { copyApiKeyToClipboard } from "./copy-api-key.js";
26
26
  export { API_KEYS_QUERY_KEY, type ApiKeyRecord, useApiKeys, } from "./use-api-keys.js";
27
27
  /** Stable settings-section id + URL hash for the API-keys surface. */
28
28
  export declare const API_KEYS_SECTION_ID = "api-keys";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/api-keys/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAK9C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,aAI/B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAChC,IAAI,CAEN"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/api-keys/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAK9C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,aAI/B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAChC,IAAI,CAEN"}
@@ -23,7 +23,7 @@ import { registerCloudRoute, } from "../shell/cloud-route-registry.js";
23
23
  export { ApiKeysSurface, default as ApiKeysRoute } from "./ApiKeysRoute.js";
24
24
  export { ApiKeysSection } from "./ApiKeysSection.js";
25
25
  export { ApiKeysView } from "./ApiKeysView.js";
26
- export { copyApiKeyPrefix, copyApiKeyToClipboard } from "./copy-api-key.js";
26
+ export { copyApiKeyToClipboard } from "./copy-api-key.js";
27
27
  export { API_KEYS_QUERY_KEY, useApiKeys, } from "./use-api-keys.js";
28
28
  /** Stable settings-section id + URL hash for the API-keys surface. */
29
29
  export const API_KEYS_SECTION_ID = "api-keys";
@@ -29,6 +29,18 @@ export interface CloudRouterShellProps {
29
29
  */
30
30
  appElement: ReactNode;
31
31
  }
32
+ /**
33
+ * Catch-all element. Renders the agent app exactly as before, EXCEPT on an apex
34
+ * control-plane host where the Steward session is resolved-and-unauthenticated —
35
+ * there it redirects to the registered cloud `/login` page (`returnTo`
36
+ * preserved) instead of booting the agent shell that would 401-wall. The apex
37
+ * 401-wall was a router fall-through: no route registers the apex root `/`, so
38
+ * an unauthenticated apex visitor hit this catch-all and booted the agent
39
+ * runtime against a backend that isn't there.
40
+ */
41
+ export declare function AppCatchAllRoute({ appElement, }: {
42
+ appElement: ReactNode;
43
+ }): React.JSX.Element;
32
44
  /**
33
45
  * The shell. Mounts the registered cloud routes + the `/dashboard/*` compat
34
46
  * redirects, and renders {@link CloudRouterShellProps.appElement} for every
@@ -1 +1 @@
1
- {"version":3,"file":"CloudRouterShell.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/shell/CloudRouterShell.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAsHf,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,GACX,EAAE,qBAAqB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAwC3C;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"CloudRouterShell.d.ts","sourceRoot":"","sources":["../../../../../../src/cloud/shell/CloudRouterShell.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAwHf,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;CACvB;AAsBD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,GACX,EAAE;IACD,UAAU,EAAE,SAAS,CAAC;CACvB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAUpB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,GACX,EAAE,qBAAqB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA6C3C;AAED,eAAe,gBAAgB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /**
3
3
  * Top-level react-router shell for the Eliza web app (web build only).
4
4
  *
@@ -24,7 +24,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
24
24
  import { QueryClientProvider } from "@tanstack/react-query";
25
25
  import { Suspense, } from "react";
26
26
  import { BrowserRouter, Navigate, Route, Routes, useLocation, useParams, } from "react-router-dom";
27
+ import { ELIZA_CLOUD_CONTROL_PLANE_HOSTS } from "../../utils/cloud-agent-base.js";
27
28
  import { queryClient } from "../lib/query-client.js";
29
+ import { useSessionAuth } from "../lib/use-session-auth.js";
28
30
  import { CloudI18nProvider, resolveInitialCloudLang, } from "./CloudI18nProvider.js";
29
31
  import { listCloudRoutes } from "./cloud-route-registry.js";
30
32
  import { StewardAuthProvider } from "./StewardProvider.js";
@@ -97,6 +99,40 @@ function CloudRouteElement({ route, }) {
97
99
  }
98
100
  return _jsx(StewardAuthProvider, { children: body });
99
101
  }
102
+ /**
103
+ * Apex control-plane hosts that serve THIS console UI but have no same-origin
104
+ * agent backend — so an unauthenticated visitor must land on the Steward
105
+ * `/login` page, not the agent shell (which 401-walls on `/api/*`).
106
+ * `api.elizacloud.ai` is the API origin (it never serves this shell); per-agent
107
+ * `<id>.elizacloud.ai` subdomains are NOT in the control-plane set and boot
108
+ * their real runtime, so they fall through untouched.
109
+ */
110
+ const APEX_UI_CONTROL_PLANE_HOSTS = new Set(
111
+ // Exclude the API origins (api. / api-staging.) — they never serve this shell.
112
+ [...ELIZA_CLOUD_CONTROL_PLANE_HOSTS].filter((h) => !/^api[.-]/.test(h)));
113
+ function isApexControlPlaneHost() {
114
+ if (typeof window === "undefined")
115
+ return false;
116
+ return APEX_UI_CONTROL_PLANE_HOSTS.has(window.location.hostname.toLowerCase());
117
+ }
118
+ /**
119
+ * Catch-all element. Renders the agent app exactly as before, EXCEPT on an apex
120
+ * control-plane host where the Steward session is resolved-and-unauthenticated —
121
+ * there it redirects to the registered cloud `/login` page (`returnTo`
122
+ * preserved) instead of booting the agent shell that would 401-wall. The apex
123
+ * 401-wall was a router fall-through: no route registers the apex root `/`, so
124
+ * an unauthenticated apex visitor hit this catch-all and booted the agent
125
+ * runtime against a backend that isn't there.
126
+ */
127
+ export function AppCatchAllRoute({ appElement, }) {
128
+ const { ready, authenticated } = useSessionAuth();
129
+ const location = useLocation();
130
+ if (isApexControlPlaneHost() && ready && !authenticated) {
131
+ const returnTo = encodeURIComponent(`${location.pathname}${location.search}`);
132
+ return _jsx(Navigate, { to: `/login?returnTo=${returnTo}`, replace: true });
133
+ }
134
+ return _jsx(_Fragment, { children: appElement });
135
+ }
100
136
  /**
101
137
  * The shell. Mounts the registered cloud routes + the `/dashboard/*` compat
102
138
  * redirects, and renders {@link CloudRouterShellProps.appElement} for every
@@ -104,6 +140,6 @@ function CloudRouteElement({ route, }) {
104
140
  */
105
141
  export function CloudRouterShell({ appElement, }) {
106
142
  const cloudRoutes = listCloudRoutes();
107
- return (_jsx(BrowserRouter, { children: _jsx(CloudProviders, { children: _jsxs(Routes, { children: [cloudRoutes.map((route) => (_jsx(Route, { path: route.path, element: _jsx(CloudRouteElement, { route: route }) }, route.path))), DASHBOARD_REDIRECTS.map(({ from, to }) => (_jsx(Route, { path: from, element: _jsx(ParamRedirect, { to: to }) }, from))), _jsx(Route, { path: "dashboard/*", element: _jsx(CloudNotFound, {}) }), _jsx(Route, { path: "*", element: appElement })] }) }) }));
143
+ return (_jsx(BrowserRouter, { children: _jsx(CloudProviders, { children: _jsxs(Routes, { children: [cloudRoutes.map((route) => (_jsx(Route, { path: route.path, element: _jsx(CloudRouteElement, { route: route }) }, route.path))), DASHBOARD_REDIRECTS.map(({ from, to }) => (_jsx(Route, { path: from, element: _jsx(ParamRedirect, { to: to }) }, from))), _jsx(Route, { path: "dashboard/*", element: _jsx(CloudNotFound, {}) }), _jsx(Route, { path: "*", element: _jsx(AppCatchAllRoute, { appElement: appElement }) })] }) }) }));
108
144
  }
109
145
  export default CloudRouterShell;
@@ -13,10 +13,9 @@ export interface ApiKeyDisplay {
13
13
  }
14
14
  export interface ApiKeysTableProps {
15
15
  keys: ApiKeyDisplay[];
16
- onCopyKey?: (id: string) => void;
17
16
  onDisableKey?: (id: string) => void;
18
17
  onDeleteKey?: (id: string) => void;
19
18
  onRegenerateKey?: (id: string) => void;
20
19
  }
21
- export declare function ApiKeysTable({ keys, onCopyKey, onDisableKey, onDeleteKey, onRegenerateKey, }: ApiKeysTableProps): import("react/jsx-runtime").JSX.Element | null;
20
+ export declare function ApiKeysTable({ keys, onDisableKey, onDeleteKey, onRegenerateKey, }: ApiKeysTableProps): import("react/jsx-runtime").JSX.Element | null;
22
21
  //# sourceMappingURL=api-keys-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-keys-table.d.ts","sourceRoot":"","sources":["../../../../../../../src/cloud-ui/components/data-list/api-keys-table.tsx"],"names":[],"mappings":"AAuBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AA6ED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,WAAW,EACX,eAAe,GAChB,EAAE,iBAAiB,kDAuLnB"}
1
+ {"version":3,"file":"api-keys-table.d.ts","sourceRoot":"","sources":["../../../../../../../src/cloud-ui/components/data-list/api-keys-table.tsx"],"names":[],"mappings":"AAiBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAwED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,eAAe,GAChB,EAAE,iBAAiB,kDAoKnB"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { CalendarClock, Copy, RefreshCw, ShieldOff, Trash2, } from "lucide-react";
2
+ import { CalendarClock, RefreshCw, ShieldOff, Trash2 } from "lucide-react";
3
3
  import { StatusBadge } from "../../../components/ui/status-badge.js";
4
4
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../../../components/ui/table.js";
5
5
  import { BrandButton } from "../brand/brand-button.js";
@@ -39,11 +39,6 @@ function formatDate(value) {
39
39
  }
40
40
  function renderActions(key, handlers, triggerClassName) {
41
41
  return (_jsx(ListActionMenu, { label: "Manage key", triggerClassName: triggerClassName, items: [
42
- {
43
- label: "Copy key",
44
- icon: Copy,
45
- onSelect: () => handlers.onCopyKey?.(key.id),
46
- },
47
42
  {
48
43
  label: "Regenerate key",
49
44
  icon: RefreshCw,
@@ -63,17 +58,16 @@ function renderActions(key, handlers, triggerClassName) {
63
58
  },
64
59
  ] }));
65
60
  }
66
- export function ApiKeysTable({ keys, onCopyKey, onDisableKey, onDeleteKey, onRegenerateKey, }) {
61
+ export function ApiKeysTable({ keys, onDisableKey, onDeleteKey, onRegenerateKey, }) {
67
62
  if (keys.length === 0) {
68
63
  return null;
69
64
  }
70
65
  const handlers = {
71
- onCopyKey,
72
66
  onDisableKey,
73
67
  onDeleteKey,
74
68
  onRegenerateKey,
75
69
  };
76
- return (_jsxs(_Fragment, { children: [_jsx(DashboardDataListMobile, { className: "space-y-3", children: keys.map((key) => (_jsxs("div", { className: "space-y-3 rounded-sm border border-white/10 bg-black/40 p-4", children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate font-semibold text-white", children: key.name }), _jsx(StatusBadge, { status: getStatusVariant(key.status), label: getStatusLabel(key.status) })] }), key.description ? (_jsx("p", { className: "line-clamp-2 text-xs text-white/60", children: key.description })) : null] }), renderActions(key, handlers)] }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2 text-xs text-white/60", children: [_jsx("span", { className: "rounded-sm border border-white/10 bg-black/60 px-1.5 py-0.5 font-mono text-xs text-white", children: `${key.keyPrefix}.......` }), _jsxs(BrandButton, { variant: "ghost", size: "sm", className: "h-8 px-2", onClick: () => onCopyKey?.(key.id), children: [_jsx(Copy, { className: "mr-1 h-3.5 w-3.5" }), "Copy"] }), _jsxs(BrandButton, { variant: "ghost", size: "sm", className: "h-8 px-2", onClick: () => onRegenerateKey?.(key.id), children: [_jsx(RefreshCw, { className: "mr-1 h-3.5 w-3.5" }), "Regenerate"] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-3 border-t border-white/10 pt-3 text-xs", children: [_jsxs("div", { children: [_jsx("p", { className: "text-white/40", children: "Usage" }), _jsxs("p", { className: "mt-1 font-medium text-white", children: [key.usageCount.toLocaleString(), " requests"] }), _jsxs("p", { className: "mt-0.5 text-white/74", children: [key.rateLimit.toLocaleString(), " / min"] })] }), _jsxs("div", { children: [_jsx("p", { className: "text-white/40", children: "Timeline" }), _jsxs("p", { className: "mt-1 text-white/60", children: ["Created ", formatDate(key.createdAt)] }), _jsxs("p", { className: "mt-0.5 text-white/60", children: ["Last used ", formatDate(key.lastUsedAt)] })] })] })] }, key.id))) }), _jsx(DashboardDataListDesktop, { className: "border-0", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Key" }), _jsx(TableHead, { children: "Usage" }), _jsx(TableHead, { children: "Timeline" }), _jsx(TableHead, { className: "text-right", children: "Actions" })] }) }), _jsx(TableBody, { children: keys.map((key) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-semibold text-white", children: key.name }), _jsx(StatusBadge, { status: getStatusVariant(key.status), label: getStatusLabel(key.status) })] }), key.description ? (_jsx("p", { className: "text-xs text-white/60", children: key.description })) : null, _jsxs("div", { className: "flex items-center gap-2 text-xs text-white/60", children: [_jsx("span", { className: "rounded-sm border border-white/10 bg-black/60 px-1.5 py-0.5 font-mono text-xs text-white", children: `${key.keyPrefix}.......` }), _jsxs(BrandButton, { variant: "ghost", size: "sm", className: "h-8 px-2", onClick: () => onCopyKey?.(key.id), children: [_jsx(Copy, { className: "mr-1 h-3.5 w-3.5" }), "Copy"] }), _jsxs(BrandButton, { variant: "ghost", size: "sm", className: "h-8 px-2", onClick: () => onRegenerateKey?.(key.id), children: [_jsx(RefreshCw, { className: "mr-1 h-3.5 w-3.5" }), "Regenerate"] })] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("span", { className: "font-medium text-white", children: [key.usageCount.toLocaleString(), " requests"] }), _jsxs("p", { className: "text-xs text-white/74", children: ["Rate limit ", key.rateLimit.toLocaleString(), " / min"] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex flex-col gap-2 text-xs text-white/60", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-3.5 w-3.5 text-[#FF5800]" }), _jsxs("span", { children: ["Created ", formatDate(key.createdAt)] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-3.5 w-3.5 text-[#FF5800]" }), _jsxs("span", { children: ["Last used ", formatDate(key.lastUsedAt)] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-3.5 w-3.5 text-[#FF5800]" }), _jsx("span", { children: key.expiresAt
70
+ return (_jsxs(_Fragment, { children: [_jsx(DashboardDataListMobile, { className: "space-y-3", children: keys.map((key) => (_jsxs("div", { className: "space-y-3 rounded-sm border border-white/10 bg-black/40 p-4", children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate font-semibold text-white", children: key.name }), _jsx(StatusBadge, { status: getStatusVariant(key.status), label: getStatusLabel(key.status) })] }), key.description ? (_jsx("p", { className: "line-clamp-2 text-xs text-white/60", children: key.description })) : null] }), renderActions(key, handlers)] }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2 text-xs text-white/60", children: [_jsx("span", { className: "rounded-sm border border-white/10 bg-black/60 px-1.5 py-0.5 font-mono text-xs text-white", children: `${key.keyPrefix}.......` }), _jsxs(BrandButton, { variant: "ghost", size: "sm", className: "h-8 px-2", onClick: () => onRegenerateKey?.(key.id), children: [_jsx(RefreshCw, { className: "mr-1 h-3.5 w-3.5" }), "Regenerate"] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-3 border-t border-white/10 pt-3 text-xs", children: [_jsxs("div", { children: [_jsx("p", { className: "text-white/40", children: "Usage" }), _jsxs("p", { className: "mt-1 font-medium text-white", children: [key.usageCount.toLocaleString(), " requests"] }), _jsxs("p", { className: "mt-0.5 text-white/74", children: [key.rateLimit.toLocaleString(), " / min"] })] }), _jsxs("div", { children: [_jsx("p", { className: "text-white/40", children: "Timeline" }), _jsxs("p", { className: "mt-1 text-white/60", children: ["Created ", formatDate(key.createdAt)] }), _jsxs("p", { className: "mt-0.5 text-white/60", children: ["Last used ", formatDate(key.lastUsedAt)] })] })] })] }, key.id))) }), _jsx(DashboardDataListDesktop, { className: "border-0", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Key" }), _jsx(TableHead, { children: "Usage" }), _jsx(TableHead, { children: "Timeline" }), _jsx(TableHead, { className: "text-right", children: "Actions" })] }) }), _jsx(TableBody, { children: keys.map((key) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-semibold text-white", children: key.name }), _jsx(StatusBadge, { status: getStatusVariant(key.status), label: getStatusLabel(key.status) })] }), key.description ? (_jsx("p", { className: "text-xs text-white/60", children: key.description })) : null, _jsxs("div", { className: "flex items-center gap-2 text-xs text-white/60", children: [_jsx("span", { className: "rounded-sm border border-white/10 bg-black/60 px-1.5 py-0.5 font-mono text-xs text-white", children: `${key.keyPrefix}.......` }), _jsxs(BrandButton, { variant: "ghost", size: "sm", className: "h-8 px-2", onClick: () => onRegenerateKey?.(key.id), children: [_jsx(RefreshCw, { className: "mr-1 h-3.5 w-3.5" }), "Regenerate"] })] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("span", { className: "font-medium text-white", children: [key.usageCount.toLocaleString(), " requests"] }), _jsxs("p", { className: "text-xs text-white/74", children: ["Rate limit ", key.rateLimit.toLocaleString(), " / min"] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex flex-col gap-2 text-xs text-white/60", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-3.5 w-3.5 text-[#FF5800]" }), _jsxs("span", { children: ["Created ", formatDate(key.createdAt)] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-3.5 w-3.5 text-[#FF5800]" }), _jsxs("span", { children: ["Last used ", formatDate(key.lastUsedAt)] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-3.5 w-3.5 text-[#FF5800]" }), _jsx("span", { children: key.expiresAt
77
71
  ? `Expires ${formatDate(key.expiresAt)}`
78
72
  : "No expiry" })] })] }) }), _jsx(TableCell, { className: "text-right", children: renderActions(key, handlers, "h-9 w-9") })] }, key.id))) })] }) })] }));
79
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-route-explorer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/cloud-ui/components/docs/api-route-explorer-client.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAc,MAAM,uBAAuB,CAAC;AAoG/E,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,GACP,EAAE;IACD,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC,2CAogBA"}
1
+ {"version":3,"file":"api-route-explorer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/cloud-ui/components/docs/api-route-explorer-client.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAc,MAAM,uBAAuB,CAAC;AAqG/E,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,GACP,EAAE;IACD,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC,2CAogBA"}
@@ -32,7 +32,8 @@ function methodBadgeClass(method) {
32
32
  case "GET":
33
33
  return `${base} bg-emerald-500/10 text-emerald-400 border-emerald-500/30 `;
34
34
  case "POST":
35
- return `${base} bg-blue-500/10 text-blue-400 border-blue-500/30 `;
35
+ // Brand rule: no blue. Slate keeps POST distinct from the amber PUT.
36
+ return `${base} bg-slate-500/10 text-slate-300 border-slate-500/30 `;
36
37
  case "PUT":
37
38
  return `${base} bg-amber-500/10 text-amber-400 border-amber-500/30 `;
38
39
  case "PATCH":
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-route-page.d.ts","sourceRoot":"","sources":["../../../../../../../src/cloud-ui/components/layout/dashboard-route-page.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,wBAAwB,EACxB,cAAc,EACd,SAAS,EACV,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,KAAK,4BAA4B,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7E,KAAK,gCAAgC,GAAG,IAAI,CAC1C,wBAAwB,CAAC,OAAO,sBAAsB,CAAC,EACvD,UAAU,CACX,CAAC;AAEF,KAAK,4BAA4B,GAAG,IAAI,CACtC,wBAAwB,CAAC,OAAO,kBAAkB,CAAC,EACnD,UAAU,CACX,CAAC;AAEF,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,GAAG,gCAAgC,CAAC;IACvD,KAAK,CAAC,EAAE,OAAO,GAAG,4BAA4B,CAAC;IAC/C,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAiBD,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,WAAW,EACX,OAAO,EACP,UAAe,EACf,QAAQ,EACR,SAAS,EACT,KAAK,EACL,MAAM,EACN,UAAmB,EACnB,eAAe,GAChB,EAAE,uBAAuB,2CAuCzB;AAED,YAAY,EACV,4BAA4B,EAC5B,gCAAgC,EAChC,uBAAuB,EACvB,4BAA4B,GAC7B,CAAC"}
1
+ {"version":3,"file":"dashboard-route-page.d.ts","sourceRoot":"","sources":["../../../../../../../src/cloud-ui/components/layout/dashboard-route-page.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,wBAAwB,EACxB,cAAc,EACd,SAAS,EACV,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,KAAK,4BAA4B,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7E,KAAK,gCAAgC,GAAG,IAAI,CAC1C,wBAAwB,CAAC,OAAO,sBAAsB,CAAC,EACvD,UAAU,CACX,CAAC;AAEF,KAAK,4BAA4B,GAAG,IAAI,CACtC,wBAAwB,CAAC,OAAO,kBAAkB,CAAC,EACnD,UAAU,CACX,CAAC;AAEF,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,GAAG,gCAAgC,CAAC;IACvD,KAAK,CAAC,EAAE,OAAO,GAAG,4BAA4B,CAAC;IAC/C,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAmBD,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,WAAW,EACX,OAAO,EACP,UAAe,EACf,QAAQ,EACR,SAAS,EACT,KAAK,EACL,MAAM,EACN,UAAmB,EACnB,eAAe,GAChB,EAAE,uBAAuB,2CAuCzB;AAED,YAAY,EACV,4BAA4B,EAC5B,gCAAgC,EAChC,uBAAuB,EACvB,4BAA4B,GAC7B,CAAC"}
@@ -4,7 +4,9 @@ import { cn } from "../../lib/utils.js";
4
4
  import { DashboardPageContainer, DashboardPageStack } from "./dashboard-page.js";
5
5
  import { useSetPageHeader } from "./page-header-context.hooks.js";
6
6
  const bannerTones = {
7
- info: "border-blue-400/30 bg-blue-400/10 text-blue-100",
7
+ // Brand rule: no blue. Slate reads as neutral "info" alongside the
8
+ // emerald/amber/red tones below.
9
+ info: "border-slate-400/30 bg-slate-400/10 text-slate-100",
8
10
  success: "border-emerald-400/30 bg-emerald-400/10 text-emerald-100",
9
11
  warning: "border-amber-400/30 bg-amber-400/10 text-amber-100",
10
12
  error: "border-red-500/40 bg-red-500/10 text-red-400",
@@ -25,5 +25,5 @@ export function ConfigField({ renderProps, renderer, pluginId, }) {
25
25
  }, children: [_jsx("span", { className: "shrink-0 mt-px", children: t("config-field.Times") }), _jsx("span", { children: err })] }, err))) })), (renderProps.hint.help || renderProps.schema.description) && (_jsxs("div", { className: "mt-1 leading-relaxed line-clamp-2", style: {
26
26
  fontSize: "var(--plugin-help-size)",
27
27
  color: "var(--plugin-help)",
28
- }, children: [renderProps.hint.help ?? renderProps.schema.description, renderProps.schema.default != null && (_jsxs("span", { className: "opacity-70", children: [" ", t("common.default", { defaultValue: "Default" }), " ", String(renderProps.schema.default), ")"] }))] }))] })] }));
28
+ }, children: [renderProps.hint.help ?? renderProps.schema.description, renderProps.schema.default != null && (_jsxs("span", { className: "opacity-90", children: [" ", t("common.default", { defaultValue: "Default" }), " ", String(renderProps.schema.default), ")"] }))] }))] })] }));
29
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Springboard.d.ts","sourceRoot":"","sources":["../../../../../../src/components/pages/Springboard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAc1D,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,8EAA8E;IAC9E,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC;+CAC2C;IAC3C,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmKD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,OAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,YAAY,EACZ,IAAI,EAAE,QAAQ,EACd,YAAY,EACZ,iBAAiB,EACjB,OAAO,EAAE,WAAW,EACpB,eAAe,EACf,YAAmB,EACnB,SAAS,GACV,EAAE,gBAAgB,2CA0SlB"}
1
+ {"version":3,"file":"Springboard.d.ts","sourceRoot":"","sources":["../../../../../../src/components/pages/Springboard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAc1D,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,8EAA8E;IAC9E,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC;+CAC2C;IAC3C,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8JD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,OAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,YAAY,EACZ,IAAI,EAAE,QAAQ,EACd,YAAY,EACZ,iBAAiB,EACjB,OAAO,EAAE,WAAW,EACpB,eAAe,EACf,YAAmB,EACnB,SAAS,GACV,EAAE,gBAAgB,2CA0UlB"}
@@ -13,8 +13,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  * the shared root `AppBackground` shows through, matching the home screen.
14
14
  */
15
15
  import { Pencil, Trash2 } from "lucide-react";
16
- import { motion, Reorder } from "motion/react";
16
+ import { Reorder } from "motion/react";
17
17
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
18
+ import { useHorizontalPager } from "../../hooks/useHorizontalPager.js";
18
19
  import { cn } from "../../lib/utils.js";
19
20
  import { moveIcon, readSpringboardLayout, reconcileLayout, SPRINGBOARD_DOCK_LIMIT, toggleFavorite, writeSpringboardLayout, } from "../../state/springboard-layout.js";
20
21
  import { emitViewInteraction } from "../../view-telemetry.js";
@@ -23,8 +24,6 @@ const LONG_PRESS_MS = 450;
23
24
  /** Finger travel (px) that aborts a long-press — a pan/swipe is not a press, so
24
25
  * a horizontal swipe-back can never ghost-fire edit mode mid-gesture. */
25
26
  const LONG_PRESS_MOVE_SLOP = 10;
26
- /** Horizontal drag distance (px) needed to flip to the adjacent page. */
27
- const SWIPE_THRESHOLD = 60;
28
27
  // Memoized so a layout reconcile (install/uninstall/sort) re-renders only the
29
28
  // tiles whose props actually changed, not all ~20 on a page.
30
29
  const IconTile = memo(function IconTile({ entry, editing, favorited, manageable, onLaunch, onToggleFavorite, onEdit, onDelete, onLongPress, }) {
@@ -62,12 +61,9 @@ const IconTile = memo(function IconTile({ entry, editing, favorited, manageable,
62
61
  // gesture interrupts the press — clear the timer so a long-press never
63
62
  // ghost-fires edit mode after the user finishes scrolling.
64
63
  onPointerCancel: clear, className: cn(
65
- // ViewTileImage handles image-vs-glyph internally, so the button is
66
- // one constant surface. The hero image (object-cover) covers the
67
- // tile when it loads; this dark neutral fill + white glyph is the
68
- // fallback so a tile reads as a real app icon (not a bare glyph
69
- // floating on the background) even before/without the image. Filter
70
- // effects (#9281) and focus rings (#9292) were removed on develop.
64
+ // ViewTileImage renders this surface as an app icon, not as a
65
+ // cropped catalog preview. The button stays one constant hit target
66
+ // and owns hover/focus chrome; the inner visual owns color/glyph.
71
67
  "h-16 w-16 overflow-hidden rounded-2xl border border-white/10 bg-black/35 text-white transition-colors hover:bg-black/45", editing && "animate-pulse"), children: _jsx(ViewTileImage, { entry: entry, source: "springboard", containerClassName: "grid h-full w-full place-items-center", glyphClassName: "h-7 w-7", imageTestId: `springboard-image-${entry.id}` }) }), editing ? (_jsx("button", { type: "button", "aria-label": favorited ? `Unpin ${entry.label}` : `Pin ${entry.label}`, "data-testid": `springboard-fav-${entry.id}`, onClick: (e) => {
72
68
  e.stopPropagation();
73
69
  onToggleFavorite(entry.id);
@@ -199,40 +195,33 @@ export function Springboard({ entries, loading = false, onLaunch, onEdgeSwipeRig
199
195
  onDeleteView,
200
196
  toggleEditMode,
201
197
  ]);
202
- return (_jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col", className), "data-testid": "springboard", children: [favoriteEntries.length > 0 ? (_jsx("div", { "data-testid": "springboard-dock", className: "mx-3 mt-2 mb-3 flex items-center justify-center gap-3 rounded-3xl border border-white/10 bg-black/45 px-3 py-3 sm:mx-4 sm:gap-4 sm:px-6", children: favoriteEntries.map((entry) => (_jsx("div", { children: renderTile(entry, true) }, `dock-${entry.id}`))) })) : null, _jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col overflow-hidden", children: [_jsx(motion.div
203
- // A stable identity (NOT `key={clampedPage}`): keying on the page
204
- // index forced a full unmount/remount of the page — Reorder.Group and
205
- // every tile on each swipe, which janks the paging. The page content
206
- // already swaps via `pages[clampedPage]`, and `dragSnapToOrigin`
207
- // animates the drag back to origin, so the swap is smooth without a
208
- // remount (#9304).
209
- , { drag: editing ? false : "x", dragConstraints: { left: 0, right: 0 }, dragElastic: 0.2, dragSnapToOrigin: true, onDragEnd: (_event, info) => {
210
- if (editing)
211
- return;
212
- if (info.offset.x < -SWIPE_THRESHOLD &&
213
- clampedPage < pages.length - 1) {
214
- setActivePage(clampedPage + 1);
215
- emitViewInteraction({
216
- source: "springboard",
217
- action: "page-swipe",
218
- count: clampedPage + 1,
219
- });
220
- }
221
- else if (info.offset.x > SWIPE_THRESHOLD && clampedPage > 0) {
222
- setActivePage(clampedPage - 1);
223
- emitViewInteraction({
224
- source: "springboard",
225
- action: "page-swipe",
226
- count: clampedPage - 1,
227
- });
228
- }
229
- else if (info.offset.x > SWIPE_THRESHOLD && clampedPage === 0) {
230
- onEdgeSwipeRight?.();
231
- }
232
- }, className: "flex min-h-0 flex-1 items-start justify-center overflow-y-auto px-6 pt-2 pb-8", children: loading && entries.length === 0 ? (_jsx("div", { className: "grid w-full max-w-2xl grid-cols-4 gap-x-4 gap-y-5 sm:grid-cols-5", children: ["a", "b", "c", "d", "e", "f", "g", "h"].map((id) => (_jsxs("div", { className: "flex flex-col items-center gap-1.5 opacity-60", children: [_jsx("div", { className: "h-16 w-16 rounded-2xl bg-bg-accent/50" }), _jsx("div", { className: "h-2.5 w-12 rounded-full bg-bg-accent/50" })] }, id))) })) : (_jsx(Reorder.Group, { axis: "y", values: pages[clampedPage] ?? [], onReorder: (next) => handleReorder(clampedPage, next), className: "grid w-full max-w-2xl grid-cols-4 gap-x-4 gap-y-5 sm:grid-cols-5", children: (pages[clampedPage] ?? []).map((id) => {
233
- const entry = byId.get(id);
234
- if (!entry)
235
- return null;
236
- return (_jsx(Reorder.Item, { value: id, drag: editing, dragListener: editing, className: "flex justify-center", children: renderTile(entry, favoriteSet.has(id)) }, id));
237
- }) })) }, "springboard-page"), showPageDots && pages.length > 1 ? (_jsx("div", { className: "flex items-center justify-center gap-2 pb-3", children: pages.map((pageIds, index) => (_jsx("button", { type: "button", "aria-label": `Page ${index + 1}`, "aria-current": index === clampedPage, onClick: () => setActivePage(index), className: cn("h-2 w-2 rounded-full transition-colors", index === clampedPage ? "bg-accent" : "bg-border") }, `dot-${index}-${pageIds[0] ?? "empty"}`))) })) : null] })] }));
198
+ const pager = useHorizontalPager({
199
+ page: clampedPage,
200
+ pageCount: pages.length,
201
+ enabled: !editing && pages.length > 1,
202
+ edgeSwipeRightEnabled: showPageDots && onEdgeSwipeRight != null,
203
+ onEdgeSwipeRight,
204
+ onPageChange: (nextPage) => {
205
+ setActivePage(nextPage);
206
+ emitViewInteraction({
207
+ source: "springboard",
208
+ action: "page-swipe",
209
+ count: nextPage,
210
+ });
211
+ },
212
+ });
213
+ return (_jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col", className), "data-testid": "springboard", children: [favoriteEntries.length > 0 ? (_jsx("div", { "data-testid": "springboard-dock", className: "mx-3 mt-2 mb-3 flex items-center justify-center gap-3 rounded-3xl border border-white/10 bg-black/45 px-3 py-3 sm:mx-4 sm:gap-4 sm:px-6", children: favoriteEntries.map((entry) => (_jsx("div", { children: renderTile(entry, true) }, `dock-${entry.id}`))) })) : null, _jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col overflow-hidden", children: [_jsx("div", { ref: pager.viewportRef, "data-testid": "springboard-page-window", className: "relative flex min-h-0 flex-1 overflow-hidden touch-pan-y", onPointerDown: pager.handlers.onPointerDown, onPointerMove: pager.handlers.onPointerMove, onPointerUp: pager.handlers.onPointerUp, onPointerCancel: pager.handlers.onPointerCancel, onLostPointerCapture: pager.handlers.onLostPointerCapture, children: _jsx("div", { ref: pager.railRef, "data-testid": "springboard-page-rail", className: "flex h-full min-h-0 w-full motion-reduce:transition-none", children: loading && entries.length === 0 ? (_jsx("div", { className: "flex h-full min-h-0 min-w-full items-start justify-center overflow-y-auto px-6 pt-2 pb-8", children: _jsx("div", { className: "grid w-full max-w-2xl grid-cols-4 gap-x-4 gap-y-5 sm:grid-cols-5", children: ["a", "b", "c", "d", "e", "f", "g", "h"].map((id) => (_jsxs("div", { className: "flex flex-col items-center gap-1.5 opacity-60", children: [_jsx("div", { className: "h-16 w-16 rounded-2xl bg-bg-accent/50" }), _jsx("div", { className: "h-2.5 w-12 rounded-full bg-bg-accent/50" })] }, id))) }) })) : (pages.map((pageIds, pageIndex) => {
214
+ const active = pageIndex === clampedPage;
215
+ return (_jsx("div", { "data-testid": `springboard-page-${pageIndex}`, "aria-hidden": !active, inert: !active || undefined, className: cn("flex h-full min-h-0 min-w-full items-start justify-center overflow-y-auto px-6 pt-2 pb-8", !active && "pointer-events-none"), children: editing && active ? (_jsx(Reorder.Group, { axis: "y", values: pageIds, onReorder: (next) => handleReorder(pageIndex, next), className: "grid w-full max-w-2xl grid-cols-4 gap-x-4 gap-y-5 sm:grid-cols-5", children: pageIds.map((id) => {
216
+ const entry = byId.get(id);
217
+ if (!entry)
218
+ return null;
219
+ return (_jsx(Reorder.Item, { value: id, drag: editing, dragListener: editing, className: "flex justify-center", children: renderTile(entry, favoriteSet.has(id)) }, id));
220
+ }) })) : (_jsx("div", { className: "grid w-full max-w-2xl grid-cols-4 gap-x-4 gap-y-5 sm:grid-cols-5", children: pageIds.map((id) => {
221
+ const entry = byId.get(id);
222
+ if (!entry)
223
+ return null;
224
+ return (_jsx("div", { className: "flex justify-center", children: renderTile(entry, favoriteSet.has(id)) }, id));
225
+ }) })) }, `springboard-page-${pageIndex}`));
226
+ })) }) }), showPageDots && pages.length > 1 ? (_jsx("div", { className: "flex items-center justify-center gap-2 pb-3", children: pages.map((pageIds, index) => (_jsx("button", { type: "button", "aria-label": `Page ${index + 1}`, "aria-current": index === clampedPage, onClick: () => setActivePage(index), className: cn("h-2 w-2 rounded-full transition-colors", index === clampedPage ? "bg-accent" : "bg-border") }, `dot-${index}-${pageIds[0] ?? "empty"}`))) })) : null] })] }));
238
227
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SpringboardSurface.d.ts","sourceRoot":"","sources":["../../../../../../src/components/pages/SpringboardSurface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA2F/B,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,eAAO,MAAM,kBAAkB,0DAE5B,uBAAuB,KAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAgF5C,CAAC"}
1
+ {"version":3,"file":"SpringboardSurface.d.ts","sourceRoot":"","sources":["../../../../../../src/components/pages/SpringboardSurface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAsI/B,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,eAAO,MAAM,kBAAkB,0DAE5B,uBAAuB,KAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAmR5C,CAAC"}