@rebasepro/studio 0.25.0-canary.ge06257e → 0.25.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaVisualizer-D-rAqwul.js","names":[],"sources":["../src/components/SchemaVisualizer/dagre-layout.ts","../src/components/SchemaVisualizer/layout-runner.ts","../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\";\n\n/**\n * What the layout reads, as plain data: each table's id and box, each\n * relation's ends. Plain so it can cross into the layout worker unchanged.\n */\nexport interface LayoutInput {\n nodes: { id: string; width: number; height: number }[];\n edges: { source: string; target: string }[];\n}\n\n/** Each table's top-left corner, by node id. */\nexport type LayoutPositions = Record<string, { x: number; y: number }>;\n\n/**\n * Place the graph with dagre, top to bottom.\n *\n * An ERD read as a column of tables fits the tall, narrow canvas the\n * visualizer is given, and it is the only layout the visualizer offers.\n * Deterministic: the same input always gives the same positions.\n *\n * The cost is in dagre's ordering phase and grows fast with cross-linked\n * graphs — 5 s at 200 tables with two relations each, 90 s at 500 — which is\n * why the visualizer runs this in a worker (`dagre-layout.worker.ts`).\n */\nexport function runDagre(input: LayoutInput): LayoutPositions {\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 for (const node of input.nodes) {\n g.setNode(node.id, {\n width: node.width,\n height: node.height\n });\n }\n for (const edge of input.edges) {\n g.setEdge(edge.source, edge.target);\n }\n\n dagre.layout(g);\n\n const positions: LayoutPositions = {};\n for (const node of input.nodes) {\n const placed = g.node(node.id);\n positions[node.id] = {\n x: placed.x - node.width / 2,\n y: placed.y - node.height / 2\n };\n }\n return positions;\n}\n","/// <reference types=\"vite/client\" />\nimport { runDagre, type LayoutInput, type LayoutPositions } from \"./dagre-layout\";\nimport type { LayoutWorkerReply } from \"./dagre-layout.worker\";\n\n/**\n * Whether this environment can start the layout worker.\n *\n * No in tests (jsdom has no `Worker`) and on a server render; there the\n * layout runs where it is asked for, as it always did.\n */\nexport const canLayOutOffThread = (): boolean =>\n typeof Worker !== \"undefined\" &&\n typeof Blob !== \"undefined\" &&\n typeof URL !== \"undefined\" &&\n typeof URL.createObjectURL === \"function\";\n\n/**\n * Lay a graph out in a worker of its own, which ends with the layout.\n *\n * The worker is inlined into the bundle (`?worker&inline`) rather than emitted\n * as a file beside it: `@rebasepro/studio` is a library, and a worker file\n * would be found only by an app bundler that rewrites\n * `new URL(…, import.meta.url)` inside dependencies, which Vite's dependency\n * pre-bundling does not. Imported on first use, so a page that never lays a\n * graph out never decodes it.\n *\n * A worker that cannot be started or loaded — a Content-Security-Policy\n * without `blob:` in `worker-src`, say — is not a broken diagram: the layout\n * runs on the main thread instead, as it did before there was a worker.\n */\nexport async function layOutOffThread(input: LayoutInput): Promise<LayoutPositions> {\n const { default: LayoutWorker } = await import(\"./dagre-layout.worker?worker&inline\");\n let worker: Worker;\n try {\n worker = new LayoutWorker({ name: \"rebase-schema-layout\" });\n } catch {\n return runDagre(input);\n }\n return new Promise<LayoutPositions>((resolve, reject) => {\n worker.addEventListener(\"message\", (event: MessageEvent<LayoutWorkerReply>) => {\n worker.terminate();\n const reply = event.data;\n if (\"positions\" in reply) resolve(reply.positions);\n else reject(new Error(reply.error));\n });\n // Only a worker that never ran gets here: the layout's own errors come\n // back as a reply.\n worker.addEventListener(\"error\", (event) => {\n event.preventDefault();\n worker.terminate();\n try {\n resolve(runDagre(input));\n } catch (error) {\n reject(error);\n }\n });\n worker.postMessage(input);\n });\n}\n","import type { Node, Edge } from \"@xyflow/react\";\nimport { runDagre, type LayoutInput, type LayoutPositions } from \"./dagre-layout\";\nimport { layOutOffThread } from \"./layout-runner\";\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/** A graph's layout input, and the key its positions are kept under. */\nexport interface LayoutPlan {\n input: LayoutInput;\n signature: string;\n}\n\n/**\n * What dagre will be given for these nodes and edges: each node's id and\n * box, each edge's ends, in order. The signature is all of it, so a changed\n * schema — a table, a column count, a relation — is a different key.\n */\nexport const planLayout = (nodes: Node[], edges: Edge[]): LayoutPlan => {\n const input: LayoutInput = {\n nodes: nodes.map((node) => {\n const data = node.data as { columns?: unknown[]; isJunction?: boolean; collectionName?: string; tableName?: string };\n const headerH = getHeaderHeight({\n isJunction: Boolean(data.isJunction),\n collectionName: data.collectionName ?? \"\",\n tableName: data.tableName ?? \"\"\n });\n return {\n id: node.id,\n width: NODE_WIDTH,\n height: estimateNodeHeight(data.columns?.length ?? 3, headerH)\n };\n }),\n edges: edges.map((edge) => ({\n source: edge.source,\n target: edge.target\n }))\n };\n const signature = `${input.nodes.map(node => `${node.id}:${node.height}`).join(\"|\")}#${input.edges.map(edge => `${edge.source}>${edge.target}`).join(\"|\")}`;\n return {\n input,\n signature\n };\n};\n\n/**\n * Positions of the graphs laid out in this page's life, by\n * {@link LayoutPlan.signature}: the most recent few.\n *\n * Dagre takes 4.5–5 s at 200 cross-linked tables and 90 s at 500, so a graph\n * is laid out once: not again when the live RLS answer arrives (a badge, not\n * the graph), nor when the view is reopened. The layout is deterministic, so\n * an entry never goes stale. Holds geometry only, nothing read from the\n * database.\n */\nconst layoutCache = new Map<string, LayoutPositions>();\nconst LAYOUT_CACHE_SIZE = 16;\n\nconst rememberLayout = (signature: string, positions: LayoutPositions): LayoutPositions => {\n layoutCache.set(signature, positions);\n if (layoutCache.size > LAYOUT_CACHE_SIZE) {\n const oldest = layoutCache.keys().next().value;\n if (oldest !== undefined) layoutCache.delete(oldest);\n }\n return positions;\n};\n\n/** The positions for this plan, if it has been laid out before. */\nexport const cachedLayout = (plan: LayoutPlan): LayoutPositions | undefined =>\n layoutCache.get(plan.signature);\n\n/** Lay the plan out here, on the calling thread. */\nexport const layOutNow = (plan: LayoutPlan): LayoutPositions =>\n cachedLayout(plan) ?? rememberLayout(plan.signature, runDagre(plan.input));\n\n/** Layouts running in a worker, by signature, so a second ask waits for the first. */\nconst layoutsInFlight = new Map<string, Promise<LayoutPositions>>();\n\n/**\n * Lay the plan out in a worker; the main thread stays free while it runs.\n * See {@link layOutOffThread}.\n */\nexport const layOutInBackground = (plan: LayoutPlan): Promise<LayoutPositions> => {\n const cached = cachedLayout(plan);\n if (cached) return Promise.resolve(cached);\n const running = layoutsInFlight.get(plan.signature);\n if (running) return running;\n const job = layOutOffThread(plan.input)\n .then((positions) => rememberLayout(plan.signature, positions))\n .finally(() => layoutsInFlight.delete(plan.signature));\n layoutsInFlight.set(plan.signature, job);\n return job;\n};\n\n/** The nodes at their laid-out positions, as new objects. */\nexport const placeNodes = (nodes: Node[], positions: LayoutPositions): Node[] =>\n nodes.map((node) => ({\n ...node,\n position: { ...(positions[node.id] ?? {\n x: 0,\n y: 0\n }) }\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, useEffect } 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 {\n cachedLayout,\n getCardinalityLabel,\n layOutInBackground,\n layOutNow,\n NODE_WIDTH,\n placeNodes,\n planLayout\n} from \"./schema-visualizer.utils\";\nimport type { RelationEdgeData } from \"./schema-visualizer.utils\";\nimport type { LayoutPositions } from \"./dagre-layout\";\nimport { canLayOutOffThread } from \"./layout-runner\";\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\n/**\n * Every table and relation the collections declare, not yet placed. Edges\n * carry their column handles without a side; {@link placeGraph} picks the\n * side once it knows where the tables are.\n */\ninterface SchemaGraph {\n nodes: Node[];\n edges: Edge[];\n}\n\nconst buildGraph = (\n collections: AdminCollection[],\n liveRls: Set<string> | null\n): SchemaGraph => {\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 return {\n nodes,\n edges\n };\n};\n\n/**\n * The graph at its laid-out positions, with each edge's handles on the sides\n * that face each other: a source left of its target exits right and enters\n * the target from the left, and the other way round.\n */\nconst placeGraph = (graph: SchemaGraph, positions: LayoutPositions): SchemaGraph => {\n const nodes = placeNodes(graph.nodes, positions);\n const edges = graph.edges.map((edge) => {\n const srcPos = positions[edge.source];\n const tgtPos = positions[edge.target];\n const srcBaseHandle = edge.sourceHandle ?? \"source-default\";\n const tgtBaseHandle = edge.targetHandle ?? \"target-default\";\n if (!srcPos || !tgtPos) {\n return {\n ...edge,\n sourceHandle: `${srcBaseHandle}-right`,\n targetHandle: `${tgtBaseHandle}-left`\n };\n }\n const sourceIsLeft = srcPos.x + NODE_WIDTH / 2 <= tgtPos.x + NODE_WIDTH / 2;\n return {\n ...edge,\n sourceHandle: `${srcBaseHandle}-${sourceIsLeft ? \"right\" : \"left\"}`,\n targetHandle: `${tgtBaseHandle}-${sourceIsLeft ? \"left\" : \"right\"}`\n };\n });\n return {\n nodes,\n edges\n };\n};\n\n// ─── Large schemas: one table's neighbourhood ─────────────────────────\n\n/**\n * Above this many tables the diagram draws one table's neighbourhood, not\n * the whole schema. Laying out 300 cross-linked tables takes dagre several\n * seconds even in a worker, and a canvas of that many tables is no longer\n * something anyone reads.\n */\nexport const LARGE_SCHEMA_TABLES = 300;\n\n/** How far from the focused table the drawn neighbourhood reaches. */\nexport type NeighbourhoodDepth = 1 | 2;\n\nconst isJunctionNode = (node: Node): boolean => Boolean((node.data as TableNodeData).isJunction);\n\n/**\n * The tables within `depth` relations of `focus`, and every relation between\n * them.\n *\n * A junction table is not a step of its own: a many-to-many is one relation,\n * drawn through its junction. So distances are counted in half steps — an edge\n * that touches a junction is one, a foreign key between two tables is two.\n */\nconst neighbourhoodOf = (graph: SchemaGraph, focus: string, depth: NeighbourhoodDepth): SchemaGraph => {\n const junctions = new Set(graph.nodes.filter(isJunctionNode).map((node) => node.id));\n const adjacent = new Map<string, string[]>();\n for (const edge of graph.edges) {\n adjacent.set(edge.source, [...(adjacent.get(edge.source) ?? []), edge.target]);\n adjacent.set(edge.target, [...(adjacent.get(edge.target) ?? []), edge.source]);\n }\n const limit = depth * 2;\n const distance = new Map<string, number>([[focus, 0]]);\n const byDistance: string[][] = Array.from({ length: limit + 1 }, () => []);\n byDistance[0].push(focus);\n for (let reached = 0; reached <= limit; reached++) {\n for (const id of byDistance[reached]) {\n if (distance.get(id) !== reached) continue;\n for (const next of adjacent.get(id) ?? []) {\n const step = junctions.has(id) || junctions.has(next) ? 1 : 2;\n const nextDistance = reached + step;\n if (nextDistance > limit || (distance.get(next) ?? Infinity) <= nextDistance) continue;\n distance.set(next, nextDistance);\n byDistance[nextDistance].push(next);\n }\n }\n }\n return {\n nodes: graph.nodes.filter((node) => distance.has(node.id)),\n edges: graph.edges.filter((edge) => distance.has(edge.source) && distance.has(edge.target))\n };\n};\n\n/** The table with the most relations: where a large schema opens. */\nconst mostConnectedTable = (graph: SchemaGraph): string | undefined => {\n const degree = new Map<string, number>();\n for (const edge of graph.edges) {\n degree.set(edge.source, (degree.get(edge.source) ?? 0) + 1);\n degree.set(edge.target, (degree.get(edge.target) ?? 0) + 1);\n }\n let best: Node | undefined;\n for (const node of graph.nodes) {\n if (isJunctionNode(node)) continue;\n if (!best || (degree.get(node.id) ?? 0) > (degree.get(best.id) ?? 0)) best = node;\n }\n return best?.id;\n};\n\n// ─── Hook ─────────────────────────────────────────────────────────────\n\nexport interface UseSchemaGraphResult {\n /**\n * The tables and relations on the canvas, placed. Empty while\n * {@link layingOut} — the layout of a graph not seen before runs in a\n * worker, never in render.\n */\n nodes: Node[];\n edges: Edge[];\n /** Every table and junction table in the schema, drawn or not. */\n allNodes: Node[];\n relayout: () => void;\n /** Tables and relations in the whole schema. */\n tableCount: number;\n relationCount: number;\n /** The layout is running off the main thread. */\n layingOut: boolean;\n /**\n * Set when the schema has more than {@link LARGE_SCHEMA_TABLES} tables:\n * which table's neighbourhood is drawn, how far it reaches, how many\n * tables that is, and how many there are in all.\n */\n neighbourhood: { focus: string; depth: NeighbourhoodDepth; shown: number; total: number } | null;\n}\n\nconst EMPTY_GRAPH: SchemaGraph = {\n nodes: [],\n edges: []\n};\nconst NO_POSITIONS: LayoutPositions = {};\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 /**\n * The table whose neighbourhood a large schema draws, by node id. When it\n * is absent, or not a table of this schema, the most connected one. Not\n * read for a schema of {@link LARGE_SCHEMA_TABLES} tables or fewer, which\n * is drawn whole.\n */\n focus: string | null = null,\n depth: NeighbourhoodDepth = 1\n): UseSchemaGraphResult => {\n const [version, setVersion] = useState(0);\n\n const relayout = useCallback(() => setVersion((v) => v + 1), []);\n\n const graph = useMemo(\n () => (collections && collections.length > 0 ? buildGraph(collections, liveRls) : EMPTY_GRAPH),\n [collections, liveRls]\n );\n\n const tables = graph.nodes.filter((node) => !isJunctionNode(node)).length;\n const large = tables > LARGE_SCHEMA_TABLES;\n const focused = large\n ? (focus && graph.nodes.some((node) => node.id === focus) ? focus : mostConnectedTable(graph)) ?? null\n : null;\n\n const drawn = useMemo(\n () => (focused ? neighbourhoodOf(graph, focused, depth) : graph),\n [graph, focused, depth]\n );\n const plan = useMemo(() => planLayout(drawn.nodes, drawn.edges), [drawn]);\n\n /**\n * Where the layout runs. Off the main thread wherever a worker can start;\n * in tests and on a server, here, as the visualizer always did.\n */\n const offThread = canLayOutOffThread();\n const [laidOut, setLaidOut] = useState<{ signature: string; positions: LayoutPositions } | null>(null);\n const [layoutError, setLayoutError] = useState<unknown>(null);\n\n const positions = drawn.nodes.length === 0\n ? NO_POSITIONS\n : cachedLayout(plan)\n ?? (laidOut?.signature === plan.signature ? laidOut.positions : undefined)\n ?? (offThread ? undefined : layOutNow(plan));\n\n useEffect(() => {\n if (!offThread || drawn.nodes.length === 0 || cachedLayout(plan)) return;\n let current = true;\n layOutInBackground(plan).then(\n (result) => {\n if (current) {\n setLaidOut({\n signature: plan.signature,\n positions: result\n });\n }\n },\n (error: unknown) => {\n if (current) setLayoutError(error);\n }\n );\n return () => {\n current = false;\n };\n }, [plan, offThread, drawn]);\n\n const placed = useMemo(\n () => (positions ? placeGraph(drawn, positions) : EMPTY_GRAPH),\n // `version` is the relayout button: new node objects put back every\n // table a person dragged.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [drawn, positions, version]\n );\n\n // The layout failing is the diagram failing, as it was when the layout\n // ran in render: the view's error boundary says so.\n if (layoutError) throw layoutError;\n\n return {\n nodes: placed.nodes,\n edges: placed.edges,\n allNodes: graph.nodes,\n relayout,\n tableCount: graph.nodes.length,\n relationCount: graph.edges.length,\n layingOut: positions === undefined,\n neighbourhood: focused\n ? {\n focus: focused,\n depth,\n shown: drawn.nodes.length,\n total: tables\n }\n : null\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 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 ToggleButtonGroup,\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 { NeighbourhoodDepth, 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 // A large schema is drawn one table's neighbourhood at a time; these say\n // which table, and how far out. See `LARGE_SCHEMA_TABLES`.\n const [focus, setFocus] = useState<string | null>(null);\n const [depth, setDepth] = useState<NeighbourhoodDepth>(1);\n const {\n nodes: layoutedNodes,\n edges: layoutedEdges,\n allNodes,\n relayout,\n tableCount,\n relationCount,\n layingOut,\n neighbourhood\n } = useSchemaGraph(collections, liveRls, focus, depth);\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\n // Sync layouted data into state. While a new layout runs, what is drawn\n // stays drawn: a neighbourhood being replaced is not an empty canvas.\n useEffect(() => {\n if (layingOut) return;\n setNodes(layoutedNodes);\n setEdges(layoutedEdges);\n }, [layoutedNodes, layoutedEdges, layingOut]);\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 when it is first drawn, and again whenever a\n * different neighbourhood replaces it.\n *\n * `fitView` needs every node's real width and height, which React Flow\n * only knows once it has rendered and measured them; a fit made before\n * that leaves the ERD a scatter of specks in a corner. React Flow's own\n * `fitView` waits for the measurement, so it is asked for in the commit\n * that hands it the new nodes — after its store has them, which a\n * child's effect does before this one runs.\n */\n const fitPending = useRef(true);\n useEffect(() => {\n if (!fitPending.current || nodes.length === 0) return;\n fitPending.current = false;\n void reactFlowInstance.fitView({\n padding: 0.15,\n duration: 400\n });\n }, [nodes, 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. A large schema draws the picked\n // table's neighbourhood instead, and fits that once it is laid out.\n const handleTableSelect = useCallback(\n (nodeId: string) => {\n setSelectedTable(nodeId);\n if (neighbourhood) {\n if (neighbourhood.focus !== nodeId) {\n fitPending.current = true;\n setFocus(nodeId);\n }\n return;\n }\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, neighbourhood]\n );\n\n const handleDepthChange = useCallback((value: string) => {\n fitPending.current = true;\n setDepth(value === \"2\" ? 2 : 1);\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 — every one in the schema, drawn or not.\n const junctionNodes = useMemo(\n () =>\n allNodes.filter(\n (n) => (n.data as TableNodeData).isJunction\n ),\n [allNodes]\n );\n\n /**\n * The RLS answer for one collection, from the same place the graph used.\n *\n * The graph's nodes already carry it, so look it up there rather than\n * re-deriving — that is what kept the sidebar dots, the node badges and the\n * total from agreeing. Every node, not only the drawn ones: a large schema\n * draws a neighbourhood, and the list shows every table.\n */\n const rlsByNodeId = useMemo(\n () => new Map(allNodes.map((n) => [n.id, Boolean((n.data as TableNodeData).rlsEnabled)])),\n [allNodes]\n );\n const rlsEnabledFor = useCallback(\n (collection: AdminCollection): boolean => rlsByNodeId.get(`table-${getTableName(collection)}`) ?? false,\n [rlsByNodeId]\n );\n\n const focusName = useMemo(() => {\n const node = neighbourhood ? allNodes.find((n) => n.id === neighbourhood.focus) : undefined;\n return node ? (node.data as TableNodeData).collectionName : \"\";\n }, [allNodes, neighbourhood]);\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: allNodes.filter((n) => (n.data as TableNodeData | undefined)?.rlsEnabled).length\n }),\n [\n tableCount,\n relationCount,\n junctionNodes.length,\n allNodes\n ]\n );\n\n return (\n <ResizablePanels\n dividerAriaLabel={t(\"resize_sidebar\")}\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 {/* A large schema: which part is drawn, and how far out */}\n {neighbourhood && (\n <div className=\"absolute top-4 left-4 right-4 flex justify-start pointer-events-none\">\n <div className=\"flex flex-wrap items-center gap-2 max-w-full px-3 py-2 bg-surface-card/90 backdrop-blur-sm rounded-lg border border-hairline shadow-sm pointer-events-auto\">\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark\"\n >\n {t(\"studio_schema_neighbourhood\", {\n shown: neighbourhood.shown,\n total: neighbourhood.total,\n table: focusName\n })}\n </Typography>\n <ToggleButtonGroup\n value={String(neighbourhood.depth)}\n onValueChange={handleDepthChange}\n options={[\n {\n value: \"1\",\n label: t(\"studio_schema_neighbourhood_depth_1\")\n },\n {\n value: \"2\",\n label: t(\"studio_schema_neighbourhood_depth_2\")\n }\n ]}\n />\n </div>\n </div>\n )}\n\n {/* The layout runs in a worker; say so while it does */}\n {layingOut && (\n <div\n role=\"status\"\n className=\"absolute inset-0 flex items-center justify-center pointer-events-none\"\n >\n <div className=\"flex items-center gap-2 px-3 py-2 bg-surface-card/90 backdrop-blur-sm rounded-lg border border-hairline shadow-sm\">\n <CircularProgress size=\"smallest\"/>\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark\"\n >\n {t(\"studio_schema_laying_out\")}\n </Typography>\n </div>\n </div>\n )}\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
+ {"version":3,"file":"SchemaVisualizer-pCYQ_Ds6.js","names":[],"sources":["../src/components/SchemaVisualizer/dagre-layout.ts","../src/components/SchemaVisualizer/layout-runner.ts","../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\";\n\n/**\n * What the layout reads, as plain data: each table's id and box, each\n * relation's ends. Plain so it can cross into the layout worker unchanged.\n */\nexport interface LayoutInput {\n nodes: { id: string; width: number; height: number }[];\n edges: { source: string; target: string }[];\n}\n\n/** Each table's top-left corner, by node id. */\nexport type LayoutPositions = Record<string, { x: number; y: number }>;\n\n/**\n * Place the graph with dagre, top to bottom.\n *\n * An ERD read as a column of tables fits the tall, narrow canvas the\n * visualizer is given, and it is the only layout the visualizer offers.\n * Deterministic: the same input always gives the same positions.\n *\n * The cost is in dagre's ordering phase and grows fast with cross-linked\n * graphs — 5 s at 200 tables with two relations each, 90 s at 500 — which is\n * why the visualizer runs this in a worker (`dagre-layout.worker.ts`).\n */\nexport function runDagre(input: LayoutInput): LayoutPositions {\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 for (const node of input.nodes) {\n g.setNode(node.id, {\n width: node.width,\n height: node.height\n });\n }\n for (const edge of input.edges) {\n g.setEdge(edge.source, edge.target);\n }\n\n dagre.layout(g);\n\n const positions: LayoutPositions = {};\n for (const node of input.nodes) {\n const placed = g.node(node.id);\n positions[node.id] = {\n x: placed.x - node.width / 2,\n y: placed.y - node.height / 2\n };\n }\n return positions;\n}\n","/// <reference types=\"vite/client\" />\nimport { runDagre, type LayoutInput, type LayoutPositions } from \"./dagre-layout\";\nimport type { LayoutWorkerReply } from \"./dagre-layout.worker\";\n\n/**\n * Whether this environment can start the layout worker.\n *\n * No in tests (jsdom has no `Worker`) and on a server render; there the\n * layout runs where it is asked for, as it always did.\n */\nexport const canLayOutOffThread = (): boolean =>\n typeof Worker !== \"undefined\" &&\n typeof Blob !== \"undefined\" &&\n typeof URL !== \"undefined\" &&\n typeof URL.createObjectURL === \"function\";\n\n/**\n * Lay a graph out in a worker of its own, which ends with the layout.\n *\n * The worker is inlined into the bundle (`?worker&inline`) rather than emitted\n * as a file beside it: `@rebasepro/studio` is a library, and a worker file\n * would be found only by an app bundler that rewrites\n * `new URL(…, import.meta.url)` inside dependencies, which Vite's dependency\n * pre-bundling does not. Imported on first use, so a page that never lays a\n * graph out never decodes it.\n *\n * A worker that cannot be started or loaded — a Content-Security-Policy\n * without `blob:` in `worker-src`, say — is not a broken diagram: the layout\n * runs on the main thread instead, as it did before there was a worker.\n */\nexport async function layOutOffThread(input: LayoutInput): Promise<LayoutPositions> {\n const { default: LayoutWorker } = await import(\"./dagre-layout.worker?worker&inline\");\n let worker: Worker;\n try {\n worker = new LayoutWorker({ name: \"rebase-schema-layout\" });\n } catch {\n return runDagre(input);\n }\n return new Promise<LayoutPositions>((resolve, reject) => {\n worker.addEventListener(\"message\", (event: MessageEvent<LayoutWorkerReply>) => {\n worker.terminate();\n const reply = event.data;\n if (\"positions\" in reply) resolve(reply.positions);\n else reject(new Error(reply.error));\n });\n // Only a worker that never ran gets here: the layout's own errors come\n // back as a reply.\n worker.addEventListener(\"error\", (event) => {\n event.preventDefault();\n worker.terminate();\n try {\n resolve(runDagre(input));\n } catch (error) {\n reject(error);\n }\n });\n worker.postMessage(input);\n });\n}\n","import type { Node, Edge } from \"@xyflow/react\";\nimport { runDagre, type LayoutInput, type LayoutPositions } from \"./dagre-layout\";\nimport { layOutOffThread } from \"./layout-runner\";\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/** A graph's layout input, and the key its positions are kept under. */\nexport interface LayoutPlan {\n input: LayoutInput;\n signature: string;\n}\n\n/**\n * What dagre will be given for these nodes and edges: each node's id and\n * box, each edge's ends, in order. The signature is all of it, so a changed\n * schema — a table, a column count, a relation — is a different key.\n */\nexport const planLayout = (nodes: Node[], edges: Edge[]): LayoutPlan => {\n const input: LayoutInput = {\n nodes: nodes.map((node) => {\n const data = node.data as { columns?: unknown[]; isJunction?: boolean; collectionName?: string; tableName?: string };\n const headerH = getHeaderHeight({\n isJunction: Boolean(data.isJunction),\n collectionName: data.collectionName ?? \"\",\n tableName: data.tableName ?? \"\"\n });\n return {\n id: node.id,\n width: NODE_WIDTH,\n height: estimateNodeHeight(data.columns?.length ?? 3, headerH)\n };\n }),\n edges: edges.map((edge) => ({\n source: edge.source,\n target: edge.target\n }))\n };\n const signature = `${input.nodes.map(node => `${node.id}:${node.height}`).join(\"|\")}#${input.edges.map(edge => `${edge.source}>${edge.target}`).join(\"|\")}`;\n return {\n input,\n signature\n };\n};\n\n/**\n * Positions of the graphs laid out in this page's life, by\n * {@link LayoutPlan.signature}: the most recent few.\n *\n * Dagre takes 4.5–5 s at 200 cross-linked tables and 90 s at 500, so a graph\n * is laid out once: not again when the live RLS answer arrives (a badge, not\n * the graph), nor when the view is reopened. The layout is deterministic, so\n * an entry never goes stale. Holds geometry only, nothing read from the\n * database.\n */\nconst layoutCache = new Map<string, LayoutPositions>();\nconst LAYOUT_CACHE_SIZE = 16;\n\nconst rememberLayout = (signature: string, positions: LayoutPositions): LayoutPositions => {\n layoutCache.set(signature, positions);\n if (layoutCache.size > LAYOUT_CACHE_SIZE) {\n const oldest = layoutCache.keys().next().value;\n if (oldest !== undefined) layoutCache.delete(oldest);\n }\n return positions;\n};\n\n/** The positions for this plan, if it has been laid out before. */\nexport const cachedLayout = (plan: LayoutPlan): LayoutPositions | undefined =>\n layoutCache.get(plan.signature);\n\n/** Lay the plan out here, on the calling thread. */\nexport const layOutNow = (plan: LayoutPlan): LayoutPositions =>\n cachedLayout(plan) ?? rememberLayout(plan.signature, runDagre(plan.input));\n\n/** Layouts running in a worker, by signature, so a second ask waits for the first. */\nconst layoutsInFlight = new Map<string, Promise<LayoutPositions>>();\n\n/**\n * Lay the plan out in a worker; the main thread stays free while it runs.\n * See {@link layOutOffThread}.\n */\nexport const layOutInBackground = (plan: LayoutPlan): Promise<LayoutPositions> => {\n const cached = cachedLayout(plan);\n if (cached) return Promise.resolve(cached);\n const running = layoutsInFlight.get(plan.signature);\n if (running) return running;\n const job = layOutOffThread(plan.input)\n .then((positions) => rememberLayout(plan.signature, positions))\n .finally(() => layoutsInFlight.delete(plan.signature));\n layoutsInFlight.set(plan.signature, job);\n return job;\n};\n\n/** The nodes at their laid-out positions, as new objects. */\nexport const placeNodes = (nodes: Node[], positions: LayoutPositions): Node[] =>\n nodes.map((node) => ({\n ...node,\n position: { ...(positions[node.id] ?? {\n x: 0,\n y: 0\n }) }\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, useEffect } 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 {\n cachedLayout,\n getCardinalityLabel,\n layOutInBackground,\n layOutNow,\n NODE_WIDTH,\n placeNodes,\n planLayout\n} from \"./schema-visualizer.utils\";\nimport type { RelationEdgeData } from \"./schema-visualizer.utils\";\nimport type { LayoutPositions } from \"./dagre-layout\";\nimport { canLayOutOffThread } from \"./layout-runner\";\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\n/**\n * Every table and relation the collections declare, not yet placed. Edges\n * carry their column handles without a side; {@link placeGraph} picks the\n * side once it knows where the tables are.\n */\ninterface SchemaGraph {\n nodes: Node[];\n edges: Edge[];\n}\n\nconst buildGraph = (\n collections: AdminCollection[],\n liveRls: Set<string> | null\n): SchemaGraph => {\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 return {\n nodes,\n edges\n };\n};\n\n/**\n * The graph at its laid-out positions, with each edge's handles on the sides\n * that face each other: a source left of its target exits right and enters\n * the target from the left, and the other way round.\n */\nconst placeGraph = (graph: SchemaGraph, positions: LayoutPositions): SchemaGraph => {\n const nodes = placeNodes(graph.nodes, positions);\n const edges = graph.edges.map((edge) => {\n const srcPos = positions[edge.source];\n const tgtPos = positions[edge.target];\n const srcBaseHandle = edge.sourceHandle ?? \"source-default\";\n const tgtBaseHandle = edge.targetHandle ?? \"target-default\";\n if (!srcPos || !tgtPos) {\n return {\n ...edge,\n sourceHandle: `${srcBaseHandle}-right`,\n targetHandle: `${tgtBaseHandle}-left`\n };\n }\n const sourceIsLeft = srcPos.x + NODE_WIDTH / 2 <= tgtPos.x + NODE_WIDTH / 2;\n return {\n ...edge,\n sourceHandle: `${srcBaseHandle}-${sourceIsLeft ? \"right\" : \"left\"}`,\n targetHandle: `${tgtBaseHandle}-${sourceIsLeft ? \"left\" : \"right\"}`\n };\n });\n return {\n nodes,\n edges\n };\n};\n\n// ─── Large schemas: one table's neighbourhood ─────────────────────────\n\n/**\n * Above this many tables the diagram draws one table's neighbourhood, not\n * the whole schema. Laying out 300 cross-linked tables takes dagre several\n * seconds even in a worker, and a canvas of that many tables is no longer\n * something anyone reads.\n */\nexport const LARGE_SCHEMA_TABLES = 300;\n\n/** How far from the focused table the drawn neighbourhood reaches. */\nexport type NeighbourhoodDepth = 1 | 2;\n\nconst isJunctionNode = (node: Node): boolean => Boolean((node.data as TableNodeData).isJunction);\n\n/**\n * The tables within `depth` relations of `focus`, and every relation between\n * them.\n *\n * A junction table is not a step of its own: a many-to-many is one relation,\n * drawn through its junction. So distances are counted in half steps — an edge\n * that touches a junction is one, a foreign key between two tables is two.\n */\nconst neighbourhoodOf = (graph: SchemaGraph, focus: string, depth: NeighbourhoodDepth): SchemaGraph => {\n const junctions = new Set(graph.nodes.filter(isJunctionNode).map((node) => node.id));\n const adjacent = new Map<string, string[]>();\n for (const edge of graph.edges) {\n adjacent.set(edge.source, [...(adjacent.get(edge.source) ?? []), edge.target]);\n adjacent.set(edge.target, [...(adjacent.get(edge.target) ?? []), edge.source]);\n }\n const limit = depth * 2;\n const distance = new Map<string, number>([[focus, 0]]);\n const byDistance: string[][] = Array.from({ length: limit + 1 }, () => []);\n byDistance[0].push(focus);\n for (let reached = 0; reached <= limit; reached++) {\n for (const id of byDistance[reached]) {\n if (distance.get(id) !== reached) continue;\n for (const next of adjacent.get(id) ?? []) {\n const step = junctions.has(id) || junctions.has(next) ? 1 : 2;\n const nextDistance = reached + step;\n if (nextDistance > limit || (distance.get(next) ?? Infinity) <= nextDistance) continue;\n distance.set(next, nextDistance);\n byDistance[nextDistance].push(next);\n }\n }\n }\n return {\n nodes: graph.nodes.filter((node) => distance.has(node.id)),\n edges: graph.edges.filter((edge) => distance.has(edge.source) && distance.has(edge.target))\n };\n};\n\n/** The table with the most relations: where a large schema opens. */\nconst mostConnectedTable = (graph: SchemaGraph): string | undefined => {\n const degree = new Map<string, number>();\n for (const edge of graph.edges) {\n degree.set(edge.source, (degree.get(edge.source) ?? 0) + 1);\n degree.set(edge.target, (degree.get(edge.target) ?? 0) + 1);\n }\n let best: Node | undefined;\n for (const node of graph.nodes) {\n if (isJunctionNode(node)) continue;\n if (!best || (degree.get(node.id) ?? 0) > (degree.get(best.id) ?? 0)) best = node;\n }\n return best?.id;\n};\n\n// ─── Hook ─────────────────────────────────────────────────────────────\n\nexport interface UseSchemaGraphResult {\n /**\n * The tables and relations on the canvas, placed. Empty while\n * {@link layingOut} — the layout of a graph not seen before runs in a\n * worker, never in render.\n */\n nodes: Node[];\n edges: Edge[];\n /** Every table and junction table in the schema, drawn or not. */\n allNodes: Node[];\n relayout: () => void;\n /** Tables and relations in the whole schema. */\n tableCount: number;\n relationCount: number;\n /** The layout is running off the main thread. */\n layingOut: boolean;\n /**\n * Set when the schema has more than {@link LARGE_SCHEMA_TABLES} tables:\n * which table's neighbourhood is drawn, how far it reaches, how many\n * tables that is, and how many there are in all.\n */\n neighbourhood: { focus: string; depth: NeighbourhoodDepth; shown: number; total: number } | null;\n}\n\nconst EMPTY_GRAPH: SchemaGraph = {\n nodes: [],\n edges: []\n};\nconst NO_POSITIONS: LayoutPositions = {};\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 /**\n * The table whose neighbourhood a large schema draws, by node id. When it\n * is absent, or not a table of this schema, the most connected one. Not\n * read for a schema of {@link LARGE_SCHEMA_TABLES} tables or fewer, which\n * is drawn whole.\n */\n focus: string | null = null,\n depth: NeighbourhoodDepth = 1\n): UseSchemaGraphResult => {\n const [version, setVersion] = useState(0);\n\n const relayout = useCallback(() => setVersion((v) => v + 1), []);\n\n const graph = useMemo(\n () => (collections && collections.length > 0 ? buildGraph(collections, liveRls) : EMPTY_GRAPH),\n [collections, liveRls]\n );\n\n const tables = graph.nodes.filter((node) => !isJunctionNode(node)).length;\n const large = tables > LARGE_SCHEMA_TABLES;\n const focused = large\n ? (focus && graph.nodes.some((node) => node.id === focus) ? focus : mostConnectedTable(graph)) ?? null\n : null;\n\n const drawn = useMemo(\n () => (focused ? neighbourhoodOf(graph, focused, depth) : graph),\n [graph, focused, depth]\n );\n const plan = useMemo(() => planLayout(drawn.nodes, drawn.edges), [drawn]);\n\n /**\n * Where the layout runs. Off the main thread wherever a worker can start;\n * in tests and on a server, here, as the visualizer always did.\n */\n const offThread = canLayOutOffThread();\n const [laidOut, setLaidOut] = useState<{ signature: string; positions: LayoutPositions } | null>(null);\n const [layoutError, setLayoutError] = useState<unknown>(null);\n\n const positions = drawn.nodes.length === 0\n ? NO_POSITIONS\n : cachedLayout(plan)\n ?? (laidOut?.signature === plan.signature ? laidOut.positions : undefined)\n ?? (offThread ? undefined : layOutNow(plan));\n\n useEffect(() => {\n if (!offThread || drawn.nodes.length === 0 || cachedLayout(plan)) return;\n let current = true;\n layOutInBackground(plan).then(\n (result) => {\n if (current) {\n setLaidOut({\n signature: plan.signature,\n positions: result\n });\n }\n },\n (error: unknown) => {\n if (current) setLayoutError(error);\n }\n );\n return () => {\n current = false;\n };\n }, [plan, offThread, drawn]);\n\n const placed = useMemo(\n () => (positions ? placeGraph(drawn, positions) : EMPTY_GRAPH),\n // `version` is the relayout button: new node objects put back every\n // table a person dragged.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [drawn, positions, version]\n );\n\n // The layout failing is the diagram failing, as it was when the layout\n // ran in render: the view's error boundary says so.\n if (layoutError) throw layoutError;\n\n return {\n nodes: placed.nodes,\n edges: placed.edges,\n allNodes: graph.nodes,\n relayout,\n tableCount: graph.nodes.length,\n relationCount: graph.edges.length,\n layingOut: positions === undefined,\n neighbourhood: focused\n ? {\n focus: focused,\n depth,\n shown: drawn.nodes.length,\n total: tables\n }\n : null\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, #9da2aa)\";\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 = \"#9da2aa\";\n } else if (isJoinPath) {\n strokeDasharray = \"3 3\";\n strokeColor = \"#9da2aa\";\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 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 ToggleButtonGroup,\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 { NeighbourhoodDepth, 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 // A large schema is drawn one table's neighbourhood at a time; these say\n // which table, and how far out. See `LARGE_SCHEMA_TABLES`.\n const [focus, setFocus] = useState<string | null>(null);\n const [depth, setDepth] = useState<NeighbourhoodDepth>(1);\n const {\n nodes: layoutedNodes,\n edges: layoutedEdges,\n allNodes,\n relayout,\n tableCount,\n relationCount,\n layingOut,\n neighbourhood\n } = useSchemaGraph(collections, liveRls, focus, depth);\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\n // Sync layouted data into state. While a new layout runs, what is drawn\n // stays drawn: a neighbourhood being replaced is not an empty canvas.\n useEffect(() => {\n if (layingOut) return;\n setNodes(layoutedNodes);\n setEdges(layoutedEdges);\n }, [layoutedNodes, layoutedEdges, layingOut]);\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 when it is first drawn, and again whenever a\n * different neighbourhood replaces it.\n *\n * `fitView` needs every node's real width and height, which React Flow\n * only knows once it has rendered and measured them; a fit made before\n * that leaves the ERD a scatter of specks in a corner. React Flow's own\n * `fitView` waits for the measurement, so it is asked for in the commit\n * that hands it the new nodes — after its store has them, which a\n * child's effect does before this one runs.\n */\n const fitPending = useRef(true);\n useEffect(() => {\n if (!fitPending.current || nodes.length === 0) return;\n fitPending.current = false;\n void reactFlowInstance.fitView({\n padding: 0.15,\n duration: 400\n });\n }, [nodes, 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. A large schema draws the picked\n // table's neighbourhood instead, and fits that once it is laid out.\n const handleTableSelect = useCallback(\n (nodeId: string) => {\n setSelectedTable(nodeId);\n if (neighbourhood) {\n if (neighbourhood.focus !== nodeId) {\n fitPending.current = true;\n setFocus(nodeId);\n }\n return;\n }\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, neighbourhood]\n );\n\n const handleDepthChange = useCallback((value: string) => {\n fitPending.current = true;\n setDepth(value === \"2\" ? 2 : 1);\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 — every one in the schema, drawn or not.\n const junctionNodes = useMemo(\n () =>\n allNodes.filter(\n (n) => (n.data as TableNodeData).isJunction\n ),\n [allNodes]\n );\n\n /**\n * The RLS answer for one collection, from the same place the graph used.\n *\n * The graph's nodes already carry it, so look it up there rather than\n * re-deriving — that is what kept the sidebar dots, the node badges and the\n * total from agreeing. Every node, not only the drawn ones: a large schema\n * draws a neighbourhood, and the list shows every table.\n */\n const rlsByNodeId = useMemo(\n () => new Map(allNodes.map((n) => [n.id, Boolean((n.data as TableNodeData).rlsEnabled)])),\n [allNodes]\n );\n const rlsEnabledFor = useCallback(\n (collection: AdminCollection): boolean => rlsByNodeId.get(`table-${getTableName(collection)}`) ?? false,\n [rlsByNodeId]\n );\n\n const focusName = useMemo(() => {\n const node = neighbourhood ? allNodes.find((n) => n.id === neighbourhood.focus) : undefined;\n return node ? (node.data as TableNodeData).collectionName : \"\";\n }, [allNodes, neighbourhood]);\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: allNodes.filter((n) => (n.data as TableNodeData | undefined)?.rlsEnabled).length\n }),\n [\n tableCount,\n relationCount,\n junctionNodes.length,\n allNodes\n ]\n );\n\n return (\n <ResizablePanels\n dividerAriaLabel={t(\"resize_sidebar\")}\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 ? \"#272a2f\" : \"#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 {/* A large schema: which part is drawn, and how far out */}\n {neighbourhood && (\n <div className=\"absolute top-4 left-4 right-4 flex justify-start pointer-events-none\">\n <div className=\"flex flex-wrap items-center gap-2 max-w-full px-3 py-2 bg-surface-card/90 backdrop-blur-sm rounded-lg border border-hairline shadow-sm pointer-events-auto\">\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark\"\n >\n {t(\"studio_schema_neighbourhood\", {\n shown: neighbourhood.shown,\n total: neighbourhood.total,\n table: focusName\n })}\n </Typography>\n <ToggleButtonGroup\n value={String(neighbourhood.depth)}\n onValueChange={handleDepthChange}\n options={[\n {\n value: \"1\",\n label: t(\"studio_schema_neighbourhood_depth_1\")\n },\n {\n value: \"2\",\n label: t(\"studio_schema_neighbourhood_depth_2\")\n }\n ]}\n />\n </div>\n </div>\n )}\n\n {/* The layout runs in a worker; say so while it does */}\n {layingOut && (\n <div\n role=\"status\"\n className=\"absolute inset-0 flex items-center justify-center pointer-events-none\"\n >\n <div className=\"flex items-center gap-2 px-3 py-2 bg-surface-card/90 backdrop-blur-sm rounded-lg border border-hairline shadow-sm\">\n <CircularProgress size=\"smallest\"/>\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark\"\n >\n {t(\"studio_schema_laying_out\")}\n </Typography>\n </div>\n </div>\n )}\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, #9da2aa, #9da2aa 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":";;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,SAAS,OAAqC;CAC1D,MAAM,IAAI,IAAI,MAAM,SAAS,MAAM;CACnC,EAAE,SAAS;EACP,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;CACb,CAAC;CACD,EAAE,2BAA2B,CAAC,EAAE;CAEhC,KAAK,MAAM,QAAQ,MAAM,OACrB,EAAE,QAAQ,KAAK,IAAI;EACf,OAAO,KAAK;EACZ,QAAQ,KAAK;CACjB,CAAC;CAEL,KAAK,MAAM,QAAQ,MAAM,OACrB,EAAE,QAAQ,KAAK,QAAQ,KAAK,MAAM;CAGtC,MAAM,OAAO,CAAC;CAEd,MAAM,YAA6B,CAAC;CACpC,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC5B,MAAM,SAAS,EAAE,KAAK,KAAK,EAAE;EAC7B,UAAU,KAAK,MAAM;GACjB,GAAG,OAAO,IAAI,KAAK,QAAQ;GAC3B,GAAG,OAAO,IAAI,KAAK,SAAS;EAChC;CACJ;CACA,OAAO;AACX;;;;;;;;;AChDA,IAAa,2BACT,OAAO,WAAW,eAClB,OAAO,SAAS,eAChB,OAAO,QAAQ,eACf,OAAO,IAAI,oBAAoB;;;;;;;;;;;;;;;AAgBnC,eAAsB,gBAAgB,OAA8C;CAChF,MAAM,EAAE,SAAS,iBAAiB,MAAM,OAAO;CAC/C,IAAI;CACJ,IAAI;EACA,SAAS,IAAI,aAAa,EAAE,MAAM,uBAAuB,CAAC;CAC9D,QAAQ;EACJ,OAAO,SAAS,KAAK;CACzB;CACA,OAAO,IAAI,SAA0B,SAAS,WAAW;EACrD,OAAO,iBAAiB,YAAY,UAA2C;GAC3E,OAAO,UAAU;GACjB,MAAM,QAAQ,MAAM;GACpB,IAAI,eAAe,OAAO,QAAQ,MAAM,SAAS;QAC5C,OAAO,IAAI,MAAM,MAAM,KAAK,CAAC;EACtC,CAAC;EAGD,OAAO,iBAAiB,UAAU,UAAU;GACxC,MAAM,eAAe;GACrB,OAAO,UAAU;GACjB,IAAI;IACA,QAAQ,SAAS,KAAK,CAAC;GAC3B,SAAS,OAAO;IACZ,OAAO,KAAK;GAChB;EACJ,CAAC;EACD,OAAO,YAAY,KAAK;CAC5B,CAAC;AACL;;ACnDA,IAAM,uBAAuB;;AAE7B,IAAM,uBAAuB;AAC7B,IAAM,aAAa;;;;AAKnB,IAAa,mBAAmB,SAIlB;CACV,IAAI,KAAK,YAAY,OAAO;CAC5B,OAAO,KAAK,mBAAmB,KAAK,YAC9B,uBACA;AACV;;;;AAKA,IAAM,sBAAsB,aAAqB,eAAuB,yBACpE,eAAe,KAAK,IAAI,aAAa,CAAC,IAAI,aAAa;;;;;AAM3D,IAAa,iBAAiB,UAAkB,eAAuB,yBACnE,eAAe,WAAW,aAAa,aAAa;;;;;;AAexD,IAAa,cAAc,OAAe,UAA8B;CACpE,MAAM,QAAqB;EACvB,OAAO,MAAM,KAAK,SAAS;GACvB,MAAM,OAAO,KAAK;GAClB,MAAM,UAAU,gBAAgB;IAC5B,YAAY,QAAQ,KAAK,UAAU;IACnC,gBAAgB,KAAK,kBAAkB;IACvC,WAAW,KAAK,aAAa;GACjC,CAAC;GACD,OAAO;IACH,IAAI,KAAK;IACT,OAAA;IACA,QAAQ,mBAAmB,KAAK,SAAS,UAAU,GAAG,OAAO;GACjE;EACJ,CAAC;EACD,OAAO,MAAM,KAAK,UAAU;GACxB,QAAQ,KAAK;GACb,QAAQ,KAAK;EACjB,EAAE;CACN;CAEA,OAAO;EACH;EACA,WAAA,GAHiB,MAAM,MAAM,KAAI,SAAQ,GAAG,KAAK,GAAG,GAAG,KAAK,QAAQ,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,MAAM,KAAI,SAAQ,GAAG,KAAK,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,KAAK,GAAG;CAIxJ;AACJ;;;;;;;;;;;AAYA,IAAM,8BAAc,IAAI,IAA6B;AACrD,IAAM,oBAAoB;AAE1B,IAAM,kBAAkB,WAAmB,cAAgD;CACvF,YAAY,IAAI,WAAW,SAAS;CACpC,IAAI,YAAY,OAAO,mBAAmB;EACtC,MAAM,SAAS,YAAY,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;EACzC,IAAI,WAAW,KAAA,GAAW,YAAY,OAAO,MAAM;CACvD;CACA,OAAO;AACX;;AAGA,IAAa,gBAAgB,SACzB,YAAY,IAAI,KAAK,SAAS;;AAGlC,IAAa,aAAa,SACtB,aAAa,IAAI,KAAK,eAAe,KAAK,WAAW,SAAS,KAAK,KAAK,CAAC;;AAG7E,IAAM,kCAAkB,IAAI,IAAsC;;;;;AAMlE,IAAa,sBAAsB,SAA+C;CAC9E,MAAM,SAAS,aAAa,IAAI;CAChC,IAAI,QAAQ,OAAO,QAAQ,QAAQ,MAAM;CACzC,MAAM,UAAU,gBAAgB,IAAI,KAAK,SAAS;CAClD,IAAI,SAAS,OAAO;CACpB,MAAM,MAAM,gBAAgB,KAAK,KAAK,CAAC,CAClC,MAAM,cAAc,eAAe,KAAK,WAAW,SAAS,CAAC,CAAC,CAC9D,cAAc,gBAAgB,OAAO,KAAK,SAAS,CAAC;CACzD,gBAAgB,IAAI,KAAK,WAAW,GAAG;CACvC,OAAO;AACX;;AAGA,IAAa,cAAc,OAAe,cACtC,MAAM,KAAK,UAAU;CACjB,GAAG;CACH,UAAU,EAAE,GAAI,UAAU,KAAK,OAAO;EAClC,GAAG;EACH,GAAG;CACP,EAAG;AACP,EAAE;AAaN,IAAa,uBACT,aACA,cACS;CACT,IAAI,gBAAgB,QAAQ,OAAO;CACnC,IAAI,cAAc,WAAW,OAAO;CACpC,OAAO;AACX;;;ACnGA,IAAM,kBAAkB,eAA8C;CAClE,MAAM,UAAwB,CAAC;CAC/B,MAAM,aAAa,WAAW,cAAc,CAAC;CAE7C,KAAK,MAAM,CAAC,UAAU,SAAS,OAAO,QAAQ,UAAU,GAAG;EACvD,IAAI,KAAK,SAAS,YAAY;EAE9B,MAAM,OACD,UAAU,QAAQ,QAAQ,KAAK,IAAI,KACnC,CAAC,OAAO,OAAO,UAAU,CAAC,CAAC,MACvB,MAAM,UAAU,KAAK,QAAQ,EAAE,IAAI,CACxC,KACI,aAAa;EAErB,MAAM,SAAS,KAAK,SAAS,YAAY,UAAU,QAAQ,QAAQ,KAAK,IAAI;EAE5E,IAAI;EACJ,IAAI,UAAU,UAAU,QAAQ,KAAK,MAAM;GACvC,MAAM,WAAW,KAAK;GACtB,IAAI,MAAM,QAAQ,QAAQ,GACtB,aAAa,SAAS,KAAK,MACvB,OAAO,MAAM,WAAW,IAAI,OAAQ,GAA+B,MAAM,CAAC,CAC9E;QACG,IAAI,OAAO,aAAa,YAAY,aAAa,MACpD,aAAa,OAAO,KAAK,QAAQ;EAEzC;EAEA,QAAQ,KAAK;GACT,MAAM;GACN,MAAM,KAAK;GACX,WAAW,KAAK;GAChB,cAAc;GACd,cAAc;GACd,YAAY,QAAQ,KAAK,YAAY,QAAQ;GAC7C;GACA;EACJ,CAAC;CACL;CAGA,IAAI,2BAA2B,UAAU,GACrC,IAAI;EACA,MAAM,oBAAoB,2BAA2B,UAAU;EAC/D,KAAK,MAAM,OAAO,OAAO,OAAO,iBAAiB,GAC7C,IAAI,IAAI,SAAS,eAAe,IAAI,UAEhC,IAAI,CAAC,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI,QAAQ,GAC5C,QAAQ,KAAK;GACT,MAAM,IAAI;GACV,MAAM;GACN,WAAW;GACX,cAAc;GACd,cAAc;GACd,YAAY;GACZ,QAAQ;GACR,cAAc,IAAI;EACtB,CAAC;OACE;GAEH,MAAM,WAAW,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI,QAAQ;GAC5D,IAAI,UAAU;IACV,SAAS,eAAe;IACxB,SAAS,eAAe,IAAI;GAChC;EACJ;CAGZ,QAAQ,CAER;CAGJ,OAAO;AACX;AAcA,IAAM,cACF,aACA,YACc;;;;;;;;;CASd,MAAM,gBAAgB,QAAgB,WAAmB,kBACrD,UAAU,QAAQ,IAAI,GAAG,OAAO,GAAG,WAAW,IAAI;CAEtD,MAAM,QAAgB,CAAC;CACvB,MAAM,QAAgB,CAAC;CACvB,MAAM,gCAAgB,IAAI,IAAoB;CAE9C,MAAM,8BAAc,IAAI,IAA0B;CAGlD,MAAM,eAAe,WAA2B;EAE5C,MAAM,KADO,YAAY,IAAI,MAClB,CAAA,EAAM,MAAM,MAAM,EAAE,YAAY;EAC3C,OAAO,KAAK,UAAU,GAAG,SAAS;CACtC;CAGA,MAAM,eAAe,QAAgB,aAAyC;EAC1E,IAAI,CAAC,UAAU,OAAO;EAEtB,MAAM,KADO,YAAY,IAAI,MAClB,CAAA,EAAM,MAAM,MAAM,EAAE,SAAS,QAAQ;EAChD,OAAO,KAAK,UAAU,GAAG,SAAS;CACtC;CAQA,KAAK,MAAM,cAAc,aAAa;EAClC,IAAI,CAAC,2BAA2B,UAAU,GAAG;EAE7C,MAAM,YAAY,aAAa,UAAU;EACzC,MAAM,SAAS,SAAS;EACxB,cAAc,IAAI,WAAW,MAAM;EAEnC,MAAM,UAAU,eAAe,UAAU;EACzC,YAAY,IAAI,QAAQ,OAAO;EAE/B,MAAM,WAA0B;GAC5B;GACA,gBAAgB,WAAW;GAC3B,MAAM,WAAW;GACjB;GACA,YAAY;GACZ,aAAa;GACb,YAAY,aACR,WAAW,UAAU,UACrB,WACA,QAAQ,WAAW,iBAAiB,WAAW,cAAc,SAAS,CAAC,CAC3E;GACA,gBAAgB,QAAQ,WAAW,OAAO;GAC1C,MAAM,OAAO,WAAW,SAAS,WAAW,WAAW,OAAO,KAAA;EAClE;EAEA,MAAM,KAAK;GACP,IAAI;GACJ,MAAM;GACN,UAAU;IAAE,GAAG;IAC3B,GAAG;GAAE;GACO,MAAM;EACV,CAAC;CACL;CAGA,MAAM,qCAAqB,IAAI,IAAY;CAE3C,KAAK,MAAM,cAAc,aAAa;EAClC,IAAI,CAAC,2BAA2B,UAAU,GAAG;EAE7C,MAAM,YAAY,aAAa,UAAU;EACzC,MAAM,eAAe,cAAc,IAAI,SAAS;EAChD,IAAI,CAAC,cAAc;EAEnB,IAAI;EACJ,IAAI;GACA,oBAAoB,2BAA2B,UAAU;EAC7D,QAAQ;GACJ;EACJ;EAEA,KAAK,MAAM,CAAC,aAAa,QAAQ,OAAO,QAAQ,iBAAiB,GAAG;GAChE,IAAI;GACJ,IAAI;IACA,mBAAmB,IAAI,OAAO;GAClC,QAAQ;IACJ;GACJ;GAEA,IAAI,CAAC,2BAA2B,gBAAgB,GAAG;GAEnD,MAAM,cAAc,aAAa,gBAAgB;GACjD,MAAM,eAAe,cAAc,IAAI,WAAW;GAClD,IAAI,CAAC,cAAc;GAMnB,IAAI,IAAI,SAAS,eAAe,IAAI,SAAS,cAAc;GAE3D,MAAM,YAAY;GAClB,MAAM,WAA6B;IAC/B,aAAa,IAAI;IACjB;IACA,cAAc,IAAI,gBAAgB;IAClC,aAAa,IAAI,SAAS;IAC1B,aAAa;IACb,OAAO,oBAAoB,IAAI,aAAa,SAAS;GACzD;GAEA,IAAI,IAAI,SAAS,gBAAgB,CAAC,mBAAmB,IAAI,IAAI,QAAQ,KAAK,GAAG;IAEzE,mBAAmB,IAAI,IAAI,QAAQ,KAAK;IACxC,MAAM,iBAAiB,YAAY,IAAI,QAAQ;IAE/C,MAAM,kBAAgC,CAClC;KACI,MAAM,IAAI,QAAQ;KAClB,MAAM;KACN,WAAW;KACX,cAAc;KACd,cAAc;KACd,YAAY;KACZ,QAAQ;IACZ,GACA;KACI,MAAM,IAAI,QAAQ;KAClB,MAAM;KACN,WAAW;KACX,cAAc;KACd,cAAc;KACd,YAAY;KACZ,QAAQ;IACZ,CACJ;IACA,YAAY,IAAI,gBAAgB,eAAe;IAE/C,MAAM,KAAK;KACP,IAAI;KACJ,MAAM;KACN,UAAU;MAAE,GAAG;MACnC,GAAG;KAAE;KACe,MAAM;MACF,WAAW,IAAI,QAAQ;MACvB,gBAAgB,IAAI,QAAQ;MAC5B,MAAM,IAAI,QAAQ;MAClB,SAAS;MACT,YAAY;MACZ,aAAa;MAIb,YAAY,aAAa,UAAU,IAAI,QAAQ,OAAO,KAAK;MAC3D,gBAAgB;KACpB;IACJ,CAAC;IAGD,MAAM,WAAW,YAAY,IAAI,YAAY,CAAC,EAAE,MAAM,MAAM,EAAE,YAAY;IAC1E,MAAM,KAAK;KACP,IAAI,QAAQ,aAAa,GAAG;KAC5B,QAAQ;KACR,QAAQ;KACR,cAAc,WAAW,UAAU,SAAS,SAAS;KACrD,cAAc,UAAU,IAAI,QAAQ;KACpC,MAAM;KACN,MAAM;MAAE,GAAG;MAC/B,OAAO;KAAM;KACO,WAAW;MAAE,MAAM,WAAW;MAClD,OAAO;MACP,QAAQ;KAAG;IACK,CAAC;IAGD,MAAM,KAAK;KACP,IAAI,QAAQ,eAAe,GAAG;KAC9B,QAAQ;KACR,QAAQ;KACR,cAAc,UAAU,IAAI,QAAQ;KACpC,cAAc,YAAY,YAAY;KACtC,MAAM;KACN,MAAM;MAAE,GAAG;MAC/B,OAAO;KAAM;KACO,WAAW;MAAE,MAAM,WAAW;MAClD,OAAO;MACP,QAAQ;KAAG;IACK,CAAC;GACL,OAAO,IAAI,IAAI,SAAS,aAEpB,MAAM,KAAK;IACP,IAAI,QAAQ,aAAa,GAAG,aAAa,GAAG;IAC5C,QAAQ;IACR,QAAQ;IACR,cAAc,YAAY,cAAc,IAAI,QAAQ;IACpD,cAAc,YAAY,YAAY;IACtC,MAAM;IACN,MAAM,EAAE,GAAG,SAAS;IACpB,WAAW;KAAE,MAAM,WAAW;KAClD,OAAO;KACP,QAAQ;IAAG;GACK,CAAC;EAET;CACJ;CACA,OAAO;EACH;EACA;CACJ;AACJ;;;;;;AAOA,IAAM,cAAc,OAAoB,cAA4C;CAqBhF,OAAO;EACH,OArBU,WAAW,MAAM,OAAO,SAqBlC;EACA,OArBU,MAAM,MAAM,KAAK,SAAS;GACpC,MAAM,SAAS,UAAU,KAAK;GAC9B,MAAM,SAAS,UAAU,KAAK;GAC9B,MAAM,gBAAgB,KAAK,gBAAgB;GAC3C,MAAM,gBAAgB,KAAK,gBAAgB;GAC3C,IAAI,CAAC,UAAU,CAAC,QACZ,OAAO;IACH,GAAG;IACH,cAAc,GAAG,cAAc;IAC/B,cAAc,GAAG,cAAc;GACnC;GAEJ,MAAM,eAAe,OAAO,IAAA,MAAiB,KAAK,OAAO,IAAA,MAAiB;GAC1E,OAAO;IACH,GAAG;IACH,cAAc,GAAG,cAAc,GAAG,eAAe,UAAU;IAC3D,cAAc,GAAG,cAAc,GAAG,eAAe,SAAS;GAC9D;EACJ,CAGI;CACJ;AACJ;AAeA,IAAM,kBAAkB,SAAwB,QAAS,KAAK,KAAuB,UAAU;;;;;;;;;AAU/F,IAAM,mBAAmB,OAAoB,OAAe,UAA2C;CACnG,MAAM,YAAY,IAAI,IAAI,MAAM,MAAM,OAAO,cAAc,CAAC,CAAC,KAAK,SAAS,KAAK,EAAE,CAAC;CACnF,MAAM,2BAAW,IAAI,IAAsB;CAC3C,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC5B,SAAS,IAAI,KAAK,QAAQ,CAAC,GAAI,SAAS,IAAI,KAAK,MAAM,KAAK,CAAC,GAAI,KAAK,MAAM,CAAC;EAC7E,SAAS,IAAI,KAAK,QAAQ,CAAC,GAAI,SAAS,IAAI,KAAK,MAAM,KAAK,CAAC,GAAI,KAAK,MAAM,CAAC;CACjF;CACA,MAAM,QAAQ,QAAQ;CACtB,MAAM,2BAAW,IAAI,IAAoB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CACrD,MAAM,aAAyB,MAAM,KAAK,EAAE,QAAQ,QAAQ,EAAE,SAAS,CAAC,CAAC;CACzE,WAAW,EAAE,CAAC,KAAK,KAAK;CACxB,KAAK,IAAI,UAAU,GAAG,WAAW,OAAO,WACpC,KAAK,MAAM,MAAM,WAAW,UAAU;EAClC,IAAI,SAAS,IAAI,EAAE,MAAM,SAAS;EAClC,KAAK,MAAM,QAAQ,SAAS,IAAI,EAAE,KAAK,CAAC,GAAG;GACvC,MAAM,OAAO,UAAU,IAAI,EAAE,KAAK,UAAU,IAAI,IAAI,IAAI,IAAI;GAC5D,MAAM,eAAe,UAAU;GAC/B,IAAI,eAAe,UAAU,SAAS,IAAI,IAAI,KAAK,aAAa,cAAc;GAC9E,SAAS,IAAI,MAAM,YAAY;GAC/B,WAAW,aAAa,CAAC,KAAK,IAAI;EACtC;CACJ;CAEJ,OAAO;EACH,OAAO,MAAM,MAAM,QAAQ,SAAS,SAAS,IAAI,KAAK,EAAE,CAAC;EACzD,OAAO,MAAM,MAAM,QAAQ,SAAS,SAAS,IAAI,KAAK,MAAM,KAAK,SAAS,IAAI,KAAK,MAAM,CAAC;CAC9F;AACJ;;AAGA,IAAM,sBAAsB,UAA2C;CACnE,MAAM,yBAAS,IAAI,IAAoB;CACvC,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC5B,OAAO,IAAI,KAAK,SAAS,OAAO,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC;EAC1D,OAAO,IAAI,KAAK,SAAS,OAAO,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC;CAC9D;CACA,IAAI;CACJ,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC5B,IAAI,eAAe,IAAI,GAAG;EAC1B,IAAI,CAAC,SAAS,OAAO,IAAI,KAAK,EAAE,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,KAAK,IAAI,OAAO;CACjF;CACA,OAAO,MAAM;AACjB;AA4BA,IAAM,cAA2B;CAC7B,OAAO,CAAC;CACR,OAAO,CAAC;AACZ;AACA,IAAM,eAAgC,CAAC;AAEvC,IAAa,kBACT,aAKA,UAA8B,MAO9B,QAAuB,MACvB,QAA4B,MACL;CACvB,MAAM,CAAC,SAAS,cAAc,SAAS,CAAC;CAExC,MAAM,WAAW,kBAAkB,YAAY,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;CAE/D,MAAM,QAAQ,cACH,eAAe,YAAY,SAAS,IAAI,WAAW,aAAa,OAAO,IAAI,aAClF,CAAC,aAAa,OAAO,CACzB;CAEA,MAAM,SAAS,MAAM,MAAM,QAAQ,SAAS,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC;CAEnE,MAAM,UADQ,SAAA,OAEP,SAAS,MAAM,MAAM,MAAM,SAAS,KAAK,OAAO,KAAK,IAAI,QAAQ,mBAAmB,KAAK,MAAM,OAChG;CAEN,MAAM,QAAQ,cACH,UAAU,gBAAgB,OAAO,SAAS,KAAK,IAAI,OAC1D;EAAC;EAAO;EAAS;CAAK,CAC1B;CACA,MAAM,OAAO,cAAc,WAAW,MAAM,OAAO,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC;;;;;CAMxE,MAAM,YAAY,mBAAmB;CACrC,MAAM,CAAC,SAAS,cAAc,SAAmE,IAAI;CACrG,MAAM,CAAC,aAAa,kBAAkB,SAAkB,IAAI;CAE5D,MAAM,YAAY,MAAM,MAAM,WAAW,IACnC,eACA,aAAa,IAAI,MACX,SAAS,cAAc,KAAK,YAAY,QAAQ,YAAY,KAAA,OAC5D,YAAY,KAAA,IAAY,UAAU,IAAI;CAElD,gBAAgB;EACZ,IAAI,CAAC,aAAa,MAAM,MAAM,WAAW,KAAK,aAAa,IAAI,GAAG;EAClE,IAAI,UAAU;EACd,mBAAmB,IAAI,CAAC,CAAC,MACpB,WAAW;GACR,IAAI,SACA,WAAW;IACP,WAAW,KAAK;IAChB,WAAW;GACf,CAAC;EAET,IACC,UAAmB;GAChB,IAAI,SAAS,eAAe,KAAK;EACrC,CACJ;EACA,aAAa;GACT,UAAU;EACd;CACJ,GAAG;EAAC;EAAM;EAAW;CAAK,CAAC;CAE3B,MAAM,SAAS,cACJ,YAAY,WAAW,OAAO,SAAS,IAAI,aAIlD;EAAC;EAAO;EAAW;CAAO,CAC9B;CAIA,IAAI,aAAa,MAAM;CAEvB,OAAO;EACH,OAAO,OAAO;EACd,OAAO,OAAO;EACd,UAAU,MAAM;EAChB;EACA,YAAY,MAAM,MAAM;EACxB,eAAe,MAAM,MAAM;EAC3B,WAAW,cAAc,KAAA;EACzB,eAAe,UACT;GACE,OAAO;GACP;GACA,OAAO,MAAM,MAAM;GACnB,OAAO;EACX,IACE;CACV;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxjBA,IAAM,iBAAiB;;;;;;AAOvB,SAAS,KAAQ,KAA8B,MAA6B;CACxE,OAAQ,IAAI,SAAS,IAAI,KAAK,YAAY;AAC9C;AAEA,SAAS,YAAY,QAA4C;CAC7D,IAAI,UAAU,OAAO,WAAW,YAAY,UAAU,QAAQ;EAC1D,MAAM,OAAQ,OAA6B;EAC3C,IAAI,MAAM,QAAQ,IAAI,GAAG,OAAO;CACpC;CACA,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO;CAClC,OAAO,CAAC;AACZ;AAEA,SAAgB,mBAAkC;CAC9C,MAAM,EAAE,kBAAkB,iBAAiB;CAC3C,MAAM,aAAa,eAAe;CAClC,MAAM,CAAC,QAAQ,aAAa,SAAwB,IAAI;CAExD,gBAAgB;EACZ,IAAI,CAAC,YAAY;GACb,UAAU,IAAI;GACd;EACJ;EACA,IAAI,YAAY;EAChB,WAAW,cAAc,CAAC,CACrB,MAAM,WAAoB;GACvB,IAAI,WAAW;GACf,MAAM,0BAAU,IAAI,IAAY;GAChC,KAAK,MAAM,OAAO,YAAY,MAAM,GAAG;IACnC,IAAI,CAAC,KAAc,KAAK,aAAa,GAAG;IACxC,MAAM,SAAS,KAAa,KAAK,YAAY,KAAK;IAClD,MAAM,QAAQ,KAAa,KAAK,WAAW;IAC3C,IAAI,OAAO,QAAQ,IAAI,GAAG,OAAO,GAAG,OAAO;GAC/C;GACA,UAAU,OAAO;EACrB,CAAC,CAAC,CACD,YAAY;GAGT,IAAI,CAAC,WAAW,UAAU,IAAI;EAClC,CAAC;EACL,aAAa;GAAE,YAAY;EAAM;CACrC,GAAG,CAAC,UAAU,CAAC;CAEf,OAAO;AACX;;;;;;;;;ACvEA,IAAM,kBAAkB,EAAE,MAAM,eAA0B;CACtD,MAAM,EACF,WACA,gBACA,SACA,YACA,YACA,gBACA,SACA;CAGJ,MAAM,UAAU,cAAc;EAC1B,MAAM,SAAuF,CAAC;EAC9F,MAAM,OAAO;EACb,MAAM,UAAU,gBAAgB;GAC5B,YAAY,QAAQ,UAAU;GACd;GACL;EACf,CAAC;EACD,MAAM,OAAO,KAAK,SAAS,IAAI,cAAc,KAAK,MAAM,KAAK,SAAS,CAAC,GAAG,OAAO,IAAI;EAErF,KAAK,SAAS,KAAK,QAAQ;GACvB,MAAM,IAAI,cAAc,KAAK,OAAO;GAEpC,IAAI,IAAI,gBAAgB,CAAC,IAAI,cAAc;IAEvC,OAAO,KAAK;KAAE,IAAI,UAAU,IAAI,KAAK;KACrD,MAAM;KACN,UAAU,SAAS;KACnB,KAAK;IAAE,CAAC;IACQ,OAAO,KAAK;KAAE,IAAI,UAAU,IAAI,KAAK;KACrD,MAAM;KACN,UAAU,SAAS;KACnB,KAAK;IAAE,CAAC;GACI;GACA,IAAI,IAAI,cAAc;IAElB,OAAO,KAAK;KAAE,IAAI,UAAU,IAAI,KAAK;KACrD,MAAM;KACN,UAAU,SAAS;KACnB,KAAK;IAAE,CAAC;IACQ,OAAO,KAAK;KAAE,IAAI,UAAU,IAAI,KAAK;KACrD,MAAM;KACN,UAAU,SAAS;KACnB,KAAK;IAAE,CAAC;IACQ,OAAO,KAAK;KAAE,IAAI,UAAU,IAAI,KAAK;KACrD,MAAM;KACN,UAAU,SAAS;KACnB,KAAK;IAAE,CAAC;IACQ,OAAO,KAAK;KAAE,IAAI,UAAU,IAAI,KAAK;KACrD,MAAM;KACN,UAAU,SAAS;KACnB,KAAK;IAAE,CAAC;GACI;EACJ,CAAC;EAGD,OAAO,KAAK;GAAE,IAAI;GAC1B,MAAM;GACN,UAAU,SAAS;GACnB,KAAK;EAAK,CAAC;EACH,OAAO,KAAK;GAAE,IAAI;GAC1B,MAAM;GACN,UAAU,SAAS;GACnB,KAAK;EAAK,CAAC;EACH,OAAO,KAAK;GAAE,IAAI;GAC1B,MAAM;GACN,UAAU,SAAS;GACnB,KAAK;EAAK,CAAC;EACH,OAAO,KAAK;GAAE,IAAI;GAC1B,MAAM;GACN,UAAU,SAAS;GACnB,KAAK;EAAK,CAAC;EAEH,OAAO;CACX,GAAG;EAAC;EAAS;EAAY;EAAgB;CAAS,CAAC;CAEnD,OACI,qBAAC,OAAD;EACI,WAAW,IACP,gHACA,WACM,oDACA,gEACN,cAAc,eAClB;YAPJ;GAUI,qBAAC,OAAD;IACI,WAAW,IACP,2DACA,aACM,qCACA,iCACV;cANJ;KAQK,QAAQ,CAAC,cACN,oBAAC,OAAD;MAAK,WAAU;gBACX,oBAAC,aAAD;OACI,kBAAkB;QAAE,MAAM;QACtD,MAAM;QACN;OAAK;OACuB,MAAK;MACR,CAAA;KACA,CAAA;KAER,cACG,oBAAC,OAAD;MACI,WAAU;MACV,MAAK;MACL,QAAO;MACP,SAAQ;gBAER,oBAAC,QAAD;OACI,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;MACL,CAAA;KACA,CAAA;KAET,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACI,oBAAC,YAAD;OACI,SAAQ;OACR,WAAW,IACP,sCACA,aACM,sDACA,+CACV;iBAEC;MACO,CAAA,GACX,mBAAmB,aAAa,CAAC,cAC9B,oBAAC,YAAD;OACI,SAAQ;OACR,WAAU;iBAET;MACO,CAAA,CAEf;;KAGL,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACK,cACG,oBAAC,SAAD;OAAS,OAAM;iBACX,oBAAC,OAAD,EAAK,WAAU,wCAAwC,CAAA;MAClD,CAAA,GAEZ,kBACG,oBAAC,SAAD;OAAS,OAAM;iBACX,oBAAC,OAAD,EAAK,WAAU,uCAAuC,CAAA;MACjD,CAAA,CAEZ;;IACJ;;GAGL,oBAAC,OAAD;IAAK,WAAU;cACT,QAAyB,KAAK,QAC5B,qBAAC,OAAD;KAEI,WAAW,IACP,iEACA,IAAI,gBAAgB,uCACpB,IAAI,gBACA,CAAC,IAAI,gBACL,mCACR;eARJ;MAUI,qBAAC,QAAD;OAAM,WAAU;iBAAhB,CACK,IAAI,gBACD,oBAAC,SAAD;QAAS,OAAM;kBACX,oBAAC,QAAD;SAAM,WAAU;mBAA2C;QAAQ,CAAA;OAC9D,CAAA,GAEZ,IAAI,gBAAgB,CAAC,IAAI,gBACtB,oBAAC,SAAD;QAAS,OAAO,QAAQ,IAAI,gBAAgB;kBACxC,oBAAC,QAAD;SAAM,WAAU;mBAA0C;QAAQ,CAAA;OAC7D,CAAA,CAEX;;MAEN,oBAAC,YAAD;OACI,SAAQ;OACR,WAAW,IACP,iDACA,IAAI,eACE,qDACA,IAAI,eACF,qCACA,+CACZ;iBAEC,IAAI;MACG,CAAA;MAEX,IAAI,SACD,oBAAC,MAAD;OACI,MAAK;OACL,WAAU;iBACb;MAEK,CAAA,IAEN,oBAAC,YAAD;OACI,SAAQ;OACR,WAAU;iBAET,IAAI;MACG,CAAA;MAGf,IAAI,cAAc,CAAC,IAAI,gBACpB,oBAAC,SAAD;OAAS,OAAM;iBACX,oBAAC,QAAD;QAAM,WAAU;kBAA0B;OAAO,CAAA;MAC5C,CAAA;KAEZ;OAzDI,IAAI,IAyDR,CACR;GACA,CAAA;GAGJ,QAAQ,KAAK,MACV,oBAAC,QAAD;IAEI,IAAI,EAAE;IACN,MAAM,EAAE;IACR,UAAU,EAAE;IACZ,OAAO,EAAE,KAAK,EAAE,IAAI;IACpB,WAAW,IACP,4CACA,EAAE,SAAS,WAAW,iBAAiB,eAC3C;GACH,GATQ,EAAE,EASV,CACJ;EACA;;AAEb;AAEA,IAAa,YAAY,KAAK,cAAc;;;;;;;;;;;;ACjP5C,IAAM,qBAAqB,EACvB,IACA,SACA,SACA,SACA,SACA,gBACA,gBACA,MACA,eACa;CACb,MAAM,WAAW;CAEjB,MAAM,CAAC,UAAU,QAAQ,UAAU,kBAAkB;EACjD;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;CAClB,CAAC;CAED,MAAM,YAAY,UAAU,cAAc;CAC1C,MAAM,aAAa,UAAU;CAC7B,MAAM,aAAa,UAAU;CAG7B,IAAI,cAAc;CAClB,IAAI;CACJ,IAAI,cAAc;CAElB,IAAI,UAAU;EACV,cAAc;EACd,cAAc;CAClB,OAAO,IAAI,YACP,cAAc;MACX,IAAI,WAAW;EAClB,kBAAkB;EAClB,cAAc;CAClB,OAAO,IAAI,YAAY;EACnB,kBAAkB;EAClB,cAAc;CAClB,OACI,cAAc;CAGlB,OACI,qBAAA,UAAA,EAAA,UAAA,CACI,oBAAC,UAAD;EACQ;EACJ,MAAM;EACN,OAAO;GACH,QAAQ;GACR;GACA;EACJ;CACH,CAAA,GACA,UAAU,SACP,oBAAC,mBAAD,EAAA,UACI,oBAAC,OAAD;EACI,OAAO;GACH,UAAU;GACV,WAAW,mCAAmC,OAAO,KAAK,OAAO;GACjE,eAAe;EACnB;EACA,WAAW,IACP,yEACA,0BACA,WACM,gCACA,aACE,kFACA,aAAa,aACX,2EACA,uDACd;YAEC,SAAS;CACT,CAAA,EACU,CAAA,CAEzB,EAAA,CAAA;AAEV;AAEA,IAAa,eAAe,KAAK,iBAAiB;;;ACxDlD,IAAM,YAAY,EACd,WAAW,UACf;AAEA,IAAM,YAAY,EACd,cAAc,aAClB;AAIA,SAAS,uBAAuB,EAC5B,eAGD;CACC,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,oBAAoB,aAAa;CACvC,MAAM,UAAU,iBAAiB;CAKjC,MAAM,SAAS,cAAc;CAG7B,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,CAAC,OAAO,YAAY,SAA6B,CAAC;CACxD,MAAM,EACF,OAAO,eACP,OAAO,eACP,UACA,UACA,YACA,eACA,WACA,kBACA,eAAe,aAAa,SAAS,OAAO,KAAK;CAErD,MAAM,CAAC,OAAO,YAAY,SAAiB,CAAC,CAAC;CAC7C,MAAM,CAAC,OAAO,YAAY,SAAiB,CAAC,CAAC;CAC7C,MAAM,CAAC,eAAe,oBAAoB,SAAwB,IAAI;CACtE,MAAM,CAAC,aAAa,kBAAkB,SAAS,EAAE;CAIjD,gBAAgB;EACZ,IAAI,WAAW;EACf,SAAS,aAAa;EACtB,SAAS,aAAa;CAC1B,GAAG;EAAC;EAAe;EAAe;CAAS,CAAC;CAE5C,MAAM,gBAAgB,aACjB,YAA0B,UAAU,QAAQ,iBAAiB,SAAS,GAAG,CAAC,GAC3E,CAAC,CACL;CACA,MAAM,gBAAgB,aACjB,YAA0B,UAAU,QAAQ,iBAAiB,SAAS,GAAG,CAAC,GAC3E,CAAC,CACL;;;;;;;;;;;;CAaA,MAAM,aAAa,OAAO,IAAI;CAC9B,gBAAgB;EACZ,IAAI,CAAC,WAAW,WAAW,MAAM,WAAW,GAAG;EAC/C,WAAW,UAAU;EACrB,kBAAuB,QAAQ;GAC3B,SAAS;GACT,UAAU;EACd,CAAC;CACL,GAAG,CAAC,OAAO,iBAAiB,CAAC;CAE7B,MAAM,gBAAgB,kBAAkB;EACpC,kBAAkB,QAAQ;GAAE,SAAS;GAC7C,UAAU;EAAI,CAAC;CACX,GAAG,CAAC,iBAAiB,CAAC;CAEtB,MAAM,kBAAkB,aAAa,GAAqB,SAAe;EACrE,iBAAiB,KAAK,EAAE;CAC5B,GAAG,CAAC,CAAC;CAEL,MAAM,kBAAkB,kBAAkB;EACtC,iBAAiB,IAAI;CACzB,GAAG,CAAC,CAAC;CAIL,MAAM,oBAAoB,aACrB,WAAmB;EAChB,iBAAiB,MAAM;EACvB,IAAI,eAAe;GACf,IAAI,cAAc,UAAU,QAAQ;IAChC,WAAW,UAAU;IACrB,SAAS,MAAM;GACnB;GACA;EACJ;EACA,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM;EAC9C,IAAI,MACA,kBAAkB,UACd,KAAK,SAAS,IAAI,KAClB,KAAK,SAAS,IAAI,IAClB;GAAE,MAAM;GAC5B,UAAU;EAAI,CACE;CAER,GACA;EAAC;EAAO;EAAmB;CAAa,CAC5C;CAEA,MAAM,oBAAoB,aAAa,UAAkB;EACrD,WAAW,UAAU;EACrB,SAAS,UAAU,MAAM,IAAI,CAAC;CAClC,GAAG,CAAC,CAAC;CAGL,MAAM,CAAC,aAAa,kBAAkB,eAAe;EACjD,IAAI;GACA,MAAM,QAAQ,aAAa,QAAQ,gCAAgC;GACnE,OAAO,UAAU,OAAO,WAAW,KAAK,IAAI;EAChD,QAAQ;GACJ,OAAO;EACX;CACJ,CAAC;CAED,gBAAgB;EACZ,IAAI;GACA,aAAa,QACT,kCACA,YAAY,SAAS,CACzB;EACJ,QAAQ,CAER;CACJ,GAAG,CAAC,WAAW,CAAC;CAGhB,MAAM,sBAAsB,cAClB,YAAY,OAAO,0BAA0B,GACnD,CAAC,WAAW,CAChB;CAEA,MAAM,sBAAsB,cAAc;EACtC,IAAI,CAAC,YAAY,KAAK,GAAG,OAAO;EAChC,MAAM,IAAI,YAAY,YAAY;EAClC,OAAO,oBAAoB,QACtB,MACG,EAAE,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,KAC/B,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,KACxC,EAAE,MAAM,YAAY,CAAC,CAAC,SAAS,CAAC,CACxC;CACJ,GAAG,CAAC,qBAAqB,WAAW,CAAC;CAGrC,MAAM,gBAAgB,cAEd,SAAS,QACJ,MAAO,EAAE,KAAuB,UACrC,GACJ,CAAC,QAAQ,CACb;;;;;;;;;CAUA,MAAM,cAAc,cACV,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,EAAE,IAAI,QAAS,EAAE,KAAuB,UAAU,CAAC,CAAC,CAAC,GACxF,CAAC,QAAQ,CACb;CACA,MAAM,gBAAgB,aACjB,eAAyC,YAAY,IAAI,SAAS,aAAa,UAAU,GAAG,KAAK,OAClG,CAAC,WAAW,CAChB;CAEA,MAAM,YAAY,cAAc;EAC5B,MAAM,OAAO,gBAAgB,SAAS,MAAM,MAAM,EAAE,OAAO,cAAc,KAAK,IAAI,KAAA;EAClF,OAAO,OAAQ,KAAK,KAAuB,iBAAiB;CAChE,GAAG,CAAC,UAAU,aAAa,CAAC;CAG5B,MAAM,QAAQ,eACH;EACH,QAAQ;EACR,WAAW;EACX,WAAW,cAAc;EAMzB,SAAS,SAAS,QAAQ,MAAO,EAAE,MAAoC,UAAU,CAAC,CAAC;CACvF,IACA;EACI;EACA;EACA,cAAc;EACd;CACJ,CACJ;CAEA,OACI,oBAAC,iBAAD;EACI,kBAAkB,EAAE,gBAAgB;EACpC,aAAY;EACZ,kBAAkB;EAClB,mBAAmB;EACnB,gBAAgB;EAChB,YACI,qBAAC,OAAD;GACI,WAAW,IACP,wDACA,kBACJ;aAJJ;IAOI,qBAAC,OAAD;KACI,WAAW,IACP,sFACA,kBACJ;eAJJ,CAMI,oBAAC,YAAD;MACI,SAAQ;MACR,WAAU;gBACb;KAEW,CAAA,GACZ,oBAAC,YAAD;MACI,SAAQ;MACR,WAAU;gBAET,MAAM;KACC,CAAA,CACX;;IAGL,oBAAC,OAAD;KAAK,WAAU;eACX,oBAAC,WAAD;MACI,MAAK;MACL,aAAY;MACZ,eAAe,QAAQ,eAAe,OAAO,EAAE;MAC/C,gBAAe;KAClB,CAAA;IACA,CAAA;IAGL,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEI,oBAAC,OAAD;MAAK,WAAU;gBACV,oBAAoB,KAAK,eAAe;OACrC,MAAM,QAAQ,aAAa,UAAU;OACrC,MAAM,SAAS,SAAS;OAExB,OACI,qBAAC,OAAD;QAEI,eACI,kBAAkB,MAAM;QAE5B,WAAW,IACP,0EARO,kBAAkB,SAUnB,0EACA,0EACV;kBAVJ;SAYI,oBAAC,OAAD;UAAK,WAAU;oBACX,oBAAC,aAAD;WACI,kBAAkB;YACd,MAAM,WAAW;YACjB,MAAM,WAAW;YACjB,MACI,OAAO,WAAW,SAClB,WACM,WAAW,OACX,KAAA;WACd;WACA,MAAK;UACR,CAAA;SACA,CAAA;SACL,qBAAC,OAAD;UAAK,WAAU;oBAAf,CACI,oBAAC,YAAD;WACI,SAAQ;WACR,WAAU;qBAET,WAAW;UACJ,CAAA,GACX,UAAU,WAAW,QAClB,oBAAC,YAAD;WACI,SAAQ;WACR,WAAU;qBAET;UACO,CAAA,CAEf;;SACL,qBAAC,OAAD;UAAK,WAAU;oBAAf,CAMK,cAAc,UAAU,KACrB,oBAAC,SAAD;WAAS,OAAM;qBACX,oBAAC,OAAD,EAAK,WAAU,wCAAwC,CAAA;UAClD,CAAA,GAEZ,WAAW,WACR,oBAAC,SAAD;WAAS,OAAM;qBACX,oBAAC,OAAD,EAAK,WAAU,uCAAuC,CAAA;UACjD,CAAA,CAEZ;;QACJ;UA1DI,MA0DJ;MAEb,CAAC;KACA,CAAA,GAGJ,cAAc,SAAS,KACpB,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACI,oBAAC,YAAD;OACI,SAAQ;OACR,WAAU;iBACb;MAEW,CAAA,GACZ,oBAAC,OAAD;OAAK,WAAU;iBACV,cAAc,KAAK,SAAS;QACzB,MAAM,IACF,KAAK;QAGT,OACI,qBAAC,OAAD;SAEI,eACI,kBACI,KAAK,EACT;SAEJ,WAAW,IACP,oEAVR,kBAAkB,KAAK,KAYT,kDACA,wEACV;mBAZJ,CAcI,oBAAC,OAAD;UACI,WAAU;UACV,MAAK;UACL,QAAO;UACP,SAAQ;oBAER,oBAAC,QAAD;WACI,eAAc;WACd,gBAAe;WACf,aAAa;WACb,GAAE;UACL,CAAA;SACA,CAAA,GACL,oBAAC,YAAD;UACI,SAAQ;UACR,WAAU;oBAET,EAAE;SACK,CAAA,CACX;WAhCI,KAAK,EAgCT;OAEb,CAAC;MACA,CAAA,CACJ;OAER;;IAGL,qBAAC,OAAD;KACI,WAAW,IACP,iDACA,kBACJ;eAJJ;MAMI,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBACb;OAEW,CAAA,GACZ,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBAET,MAAM;OACC,CAAA,CACX;;MACL,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBACb;OAEW,CAAA,GACZ,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBAET,MAAM;OACC,CAAA,CACX;;MACL,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBACb;OAEW,CAAA,GACZ,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,OAAD,EAAK,WAAU,wCAAwC,CAAA,GACvD,oBAAC,YAAD;SACI,SAAQ;SACR,WAAU;mBAET,MAAM;QACC,CAAA,CACX;SACJ;;KACJ;;GACJ;;EAET,aACI,qBAAC,OAAD;GAAK,WAAU;aAAf,CAEI,qBAAC,OAAD;IACI,WAAW,IACP,gFACA,kBACJ;cAJJ,CAMI,qBAAC,OAAD;KAAK,WAAU;eAAf,CACI,oBAAC,YAAD;MACI,SAAQ;MACR,WAAU;gBACb;KAEW,CAAA,GAIZ,oBAAC,YAAD;MACI,SAAQ;MACR,WAAU;gBAET,EAAE,iCAAiC;KAC5B,CAAA,CACX;QACL,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEI,oBAAC,SAAD;MAAS,OAAM;gBACX,oBAAC,YAAD;OAAY,cAAW;OACnB,MAAK;OACL,SAAS;iBAET,oBAAC,OAAD;QACI,WAAU;QACV,MAAK;QACL,QAAO;QACP,SAAQ;kBAER,oBAAC,QAAD;SACI,eAAc;SACd,gBAAe;SACf,aAAa;SACb,GAAE;QACL,CAAA;OACA,CAAA;MACG,CAAA;KACP,CAAA,GAGT,oBAAC,SAAD;MAAS,OAAM;gBACX,oBAAC,YAAD;OAAY,cAAW;OACnB,MAAK;OACL,SAAS;iBAET,oBAAC,OAAD;QACI,WAAU;QACV,MAAK;QACL,QAAO;QACP,SAAQ;kBAER,oBAAC,QAAD;SACI,eAAc;SACd,gBAAe;SACf,aAAa;SACb,GAAE;QACL,CAAA;OACA,CAAA;MACG,CAAA;KACP,CAAA,CACR;MACJ;OAGL,qBAAC,OAAD;IAAK,WAAU;cAAf;KACI,qBAAC,WAAD;MACW;MACA;MACQ;MACA;MACf,aAAa;MACb,aAAa;MACF;MACA;MACX,SAAA;MACA,gBAAgB,EAAE,SAAS,IAAK;MAChC,SAAS;MACT,SAAS;MACT,YAAY,EAAE,iBAAiB,KAAK;MACpC,WAAW,SAAS,SAAS;MAC7B,WAAU;gBAfd;OAiBI,oBAAC,YAAD;QACI,SAAS,kBAAkB;QAC3B,KAAK;QACL,MAAM;QACN,WAAU;QACV,OAAO,SAAS,YAAY;OAC/B,CAAA;OACD,oBAAC,UAAD;QACI,iBAAiB;QAEjB,WAAU;OACb,CAAA;OACD,oBAAC,SAAD;QACI,kBAAkB,MAAM;SACpB,MAAM,IAAI,EAAE;SACZ,IAAI,EAAE,YAAY,OAAO;SACzB,IAAI,EAAE,YAAY,OAAO;SACzB,OAAO;QACX;QACA,YAAY,MAAM;SAId,IAHU,EAAE,KAGN,YAAY,OAAO,SAAS,YAAY;SAC9C,OAAO,SAAS,YAAY;QAChC;QACA,WAAW,SAAS,qBAAqB;QACzC,WAAU;OACb,CAAA;MACM;;KAGV,iBACG,oBAAC,OAAD;MAAK,WAAU;gBACX,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBAET,EAAE,+BAA+B;SAC9B,OAAO,cAAc;SACrB,OAAO,cAAc;SACrB,OAAO;QACX,CAAC;OACO,CAAA,GACZ,oBAAC,mBAAD;QACI,OAAO,OAAO,cAAc,KAAK;QACjC,eAAe;QACf,SAAS,CACL;SACI,OAAO;SACP,OAAO,EAAE,qCAAqC;QAClD,GACA;SACI,OAAO;SACP,OAAO,EAAE,qCAAqC;QAClD,CACJ;OACH,CAAA,CACA;;KACJ,CAAA;KAIR,aACG,oBAAC,OAAD;MACI,MAAK;MACL,WAAU;gBAEV,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,kBAAD,EAAkB,MAAK,WAAW,CAAA,GAClC,oBAAC,YAAD;QACI,SAAQ;QACR,WAAU;kBAET,EAAE,0BAA0B;OACrB,CAAA,CACX;;KACJ,CAAA;KAIT,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACI,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,OAAD,EAAK,WAAU,kCAAkC,CAAA,GACjD,oBAAC,YAAD;SACI,SAAQ;SACR,WAAU;mBACb;QAEW,CAAA,CACX;;OACL,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,OAAD,EAAK,WAAU,kCAAkC,CAAA,GACjD,oBAAC,YAAD;SACI,SAAQ;SACR,WAAU;mBACb;QAEW,CAAA,CACX;;OACL,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,OAAD;SACI,WAAU;SACV,OAAO,EACH,iBACI,2FACR;QACH,CAAA,GACD,oBAAC,YAAD;SACI,SAAQ;SACR,WAAU;mBACb;QAEW,CAAA,CACX;;OACL,oBAAC,OAAD,EAAK,WAAU,6BAA6B,CAAA;OAC5C,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,QAAD;SAAM,WAAU;mBAAa;QAAQ,CAAA,GACrC,oBAAC,YAAD;SACI,SAAQ;SACR,WAAU;mBACb;QAEW,CAAA,CACX;;OACL,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,QAAD;SAAM,WAAU;mBAAa;QAAQ,CAAA,GACrC,oBAAC,YAAD;SACI,SAAQ;SACR,WAAU;mBACb;QAEW,CAAA,CACX;;MACJ;;IACJ;KACJ;;CAEZ,CAAA;AAET;AAIA,IAAa,yBAAyB;CAClC,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,EAAE,aAAa,wBACjB,4BAA4B;;;;;;;;;;;;CAehC,IAAI,wBAAwB,KAAA,GACxB,OACI,oBAAC,OAAD;EAAK,WAAU;YACX,qBAAC,OAAD;GAAK,WAAU;aAAf,CACI,oBAAC,kBAAD,EAAkB,MAAK,QAAQ,CAAA,GAC/B,oBAAC,YAAD;IACI,SAAQ;IACR,OAAM;cACT;GAEW,CAAA,CACX;;CACJ,CAAA;CAIb,IAAI,oBAAoB,WAAW,GAC/B,OACI,oBAAC,OAAD;EAAK,WAAU;YACX,qBAAC,OAAD;GAAK,WAAU;aAAf;IACI,oBAAC,YAAD;KAAY,SAAQ;KAAY,WAAU;eACrC,EAAE,8BAA8B;IACzB,CAAA;IACZ,oBAAC,YAAD;KAAY,SAAQ;KAAQ,OAAM;KAAY,WAAU;eACnD,EAAE,mCAAmC;IAC9B,CAAA;IACZ,oBAAC,YAAD;KAAY,SAAQ;KAAQ,OAAM;KAAY,WAAU;eACnD,EAAE,+BAA+B;IAC1B,CAAA;GACX;;CACJ,CAAA;CAMb,OACI,oBAAC,OAAD;EAAK,WAAU;YACX,oBAAC,mBAAD,EAAA,UACI,oBAAC,wBAAD,EAAqC,aAAA,oBAAa,CAAA,EACnC,CAAA;CAClB,CAAA;AAEb"}