stackcanvas 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # stackcanvas
2
2
 
3
- [Telemetry: opt-in, anonymous, no infra data — see TELEMETRY.md](TELEMETRY.md)
4
-
5
3
  Live infrastructure canvas for [Claude Code](https://claude.com/claude-code).
6
4
  The agent writes and plans your Terraform — stackcanvas shows it as a living
7
5
  diagram. Drag new resources onto the canvas; the agent turns them into
@@ -16,7 +14,7 @@ localhost, reading your local state and plan.
16
14
  2. The graph re-renders live whenever `*.tfstate` or `.stackcanvas/plan.json`
17
15
  change — you watch the agent work.
18
16
  3. You drag resources from the palette (or right-click existing ones to
19
- request changes / removal) and hit **Apply**. Connections between two
17
+ request changes / removal) and hit **Send to agent**. Connections between two
20
18
  not-yet-created (draft) resources aren't included in the intent yet —
21
19
  connect drafts to existing resources, or describe the relation in the
22
20
  draft's wishes field.
@@ -30,10 +28,6 @@ localhost, reading your local state and plan.
30
28
  claude plugin marketplace add pshenok/stackcanvas
31
29
  claude plugin install stackcanvas@stackcanvas
32
30
 
33
- or without the plugin system:
34
-
35
- claude mcp add stackcanvas -- npx -y stackcanvas
36
-
37
31
  Then, inside a repo with Terraform:
38
32
 
39
33
  /stackcanvas
@@ -41,6 +35,13 @@ Then, inside a repo with Terraform:
41
35
  This is the verified path — the CI `check-plugin` job validates the plugin
42
36
  and marketplace manifests on every push.
43
37
 
38
+ Or without the plugin system:
39
+
40
+ claude mcp add stackcanvas -- npx -y stackcanvas
41
+
42
+ Then, inside a repo with Terraform, just ask: *open the stackcanvas canvas
43
+ for this repo*.
44
+
44
45
  ## Other MCP clients
45
46
 
46
47
  stackcanvas is a standard [MCP](https://modelcontextprotocol.io) server, so
@@ -98,7 +99,7 @@ to pin it — both take precedence over auto-detection.
98
99
  | `open_canvas` | Start the canvas for a Terraform root, open the browser |
99
100
  | `load_plan` | Register a plan (JSON or binary) for diff highlighting |
100
101
  | `get_graph_summary` | Text summary of the graph for the agent |
101
- | `await_canvas_intent` | Block until the user clicks Apply; returns their edits |
102
+ | `await_canvas_intent` | Block until the user clicks Send to agent; returns their edits |
102
103
 
103
104
  ## Demo
104
105
 
@@ -106,13 +107,14 @@ to pin it — both take precedence over auto-detection.
106
107
 
107
108
  ## Telemetry
108
109
 
109
- stackcanvas can send a handful of anonymous usage counters (installs,
110
- canvases opened, edits sent, scans run) **opt-in only**, nothing is sent
111
- until you click **Allow** on the one-time canvas banner, and `DO_NOT_TRACK=1`
112
- / `STACKCANVAS_TELEMETRY=0` always turn it off regardless of that decision.
113
- No resource names, infrastructure data, or file paths ever leave your
114
- machine. Full payload, consent model, and how to verify it yourself:
115
- [TELEMETRY.md](TELEMETRY.md).
110
+ stackcanvas can send a handful of anonymous (pseudonymous install id — see
111
+ TELEMETRY.md) usage counters (installs, canvases opened, intents sent;
112
+ scan/drift counters reserved five ever, see TELEMETRY.md) **opt-in
113
+ only**, nothing is sent until you click **Allow** on the one-time canvas
114
+ banner, and `DO_NOT_TRACK=1` / `STACKCANVAS_TELEMETRY=0` always turn it off
115
+ regardless of that decision. No resource names, infrastructure data, or file
116
+ paths ever leave your machine. Full payload, consent model, and how to
117
+ verify it yourself: [TELEMETRY.md](TELEMETRY.md).
116
118
 
117
119
  ## Development
118
120
 
package/dist/cli.js CHANGED
@@ -926,7 +926,7 @@ function openBrowser(url) {
926
926
  }
927
927
 
928
928
  // src/version.ts
929
- var VERSION = "0.2.0";
929
+ var VERSION = "0.2.1";
930
930
 
931
931
  // src/server.ts
932
932
  function looksLikeTerraformRoot(dir) {
@@ -966,7 +966,11 @@ function createMcpServer(deps = {}) {
966
966
  canvas.setAgentStatus("idle");
967
967
  open(url);
968
968
  }
969
- return ok(`Canvas running at ${url}. The graph live-updates as tfstate changes. Run \`terraform plan -out=tfplan && terraform show -json tfplan > .stackcanvas/plan.json\` (or \`tofu plan \u2026\` with OpenTofu) to show the plan diff, then call await_canvas_intent to receive user edits.`);
969
+ const stale = canvas.getStale();
970
+ const warning = stale === null ? "" : `
971
+
972
+ WARNING: the graph may be incomplete \u2014 ${stale}. Tell the user.`;
973
+ return ok(`Canvas running at ${url}. The graph live-updates as tfstate changes. Run \`terraform plan -out=tfplan && terraform show -json tfplan > .stackcanvas/plan.json\` (or \`tofu plan \u2026\` with OpenTofu) to show the plan diff, then call await_canvas_intent to receive user edits.` + warning);
970
974
  });
971
975
  mcp.registerTool("load_plan", {
972
976
  description: "Register a Terraform plan for diff highlighting on the canvas. Accepts a JSON plan (terraform show -json output) or a binary plan file.",
@@ -988,10 +992,14 @@ function createMcpServer(deps = {}) {
988
992
  inputSchema: {}
989
993
  }, async () => {
990
994
  if (!canvas) return fail("No canvas open. Call open_canvas first.");
991
- return ok(summarizeGraph(canvas.getGraph()));
995
+ const stale = canvas.getStale();
996
+ const warning = stale === null ? "" : `
997
+
998
+ WARNING: the graph may be incomplete \u2014 ${stale}. Tell the user.`;
999
+ return ok(summarizeGraph(canvas.getGraph()) + warning);
992
1000
  });
993
1001
  mcp.registerTool("await_canvas_intent", {
994
- description: "Block until the user clicks Apply on the canvas, then return their requested changes as intent JSON: {intent: {add, modify, remove} | null}. null = timeout, call again in a loop to keep waiting. After receiving an intent, write the Terraform code for it.",
1002
+ description: "Block until the user clicks Send-to-agent on the canvas, then return their requested changes as intent JSON: {intent: {add, modify, remove} | null}. null = timeout, call again in a loop to keep waiting. After receiving an intent, write the Terraform code for it. After implementing an intent, run terraform plan (write the JSON to .stackcanvas/plan.json) and call this tool again to keep the loop alive. Never run terraform apply unless the human explicitly asks.",
995
1003
  inputSchema: {
996
1004
  timeoutSeconds: z2.number().positive().max(3600).default(45).describe("How long to wait before returning {intent: null}. Default 45s: MCP clients commonly abort requests after 60s, so keep this under your client's request timeout and call the tool in a loop instead of passing large values.")
997
1005
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "stackcanvas",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
- "main": "src/server.ts",
5
+ "main": "dist/cli.js",
6
6
  "bin": {
7
7
  "stackcanvas": "dist/cli.js"
8
8
  },
@@ -28,5 +28,24 @@
28
28
  "@stackcanvas/server": "workspace:*",
29
29
  "@types/node": "^22.0.0",
30
30
  "tsup": "^8.0.0"
31
- }
31
+ },
32
+ "description": "Live infrastructure canvas for Claude Code: see what your AI agent is about to do to your Terraform \u2014 and approve it \u2014 before it happens. Local-first, credentials never leave your machine.",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/pshenok/stackcanvas.git"
37
+ },
38
+ "homepage": "https://stackcanvas.dev",
39
+ "bugs": "https://github.com/pshenok/stackcanvas/issues",
40
+ "author": "Kostyantyn Pshenychnyy",
41
+ "keywords": [
42
+ "terraform",
43
+ "opentofu",
44
+ "mcp",
45
+ "claude-code",
46
+ "infrastructure-as-code",
47
+ "devtools",
48
+ "visualization",
49
+ "ai-agent"
50
+ ]
32
51
  }
@@ -1 +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: #555;--xy-background-pattern-lines-color-default: #333;--xy-background-pattern-cross-color-default: #333;--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;touch-action:none}.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))}*{box-sizing:border-box}body{margin:0;font-family:ui-sans-serif,system-ui,sans-serif}.app{width:100vw;height:100vh;display:flex;flex-direction:column}.topbar{display:flex;gap:12px;align-items:center;padding:8px 14px;border-bottom:1px solid #e2e8f0;background:#fff;z-index:10}.brand{font-weight:700}.agent-badge{font-size:12px;padding:2px 8px;border-radius:999px;background:#f1f5f9}.agent-writing{background:#fef9c3}.agent-planning{background:#dbeafe}.stale-banner{color:#b91c1c;font-size:12px}.notice{font-size:12px;color:#334155}.resource-node{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1.5px solid #94a3b8;border-radius:8px;background:#fff;width:190px}.resource-node .labels{display:flex;flex-direction:column;overflow:hidden}.resource-node .name{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.resource-node .type{font-size:10px;color:#64748b}.resource-node.status-create{border-color:#22c55e;box-shadow:0 0 0 2px #22c55e33}.resource-node.status-update{border-color:#eab308;box-shadow:0 0 0 2px #eab30833}.resource-node.status-delete{border-color:#ef4444;box-shadow:0 0 0 2px #ef444433}.resource-node.status-replace{border-color:#a855f7;box-shadow:0 0 0 2px #a855f733}.draft{border-style:dashed;opacity:.85}.removed{border-style:dashed;border-color:#ef4444;opacity:.6;text-decoration:line-through}.modified:after{content:"";position:absolute}.react-flow__node-group{border-radius:10px;border:1px dashed #cbd5e1;background:#f8fafc80}.canvas-row{flex:1;display:flex;min-height:0}.canvas-row>.react-flow{flex:1}.inspector{width:320px;border-left:1px solid #e2e8f0;padding:12px;overflow:auto;background:#fff;font-size:13px}.inspector-head{display:flex;justify-content:space-between;align-items:center}.inspector table{width:100%;border-collapse:collapse}.inspector td{border-top:1px solid #f1f5f9;padding:4px 6px;vertical-align:top;word-break:break-all}.inspector .before code{color:#b91c1c;text-decoration:line-through}.inspector .after code{color:#15803d}.status-pill,.type-pill{font-size:11px;padding:2px 8px;border-radius:999px;background:#f1f5f9;margin-right:6px}.status-pill.status-create{background:#dcfce7}.status-pill.status-update{background:#fef9c3}.status-pill.status-delete{background:#fee2e2}.status-pill.status-replace{background:#f3e8ff}.source-row{display:flex;align-items:center;gap:6px;margin:6px 0;font-size:11px;color:#64748b}.inspector-actions{display:flex;gap:6px;margin:8px 0}.inspector-actions button{font-size:12px;padding:4px 8px;border:1px solid #e2e8f0;border-radius:6px;background:#fff;cursor:pointer}.inspector section input,.inspector section textarea{width:100%;font-size:13px;padding:6px;border:1px solid #e2e8f0;border-radius:6px;font-family:inherit}.inspector section textarea{min-height:70px;resize:vertical}.palette{width:200px;border-right:1px solid #e2e8f0;padding:10px;overflow:auto;background:#fff;display:flex;flex-direction:column;gap:4px}.palette-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border:1px solid #e2e8f0;border-radius:6px;background:#fff;cursor:pointer;font-size:12px;text-align:left}.palette-item:hover{background:#f1f5f9}.palette-custom{display:flex;gap:4px;margin-top:8px}.palette-custom input{flex:1;min-width:0;font-size:12px;padding:4px}.apply{background:#0f172a;color:#fff;border:none;border-radius:6px;padding:6px 14px;cursor:pointer}.apply:disabled{opacity:.4}.context-menu{position:fixed;z-index:50;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 8px 24px #0002;display:flex;flex-direction:column;min-width:170px;overflow:hidden}.context-menu button{border:none;background:none;text-align:left;padding:8px 12px;cursor:pointer;font-size:13px}.context-menu button:hover{background:#f1f5f9}.palette-pack{display:flex;flex-direction:column;gap:4px}.palette-pack h5{margin:6px 0 2px;font-size:11px;color:#64748b;text-transform:uppercase;letter-spacing:.05em}.consent-banner{position:fixed;right:16px;bottom:16px;z-index:40;width:300px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 8px 24px #0002;padding:12px 14px;display:flex;flex-direction:column;gap:10px}.consent-banner-head{display:flex;align-items:flex-start;gap:8px}.consent-banner-head .icon{flex-shrink:0;color:#64748b;margin-top:1px}.consent-banner-head p{margin:0;font-size:12px;line-height:1.45;color:#334155}.consent-banner-head a{color:#0f172a}.consent-actions{display:flex;gap:6px;justify-content:flex-end}.consent-actions button{font-size:12px;padding:4px 10px;border:1px solid #e2e8f0;border-radius:6px;background:#fff;cursor:pointer}.consent-actions button:first-child{background:#0f172a;color:#fff;border-color:#0f172a}
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: #555;--xy-background-pattern-lines-color-default: #333;--xy-background-pattern-cross-color-default: #333;--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;touch-action:none}.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))}*{box-sizing:border-box}body{margin:0;font-family:ui-sans-serif,system-ui,sans-serif}.app{width:100vw;height:100vh;display:flex;flex-direction:column}.topbar{display:flex;gap:12px;align-items:center;padding:8px 14px;border-bottom:1px solid #e2e8f0;background:#fff;z-index:10}.brand{font-weight:700}.agent-badge{font-size:12px;padding:2px 8px;border-radius:999px;background:#f1f5f9}.agent-writing{background:#fef9c3}.agent-planning{background:#dbeafe}.stale-banner{color:#b91c1c;font-size:12px}.notice{font-size:12px;color:#334155}.resource-node{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1.5px solid #94a3b8;border-radius:8px;background:#fff;width:190px}.resource-node .labels{display:flex;flex-direction:column;overflow:hidden}.resource-node .name{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.resource-node .type{font-size:10px;color:#64748b}.resource-node.status-create{border-color:#22c55e;box-shadow:0 0 0 2px #22c55e33}.resource-node.status-update{border-color:#eab308;box-shadow:0 0 0 2px #eab30833}.resource-node.status-delete{border-color:#ef4444;box-shadow:0 0 0 2px #ef444433}.resource-node.status-replace{border-color:#a855f7;box-shadow:0 0 0 2px #a855f733}.draft{border-style:dashed;opacity:.85}.removed{border-style:dashed;border-color:#ef4444;opacity:.6;text-decoration:line-through}.modified:after{content:"";position:absolute}.react-flow__node-group{border-radius:10px;border:1px dashed #cbd5e1;background:#f8fafc80}.canvas-row{flex:1;display:flex;min-height:0;position:relative}.canvas-row>.react-flow{flex:1}.layout-hint{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;color:#94a3b8;pointer-events:none;z-index:5}.inspector{width:320px;border-left:1px solid #e2e8f0;padding:12px;overflow:auto;background:#fff;font-size:13px}.inspector-head{display:flex;justify-content:space-between;align-items:center}.inspector table{width:100%;border-collapse:collapse}.inspector td{border-top:1px solid #f1f5f9;padding:4px 6px;vertical-align:top;word-break:break-all}.inspector .before code{color:#b91c1c;text-decoration:line-through}.inspector .after code{color:#15803d}.status-pill,.type-pill{font-size:11px;padding:2px 8px;border-radius:999px;background:#f1f5f9;margin-right:6px}.status-pill.status-create{background:#dcfce7}.status-pill.status-update{background:#fef9c3}.status-pill.status-delete{background:#fee2e2}.status-pill.status-replace{background:#f3e8ff}.source-row{display:flex;align-items:center;gap:6px;margin:6px 0;font-size:11px;color:#64748b}.inspector-actions{display:flex;gap:6px;margin:8px 0}.inspector-actions button{font-size:12px;padding:4px 8px;border:1px solid #e2e8f0;border-radius:6px;background:#fff;cursor:pointer}.inspector section input,.inspector section textarea{width:100%;font-size:13px;padding:6px;border:1px solid #e2e8f0;border-radius:6px;font-family:inherit}.inspector section textarea{min-height:70px;resize:vertical}.palette{width:200px;border-right:1px solid #e2e8f0;padding:10px;overflow:auto;background:#fff;display:flex;flex-direction:column;gap:4px}.palette-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border:1px solid #e2e8f0;border-radius:6px;background:#fff;cursor:pointer;font-size:12px;text-align:left}.palette-item:hover{background:#f1f5f9}.palette-custom{display:flex;gap:4px;margin-top:8px}.palette-custom input{flex:1;min-width:0;font-size:12px;padding:4px}.apply{background:#0f172a;color:#fff;border:none;border-radius:6px;padding:6px 14px;cursor:pointer}.apply:disabled{opacity:.4}.context-menu{position:fixed;z-index:50;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 8px 24px #0002;display:flex;flex-direction:column;min-width:170px;overflow:hidden}.context-menu button{border:none;background:none;text-align:left;padding:8px 12px;cursor:pointer;font-size:13px}.context-menu button:hover{background:#f1f5f9}.palette-pack{display:flex;flex-direction:column;gap:4px}.palette-pack h5{margin:6px 0 2px;font-size:11px;color:#64748b;text-transform:uppercase;letter-spacing:.05em}.consent-banner{position:fixed;right:16px;bottom:16px;z-index:40;width:300px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 8px 24px #0002;padding:12px 14px;display:flex;flex-direction:column;gap:10px}.consent-banner-head{display:flex;align-items:flex-start;gap:8px}.consent-banner-head .icon{flex-shrink:0;color:#64748b;margin-top:1px}.consent-banner-head p{margin:0;font-size:12px;line-height:1.45;color:#334155}.consent-banner-head a{color:#0f172a}.consent-actions{display:flex;gap:6px;justify-content:flex-end}.consent-actions button{font-size:12px;padding:4px 10px;border:1px solid #e2e8f0;border-radius:6px;background:#fff;cursor:pointer}.consent-actions button:first-child{background:#0f172a;color:#fff;border-color:#0f172a}