@a-company/paradigm 3.21.0 → 3.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp.js CHANGED
@@ -2026,7 +2026,7 @@ function registerResources(server, getContext2) {
2026
2026
 
2027
2027
  // ../paradigm-mcp/src/tools/index.ts
2028
2028
  import * as os3 from "os";
2029
- import * as path25 from "path";
2029
+ import * as path26 from "path";
2030
2030
  import {
2031
2031
  ListToolsRequestSchema,
2032
2032
  CallToolRequestSchema
@@ -2790,7 +2790,7 @@ function navigateExplore(config, target, rootDir) {
2790
2790
  }
2791
2791
  if (result.paths.length === 0) {
2792
2792
  const areaSymbols = Object.entries(config.symbols).filter(
2793
- ([sym, path26]) => sym.toLowerCase().includes(targetLower) || path26.toLowerCase().includes(targetLower)
2793
+ ([sym, path27]) => sym.toLowerCase().includes(targetLower) || path27.toLowerCase().includes(targetLower)
2794
2794
  ).slice(0, 10);
2795
2795
  result.paths = [...new Set(areaSymbols.map(([, p]) => p))];
2796
2796
  result.symbols = areaSymbols.map(([s]) => s);
@@ -11288,8 +11288,8 @@ function generateRunId() {
11288
11288
  var TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g;
11289
11289
  function interpolate(value, scope) {
11290
11290
  if (typeof value === "string") {
11291
- return value.replace(TEMPLATE_REGEX, (_match, path26) => {
11292
- const resolved = resolvePath(path26.trim(), scope);
11291
+ return value.replace(TEMPLATE_REGEX, (_match, path27) => {
11292
+ const resolved = resolvePath(path27.trim(), scope);
11293
11293
  return resolved !== void 0 ? String(resolved) : _match;
11294
11294
  });
11295
11295
  }
@@ -11322,8 +11322,8 @@ function resolvePath(dotPath, scope) {
11322
11322
  return void 0;
11323
11323
  }
11324
11324
  }
11325
- function deepGet(obj, path26) {
11326
- const parts = path26.split(/[.\[\]]+/).filter(Boolean);
11325
+ function deepGet(obj, path27) {
11326
+ const parts = path27.split(/[.\[\]]+/).filter(Boolean);
11327
11327
  let current = obj;
11328
11328
  for (const part of parts) {
11329
11329
  if (current == null || typeof current !== "object") return void 0;
@@ -11559,11 +11559,11 @@ async function runPersonaObject(rootDir, persona, options) {
11559
11559
  }
11560
11560
  async function runChain(rootDir, chainId, options) {
11561
11561
  const start = Date.now();
11562
- const fs22 = await import("fs");
11563
- const path26 = await import("path");
11562
+ const fs23 = await import("fs");
11563
+ const path27 = await import("path");
11564
11564
  const yaml14 = await import("js-yaml");
11565
- const chainPath = path26.join(rootDir, ".paradigm", "personas", "chains", `${chainId}.yaml`);
11566
- if (!fs22.existsSync(chainPath)) {
11565
+ const chainPath = path27.join(rootDir, ".paradigm", "personas", "chains", `${chainId}.yaml`);
11566
+ if (!fs23.existsSync(chainPath)) {
11567
11567
  return {
11568
11568
  chain_id: chainId,
11569
11569
  status: "error",
@@ -11572,7 +11572,7 @@ async function runChain(rootDir, chainId, options) {
11572
11572
  duration_ms: Date.now() - start
11573
11573
  };
11574
11574
  }
11575
- const chain = yaml14.load(fs22.readFileSync(chainPath, "utf8"));
11575
+ const chain = yaml14.load(fs23.readFileSync(chainPath, "utf8"));
11576
11576
  let permutation;
11577
11577
  if (options.permutation && chain.permutations) {
11578
11578
  permutation = chain.permutations.find((p) => p.id === options.permutation);
@@ -11676,8 +11676,8 @@ function validateInterpolation(persona) {
11676
11676
  const serialized = JSON.stringify(step);
11677
11677
  const templates = serialized.match(TEMPLATE_REGEX) || [];
11678
11678
  for (const template of templates) {
11679
- const path26 = template.replace("{{", "").replace("}}", "").trim();
11680
- const [namespace, ...rest] = path26.split(".");
11679
+ const path27 = template.replace("{{", "").replace("}}", "").trim();
11680
+ const [namespace, ...rest] = path27.split(".");
11681
11681
  const key = rest.join(".");
11682
11682
  switch (namespace) {
11683
11683
  case "fixtures":
@@ -12689,8 +12689,258 @@ function summarizeStep(step) {
12689
12689
  return result;
12690
12690
  }
12691
12691
 
12692
- // ../paradigm-mcp/src/tools/fallback-grep.ts
12692
+ // ../paradigm-mcp/src/tools/graph.ts
12693
+ import * as fs22 from "fs";
12693
12694
  import * as path24 from "path";
12695
+ var GRAPHS_DIR = ".paradigm/graphs";
12696
+ var CATEGORY_PREFIXES = {
12697
+ component: "#",
12698
+ flow: "$",
12699
+ gate: "^",
12700
+ signal: "!",
12701
+ aspect: "~"
12702
+ };
12703
+ var NODE_WIDTH = 200;
12704
+ var NODE_HEIGHT = 60;
12705
+ var NODE_GAP = 20;
12706
+ var GROUP_PADDING = 40;
12707
+ var GROUP_HEADER = 50;
12708
+ var GROUP_GAP = 60;
12709
+ function getGraphToolsList() {
12710
+ return [
12711
+ {
12712
+ name: "paradigm_graph_generate",
12713
+ description: "Generate a named GraphState JSON file for the Paradigm Symbol Graph UI. Writes to .paradigm/graphs/{name}.graph.json. View saved graphs with `paradigm graph` CLI. Returns a summary with node/edge counts and file path. ~100 tokens.",
12714
+ inputSchema: {
12715
+ type: "object",
12716
+ properties: {
12717
+ name: {
12718
+ type: "string",
12719
+ description: 'Graph name (kebab-case). Used as filename: {name}.graph.json. E.g. "auth-flow", "full-project", "checkout-subsystem".'
12720
+ },
12721
+ symbols: {
12722
+ type: "array",
12723
+ items: { type: "string" },
12724
+ description: 'Symbol names to include (e.g. ["#auth-middleware", "^authenticated"]). Omit to include all from scan-index.'
12725
+ },
12726
+ groups: {
12727
+ type: "array",
12728
+ items: {
12729
+ type: "object",
12730
+ properties: {
12731
+ label: { type: "string", description: "Group display label" },
12732
+ symbols: {
12733
+ type: "array",
12734
+ items: { type: "string" },
12735
+ description: "Symbol names belonging to this group"
12736
+ }
12737
+ },
12738
+ required: ["label", "symbols"]
12739
+ },
12740
+ description: "Optional groupings of symbols."
12741
+ },
12742
+ links: {
12743
+ type: "array",
12744
+ items: {
12745
+ type: "object",
12746
+ properties: {
12747
+ source: { type: "string", description: "Source group label" },
12748
+ target: { type: "string", description: "Target group label" },
12749
+ label: { type: "string", description: "Edge label" }
12750
+ },
12751
+ required: ["source", "target"]
12752
+ },
12753
+ description: "Edges between groups (by label name)."
12754
+ }
12755
+ },
12756
+ required: ["name"]
12757
+ },
12758
+ annotations: {
12759
+ readOnlyHint: false,
12760
+ destructiveHint: false
12761
+ }
12762
+ }
12763
+ ];
12764
+ }
12765
+ async function handleGraphTool(name, args, ctx) {
12766
+ if (name !== "paradigm_graph_generate") {
12767
+ return { handled: false, text: "" };
12768
+ }
12769
+ try {
12770
+ const graphName = args.name || "untitled";
12771
+ const slug = graphName.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
12772
+ const result = buildGraphState(
12773
+ ctx.rootDir,
12774
+ args.symbols,
12775
+ args.groups,
12776
+ args.links,
12777
+ graphName
12778
+ );
12779
+ const json = JSON.stringify(result, null, 2);
12780
+ const graphsDir = path24.join(ctx.rootDir, GRAPHS_DIR);
12781
+ if (!fs22.existsSync(graphsDir)) fs22.mkdirSync(graphsDir, { recursive: true });
12782
+ const outPath = path24.join(graphsDir, `${slug}.graph.json`);
12783
+ fs22.writeFileSync(outPath, json, "utf8");
12784
+ const summary = JSON.stringify({
12785
+ file: outPath,
12786
+ name: graphName,
12787
+ slug,
12788
+ nodes: result.nodes.length,
12789
+ edges: result.edges.length,
12790
+ size: `${(json.length / 1024).toFixed(1)} KB`,
12791
+ hint: `Graph saved. Run \`paradigm graph\` to view in browser.`
12792
+ }, null, 2);
12793
+ trackToolCall(summary.length, name);
12794
+ return { handled: true, text: summary };
12795
+ } catch (err2) {
12796
+ const text = JSON.stringify({ error: err2.message }, null, 2);
12797
+ trackToolCall(text.length, name);
12798
+ return { handled: true, text };
12799
+ }
12800
+ }
12801
+ var SCAN_CATEGORY_MAP = {
12802
+ components: "component",
12803
+ flows: "flow",
12804
+ gates: "gate",
12805
+ signals: "signal",
12806
+ aspects: "aspect"
12807
+ };
12808
+ function loadScanIndex(rootDir) {
12809
+ const indexPath = path24.join(rootDir, ".paradigm", "scan-index.json");
12810
+ if (!fs22.existsSync(indexPath)) return [];
12811
+ const raw = JSON.parse(fs22.readFileSync(indexPath, "utf8"));
12812
+ const symbols = [];
12813
+ for (const [sectionKey, categoryName] of Object.entries(SCAN_CATEGORY_MAP)) {
12814
+ const section = raw[sectionKey];
12815
+ if (!section || typeof section !== "object") continue;
12816
+ for (const [id, sym] of Object.entries(section)) {
12817
+ const s = sym;
12818
+ symbols.push({
12819
+ id,
12820
+ name: id,
12821
+ category: categoryName,
12822
+ prefix: CATEGORY_PREFIXES[categoryName] || "#",
12823
+ description: s.description,
12824
+ path: s.path,
12825
+ tags: s.tags
12826
+ });
12827
+ }
12828
+ }
12829
+ return symbols;
12830
+ }
12831
+ function resolveSymbol(name, allSymbols) {
12832
+ const stripped = name.replace(/^[#$^!~]/, "");
12833
+ return allSymbols.find(
12834
+ (s) => s.id === stripped || s.name === stripped || s.id === name || s.name === name
12835
+ );
12836
+ }
12837
+ function buildGraphState(rootDir, symbolFilter, groups, links, graphName = "Generated Graph") {
12838
+ const allSymbols = loadScanIndex(rootDir);
12839
+ let included;
12840
+ if (symbolFilter && symbolFilter.length > 0) {
12841
+ included = symbolFilter.map((name) => resolveSymbol(name, allSymbols)).filter(Boolean);
12842
+ } else {
12843
+ included = allSymbols;
12844
+ }
12845
+ const nodes = [];
12846
+ const edges = [];
12847
+ const groupIdMap = /* @__PURE__ */ new Map();
12848
+ const assignedSymbols = /* @__PURE__ */ new Set();
12849
+ let nextGroupX = 0;
12850
+ if (groups && groups.length > 0) {
12851
+ for (const group of groups) {
12852
+ const groupId = `group-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
12853
+ groupIdMap.set(group.label, groupId);
12854
+ const memberSymbols = group.symbols.map((name) => resolveSymbol(name, included)).filter(Boolean);
12855
+ const cols = Math.ceil(Math.sqrt(memberSymbols.length));
12856
+ const rows = Math.ceil(memberSymbols.length / cols);
12857
+ for (let i = 0; i < memberSymbols.length; i++) {
12858
+ const sym = memberSymbols[i];
12859
+ const col = i % cols;
12860
+ const row = Math.floor(i / cols);
12861
+ const prefix = CATEGORY_PREFIXES[sym.category] || "#";
12862
+ nodes.push({
12863
+ id: `sym-${sym.id}`,
12864
+ type: "symbolNode",
12865
+ position: {
12866
+ x: GROUP_PADDING + col * (NODE_WIDTH + NODE_GAP),
12867
+ y: GROUP_HEADER + GROUP_PADDING + row * (NODE_HEIGHT + NODE_GAP)
12868
+ },
12869
+ parentId: groupId,
12870
+ data: {
12871
+ type: "symbol",
12872
+ symbol: sym,
12873
+ label: `${prefix}${sym.name}`
12874
+ }
12875
+ });
12876
+ assignedSymbols.add(sym.id);
12877
+ }
12878
+ const cols2 = Math.max(cols, 1);
12879
+ const rows2 = Math.max(rows, 1);
12880
+ const groupWidth = GROUP_PADDING * 2 + cols2 * NODE_WIDTH + (cols2 - 1) * NODE_GAP;
12881
+ const groupHeight = GROUP_HEADER + GROUP_PADDING * 2 + rows2 * NODE_HEIGHT + (rows2 - 1) * NODE_GAP;
12882
+ nodes.unshift({
12883
+ id: groupId,
12884
+ type: "groupNode",
12885
+ position: { x: nextGroupX, y: 0 },
12886
+ style: { width: groupWidth, height: groupHeight },
12887
+ data: { type: "group", label: group.label }
12888
+ });
12889
+ nextGroupX += groupWidth + GROUP_GAP;
12890
+ }
12891
+ }
12892
+ const ungrouped = included.filter((s) => !assignedSymbols.has(s.id));
12893
+ if (ungrouped.length > 0) {
12894
+ const startY = groups && groups.length > 0 ? 400 : 0;
12895
+ const cols = Math.ceil(Math.sqrt(ungrouped.length));
12896
+ for (let i = 0; i < ungrouped.length; i++) {
12897
+ const sym = ungrouped[i];
12898
+ const col = i % cols;
12899
+ const row = Math.floor(i / cols);
12900
+ const prefix = CATEGORY_PREFIXES[sym.category] || "#";
12901
+ nodes.push({
12902
+ id: `sym-${sym.id}`,
12903
+ type: "symbolNode",
12904
+ position: {
12905
+ x: col * (NODE_WIDTH + NODE_GAP),
12906
+ y: startY + row * (NODE_HEIGHT + NODE_GAP)
12907
+ },
12908
+ data: {
12909
+ type: "symbol",
12910
+ symbol: sym,
12911
+ label: `${prefix}${sym.name}`
12912
+ }
12913
+ });
12914
+ }
12915
+ }
12916
+ if (links && links.length > 0) {
12917
+ for (const link of links) {
12918
+ const sourceId = groupIdMap.get(link.source);
12919
+ const targetId = groupIdMap.get(link.target);
12920
+ if (sourceId && targetId) {
12921
+ edges.push({
12922
+ id: `e-${sourceId}-${targetId}`,
12923
+ source: sourceId,
12924
+ target: targetId,
12925
+ type: "default",
12926
+ label: link.label,
12927
+ data: { label: link.label }
12928
+ });
12929
+ }
12930
+ }
12931
+ }
12932
+ return {
12933
+ version: "1.0",
12934
+ name: graphName,
12935
+ projectId: path24.basename(rootDir),
12936
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
12937
+ nodes,
12938
+ edges
12939
+ };
12940
+ }
12941
+
12942
+ // ../paradigm-mcp/src/tools/fallback-grep.ts
12943
+ import * as path25 from "path";
12694
12944
  import { execSync as execSync5 } from "child_process";
12695
12945
  function grepForReferences(rootDir, symbol, options = {}) {
12696
12946
  const { maxResults = 20 } = options;
@@ -12719,7 +12969,7 @@ function grepForReferences(rootDir, symbol, options = {}) {
12719
12969
  const match = line.match(/^(.+?):(\d+):(.*)$/);
12720
12970
  if (match) {
12721
12971
  const [, filePath, lineNum, content] = match;
12722
- const relativePath = path24.relative(rootDir, filePath);
12972
+ const relativePath = path25.relative(rootDir, filePath);
12723
12973
  let context2 = "unknown";
12724
12974
  if (relativePath.includes(".purpose") || relativePath.includes("portal.yaml")) {
12725
12975
  context2 = "purpose";
@@ -12982,6 +13232,8 @@ function registerTools(server, getContext2, reloadContext2) {
12982
13232
  ...getPersonaToolsList(),
12983
13233
  // Protocol tools
12984
13234
  ...getProtocolsToolsList(),
13235
+ // Graph generation tool
13236
+ ...getGraphToolsList(),
12985
13237
  // Plugin update check
12986
13238
  {
12987
13239
  name: "paradigm_plugin_check",
@@ -13615,7 +13867,7 @@ Update command:
13615
13867
  const { rebuildStaticFiles: rebuildStaticFiles2 } = await import("./reindex-CMZARW5K.js");
13616
13868
  const memberResults = [];
13617
13869
  for (const member of ctx.workspace.config.members) {
13618
- const memberAbsPath = path25.resolve(path25.dirname(ctx.workspace.workspacePath), member.path);
13870
+ const memberAbsPath = path26.resolve(path26.dirname(ctx.workspace.workspacePath), member.path);
13619
13871
  try {
13620
13872
  const result = await rebuildStaticFiles2(memberAbsPath);
13621
13873
  memberResults.push({
@@ -13804,6 +14056,15 @@ Update command:
13804
14056
  };
13805
14057
  }
13806
14058
  }
14059
+ if (name === "paradigm_graph_generate") {
14060
+ const result = await handleGraphTool(name, args, ctx);
14061
+ if (result.handled) {
14062
+ trackToolCall(result.text.length, name);
14063
+ return {
14064
+ content: [{ type: "text", text: result.text }]
14065
+ };
14066
+ }
14067
+ }
13807
14068
  if (name === "paradigm_reindex") {
13808
14069
  const reload = reloadContext2 || (async () => {
13809
14070
  });
@@ -0,0 +1 @@
1
+ .react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{--bg-base: #0a0a0f;--bg-panel: #0f172a;--bg-surface: #1e293b;--bg-hover: #334155;--text-primary: #e2e8f0;--text-secondary: #94a3b8;--text-muted: #64748b;--border: #1e293b;--border-focus: #475569;--color-component: #86efac;--color-flow: #fbbf24;--color-gate: #f87171;--color-signal: #fde047;--color-aspect: #a78bfa}*{margin:0;padding:0;box-sizing:border-box}body{background:var(--bg-base);color:var(--text-primary);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;overflow:hidden}.app{display:flex;height:100vh;width:100vw}.app__main{flex:1;display:flex;flex-direction:column;min-width:0}.symbol-panel{width:280px;min-width:280px;background:var(--bg-panel);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden}.symbol-panel__header{display:flex;align-items:center;justify-content:space-between;padding:16px;border-bottom:1px solid var(--border)}.symbol-panel__header h2{font-size:14px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--text-secondary)}.symbol-panel__count{font-size:12px;background:var(--bg-surface);color:var(--text-muted);padding:2px 8px;border-radius:10px}.symbol-panel__search{margin:12px 16px;padding:8px 12px;background:var(--bg-surface);border:1px solid var(--border);border-radius:6px;color:var(--text-primary);font-size:13px;outline:none;transition:border-color .15s}.symbol-panel__search:focus{border-color:var(--border-focus)}.symbol-panel__search::placeholder{color:var(--text-muted)}.symbol-panel__list{flex:1;overflow-y:auto;padding:0 8px 16px}.symbol-panel__section{margin-top:4px}.symbol-panel__section-header{display:flex;align-items:center;gap:6px;width:100%;padding:6px 8px;background:none;border:none;font-size:12px;font-weight:600;cursor:pointer;text-transform:uppercase;letter-spacing:.05em}.symbol-panel__section-arrow{font-size:10px;width:12px}.symbol-panel__section-count{margin-left:auto;font-size:11px;opacity:.5;font-weight:400}.symbol-panel__section-items{padding:2px 0}.symbol-panel__item{display:flex;flex-direction:column;gap:2px;padding:6px 8px 6px 12px;margin:1px 0;border-left:2px solid transparent;border-radius:4px;cursor:grab;transition:background .1s}.symbol-panel__item:hover{background:var(--bg-surface)}.symbol-panel__item:active{cursor:grabbing}.symbol-panel__item-name{font-size:13px;font-weight:500;font-family:SF Mono,Fira Code,monospace}.symbol-panel__item-desc{font-size:11px;color:var(--text-muted);line-height:1.3}.toolbar{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--bg-panel);border-bottom:1px solid var(--border)}.toolbar__name{background:transparent;border:1px solid transparent;color:var(--text-primary);font-size:14px;font-weight:600;padding:4px 8px;border-radius:4px;width:200px;outline:none;transition:border-color .15s}.toolbar__name:hover,.toolbar__name:focus{border-color:var(--border-focus)}.toolbar__actions{display:flex;align-items:center;gap:6px;margin-left:auto}.toolbar__btn{padding:6px 12px;background:var(--bg-surface);border:1px solid var(--border);color:var(--text-secondary);font-size:12px;font-weight:500;border-radius:6px;cursor:pointer;transition:all .15s}.toolbar__btn:hover{background:var(--bg-hover);color:var(--text-primary)}.toolbar__btn--primary{background:#1d4ed8;border-color:#2563eb;color:#fff}.toolbar__btn--primary:hover{background:#2563eb}.toolbar__divider{width:1px;height:20px;background:var(--border);margin:0 4px}.canvas-wrapper{flex:1;position:relative}.paradigm-flow{background:var(--bg-base)!important}.react-flow__controls{background:var(--bg-panel)!important;border:1px solid var(--border)!important;border-radius:8px!important;overflow:hidden}.react-flow__controls-button{background:var(--bg-panel)!important;border-bottom:1px solid var(--border)!important;fill:var(--text-secondary)!important}.react-flow__controls-button:hover{background:var(--bg-surface)!important}.react-flow__minimap{border:1px solid var(--border)!important;border-radius:8px!important;overflow:hidden}.react-flow__edge-path{stroke:var(--text-muted)!important;stroke-width:2}.react-flow__edge.selected .react-flow__edge-path{stroke:var(--text-primary)!important}.react-flow__edge-text{fill:var(--text-secondary)!important;font-size:11px}.react-flow__edge-textbg{fill:var(--bg-panel)!important}.symbol-node{background:var(--bg-panel);border:1px solid var(--border);border-left:3px solid var(--color-component);border-radius:8px;padding:8px 12px;min-width:160px;max-width:240px;transition:border-color .15s}.symbol-node:hover{border-color:var(--border-focus)}.symbol-node__name{font-size:13px;font-weight:600;font-family:SF Mono,Fira Code,monospace;line-height:1.4}.symbol-node__desc{font-size:11px;color:var(--text-muted);line-height:1.3;margin-top:2px}.group-node{background:#1e293b80;border:2px dashed var(--border-focus);border-radius:12px;width:100%;height:100%;position:relative}.group-node__header{padding:8px 16px;border-bottom:1px solid var(--border);border-radius:12px 12px 0 0;background:#0f172acc;cursor:default}.group-node__label{font-size:13px;font-weight:600;color:var(--text-primary)}.group-node__input{background:var(--bg-surface);border:1px solid var(--border-focus);color:var(--text-primary);font-size:13px;font-weight:600;padding:2px 8px;border-radius:4px;outline:none;width:100%}.group-node__resizer-line{border-color:var(--color-component)!important}.group-node__resizer-handle{background:var(--color-component)!important;width:8px!important;height:8px!important;border-radius:2px!important}.group-handle{width:10px!important;height:10px!important;background:var(--bg-hover)!important;border:2px solid var(--text-muted)!important}.export-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:flex;align-items:center;justify-content:center;z-index:100}.export-dialog{background:var(--bg-panel);border:1px solid var(--border);border-radius:12px;width:640px;max-width:90vw;max-height:80vh;display:flex;flex-direction:column}.export-dialog__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border)}.export-dialog__header h3{font-size:16px;font-weight:600}.export-dialog__close{background:none;border:none;color:var(--text-muted);font-size:24px;cursor:pointer;line-height:1;padding:0 4px}.export-dialog__close:hover{color:var(--text-primary)}.export-dialog__content{flex:1;margin:16px 20px;padding:12px;background:var(--bg-base);border:1px solid var(--border);border-radius:8px;color:var(--text-primary);font-size:13px;font-family:SF Mono,Fira Code,monospace;resize:none;outline:none;overflow-y:auto}.export-dialog__actions{display:flex;gap:8px;padding:16px 20px;border-top:1px solid var(--border);justify-content:flex-end}.load-dialog{width:520px}.load-dialog__body{padding:16px 20px;display:flex;flex-direction:column;gap:12px}.load-dialog__section{display:flex;flex-direction:column;gap:8px}.load-dialog__label{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary)}.load-dialog__divider{display:flex;align-items:center;gap:12px;color:var(--text-muted);font-size:12px}.load-dialog__divider:before,.load-dialog__divider:after{content:"";flex:1;height:1px;background:var(--border)}.load-dialog__status{font-size:12px;padding:8px 12px;border-radius:6px}.load-dialog__status--error{background:#f871711a;color:#f87171;border:1px solid rgba(248,113,113,.2)}.load-dialog__status--success{background:#86efac1a;color:#86efac;border:1px solid rgba(134,239,172,.2)}.load-dialog__loading{color:#94a3b8;font-size:.85rem;padding:8px 0}.load-dialog__graph-list{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow-y:auto}.load-dialog__graph-item{display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:8px 12px;background:#ffffff08;border:1px solid rgba(255,255,255,.06);border-radius:6px;cursor:pointer;text-align:left;color:inherit;font:inherit;transition:background .15s,border-color .15s}.load-dialog__graph-item:hover{background:#ffffff14;border-color:#7dd3fc4d}.load-dialog__graph-name{font-weight:500;font-size:.9rem;color:#e2e8f0}.load-dialog__graph-meta{font-size:.75rem;color:#64748b}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-hover);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}