@rebasepro/studio 0.24.0 → 0.24.1-canary.gcb918fc

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 (48) hide show
  1. package/dist/{ApiExplorer-CCtczj3F.js → ApiExplorer-Bzt7Ye49.js} +5 -3
  2. package/dist/ApiExplorer-Bzt7Ye49.js.map +1 -0
  3. package/dist/{ApiKeysView-B66TdzhH.js → ApiKeysView-Bqf6XYR1.js} +2 -1
  4. package/dist/{ApiKeysView-B66TdzhH.js.map → ApiKeysView-Bqf6XYR1.js.map} +1 -1
  5. package/dist/{BackupsView-DmSMzAbT.js → BackupsView-BhOHB6iP.js} +6 -20
  6. package/dist/BackupsView-BhOHB6iP.js.map +1 -0
  7. package/dist/{BranchesView-DP1a87E-.js → BranchesView-BHfwGTYc.js} +5 -3
  8. package/dist/BranchesView-BHfwGTYc.js.map +1 -0
  9. package/dist/{CronJobsView-TDErHTuK.js → CronJobsView-CMNK7eWO.js} +52 -59
  10. package/dist/CronJobsView-CMNK7eWO.js.map +1 -0
  11. package/dist/{JSEditor-B59pwg4c.js → JSEditor-DIO2Mw8q.js} +115 -96
  12. package/dist/JSEditor-DIO2Mw8q.js.map +1 -0
  13. package/dist/{LogsExplorer-BKM4yfP8.js → LogsExplorer-C-bwDFIl.js} +19 -23
  14. package/dist/LogsExplorer-C-bwDFIl.js.map +1 -0
  15. package/dist/{RLSEditor-BlLLV6xs.js → RLSEditor-jFD_d06f.js} +10 -8
  16. package/dist/RLSEditor-jFD_d06f.js.map +1 -0
  17. package/dist/{SQLEditor-1An7w0j6.js → SQLEditor-D5Ci4pyW.js} +372 -545
  18. package/dist/SQLEditor-D5Ci4pyW.js.map +1 -0
  19. package/dist/{SchemaVisualizer-DCLVfA5t.js → SchemaVisualizer-D-rAqwul.js} +479 -206
  20. package/dist/SchemaVisualizer-D-rAqwul.js.map +1 -0
  21. package/dist/{StorageView-CbsHuCBf.js → StorageView-CPkDKbsX.js} +498 -241
  22. package/dist/StorageView-CPkDKbsX.js.map +1 -0
  23. package/dist/assets/dagre-layout.worker-BFnhAzuR.js.map +1 -0
  24. package/dist/components/SQLEditor/SQLEditor.d.ts +11 -0
  25. package/dist/components/SchemaVisualizer/dagre-layout.d.ts +32 -0
  26. package/dist/components/SchemaVisualizer/dagre-layout.worker.d.ts +13 -0
  27. package/dist/components/SchemaVisualizer/layout-runner.d.ts +23 -0
  28. package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +21 -10
  29. package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +38 -1
  30. package/dist/components/StorageView/useVisibleFileMetadata.d.ts +46 -0
  31. package/dist/dagre-layout.worker-j9NbH9Gb.js +27 -0
  32. package/dist/dagre-layout.worker-j9NbH9Gb.js.map +1 -0
  33. package/dist/index.es.js +16 -14
  34. package/dist/index.es.js.map +1 -1
  35. package/dist/sql_utils-BZD31xE3.js +478 -0
  36. package/dist/sql_utils-BZD31xE3.js.map +1 -0
  37. package/dist/utils/sql_utils.d.ts +60 -0
  38. package/package.json +9 -9
  39. package/dist/ApiExplorer-CCtczj3F.js.map +0 -1
  40. package/dist/BackupsView-DmSMzAbT.js.map +0 -1
  41. package/dist/BranchesView-DP1a87E-.js.map +0 -1
  42. package/dist/CronJobsView-TDErHTuK.js.map +0 -1
  43. package/dist/JSEditor-B59pwg4c.js.map +0 -1
  44. package/dist/LogsExplorer-BKM4yfP8.js.map +0 -1
  45. package/dist/RLSEditor-BlLLV6xs.js.map +0 -1
  46. package/dist/SQLEditor-1An7w0j6.js.map +0 -1
  47. package/dist/SchemaVisualizer-DCLVfA5t.js.map +0 -1
  48. package/dist/StorageView-CbsHuCBf.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"SchemaVisualizer-DCLVfA5t.js","names":[],"sources":["../src/components/SchemaVisualizer/schema-visualizer.utils.ts","../src/components/SchemaVisualizer/useSchemaGraph.ts","../src/components/SchemaVisualizer/useLiveRls.ts","../src/components/SchemaVisualizer/TableNode.tsx","../src/components/SchemaVisualizer/RelationEdge.tsx","../src/components/SchemaVisualizer/SchemaVisualizer.tsx"],"sourcesContent":["import dagre from \"dagre\";\nimport type { Node, Edge } from \"@xyflow/react\";\n\n// ─── Layout Constants ─────────────────────────────────────────────────\nexport const NODE_WIDTH = 280;\n/** Header with a single line of text (junction tables or tableName === collectionName). */\nconst HEADER_HEIGHT_SINGLE = 33;\n/** Header with two lines (name + subtitle when collectionName !== tableName). */\nconst HEADER_HEIGHT_DOUBLE = 47;\nconst ROW_HEIGHT = 28; // height per column row\n\n/**\n * Compute the correct header height for a table node.\n */\nexport const getHeaderHeight = (opts: {\n isJunction: boolean;\n collectionName: string;\n tableName: string;\n}): number => {\n if (opts.isJunction) return HEADER_HEIGHT_SINGLE;\n return opts.collectionName !== opts.tableName\n ? HEADER_HEIGHT_DOUBLE\n : HEADER_HEIGHT_SINGLE;\n};\n\n/**\n * Estimate the pixel height of a table node based on column count.\n */\nconst estimateNodeHeight = (columnCount: number, headerHeight: number = HEADER_HEIGHT_DOUBLE): number =>\n headerHeight + Math.max(columnCount, 1) * ROW_HEIGHT + 4; // +4 for bottom padding\n\n/**\n * Get the vertical center Y of a specific column row (0-indexed)\n * relative to the top of the node.\n */\nexport const getColumnRowY = (rowIndex: number, headerHeight: number = HEADER_HEIGHT_DOUBLE): number =>\n headerHeight + rowIndex * ROW_HEIGHT + ROW_HEIGHT / 2;\n\n// ─── Auto-Layout via Dagre ────────────────────────────────────────────\n\n/**\n * Compute node positions using the dagre graph layout engine.\n * Returns a new array of nodes with updated `position`.\n *\n * The graph always ranks top to bottom: an ERD read as a column of tables\n * fits the tall, narrow canvas the visualizer is given, and it is the only\n * layout the visualizer offers.\n */\nexport const getLayoutedElements = (\n nodes: Node[],\n edges: Edge[]\n): { nodes: Node[]; edges: Edge[] } => {\n const g = new dagre.graphlib.Graph();\n g.setGraph({\n rankdir: \"TB\",\n nodesep: 100,\n ranksep: 180,\n edgesep: 60,\n marginx: 60,\n marginy: 60\n });\n g.setDefaultEdgeLabel(() => ({}));\n\n const nodeHeights = new Map<string, number>();\n\n nodes.forEach((node) => {\n const data = node.data as { columns?: unknown[]; isJunction?: boolean; collectionName?: string; tableName?: string };\n const columnCount = data.columns?.length ?? 3;\n const headerH = getHeaderHeight({\n isJunction: Boolean(data.isJunction),\n collectionName: data.collectionName ?? \"\",\n tableName: data.tableName ?? \"\"\n });\n const h = estimateNodeHeight(columnCount, headerH);\n nodeHeights.set(node.id, h);\n g.setNode(node.id, {\n width: NODE_WIDTH,\n height: h\n });\n });\n\n edges.forEach((edge) => {\n g.setEdge(edge.source, edge.target);\n });\n\n dagre.layout(g);\n\n const layoutedNodes = nodes.map((node) => {\n const nodeWithPosition = g.node(node.id);\n const h = nodeHeights.get(node.id) ?? estimateNodeHeight(3);\n return {\n ...node,\n position: {\n x: nodeWithPosition.x - NODE_WIDTH / 2,\n y: nodeWithPosition.y - h / 2\n }\n };\n });\n\n return { nodes: layoutedNodes,\nedges };\n};\n\n// ─── Edge styling by relation type ────────────────────────────────────\n\nexport interface RelationEdgeData {\n cardinality: \"one\" | \"many\";\n direction: \"owning\" | \"inverse\";\n relationName: string;\n hasJunction: boolean;\n hasJoinPath: boolean;\n label: string;\n}\n\nexport const getCardinalityLabel = (\n cardinality: \"one\" | \"many\",\n direction: \"owning\" | \"inverse\"\n): string => {\n if (cardinality === \"many\") return \"M:N\";\n if (direction === \"inverse\") return \"1:1 ←\";\n return \"N:1\";\n};\n","import { useMemo, useState, useCallback } from \"react\";\nimport type { Node, Edge } from \"@xyflow/react\";\nimport { MarkerType } from \"@xyflow/react\";\nimport { isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport type { Relation, ResolvedRelation } from \"@rebasepro/types\";\nimport { getTableName, resolveCollectionRelations } from \"@rebasepro/common\";\nimport { getLayoutedElements, getCardinalityLabel, NODE_WIDTH } from \"./schema-visualizer.utils\";\nimport type { RelationEdgeData } from \"./schema-visualizer.utils\";\nimport type { AdminCollection, AdminPostgresCollection } from \"@rebasepro/cms-types\";\n\n// ─── Column info extracted from a collection ──────────────────────────\n\nexport interface ColumnInfo {\n name: string;\n type: string;\n /**\n * The type the collection declares — `string`, `number`, `date`… — or\n * `FK` for a relation's key. Not the database's column type: the diagram\n * is drawn from the collections, and a property's column type depends on\n * more than its `type` (`columnType`, `isId`, integer validation).\n */\n typeLabel: string;\n isPrimaryKey: boolean;\n isForeignKey: boolean;\n isRequired: boolean;\n isEnum: boolean;\n enumValues?: string[];\n relationName?: string;\n}\n\n// ─── Node data shape ──────────────────────────────────────────────────\n\nexport interface TableNodeData {\n tableName: string;\n collectionName: string;\n slug: string;\n columns: ColumnInfo[];\n isJunction: boolean;\n isUnmanaged: boolean;\n rlsEnabled: boolean;\n historyEnabled: boolean;\n icon?: string;\n [key: string]: unknown;\n}\n\n// ─── Extract columns from a AdminCollection ─────────────────────────\n\nconst extractColumns = (collection: AdminCollection): ColumnInfo[] => {\n const columns: ColumnInfo[] = [];\n const properties = collection.properties ?? {};\n\n for (const [propName, prop] of Object.entries(properties)) {\n if (prop.type === \"relation\") continue; // Relations are shown as edges, not columns\n\n const isPk =\n (\"isId\" in prop && Boolean(prop.isId)) ||\n (!Object.values(properties).some(\n (p) => \"isId\" in p && Boolean(p.isId)\n ) &&\n propName === \"id\");\n\n const isEnum = prop.type === \"string\" && \"enum\" in prop && Boolean(prop.enum);\n\n let enumValues: string[] | undefined;\n if (isEnum && \"enum\" in prop && prop.enum) {\n const enumProp = prop.enum;\n if (Array.isArray(enumProp)) {\n enumValues = enumProp.map((v: unknown) =>\n typeof v === \"string\" ? v : String((v as Record<string, unknown>)?.id ?? v)\n );\n } else if (typeof enumProp === \"object\" && enumProp !== null) {\n enumValues = Object.keys(enumProp);\n }\n }\n\n columns.push({\n name: propName,\n type: prop.type,\n typeLabel: prop.type,\n isPrimaryKey: isPk,\n isForeignKey: false,\n isRequired: Boolean(prop.validation?.required),\n isEnum,\n enumValues\n });\n }\n\n // Add FK columns from owning relations\n if (isPostgresCollectionConfig(collection)) {\n try {\n const resolvedRelations = resolveCollectionRelations(collection);\n for (const rel of Object.values(resolvedRelations)) {\n if (rel.kind === \"belongsTo\" && rel.localKey) {\n // Only add if not already present as a regular column\n if (!columns.some((c) => c.name === rel.localKey)) {\n columns.push({\n name: rel.localKey,\n type: \"number\",\n typeLabel: \"FK\",\n isPrimaryKey: false,\n isForeignKey: true,\n isRequired: false,\n isEnum: false,\n relationName: rel.relationName\n });\n } else {\n // Mark existing column as FK\n const existing = columns.find((c) => c.name === rel.localKey);\n if (existing) {\n existing.isForeignKey = true;\n existing.relationName = rel.relationName;\n }\n }\n }\n }\n } catch {\n // Ignore resolution errors\n }\n }\n\n return columns;\n};\n\n// ─── Build graph from collections ─────────────────────────────────────\n\nconst buildGraph = (\n collections: AdminCollection[],\n liveRls: Set<string> | null\n): { nodes: Node[]; edges: Edge[] } => {\n /**\n * Is RLS on for this table?\n *\n * The database is the authority whenever we could reach it. Declaring rules\n * in code is not the same as having them applied, and the hosted console\n * never sees the declarations at all — see `useLiveRlsTables`. Only when\n * there is no SQL capability do we fall back to what the config says.\n */\n const isRlsEnabled = (schema: string, tableName: string, declaresRules: boolean): boolean =>\n liveRls ? liveRls.has(`${schema}.${tableName}`) : declaresRules;\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n const tableToNodeId = new Map<string, string>();\n // Track columns per node so we can resolve handle IDs for edges\n const nodeColumns = new Map<string, ColumnInfo[]>();\n\n // Helper: find the PK column name for a node (falls back to \"id\")\n const getPkHandle = (nodeId: string): string => {\n const cols = nodeColumns.get(nodeId);\n const pk = cols?.find((c) => c.isPrimaryKey);\n return pk ? `target-${pk.name}` : \"target-default\";\n };\n\n // Helper: find the FK column name for a node pointing to a relation\n const getFkHandle = (nodeId: string, localKey: string | undefined): string => {\n if (!localKey) return \"source-default\";\n const cols = nodeColumns.get(nodeId);\n const fk = cols?.find((c) => c.name === localKey);\n return fk ? `source-${fk.name}` : \"source-default\";\n };\n\n // 1. Create nodes for each collection\n //\n // Named by `getTableName`, the table `db push` creates: `table`, or the\n // slug in snake case. The slug as it stands (`blog-posts`) names a table\n // the database does not have, and the live RLS answer is keyed by the real\n // one.\n for (const collection of collections) {\n if (!isPostgresCollectionConfig(collection)) continue;\n\n const tableName = getTableName(collection);\n const nodeId = `table-${tableName}`;\n tableToNodeId.set(tableName, nodeId);\n\n const columns = extractColumns(collection);\n nodeColumns.set(nodeId, columns);\n\n const nodeData: TableNodeData = {\n tableName,\n collectionName: collection.name,\n slug: collection.slug,\n columns,\n isJunction: false,\n isUnmanaged: false,\n rlsEnabled: isRlsEnabled(\n collection.schema ?? \"public\",\n tableName,\n Boolean(collection.securityRules && collection.securityRules.length > 0)\n ),\n historyEnabled: Boolean(collection.history),\n icon: typeof collection.icon === \"string\" ? collection.icon : undefined\n };\n\n nodes.push({\n id: nodeId,\n type: \"tableNode\",\n position: { x: 0,\ny: 0 },\n data: nodeData\n });\n }\n\n // 2. Create junction table nodes and edges\n const processedJunctions = new Set<string>();\n\n for (const collection of collections) {\n if (!isPostgresCollectionConfig(collection)) continue;\n\n const tableName = getTableName(collection);\n const sourceNodeId = tableToNodeId.get(tableName);\n if (!sourceNodeId) continue;\n\n let resolvedRelations: Record<string, ResolvedRelation>;\n try {\n resolvedRelations = resolveCollectionRelations(collection);\n } catch {\n continue;\n }\n\n for (const [relationKey, rel] of Object.entries(resolvedRelations)) {\n let targetCollection: AdminCollection;\n try {\n targetCollection = rel.target();\n } catch {\n continue;\n }\n\n if (!isPostgresCollectionConfig(targetCollection)) continue;\n\n const targetTable = getTableName(targetCollection);\n const targetNodeId = tableToNodeId.get(targetTable);\n if (!targetNodeId) continue;\n\n // Draw the side that owns the storage: a foreign key on this table,\n // or the junction. `hasOne`/`hasMany` are the mirror image of a\n // `belongsTo` that is drawn from the other end, and `via` has no\n // single edge to draw.\n if (rel.kind !== \"belongsTo\" && rel.kind !== \"manyToMany\") continue;\n\n const direction = \"owning\" as const;\n const edgeData: RelationEdgeData = {\n cardinality: rel.cardinality,\n direction,\n relationName: rel.relationName ?? relationKey,\n hasJunction: rel.kind === \"manyToMany\",\n hasJoinPath: false,\n label: getCardinalityLabel(rel.cardinality, direction)\n };\n\n if (rel.kind === \"manyToMany\" && !processedJunctions.has(rel.through.table)) {\n // Many-to-many: create junction node + two edges\n processedJunctions.add(rel.through.table);\n const junctionNodeId = `junction-${rel.through.table}`;\n\n const junctionColumns: ColumnInfo[] = [\n {\n name: rel.through.sourceColumn,\n type: \"number\",\n typeLabel: \"FK\",\n isPrimaryKey: true,\n isForeignKey: true,\n isRequired: true,\n isEnum: false\n },\n {\n name: rel.through.targetColumn,\n type: \"number\",\n typeLabel: \"FK\",\n isPrimaryKey: true,\n isForeignKey: true,\n isRequired: true,\n isEnum: false\n }\n ];\n nodeColumns.set(junctionNodeId, junctionColumns);\n\n nodes.push({\n id: junctionNodeId,\n type: \"tableNode\",\n position: { x: 0,\ny: 0 },\n data: {\n tableName: rel.through.table,\n collectionName: rel.through.table,\n slug: rel.through.table,\n columns: junctionColumns,\n isJunction: true,\n isUnmanaged: false,\n // Junction tables get derived policies rather than\n // declared ones, so the config can never say — but the\n // database can, and usually says yes.\n rlsEnabled: isRlsEnabled(\"public\", rel.through.table, false),\n historyEnabled: false\n } satisfies TableNodeData\n });\n\n // Source table → junction (junction references source PK)\n const sourcePk = nodeColumns.get(sourceNodeId)?.find((c) => c.isPrimaryKey);\n edges.push({\n id: `edge-${sourceNodeId}-${junctionNodeId}`,\n source: sourceNodeId,\n target: junctionNodeId,\n sourceHandle: sourcePk ? `source-${sourcePk.name}` : \"source-default\",\n targetHandle: `target-${rel.through.sourceColumn}`,\n type: \"relationEdge\",\n data: { ...edgeData,\nlabel: \"1:N\" } as Record<string, unknown>,\n markerEnd: { type: MarkerType.ArrowClosed,\nwidth: 16,\nheight: 16 }\n });\n\n // Junction → target table\n edges.push({\n id: `edge-${junctionNodeId}-${targetNodeId}`,\n source: junctionNodeId,\n target: targetNodeId,\n sourceHandle: `source-${rel.through.targetColumn}`,\n targetHandle: getPkHandle(targetNodeId),\n type: \"relationEdge\",\n data: { ...edgeData,\nlabel: \"N:1\" } as Record<string, unknown>,\n markerEnd: { type: MarkerType.ArrowClosed,\nwidth: 16,\nheight: 16 }\n });\n } else if (rel.kind === \"belongsTo\") {\n // A foreign key on this table.\n edges.push({\n id: `edge-${sourceNodeId}-${targetNodeId}-${relationKey}`,\n source: sourceNodeId,\n target: targetNodeId,\n sourceHandle: getFkHandle(sourceNodeId, rel.localKey),\n targetHandle: getPkHandle(targetNodeId),\n type: \"relationEdge\",\n data: { ...edgeData } as Record<string, unknown>,\n markerEnd: { type: MarkerType.ArrowClosed,\nwidth: 16,\nheight: 16 }\n });\n }\n }\n }\n // 3. Apply dagre layout first to get node positions\n const layoutResult = getLayoutedElements(nodes, edges);\n\n // 4. Build a position lookup from the laid-out nodes\n const nodePositions = new Map<string, { x: number }>();\n for (const node of layoutResult.nodes) {\n nodePositions.set(node.id, { x: node.position.x });\n }\n\n // 5. Resolve handle sides based on relative node positions.\n // If source is left of target → source exits Right, target enters Left.\n // If source is right of target → source exits Left, target enters Right.\n for (const edge of layoutResult.edges) {\n const srcPos = nodePositions.get(edge.source);\n const tgtPos = nodePositions.get(edge.target);\n\n const srcBaseHandle = (edge.sourceHandle ?? \"source-default\") as string;\n const tgtBaseHandle = (edge.targetHandle ?? \"target-default\") as string;\n\n if (srcPos && tgtPos) {\n const srcCenterX = srcPos.x + NODE_WIDTH / 2;\n const tgtCenterX = tgtPos.x + NODE_WIDTH / 2;\n const sourceIsLeft = srcCenterX <= tgtCenterX;\n\n edge.sourceHandle = `${srcBaseHandle}-${sourceIsLeft ? \"right\" : \"left\"}`;\n edge.targetHandle = `${tgtBaseHandle}-${sourceIsLeft ? \"left\" : \"right\"}`;\n } else {\n edge.sourceHandle = `${srcBaseHandle}-right`;\n edge.targetHandle = `${tgtBaseHandle}-left`;\n }\n }\n\n return layoutResult;\n};\n\n// ─── Hook ─────────────────────────────────────────────────────────────\n\nexport interface UseSchemaGraphResult {\n nodes: Node[];\n edges: Edge[];\n relayout: () => void;\n tableCount: number;\n relationCount: number;\n}\n\nexport const useSchemaGraph = (\n collections: AdminCollection[] | undefined,\n /**\n * `schema.table` for every table the database reports RLS on, or `null`\n * when there is no SQL capability to ask. See {@link useLiveRlsTables}.\n */\n liveRls: Set<string> | null = null\n): UseSchemaGraphResult => {\n const [version, setVersion] = useState(0);\n\n const relayout = useCallback(() => setVersion((v) => v + 1), []);\n\n const { nodes, edges, tableCount, relationCount } = useMemo(() => {\n if (!collections || collections.length === 0) {\n return { nodes: [],\nedges: [],\ntableCount: 0,\nrelationCount: 0 };\n }\n const result = buildGraph(collections, liveRls);\n return {\n ...result,\n tableCount: result.nodes.length,\n relationCount: result.edges.length\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [collections, version, liveRls]);\n\n return {\n nodes,\n edges,\n relayout,\n tableCount,\n relationCount\n };\n};\n","/**\n * Which tables actually have row-level security switched on, according to the\n * database.\n *\n * ## Why this is not read from the collection config\n *\n * The graph used to derive \"RLS protected\" from `collection.securityRules` —\n * i.e. from what the *codebase* declares. Two problems with that:\n *\n * - **It is not the truth.** Rules in code are only live once a migration has\n * applied them, and `ALTER TABLE … DISABLE ROW LEVEL SECURITY` in a psql\n * session is invisible to it. A security indicator that reports intent\n * rather than state is worse than none: it is green precisely when you most\n * need it to be red.\n * - **The hosted console has no codebase.** There, collections arrive from the\n * tenant's `/api/meta/contract`, which deliberately strips `securityRules`\n * (they carry the raw SQL of every policy guarding the project). So the\n * console showed \"RLS protected: 0\" and no badge on any table, for every\n * project, however well secured.\n *\n * `pg_tables.rowsecurity` answers the question directly and is available\n * wherever `databaseAdmin` is — which is both hosts.\n *\n * When there is no SQL capability at all the hook reports `null`, and callers\n * fall back to the config-derived flag rather than claiming everything is\n * unprotected.\n */\nimport { useEffect, useState } from \"react\";\nimport { useRebaseContext } from \"@rebasepro/app\";\n\n/** `schema.table` for every table with RLS enabled, or `null` if unknown. */\nexport type LiveRlsTables = Set<string> | null;\n\nconst RLS_TABLES_SQL = `\n SELECT schemaname, tablename, rowsecurity\n FROM pg_tables\n WHERE schemaname NOT IN ('information_schema', 'pg_catalog');\n`;\n\n/** Postgres column names arrive lower-cased over the wire, but not on every driver. */\nfunction pick<T>(row: Record<string, unknown>, name: string): T | undefined {\n return (row[name] ?? row[name.toUpperCase()]) as T | undefined;\n}\n\nfunction extractRows(result: unknown): Record<string, unknown>[] {\n if (result && typeof result === \"object\" && \"rows\" in result) {\n const rows = (result as { rows: unknown }).rows;\n if (Array.isArray(rows)) return rows as Record<string, unknown>[];\n }\n if (Array.isArray(result)) return result as Record<string, unknown>[];\n return [];\n}\n\nexport function useLiveRlsTables(): LiveRlsTables {\n const { databaseAdmin } = useRebaseContext();\n const executeSql = databaseAdmin?.executeSql;\n const [tables, setTables] = useState<LiveRlsTables>(null);\n\n useEffect(() => {\n if (!executeSql) {\n setTables(null);\n return;\n }\n let cancelled = false;\n executeSql(RLS_TABLES_SQL)\n .then((result: unknown) => {\n if (cancelled) return;\n const enabled = new Set<string>();\n for (const row of extractRows(result)) {\n if (!pick<boolean>(row, \"rowsecurity\")) continue;\n const schema = pick<string>(row, \"schemaname\") ?? \"public\";\n const table = pick<string>(row, \"tablename\");\n if (table) enabled.add(`${schema}.${table}`);\n }\n setTables(enabled);\n })\n .catch(() => {\n // No answer is not the same as \"nothing is protected\" — leave it\n // unknown so the caller keeps its config-derived fallback.\n if (!cancelled) setTables(null);\n });\n return () => { cancelled = true; };\n }, [executeSql]);\n\n return tables;\n}\n","import React, { memo, useMemo } from \"react\";\nimport { Handle, Position } from \"@xyflow/react\";\nimport type { NodeProps } from \"@xyflow/react\";\nimport { Typography, Chip, Tooltip, cls } from \"@rebasepro/ui\";\nimport { IconForView } from \"@rebasepro/app\";\nimport type { TableNodeData, ColumnInfo } from \"./useSchemaGraph\";\nimport { getColumnRowY, getHeaderHeight } from \"./schema-visualizer.utils\";\n\n/**\n * Custom React Flow node that renders a database table as a card.\n *\n * Every PK and FK column renders handles on BOTH Left and Right sides.\n * The edge builder picks which side to use based on relative node position.\n */\nconst TableNodeInner = ({ data, selected }: NodeProps) => {\n const {\n tableName,\n collectionName,\n columns,\n isJunction,\n rlsEnabled,\n historyEnabled,\n icon\n } = data as TableNodeData;\n\n // Build handles: every PK/FK column gets Left + Right handles\n const handles = useMemo(() => {\n const result: { id: string; type: \"source\" | \"target\"; position: Position; top: number }[] = [];\n const cols = columns as ColumnInfo[];\n const headerH = getHeaderHeight({\n isJunction: Boolean(isJunction),\n collectionName: collectionName as string,\n tableName: tableName as string\n });\n const midY = cols.length > 0 ? getColumnRowY(Math.floor(cols.length / 2), headerH) : 30;\n\n cols.forEach((col, idx) => {\n const y = getColumnRowY(idx, headerH);\n\n if (col.isForeignKey && !col.isPrimaryKey) {\n // FK: source handles on both sides\n result.push({ id: `source-${col.name}-right`,\ntype: \"source\",\nposition: Position.Right,\ntop: y });\n result.push({ id: `source-${col.name}-left`,\ntype: \"source\",\nposition: Position.Left,\ntop: y });\n }\n if (col.isPrimaryKey) {\n // PK: target + source handles on both sides\n result.push({ id: `target-${col.name}-right`,\ntype: \"target\",\nposition: Position.Right,\ntop: y });\n result.push({ id: `target-${col.name}-left`,\ntype: \"target\",\nposition: Position.Left,\ntop: y });\n result.push({ id: `source-${col.name}-right`,\ntype: \"source\",\nposition: Position.Right,\ntop: y });\n result.push({ id: `source-${col.name}-left`,\ntype: \"source\",\nposition: Position.Left,\ntop: y });\n }\n });\n\n // Default handles on both sides\n result.push({ id: \"target-default-right\",\ntype: \"target\",\nposition: Position.Right,\ntop: midY });\n result.push({ id: \"target-default-left\",\ntype: \"target\",\nposition: Position.Left,\ntop: midY });\n result.push({ id: \"source-default-right\",\ntype: \"source\",\nposition: Position.Right,\ntop: midY });\n result.push({ id: \"source-default-left\",\ntype: \"source\",\nposition: Position.Left,\ntop: midY });\n\n return result;\n }, [columns, isJunction, collectionName, tableName]);\n\n return (\n <div\n className={cls(\n \"relative rounded-lg border bg-surface-card shadow-sm transition-all duration-200 min-w-[240px] max-w-[320px]\",\n selected\n ? \"border-primary ring-2 ring-primary/20 shadow-md\"\n : \"border-hairline hover:shadow-md hover:border-hairline-strong\",\n isJunction && \"border-dashed\"\n )}\n >\n {/* ── Header ── */}\n <div\n className={cls(\n \"flex items-center gap-2 px-3 py-2 border-b rounded-t-lg\",\n isJunction\n ? \"bg-surface-sheet border-hairline\"\n : \"bg-surface-card border-hairline\"\n )}\n >\n {icon && !isJunction && (\n <div className=\"text-primary shrink-0\">\n <IconForView\n collectionOrView={{ slug: tableName,\nname: collectionName,\nicon }}\n size=\"smallest\"\n />\n </div>\n )}\n {isJunction && (\n <svg\n className=\"w-3.5 h-3.5 text-text-disabled dark:text-text-disabled-dark shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"\n />\n </svg>\n )}\n <div className=\"flex flex-col min-w-0\">\n <Typography\n variant=\"caption\"\n className={cls(\n \"font-semibold truncate text-[12px]\",\n isJunction\n ? \"text-text-secondary dark:text-text-secondary-dark\"\n : \"text-text-primary dark:text-text-primary-dark\"\n )}\n >\n {tableName}\n </Typography>\n {collectionName !== tableName && !isJunction && (\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark truncate\"\n >\n {collectionName}\n </Typography>\n )}\n </div>\n\n {/* Badges */}\n <div className=\"ml-auto flex items-center gap-1 shrink-0\">\n {rlsEnabled && (\n <Tooltip title=\"RLS enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n </Tooltip>\n )}\n {historyEnabled && (\n <Tooltip title=\"History enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-blue-400\"/>\n </Tooltip>\n )}\n </div>\n </div>\n\n {/* ── Columns ── */}\n <div className=\"divide-y divide-surface-100 dark:divide-surface-950/60\">\n {(columns as ColumnInfo[]).map((col: ColumnInfo) => (\n <div\n key={col.name}\n className={cls(\n \"flex items-center gap-2 px-3 py-1.5 text-xs transition-colors\",\n col.isPrimaryKey && \"bg-amber-50/50 dark:bg-amber-950/10\",\n col.isForeignKey &&\n !col.isPrimaryKey &&\n \"bg-blue-50/40 dark:bg-blue-950/10\"\n )}\n >\n <span className=\"w-3 shrink-0 text-center\">\n {col.isPrimaryKey && (\n <Tooltip title=\"Primary Key\">\n <span className=\"text-amber-500 text-[10px] font-semibold\">🔑</span>\n </Tooltip>\n )}\n {col.isForeignKey && !col.isPrimaryKey && (\n <Tooltip title={`FK → ${col.relationName ?? \"?\"}`}>\n <span className=\"text-blue-400 text-[10px] font-semibold\">🔗</span>\n </Tooltip>\n )}\n </span>\n\n <Typography\n variant=\"caption\"\n className={cls(\n \"font-mono text-[11px] truncate flex-1 min-w-0\",\n col.isPrimaryKey\n ? \"font-semibold text-amber-700 dark:text-amber-400\"\n : col.isForeignKey\n ? \"text-blue-600 dark:text-blue-400\"\n : \"text-text-primary dark:text-text-primary-dark\"\n )}\n >\n {col.name}\n </Typography>\n\n {col.isEnum ? (\n <Chip\n size=\"smallest\"\n className=\"bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400 border-violet-200 dark:border-violet-800 text-[9px] py-0\"\n >\n enum\n </Chip>\n ) : (\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-mono shrink-0\"\n >\n {col.typeLabel}\n </Typography>\n )}\n\n {col.isRequired && !col.isPrimaryKey && (\n <Tooltip title=\"Required\">\n <span className=\"text-red-400 text-[9px]\">•</span>\n </Tooltip>\n )}\n </div>\n ))}\n </div>\n\n {/* ── Handles at node root ── */}\n {handles.map((h) => (\n <Handle\n key={h.id}\n id={h.id}\n type={h.type}\n position={h.position}\n style={{ top: h.top }}\n className={cls(\n \"!w-2 !h-2 !border-2 !border-surface-card\",\n h.type === \"source\" ? \"!bg-blue-400\" : \"!bg-amber-400\"\n )}\n />\n ))}\n </div>\n );\n};\n\nexport const TableNode = memo(TableNodeInner);\n","import React, { memo } from \"react\";\nimport { BaseEdge, getSmoothStepPath, EdgeLabelRenderer } from \"@xyflow/react\";\nimport type { EdgeProps } from \"@xyflow/react\";\nimport { cls } from \"@rebasepro/ui\";\nimport type { RelationEdgeData } from \"./schema-visualizer.utils\";\n\n/**\n * Custom React Flow edge that renders relations with style variations\n * based on cardinality and direction.\n *\n * - Owning one-to-one: solid, primary color\n * - Many-to-many (junction): solid, violet\n * - Inverse: dashed, muted\n * - Join path: dotted, muted\n */\nconst RelationEdgeInner = ({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n data,\n selected\n}: EdgeProps) => {\n const edgeData = data as RelationEdgeData | undefined;\n\n const [edgePath, labelX, labelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n borderRadius: 8\n });\n\n const isInverse = edgeData?.direction === \"inverse\";\n const isJoinPath = edgeData?.hasJoinPath;\n const isJunction = edgeData?.hasJunction;\n\n // Determine stroke style\n let strokeColor = \"var(--rf-edge-stroke, #94a3b8)\";\n let strokeDasharray: string | undefined;\n let strokeWidth = 1.5;\n\n if (selected) {\n strokeColor = \"var(--rf-edge-stroke-selected, #6366f1)\";\n strokeWidth = 2.5;\n } else if (isJunction) {\n strokeColor = \"#8b5cf6\"; // violet\n } else if (isInverse) {\n strokeDasharray = \"6 3\";\n strokeColor = \"#94a3b8\";\n } else if (isJoinPath) {\n strokeDasharray = \"3 3\";\n strokeColor = \"#94a3b8\";\n } else {\n strokeColor = \"#6366f1\"; // primary/indigo\n }\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n style={{\n stroke: strokeColor,\n strokeWidth,\n strokeDasharray\n }}\n />\n {edgeData?.label && (\n <EdgeLabelRenderer>\n <div\n style={{\n position: \"absolute\",\n transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n pointerEvents: \"all\"\n }}\n className={cls(\n \"px-1.5 py-0.5 rounded text-[9px] font-mono font-semibold leading-none\",\n \"bg-surface-card border\",\n selected\n ? \"border-primary text-primary\"\n : isJunction\n ? \"border-violet-200 dark:border-violet-800 text-violet-600 dark:text-violet-400\"\n : isInverse || isJoinPath\n ? \"border-hairline-strong text-text-disabled dark:text-text-disabled-dark\"\n : \"border-primary/30 dark:border-primary/30 text-primary\"\n )}\n >\n {edgeData.label}\n </div>\n </EdgeLabelRenderer>\n )}\n </>\n );\n};\n\nexport const RelationEdge = memo(RelationEdgeInner);\n","import React, { useState, useCallback, useMemo, useEffect, useRef } from \"react\";\nimport {\n ReactFlow,\n Controls,\n MiniMap,\n Background,\n BackgroundVariant,\n useReactFlow,\n useNodesInitialized,\n ReactFlowProvider,\n applyNodeChanges,\n applyEdgeChanges\n} from \"@xyflow/react\";\nimport type { Node, Edge, NodeChange, EdgeChange } from \"@xyflow/react\";\nimport \"@xyflow/react/dist/style.css\";\nimport {\n SearchBar,\n TextField,\n Tooltip,\n Alert,\n Typography,\n cls,\n defaultBorderMixin,\n CircularProgress,\n ResizablePanels,\n IconButton,\n useIsDarkMode\n} from \"@rebasepro/ui\";\nimport {\n useStudioCollectionRegistry,\n IconForView,\n useTranslation\n} from \"@rebasepro/app\";\n\nimport { isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { getTableName } from \"@rebasepro/common\";\nimport { useSchemaGraph } from \"./useSchemaGraph\";\nimport { useLiveRlsTables } from \"./useLiveRls\";\nimport type { TableNodeData } from \"./useSchemaGraph\";\nimport { TableNode } from \"./TableNode\";\nimport { RelationEdge } from \"./RelationEdge\";\nimport type { AdminCollection } from \"@rebasepro/cms-types\";\n\n// ─── Custom node / edge type registrations ────────────────────────────\n\nconst nodeTypes = {\n tableNode: TableNode\n};\n\nconst edgeTypes = {\n relationEdge: RelationEdge\n};\n\n// ─── Inner component (needs ReactFlowProvider) ─────────────────────────\n\nfunction SchemaVisualizerCanvas({\n collections\n}: {\n collections: AdminCollection[];\n}) {\n const { t } = useTranslation();\n const reactFlowInstance = useReactFlow();\n const liveRls = useLiveRlsTables();\n // React Flow stamps its own `light` / `dark` class on the canvas. Left at\n // the default it stamps `light` under a dark app, which is a class our\n // theme used to answer to, and the whole canvas re-lit itself. Follow the\n // app instead so its controls, minimap and our tokens all agree.\n const isDark = useIsDarkMode();\n const {\n nodes: layoutedNodes,\n edges: layoutedEdges,\n relayout,\n tableCount,\n relationCount\n } = useSchemaGraph(collections, liveRls);\n\n const [nodes, setNodes] = useState<Node[]>([]);\n const [edges, setEdges] = useState<Edge[]>([]);\n const [selectedTable, setSelectedTable] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const initialFitDone = useRef(false);\n\n // Sync layouted data into state\n useEffect(() => {\n setNodes(layoutedNodes);\n setEdges(layoutedEdges);\n }, [layoutedNodes, layoutedEdges]);\n\n const onNodesChange = useCallback(\n (changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)),\n []\n );\n const onEdgesChange = useCallback(\n (changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)),\n []\n );\n\n /**\n * Fit the graph into view, once, as soon as it can be measured.\n *\n * This used to fire on a 200ms timer. `fitView` needs every node's real\n * width and height, which React Flow only knows after it has laid them out\n * and measured them — with a dozen tables, a cold Monaco-sized bundle and\n * a slow first paint, 200ms is often too early. The fit then ran against\n * zero-sized nodes and produced a viewport nothing lines up with, and\n * `initialFitDone` made sure it was never retried: the ERD opened as a\n * scatter of specks in a corner and stayed there until you hit \"fit\" by\n * hand. `useNodesInitialized` is the event this was approximating.\n */\n const nodesInitialized = useNodesInitialized();\n useEffect(() => {\n if (!nodesInitialized || nodes.length === 0 || initialFitDone.current) return;\n initialFitDone.current = true;\n reactFlowInstance.fitView({ padding: 0.15,\nduration: 400 });\n }, [nodesInitialized, nodes.length, reactFlowInstance]);\n\n const handleFitView = useCallback(() => {\n reactFlowInstance.fitView({ padding: 0.15,\nduration: 400 });\n }, [reactFlowInstance]);\n\n const handleNodeClick = useCallback((_: React.MouseEvent, node: Node) => {\n setSelectedTable(node.id);\n }, []);\n\n const handlePaneClick = useCallback(() => {\n setSelectedTable(null);\n }, []);\n\n // Focus on a table from the sidebar\n const handleTableSelect = useCallback(\n (nodeId: string) => {\n setSelectedTable(nodeId);\n const node = nodes.find((n) => n.id === nodeId);\n if (node) {\n reactFlowInstance.setCenter(\n node.position.x + 140,\n node.position.y + 60,\n { zoom: 1.2,\nduration: 400 }\n );\n }\n },\n [nodes, reactFlowInstance]\n );\n\n // Sidebar panel size\n const [sidebarSize, setSidebarSize] = useState(() => {\n try {\n const saved = localStorage.getItem(\"rebase_schema_viz_sidebar_size\");\n return saved !== null ? parseFloat(saved) : 20;\n } catch {\n return 20;\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(\n \"rebase_schema_viz_sidebar_size\",\n sidebarSize.toString()\n );\n } catch {\n // Ignore\n }\n }, [sidebarSize]);\n\n // Group collections for sidebar\n const postgresCollections = useMemo(\n () => collections.filter(isPostgresCollectionConfig),\n [collections]\n );\n\n const filteredCollections = useMemo(() => {\n if (!searchQuery.trim()) return postgresCollections;\n const q = searchQuery.toLowerCase();\n return postgresCollections.filter(\n (c) =>\n c.name.toLowerCase().includes(q) ||\n getTableName(c).toLowerCase().includes(q) ||\n c.slug?.toLowerCase().includes(q)\n );\n }, [postgresCollections, searchQuery]);\n\n // Junction table nodes\n const junctionNodes = useMemo(\n () =>\n nodes.filter(\n (n) => (n.data as TableNodeData).isJunction\n ),\n [nodes]\n );\n\n /**\n * The RLS answer for one collection, from the same place the graph used.\n *\n * `nodes` already carries it, so look it up there rather than re-deriving —\n * that is what kept the sidebar dots, the node badges and the total from\n * agreeing.\n */\n const rlsEnabledFor = useCallback(\n (collection: AdminCollection): boolean => {\n const node = nodes.find((n) => n.id === `table-${getTableName(collection)}`);\n return Boolean((node?.data as TableNodeData | undefined)?.rlsEnabled);\n },\n [nodes]\n );\n\n // Stats\n const stats = useMemo(\n () => ({\n tables: tableCount,\n relations: relationCount,\n junctions: junctionNodes.length,\n // Counted off the nodes, which already carry the database's answer\n // where there was one — the same number the per-table badges show.\n // Counting `securityRules` here instead made the total disagree\n // with the badges, and read 0 in the hosted console, where the\n // contract endpoint strips those rules.\n withRls: nodes.filter((n) => (n.data as TableNodeData | undefined)?.rlsEnabled).length\n }),\n [\n tableCount,\n relationCount,\n junctionNodes.length,\n nodes\n ]\n );\n\n return (\n <ResizablePanels\n orientation=\"horizontal\"\n panelSizePercent={sidebarSize}\n onPanelSizeChange={setSidebarSize}\n minPanelSizePx={220}\n firstPanel={\n <div\n className={cls(\n \"flex flex-col h-full w-full bg-surface-card border-r\",\n defaultBorderMixin\n )}\n >\n {/* Sidebar header */}\n <div\n className={cls(\n \"flex items-center justify-between px-3 py-2 border-b bg-surface-sheet min-h-[48px]\",\n defaultBorderMixin\n )}\n >\n <Typography\n variant=\"caption\"\n className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\"\n >\n Tables\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-mono\"\n >\n {stats.tables}\n </Typography>\n </div>\n\n {/* Search */}\n <div className=\"px-2 py-1.5 border-b border-hairline\">\n <SearchBar\n size=\"smallest\"\n placeholder=\"Filter tables…\"\n onTextSearch={(val) => setSearchQuery(val ?? \"\")}\n innerClassName=\"text-xs\"\n />\n </div>\n\n {/* Table list */}\n <div className=\"flex-grow overflow-y-auto no-scrollbar p-1\">\n {/* Regular collections */}\n <div className=\"space-y-0.5\">\n {filteredCollections.map((collection) => {\n const table = getTableName(collection);\n const nodeId = `table-${table}`;\n const isSelected = selectedTable === nodeId;\n return (\n <div\n key={nodeId}\n onClick={() =>\n handleTableSelect(nodeId)\n }\n className={cls(\n \"flex items-center p-1.5 cursor-pointer rounded transition-colors group\",\n isSelected\n ? \"bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light\"\n : \"hover:bg-surface-hover text-text-secondary dark:text-text-secondary-dark\"\n )}\n >\n <div className=\"shrink-0 mr-1.5 text-text-disabled dark:text-text-disabled-dark\">\n <IconForView\n collectionOrView={{\n slug: collection.slug,\n name: collection.name,\n icon:\n typeof collection.icon ===\n \"string\"\n ? collection.icon\n : undefined\n }}\n size=\"smallest\"\n />\n </div>\n <div className=\"flex flex-col min-w-0 flex-1\">\n <Typography\n variant=\"caption\"\n className=\"text-xs truncate font-medium\"\n >\n {collection.name}\n </Typography>\n {table !== collection.name && (\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-mono truncate\"\n >\n {table}\n </Typography>\n )}\n </div>\n <div className=\"flex items-center gap-1 shrink-0 ml-1\">\n {/* Same source as the node badge and the\n \"RLS protected\" total: the database when\n it could be asked, the config otherwise.\n Reading `securityRules` directly here made\n the three disagree. */}\n {rlsEnabledFor(collection) && (\n <Tooltip title=\"RLS enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n </Tooltip>\n )}\n {collection.history && (\n <Tooltip title=\"History enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-blue-400\"/>\n </Tooltip>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Junction tables */}\n {junctionNodes.length > 0 && (\n <div className=\"mt-3\">\n <Typography\n variant=\"caption\"\n className=\"px-1.5 text-[10px] uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark font-medium\"\n >\n Junction Tables\n </Typography>\n <div className=\"mt-1 space-y-0.5\">\n {junctionNodes.map((node) => {\n const d =\n node.data as TableNodeData;\n const isSelected =\n selectedTable === node.id;\n return (\n <div\n key={node.id}\n onClick={() =>\n handleTableSelect(\n node.id\n )\n }\n className={cls(\n \"flex items-center p-1.5 cursor-pointer rounded transition-colors\",\n isSelected\n ? \"bg-primary/10 text-primary dark:bg-primary/20\"\n : \"hover:bg-surface-hover text-text-disabled dark:text-text-disabled-dark\"\n )}\n >\n <svg\n className=\"w-3.5 h-3.5 mr-1.5 shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"\n />\n </svg>\n <Typography\n variant=\"caption\"\n className=\"text-xs font-mono truncate\"\n >\n {d.tableName}\n </Typography>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n\n {/* Stats footer */}\n <div\n className={cls(\n \"px-3 py-2 border-t bg-surface-sheet space-y-1\",\n defaultBorderMixin\n )}\n >\n <div className=\"flex items-center justify-between\">\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Tables\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] font-mono font-medium\"\n >\n {stats.tables}\n </Typography>\n </div>\n <div className=\"flex items-center justify-between\">\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Relations\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] font-mono font-medium\"\n >\n {stats.relations}\n </Typography>\n </div>\n <div className=\"flex items-center justify-between\">\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n RLS protected\n </Typography>\n <div className=\"flex items-center gap-1\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] font-mono font-medium\"\n >\n {stats.withRls}\n </Typography>\n </div>\n </div>\n </div>\n </div>\n }\n secondPanel={\n <div className=\"flex-grow flex flex-col min-w-0 h-full w-full bg-surface-card\">\n {/* Toolbar */}\n <div\n className={cls(\n \"flex items-center justify-between pr-2 border-b bg-surface-card min-h-[46px]\",\n defaultBorderMixin\n )}\n >\n <div className=\"flex flex-col min-w-0 px-4\">\n <Typography\n variant=\"subtitle2\"\n className=\"font-medium text-text-primary dark:text-text-primary-dark\"\n >\n Schema Visualizer\n </Typography>\n {/* What this diagram is drawn from. Everything on it\n but the RLS markers comes from the collection\n configs, not from the database's catalog. */}\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark truncate\"\n >\n {t(\"studio_schema_visualizer_source\")}\n </Typography>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {/* Fit view */}\n <Tooltip title=\"Fit to view\">\n <IconButton aria-label=\"Fit to view\"\n size=\"small\"\n onClick={handleFitView}\n >\n <svg\n className=\"w-4 h-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4\"\n />\n </svg>\n </IconButton>\n </Tooltip>\n\n {/* Re-layout */}\n <Tooltip title=\"Re-layout\">\n <IconButton aria-label=\"Re-layout\"\n size=\"small\"\n onClick={relayout}\n >\n <svg\n className=\"w-4 h-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </IconButton>\n </Tooltip>\n </div>\n </div>\n\n {/* Canvas */}\n <div className=\"flex-grow relative\">\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeClick={handleNodeClick}\n onPaneClick={handlePaneClick}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n fitView\n fitViewOptions={{ padding: 0.15 }}\n minZoom={0.1}\n maxZoom={2}\n proOptions={{ hideAttribution: true }}\n colorMode={isDark ? \"dark\" : \"light\"}\n className=\"bg-surface-well\"\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={20}\n size={1}\n className=\"!bg-surface-well\"\n color={isDark ? \"#2a2a2a\" : \"#d9d9dc\"}\n />\n <Controls\n showInteractive={false}\n // mb clears the legend overlay pinned at bottom-left\n className=\"!mb-16 !bg-surface-card !border !border-hairline !shadow-sm !rounded-lg [--xy-controls-button-background-color:var(--color-surface-card)] [--xy-controls-button-background-color-hover:var(--color-surface-card-hover)] [--xy-controls-button-color:var(--color-text-secondary)] dark:[--xy-controls-button-color:var(--color-text-secondary-dark)] [--xy-controls-button-color-hover:var(--color-text-primary)] dark:[--xy-controls-button-color-hover:var(--color-text-primary-dark)] [--xy-controls-button-border-color:var(--color-hairline)]\"\n />\n <MiniMap\n nodeStrokeColor={(n) => {\n const d = n.data as TableNodeData;\n if (d.isJunction) return \"#a78bfa\";\n if (d.rlsEnabled) return \"#22c55e\";\n return \"#6366f1\";\n }}\n nodeColor={(n) => {\n const d = n.data as TableNodeData;\n // Tinted, not white: the minimap is a card, and its nodes\n // read as tiles on it in either theme.\n if (d.isJunction) return isDark ? \"#3b2f6b\" : \"#ede9fe\";\n return isDark ? \"#2b2d4a\" : \"#eef2ff\";\n }}\n maskColor={isDark ? \"rgba(0,0,0,0.35)\" : \"rgba(0,0,0,0.08)\"}\n className=\"!bg-surface-card !border !border-hairline !shadow-sm !rounded-lg\"\n />\n </ReactFlow>\n\n {/* Legend overlay */}\n <div className=\"absolute bottom-4 left-4 flex items-center gap-3 px-3 py-2 bg-surface-card/90 backdrop-blur-sm rounded-lg border border-hairline shadow-sm\">\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-6 h-0.5 bg-indigo-500 rounded\"/>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Owning\n </Typography>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-6 h-0.5 bg-violet-500 rounded\"/>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n M:N\n </Typography>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <div\n className=\"w-6 h-0.5 rounded\"\n style={{\n backgroundImage:\n \"repeating-linear-gradient(90deg, #94a3b8, #94a3b8 4px, transparent 4px, transparent 7px)\"\n }}\n />\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Inverse\n </Typography>\n </div>\n <div className=\"h-3 w-px bg-surface-raised\"/>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[9px]\">🔑</span>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n PK\n </Typography>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[9px]\">🔗</span>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n FK\n </Typography>\n </div>\n </div>\n </div>\n </div>\n }\n />\n );\n}\n\n// ─── Outer wrapper (provides ReactFlowProvider) ────────────────────────\n\nexport const SchemaVisualizer = () => {\n const { t } = useTranslation();\n const { collections: registryCollections } =\n useStudioCollectionRegistry() as {\n collections?: AdminCollection[];\n };\n\n /**\n * `undefined` and `[]` are different answers, and this view used to render\n * the same spinner for both. A project that declares no collections — a\n * fresh scaffold, a backend-only project someone opened the console\n * against — sat on \"Loading schema…\" forever, because there was nothing\n * coming.\n *\n * `undefined` is the Studio bridge before the admin's collection registry\n * has registered itself: genuinely still loading. `[]` is that registry\n * saying there is nothing to draw.\n */\n if (registryCollections === undefined) {\n return (\n <div className=\"flex items-center justify-center h-full w-full\">\n <div className=\"text-center space-y-3\">\n <CircularProgress size=\"small\"/>\n <Typography\n variant=\"body2\"\n color=\"secondary\"\n >\n Loading schema…\n </Typography>\n </div>\n </div>\n );\n }\n\n if (registryCollections.length === 0) {\n return (\n <div className=\"flex items-center justify-center h-full w-full p-6\">\n <div className=\"max-w-md text-center space-y-2\">\n <Typography variant=\"subtitle2\" className=\"block\">\n {t(\"studio_schema_no_collections\")}\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\" className=\"block\">\n {t(\"studio_schema_no_collections_body\")}\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\" className=\"block\">\n {t(\"studio_schema_introspect_hint\")}\n </Typography>\n </div>\n </div>\n );\n }\n\n const collections = registryCollections;\n\n return (\n <div className=\"flex h-full w-full bg-surface-card overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <ReactFlowProvider>\n <SchemaVisualizerCanvas collections={collections}/>\n </ReactFlowProvider>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"StorageView-CbsHuCBf.js","names":[],"sources":["../src/components/StorageView/StorageView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport {\n ArrowLeftIcon,\n Button,\n Checkbox,\n CheckIcon,\n Chip,\n CircularProgress,\n cls,\n CopyIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n DownloadIcon,\n FileIcon,\n FileTextIcon,\n FileUpload,\n FolderIcon,\n FolderPlusIcon,\n HomeIcon,\n IconButton,\n iconSize,\n ImageIcon,\n LayoutGridIcon,\n ListIcon,\n LoadingButton,\n Music2Icon,\n PlusIcon,\n RefreshCwIcon,\n ResizablePanels,\n Select,\n SelectItem,\n TextField,\n Tooltip,\n Trash2Icon,\n Typography,\n UploadCloudIcon,\n VideoIcon,\n XIcon,\n isKeyHandled\n} from \"@rebasepro/ui\";\nimport { useStorageSource, useStorageSources, useSnackbarController, ErrorView, useApiBase, useApiConfig, useTranslation } from \"@rebasepro/app\";\nimport { DEFAULT_STORAGE_SOURCE_KEY, isPublicStorageKey, type DownloadConfig, type StorageReference, type StorageSource } from \"@rebasepro/types\";\nimport { classifyLoadFailure, type LoadFailure } from \"../load-failure\";\nimport { useSearchParams } from \"react-router\";\nimport { useDropzone } from \"react-dropzone\";\n\n// ──────────────────────────────────────────────\n// Types\n// ──────────────────────────────────────────────\n\ninterface StorageFile {\n name: string;\n fullPath: string;\n isFolder: boolean;\n /** Only populated when metadata is fetched */\n size?: number;\n contentType?: string;\n downloadUrl?: string;\n}\n\n/**\n * A link to one file, as the preview shows, downloads and copies it.\n *\n * Minted when it is used rather than taken from the listing: a private file's\n * URL carries a download token (five minutes by default) minted when the folder\n * was listed, and a preview, a download or a copied link built from that one\n * stopped working while the page stayed open.\n */\ninterface FileLink {\n url: string;\n /**\n * How long the link's token was minted for, in seconds: a link to a private\n * file, which opens it for whoever holds the link until then. Absent when\n * the source does not say.\n */\n expiresInSeconds?: number;\n /** A public file's link: no token, open to anyone, and it does not expire. */\n isPublic: boolean;\n}\n\nfunction fileLinkOf(config: DownloadConfig, fullPath: string): FileLink | null {\n if (!config.url) return null;\n const metadata = config.metadata;\n return {\n url: config.url,\n expiresInSeconds: metadata?.token && typeof metadata.tokenExpiresIn === \"number\" ? metadata.tokenExpiresIn : undefined,\n // The SDK skips the metadata round trip only for a key under the public\n // prefix, and the server says so on every other public object.\n isPublic: metadata ? metadata.public === true : isPublicStorageKey(fullPath)\n };\n}\n\n/** A URL the browser can open from anywhere: relative ones are this page's origin's. */\nfunction absoluteUrl(url: string): string {\n return url.startsWith(\"http\") ? url : `${window.location.origin}${url.startsWith(\"/\") ? \"\" : \"/\"}${url}`;\n}\n\n// ──────────────────────────────────────────────\n// Helpers\n// ──────────────────────────────────────────────\n\n/**\n * The query parameter holding the folder being browsed.\n *\n * Namespaced because Studio does not always own the URL it renders under: the\n * SaaS console embeds this view in a page that keeps its own state in `?tab=`\n * and `?sub=`, and a bare `path` is a name any host app might already be using.\n */\nconst STORAGE_PATH_PARAM = \"storagePath\";\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\nfunction getFileIcon(contentType?: string) {\n if (!contentType) return FileTextIcon;\n if (contentType.startsWith(\"image/\")) return ImageIcon;\n if (contentType.startsWith(\"video/\")) return VideoIcon;\n if (contentType.startsWith(\"audio/\")) return Music2Icon;\n return FileTextIcon;\n}\n\nfunction getExtension(name: string): string {\n const parts = name.split(\".\");\n return parts.length > 1 ? parts[parts.length - 1].toUpperCase() : \"\";\n}\n\nfunction breadcrumbSegments(path: string, rootLabel: string): { label: string; path: string }[] {\n if (!path || path === \"/\") return [{ label: rootLabel,\npath: \"\" }];\n const parts = path.split(\"/\").filter(Boolean);\n const segments = [{ label: rootLabel,\npath: \"\" }];\n let accumulated = \"\";\n for (const part of parts) {\n accumulated = accumulated ? `${accumulated}/${part}` : part;\n segments.push({ label: part,\npath: accumulated });\n }\n return segments;\n}\n\n/**\n * Everything directly under `prefix`, across every page.\n *\n * A listing answers a page at a time — a thousand entries on the built-in\n * controllers — and hands back a token for the next. Reading only the first\n * showed a large folder cut short, and deleted the first page of it.\n */\nasync function listAllPages(\n source: StorageSource,\n prefix: string\n): Promise<{ items: StorageReference[]; prefixes: StorageReference[] }> {\n const items: StorageReference[] = [];\n const prefixes: StorageReference[] = [];\n let pageToken: string | undefined;\n do {\n const page = pageToken\n ? await source.listObjects(prefix, { pageToken })\n : await source.listObjects(prefix);\n items.push(...(page.items ?? []));\n prefixes.push(...(page.prefixes ?? []));\n pageToken = page.nextPageToken;\n } while (pageToken);\n return { items, prefixes };\n}\n\n/**\n * Delete a folder: every file in it, every folder below it, and the folder\n * itself — the directory on local disk, or the zero-byte `name/` marker that\n * \"New folder\" writes on S3 and GCS, which no listing shows as an entry.\n */\nasync function deleteFolderRecursive(source: StorageSource, prefix: string): Promise<void> {\n const { items, prefixes } = await listAllPages(source, prefix);\n for (const item of items) {\n await source.deleteObject(item.fullPath);\n }\n for (const sub of prefixes) {\n await deleteFolderRecursive(source, sub.fullPath);\n }\n try {\n await source.deleteObject(prefix.endsWith(\"/\") ? prefix : `${prefix}/`);\n } catch {\n // A folder with no object of its own (most object-store folders) has\n // nothing to delete here. Whether it is gone is read back by the\n // caller from the folder that held it.\n }\n}\n\n// ──────────────────────────────────────────────\n// Upload Dialog\n// ──────────────────────────────────────────────\n\nfunction UploadDialog({\n open,\n currentPath,\n onClose,\n onUpload\n}: {\n open: boolean;\n currentPath: string;\n onClose: () => void;\n onUpload: (files: File[]) => Promise<void>;\n}) {\n const { t } = useTranslation();\n const [uploading, setUploading] = useState(false);\n const [selectedFiles, setSelectedFiles] = useState<File[]>([]);\n const [error, setError] = useState<string | null>(null);\n\n const handleFilesAdded = useCallback((files: File[]) => {\n setSelectedFiles(prev => [...prev, ...files]);\n }, []);\n\n const handleRemoveFile = useCallback((index: number) => {\n setSelectedFiles(prev => prev.filter((_, i) => i !== index));\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (selectedFiles.length === 0) return;\n setUploading(true);\n setError(null);\n try {\n await onUpload(selectedFiles);\n setSelectedFiles([]);\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : t(\"studio_storage_upload_failed\"));\n } finally {\n setUploading(false);\n }\n }, [selectedFiles, onUpload, onClose, t]);\n\n const handleClose = useCallback(() => {\n if (!uploading) {\n setSelectedFiles([]);\n setError(null);\n onClose();\n }\n }, [uploading, onClose]);\n\n return (\n <Dialog open={open} onOpenChange={(o) => !o && handleClose()} maxWidth=\"md\">\n <DialogTitle>\n {t(\"studio_storage_upload_dialog_title\")}\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark mt-0.5 block\">\n {t(\"studio_storage_upload_to\")} <span className=\"font-mono text-primary\">/{currentPath || t(\"studio_storage_root_folder\")}</span>\n </Typography>\n </DialogTitle>\n <DialogContent className=\"space-y-4\">\n <FileUpload\n onFilesAdded={handleFilesAdded}\n size=\"large\"\n uploadDescription={\n <div className=\"flex flex-col items-center justify-center pointer-events-none\">\n <UploadCloudIcon className=\"text-surface-accent-400 mb-2 w-8 h-8\"/>\n <Typography variant=\"label\">\n {t(\"studio_storage_drop_or_browse\")}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {t(\"studio_storage_any_file_type\")}\n </Typography>\n </div>\n }\n />\n\n {error && (\n <Typography variant=\"caption\" className=\"text-red-500 block whitespace-pre-line\">\n {error}\n </Typography>\n )}\n\n {selectedFiles.length > 0 && (\n <div className=\"space-y-2\">\n <Typography variant=\"caption\" color=\"secondary\">\n {t(\"studio_storage_selected_files\", { count: selectedFiles.length })}\n </Typography>\n <div className=\"max-h-40 overflow-auto space-y-1\">\n {selectedFiles.map((file, index) => (\n <div\n key={`${file.name}-${index}`}\n className=\"flex items-center justify-between p-2 rounded bg-surface-raised\"\n >\n <div className=\"flex-1 min-w-0 mr-2\">\n <Typography variant=\"body2\" className=\"truncate\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {formatFileSize(file.size)}\n </Typography>\n </div>\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n handleRemoveFile(index);\n }}\n disabled={uploading}\n >\n <XIcon size={14}/>\n </IconButton>\n </div>\n ))}\n </div>\n </div>\n )}\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={handleClose} disabled={uploading}>\n {t(\"studio_storage_cancel\")}\n </Button>\n <Button\n variant=\"filled\"\n onClick={handleUpload}\n disabled={selectedFiles.length === 0 || uploading}\n startIcon={uploading ? <CircularProgress size=\"smallest\"/> : <UploadCloudIcon size={14}/>}\n >\n {uploading\n ? t(\"studio_storage_uploading\")\n : selectedFiles.length > 0\n ? t(\"studio_storage_upload_count\", { count: selectedFiles.length })\n : t(\"studio_storage_upload\")}\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n// ──────────────────────────────────────────────\n// FileIcon preview panel\n// ──────────────────────────────────────────────\n\nfunction FilePreviewPanel({\n file,\n onClose,\n onDelete,\n link,\n mintLink\n}: {\n file: StorageFile;\n onClose: () => void;\n onDelete: () => void;\n /** The link minted when the file was opened, shown and previewed. */\n link: FileLink | null;\n /** A fresh link for a download or a copy, minted at the click. */\n mintLink: () => Promise<FileLink | null>;\n}) {\n const { t } = useTranslation();\n const snackbarController = useSnackbarController();\n const isImage = file.contentType?.startsWith(\"image/\");\n const isVideo = file.contentType?.startsWith(\"video/\");\n const isAudio = file.contentType?.startsWith(\"audio/\");\n const FileIconComponent = getFileIcon(file.contentType);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [urlCopied, setUrlCopied] = useState(false);\n\n const reportLinkError = (e: unknown) => {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n };\n\n const handleDownload = () => {\n mintLink()\n .then(fresh => { if (fresh) window.open(fresh.url, \"_blank\"); })\n .catch(reportLinkError);\n };\n\n const handleCopy = () => {\n mintLink()\n .then(async fresh => {\n if (!fresh) return;\n await navigator.clipboard.writeText(absoluteUrl(fresh.url));\n setUrlCopied(true);\n setTimeout(() => setUrlCopied(false), 2000);\n })\n .catch(reportLinkError);\n };\n\n return (\n <>\n <div className={cls(\n \"flex flex-col h-full border-l\",\n defaultBorderMixin,\n \"bg-surface-card\"\n )}>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between p-3 border-b shrink-0\", defaultBorderMixin)}>\n <Typography variant=\"body2\" className=\"font-medium truncate flex-1 mr-2\">\n {file.name}\n </Typography>\n <div className=\"flex items-center gap-0.5\">\n {link && (\n <Tooltip title={t(\"studio_storage_download\")}>\n <IconButton aria-label={t(\"studio_storage_download\")}\n size=\"small\"\n onClick={handleDownload}\n >\n <DownloadIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n <Tooltip title={t(\"studio_storage_delete\")}>\n <IconButton aria-label={t(\"studio_storage_delete\")}\n size=\"small\"\n onClick={() => setDeleteDialogOpen(true)}\n className=\"text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20\"\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <IconButton size=\"small\" onClick={onClose}>\n <XIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n\n {/* Preview */}\n <div className=\"flex-1 overflow-auto\">\n <div className={cls(\"flex flex-col items-center justify-center min-h-[200px] p-4 bg-surface-sheet border-b\", defaultBorderMixin)}>\n {(() => {\n const ext = getExtension(file.name)?.toLowerCase() || \"\";\n const isImage = file.contentType?.startsWith(\"image/\") || [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"].includes(ext);\n const isVideo = file.contentType?.startsWith(\"video/\") || [\"mp4\", \"webm\", \"ogg\", \"mov\"].includes(ext);\n const isAudio = file.contentType?.startsWith(\"audio/\") || [\"mp3\", \"wav\", \"ogg\", \"m4a\"].includes(ext);\n const previewUrl = link?.url;\n\n if (isImage && previewUrl) {\n return (\n <img\n src={previewUrl}\n alt={file.name}\n className=\"max-w-full max-h-[400px] object-contain rounded-md shadow-sm\"\n />\n );\n } else if (isVideo && previewUrl) {\n return (\n <video\n src={previewUrl}\n className=\"max-w-full max-h-[400px] rounded-md\"\n controls\n />\n );\n } else if (isAudio && previewUrl) {\n return (\n <div className=\"flex flex-col items-center gap-4\">\n <Music2Icon className=\"text-surface-accent-400 w-10 h-10\"/>\n <audio src={previewUrl} controls className=\"w-full max-w-xs\"/>\n </div>\n );\n } else {\n return (\n <div className=\"flex flex-col items-center gap-3 text-surface-accent-400\">\n <FileIconComponent className=\"w-10 h-10\"/>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n {t(\"studio_storage_no_preview\")}\n </Typography>\n </div>\n );\n }\n })()}\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"p-4 space-y-3\">\n <div>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark text-[10px] uppercase tracking-wider font-semibold mb-1 block\">\n {t(\"studio_storage_file_info\")}\n </Typography>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_name\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] break-all\">\n {file.name}\n </Typography>\n </div>\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_type\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {file.contentType || t(\"studio_storage_type_unknown\")}\n </Typography>\n </div>\n {file.size !== undefined && (\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_size\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {formatFileSize(file.size)}\n </Typography>\n </div>\n )}\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_extension\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] font-mono\">\n {getExtension(file.name) || \"—\"}\n </Typography>\n </div>\n <div className=\"col-span-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_path\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] font-mono break-all\">\n {file.fullPath}\n </Typography>\n </div>\n </div>\n\n {link && (\n <div className=\"pt-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px] block mb-1\">\n {t(\"studio_storage_url\")}\n </Typography>\n <div\n className={cls(\n \"flex items-center gap-2 p-2 rounded cursor-pointer transition-colors\",\n \"bg-surface-raised hover:bg-surface-raised-hover\"\n )}\n onClick={handleCopy}\n >\n <Typography variant=\"caption\" className=\"font-mono text-[11px] truncate flex-1 min-w-0 text-primary\">\n {absoluteUrl(link.url)}\n </Typography>\n <Tooltip title={urlCopied ? t(\"studio_storage_url_copied\") : t(\"studio_storage_copy_url\")}>\n <div className=\"shrink-0\">\n {urlCopied\n ? <CheckIcon size={14} className=\"text-green-500\"/>\n : <CopyIcon size={14} className=\"text-surface-accent-400\"/>\n }\n </div>\n </Tooltip>\n </div>\n {link.isPublic ? (\n <Typography variant=\"caption\" color=\"secondary\" className=\"block mt-1 text-[11px]\">\n {t(\"studio_storage_url_public\")}\n </Typography>\n ) : link.expiresInSeconds !== undefined ? (\n <Typography variant=\"caption\" color=\"secondary\" className=\"block mt-1 text-[11px]\">\n {t(\"studio_storage_url_temporary\", { minutes: Math.max(1, Math.round(link.expiresInSeconds / 60)) })}\n </Typography>\n ) : null}\n </div>\n )}\n </div>\n </div>\n\n {/* Delete Confirmation */}\n <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>\n <DialogTitle hidden>{t(\"studio_storage_delete_file_title\")}</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"mb-2\">\n {t(\"studio_storage_delete_file_question\")}\n </Typography>\n <Typography className=\"text-surface-accent-600 dark:text-surface-accent-400\">\n {t(\"studio_storage_delete_file_body\", { name: file.name })}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setDeleteDialogOpen(false)}>\n {t(\"studio_storage_cancel\")}\n </Button>\n <Button\n variant=\"filled\"\n color=\"error\"\n onClick={() => {\n setDeleteDialogOpen(false);\n onDelete();\n }}\n >\n {t(\"studio_storage_delete\")}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n}\n\n// ──────────────────────────────────────────────\n// Main StorageView Export\n// ──────────────────────────────────────────────\n\nexport const StorageView = () => {\n const { t } = useTranslation();\n const defaultStorageSource = useStorageSource();\n const storageSources = useStorageSources();\n const snackbarController = useSnackbarController();\n\n // Available backends to browse. Always includes the default; named\n // sources come from `<Rebase storageSources={...}>`.\n const sourceKeys = useMemo(() => {\n const keys = Object.keys(storageSources.sources);\n if (!keys.includes(DEFAULT_STORAGE_SOURCE_KEY)) keys.unshift(DEFAULT_STORAGE_SOURCE_KEY);\n return keys;\n }, [storageSources.sources]);\n\n const [selectedSourceKey, setSelectedSourceKey] = useState<string>(DEFAULT_STORAGE_SOURCE_KEY);\n\n const storageSource = storageSources.sources[selectedSourceKey] ?? defaultStorageSource;\n\n // Navigation\n const [searchParams, setSearchParams] = useSearchParams();\n // Accepts the historical unqualified `path` so links already shared keep\n // working; only the namespaced one is ever written.\n const currentPath = searchParams.get(STORAGE_PATH_PARAM) ?? searchParams.get(\"path\") ?? \"\";\n const [loading, setLoading] = useState(true);\n /** Why the listing failed, classified — see `load-failure.ts`. */\n const [failure, setFailure] = useState<LoadFailure | null>(null);\n\n // Contents\n const [folders, setFolders] = useState<StorageFile[]>([]);\n const [files, setFiles] = useState<StorageFile[]>([]);\n\n // Selection and preview\n const [selectedFile, setSelectedFile] = useState<StorageFile | null>(null);\n const [selectedLink, setSelectedLink] = useState<FileLink | null>(null);\n /** The latest link asked for the preview: an earlier one answering later is dropped. */\n const linkRequestRef = useRef(0);\n\n // Upload\n const [uploadDialogOpen, setUploadDialogOpen] = useState(false);\n\n // View mode\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n\n // Multi-selection\n const [selectedPaths, setSelectedPaths] = useState<Set<string>>(new Set());\n const lastClickedRef = useRef<string | null>(null);\n\n // Bulk / folder delete\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [deleteDialogTarget, setDeleteDialogTarget] = useState<\"selection\" | StorageFile | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n // New folder\n const [newFolderDialogOpen, setNewFolderDialogOpen] = useState(false);\n const [newFolderName, setNewFolderName] = useState(\"\");\n const [creatingFolder, setCreatingFolder] = useState(false);\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n\n const storageSourceRef = React.useRef(storageSource);\n useEffect(() => {\n storageSourceRef.current = storageSource;\n }, [storageSource]);\n\n /**\n * The latest listing asked for. Only its answer is shown: a listing awaits\n * every file's metadata, so an earlier one — of the source or folder the\n * view has since left — can answer after a later one, and showed that\n * source's files under the one selected, where their actions ran.\n */\n const listingRequestRef = useRef(0);\n\n // ── Fetch directory contents ──\n const fetchContents = useCallback(async (path: string) => {\n const request = ++listingRequestRef.current;\n const isCurrent = () => request === listingRequestRef.current;\n // One source for the whole listing, metadata included.\n const source = storageSourceRef.current;\n setLoading(true);\n setFailure(null);\n try {\n const result = await listAllPages(source, path);\n\n const folderItems: StorageFile[] = result.prefixes.map(ref => ({\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: true\n }));\n\n // Build file items and fetch metadata for each\n const fileItems: StorageFile[] = await Promise.all(\n result.items.map(async (ref) => {\n try {\n const downloadConfig = await source.getSignedUrl(ref.fullPath);\n return {\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: false,\n size: downloadConfig.metadata?.size,\n contentType: downloadConfig.metadata?.contentType,\n downloadUrl: downloadConfig.url ?? undefined\n };\n } catch {\n return {\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: false\n };\n }\n })\n );\n\n if (!isCurrent()) return;\n setFolders(folderItems);\n setFiles(fileItems);\n } catch (e) {\n if (!isCurrent()) return;\n console.error(\"Storage list error:\", e);\n // A refusal from the project's own `storageAuthorize` hook is not a\n // fault — see `load-failure.ts`. Rendering both the same way told\n // a customer with a working project that their storage was broken.\n setFailure(classifyLoadFailure(e));\n } finally {\n if (isCurrent()) setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n // `selectedSourceKey` is a dep so switching backend re-lists; the\n // ref-sync effect above runs first, so `storageSourceRef` is current.\n fetchContents(currentPath);\n }, [currentPath, fetchContents, selectedSourceKey]);\n\n /** A link to `file`, minted now through the source being browsed — see {@link FileLink}. */\n const mintFileLink = useCallback(async (file: StorageFile): Promise<FileLink | null> =>\n fileLinkOf(await storageSourceRef.current.getSignedUrl(file.fullPath), file.fullPath), []);\n\n /** Open `file` in the preview, with a link minted for it now. */\n const openPreview = useCallback((file: StorageFile) => {\n const request = ++linkRequestRef.current;\n setSelectedFile(file);\n setSelectedLink(null);\n mintFileLink(file)\n .then(link => { if (request === linkRequestRef.current) setSelectedLink(link); })\n .catch(() => { if (request === linkRequestRef.current) setSelectedLink(null); });\n }, [mintFileLink]);\n\n /** A fresh link to the previewed file, for a download or a copy. It replaces the one shown. */\n const refreshPreviewLink = useCallback(async (): Promise<FileLink | null> => {\n if (!selectedFile) return null;\n const request = ++linkRequestRef.current;\n const link = await mintFileLink(selectedFile);\n if (request === linkRequestRef.current) setSelectedLink(link);\n return link;\n }, [selectedFile, mintFileLink]);\n\n // What was previewed and selected belongs to the listing it was picked in.\n const clearSelection = useCallback(() => {\n setSelectedFile(null);\n setSelectedLink(null);\n setSelectedPaths(new Set());\n lastClickedRef.current = null;\n }, []);\n\n // Navigate to path\n //\n // Updates only this view's own parameter and leaves the rest of the query\n // string alone. It used to assign the whole thing (`setSearchParams({})`),\n // which is fine when Studio owns the URL but destructive when it is\n // embedded: the SaaS console keeps the open tab in `?tab=`, so opening a\n // folder erased it and bounced the user out of Storage entirely. The key is\n // namespaced for the same reason — a bare `path` is a name a host app can\n // easily be using for something else.\n const handleNavigate = useCallback((path: string) => {\n setSearchParams(prev => {\n const next = new URLSearchParams(prev);\n if (path) next.set(STORAGE_PATH_PARAM, path);\n else next.delete(STORAGE_PATH_PARAM);\n return next;\n }, { replace: true });\n clearSelection();\n }, [setSearchParams, clearSelection]);\n\n // Switching source keeps the folder path, not the files picked in it: the\n // preview's and the selection's actions run through the selected source,\n // so a file opened in one would be deleted, by its path, from the other.\n const handleSourceChange = useCallback((key: string) => {\n setSelectedSourceKey(key);\n clearSelection();\n }, [clearSelection]);\n\n // Navigate up one level\n const handleNavigateUp = useCallback(() => {\n const parts = currentPath.split(\"/\").filter(Boolean);\n parts.pop();\n handleNavigate(parts.join(\"/\"));\n }, [currentPath, handleNavigate]);\n\n // All items (folders + files) in display order, for shift-range select\n const allItems = useMemo(() => [...folders, ...files], [folders, files]);\n\n // ── Multi-select click handler ──\n const handleItemClick = useCallback((item: StorageFile, e: React.MouseEvent) => {\n const path = item.fullPath;\n if (e.metaKey || e.ctrlKey) {\n // Toggle individual item\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n lastClickedRef.current = path;\n } else if (e.shiftKey && lastClickedRef.current) {\n // Range select\n const allPaths = allItems.map(i => i.fullPath);\n const anchorIdx = allPaths.indexOf(lastClickedRef.current);\n const currentIdx = allPaths.indexOf(path);\n if (anchorIdx >= 0 && currentIdx >= 0) {\n const [start, end] = anchorIdx < currentIdx ? [anchorIdx, currentIdx] : [currentIdx, anchorIdx];\n setSelectedPaths(prev => {\n const next = new Set(prev);\n for (let i = start; i <= end; i++) next.add(allPaths[i]);\n return next;\n });\n }\n } else {\n // Exclusive select\n setSelectedPaths(new Set([path]));\n lastClickedRef.current = path;\n // Also open preview if it's a file\n if (!item.isFolder) {\n openPreview(item);\n } else {\n setSelectedFile(null);\n setSelectedLink(null);\n }\n }\n }, [allItems, openPreview]);\n\n // Double-click: open folder or preview file\n const handleItemDoubleClick = useCallback((item: StorageFile) => {\n if (item.isFolder) {\n handleNavigate(item.fullPath);\n } else {\n openPreview(item);\n }\n }, [handleNavigate, openPreview]);\n\n // Upload files\n const handleUpload = useCallback(async (uploadFiles: File[]) => {\n for (const file of uploadFiles) {\n const key = currentPath ? `${currentPath}/${file.name}` : file.name;\n await storageSourceRef.current.putObject({\n file,\n key\n });\n }\n snackbarController.open({\n type: \"success\",\n message: t(\"studio_storage_files_uploaded\", { count: uploadFiles.length })\n });\n await fetchContents(currentPath);\n }, [currentPath, snackbarController, fetchContents, t]);\n\n // Create new folder\n const handleCreateFolder = useCallback(async () => {\n if (!newFolderName.trim() || !apiConfig?.apiUrl) return;\n\n // Validate folder name\n const name = newFolderName.trim();\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n snackbarController.open({ type: \"error\",\nmessage: t(\"studio_storage_folder_name_has_slash\") });\n return;\n }\n\n // Check if folder already exists\n const existingFolder = folders.find(f => f.name === name);\n if (existingFolder) {\n snackbarController.open({ type: \"error\",\nmessage: t(\"studio_storage_folder_exists\", { name }) });\n return;\n }\n\n setCreatingFolder(true);\n try {\n const folderPath = currentPath ? `default/${currentPath}/${name}` : `default/${name}`;\n const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;\n const response = await fetch(`${apiBase}/storage/folder`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(token ? { \"Authorization\": `Bearer ${token}` } : {})\n },\n // The selected source, as every other action here routes by\n // it: without `storageId` the folder went to the default\n // backend whichever one was being browsed.\n body: JSON.stringify({\n path: folderPath,\n ...(selectedSourceKey !== DEFAULT_STORAGE_SOURCE_KEY ? { storageId: selectedSourceKey } : {})\n })\n });\n\n if (!response.ok) {\n const err = await response.json().catch(() => ({ error: t(\"studio_storage_create_folder_failed\") }));\n throw new Error(err.error || t(\"studio_storage_create_folder_failed\"));\n }\n\n snackbarController.open({ type: \"success\",\nmessage: t(\"studio_storage_folder_created\", { name }) });\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setCreatingFolder(false);\n }\n }, [newFolderName, currentPath, apiConfig, apiBase, snackbarController, fetchContents, folders, selectedSourceKey, t]);\n\n // Drag-and-drop on main view\n const handleDropFiles = useCallback(async (droppedFiles: File[]) => {\n if (droppedFiles.length === 0) return;\n try {\n for (const file of droppedFiles) {\n const key = currentPath ? `${currentPath}/${file.name}` : file.name;\n await storageSourceRef.current.putObject({ file,\nkey });\n }\n snackbarController.open({\n type: \"success\",\n message: t(\"studio_storage_files_uploaded\", { count: droppedFiles.length })\n });\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n }\n }, [currentPath, snackbarController, fetchContents, t]);\n\n const {\n getRootProps: getDropRootProps,\n getInputProps: getDropInputProps,\n isDragActive\n } = useDropzone({\n onDrop: handleDropFiles,\n noClick: true,\n noKeyboard: true,\n noDragEventsBubbling: true\n });\n\n /**\n * Delete files and folders from the source being browsed, then read back\n * the folder they were listed in and return the ones still there.\n *\n * Nothing is reported deleted on the strength of the requests alone: a\n * delete can answer success and leave the object behind — a directory that\n * is not empty yet, a folder marker, a hook — and the folder listing is\n * what the user sees next. One source for the whole operation, taken when\n * it starts.\n */\n const deleteItems = useCallback(async (items: StorageFile[], folder: string): Promise<StorageFile[]> => {\n const source = storageSourceRef.current;\n for (const item of items) {\n if (item.isFolder) {\n await deleteFolderRecursive(source, item.fullPath);\n } else {\n await source.deleteObject(item.fullPath);\n }\n }\n const listed = await listAllPages(source, folder);\n const stillListed = new Set([...listed.items, ...listed.prefixes].map(ref => ref.fullPath));\n return items.filter(item => stillListed.has(item.fullPath));\n }, []);\n\n /** What a delete left behind, said as the error it is rather than a success. */\n const reportStillThere = useCallback((remaining: StorageFile[]) => {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_storage_delete_incomplete\", {\n count: remaining.length,\n names: remaining.map(item => item.name).join(\", \")\n })\n });\n }, [snackbarController, t]);\n\n // Delete a single file\n const handleDeleteFile = useCallback(async (file: StorageFile) => {\n try {\n const remaining = await deleteItems([file], currentPath);\n if (remaining.length > 0) {\n reportStillThere(remaining);\n } else {\n snackbarController.open({ type: \"success\",\nmessage: t(\"studio_storage_file_deleted\", { name: file.name }) });\n }\n setSelectedFile(null);\n setSelectedLink(null);\n setSelectedPaths(prev => {\n const next = new Set(prev);\n next.delete(file.fullPath);\n return next;\n });\n fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n }, [currentPath, snackbarController, fetchContents, deleteItems, reportStillThere, t]);\n\n // Bulk delete (selected items)\n const handleBulkDelete = useCallback(async () => {\n setDeleting(true);\n try {\n const items = allItems.filter(i => selectedPaths.has(i.fullPath));\n const remaining = await deleteItems(items, currentPath);\n if (remaining.length > 0) {\n reportStillThere(remaining);\n } else {\n snackbarController.open({ type: \"success\",\nmessage: t(\"studio_storage_items_deleted\", { count: items.length }) });\n }\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }, [allItems, selectedPaths, currentPath, snackbarController, fetchContents, deleteItems, reportStillThere, t]);\n\n // Confirm delete for a single file or folder\n const handleConfirmDeleteItem = useCallback(async () => {\n if (!deleteDialogTarget || deleteDialogTarget === \"selection\") return;\n setDeleting(true);\n try {\n const remaining = await deleteItems([deleteDialogTarget], currentPath);\n if (remaining.length > 0) {\n reportStillThere(remaining);\n } else {\n snackbarController.open({ type: \"success\",\nmessage: deleteDialogTarget.isFolder\n ? t(\"studio_storage_folder_deleted\", { name: deleteDialogTarget.name })\n : t(\"studio_storage_file_deleted\", { name: deleteDialogTarget.name }) });\n }\n if (!deleteDialogTarget.isFolder && selectedFile?.fullPath === deleteDialogTarget.fullPath) {\n setSelectedFile(null);\n setSelectedLink(null);\n }\n setSelectedPaths(prev => {\n const next = new Set(prev);\n next.delete(deleteDialogTarget.fullPath);\n return next;\n });\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }, [deleteDialogTarget, selectedFile, currentPath, snackbarController, fetchContents, deleteItems, reportStillThere, t]);\n\n // Select all / deselect\n const handleSelectAll = useCallback(() => {\n if (selectedPaths.size === allItems.length) {\n setSelectedPaths(new Set());\n } else {\n setSelectedPaths(new Set(allItems.map(i => i.fullPath)));\n }\n }, [allItems, selectedPaths]);\n\n // ── Keyboard shortcuts ──\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n // Don't handle shortcuts when a dialog is open\n if (deleteDialogOpen || uploadDialogOpen || newFolderDialogOpen) return;\n // Nor a key a layer above already acted on — Escape that closed a\n // menu must not also clear the selection.\n if (isKeyHandled(e)) return;\n // Cmd/Ctrl+A: select all\n if ((e.metaKey || e.ctrlKey) && e.key === \"a\") {\n e.preventDefault();\n handleSelectAll();\n }\n // Escape: deselect\n if (e.key === \"Escape\") {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n }\n // Delete / Backspace: delete selected\n if ((e.key === \"Delete\" || e.key === \"Backspace\") && selectedPaths.size > 0 && !e.metaKey && !e.ctrlKey) {\n // Don't trigger if user is typing in an input\n if ((e.target as HTMLElement)?.tagName === \"INPUT\" || (e.target as HTMLElement)?.tagName === \"TEXTAREA\") return;\n e.preventDefault();\n setDeleteDialogTarget(\"selection\");\n setDeleteDialogOpen(true);\n }\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n }, [handleSelectAll, selectedPaths, deleteDialogOpen, uploadDialogOpen, newFolderDialogOpen]);\n\n // Handle refresh\n const handleRefresh = useCallback(() => {\n fetchContents(currentPath);\n }, [currentPath, fetchContents]);\n\n const segments = breadcrumbSegments(currentPath, t(\"studio_storage_root\"));\n\n\n // ── Render file grid/list ──\n const renderContents = () => {\n if (loading) {\n return (\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 {t(\"studio_storage_loading\")}\n </Typography>\n </div>\n </div>\n );\n }\n\n if (failure?.kind === \"denied\") {\n return (\n <div className=\"flex-grow flex items-center justify-center p-6 overflow-auto\">\n <div className=\"max-w-md text-center\">\n <Typography variant=\"subtitle2\" className=\"block\">\n {t(\"studio_storage_denied_title\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark block mt-2\">\n {t(\"studio_storage_denied_hint\")}\n </Typography>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark block mt-3 font-mono break-all\">\n {failure.detail}\n </Typography>\n </div>\n </div>\n );\n }\n\n if (failure) {\n return (\n <div className=\"flex-grow flex items-center justify-center p-6 overflow-auto\">\n <ErrorView\n title={t(\"studio_storage_read_failed\")}\n error={failure.detail}\n onRetry={failure.retryable ? handleRefresh : undefined}\n />\n </div>\n );\n }\n\n\n if (allItems.length === 0) {\n return (\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\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1} d=\"M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z\"/>\n </svg>\n <Typography variant=\"body2\">\n {t(\"studio_storage_empty\")}\n </Typography>\n <div className=\"flex items-center gap-2 mt-3\">\n <Button variant=\"text\" onClick={() => {\n setNewFolderName(\"\");\n setNewFolderDialogOpen(true);\n }}>\n <FolderPlusIcon size={iconSize.smallest}/>\n {t(\"studio_storage_new_folder\")}\n </Button>\n <Button onClick={() => setUploadDialogOpen(true)}>\n <PlusIcon size={iconSize.smallest}/>\n {t(\"studio_storage_upload_files\")}\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n if (viewMode === \"list\") {\n return (\n <div className=\"flex-grow overflow-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className={cls(\"border-b text-left text-[10px] uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\", defaultBorderMixin)}>\n <th className=\"pl-3 pr-0 py-2 w-8\">\n <Checkbox\n size=\"small\"\n checked={allItems.length > 0 && selectedPaths.size === allItems.length}\n indeterminate={selectedPaths.size > 0 && selectedPaths.size < allItems.length}\n onCheckedChange={handleSelectAll}\n />\n </th>\n <th className=\"px-2 py-2 font-semibold\">{t(\"studio_storage_name\")}</th>\n <th className=\"px-4 py-2 font-semibold w-24\">{t(\"studio_storage_type\")}</th>\n <th className=\"px-4 py-2 font-semibold w-24 text-right\">{t(\"studio_storage_size\")}</th>\n <th className=\"px-2 py-2 w-10\"/>\n </tr>\n </thead>\n <tbody>\n {folders.map(folder => {\n const isChecked = selectedPaths.has(folder.fullPath);\n return (\n <tr\n key={folder.fullPath}\n data-storage-item\n className={cls(\n \"cursor-pointer transition-colors border-b group\",\n defaultBorderMixin,\n isChecked\n ? \"bg-primary/5 dark:bg-primary/10\"\n : \"hover:bg-surface-hover\"\n )}\n onClick={(e) => handleItemClick(folder, e)}\n onDoubleClick={() => handleItemDoubleClick(folder)}\n >\n <td className=\"pl-3 pr-0 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n size=\"small\"\n checked={isChecked}\n onCheckedChange={() => {\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(folder.fullPath)) next.delete(folder.fullPath);\n else next.add(folder.fullPath);\n return next;\n });\n }}\n />\n </td>\n <td className=\"px-2 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <FolderIcon size={iconSize.smallest} className=\"text-amber-500 dark:text-amber-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {folder.name}\n </Typography>\n </div>\n </td>\n <td className=\"px-4 py-2.5\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_storage_folder\")}\n </Typography>\n </td>\n <td className=\"px-4 py-2.5 text-right\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n —\n </Typography>\n </td>\n <td className=\"px-2 py-2.5\"/>\n </tr>\n );\n })}\n {files.map(file => {\n const FileIconComp = getFileIcon(file.contentType);\n const isChecked = selectedPaths.has(file.fullPath);\n return (\n <tr\n key={file.fullPath}\n data-storage-item\n className={cls(\n \"cursor-pointer transition-colors border-b group\",\n defaultBorderMixin,\n isChecked\n ? \"bg-primary/5 dark:bg-primary/10\"\n : \"hover:bg-surface-hover\"\n )}\n onClick={(e) => handleItemClick(file, e)}\n onDoubleClick={() => handleItemDoubleClick(file)}\n >\n <td className=\"pl-3 pr-0 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n size=\"small\"\n checked={isChecked}\n onCheckedChange={() => {\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(file.fullPath)) next.delete(file.fullPath);\n else next.add(file.fullPath);\n return next;\n });\n }}\n />\n </td>\n <td className=\"px-2 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <FileIconComp size={iconSize.smallest} className=\"text-surface-accent-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] truncate\">\n {file.name}\n </Typography>\n </div>\n </td>\n <td className=\"px-4 py-2.5\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {getExtension(file.name) || file.contentType?.split(\"/\")[1]?.toUpperCase() || \"—\"}\n </Typography>\n </td>\n <td className=\"px-4 py-2.5 text-right\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark font-mono text-[11px]\">\n {file.size !== undefined ? formatFileSize(file.size) : \"—\"}\n </Typography>\n </td>\n <td className=\"px-2 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <IconButton\n size=\"smallest\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={() => {\n setDeleteDialogTarget(file);\n setDeleteDialogOpen(true);\n }}\n >\n <Trash2Icon size={14}/>\n </IconButton>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n }\n\n // Grid view\n return (\n <div className=\"flex-grow overflow-auto p-4\">\n {/* Folder cards */}\n {folders.length > 0 && (\n <div className=\"mb-4\">\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark mb-2 block\">\n {t(\"studio_storage_folders\")}\n </Typography>\n <div className=\"grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]\">\n {folders.map(folder => {\n const isChecked = selectedPaths.has(folder.fullPath);\n return (\n <div\n key={folder.fullPath}\n data-storage-item\n className={cls(\n \"rounded-lg p-3 cursor-pointer border\",\n \"transition-colors duration-150\",\n defaultBorderMixin,\n \"hover:bg-surface-hover hover:shadow-sm\",\n \"flex items-center gap-2\",\n isChecked && \"ring-2 ring-primary bg-primary/5 dark:bg-primary/10\"\n )}\n onClick={(e) => handleItemClick(folder, e)}\n onDoubleClick={() => handleItemDoubleClick(folder)}\n >\n <FolderIcon size={iconSize.smallest} className=\"text-amber-500 dark:text-amber-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {folder.name}\n </Typography>\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* FileIcon cards */}\n {files.length > 0 && (\n <div>\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark mb-2 block\">\n {t(\"studio_storage_files_heading\", { count: files.length })}\n </Typography>\n <div className=\"grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]\">\n {files.map(file => {\n const FileIconComp = getFileIcon(file.contentType);\n const ext = getExtension(file.name)?.toLowerCase() || \"\";\n const isImage = file.contentType?.startsWith(\"image/\") || [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"].includes(ext);\n const isChecked = selectedPaths.has(file.fullPath);\n\n return (\n <div\n key={file.fullPath}\n data-storage-item\n className={cls(\n \"rounded-lg overflow-hidden cursor-pointer border\",\n \"transition-shadow duration-150\",\n defaultBorderMixin,\n \"hover:shadow-md\",\n isChecked && \"ring-2 ring-primary\"\n )}\n onClick={(e) => handleItemClick(file, e)}\n onDoubleClick={() => handleItemDoubleClick(file)}\n >\n {/* Thumbnail or icon */}\n <div className=\"aspect-square relative overflow-hidden bg-surface-raised flex items-center justify-center\">\n {isImage && file.downloadUrl ? (\n <img\n src={file.downloadUrl}\n alt={file.name}\n className=\"w-full h-full object-cover\"\n loading=\"lazy\"\n />\n ) : (\n <FileIconComp className=\"text-surface-accent-400 dark:text-surface-accent-500 w-8 h-8\"/>\n )}\n\n {/* Extension badge */}\n {getExtension(file.name) && (\n <div className=\"absolute bottom-1.5 right-1.5 px-1.5 py-0.5 rounded text-[9px] font-semibold uppercase bg-black/50 text-white backdrop-blur-sm\">\n {getExtension(file.name)}\n </div>\n )}\n </div>\n\n {/* Name & size */}\n <div className=\"p-2.5\">\n <Typography variant=\"body2\" className=\"text-[12px] font-medium truncate text-surface-900 dark:text-white\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate block mt-0.5 text-[11px]\">\n {file.size !== undefined ? formatFileSize(file.size) : \"—\"}\n </Typography>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"flex h-full w-full bg-surface-card overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <div className=\"flex h-full w-full\">\n {/* Main content */}\n <div className=\"flex-grow flex flex-col min-w-0 h-full\">\n {/* Toolbar */}\n <div className={cls(\"flex items-center justify-between pr-2 border-b bg-surface-card shrink-0 h-10\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-1.5 flex-grow overflow-hidden px-3 py-2\">\n {/* Breadcrumbs — always visible */}\n {currentPath && (\n <Tooltip title={t(\"studio_storage_go_up\")}>\n <IconButton aria-label={t(\"studio_storage_go_up\")} size=\"small\" onClick={handleNavigateUp}>\n <ArrowLeftIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n <div className=\"flex items-center gap-0.5 overflow-x-auto no-scrollbar\">\n {segments.map((seg, i) => (\n <React.Fragment key={seg.path}>\n {i > 0 && (\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark mx-0.5\">/</Typography>\n )}\n <Button\n variant=\"text\"\n size=\"small\"\n className={cls(\n \"px-1.5 py-0.5 min-h-0 min-w-0 h-6 text-xs whitespace-nowrap normal-case font-normal\",\n i === segments.length - 1\n ? \"text-text-primary dark:text-text-primary-dark font-medium\"\n : \"text-text-secondary dark:text-text-secondary-dark\"\n )}\n onClick={() => handleNavigate(seg.path)}\n >\n {seg.label}\n </Button>\n </React.Fragment>\n ))}\n </div>\n\n <div className=\"flex-1\"/>\n\n {/* Selection actions or file count */}\n {selectedPaths.size > 0 ? (\n <div className=\"flex items-center gap-1.5 shrink-0\">\n <Typography variant=\"body2\" className=\"text-[13px] font-medium whitespace-nowrap\">\n {t(\"studio_storage_selected_count\", { count: selectedPaths.size })}\n </Typography>\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={() => {\n setDeleteDialogTarget(\"selection\");\n setDeleteDialogOpen(true);\n }}\n >\n <Trash2Icon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_delete\")}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={() => {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n }}\n >\n <XIcon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_deselect\")}\n </Button>\n </div>\n ) : !loading ? (\n <Chip size=\"small\" className=\"shrink-0 text-[10px]\">\n {t(\"studio_storage_file_count\", { count: files.length })}\n {folders.length > 0 ? `, ${t(\"studio_storage_folder_count\", { count: folders.length })}` : \"\"}\n </Chip>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center justify-end gap-1.5 pr-1\">\n\n {/* Backend picker — only shown when more than one storage source is available */}\n {sourceKeys.length > 1 && (\n <Select\n size=\"small\"\n position=\"item-aligned\"\n value={selectedSourceKey}\n onValueChange={(value) => {\n if (value && value !== selectedSourceKey) handleSourceChange(value);\n }}\n renderValue={(key) => {\n const label = storageSources.registry[key]?.label;\n return label ?? (key === DEFAULT_STORAGE_SOURCE_KEY ? t(\"studio_storage_default_source\") : key);\n }}>\n {sourceKeys.map((key) => (\n <SelectItem key={key} value={key}>\n {storageSources.registry[key]?.label\n ?? (key === DEFAULT_STORAGE_SOURCE_KEY ? t(\"studio_storage_default_source\") : key)}\n </SelectItem>\n ))}\n </Select>\n )}\n\n <Tooltip title={t(\"studio_storage_grid_view\")}>\n <IconButton aria-label={t(\"studio_storage_grid_view\")}\n size=\"small\"\n onClick={() => setViewMode(\"grid\")}\n className={cls(viewMode === \"grid\" && \"bg-surface-raised\")}\n >\n <LayoutGridIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <Tooltip title={t(\"studio_storage_list_view\")}>\n <IconButton aria-label={t(\"studio_storage_list_view\")}\n size=\"small\"\n onClick={() => setViewMode(\"list\")}\n className={cls(viewMode === \"list\" && \"bg-surface-raised\")}\n >\n <ListIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <div className={cls(\"h-4 w-px mx-0.5\", defaultBorderMixin, \"bg-surface-raised\")}/>\n\n <Tooltip title={t(\"studio_storage_refresh\")}>\n <IconButton aria-label={t(\"studio_storage_refresh\")} size=\"small\" onClick={handleRefresh} disabled={loading}>\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <Tooltip title={t(\"studio_storage_new_folder\")}>\n <IconButton aria-label={t(\"studio_storage_new_folder\")}\n size=\"small\"\n onClick={() => {\n setNewFolderName(\"\");\n setNewFolderDialogOpen(true);\n }}\n >\n <FolderPlusIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => setUploadDialogOpen(true)}\n >\n <UploadCloudIcon size={iconSize.smallest} className=\"mr-1\"/>\n {t(\"studio_storage_upload\")}\n </Button>\n </div>\n </div>\n\n {/* File grid / list — drop zone */}\n <div {...getDropRootProps()}\n className=\"flex-grow flex flex-col overflow-hidden min-h-0 relative\"\n onClick={(e) => {\n const target = e.target as HTMLElement;\n if (!target.closest(\"[data-storage-item]\") && selectedPaths.size > 0) {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n }\n }}\n >\n <input {...getDropInputProps()} />\n {renderContents()}\n {/* Drag overlay */}\n {isDragActive && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-primary/5 dark:bg-primary/10 backdrop-blur-[2px]\">\n <div className=\"flex flex-col items-center gap-2 p-6 rounded-xl border-2 border-dashed border-primary bg-surface-scrim\">\n <UploadCloudIcon className=\"w-10 h-10 text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"text-primary font-semibold\">\n {t(\"studio_storage_drop_to_upload\")}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {t(\"studio_storage_upload_to\")} /{currentPath || t(\"studio_storage_root_folder\")}\n </Typography>\n </div>\n </div>\n )}\n </div>\n\n {/* Status bar */}\n <div className={cls(\"px-4 py-1.5 border-t bg-surface-sheet flex items-center justify-between shrink-0\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-4 text-[11px]\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark font-semibold uppercase tracking-tighter\">\n {t(\"studio_storage_path\")}\n </span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">\n /{currentPath || \"\"}\n </span>\n </div>\n {selectedPaths.size > 0 ? (\n <div className=\"text-[11px] text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_storage_items_selected\", { count: selectedPaths.size })}\n </div>\n ) : selectedFile ? (\n <div className=\"text-[11px] text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_storage_selected_label\")} <span className=\"font-mono\">{selectedFile.name}</span>\n </div>\n ) : null}\n </div>\n </div>\n\n {/* Preview panel */}\n {selectedFile && (\n <div className=\"w-80 lg:w-96 shrink-0\">\n <FilePreviewPanel\n file={selectedFile}\n link={selectedLink}\n mintLink={refreshPreviewLink}\n onClose={() => {\n setSelectedFile(null);\n setSelectedLink(null);\n }}\n onDelete={() => handleDeleteFile(selectedFile)}\n />\n </div>\n )}\n </div>\n\n {/* Upload Dialog */}\n <UploadDialog\n open={uploadDialogOpen}\n currentPath={currentPath}\n onClose={() => setUploadDialogOpen(false)}\n onUpload={handleUpload}\n />\n\n {/* Delete confirmation dialog */}\n <Dialog\n open={deleteDialogOpen}\n onOpenChange={(open) => {\n if (!open && !deleting) {\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }}\n >\n <DialogTitle hidden>{t(\"studio_storage_delete_confirmation_title\")}</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n {deleteDialogTarget === \"selection\"\n ? t(\"studio_storage_delete_items_question\", { count: selectedPaths.size })\n : deleteDialogTarget?.isFolder\n ? t(\"studio_storage_delete_folder_question\", { name: deleteDialogTarget.name })\n : deleteDialogTarget\n ? t(\"studio_storage_delete_file_question\")\n : t(\"studio_storage_delete_question\")}\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n {deleteDialogTarget === \"selection\"\n ? t(\"studio_storage_delete_selection_body\")\n : deleteDialogTarget && !deleteDialogTarget.isFolder\n ? t(\"studio_storage_delete_file_body\", { name: deleteDialogTarget.name })\n : t(\"studio_storage_delete_folder_body\")}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => {\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }}\n disabled={deleting}\n >\n {t(\"studio_storage_cancel\")}\n </Button>\n <LoadingButton\n color=\"error\"\n loading={deleting}\n onClick={deleteDialogTarget === \"selection\" ? handleBulkDelete : handleConfirmDeleteItem}\n >\n <Trash2Icon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_delete\")}\n </LoadingButton>\n </DialogActions>\n </Dialog>\n\n {/* New Folder Dialog */}\n <Dialog\n open={newFolderDialogOpen}\n onOpenChange={(open) => {\n if (!open && !creatingFolder) {\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n }\n }}\n >\n <DialogTitle hidden>{t(\"studio_storage_new_folder_title\")}</DialogTitle>\n <DialogContent>\n <TextField\n autoFocus\n size=\"small\"\n label={t(\"studio_storage_folder_name\")}\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && newFolderName.trim()) {\n e.preventDefault();\n handleCreateFolder();\n }\n }}\n disabled={creatingFolder}\n placeholder={t(\"studio_storage_folder_name_placeholder\")}\n />\n {currentPath && (\n <Typography variant=\"caption\" color=\"secondary\" className=\"mt-2\">\n {t(\"studio_storage_created_in\")} <span className=\"font-mono\">/{currentPath}/</span>\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => {\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n }}\n disabled={creatingFolder}\n >\n {t(\"studio_storage_cancel\")}\n </Button>\n <LoadingButton\n color=\"primary\"\n loading={creatingFolder}\n disabled={!newFolderName.trim()}\n onClick={handleCreateFolder}\n >\n <FolderPlusIcon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_create\")}\n </LoadingButton>\n </DialogActions>\n </Dialog>\n </div>\n );\n};\n"],"mappings":"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"}