claude-figjam 1.1.0 → 1.3.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.
Files changed (2) hide show
  1. package/dist/tools.js +16 -3
  2. package/package.json +1 -1
package/dist/tools.js CHANGED
@@ -45,7 +45,7 @@ export const deleteNodesSchema = z.object({
45
45
  node_ids: z.union([z.string(), z.array(z.string())]),
46
46
  });
47
47
  export function registerTools(server, bridge) {
48
- server.tool("figjam_read_board", "Returns full board state: all nodes with IDs, types, positions, content, and colors.", {}, async () => {
48
+ server.tool("figjam_read_board", "Returns full board state: all nodes with IDs, types, positions, content, and colors. ALWAYS call this before making any changes to understand the current state of the board. After a page switch, call this again to confirm you are on the correct page before writing.", {}, async () => {
49
49
  try {
50
50
  const result = await bridge.execute("read_board", {});
51
51
  return {
@@ -62,7 +62,7 @@ export function registerTools(server, bridge) {
62
62
  };
63
63
  }
64
64
  });
65
- server.tool("figjam_create_node", "Creates any FigJam node (sticky, shape, text, section, table, code_block). Returns the new node ID. IMPORTANT: For multi-line content, use actual newline characters in the string — do NOT use \\n escape sequences, as they will render literally. LAYOUT RULES: Minimum vertical gap between stacked nodes = 80px. Minimum horizontal gap between pipeline stages = 328px. Use shape nodes as group containers instead of sections (sections cannot be resized programmatically). Container pattern: create container shape first (color #f0f2ff, 48px padding all sides), then child nodes on top. When a node connects both up and down in a column, place the upstream target ABOVE the hub node to separate TOP/BOTTOM slot usage. width+height must both be passed together to resize — passing only one is silently ignored.", {
65
+ server.tool("figjam_create_node", "Creates any FigJam node (sticky, shape, text, section, table, code_block). Returns the new node ID. IMPORTANT: For multi-line content, use actual newline characters in the string — do NOT use \\n escape sequences, as they will render literally. LAYOUT RULES: Use a 4px grid all x/y positions and width/height values must be multiples of 4. Use shape nodes as group containers instead of sections (sections cannot be resized programmatically). BASE ATOM: default node size is 224×116px (rounded_rectangle) or 200×200px (diamond). Node size is content-driven — scale to content in 4px increments. Container padding is 32px all sides, so a single-node container = node_width+64 × node_height+64. Container pattern: create container shape first (color #f0f2ff), then child nodes on top — z-order depends on creation order, so the container MUST be created before its children or it will cover them. Section labels (the stage name above each container) should be type=text, font_size=24, bold=true, same x as container, y = container_y − 44 (leaves room for the 36px bold label plus an 8px gap above the container top). Note: FigJam text nodes snap to preset sizes — 24px = Medium, 16px = Small (snaps and shows as 'Small' in inspector), 12px = Small (shows as '12' in inspector, not 'Small' — prefer 16). Shape text size: pass font_size=16 to get Small text (recommended default for all content nodes), font_size=24 for Medium. Always pass font_size explicitly — if omitted, FigJam picks the size based on canvas state which is unpredictable. For connector branch labels, use type=shape (rounded_rectangle, width=96, height=32, color=#ffffff, font_size=16) — do NOT use the connector label param (always renders at midpoint). HORIZONTAL SEGMENT placement: junction_y = source_bottom + (target_top - source_bottom) / 2; label_y = junction_y - label_height/2. Left-going connectors (target is left of source): label_x = target_center_x + 8 (label sits right of elbow, on the approach from source). Right-going connectors: label_x = target_center_x - label_width - 8 (label sits left of elbow). NEARLY-VERTICAL connector (no horizontal segment — target center_x ≈ source center_x): center the label on the connector and place it in the lower unique segment — label_x = target_center_x - label_width/2, label_y = (junction_y + target_top) / 2 - label_height/2. LABEL/NODE CONTENT RULE: connector label carries the branch condition only (e.g., 'Video', '2D image', '>250MB'); node content carries the outcome/method only. Do NOT repeat the branch condition in both — strip it from the node first line once a connector label is placed. TOPOLOGY-FIRST PLACEMENT: always map the full connection graph before placing any node. Placement is deterministic once topology is known: (1) identify the anchor row — the row with the most nodes, which defines section width; (2) place anchor row at minimum spacing; (3) every other node centers over its direct connection targets only, not the full row width — if a hub connects to 3 of 5 nodes in a row, center it over those 3, not all 5; (4) hub centered over targets: hub_x = (leftmost_target_center + rightmost_target_center) / 2 − hub_width / 2; (5) targets spread from hub: leftmost_x = hub_center_x − ((n × node_w) + ((n−1) × col_gap)) / 2. SPACING RULES — all values are minimums, not fixed; actual gaps can scale up proportionally, always rounded to nearest 4px: row gap (vertical between rows within a section) = 240px minimum; column gap (horizontal between nodes in same row) = 120px minimum; stage gap (vertical between sections in a vertical pipeline) = 328px minimum; stage gap (horizontal between sections in a horizontal pipeline) = 328px minimum. Scale container height/width to fit all rows with their gaps plus 32px padding top and bottom. When a node connects both up and down in a column, place the upstream target ABOVE the hub node to separate TOP/BOTTOM slot usage. width+height must both be passed together to resize — passing only one is silently ignored. Decision nodes (branching logic) use shape=diamond. TWO valid fan-out strategies: (A) LEFT/RIGHT EXIT: place the diamond at the far left or right of all targets so |dx| > |dy| for every target — use when hub is in the same row as targets. (B) BOTTOM EXIT: place the diamond horizontally CENTERED over the target row, use from_magnet=BOTTOM AND to_magnet=TOP on every fan-out connector — use when all targets are in a row below the hub. Both magnets are required: from_magnet=BOTTOM alone causes far-offset OR closely-spaced targets to receive side entry instead of top entry. FEEDBACK LOOP ALIGNMENT: when a node has a bidirectional or feedback connection to a node in a distant stage, place them on the same horizontal row (same y-coordinate center) so the connector routes as a clean horizontal line. A feedback node at a different y than its remote target produces a diagonal connector that is visually harder to follow — reorder the local column so the feedback node sits at the matching row. COLUMN REORDER SAFETY: before moving any node in a column, call figjam_read_board and list every node in that column with its current y position. Plan all moves together and check that no two nodes land on the same y after the reorder. Execute all moves before declaring done — partial moves leave nodes at conflicting positions.", {
66
66
  type: z.enum([
67
67
  "sticky",
68
68
  "shape",
@@ -84,6 +84,10 @@ export function registerTools(server, bridge) {
84
84
  .number()
85
85
  .optional()
86
86
  .describe("Font size in pixels (text nodes only)"),
87
+ bold: z
88
+ .boolean()
89
+ .optional()
90
+ .describe("Bold text (text nodes only). Default false."),
87
91
  shape: z
88
92
  .enum([
89
93
  "rounded_rectangle",
@@ -130,7 +134,7 @@ export function registerTools(server, bridge) {
130
134
  };
131
135
  }
132
136
  });
133
- server.tool("figjam_create_connector", "Draws a connector (arrow/line) between two existing nodes by their IDs. LAYOUT RULES: Always use style=elbowed (default) — elbowed connectors route around nodes better than curved. For fan-out from a hub node, place the hub far enough left/right that |dx| > |dy| for ALL targets, forcing clean horizontal RIGHT→LEFT routing. When adding a node to an existing column, restack ALL column nodes with even 80px gaps. CONNECTOR SEMANTICS: only use connectors for data flow relationships. Configuration or contextual relationships should be annotated in the node label instead (e.g. '⚙ configures X') — connectors imply flow.", {
137
+ server.tool("figjam_create_connector", "Draws a connector (arrow/line) between two existing nodes by their IDs. LAYOUT RULES: Always use style=elbowed (default) — elbowed connectors route around nodes better than curved. BOTTOM EXIT fan-out: ALWAYS set BOTH from_magnet=BOTTOM AND to_magnet=TOP on every fan-out connector. Without to_magnet=TOP, targets with large horizontal offset (|dx|>|dy|) auto-detect LEFT/RIGHT entry instead of TOP — and even targets with small |dx| can receive side-entry when adjacent nodes are close together. Both magnets are required for a clean tree structure. CONVERGENCE (many nodes → one node): when a wide horizontal spread of nodes converge to a single target, split by approach side — nodes whose center is LEFT of the target center use to_magnet=LEFT, nodes to the RIGHT use to_magnet=RIGHT. All connectors hitting the same single TOP center causes pile-up. HUB BRANCHING TO TWO SIDE-BY-SIDE TARGETS: use from_magnet=BOTTOM + to_magnet=TOP on both — without explicit magnets, AUTO routes both from the same bottom-center point, producing a misleading double-headed arrow appearance between the targets. When adding a node to an existing column, restack ALL column nodes with even 80px gaps. Cross-layer connectors that would cut through container shapes are a layout problem — fix by repositioning nodes or using annotation instead of drawing the connector. CONNECTOR SEMANTICS: only use connectors for data flow relationships. Configuration or contextual relationships should be annotated in the node label instead (e.g. '⚙ configures X') — connectors imply flow. CONNECTOR LABELS: always label decision branch connectors with the branch condition (e.g. '2D image', 'video', '>250MB', 'Cancel', 'Confirm'). Do NOT use the connector label param — it always renders at the geometric midpoint of the path with no position control. Instead, create a floating type=shape node (rounded_rectangle, width=96, height=32, color=#ffffff, font_size=16). For BOTTOM EXIT fan-outs: left-going connectors — label_x = target_center_x + 8 (right of elbow); right-going — label_x = target_center_x - 104 (left of elbow); label_y = junction_y - 16. For nearly-vertical connectors: label_x = target_center_x - 48, label_y = (junction_y + target_top) / 2 - 16. Connector label = branch condition only; node content = outcome only — do not duplicate. Label key flow transitions to describe what changed. Labels are not needed on obvious convergence arrows where multiple paths merge to a single result node.", {
134
138
  from_id: z.string().describe("Source node ID"),
135
139
  to_id: z.string().describe("Target node ID"),
136
140
  style: z
@@ -145,6 +149,14 @@ export function registerTools(server, bridge) {
145
149
  .enum(["forward", "back", "both", "none"])
146
150
  .optional()
147
151
  .describe("Arrow direction, default: forward"),
152
+ from_magnet: z
153
+ .enum(["AUTO", "TOP", "BOTTOM", "LEFT", "RIGHT"])
154
+ .optional()
155
+ .describe("Override source connection point. AUTO = auto-detect from geometry."),
156
+ to_magnet: z
157
+ .enum(["AUTO", "TOP", "BOTTOM", "LEFT", "RIGHT"])
158
+ .optional()
159
+ .describe("Override target connection point. AUTO = auto-detect from geometry."),
148
160
  }, async (params) => {
149
161
  try {
150
162
  const result = await bridge.execute("create_connector", params);
@@ -172,6 +184,7 @@ export function registerTools(server, bridge) {
172
184
  .number()
173
185
  .optional()
174
186
  .describe("Font size in pixels (text nodes only)"),
187
+ bold: z.boolean().optional().describe("Bold text (text nodes only)."),
175
188
  }, async (params) => {
176
189
  try {
177
190
  const result = await bridge.execute("update_node", params);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-figjam",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "MCP server for live FigJam board editing from Claude Code",
5
5
  "type": "module",
6
6
  "bin": {