klypix-mcp 1.18.0 → 1.18.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klypix-mcp",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "description": "An open, local-first, agent-neutral canvas file your AI reads and writes over MCP — works with Claude, Cursor, Cline, any model.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -498,13 +498,20 @@ export async function tidyBrain(buffer) {
498
498
 
499
499
  // Normalize every text card to the compact brain font (so the render matches
500
500
  // our height measure → no overlap) + cache each card's measured height.
501
+ // ALSO drop each card's `authoredInParent` anchor: the KLYPIX app freezes a
502
+ // child's in-container position in that field and RE-DERIVES the card's x/y
503
+ // from it on every render (ContainerItem group-scale) — so it would ignore
504
+ // the masonry x/y we write and snap cards back to their old single-column
505
+ // spots (the container then auto-grows to wrap them → skyscraper). Clearing
506
+ // the anchor makes THIS layout the card's authored baseline; the app re-seeds
507
+ // from our x/y. Verified against the app's render math in test/layout-cluster.
501
508
  const meta = new Map(); // id -> { h }
502
509
  for (const c of struct.cards) {
503
510
  if (c.type === 'container') continue;
504
511
  const wrapped = wrapText(String(c.text ?? ''));
505
512
  let createdAt = 0;
506
513
  const ip = `items/${shard(c.id)}/${c.id}.json`;
507
- try { const f = zip.file(ip); if (f) { const j = JSON.parse(await f.async('string')); createdAt = Number(j.createdAt) || 0; j.fontSize = G.FONT; j.content = wrapped; zip.file(ip, JSON.stringify(j)); } } catch { /* leave as-is */ }
514
+ try { const f = zip.file(ip); if (f) { const j = JSON.parse(await f.async('string')); createdAt = Number(j.createdAt) || 0; j.fontSize = G.FONT; j.content = wrapped; delete j.authoredInParent; zip.file(ip, JSON.stringify(j)); } } catch { /* leave as-is */ }
508
515
  meta.set(c.id, { h: measureCardH(wrapped), createdAt });
509
516
  }
510
517
 
@@ -738,6 +745,12 @@ export async function tidyBrain(buffer) {
738
745
  for (const kid of plans.get(cid).kids) {
739
746
  canvas.positions[kid.id] = { ...canvas.positions[kid.id], x: x + kid.dx, y: y + kid.dy, w: G.CARD_W, h: kid.h };
740
747
  }
748
+ // Drop the container's frozen group-scale baseline so the app's
749
+ // child-scaling pass early-returns (ContainerItem: `if
750
+ // (!item.authoredW) return`) and honors our masonry child x/y
751
+ // instead of scaling children off a stale authored size.
752
+ const cp = `items/${shard(cid)}/${cid}.json`;
753
+ try { const f = zip.file(cp); if (f) { const j = JSON.parse(await f.async('string')); if (j.authoredW != null || j.authoredH != null) { delete j.authoredW; delete j.authoredH; zip.file(cp, JSON.stringify(j)); } } } catch { /* leave as-is */ }
741
754
  }
742
755
  canvas.settings = { ...(canvas.settings || {}), brainLayout: 'cluster-v1' };
743
756
  }