tabtree-mcp 1.6.0 → 1.6.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tabtree-mcp.mjs +31 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabtree-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "mcpName": "app.tabtree/tabtree",
5
5
  "description": "Let Claude read your TabTree library and build mind maps, kanban boards and whiteboards in your TabTree account — team spaces included.",
6
6
  "repository": {
package/tabtree-mcp.mjs CHANGED
@@ -672,6 +672,13 @@ function fitSize(text, w, h, kind, want){
672
672
  return { size: 9, shrunk: true, readable: false };
673
673
  }
674
674
 
675
+ // L'ordre de superposition (`z`, ≡ `zOk` de l'app) : un entier [-9, 9], 0 omis. À z égal l'app peint
676
+ // par nature — images < formes < flèches < tracés < post-it — donc un panneau coloré REMPLI recouvre
677
+ // un logo posé dedans : `z:-1` sur le panneau (ou `z:1` sur l'image) est ce qui le remet devant.
678
+ function zMcp(v){
679
+ const n = Math.round(+v);
680
+ return (v != null && isFinite(n) && n >= -9 && n <= 9 && n !== 0) ? n : undefined;
681
+ }
675
682
  function buildBoardDoc(name, elements, connections, style){
676
683
  if(!Array.isArray(elements) || !elements.length) throw new Error("`elements` must be a non-empty list.");
677
684
  const imgCount = elements.filter(e=>e && e.kind === "image").length;
@@ -689,7 +696,7 @@ function buildBoardDoc(name, elements, connections, style){
689
696
  if(w && !h) h = Math.max(24, Math.round(w / ar));
690
697
  else if(h && !w) w = Math.max(24, Math.round(h * ar));
691
698
  else if(!w && !h){ if(ar >= 1){ w = 220; h = Math.max(24, Math.round(220 / ar)); } else { h = 220; w = Math.max(24, Math.round(220 * ar)); } }
692
- return { lid: String(e.id != null ? e.id : i), kind: "image", text: "", src: im.src,
699
+ return { lid: String(e.id != null ? e.id : i), kind: "image", text: "", src: im.src, z: zMcp(e.z),
693
700
  x: Number.isFinite(+e.x) && e.x!=null ? Math.round(+e.x) : null,
694
701
  y: Number.isFinite(+e.y) && e.y!=null ? Math.round(+e.y) : null, w, h };
695
702
  }
@@ -708,7 +715,8 @@ function buildBoardDoc(name, elements, connections, style){
708
715
  // L'image sans octets sur un élément de board (2026-09-03) : même frontière que
709
716
  // pour un nœud — un id inventé retombe sur « aucune image », jamais sur du balisage.
710
717
  // Pas sur un secteur, un gradin ou un chevron : leur silhouette n'a pas de « haut ».
711
- pic: (e.pic != null && !["arc","trap","chev"].includes(kind)) ? okPic(String(e.pic)) : "" };
718
+ pic: (e.pic != null && !["arc","trap","chev"].includes(kind)) ? okPic(String(e.pic)) : "",
719
+ z: zMcp(e.z) };
712
720
  });
713
721
  const byId = new Map();
714
722
  for(const e of els){
@@ -775,10 +783,10 @@ function buildBoardDoc(name, elements, connections, style){
775
783
  for(const e of els){
776
784
  if(e.kind==="image"){
777
785
  refOf.set(e.lid, "im:" + images.length);
778
- images.push({ id: "im" + images.length, src: e.src, x: e.x, y: e.y, w: e.w, h: e.h });
786
+ images.push({ id: "im" + images.length, src: e.src, x: e.x, y: e.y, w: e.w, h: e.h, z: e.z });
779
787
  } else if(e.kind==="sticky"){
780
788
  refOf.set(e.lid, "st:" + stickies.length);
781
- stickies.push({ text:e.text, x:e.x, y:e.y, w:e.w, h:e.h, color:e.color, size:e.size||undefined, pic:e.pic||undefined });
789
+ stickies.push({ text:e.text, x:e.x, y:e.y, w:e.w, h:e.h, color:e.color, size:e.size||undefined, pic:e.pic||undefined, z:e.z });
782
790
  } else {
783
791
  refOf.set(e.lid, "sh:" + shapes.length);
784
792
  shapes.push({ kind:e.kind, x:e.x, y:e.y, w:e.w, h:e.h, color:e.kind==="text" ? "#ffffff" : e.color, text:e.text, size:e.size||undefined,
@@ -786,7 +794,8 @@ function buildBoardDoc(name, elements, connections, style){
786
794
  tk: e.tk != null ? e.tk : undefined,
787
795
  a0: e.a0 != null ? e.a0 : undefined,
788
796
  a1: e.a1 != null ? e.a1 : undefined,
789
- ir: e.ir != null ? e.ir : undefined });
797
+ ir: e.ir != null ? e.ir : undefined,
798
+ z: e.z });
790
799
  }
791
800
  }
792
801
  const arrows = conns.map(c=>{
@@ -995,15 +1004,27 @@ function boardToText(doc){
995
1004
  const o = arr[i];
996
1005
  return o ? `${tag}${i} ${JSON.stringify((o.text||"(no text)").slice(0,60))}` : tag+i+" ?";
997
1006
  };
1007
+ const zs = o=>o && o.z ? ` z:${o.z}` : "";
998
1008
  lines.push(`Shapes (${(doc.shapes||[]).length}):`);
999
- (doc.shapes||[]).forEach((s,i)=>lines.push(` sh:${i} [${s.kind}] ${JSON.stringify((s.text||"").slice(0,80))} @(${s.x},${s.y}) ${s.w}×${s.h}`));
1009
+ (doc.shapes||[]).forEach((s,i)=>lines.push(` sh:${i} [${s.kind}] ${JSON.stringify((s.text||"").slice(0,80))} @(${s.x},${s.y}) ${s.w}×${s.h}${zs(s)}`));
1010
+ // Les images d'un board (2026-10-23) : le type et le poids, jamais les octets — une data URL de
1011
+ // 200 Ko dans la réponse noierait tout le reste, et Claude n'en lirait rien.
1012
+ const imgs = (doc.images||[]);
1013
+ if(imgs.length){
1014
+ lines.push(`Images (${imgs.length}):`);
1015
+ imgs.forEach((m,i)=>{
1016
+ const t = (/^data:image\/([a-z+.-]+)/i.exec(m.src||"") || [,"?"])[1].replace("svg+xml","svg");
1017
+ lines.push(` im:${i} [${t}, ${Math.round(String(m.src||"").length * 3 / 4 / 1024)} KB] @(${m.x},${m.y}) ${m.w}×${m.h}${zs(m)}`);
1018
+ });
1019
+ }
1000
1020
  if((doc.stickies||[]).length){
1001
1021
  lines.push(`Sticky notes (${doc.stickies.length}):`);
1002
1022
  doc.stickies.forEach((s,i)=>lines.push(` st:${i} ${JSON.stringify((s.text||"").slice(0,80))} @(${s.x},${s.y})`));
1003
1023
  }
1004
1024
  const named = ref=>{
1005
1025
  if(typeof ref !== "string") return null;
1006
- const m = /^(sh|st):(\d+)$/.exec(ref); if(!m) return null;
1026
+ const m = /^(sh|st|im):(\d+)$/.exec(ref); if(!m) return null;
1027
+ if(m[1]==="im") return (doc.images||[])[+m[2]] ? "im:" + m[2] + " (image)" : "im:" + m[2] + " ?";
1007
1028
  return label(m[1]==="sh" ? (doc.shapes||[]) : (doc.stickies||[]), +m[2], m[1]+":");
1008
1029
  };
1009
1030
  if((doc.arrows||[]).length){
@@ -1731,7 +1752,8 @@ const TOOLS = [
1731
1752
  a1:{ type:"number", description:"arc only — end angle in degrees (a1 > a0; a full circle is a0=0, a1=360)" },
1732
1753
  ir:{ type:"number", description:"arc only — inner-radius fraction 0..0.9 (0 = pie slice, 0.55 = donut)" },
1733
1754
  file:{ type:"string", description:"image only — full path of a PNG/JPEG/WebP/GIF/SVG file on this machine. Rasters are re-encoded to about 840 px; an SVG is kept as is (300 KB max)." },
1734
- data:{ type:"string", description:"image only — a data:image/(png|jpeg|webp|gif|svg+xml) URL instead of a file" }
1755
+ data:{ type:"string", description:"image only — a data:image/(png|jpeg|webp|gif|svg+xml) URL instead of a file" },
1756
+ z:{ type:"number", description:"Stacking order, -9..9 (default 0). At equal z the app paints images UNDER shapes, shapes under sticky notes — so a filled background panel hides a logo placed on it. Give such a panel z:-1 (or the image z:1) to keep the picture visible." }
1735
1757
  }, additionalProperties:false } },
1736
1758
  connections:{ type:"array", description:"Arrows anchored between elements (by id)", items:{ type:"object", properties:{
1737
1759
  from:{ type:"string" }, to:{ type:"string" }
@@ -2168,7 +2190,7 @@ function handle(msg){
2168
2190
  // déjà dérivé (1.0.0 ici, 1.1.0 dans le manifeste) sans que rien ne le
2169
2191
  // signale : un client affiche l'une, le registre publie l'autre. Un
2170
2192
  // autotest les compare désormais toutes les quatre.
2171
- serverInfo: { name: "tabtree", version: "1.6.0" }
2193
+ serverInfo: { name: "tabtree", version: "1.6.1" }
2172
2194
  });
2173
2195
  } else if(method === "notifications/initialized" || (method||"").startsWith("notifications/")){
2174
2196
  // notification : pas de réponse