@rebasepro/studio 0.16.1-canary.gef08a6e → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/studio",
3
3
  "type": "module",
4
- "version": "0.16.1-canary.gef08a6e",
4
+ "version": "0.17.0",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.es.js",
7
7
  "module": "./dist/index.es.js",
@@ -16,22 +16,22 @@
16
16
  "pgsql-ast-parser": "12.0.2",
17
17
  "prism-react-renderer": "^2.4.1",
18
18
  "react-dropzone": "^19.1.1",
19
- "@rebasepro/admin-types": "0.16.1-canary.gef08a6e",
20
- "@rebasepro/app": "0.16.1-canary.gef08a6e",
21
- "@rebasepro/common": "0.16.1-canary.gef08a6e",
22
- "@rebasepro/client": "0.16.1-canary.gef08a6e",
23
- "@rebasepro/types": "0.16.1-canary.gef08a6e",
24
- "@rebasepro/ui": "0.16.1-canary.gef08a6e",
25
- "@rebasepro/utils": "0.16.1-canary.gef08a6e"
19
+ "@rebasepro/cms-types": "0.17.0",
20
+ "@rebasepro/client": "0.17.0",
21
+ "@rebasepro/common": "0.17.0",
22
+ "@rebasepro/types": "0.17.0",
23
+ "@rebasepro/ui": "0.17.0",
24
+ "@rebasepro/app": "0.17.0",
25
+ "@rebasepro/utils": "0.17.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": ">=19.2.7",
29
29
  "react-dom": ">=19.2.7",
30
30
  "react-router": "^8.3.0",
31
- "@rebasepro/admin": "0.16.1-canary.gef08a6e"
31
+ "@rebasepro/cms": "0.17.0"
32
32
  },
33
33
  "peerDependenciesMeta": {
34
- "@rebasepro/admin": {
34
+ "@rebasepro/cms": {
35
35
  "optional": true
36
36
  }
37
37
  },
@@ -42,7 +42,7 @@ import { createRebaseClient } from "@rebasepro/client";
42
42
  import { JSMonacoEditor } from "./JSMonacoEditor";
43
43
  import { JSEditorSidebar, JSSnippet } from "./JSEditorSidebar";
44
44
  import { AuthSimulationSelector } from "../AuthSimulationSelector";
45
- import type { AdminCollection } from "@rebasepro/admin-types";
45
+ import type { AdminCollection } from "@rebasepro/cms-types";
46
46
 
47
47
  // ─── Types ───────────────────────────────────────────────────────────
48
48
 
@@ -149,7 +149,7 @@ interface AdminUser {
149
149
  updatedAt: string;
150
150
  }
151
151
 
152
- interface RebaseAdmin {
152
+ interface RebaseCMS {
153
153
  listUsers(): Promise<{ users: AdminUser[] }>;
154
154
  getUser(userId: string): Promise<{ user: AdminUser }>;
155
155
  createUser(data: { email: string; displayName?: string; password?: string; roles?: string[] }): Promise<{ user: AdminUser }>;
@@ -223,7 +223,7 @@ interface RebaseClient {
223
223
  /** Authentication methods */
224
224
  auth: RebaseAuth;
225
225
  /** User/role admin methods */
226
- admin: RebaseAdmin;
226
+ admin: RebaseCMS;
227
227
  /** Storage operations */
228
228
  storage?: StorageSource;
229
229
  /** Call a custom server-side endpoint */
@@ -1,6 +1,6 @@
1
1
  import React, { Suspense, useLayoutEffect, useMemo } from "react";
2
2
  import { useRebaseRegistryDispatch } from "@rebasepro/app";
3
- import type { RebaseStudioConfig, AppView } from "@rebasepro/admin-types";
3
+ import type { RebaseStudioConfig, AppView } from "@rebasepro/cms-types";
4
4
  import { CircularProgressCenter, lazyChunk } from "@rebasepro/ui";
5
5
 
6
6
  // Lazy-loaded studio tools — each fetched only when its route is visited.
@@ -23,9 +23,12 @@ import { StudioHomePage } from "./StudioHomePage";
23
23
  * Declarative component to configure the Studio in Rebase.
24
24
  * Renders nothing — purely registers config into the RebaseRegistry.
25
25
  *
26
- * The "schema" tool (collection editor view) is now a built-in admin feature.
27
- * When `<RebaseAdmin collectionEditor={...}>` is used, the schema view is
28
- * automatically injected into Studio — no manual wiring needed.
26
+ * The "schema" tool (collection editor view) is a Studio tool, but it is not
27
+ * registered here: it ships from the panel package because it needs the project's
28
+ * collection source to write back to. When `<RebaseCMS collectionEditor={...}>`
29
+ * is used, the schema view is automatically injected into Studio — no manual
30
+ * wiring needed. Where it is mounted from is an implementation detail; it is a
31
+ * schema-editing tool and belongs beside SQL and RLS.
29
32
  */
30
33
  const DEFAULT_HOME_PAGE = <StudioHomePage/>;
31
34
 
@@ -37,7 +37,7 @@ import { useLiveRlsTables } from "./useLiveRls";
37
37
  import type { TableNodeData } from "./useSchemaGraph";
38
38
  import { TableNode } from "./TableNode";
39
39
  import { RelationEdge } from "./RelationEdge";
40
- import type { AdminCollection } from "@rebasepro/admin-types";
40
+ import type { AdminCollection } from "@rebasepro/cms-types";
41
41
 
42
42
  // ─── Custom node / edge type registrations ────────────────────────────
43
43
 
@@ -6,7 +6,7 @@ import type { Relation, ResolvedRelation } from "@rebasepro/types";
6
6
  import { resolveCollectionRelations } from "@rebasepro/common";
7
7
  import { getLayoutedElements, getCardinalityLabel, getTypeLabel, NODE_WIDTH } from "./schema-visualizer.utils";
8
8
  import type { LayoutDirection, RelationEdgeData } from "./schema-visualizer.utils";
9
- import type { AdminCollection, AdminPostgresCollection } from "@rebasepro/admin-types";
9
+ import type { AdminCollection, AdminPostgresCollection } from "@rebasepro/cms-types";
10
10
 
11
11
  // ─── Column info extracted from a collection ──────────────────────────
12
12
 
@@ -43,6 +43,7 @@ import {
43
43
  } from "@rebasepro/ui";
44
44
  import { useStorageSource, useStorageSources, useSnackbarController, ErrorView, useApiBase, useApiConfig } from "@rebasepro/app";
45
45
  import { DEFAULT_STORAGE_SOURCE_KEY, type StorageListResult } from "@rebasepro/types";
46
+ import { classifyStorageFailure, type StorageFailure } from "./storage-failure";
46
47
  import { useSearchParams } from "react-router";
47
48
  import { useDropzone } from "react-dropzone";
48
49
 
@@ -502,7 +503,8 @@ export const StorageView = () => {
502
503
  // working; only the namespaced one is ever written.
503
504
  const currentPath = searchParams.get(STORAGE_PATH_PARAM) || searchParams.get("path") || "";
504
505
  const [loading, setLoading] = useState(true);
505
- const [error, setError] = useState<string | null>(null);
506
+ /** Why the listing failed, classified — see `storage-failure.ts`. */
507
+ const [failure, setFailure] = useState<StorageFailure | null>(null);
506
508
 
507
509
  // Contents
508
510
  const [folders, setFolders] = useState<StorageFile[]>([]);
@@ -542,7 +544,7 @@ export const StorageView = () => {
542
544
  // ── Fetch directory contents ──
543
545
  const fetchContents = useCallback(async (path: string) => {
544
546
  setLoading(true);
545
- setError(null);
547
+ setFailure(null);
546
548
  try {
547
549
  const result: StorageListResult = await storageSourceRef.current.listObjects(path);
548
550
 
@@ -579,7 +581,10 @@ export const StorageView = () => {
579
581
  setFiles(fileItems);
580
582
  } catch (e) {
581
583
  console.error("Storage list error:", e);
582
- setError(e instanceof Error ? e.message : String(e));
584
+ // A refusal from the project's own `storageAuthorize` hook is not a
585
+ // fault — see `storage-failure.ts`. Rendering both the same way told
586
+ // a customer with a working project that their storage was broken.
587
+ setFailure(classifyStorageFailure(e));
583
588
  } finally {
584
589
  setLoading(false);
585
590
  }
@@ -941,10 +946,35 @@ message: e instanceof Error ? e.message : String(e) });
941
946
  );
942
947
  }
943
948
 
944
- if (error) {
949
+ if (failure?.kind === "denied") {
945
950
  return (
946
951
  <div className="flex-grow flex items-center justify-center p-6 overflow-auto">
947
- <ErrorView title="Error loading storage" error={error} onRetry={handleRefresh}/>
952
+ <div className="max-w-md text-center">
953
+ <Typography variant="subtitle2" className="block">
954
+ This project&apos;s storage rules refused this listing
955
+ </Typography>
956
+ <Typography variant="body2" className="text-text-secondary dark:text-text-secondary-dark block mt-2">
957
+ Nothing is wrong with the project. Its <code>storageAuthorize</code> hook
958
+ decides who may read which keys, and it declined this path for the
959
+ signed-in account — commonly because a listing must name a prefix the
960
+ rule recognises rather than the bucket root.
961
+ </Typography>
962
+ <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark block mt-3 font-mono break-all">
963
+ {failure.detail}
964
+ </Typography>
965
+ </div>
966
+ </div>
967
+ );
968
+ }
969
+
970
+ if (failure) {
971
+ return (
972
+ <div className="flex-grow flex items-center justify-center p-6 overflow-auto">
973
+ <ErrorView
974
+ title="Could not read this project's storage"
975
+ error={failure.detail}
976
+ onRetry={failure.retryable ? handleRefresh : undefined}
977
+ />
948
978
  </div>
949
979
  );
950
980
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Why a storage listing failed — and whether it is the platform's fault.
3
+ *
4
+ * ## Why this is a function
5
+ *
6
+ * The pane rendered one view for every failure: a red card titled **"Error
7
+ * loading storage"** with whatever string came back underneath.
8
+ *
9
+ * On 2026-08-27 a customer opened the Files tab of a healthy project and read
10
+ * "Error loading storage — Not authorized for this object". Nothing was broken.
11
+ * That project's own `storageAuthorize` hook refuses an unscoped listing on
12
+ * purpose — its comment says a `list` that is not scoped to a team "is precisely
13
+ * the enumeration this exists to stop" — and the Files tab always opens at the
14
+ * bucket root. The console took a policy working exactly as written and
15
+ * presented it as a fault in the platform, which sends the reader hunting for a
16
+ * problem that does not exist.
17
+ *
18
+ * A refusal and a failure need different words, a different colour, and a
19
+ * different next step: one is "your rule said no", the other is "we could not
20
+ * ask". Retry is an affordance for the second and noise on the first.
21
+ */
22
+
23
+ export type StorageFailureKind =
24
+ /** The project's own authorize hook said no. Not an error. */
25
+ | "denied"
26
+ /** We could not reach or read the store. */
27
+ | "unavailable";
28
+
29
+ export interface StorageFailure {
30
+ kind: StorageFailureKind;
31
+ /** The message as the server gave it, never re-worded. */
32
+ detail: string;
33
+ /** Offering retry against a standing policy teaches people the button does nothing. */
34
+ retryable: boolean;
35
+ }
36
+
37
+ /** HTTP status carried by whatever the SDK threw, if it carries one. */
38
+ function statusOf(err: unknown): number | null {
39
+ if (typeof err !== "object" || err === null) return null;
40
+ for (const key of ["status", "statusCode", "code"]) {
41
+ const v = (err as Record<string, unknown>)[key];
42
+ if (typeof v === "number" && v >= 100 && v < 600) return v;
43
+ }
44
+ return null;
45
+ }
46
+
47
+ export function classifyStorageFailure(err: unknown): StorageFailure {
48
+ const detail = err instanceof Error ? err.message : String(err);
49
+ const status = statusOf(err);
50
+
51
+ // 403 is the hook's answer; 401 means the caller is not signed in as anyone
52
+ // the hook can evaluate, which is the same conversation from the other end.
53
+ // The message match is the fallback, because an SDK that flattens an error
54
+ // to a string is exactly how this reached the screen unclassified.
55
+ const denied =
56
+ status === 403 ||
57
+ status === 401 ||
58
+ /\bnot authori[sz]ed\b|\bforbidden\b|\bpermission denied\b/i.test(detail);
59
+
60
+ return denied
61
+ ? { kind: "denied", detail, retryable: false }
62
+ : { kind: "unavailable", detail, retryable: true };
63
+ }
@@ -1,4 +1,4 @@
1
- import type { AppView, HomePageSection, PluginGenericProps } from "@rebasepro/admin-types";
1
+ import type { AppView, HomePageSection, PluginGenericProps } from "@rebasepro/cms-types";
2
2
  import React, { useEffect, useMemo, useState } from "react";
3
3
  import { Card, cls, Container, ExpandablePanel, Typography } from "@rebasepro/ui";
4
4
  import { IconForView, useRebaseContext, useRebaseRegistry, useRestoreScroll, useSlot } from "@rebasepro/app";
@@ -2,7 +2,7 @@
2
2
  import { describe, it, expect } from "@jest/globals";
3
3
  import { determineTableAndPK, extractTablesFromQuery, resolveQueryCollections } from "./sql_utils";
4
4
  import type { TableInfo } from "../components/SQLEditor/sql_editor_types";
5
- import type { AdminCollection } from "@rebasepro/admin-types";
5
+ import type { AdminCollection } from "@rebasepro/cms-types";
6
6
 
7
7
  const mockSchemas: Record<string, TableInfo[]> = {
8
8
  "public": [
@@ -62,7 +62,7 @@ alias: item.name.alias });
62
62
  }
63
63
 
64
64
  import { toSnakeCase } from "@rebasepro/utils";
65
- import type { AdminCollection } from "@rebasepro/admin-types";
65
+ import type { AdminCollection } from "@rebasepro/cms-types";
66
66
 
67
67
  /**
68
68
  * Resolve which collections are referenced by a SQL query.
@@ -1 +0,0 @@
1
- {"version":3,"file":"JSEditor-BZnvK1n6.js","names":[],"sources":["../src/components/JSEditor/JSMonacoEditor.tsx","../src/components/JSEditor/JSEditorSidebar.tsx","../src/components/JSEditor/JSEditor.tsx"],"sourcesContent":["\nimport React, { useRef, useEffect } from \"react\";\nimport Editor, { Monaco, OnMount } from \"@monaco-editor/react\";\nimport type { editor } from \"monaco-editor\";\nimport { cls, defaultBorderMixin, FileIcon } from \"@rebasepro/ui\";\nimport { useModeController } from \"@rebasepro/app\";\nimport { ALL_WHERE_FILTER_OPS } from \"@rebasepro/types\";\n\n/**\n * Shape of `monaco.languages.typescript` at runtime.\n * The bundled type stubs mark this API as deprecated, but it is fully functional.\n */\ninterface MonacoTypeScriptApi {\n typescriptDefaults: {\n setCompilerOptions(options: Record<string, unknown>): void;\n setDiagnosticsOptions(options: Record<string, unknown>): void;\n addExtraLib(content: string, filePath: string): void;\n };\n ScriptTarget: Record<string, number>;\n ModuleKind: Record<string, number>;\n}\n\n/**\n * Ambient type definitions for the Rebase client SDK injected into Monaco.\n *\n * Hand-mirrored from `@rebasepro/client`, so it drifts: this block spent long\n * enough declaring the ten Firestore-era filter operators and a\n * `where?: Record<string, string>` that the editor was autocompleting a query\n * shape the server rejects. The operator union is interpolated from\n * {@link ALL_WHERE_FILTER_OPS} for that reason — it is the one part that cannot\n * fall behind. The rest still has to be kept in step by hand.\n */\nconst REBASE_CLIENT_TYPES = `\n// ─── Rebase Client SDK Type Definitions ─────────────────────────────\n\n/** A flat database row with the id merged at the top level. */\n\ninterface FindParams {\n limit?: number;\n offset?: number;\n page?: number;\n /** \\`{ status: [\"==\", \"active\"] }\\` — a tuple per field, or an array of tuples. */\n where?: Record<string, [WhereFilterOp, any] | [WhereFilterOp, any][]>;\n logical?: LogicalCondition;\n /** \\`[\"created_at\", \"desc\"]\\` */\n orderBy?: [string, \"asc\" | \"desc\"];\n include?: string[];\n searchString?: string;\n}\n\ninterface FindResult<M extends Record<string, any> = any> {\n data: M[];\n meta: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n };\n}\n\ntype WhereFilterOp = ${ALL_WHERE_FILTER_OPS.map(op => `\"${op}\"`).join(\" | \")};\n\ninterface FilterCondition {\n column: string;\n operator: WhereFilterOp;\n value: any;\n}\n\ninterface LogicalCondition {\n type: \"and\" | \"or\";\n conditions: (FilterCondition | LogicalCondition)[];\n}\n\ninterface QueryBuilder<M extends Record<string, any> = any> {\n where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;\n orderBy(column: keyof M & string, direction?: \"asc\" | \"desc\"): QueryBuilder<M>;\n limit(count: number): QueryBuilder<M>;\n offset(count: number): QueryBuilder<M>;\n search(searchString: string): QueryBuilder<M>;\n find(): Promise<FindResult<M>>;\n count(): Promise<number>;\n}\n\ninterface CollectionClient<M extends Record<string, any> = any> {\n find(params?: FindParams): Promise<FindResult<M>>;\n findById(id: string | number): Promise<M | undefined>;\n create(data: Partial<M>, id?: string | number): Promise<M>;\n update(id: string | number, data: Partial<M>): Promise<M>;\n delete(id: string | number): Promise<void>;\n where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;\n orderBy(column: keyof M & string, direction?: \"asc\" | \"desc\"): QueryBuilder<M>;\n limit(count: number): QueryBuilder<M>;\n offset(count: number): QueryBuilder<M>;\n search(searchString: string): QueryBuilder<M>;\n listen?(params: FindParams | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;\n listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;\n count?(params?: FindParams): Promise<number>;\n}\n\ninterface User {\n uid: string;\n email: string | null;\n displayName: string | null;\n photoURL: string | null;\n providerId: string;\n isAnonymous: boolean;\n emailVerified?: boolean;\n roles?: string[];\n metadata?: Record<string, unknown>;\n}\n\ninterface RebaseSession {\n accessToken: string;\n refreshToken: string;\n expiresAt: number;\n user: User;\n}\n\ntype AuthChangeEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED';\n\ninterface RebaseAuth {\n signInWithEmail(email: string, password: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;\n signUp(email: string, password: string, displayName?: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;\n signInWithGoogle(idToken: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;\n signOut(): Promise<void>;\n refreshSession(): Promise<RebaseSession>;\n getUser(): Promise<User>;\n updateUser(updates: { displayName?: string; photoURL?: string }): Promise<User>;\n resetPasswordForEmail(email: string): Promise<{ success: boolean; message: string }>;\n resetPassword(token: string, password: string): Promise<{ success: boolean; message: string }>;\n changePassword(oldPassword: string, newPassword: string): Promise<{ success: boolean; message: string }>;\n sendVerificationEmail(): Promise<{ success: boolean; message: string }>;\n verifyEmail(token: string): Promise<{ success: boolean; message: string }>;\n getSessions(): Promise<RebaseSession[]>;\n revokeSession(sessionId: string): Promise<{ success: boolean }>;\n revokeAllSessions(): Promise<{ success: boolean }>;\n getSession(): RebaseSession | null;\n onAuthStateChange(callback: (event: AuthChangeEvent, session: RebaseSession | null) => void): () => void;\n}\n\ninterface AdminUser {\n uid: string;\n email: string;\n displayName: string | null;\n photoURL: string | null;\n provider: string;\n roles: string[];\n createdAt: string;\n updatedAt: string;\n}\n\ninterface RebaseAdmin {\n listUsers(): Promise<{ users: AdminUser[] }>;\n getUser(userId: string): Promise<{ user: AdminUser }>;\n createUser(data: { email: string; displayName?: string; password?: string; roles?: string[] }): Promise<{ user: AdminUser }>;\n updateUser(userId: string, data: { email?: string; displayName?: string; password?: string; roles?: string[] }): Promise<{ user: AdminUser }>;\n deleteUser(userId: string): Promise<{ success: boolean }>;\n bootstrap(): Promise<{ success: boolean; message: string; user: { uid: string; roles: string[] } }>;\n}\n\ninterface UploadFileProps {\n file: FileIcon;\n fileName?: string;\n path?: string;\n metadata?: Record<string, unknown>;\n bucket?: string;\n}\n\ninterface UploadFileResult {\n path: string;\n bucket?: string;\n downloadUrl?: string;\n}\n\ninterface DownloadConfig {\n url: string | null;\n fileNotFound?: boolean;\n metadata?: Record<string, unknown>;\n}\n\ninterface StorageSource {\n putObject(props: UploadFileProps): Promise<UploadFileResult>;\n getSignedUrl(pathOrUrl: string, bucket?: string): Promise<DownloadConfig>;\n getObject(path: string, bucket?: string): Promise<FileIcon | null>;\n deleteObject(path: string, bucket?: string): Promise<void>;\n listObjects(path: string, options?: { bucket?: string; maxResults?: number; pageToken?: string }): Promise<unknown>;\n}\n\ntype RebaseData = {\n /** Look up a collection by slug. */\n collection(slug: string): CollectionClient;\n} & {\n /** Dynamic collection access — e.g. client.data.authors */\n [collectionSlug: string]: CollectionClient;\n};\n\n/**\n * The Rebase client instance. Use \\`client.data\\`, \\`client.auth\\`, \\`client.admin\\`,\n * \\`client.storage\\`, and \\`client.call()\\` to interact with your Rebase backend.\n *\n * @example\n * // Query a collection\n * const result = await client.data.products.find({ limit: 10 });\n *\n * // Create a record\n * await client.data.products.create({ name: \"Camera\", price: 299 });\n *\n * // Fluent query\n * const expensive = await client.data.products.where(\"price\", \">\", 100).orderBy(\"price\", \"desc\").limit(5).find();\n *\n * // Auth\n * const session = client.auth.getSession();\n *\n * // Admin\n * const { users } = await client.admin.listUsers();\n *\n * // Custom endpoint\n * const result = await client.call(\"/my-endpoint\", { myData: 123 });\n */\ninterface RebaseClient {\n /** Data access — dynamic collection accessors */\n data: RebaseData;\n /** Authentication methods */\n auth: RebaseAuth;\n /** User/role admin methods */\n admin: RebaseAdmin;\n /** Storage operations */\n storage?: StorageSource;\n /** Call a custom server-side endpoint */\n call<T = unknown>(endpoint: string, payload?: unknown): Promise<T>;\n /** Direct collection access (shorthand) */\n [collectionSlug: string]: unknown;\n}\n\n/** The pre-configured client instance. Already authenticated with the current user session. */\ndeclare const client: RebaseClient;\n\n/** Execution context with user and collection information. */\ninterface JSEditorContext {\n /** The user the script is running as. */\n user: {\n uid: string;\n displayName: string | null;\n email: string | null;\n roles?: string[];\n } | null;\n /** Registered collections with their property names. */\n collections: Array<{\n slug: string;\n name: string;\n properties: string[];\n }>;\n}\n\n/** Execution context — contains info about the selected user and registered collections. */\ndeclare const context: JSEditorContext;\n`;\n\nexport type CollectionInfo = {\n slug: string;\n name: string;\n properties: string[];\n};\n\nexport type JSMonacoEditorProps = {\n value: string;\n onChange: (value: string | undefined) => void;\n onRun?: (selectedText?: string) => void;\n className?: string;\n readOnly?: boolean;\n autoFocus?: boolean;\n /** Collection slugs for basic autocomplete */\n collectionSlugs?: string[];\n /** Full collection info with property names for richer type generation */\n collections?: CollectionInfo[];\n};\n\nexport const JSMonacoEditor = ({\n value,\n onChange,\n onRun,\n className,\n readOnly = false,\n autoFocus = true,\n collectionSlugs = [],\n collections = []\n}: JSMonacoEditorProps) => {\n const { mode } = useModeController();\n const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);\n const monacoRef = useRef<Monaco | null>(null);\n const onRunRef = useRef(onRun);\n onRunRef.current = onRun;\n const typesRegisteredRef = useRef(false);\n\n const handleEditorOnMount: OnMount = (editorInstance, monaco) => {\n editorRef.current = editorInstance;\n monacoRef.current = monaco;\n\n // Register Cmd/Ctrl+Enter to run\n editorInstance.addAction({\n id: \"run-script\",\n label: \"Run Script\",\n keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],\n contextMenuGroupId: \"operation\",\n contextMenuOrder: 0,\n run: () => {\n if (onRunRef.current) {\n const selection = editorInstance.getSelection();\n let selectedText: string | undefined;\n if (selection && !selection.isEmpty()) {\n selectedText = editorInstance.getModel()?.getValueInRange(selection)?.trim();\n }\n onRunRef.current(selectedText || undefined);\n }\n }\n });\n\n // Configure TypeScript/JavaScript defaults for richer completion\n if (!typesRegisteredRef.current) {\n typesRegisteredRef.current = true;\n\n // Configure TS compiler for modern JS with top-level await\n // Note: cast through `any` because the bundled type stubs mark\n // `monaco.languages.typescript` as deprecated while the runtime\n // API is fully functional.\n const ts = (monaco.languages as unknown as { typescript: MonacoTypeScriptApi }).typescript;\n ts.typescriptDefaults.setCompilerOptions({\n target: ts.ScriptTarget.ESNext,\n module: ts.ModuleKind.ESNext,\n allowJs: true,\n checkJs: false,\n strict: false,\n noEmit: true,\n allowNonTsExtensions: true\n });\n\n // Suppress diagnostics that don't apply to our script context.\n // Scripts are executed inside `new AsyncFunction(...)` at runtime,\n // so top-level `await` (1375) and `return` (1108) are valid.\n ts.typescriptDefaults.setDiagnosticsOptions({\n diagnosticCodesToIgnore: [1375, 1108]\n });\n\n // Inject the Rebase Client SDK type definitions\n ts.typescriptDefaults.addExtraLib(\n REBASE_CLIENT_TYPES,\n \"ts:rebase-client.d.ts\"\n );\n\n // Generate collection-specific types from registered collections\n if (collections.length > 0) {\n const lines: string[] = [];\n for (const col of collections) {\n // Generate a typed interface for each collection\n const ifaceName = col.slug.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const propsType = col.properties.length > 0\n ? `{ ${col.properties.map(p => `${p}: any`).join(\"; \")} }`\n : \"Record<string, any>\";\n lines.push(`/** Collection: ${col.name} (${col.slug}) */`);\n lines.push(`interface ${ifaceName}_Row ${propsType}`);\n lines.push(`declare namespace client.data { const ${col.slug}: CollectionClient<${ifaceName}_Row>; }`);\n }\n ts.typescriptDefaults.addExtraLib(\n lines.join(\"\\n\"),\n \"ts:rebase-collections.d.ts\"\n );\n } else if (collectionSlugs.length > 0) {\n // Fallback: just slug names without property info\n const collectionHints = collectionSlugs.map(slug =>\n `/** Collection: ${slug} */\\ndeclare namespace client.data { const ${slug}: CollectionClient; }`\n ).join(\"\\n\");\n ts.typescriptDefaults.addExtraLib(\n collectionHints,\n \"ts:rebase-collections.d.ts\"\n );\n }\n }\n\n if (autoFocus) {\n editorInstance.focus();\n }\n };\n\n return (\n <div className={cls(\"relative w-full h-full overflow-hidden\", className)}>\n <Editor\n height=\"100%\"\n defaultLanguage=\"typescript\"\n path=\"rebase-script.ts\"\n value={value}\n onChange={onChange}\n onMount={handleEditorOnMount}\n theme={mode === \"dark\" ? \"vs-dark\" : \"vs\"}\n options={{\n minimap: { enabled: false },\n fontSize: 14,\n lineNumbers: \"on\",\n scrollBeyondLastLine: false,\n automaticLayout: true,\n readOnly,\n tabSize: 2,\n wordWrap: \"on\",\n suggestOnTriggerCharacters: true,\n quickSuggestions: true,\n parameterHints: { enabled: true }\n }}\n />\n </div>\n );\n};\n","\nimport React, { useState } from \"react\";\nimport {\n cls,\n defaultBorderMixin,\n IconButton,\n iconSize,\n PlusIcon,\n Tab,\n Tabs,\n Tooltip,\n Trash2Icon,\n Typography\n} from \"@rebasepro/ui\";\n\nexport interface JSSnippet {\n id: string;\n name: string;\n code: string;\n createdAt: number;\n isFavorite?: boolean;\n}\n\ninterface CollectionInfo {\n slug: string;\n properties: string[];\n}\n\ninterface JSEditorSidebarProps {\n collections: CollectionInfo[];\n snippets: JSSnippet[];\n history: string[];\n onSelectSnippet: (code: string) => void;\n onDeleteSnippet: (id: string) => void;\n onInsertCode: (code: string) => void;\n}\n\nconst QUICK_REFERENCE: { label: string; code: string; description: string }[] = [\n {\n label: \"Find all\",\n code: \"const result = await client.data.collection(\\\"COLLECTION\\\").find({ limit: 20 });\\nreturn result;\",\n description: \"Fetch records with pagination\"\n },\n {\n label: \"Find by ID\",\n code: \"const item = await client.data.collection(\\\"COLLECTION\\\").findById(\\\"ID\\\");\\nreturn item;\",\n description: \"Fetch a single record\"\n },\n {\n label: \"Create\",\n code: \"const created = await client.data.collection(\\\"COLLECTION\\\").create({\\n // your fields here\\n});\\nreturn created;\",\n description: \"Insert a new record\"\n },\n {\n label: \"Update\",\n code: \"const updated = await client.data.collection(\\\"COLLECTION\\\").update(\\\"ID\\\", {\\n // fields to update\\n});\\nreturn updated;\",\n description: \"Update an existing record\"\n },\n {\n label: \"Delete\",\n code: \"await client.data.collection(\\\"COLLECTION\\\").delete(\\\"ID\\\");\\nreturn { success: true };\",\n description: \"Delete a record\"\n },\n {\n label: \"Fluent query\",\n code: \"const result = await client.data.collection(\\\"COLLECTION\\\")\\n .where(\\\"field\\\", \\\">\\\", value)\\n .orderBy(\\\"field\\\", \\\"desc\\\")\\n .limit(10)\\n .find();\\nreturn result;\",\n description: \"Chained query builder\"\n },\n {\n label: \"Auth: Current session\",\n code: \"const session = client.auth.getSession();\\nreturn session;\",\n description: \"Get current auth session\"\n },\n {\n label: \"Admin: List users\",\n code: \"const { users } = await client.admin.listUsers();\\nreturn users;\",\n description: \"List all registered users\"\n },\n {\n label: \"Custom endpoint\",\n code: \"const result = await client.call(\\\"/my-endpoint\\\", { key: \\\"value\\\" });\\nreturn result;\",\n description: \"Call a custom API endpoint\"\n }\n];\n\nexport const JSEditorSidebar = ({\n collections,\n snippets,\n history,\n onSelectSnippet,\n onDeleteSnippet,\n onInsertCode\n}: JSEditorSidebarProps) => {\n const [activeTab, setActiveTab] = useState<\"collections\" | \"snippets\" | \"history\" | \"reference\">(\"collections\");\n\n return (\n <div className={cls(\"flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r\", defaultBorderMixin)}>\n <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as \"collections\" | \"snippets\" | \"history\" | \"reference\")} variant=\"boxy\" className=\"border-b border-surface-200 dark:border-surface-950\">\n <Tab value=\"collections\">\n <Tooltip title=\"Browse collections\">\n <span className=\"text-xs\">Collections</span>\n </Tooltip>\n </Tab>\n <Tab value=\"reference\">\n <Tooltip title=\"SDK quick reference\">\n <span className=\"text-xs\">Reference</span>\n </Tooltip>\n </Tab>\n <Tab value=\"snippets\">\n <span className=\"text-xs\">Snippets</span>\n </Tab>\n <Tab value=\"history\">\n <span className=\"text-xs\">History</span>\n </Tab>\n </Tabs>\n\n <div className=\"flex-grow overflow-hidden relative\">\n {/* Collections browser */}\n {activeTab === \"collections\" && (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">\n Collections\n </Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar\">\n {collections.length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n No collections found\n </Typography>\n </div>\n ) : (\n collections.map(col => (\n <CollectionItem\n key={col.slug}\n collection={col}\n onInsertCode={onInsertCode}\n />\n ))\n )}\n </div>\n </div>\n )}\n\n {/* Quick reference */}\n {activeTab === \"reference\" && (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">\n SDK Reference\n </Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar\">\n {QUICK_REFERENCE.map((ref, i) => (\n <div\n key={i}\n className={cls(\n \"group p-2.5 rounded-lg border hover:border-primary/40 dark:hover:border-primary/40\",\n \"bg-white dark:bg-surface-900 transition-all cursor-pointer\",\n defaultBorderMixin\n )}\n onClick={() => onInsertCode(ref.code)}\n >\n <div className=\"flex items-center justify-between mb-1\">\n <Typography variant=\"body2\" className=\"text-text-primary dark:text-text-primary-dark font-semibold text-[13px]\">\n {ref.label}\n </Typography>\n <svg className=\"w-3.5 h-3.5 text-text-disabled group-hover:text-primary transition-colors\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"/>\n </svg>\n </div>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark text-[10px] leading-tight\">\n {ref.description}\n </Typography>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Snippets */}\n {activeTab === \"snippets\" && (() => {\n const favorites = snippets.filter(s => s.isFavorite);\n const others = snippets.filter(s => !s.isFavorite);\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">Snippets</Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar\">\n {snippets.length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n No saved snippets yet. Use the save button to store reusable code.\n </Typography>\n </div>\n ) : (\n <>\n {favorites.length > 0 && (\n <div className=\"mb-3\">\n <Typography variant=\"caption\" className=\"text-[10px] font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1 flex items-center\">\n <svg className=\"w-3 h-3 mr-1 text-amber-500\" fill=\"currentColor\" viewBox=\"0 0 20 20\"><path d=\"M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z\"/></svg>\n Favorites\n </Typography>\n <div className=\"space-y-2\">\n {favorites.map(snippet => (\n <SnippetItem key={snippet.id} snippet={snippet} onSelect={onSelectSnippet} onDelete={onDeleteSnippet}/>\n ))}\n </div>\n </div>\n )}\n {others.length > 0 && (\n <div>\n {favorites.length > 0 && (\n <Typography variant=\"caption\" className=\"text-[10px] font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1\">\n Others\n </Typography>\n )}\n <div className=\"space-y-2\">\n {others.map(snippet => (\n <SnippetItem key={snippet.id} snippet={snippet} onSelect={onSelectSnippet} onDelete={onDeleteSnippet}/>\n ))}\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n );\n })()}\n\n {/* History */}\n {activeTab === \"history\" && (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">History</Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar\">\n {history.length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n No execution history yet\n </Typography>\n </div>\n ) : (\n [...history].reverse().map((code, i) => (\n <div\n key={i}\n className=\"p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-900 cursor-pointer group transition-colors flex items-start\"\n onClick={() => onSelectSnippet(code)}\n >\n <svg className=\"w-3 h-3 mt-1 mr-2 text-text-disabled dark:text-text-disabled-dark group-hover:text-primary transition-colors flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"/>\n </svg>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark group-hover:text-text-primary dark:group-hover:text-text-primary-dark text-[11px] line-clamp-2 leading-tight flex-grow font-mono\">\n {code.length > 120 ? code.substring(0, 120) + \"…\" : code}\n </Typography>\n </div>\n ))\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n\n// ─── Sub-components ──────────────────────────────────────────────────\n\nfunction CollectionItem({ collection, onInsertCode }: { collection: CollectionInfo; onInsertCode: (code: string) => void }) {\n const [expanded, setExpanded] = useState(false);\n\n return (\n <div>\n <div\n className=\"flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative\"\n onClick={() => setExpanded(!expanded)}\n >\n <svg\n className={cls(\"w-3 h-3 mr-1 transition-transform\", expanded && \"rotate-90\")}\n fill=\"currentColor\" viewBox=\"0 0 20 20\"\n >\n <path d=\"M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z\"/>\n </svg>\n <svg className=\"w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4\"/>\n </svg>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark text-xs truncate flex-1 min-w-0\">\n {collection.slug}\n </Typography>\n <Tooltip title=\"Insert find() snippet\">\n <IconButton\n size=\"smallest\"\n className=\"opacity-0 group-hover:opacity-100 text-text-disabled hover:text-primary transition-all\"\n onClick={(e) => {\n e.stopPropagation();\n onInsertCode(`const result = await client.data.${collection.slug}.find({ limit: 20 });\\nreturn result;`);\n }}\n >\n <PlusIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n </div>\n {expanded && collection.properties.length > 0 && (\n <div className={cls(\"ml-5 mt-1 space-y-0.5 border-l\", defaultBorderMixin)}>\n {collection.properties.map(prop => (\n <div\n key={prop}\n className=\"flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r cursor-pointer transition-colors group/prop relative min-h-[28px]\"\n onClick={() => onInsertCode(`\"${prop}\"`)}\n >\n <svg className=\"w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5} d=\"M9 4.5v15m6-15v15m-10.5-1.5h15c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125h-15c-.621 0-1.125.504-1.125 1.125v12.75c0 .621.504 1.125 1.125 1.125Z\"/></svg>\n <Typography variant=\"caption\" className=\"text-text-primary dark:text-text-primary-dark text-[11px] truncate flex-grow font-mono\">{prop}</Typography>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction SnippetItem({ snippet, onSelect, onDelete }: { snippet: JSSnippet; onSelect: (code: string) => void; onDelete: (id: string) => void }) {\n return (\n <div\n className={cls(\"group p-2 rounded border hover:border-surface-400 dark:hover:border-surface-600 bg-white dark:bg-surface-900 transition-all cursor-pointer relative\", defaultBorderMixin)}\n onClick={() => onSelect(snippet.code)}\n >\n <Typography variant=\"body2\" className=\"text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6\">\n {snippet.name}\n </Typography>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate font-mono\">\n {snippet.code}\n </Typography>\n <IconButton\n size=\"smallest\"\n className=\"absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity\"\n onClick={(e) => {\n e.stopPropagation();\n onDelete(snippet.id);\n }}\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </div>\n );\n}\n","\nimport React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport { Highlight, themes } from \"prism-react-renderer\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport {\n Button,\n CellRendererParams,\n Chip,\n CircularProgress,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n DownloadIcon,\n IconButton,\n iconSize,\n Menu,\n MenuIcon,\n MenuItem,\n MoreVerticalIcon,\n PencilIcon,\n PlayIcon,\n PlusIcon,\n ResizablePanels,\n SaveIcon,\n Tab,\n Tabs,\n TerminalIcon,\n TextField,\n Tooltip,\n Typography,\n VirtualTable,\n VirtualTableColumn,\n XIcon\n} from \"@rebasepro/ui\";\nimport { useStudioUrlController, useStudioCollectionRegistry, useStudioSidePanelController } from \"@rebasepro/app\";\nimport { useRebaseContext, useRebaseClient, useSnackbarController, useApiConfig, useTranslation, useModeController, ErrorView, SelectableUser, IconForView } from \"@rebasepro/app\";\n\nimport { createRebaseClient } from \"@rebasepro/client\";\nimport { JSMonacoEditor } from \"./JSMonacoEditor\";\nimport { JSEditorSidebar, JSSnippet } from \"./JSEditorSidebar\";\nimport { AuthSimulationSelector } from \"../AuthSimulationSelector\";\nimport type { AdminCollection } from \"@rebasepro/admin-types\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\ninterface ConsoleEntry {\n type: \"log\" | \"warn\" | \"error\" | \"info\";\n args: unknown[];\n timestamp: number;\n}\n\ninterface ExecutionResult {\n value: unknown;\n console: ConsoleEntry[];\n duration: number;\n error?: string;\n timestamp: number;\n}\n\ninterface EditorTab {\n id: string;\n name: string;\n code: string;\n}\n\n// SelectedUser is now SelectableUser from @rebasepro/app\n\n// ─── Constants ───────────────────────────────────────────────────────\n\nconst STORAGE_PREFIX = \"rebase_js_\";\nconst MAX_HISTORY = 50;\n\n/**\n * The starter script.\n *\n * It runs. That is the point: the console opens with the Run button armed, and\n * a starter that reads `collection(\"your_collection\")` made the first thing a\n * new user did be a 404 — a placeholder presented as working code. `slug` is\n * the project's first collection when one is known.\n */\nconst makeDefaultCode = (slug?: string) => `// Available: client (RebaseClient)\n// Press Cmd+Enter (Ctrl+Enter) to run\n//\n// Examples:\n// const users = await client.admin.listUsers();\n// const session = client.auth.getSession();\n${slug\n ? `\nconst result = await client.data.collection(${JSON.stringify(slug)}).find({ limit: 10 });\nreturn result;\n`\n : `\n// Name one of your collections to query it:\n// const result = await client.data.collection(\"posts\").find({ limit: 10 });\n// return result;\n\nreturn client.auth.getSession();\n`}`;\n\nconst DEFAULT_CODE = makeDefaultCode();\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nfunction loadFromStorage<T>(key: string, fallback: T): T {\n try {\n const raw = localStorage.getItem(STORAGE_PREFIX + key);\n return raw ? JSON.parse(raw) : fallback;\n } catch {\n return fallback;\n }\n}\n\nfunction saveToStorage<T>(key: string, value: T) {\n try {\n localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(value));\n } catch { /* quota */ }\n}\n\nfunction formatJSON(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\n// ─── Helpers: Collection matching for JS results ────────────────────\n\ninterface MatchedJSCollection {\n collectionSlug: string;\n collection: AdminCollection;\n pkColumn: string;\n}\n\n/**\n * Given the raw SDK result, try to detect which collections are present.\n * JS SDK results typically come back as `{ data: [{ id, values }] }` or plain arrays.\n * The heuristic: if the executed code contains `collection(\"<slug>\")` or `client.data.<slug>`,\n * and the result has rows with an \"id\" column, we match those slugs.\n */\nfunction detectCollectionsInResult(\n code: string,\n resultValue: unknown,\n collections: AdminCollection[]\n): MatchedJSCollection[] {\n if (!resultValue || !collections?.length) return [];\n\n // Extract collection slugs mentioned in the code\n const mentionedSlugs = new Set<string>();\n\n // Match: collection(\"slug\") or collection('slug')\n const collectionCallRegex = /\\.collection\\([\"']([^\"']+)[\"']\\)/g;\n let m: RegExpExecArray | null;\n while ((m = collectionCallRegex.exec(code)) !== null) {\n mentionedSlugs.add(m[1]);\n }\n\n // Match: client.data.<slug>. (dot-access pattern)\n const dotAccessRegex = /client\\.data\\.([a-zA-Z_][a-zA-Z0-9_]*)\\./g;\n while ((m = dotAccessRegex.exec(code)) !== null) {\n if (![\"collection\", \"find\", \"findById\", \"create\", \"update\", \"delete\"].includes(m[1])) {\n mentionedSlugs.add(m[1]);\n }\n }\n\n if (mentionedSlugs.size === 0) return [];\n\n // Check if result has rows with an \"id\" field\n let rows: Record<string, unknown>[] = [];\n const rv = resultValue as Record<string, unknown>;\n if (rv?.data && Array.isArray(rv.data)) {\n rows = rv.data;\n } else if (Array.isArray(resultValue)) {\n rows = resultValue;\n }\n\n const hasId = rows.length > 0 && rows.some(r => r?.id != null);\n if (!hasId) return [];\n\n const matched: MatchedJSCollection[] = [];\n for (const slug of mentionedSlugs) {\n const normalised = toSnakeCase(slug);\n const col = collections.find(c => {\n const tableName = (\"table\" in c ? c.table : undefined) || toSnakeCase(c.slug);\n return c.slug === slug || tableName === normalised || toSnakeCase(c.slug) === normalised;\n });\n if (col) {\n matched.push({\n collectionSlug: col.slug,\n collection: col,\n pkColumn: \"id\"\n });\n }\n }\n\n return matched;\n}\n\n// ─── Main Component ─────────────────────────────────────────────────\n\nexport function JSEditor() {\n // Contexts\n const rebaseContext = useRebaseContext();\n const rebaseClient = useRebaseClient();\n const apiConfig = useApiConfig();\n const snackbar = useSnackbarController();\n const collectionRegistry = useStudioCollectionRegistry();\n const sidePanelController = useStudioSidePanelController();\n const { t } = useTranslation();\n\n // User management for the \"Run as\" picker\n const currentUser = rebaseContext.authController?.user;\n\n // State\n const [tabs, setTabs] = useState<EditorTab[]>(() =>\n loadFromStorage(\"tabs\", [{ id: \"1\",\nname: \"Script 1\",\ncode: DEFAULT_CODE }])\n );\n const [activeTabId, setActiveTabId] = useState<string>(() =>\n loadFromStorage(\"activeTab\", \"1\")\n );\n const [result, setResult] = useState<ExecutionResult | null>(null);\n const [isRunning, setIsRunning] = useState(false);\n const [snippets, setSnippets] = useState<JSSnippet[]>(() =>\n loadFromStorage(\"snippets\", [])\n );\n const [history, setHistory] = useState<string[]>(() =>\n loadFromStorage(\"history\", [])\n );\n const [showSaveDialog, setShowSaveDialog] = useState(false);\n const [snippetName, setSnippetName] = useState(\"\");\n const [resultView, setResultView] = useState<\"json\" | \"table\" | \"console\">(\"json\");\n\n // \"Run as\" user state — null means \"self\" (current admin)\n const [selectedUser, setSelectedUser] = useState<SelectableUser | null>(null);\n const [authMode, setAuthMode] = useState<\"jwt\" | \"none\">(\"jwt\");\n\n const [sidebarSize, setSidebarSize] = useState<number>(() => {\n try {\n const stored = localStorage.getItem(STORAGE_PREFIX + \"sidebar_size\");\n return stored ? Number(stored) : 18;\n } catch { return 18; }\n });\n const [editorHeight, setEditorHeight] = useState<number>(() => {\n try {\n const stored = localStorage.getItem(STORAGE_PREFIX + \"editor_height\");\n return stored ? Number(stored) : 55;\n } catch { return 55; }\n });\n\n // Derived\n const activeTab = tabs.find(t => t.id === activeTabId) ?? tabs[0];\n\n // Collection info for the sidebar and Monaco\n const collectionInfos = useMemo(() => {\n const collections = collectionRegistry?.collections ?? [];\n return collections.map(col => ({\n slug: col.slug,\n name: col.name,\n properties: Object.keys(col.properties ?? {})\n }));\n }, [collectionRegistry?.collections]);\n\n const collectionSlugs = useMemo(() => collectionInfos.map(c => c.slug), [collectionInfos]);\n\n /**\n * Name a real collection in the starter script once the registry knows one.\n *\n * The registry is empty on first paint — in the hosted console it arrives\n * over the network — so the placeholder-free default cannot be built in the\n * `useState` initialiser. Rewriting only a tab whose content is still\n * *exactly* the untouched default means this can never overwrite anything\n * anyone typed, and never fires twice.\n */\n const firstSlug = collectionSlugs[0];\n useEffect(() => {\n if (!firstSlug) return;\n setTabs(prev => {\n if (!prev.some(tab => tab.code === DEFAULT_CODE)) return prev;\n const seeded = makeDefaultCode(firstSlug);\n return prev.map(tab => (tab.code === DEFAULT_CODE ? { ...tab,\ncode: seeded } : tab));\n });\n }, [firstSlug]);\n\n // Users list for the picker — mapped to SelectableUser shape\n const users = useMemo((): SelectableUser[] => {\n const managed: SelectableUser[] = [];\n // Ensure the current user is in the list\n if (currentUser) {\n managed.push({\n uid: currentUser.uid,\n displayName: currentUser.displayName,\n email: currentUser.email,\n photoURL: currentUser.photoURL,\n roles: currentUser.roles\n });\n }\n return managed;\n }, [currentUser]);\n\n // Current user as SelectableUser for the popover\n const currentSelectableUser = useMemo((): SelectableUser | null => {\n if (!currentUser) return null;\n return {\n uid: currentUser.uid,\n displayName: currentUser.displayName,\n email: currentUser.email,\n photoURL: currentUser.photoURL,\n roles: currentUser.roles\n };\n }, [currentUser]);\n\n // ─── Persistence ─────────────────────────────────────────────\n\n useEffect(() => { saveToStorage(\"tabs\", tabs); }, [tabs]);\n useEffect(() => { saveToStorage(\"activeTab\", activeTabId); }, [activeTabId]);\n useEffect(() => { saveToStorage(\"snippets\", snippets); }, [snippets]);\n useEffect(() => { saveToStorage(\"history\", history); }, [history]);\n\n useEffect(() => {\n try { localStorage.setItem(STORAGE_PREFIX + \"sidebar_size\", sidebarSize.toString()); } catch { /* ignore */ }\n }, [sidebarSize]);\n\n useEffect(() => {\n try { localStorage.setItem(STORAGE_PREFIX + \"editor_height\", editorHeight.toString()); } catch { /* ignore */ }\n }, [editorHeight]);\n\n // ─── Tab management ──────────────────────────────────────────\n\n const updateActiveCode = useCallback((code: string | undefined) => {\n setTabs(prev => prev.map(t => t.id === activeTabId ? { ...t,\ncode: code ?? \"\" } : t));\n }, [activeTabId]);\n\n const addTab = useCallback(() => {\n const id = String(Date.now());\n const newTab: EditorTab = { id,\nname: `Script ${tabs.length + 1}`,\ncode: DEFAULT_CODE };\n setTabs(prev => [...prev, newTab]);\n setActiveTabId(id);\n }, [tabs.length]);\n\n const closeTab = useCallback((tabId: string) => {\n setTabs(prev => {\n const filtered = prev.filter(t => t.id !== tabId);\n if (filtered.length === 0) {\n const fresh = { id: String(Date.now()),\nname: \"Script 1\",\ncode: DEFAULT_CODE };\n setActiveTabId(fresh.id);\n return [fresh];\n }\n if (activeTabId === tabId) {\n setActiveTabId(filtered[filtered.length - 1].id);\n }\n return filtered;\n });\n }, [activeTabId]);\n\n // ─── Create an authenticated client for execution ────────────\n\n const buildClient = useCallback(async () => {\n const apiUrl = apiConfig?.apiUrl;\n const getAuthToken = apiConfig?.getAuthToken;\n\n if (!apiUrl) {\n throw new Error(\"API URL not configured. Make sure apiUrl is set.\");\n }\n\n if (authMode === \"none\") {\n const client = createRebaseClient({\n baseUrl: apiUrl,\n token: undefined\n });\n return { client,\nisScoped: true };\n }\n\n // If not running as another user safely reuse the application's global client.\n if (!selectedUser || (currentUser && selectedUser.uid === currentUser.uid)) {\n if (!rebaseClient) {\n throw new Error(\"Application client is not initialized.\");\n }\n return { client: rebaseClient,\nisScoped: false };\n }\n\n // Get the current auth token\n let token: string | undefined;\n if (getAuthToken) {\n token = (await getAuthToken()) ?? undefined;\n }\n\n if (!token) {\n throw new Error(\"No auth token available. Please sign in first.\");\n }\n\n // Create a fresh 'scoped' client. This enables us to attach custom auth later for this specific execution\n // without mutating the global client.\n const client = createRebaseClient({\n baseUrl: apiUrl,\n token\n });\n\n return { client,\nisScoped: true };\n }, [apiConfig, rebaseClient, selectedUser, currentUser, authMode]);\n\n // ─── Execution engine ────────────────────────────────────────\n\n const executeCode = useCallback(async (codeOverride?: string) => {\n const code = codeOverride ?? activeTab?.code ?? \"\";\n if (!code.trim()) return;\n\n setIsRunning(true);\n setResult(null);\n\n // Add to history\n setHistory(prev => {\n const deduped = prev.filter(h => h !== code);\n return [...deduped, code].slice(-MAX_HISTORY);\n });\n\n const consoleEntries: ConsoleEntry[] = [];\n const startTime = performance.now();\n let scopedClientToCleanUp: { ws?: { disconnect(): void } } | null = null;\n\n // Capture console methods\n const originalConsole = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n info: console.info\n };\n\n const captureConsole = (type: ConsoleEntry[\"type\"]) => (...args: unknown[]) => {\n consoleEntries.push({ type,\nargs,\ntimestamp: Date.now() });\n originalConsole[type](...args);\n };\n\n try {\n console.log = captureConsole(\"log\");\n console.warn = captureConsole(\"warn\");\n console.error = captureConsole(\"error\");\n console.info = captureConsole(\"info\");\n\n // Build an authenticated client\n const { client, isScoped } = await buildClient();\n if (isScoped) {\n scopedClientToCleanUp = client;\n }\n\n // Build context object with useful info about selected user\n const context = {\n user: selectedUser ?? (currentUser ? {\n uid: currentUser.uid,\n displayName: currentUser.displayName,\n email: currentUser.email,\n roles: currentUser.roles\n } : null),\n collections: collectionInfos\n };\n\n // Create async function with `client` and `context` injected\n\n const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;\n const fn = new AsyncFunction(\"client\", \"context\", code);\n\n const value = await fn(client, context);\n const duration = performance.now() - startTime;\n\n setResult({\n value,\n console: consoleEntries,\n duration,\n timestamp: Date.now()\n });\n\n // Auto-detect best view\n const resultObj = value as Record<string, unknown> | undefined;\n if (resultObj?.data && Array.isArray(resultObj.data)) {\n setResultView(\"table\");\n } else if (consoleEntries.length > 0 && value === undefined) {\n setResultView(\"console\");\n } else {\n setResultView(\"json\");\n }\n } catch (err: unknown) {\n const duration = performance.now() - startTime;\n setResult({\n value: undefined,\n console: consoleEntries,\n duration,\n error: err instanceof Error ? err.message : String(err),\n timestamp: Date.now()\n });\n setResultView(\"json\");\n } finally {\n console.log = originalConsole.log;\n console.warn = originalConsole.warn;\n console.error = originalConsole.error;\n console.info = originalConsole.info;\n setIsRunning(false);\n\n if (scopedClientToCleanUp) {\n // Ensure we disconnect WebSockets to prevent leaking connections\n scopedClientToCleanUp.ws?.disconnect();\n }\n }\n }, [activeTab?.code, buildClient, selectedUser, currentUser, collectionInfos]);\n\n // ─── Snippet management ──────────────────────────────────────\n\n const saveSnippet = useCallback(() => {\n if (!snippetName.trim() || !activeTab?.code.trim()) return;\n const snippet: JSSnippet = {\n id: String(Date.now()),\n name: snippetName.trim(),\n code: activeTab.code,\n createdAt: Date.now()\n };\n setSnippets(prev => [snippet, ...prev]);\n setShowSaveDialog(false);\n setSnippetName(\"\");\n snackbar.open({ type: \"success\",\nmessage: \"Snippet saved\" });\n }, [snippetName, activeTab?.code, snackbar]);\n\n const deleteSnippet = useCallback((id: string) => {\n setSnippets(prev => prev.filter(s => s.id !== id));\n }, []);\n\n // ─── Export ──────────────────────────────────────────────────\n\n const exportResult = useCallback(() => {\n if (!result?.value) return;\n const blob = new Blob([formatJSON(result.value)], { type: \"application/json\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `rebase-result-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n }, [result]);\n\n // ─── Table columns for array data ────────────────────────────\n\n const tableData = useMemo(() => {\n if (!result?.value) return { columns: [] as VirtualTableColumn[],\ndata: [] as Record<string, unknown>[] };\n\n let rows: Record<string, unknown>[] = [];\n const val = result.value as Record<string, unknown>;\n if (val?.data && Array.isArray(val.data)) {\n rows = (val.data as Record<string, unknown>[]).map((entity) => ({\n id: entity.id,\n ...(entity.values as Record<string, unknown> ?? {}),\n ...(entity.values ? {} : entity)\n }));\n } else if (Array.isArray(result.value)) {\n rows = result.value;\n }\n\n if (rows.length === 0) return { columns: [] as VirtualTableColumn[],\ndata: [] as Record<string, unknown>[] };\n\n const keys = new Set<string>();\n rows.slice(0, 20).forEach(row => {\n if (row && typeof row === \"object\") {\n Object.keys(row).forEach(k => keys.add(k));\n }\n });\n\n const columns: VirtualTableColumn[] = Array.from(keys).map(key => ({\n key,\n title: key,\n width: key === \"id\" ? 100 : 200\n }));\n\n return { columns,\ndata: rows };\n }, [result]);\n\n // ─── Matched collections for entity actions ──────────────────\n\n const matchedCollections = useMemo(() => {\n if (!result?.value || result.error) return [];\n return detectCollectionsInResult(\n activeTab?.code ?? \"\",\n result.value,\n collectionRegistry?.collections ?? []\n );\n }, [result, activeTab?.code, collectionRegistry?.collections]);\n\n const getRowEntityActions = useCallback((rowData: Record<string, unknown>): { collection: MatchedJSCollection; entityId: string | number }[] => {\n if (!rowData || matchedCollections.length === 0) return [];\n return matchedCollections\n .filter(mc => rowData[mc.pkColumn] != null)\n .map(mc => ({\n collection: mc,\n entityId: rowData[mc.pkColumn] as string | number\n }));\n }, [matchedCollections]);\n\n // ─── Export handlers ─────────────────────────────────────────\n\n const handleExportCSV = useCallback(() => {\n if (tableData.data.length === 0) return;\n const headers = tableData.columns.map(c => c.key).join(\",\");\n const rows = tableData.data.map(row =>\n tableData.columns.map(c => {\n const val = row[c.key];\n const str = val === null || val === undefined ? \"\" : String(val);\n return str.includes(\",\") ? `\"${str}\"` : str;\n }).join(\",\")\n );\n const csv = [headers, ...rows].join(\"\\n\");\n const blob = new Blob([csv], { type: \"text/csv\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `js_results_${new Date().toISOString().slice(0, 19)}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n }, [tableData]);\n\n const handleExportMarkdown = useCallback(() => {\n if (tableData.data.length === 0) return;\n const headers = tableData.columns.map(c => c.key);\n const headerRow = `| ${headers.join(\" | \")} |`;\n const dividerRow = `| ${headers.map(() => \"---\").join(\" | \")} |`;\n const dataRows = tableData.data.map(row =>\n `| ${headers.map(h => {\n const val = row[h];\n if (val === null || val === undefined) return \"\";\n return String(val).replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n }).join(\" | \")} |`\n );\n const markdown = [headerRow, dividerRow, ...dataRows].join(\"\\n\");\n navigator.clipboard.writeText(markdown).then(() => {\n snackbar.open({ type: \"success\",\nmessage: t(\"studio_sql_markdown_copied\") });\n }).catch(() => {\n snackbar.open({ type: \"error\",\nmessage: t(\"studio_sql_markdown_copy_failed\") });\n });\n }, [tableData, snackbar, t]);\n\n // ─── Render ──────────────────────────────────────────────────\n\n return (\n <div className=\"flex flex-col h-full w-full\">\n {/* Main content */}\n <div className=\"flex-grow overflow-hidden\">\n <ResizablePanels\n orientation=\"horizontal\"\n panelSizePercent={sidebarSize}\n onPanelSizeChange={setSidebarSize}\n minPanelSizePx={200}\n firstPanel={\n <JSEditorSidebar\n collections={collectionInfos}\n snippets={snippets}\n history={history}\n onSelectSnippet={(code) => updateActiveCode(code)}\n onDeleteSnippet={deleteSnippet}\n onInsertCode={(code) => updateActiveCode(code)}\n />\n }\n secondPanel={\n <div className=\"flex flex-col h-full overflow-hidden\">\n {/* Toolbar: matching SQL Editor layout */}\n <div className={cls(\"flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-950\", defaultBorderMixin)}>\n <div className=\"flex items-center flex-grow overflow-hidden mr-4\">\n <div className=\"flex items-center no-scrollbar overflow-x-auto min-w-0\">\n <Tabs value={activeTabId} onValueChange={setActiveTabId} variant=\"boxy\" className=\"w-[unset] flex-shrink-0\" innerClassName=\"bg-white dark:bg-surface-950\">\n {tabs.map(tab => (\n <Tab key={tab.id} value={tab.id} className=\"flex items-center justify-between group max-w-[200px]\">\n <TerminalIcon size={iconSize.smallest} className=\"text-amber-500 mr-1.5 flex-shrink-0\"/>\n <span className=\"truncate\">{tab.name}</span>\n {tabs.length > 1 && (\n <IconButton\n size=\"smallest\"\n onClick={(e) => { e.stopPropagation(); closeTab(tab.id); }}\n className=\"ml-1 !p-0.5 opacity-0 group-hover:opacity-100 hover:text-red-500 transition-opacity\"\n >\n <XIcon size={iconSize.smallest}/>\n </IconButton>\n )}\n </Tab>\n ))}\n </Tabs>\n <IconButton\n size=\"small\"\n onClick={addTab}\n className=\"ml-2 flex-shrink-0\"\n >\n <PlusIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-1.5\">\n <Tooltip title=\"Save as snippet\">\n <IconButton\n size=\"small\"\n onClick={() => {\n setSnippetName(\"\");\n setShowSaveDialog(true);\n }}\n disabled={!activeTab?.code.trim()}\n >\n <SaveIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n {result?.value != null && (\n <Tooltip title=\"Export result as JSON\">\n <IconButton size=\"small\" onClick={exportResult}>\n <DownloadIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n\n <Button\n size=\"small\"\n color=\"primary\"\n disabled={isRunning || !activeTab?.code.trim()}\n onClick={() => executeCode()}\n >\n {isRunning ? <CircularProgress size=\"smallest\" className=\"mr-2\"/> : <PlayIcon size={iconSize.smallest} className=\"mr-2\"/>}\n Run\n </Button>\n </div>\n </div>\n\n {/* Editor + Results split */}\n <div className=\"flex-grow overflow-hidden\">\n <ResizablePanels\n orientation=\"vertical\"\n panelSizePercent={editorHeight}\n onPanelSizeChange={setEditorHeight}\n minPanelSizePx={100}\n firstPanel={\n <div className=\"h-full w-full overflow-hidden flex flex-col\">\n {/* Auth Simulation UI */}\n <div className=\"p-2 px-3 border-b border-surface-200 dark:border-surface-950 bg-surface-50 dark:bg-surface-900 flex items-center shrink-0\">\n <AuthSimulationSelector\n authMode={authMode}\n setAuthMode={setAuthMode}\n selectedUser={selectedUser}\n setSelectedUser={setSelectedUser}\n users={users}\n loading={false}\n currentUser={currentSelectableUser}\n />\n </div>\n <div className=\"flex-1 min-h-0\">\n <JSMonacoEditor\n value={activeTab?.code ?? \"\"}\n onChange={updateActiveCode}\n onRun={(selectedText) => executeCode(selectedText)}\n collectionSlugs={collectionSlugs}\n collections={collectionInfos}\n autoFocus\n />\n </div>\n </div>\n }\n secondPanel={\n <div className=\"h-full w-full flex flex-col bg-surface-50 dark:bg-surface-950 overflow-hidden min-h-0\">\n {/* Result header — matches SQL editor */}\n <div className={cls(\"p-2 px-4 bg-surface-100 dark:bg-surface-900 border-b shrink-0 flex items-center\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold text-text-disabled dark:text-text-disabled-dark uppercase tracking-widest text-[10px]\">\n {t(\"studio_sql_query_results\")}\n </Typography>\n\n {result && (\n <>\n <div className=\"flex-grow\"/>\n\n <Tabs value={resultView} onValueChange={(val) => setResultView(val as \"json\" | \"table\" | \"console\")} variant=\"pill\" className=\"w-[unset] mr-2\">\n <Tab value=\"json\">JSON</Tab>\n {tableData.data.length > 0 && <Tab value=\"table\">Table</Tab>}\n {result.console.length > 0 && <Tab value=\"console\">Console ({result.console.length})</Tab>}\n </Tabs>\n\n <Chip size=\"smallest\" colorScheme={result.error ? \"redDarker\" : \"greenDarker\"}>\n {result.error ? \"Error\" : `${result.duration.toFixed(0)}ms`}\n </Chip>\n </>\n )}\n </div>\n\n {/* Result content */}\n <div className=\"flex-grow flex flex-col min-h-0 overflow-hidden\">\n {isRunning && (\n <div className=\"flex-grow flex items-center justify-center\">\n <div className=\"text-center\">\n <CircularProgress size=\"medium\"/>\n <Typography variant=\"body2\" className=\"mt-4 text-text-secondary dark:text-text-secondary-dark font-mono tracking-tight animate-pulse\">\n EXECUTING SCRIPT...\n </Typography>\n </div>\n </div>\n )}\n\n {!isRunning && !result && (\n <div className=\"flex-grow flex items-center justify-center text-text-disabled dark:text-text-disabled-dark\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-4 opacity-50\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1} d=\"M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\"/></svg>\n <Typography variant=\"body2\">Write JavaScript and press <kbd className=\"px-1.5 py-0.5 rounded bg-surface-200 dark:bg-surface-700 text-[11px] font-mono\">⌘ Enter</kbd> to run</Typography>\n </div>\n </div>\n )}\n\n {!isRunning && result?.error && (\n <div className=\"flex-grow flex items-center justify-center p-6 overflow-auto\">\n <ErrorView\n title=\"Execution Error\"\n error={result.error}\n />\n </div>\n )}\n\n {!isRunning && result && !result.error && resultView === \"json\" && (\n <pre className=\"flex-grow overflow-auto p-4 text-[13px] font-mono leading-relaxed text-text-primary dark:text-text-primary-dark whitespace-pre-wrap break-words\">\n {result.value === undefined ? (\n <span className=\"text-text-disabled italic\">undefined (no return value)</span>\n ) : (\n <JSONHighlight value={result.value}/>\n )}\n </pre>\n )}\n\n {!isRunning && result && !result.error && resultView === \"table\" && tableData.data.length > 0 && (\n <div className=\"flex-grow flex flex-col overflow-hidden min-h-0\">\n {/* Collection badges bar — matching SQL editor */}\n {matchedCollections.length > 0 && (\n <div className={cls(\"px-4 py-1.5 border-b flex items-center gap-2 shrink-0 bg-surface-50 dark:bg-surface-900\", defaultBorderMixin)}>\n <Tooltip title={t(\"studio_sql_admin_collections_tooltip\")}>\n <Typography variant=\"caption\" className=\"text-[10px] font-semibold uppercase tracking-widest text-text-disabled dark:text-text-disabled-dark mr-1 shrink-0 cursor-help\">{t(\"studio_sql_collections_label\")}</Typography>\n </Tooltip>\n <div className=\"flex items-center gap-1.5 overflow-x-auto no-scrollbar\">\n {matchedCollections.map(mc => (\n <Tooltip key={mc.collectionSlug} title={`${mc.collection.name} (${mc.collectionSlug})`}>\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 dark:bg-primary-dark/15 text-primary dark:text-primary-dark whitespace-nowrap border border-primary/20 dark:border-primary-dark/20\">\n {typeof mc.collection.icon === \"string\" && (\n <IconForView collectionOrView={{ icon: mc.collection.icon } as never} className=\"text-[12px]\"/>\n )}\n {mc.collection.name}\n </span>\n </Tooltip>\n ))}\n </div>\n </div>\n )}\n <div className=\"flex-grow relative h-full min-h-0 min-w-0\">\n <VirtualTable\n data={tableData.data}\n columns={\n matchedCollections.length > 0\n ? [{ key: \"__entity_action__\",\ntitle: \"\",\nwidth: 36,\nsortable: false,\nresizable: false }, ...tableData.columns]\n : tableData.columns\n }\n rowHeight={32}\n headerHeight={32}\n cellRenderer={({ rowData, column, rowIndex }: CellRendererParams<Record<string, unknown>>) => {\n // Entity action column\n if (column.key === \"__entity_action__\") {\n const rowActions = getRowEntityActions(rowData ?? {});\n if (rowActions.length === 0) return <div className=\"h-full w-full\"/>;\n if (rowActions.length === 1) {\n const ra = rowActions[0];\n return (\n <div className=\"h-full flex items-center justify-center\">\n <Tooltip title={t(\"studio_sql_edit_entity\", { name: ra.collection.collection.name,\nid: String(ra.entityId) })}>\n <IconButton\n size=\"small\"\n className=\"text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300\"\n onClick={(e) => {\n e.stopPropagation();\n sidePanelController.open({\n path: ra.collection.collectionSlug,\n entityId: ra.entityId,\n collection: ra.collection.collection,\n updateUrl: false\n });\n }}\n >\n <PencilIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n </div>\n );\n }\n // Multiple matched collections\n return (\n <div className=\"h-full flex items-center justify-center\">\n <Menu\n trigger={\n <IconButton\n size={\"small\"}\n className=\"text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300\"\n onClick={(e) => e.stopPropagation()}\n >\n <MoreVerticalIcon size={iconSize.smallest}/>\n </IconButton>\n }\n >\n {rowActions.map(ra => (\n <MenuItem\n key={ra.collection.collectionSlug}\n dense\n onClick={() => {\n sidePanelController.open({\n path: ra.collection.collectionSlug,\n entityId: ra.entityId,\n collection: ra.collection.collection,\n updateUrl: false\n });\n }}\n >\n {t(\"studio_sql_edit_entity\", { name: ra.collection.collection.name,\nid: String(ra.entityId) })}\n </MenuItem>\n ))}\n </Menu>\n </div>\n );\n }\n\n // Regular data cell\n if (!rowData) return null;\n const val = rowData[column.key];\n const displayValue = typeof val === \"object\" && val !== null ? JSON.stringify(val) : String(val ?? \"\");\n return (\n <div className=\"px-4 py-1.5 h-full flex items-center whitespace-nowrap text-[13px] text-text-primary dark:text-text-primary-dark font-mono\">\n <div className=\"truncate flex-grow\" title={displayValue}>\n {displayValue === \"\" ? <span className=\"text-text-disabled dark:text-text-disabled-dark italic text-[11px]\">NULL</span> : displayValue}\n </div>\n </div>\n );\n }}\n />\n </div>\n </div>\n )}\n\n {!isRunning && result && resultView === \"console\" && (\n <div className=\"flex-grow overflow-auto p-2 space-y-1 font-mono text-[12px]\">\n {result.console.length === 0 ? (\n <Typography variant=\"caption\" className=\"text-text-disabled p-2\">No console output</Typography>\n ) : (\n result.console.map((entry, i) => (\n <div\n key={i}\n className={cls(\n \"px-2 py-1 rounded flex items-start gap-2\",\n entry.type === \"error\" && \"bg-red-50 dark:bg-red-950/30 text-red-700 dark:text-red-400\",\n entry.type === \"warn\" && \"bg-amber-50 dark:bg-amber-950/30 text-amber-700 dark:text-amber-400\",\n entry.type === \"log\" && \"text-text-primary dark:text-text-primary-dark\",\n entry.type === \"info\" && \"text-blue-700 dark:text-blue-400\"\n )}\n >\n <span className=\"text-[10px] opacity-50 flex-shrink-0 mt-0.5\">\n {entry.type === \"error\" ? \"❌\" : entry.type === \"warn\" ? \"⚠️\" : entry.type === \"info\" ? \"ℹ️\" : \"›\"}\n </span>\n <span className=\"whitespace-pre-wrap break-words\">\n {entry.args.map(a => typeof a === \"object\" ? JSON.stringify(a, null, 2) : String(a)).join(\" \")}\n </span>\n </div>\n ))\n )}\n\n {result.value !== undefined && (\n <div className={cls(\"px-2 py-1 mt-2 border-t pt-2\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"text-text-disabled text-[10px] uppercase tracking-wider mb-1 block\">Return Value</Typography>\n <pre className=\"text-text-primary dark:text-text-primary-dark whitespace-pre-wrap break-words text-[12px]\">\n {formatJSON(result.value)}\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Footer bar — matching SQL editor */}\n {!isRunning && result && !result.error && resultView === \"table\" && tableData.data.length > 0 && (\n <div className={cls(\"p-2 px-4 border-t bg-surface-50 dark:bg-surface-900 flex justify-between items-center shrink-0\", defaultBorderMixin)}>\n <div className=\"flex space-x-4\">\n <div className=\"flex items-center text-[11px]\">\n <span className=\"font-semibold text-text-disabled dark:text-text-disabled-dark mr-2 uppercase tracking-tighter\">{t(\"studio_sql_rows\")}</span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">{tableData.data.length}</span>\n </div>\n <div className=\"flex items-center text-[11px]\">\n <span className=\"font-semibold text-text-disabled dark:text-text-disabled-dark mr-2 uppercase tracking-tighter\">{t(\"studio_sql_time\")}</span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">{result.duration.toFixed(0)}ms</span>\n </div>\n </div>\n <div className=\"flex gap-2 overflow-x-auto no-scrollbar items-center px-2\">\n <Button\n size=\"small\"\n variant=\"text\"\n className=\"text-[10px] uppercase font-semibold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\"\n onClick={handleExportMarkdown}\n >\n {t(\"studio_sql_copy_markdown\")}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n className=\"text-[10px] uppercase font-semibold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\"\n onClick={exportResult}\n >\n {t(\"studio_sql_export_json\")}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n className=\"text-[10px] uppercase font-semibold text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\"\n onClick={handleExportCSV}\n >\n {t(\"studio_sql_export_csv\")}\n </Button>\n </div>\n </div>\n )}\n </div>\n }\n />\n </div>\n </div>\n }\n />\n </div>\n\n {/* Save snippet dialog */}\n <Dialog open={showSaveDialog} onOpenChange={setShowSaveDialog}>\n <DialogTitle>Save Snippet</DialogTitle>\n <DialogContent>\n <TextField\n label=\"Snippet name\"\n value={snippetName}\n onChange={(e) => setSnippetName(e.target.value)}\n placeholder=\"e.g. List all products\"\n autoFocus\n onKeyDown={(e) => {\n if (e.key === \"Enter\") saveSnippet();\n }}\n />\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setShowSaveDialog(false)}>Cancel</Button>\n <Button variant=\"filled\" color=\"primary\" onClick={saveSnippet} disabled={!snippetName.trim()}>Save</Button>\n </DialogActions>\n </Dialog>\n </div>\n );\n}\n\n// ─── JSON Syntax Highlighting ────────────────────────────────────────\n\nfunction JSONHighlight({ value }: { value: unknown }) {\n const json = formatJSON(value);\n const { mode } = useModeController();\n\n return (\n <Highlight\n theme={mode === \"dark\" ? themes.vsDark : themes.github}\n code={json}\n language=\"json\"\n >\n {({ style, tokens, getLineProps, getTokenProps }) => (\n <span style={{ ...style,\nbackgroundColor: \"transparent\" }}>\n {tokens.map((line, i) => (\n <div key={i} {...getLineProps({ line })}>\n {line.map((token, key) => (\n <span key={key} {...getTokenProps({ token })}/>\n ))}\n </div>\n ))}\n </span>\n )}\n </Highlight>\n );\n}\n"],"mappings":"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"}