beplus-mcp 0.35.0 → 0.37.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/index.js CHANGED
@@ -24848,7 +24848,7 @@ function hangsRight(doc, node) {
24848
24848
  return out.some((e) => doc.nodes[e.target] && FEEDS.has(kindOf(doc.nodes[e.target])));
24849
24849
  }
24850
24850
  var overlap = (a, b) => a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
24851
- function arrangeRows(doc, rows, origin, gap = DEFAULT_GAP, widths = DEFAULT_WIDTHS) {
24851
+ function arrangeRows(doc, rows, origin, gap = DEFAULT_GAP, widths = DEFAULT_WIDTHS, scope) {
24852
24852
  const placed = [];
24853
24853
  const floorOf = (node, role) => role === "other" ? node.size.w : widths[role];
24854
24854
  const place = (id, row, asTitle, x, y2, alignRight) => {
@@ -24890,7 +24890,7 @@ function arrangeRows(doc, rows, origin, gap = DEFAULT_GAP, widths = DEFAULT_WIDT
24890
24890
  for (let j = i + 1; j < placed.length; j++) if (overlap(placed[i], placed[j])) inner.push([placed[i].id, placed[j].id]);
24891
24891
  }
24892
24892
  const ids = new Set(placed.map((p) => p.id));
24893
- const others = Object.values(doc.nodes).filter((n) => !ids.has(n.id) && kindOf(n) !== "section").map((n) => ({ id: n.id, x: n.position.x, y: n.position.y, ...usedSize(n) }));
24893
+ const others = Object.values(doc.nodes).filter((n) => !ids.has(n.id) && kindOf(n) !== "section" && (scope === void 0 || scope.has(n.id))).map((n) => ({ id: n.id, x: n.position.x, y: n.position.y, ...usedSize(n) }));
24894
24894
  const outer = [];
24895
24895
  for (const p of placed) for (const o of others) if (overlap(p, o)) outer.push([p.id, o.id]);
24896
24896
  const left = Math.min(origin.x, ...placed.map((p) => p.x));
@@ -24900,6 +24900,322 @@ function arrangeRows(doc, rows, origin, gap = DEFAULT_GAP, widths = DEFAULT_WIDT
24900
24900
  return { placed, inner, outer, freeOrigin };
24901
24901
  }
24902
24902
 
24903
+ // src/canvas/tabs.ts
24904
+ var TAB_FIELD = "aba";
24905
+ var TAB_ID_FIELD = "abaId";
24906
+ var TAB_ORDER_FIELD = "abaOrdem";
24907
+ var TAB_NEAR = 2e3;
24908
+ var AVULSOS_TAB = "avulsos";
24909
+ var SECTION = "section";
24910
+ var isFiniteNumber2 = (v) => typeof v === "number" && Number.isFinite(v);
24911
+ function isArchived(node) {
24912
+ const flag = node?.data["archived"];
24913
+ return typeof flag === "object" && flag !== null;
24914
+ }
24915
+ function isSectionNode(node) {
24916
+ return node !== void 0 && node.data[KIND_FIELD] === SECTION;
24917
+ }
24918
+ function isTabSection(node) {
24919
+ return node !== void 0 && isSectionNode(node) && node.data[TAB_FIELD] === true && !isArchived(node);
24920
+ }
24921
+ var rectHas = (r, x, y) => x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
24922
+ function distanceTo(r, x, y) {
24923
+ const dx = Math.max(r.left - x, 0, x - r.right);
24924
+ const dy = Math.max(r.top - y, 0, y - r.bottom);
24925
+ return Math.hypot(dx, dy);
24926
+ }
24927
+ function boardPosition(doc, node) {
24928
+ let x = node.position.x;
24929
+ let y = node.position.y;
24930
+ const seen = /* @__PURE__ */ new Set([node.id]);
24931
+ let parent = node.parentId === null ? void 0 : doc.nodes[node.parentId];
24932
+ while (parent !== void 0 && !seen.has(parent.id)) {
24933
+ seen.add(parent.id);
24934
+ x += parent.position.x;
24935
+ y += parent.position.y;
24936
+ parent = parent.parentId === null ? void 0 : doc.nodes[parent.parentId];
24937
+ }
24938
+ return { x, y };
24939
+ }
24940
+ function sectionRect(doc, node) {
24941
+ const at = boardPosition(doc, node);
24942
+ return { left: at.x, top: at.y, right: at.x + node.size.w, bottom: at.y + node.size.h };
24943
+ }
24944
+ function savedMeasure(node) {
24945
+ const value = node.data[MEASURED_KEY];
24946
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
24947
+ const record2 = value;
24948
+ if (!isFiniteNumber2(record2["w"]) || !isFiniteNumber2(record2["h"])) return void 0;
24949
+ return { w: record2["w"], h: record2["h"] };
24950
+ }
24951
+ function drawnRect(doc, node) {
24952
+ const at = boardPosition(doc, node);
24953
+ const saved = savedMeasure(node);
24954
+ const w = Math.max(node.size.w, saved?.w ?? 0);
24955
+ const h = Math.max(node.size.h, saved?.h ?? 0);
24956
+ return { left: at.x, top: at.y, right: at.x + w, bottom: at.y + h };
24957
+ }
24958
+ function savedCenter(doc, node) {
24959
+ const at = boardPosition(doc, node);
24960
+ const saved = savedMeasure(node);
24961
+ const w = Math.max(node.size.w, saved?.w ?? 0);
24962
+ const h = Math.max(node.size.h, saved?.h ?? 0);
24963
+ return { x: at.x + w / 2, y: at.y + h / 2 };
24964
+ }
24965
+ function tabOrderOf(node) {
24966
+ const raw = node.data[TAB_ORDER_FIELD];
24967
+ return typeof raw === "number" && Number.isFinite(raw) ? raw : null;
24968
+ }
24969
+ function orderedTabSections(doc) {
24970
+ return Object.values(doc.nodes).filter((node) => isTabSection(node)).sort((a, b) => {
24971
+ const oa = tabOrderOf(a);
24972
+ const ob = tabOrderOf(b);
24973
+ if (oa !== null && ob === null) return -1;
24974
+ if (oa === null && ob !== null) return 1;
24975
+ if (oa !== null && ob !== null && oa !== ob) return oa - ob;
24976
+ return a.position.y - b.position.y || a.position.x - b.position.x;
24977
+ });
24978
+ }
24979
+ var partitions = /* @__PURE__ */ new WeakMap();
24980
+ function build(doc) {
24981
+ const tabs = orderedTabSections(doc).map((node) => ({
24982
+ id: node.id,
24983
+ ...sectionRect(doc, node)
24984
+ }));
24985
+ const live = new Map(tabs.map((tab) => [tab.id, tab]));
24986
+ const home = /* @__PURE__ */ new Map();
24987
+ const walking = /* @__PURE__ */ new Set();
24988
+ const byPlace = (node) => {
24989
+ const center = savedCenter(doc, node);
24990
+ for (const tab of tabs) if (rectHas(tab, center.x, center.y)) return tab.id;
24991
+ let near = null;
24992
+ let best = TAB_NEAR;
24993
+ for (const tab of tabs) {
24994
+ const distance = distanceTo(tab, center.x, center.y);
24995
+ if (distance <= best && (near === null || distance < best)) {
24996
+ near = tab;
24997
+ best = distance;
24998
+ }
24999
+ }
25000
+ return near === null ? AVULSOS_TAB : near.id;
25001
+ };
25002
+ const resolve3 = (node) => {
25003
+ const known = home.get(node.id);
25004
+ if (known !== void 0) return known;
25005
+ let tab;
25006
+ const mark = node.data[TAB_ID_FIELD];
25007
+ if (live.has(node.id)) {
25008
+ tab = node.id;
25009
+ } else if (typeof mark === "string" && mark !== "") {
25010
+ tab = live.has(mark) ? mark : AVULSOS_TAB;
25011
+ } else if (node.parentId !== null && !walking.has(node.id)) {
25012
+ const parent = doc.nodes[node.parentId];
25013
+ if (parent === void 0) {
25014
+ tab = byPlace(node);
25015
+ } else {
25016
+ walking.add(node.id);
25017
+ tab = resolve3(parent);
25018
+ walking.delete(node.id);
25019
+ }
25020
+ } else {
25021
+ tab = byPlace(node);
25022
+ }
25023
+ home.set(node.id, tab);
25024
+ return tab;
25025
+ };
25026
+ const members = /* @__PURE__ */ new Map();
25027
+ for (const node of Object.values(doc.nodes)) {
25028
+ if (isArchived(node)) continue;
25029
+ const tab = resolve3(node);
25030
+ const list = members.get(tab);
25031
+ if (list === void 0) members.set(tab, [node.id]);
25032
+ else list.push(node.id);
25033
+ }
25034
+ return { tabs, home, members };
25035
+ }
25036
+ function partitionOf(doc) {
25037
+ let part = partitions.get(doc);
25038
+ if (part === void 0) {
25039
+ part = build(doc);
25040
+ partitions.set(doc, part);
25041
+ }
25042
+ return part;
25043
+ }
25044
+
25045
+ // src/canvas/tab-scope.ts
25046
+ var LOCK_FIELD = "travada";
25047
+ var TAB_END_GAP = 120;
25048
+ var TAB_GROW_PAD = 48;
25049
+ var TAB_TOP_ROOM = 96;
25050
+ var TAB_CLEAR_GAP = 48;
25051
+ var SHORT_ID = 8;
25052
+ var MIN_PREFIX = 4;
25053
+ var LISTED = 12;
25054
+ var oneLine = (text, max) => {
25055
+ const flat = text.replace(/\s+/g, " ").trim();
25056
+ return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
25057
+ };
25058
+ function tabTitle(node) {
25059
+ const title = typeof node.data.title === "string" ? node.data.title.trim() : "";
25060
+ const label = typeof node.data.label === "string" ? node.data.label.trim() : "";
25061
+ if (title !== "" && title !== "Se\xE7\xE3o") return title;
25062
+ return label || title || "Se\xE7\xE3o";
25063
+ }
25064
+ var isLockedTab = (node) => node !== void 0 && node.data[LOCK_FIELD] === true;
25065
+ function shortTabIds(ids) {
25066
+ const longest = Math.max(0, ...ids.map((id) => id.length));
25067
+ let length = SHORT_ID;
25068
+ while (length < longest && new Set(ids.map((id) => id.slice(0, length))).size < ids.length) length += 1;
25069
+ return new Map(ids.map((id) => [id, id.slice(0, length)]));
25070
+ }
25071
+ function tabIndex(doc) {
25072
+ const part = partitionOf(doc);
25073
+ const shorts = shortTabIds(part.tabs.map((tab) => tab.id));
25074
+ const tabs = part.tabs.map((tab) => {
25075
+ const node = doc.nodes[tab.id];
25076
+ return {
25077
+ id: tab.id,
25078
+ short: shorts.get(tab.id),
25079
+ title: tabTitle(node),
25080
+ locked: isLockedTab(node),
25081
+ cards: Math.max(0, (part.members.get(tab.id)?.length ?? 0) - 1)
25082
+ };
25083
+ });
25084
+ const byId = new Map(tabs.map((tab) => [tab.id, tab]));
25085
+ return {
25086
+ tabs,
25087
+ loose: part.members.get(AVULSOS_TAB)?.length ?? 0,
25088
+ label: (tab) => tab === AVULSOS_TAB ? AVULSOS_TAB : byId.get(tab)?.short ?? tab,
25089
+ info: (tab) => byId.get(tab)
25090
+ };
25091
+ }
25092
+ var hasTabs = (doc) => partitionOf(doc).tabs.length > 0;
25093
+ function homeTabOf(doc, nodeId3) {
25094
+ const part = partitionOf(doc);
25095
+ return part.tabs.length === 0 ? null : part.home.get(nodeId3) ?? null;
25096
+ }
25097
+ function membersOfTab(doc, tab) {
25098
+ return new Set(partitionOf(doc).members.get(tab) ?? []);
25099
+ }
25100
+ function tabList(doc) {
25101
+ const tabs = partitionOf(doc).tabs;
25102
+ if (tabs.length === 0) return "Este board n\xE3o tem abas (uma aba \xE9 uma se\xE7\xE3o com aba=true).";
25103
+ const shown = tabs.slice(0, LISTED).map((tab) => `"${oneLine(tabTitle(doc.nodes[tab.id]), 40)}" (${tab.id})`);
25104
+ const more = tabs.length > LISTED ? `, \u2026 e mais ${tabs.length - LISTED}` : "";
25105
+ return `Abas do board: ${shown.join(", ")}${more}.`;
25106
+ }
25107
+ function kindLabel(node) {
25108
+ const kind = node.data.kind;
25109
+ return typeof kind === "string" && kind ? kind : node.type;
25110
+ }
25111
+ function resolveTab(doc, ref, opts = {}) {
25112
+ const value = ref.trim();
25113
+ if (value === AVULSOS_TAB) {
25114
+ if (opts.allowAvulsos && hasTabs(doc)) return AVULSOS_TAB;
25115
+ throw new CanvasToolError(
25116
+ hasTabs(doc) ? '"avulsos" n\xE3o \xE9 uma aba: \xE9 onde ficam os cart\xF5es que n\xE3o moram em aba. Para criar sem aba, omita aba.' : `Aba "avulsos" n\xE3o existe. ${tabList(doc)}`
25117
+ );
25118
+ }
25119
+ if (Object.hasOwn(doc.nodes, value)) {
25120
+ const node = doc.nodes[value];
25121
+ if (isTabSection(node)) return node.id;
25122
+ const name = `"${oneLine(tabTitle(node), 40)}"`;
25123
+ if (isSectionNode(node) && node.data[TAB_FIELD] === true && isArchived(node)) {
25124
+ throw new CanvasToolError(
25125
+ `A aba ${name} (${value}) est\xE1 arquivada: os cart\xF5es que apontam para ela ficam em Avulsos. Escolha outra aba. ${tabList(doc)}`
25126
+ );
25127
+ }
25128
+ throw new CanvasToolError(
25129
+ `${value} \xE9 ${isSectionNode(node) ? "uma se\xE7\xE3o comum" : `um n\xF3 ${kindLabel(node)}`} (${name}), n\xE3o uma aba. Aba \xE9 a se\xE7\xE3o com aba=true. ${tabList(doc)}`
25130
+ );
25131
+ }
25132
+ if (value.length >= MIN_PREFIX) {
25133
+ const hits = partitionOf(doc).tabs.filter((tab) => tab.id.startsWith(value));
25134
+ if (hits.length === 1) return hits[0].id;
25135
+ if (hits.length > 1) {
25136
+ const names = hits.slice(0, LISTED).map((tab) => `"${oneLine(tabTitle(doc.nodes[tab.id]), 40)}" (${tab.id})`);
25137
+ throw new CanvasToolError(`"${value}" serve a mais de uma aba: ${names.join(", ")}. Use o id inteiro.`);
25138
+ }
25139
+ }
25140
+ throw new CanvasToolError(`Aba "${oneLine(value, 60)}" n\xE3o existe neste board. ${tabList(doc)}`);
25141
+ }
25142
+ function unionOf(rects) {
25143
+ let out = null;
25144
+ for (const r of rects) {
25145
+ out = out === null ? { ...r } : {
25146
+ left: Math.min(out.left, r.left),
25147
+ top: Math.min(out.top, r.top),
25148
+ right: Math.max(out.right, r.right),
25149
+ bottom: Math.max(out.bottom, r.bottom)
25150
+ };
25151
+ }
25152
+ return out;
25153
+ }
25154
+ function endOfTabContent(doc, tabId, size) {
25155
+ const part = partitionOf(doc);
25156
+ const section = doc.nodes[tabId];
25157
+ const boxes = (part.members.get(tabId) ?? []).flatMap((id) => {
25158
+ const node = doc.nodes[id];
25159
+ return id === tabId || node === void 0 || isArchived(node) ? [] : [drawnRect(doc, node)];
25160
+ });
25161
+ const content = unionOf(boxes);
25162
+ const frame = sectionRect(doc, section);
25163
+ const spot = content === null ? { x: frame.left + TAB_GROW_PAD, y: frame.top + TAB_TOP_ROOM } : { x: content.left, y: content.bottom + TAB_END_GAP };
25164
+ const others = part.tabs.filter((tab) => tab.id !== tabId);
25165
+ const cx = spot.x + size.w / 2;
25166
+ let y = spot.y;
25167
+ for (let guard = 0; guard <= others.length; guard += 1) {
25168
+ const hit = others.find((rect) => rectHas(rect, cx, y + size.h / 2));
25169
+ if (hit === void 0) break;
25170
+ y = hit.bottom + TAB_CLEAR_GAP;
25171
+ }
25172
+ return { x: Math.round(spot.x), y: Math.round(y) };
25173
+ }
25174
+ function placementNote(doc, node) {
25175
+ if (isTabSection(node)) return "";
25176
+ const after = { ...doc, nodes: { ...doc.nodes, [node.id]: node } };
25177
+ const index = tabIndex(after);
25178
+ if (index.tabs.length === 0) return "";
25179
+ const home = partitionOf(after).home.get(node.id);
25180
+ if (home === void 0) return "";
25181
+ const info = index.info(home);
25182
+ if (info === void 0) {
25183
+ return " Aten\xE7\xE3o: ficou em Avulsos (nenhuma aba o segura); passe aba=<id da aba> para ele morar numa aba.";
25184
+ }
25185
+ return ` Aba: "${oneLine(info.title, 40)}" (aba=${info.short}).`;
25186
+ }
25187
+ function flatten(ops) {
25188
+ return ops.flatMap((op) => op.type === "batch" ? flatten(op.ops) : [op]);
25189
+ }
25190
+ function badAbaId(doc, value) {
25191
+ if (value === null) return null;
25192
+ if (typeof value !== "string") return `data.${TAB_ID_FIELD} precisa ser o id (texto) de uma aba, ou null para tirar o campo.`;
25193
+ if (Object.hasOwn(doc.nodes, value) && isTabSection(doc.nodes[value])) return null;
25194
+ let hint;
25195
+ try {
25196
+ const full = resolveTab(doc, value);
25197
+ hint = `Use o id inteiro: ${full}.`;
25198
+ } catch (e) {
25199
+ hint = e instanceof Error ? e.message : "";
25200
+ }
25201
+ return `data.${TAB_ID_FIELD} "${oneLine(value, 60)}" n\xE3o \xE9 o id de uma aba viva. ${hint} Para tirar o campo, mande null.`;
25202
+ }
25203
+ function abaIdViolation(doc, ops) {
25204
+ for (const op of flatten(ops)) {
25205
+ if (op.type === "node.add") {
25206
+ const value = op.node.data[TAB_ID_FIELD];
25207
+ if (value !== void 0) {
25208
+ const bad = badAbaId(doc, value);
25209
+ if (bad) return bad;
25210
+ }
25211
+ } else if (op.type === "node.set" && op.path === `data.${TAB_ID_FIELD}` && op.value.kind === "value") {
25212
+ const bad = badAbaId(doc, op.value.value);
25213
+ if (bad) return bad;
25214
+ }
25215
+ }
25216
+ return null;
25217
+ }
25218
+
24903
25219
  // src/canvas/summary.ts
24904
25220
  var MAX_TEXT = 160;
24905
25221
  var MAX_BLOCKS = 40;
@@ -24931,7 +25247,7 @@ function visibleData(data) {
24931
25247
  return rest;
24932
25248
  }
24933
25249
  var NAME_KEYS = /* @__PURE__ */ new Set([KIND_FIELD, "label", "nodeTitle"]);
24934
- function oneLine(text, max = MAX_TEXT) {
25250
+ function oneLine2(text, max = MAX_TEXT) {
24935
25251
  const flat = text.replace(/\s+/g, " ").trim();
24936
25252
  return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
24937
25253
  }
@@ -24950,7 +25266,7 @@ function scalar(v) {
24950
25266
  if (typeof v === "string") {
24951
25267
  if (v.startsWith("data:")) return "[data-url]";
24952
25268
  if (/^https?:\/\//.test(v)) return "[url]";
24953
- return JSON.stringify(oneLine(v, 60));
25269
+ return JSON.stringify(oneLine2(v, 60));
24954
25270
  }
24955
25271
  if (typeof v === "number" || typeof v === "boolean" || v === null) return String(v);
24956
25272
  return null;
@@ -24959,7 +25275,7 @@ function blocksLines(blocks) {
24959
25275
  if (!Array.isArray(blocks)) return [];
24960
25276
  const out = blocks.slice(0, MAX_BLOCKS).map((b) => {
24961
25277
  const rec = b ?? {};
24962
- return ` \xB7 ${String(rec.id ?? "?")}: ${oneLine(String(rec.text ?? ""), 120)}`;
25278
+ return ` \xB7 ${String(rec.id ?? "?")}: ${oneLine2(String(rec.text ?? ""), 120)}`;
24963
25279
  });
24964
25280
  if (blocks.length > MAX_BLOCKS) out.push(` \xB7 \u2026 +${blocks.length - MAX_BLOCKS} blocos`);
24965
25281
  return out;
@@ -24972,21 +25288,22 @@ function rowsLines(rows) {
24972
25288
  const ideas = Array.isArray(rec.ideas) ? rec.ideas : [];
24973
25289
  const ideaText = ideas.slice(0, 6).map((i) => {
24974
25290
  const idea = i ?? {};
24975
- return `${String(idea.id ?? "?")}="${oneLine(String(idea.text ?? ""), 60)}"`;
25291
+ return `${String(idea.id ?? "?")}="${oneLine2(String(idea.text ?? ""), 60)}"`;
24976
25292
  }).join("; ");
24977
25293
  out.push(` \xB7 linha ${String(rec.id ?? "?")} (bloco ${String(rec.narrationBlockId ?? "?")}): ${ideas.length} ideia(s) ${ideaText}`);
24978
25294
  }
24979
25295
  if (rows.length > MAX_BLOCKS) out.push(` \xB7 \u2026 +${rows.length - MAX_BLOCKS} linhas`);
24980
25296
  return out;
24981
25297
  }
24982
- function nodeLines(node, run) {
25298
+ function nodeLines(node, run, aba) {
24983
25299
  const kind = nodeKind(node);
24984
25300
  const spec = kind ? KINDS[kind] : void 0;
24985
- const head = `- ${node.id} \xB7 ${kind ?? node.type} \xB7 "${oneLine(nodeName(node), 60)}" @(${Math.round(node.position.x)},${Math.round(node.position.y)}) ${sizeLabel(node)}`;
25301
+ const head = `- ${node.id} \xB7 ${kind ?? node.type} \xB7 "${oneLine2(nodeName(node), 60)}" @(${Math.round(node.position.x)},${Math.round(node.position.y)}) ${sizeLabel(node)}` + (aba ? ` \xB7 aba=${aba}` : "");
24986
25302
  const facts = [];
24987
25303
  const extra = [];
24988
25304
  const d = node.data;
24989
25305
  if (d.archived) facts.push("ARQUIVADO");
25306
+ if (aba && isTabSection(node)) facts.push("\xE9 aba");
24990
25307
  const approval = d.approval?.state;
24991
25308
  if (typeof approval === "string") facts.push(`aprova\xE7\xE3o: ${approval}`);
24992
25309
  if (Array.isArray(d.history)) facts.push(`${d.history.length} gera\xE7\xE3o(\xF5es)`);
@@ -25007,10 +25324,11 @@ function nodeLines(node, run) {
25007
25324
  if (fact) facts.push(fact);
25008
25325
  }
25009
25326
  const main2 = spec?.main;
25010
- if (main2 && typeof d[main2] === "string" && d[main2]) facts.push(`${main2}: "${oneLine(d[main2])}"`);
25327
+ if (main2 && typeof d[main2] === "string" && d[main2]) facts.push(`${main2}: "${oneLine2(d[main2])}"`);
25011
25328
  for (const [key, value] of Object.entries(d)) {
25012
25329
  if (NAME_KEYS.has(key) || HEAVY_KEYS.has(key) || key === main2) continue;
25013
25330
  if (["blocks", "rows", "items", "archived", "approval"].includes(key)) continue;
25331
+ if (aba && (key === TAB_ID_FIELD || key === TAB_FIELD && isTabSection(node))) continue;
25014
25332
  const s = scalar(value);
25015
25333
  if (s !== null) facts.push(`${key}=${s}`);
25016
25334
  else if (Array.isArray(value)) facts.push(`${key}=[${value.length}]`);
@@ -25032,27 +25350,63 @@ function timelineFact(d) {
25032
25350
  const clips = seq.audioClips && typeof seq.audioClips === "object" && !Array.isArray(seq.audioClips) ? Object.values(seq.audioClips).reduce((t, list) => t + (Array.isArray(list) ? list.length : 0), 0) : 0;
25033
25351
  return `sequ\xEAncia (legado, sem data.timeline): ${plans} plano(s), ${clips} clipe(s) de \xE1udio, ${JSON.stringify(seq).length} caracteres (inteira em canvas_read_node sem path; ${volume})`;
25034
25352
  }
25035
- function boardSummary(snap) {
25353
+ var cardsText = (n) => n === 1 ? "1 cart\xE3o" : `${n} cart\xF5es`;
25354
+ function boardSummary(snap, opts = {}) {
25036
25355
  const doc = snap.doc;
25037
- const nodes = Object.values(doc.nodes).sort((a, b) => a.position.x - b.position.x || a.position.y - b.position.y);
25038
- const edges = Object.values(doc.edges);
25356
+ const part = partitionOf(doc);
25357
+ const index = tabIndex(doc);
25358
+ const tabbed = index.tabs.length > 0;
25359
+ const scope = opts.tab === void 0 ? null : new Set(part.members.get(opts.tab) ?? []);
25360
+ const everyNode = Object.values(doc.nodes).sort((a, b) => a.position.x - b.position.x || a.position.y - b.position.y);
25361
+ const everyEdge = Object.values(doc.edges);
25362
+ const nodes = scope ? everyNode.filter((n) => scope.has(n.id)) : everyNode;
25363
+ const edges = scope ? everyEdge.filter((e) => scope.has(e.source) || scope.has(e.target)) : everyEdge;
25039
25364
  const runs = new Map(snap.runs.map((r) => [r.nodeId, r]));
25040
- const lines = [
25041
- `Board "${snap.title}" \xB7 id ${snap.id} \xB7 seq ${snap.seq} \xB7 seu papel: ${snap.role} \xB7 ${nodes.length} n\xF3(s) \xB7 ${edges.length} liga\xE7\xE3o(\xF5es)`
25042
- ];
25365
+ const who2 = `Board "${snap.title}" \xB7 id ${snap.id} \xB7 seq ${snap.seq} \xB7 seu papel: ${snap.role}`;
25366
+ const lines = [];
25367
+ if (scope === null) {
25368
+ lines.push(`${who2} \xB7 ${nodes.length} n\xF3(s) \xB7 ${edges.length} liga\xE7\xE3o(\xF5es)`);
25369
+ } else {
25370
+ const external = edges.filter((e) => !(scope.has(e.source) && scope.has(e.target))).length;
25371
+ const info = index.info(opts.tab);
25372
+ const name = info ? `a aba "${oneLine2(info.title, 60)}" (aba=${info.short})` : "os avulsos (aba=avulsos)";
25373
+ lines.push(
25374
+ `${who2} \xB7 s\xF3 ${name}: ${nodes.length} n\xF3(s) \xB7 ${edges.length} liga\xE7\xE3o(\xF5es)${external ? `, ${external} externa(s)` : ""}`,
25375
+ `O board inteiro tem ${everyNode.length} n\xF3(s) e ${everyEdge.length} liga\xE7\xE3o(\xF5es); leia sem aba para ver tudo.`
25376
+ );
25377
+ }
25043
25378
  const ialab = doc.meta?.ialab;
25044
25379
  if (ialab === null) lines.push("IA Lab: sem v\xEDnculo (gera\xE7\xF5es saem dos diamantes de quem roda).");
25045
25380
  else if (ialab && typeof ialab === "object") {
25046
25381
  const label = ["code", "name", "projectCode", "projectName"].map((k) => ialab[k]).find((v) => typeof v === "string");
25047
25382
  lines.push(`IA Lab: vinculado${label ? ` a ${String(label)}` : ""}.`);
25048
25383
  }
25049
- if (doc.meta?.guide) lines.push(`Guia: ${oneLine(doc.meta.guide)}`);
25384
+ if (doc.meta?.guide) lines.push(`Guia: ${oneLine2(doc.meta.guide)}`);
25385
+ if (tabbed) {
25386
+ lines.push("", `Abas (${index.tabs.length}), cart\xF5es de cada uma sem contar a se\xE7\xE3o (canvas_read_board com aba=<id> l\xEA uma s\xF3):`);
25387
+ for (const t of index.tabs) {
25388
+ const id = t.id === t.short ? "" : ` \xB7 id ${t.id}`;
25389
+ lines.push(`- aba=${t.short} \xB7 "${oneLine2(t.title, 60)}" \xB7 ${cardsText(t.cards)}${t.locked ? " \xB7 travada" : ""}${id}`);
25390
+ }
25391
+ if (index.loose) lines.push(`- aba=${AVULSOS_TAB} \xB7 ${cardsText(index.loose)} (n\xE3o moram em aba)`);
25392
+ }
25050
25393
  lines.push("", "N\xF3s (tamanho: medido = como est\xE1 na tela; gravado = piso, o cart\xE3o pode ser maior):");
25051
25394
  if (!nodes.length) lines.push("(vazio)");
25052
- for (const n of nodes) lines.push(...nodeLines(n, runs.get(n.id)));
25395
+ for (const n of nodes) {
25396
+ const home = tabbed ? part.home.get(n.id) : void 0;
25397
+ lines.push(...nodeLines(n, runs.get(n.id), home === void 0 ? void 0 : index.label(home)));
25398
+ }
25053
25399
  lines.push("", "Liga\xE7\xF5es:");
25054
25400
  if (!edges.length) lines.push("(nenhuma)");
25055
- for (const e of edges) lines.push(`- ${e.id}: ${e.source}.${e.sourcePort} \u2192 ${e.target}.${e.targetPort}`);
25401
+ for (const e of edges) {
25402
+ let note = "";
25403
+ if (scope && !(scope.has(e.source) && scope.has(e.target))) {
25404
+ const far = part.home.get(scope.has(e.source) ? e.target : e.source);
25405
+ const farInfo = far === void 0 ? void 0 : index.info(far);
25406
+ note = far === void 0 ? " \xB7 externo (o outro lado \xE9 um n\xF3 arquivado ou ausente)" : farInfo ? ` \xB7 externo (outro lado: aba "${oneLine2(farInfo.title, 40)}" aba=${farInfo.short})` : ` \xB7 externo (outro lado: ${AVULSOS_TAB})`;
25407
+ }
25408
+ lines.push(`- ${e.id}: ${e.source}.${e.sourcePort} \u2192 ${e.target}.${e.targetPort}${note}`);
25409
+ }
25056
25410
  return lines.join("\n");
25057
25411
  }
25058
25412
  function nodeDetail(node) {
@@ -25120,7 +25474,7 @@ function changesSince(envelopes, doc) {
25120
25474
  count.set(key, (count.get(key) ?? 0) + 1);
25121
25475
  }
25122
25476
  const parts = [...count].map(([t, n]) => `${n}\xD7 ${t}`);
25123
- const names = [...touchedNodes(envelopes)].map((id) => doc.nodes[id] ? `"${oneLine(nodeName(doc.nodes[id]), 40)}"` : `${id} (apagado)`).slice(0, 8);
25477
+ const names = [...touchedNodes(envelopes)].map((id) => doc.nodes[id] ? `"${oneLine2(nodeName(doc.nodes[id]), 40)}"` : `${id} (apagado)`).slice(0, 8);
25124
25478
  return `${envelopes.length} opera\xE7\xE3o(\xF5es) de outras pessoas desde a sua leitura (${parts.join(", ")})` + (names.length ? `; n\xF3s tocados: ${names.join(", ")}` : "");
25125
25479
  }
25126
25480
  function touchedNodes(envelopes) {
@@ -25151,7 +25505,7 @@ function mmss(t) {
25151
25505
  function nodeRef(doc, nodeId3) {
25152
25506
  const node = doc.nodes[nodeId3];
25153
25507
  const kind = nodeKind(node) ?? node.type;
25154
- return `"${oneLine(nodeName(node), 60)}" (${kind}, ${node.id})`;
25508
+ return `"${oneLine2(nodeName(node), 60)}" (${kind}, ${node.id})`;
25155
25509
  }
25156
25510
  function hashString(text) {
25157
25511
  let h1 = 3735928559;
@@ -25208,7 +25562,7 @@ function describeAnchor(anchor, doc) {
25208
25562
  const t = mmss(Number(anchor.t) || 0);
25209
25563
  if (kind === "montage") {
25210
25564
  const name = nodeName(node);
25211
- const title = name === KINDS.montage.title ? "" : ` "${oneLine(name, 60)}"`;
25565
+ const title = name === KINDS.montage.title ? "" : ` "${oneLine2(name, 60)}"`;
25212
25566
  return { text: `em ${t} da Montagem${title} (${node.id})`, nodeId: anchor.nodeId };
25213
25567
  }
25214
25568
  return { text: `em ${t} de ${ref}`, nodeId: anchor.nodeId };
@@ -25243,11 +25597,11 @@ function threadLines(t, doc) {
25243
25597
  const where = describeAnchor(t.anchor, doc);
25244
25598
  const state = t.resolvedAt ? " \xB7 RESOLVIDO" : "";
25245
25599
  const reacts = reactionsText(t.reactions);
25246
- const lines = t.deleted ? [`- ${t.id} \xB7 ${where.text}${state}`, " (coment\xE1rio raiz apagado: n\xE3o d\xE1 para responder nem resolver)"] : [`- ${t.id} \xB7 ${where.text} \xB7 ${who(t)}${state}${reacts ? ` \xB7 ${reacts}` : ""}`, ` pedido: ${oneLine(t.body ?? "", 400)}`];
25600
+ const lines = t.deleted ? [`- ${t.id} \xB7 ${where.text}${state}`, " (coment\xE1rio raiz apagado: n\xE3o d\xE1 para responder nem resolver)"] : [`- ${t.id} \xB7 ${where.text} \xB7 ${who(t)}${state}${reacts ? ` \xB7 ${reacts}` : ""}`, ` pedido: ${oneLine2(t.body ?? "", 400)}`];
25247
25601
  if (where.gone && !t.deleted) lines.push(` \u26A0 ALVO SUMIU: ${where.gone}. Responda com pergunta, n\xE3o resolva.`);
25248
25602
  const replies = Array.isArray(t.replies) ? t.replies : [];
25249
25603
  for (const r of replies.slice(-MAX_REPLIES)) {
25250
- lines.push(r.deleted ? " \u21B3 (resposta apagada)" : ` \u21B3 ${who(r)}: ${oneLine(r.body ?? "", 140)}`);
25604
+ lines.push(r.deleted ? " \u21B3 (resposta apagada)" : ` \u21B3 ${who(r)}: ${oneLine2(r.body ?? "", 140)}`);
25251
25605
  }
25252
25606
  if (replies.length > MAX_REPLIES) lines.splice(lines.length - MAX_REPLIES, 0, ` \u21B3 \u2026 ${replies.length - MAX_REPLIES} resposta(s) antes`);
25253
25607
  return lines;
@@ -25327,7 +25681,7 @@ Para aplicar: leia o n\xF3 com canvas_read_board (node_ids), edite pelas canvas_
25327
25681
  () => api.createComment(board_id, { body: text, parentId: comment_id, ...m?.length ? { mentions: m } : {} })
25328
25682
  );
25329
25683
  const open3 = thread?.resolvedAt ? "A thread est\xE1 resolvida; reabra com canvas_reopen_comment se precisar." : "A thread segue aberta.";
25330
- return `\u2705 Resposta ${newReplyId(thread)} em ${comment_id}: "${oneLine(text, 120)}". ${open3}`;
25684
+ return `\u2705 Resposta ${newReplyId(thread)} em ${comment_id}: "${oneLine2(text, 120)}". ${open3}`;
25331
25685
  })
25332
25686
  );
25333
25687
  server.registerTool(
@@ -25350,10 +25704,10 @@ Para aplicar: leia o n\xF3 com canvas_read_board (node_ids), edite pelas canvas_
25350
25704
  } catch (e) {
25351
25705
  const why = e instanceof Error ? e.message : String(e);
25352
25706
  throw new CanvasToolError(
25353
- `A resposta "${oneLine(done, 80)}" (${replyId}) foi postada em ${comment_id}, mas resolver falhou: ${why} N\xE3o repita canvas_resolve_comment (postaria a resposta de novo): avise a pessoa para resolver no Canvas.`
25707
+ `A resposta "${oneLine2(done, 80)}" (${replyId}) foi postada em ${comment_id}, mas resolver falhou: ${why} N\xE3o repita canvas_resolve_comment (postaria a resposta de novo): avise a pessoa para resolver no Canvas.`
25354
25708
  );
25355
25709
  }
25356
- return `\u2705 Resolvido ${comment_id}: "${oneLine(done, 120)}" (resposta ${replyId}).`;
25710
+ return `\u2705 Resolvido ${comment_id}: "${oneLine2(done, 120)}" (resposta ${replyId}).`;
25357
25711
  })
25358
25712
  );
25359
25713
  server.registerTool(
@@ -25367,7 +25721,7 @@ Para aplicar: leia o n\xF3 com canvas_read_board (node_ids), edite pelas canvas_
25367
25721
  async ({ board_id, comment_id, why }) => run(async () => {
25368
25722
  await onComment(() => api.reopenComment(board_id, comment_id));
25369
25723
  if (why) await onComment(() => api.createComment(board_id, { body: why, parentId: comment_id }));
25370
- return `\u2705 Reaberto ${comment_id}${why ? ` com a resposta "${oneLine(why, 120)}"` : ""}.`;
25724
+ return `\u2705 Reaberto ${comment_id}${why ? ` com a resposta "${oneLine2(why, 120)}"` : ""}.`;
25371
25725
  })
25372
25726
  );
25373
25727
  server.registerTool(
@@ -25422,6 +25776,13 @@ Para aplicar: leia o n\xF3 com canvas_read_board (node_ids), edite pelas canvas_
25422
25776
  );
25423
25777
  }
25424
25778
 
25779
+ // src/tools/canvas-aba-arg.ts
25780
+ var abaArg = (what) => external_exports.string().trim().min(1).max(128).optional().describe(what);
25781
+ var ABA_ADD_NODE = "Aba onde o cart\xE3o mora: o id da se\xE7\xE3o da aba ou o come\xE7o \xFAnico dele (o aba=\u2026 da leitura). Grava data.abaId e, sem position, o cart\xE3o nasce no fim do conte\xFAdo da aba (abaixo do cart\xE3o mais baixo, alinhado \xE0 esquerda do conte\xFAdo). data.abaId vale como aba; se os dois divergirem, recusa. Aba que n\xE3o existe ou que n\xE3o \xE9 aba \xE9 recusada.";
25782
+ var ABA_READ_BOARD = 'L\xEA s\xF3 uma aba: a se\xE7\xE3o, quem mora nela e os fios que tocam nesses n\xF3s (o fio que vai a outra aba vem marcado como externo). Aceita o id da se\xE7\xE3o, o come\xE7o \xFAnico dele (o aba=\u2026 da leitura) ou "avulsos". Sem aba, l\xEA o board inteiro.';
25783
+ var ABA_UPLOAD_MEDIA = "Aba do n\xF3 M\xEDdia novo (id da se\xE7\xE3o da aba ou o come\xE7o \xFAnico dele): grava data.abaId e p\xF5e o n\xF3 no fim do conte\xFAdo da aba, em vez de cair \xE0 direita de tudo, em Avulsos. S\xF3 vale com board_id e sem node_id (n\xF3 que j\xE1 existe n\xE3o muda de aba).";
25784
+ var ABA_ARRANGE_ROWS = 'Aba onde conferir a sobreposi\xE7\xE3o (id da se\xE7\xE3o ou o come\xE7o \xFAnico dele): s\xF3 os cart\xF5es dela contam como "de fora". Sem aba, vale a aba dos n\xF3s arrumados, quando \xE9 uma s\xF3; sen\xE3o, o board inteiro. N\xE3o muda a aba de nenhum n\xF3.';
25785
+
25425
25786
  // src/tools/canvas-layout.ts
25426
25787
  var nodeIdArg = external_exports.string().min(1).max(128);
25427
25788
  var width = external_exports.number().finite().min(40).max(2e4);
@@ -25461,8 +25822,24 @@ function layoutOps(doc, a) {
25461
25822
  }
25462
25823
  return ops;
25463
25824
  }
25464
- function report(doc, a) {
25465
- const name = (id) => `"${oneLine(nodeName(doc.nodes[id]), 36)}"`;
25825
+ function scopeFor(doc, rows, aba) {
25826
+ const tab = aba ? resolveTab(doc, aba) : void 0;
25827
+ if (!hasTabs(doc)) return void 0;
25828
+ const arranged = rows.flatMap((r) => [...r.title ? [r.title] : [], ...r.left, ...r.right]);
25829
+ const homes = new Set(arranged.map((id) => homeTabOf(doc, id)));
25830
+ const only = homes.size === 1 ? [...homes][0] : null;
25831
+ const chosen = tab ?? (only && only !== AVULSOS_TAB ? only : void 0);
25832
+ if (chosen === void 0) return void 0;
25833
+ const info = tabIndex(doc).info(chosen);
25834
+ return {
25835
+ ids: membersOfTab(doc, chosen),
25836
+ title: info.title,
25837
+ short: info.short,
25838
+ foreign: tab === void 0 ? [] : arranged.filter((id) => homeTabOf(doc, id) !== tab)
25839
+ };
25840
+ }
25841
+ function report(doc, a, scope) {
25842
+ const name = (id) => `"${oneLine2(nodeName(doc.nodes[id]), 36)}"`;
25466
25843
  const lines = [];
25467
25844
  const unmeasured = a.placed.filter((p) => p.source === "gravado");
25468
25845
  const estimated = a.placed.filter((p) => p.source === "medido" && p.estimated);
@@ -25484,9 +25861,17 @@ function report(doc, a) {
25484
25861
  lines.push("Todos os n\xF3s arrumados t\xEAm medida da tela.");
25485
25862
  }
25486
25863
  const pairs = (list) => list.map(([x, y]) => `${name(x)} \xD7 ${name(y)}`).join("; ");
25864
+ const where = scope ? ` (s\xF3 na aba "${oneLine2(scope.title, 40)}" aba=${scope.short})` : "";
25487
25865
  lines.push(
25488
- a.inner.length || a.outer.length ? `Sobreposi\xE7\xF5es com os tamanhos usados: ${pairs([...a.inner, ...a.outer])}.` : "Sobreposi\xE7\xF5es com os tamanhos usados: nenhuma."
25866
+ a.inner.length || a.outer.length ? `Sobreposi\xE7\xF5es com os tamanhos usados${where}: ${pairs([...a.inner, ...a.outer])}.` : `Sobreposi\xE7\xF5es com os tamanhos usados${where}: nenhuma.`
25489
25867
  );
25868
+ if (scope?.foreign.length) {
25869
+ const n = scope.foreign.length;
25870
+ const names = scope.foreign.slice(0, 5).map(name).join(", ") + (n > 5 ? ` e mais ${n - 5}` : "");
25871
+ lines.push(
25872
+ `Aten\xE7\xE3o: ${names} ${n === 1 ? "mora" : "moram"} em outra aba que n\xE3o a pedida; o arranjo grava s\xF3 posi\xE7\xE3o e n\xE3o troca a aba de ningu\xE9m (data.abaId).`
25873
+ );
25874
+ }
25490
25875
  lines.push('Caixas ([papel] id "nome" @(x,y) largura\xD7altura usada, origem da altura):');
25491
25876
  const rows = /* @__PURE__ */ new Map();
25492
25877
  for (const p of a.placed) {
@@ -25510,10 +25895,11 @@ function registerCanvasLayoutTools(server, deps) {
25510
25895
  "canvas_arrange_rows",
25511
25896
  {
25512
25897
  title: "Canvas: arrumar em linhas",
25513
- description: "Arruma n\xF3s em linhas, uma por vers\xE3o (revis\xE3o de imagem) ou por take (anima\xE7\xE3o), de cima para baixo a partir de origin: title em cima; \xE0 esquerda a coluna left (prompt; m\xEDdia/imagem que alimenta o prompt ou o gerador, como a refer\xEAncia e o frame de entrada, fica alinhada \xE0 direita embaixo dele); \xE0 direita a coluna right (gerador) com o topo no do prompt. Qualquer tipo entra em qualquer coluna. Usa o tamanho medido da tela (ou o gravado, avisando) e p\xF5e a largura pelo tipo real do n\xF3: prompt 1024, gerador de imagem 560, de v\xEDdeo 560, refer\xEAncia (m\xEDdia) 320, frame (m\xEDdia/imagem ligada na porta frame do v\xEDdeo) 320, t\xEDtulo 1024. Grava s\xF3 posi\xE7\xE3o e tamanho num passo, sem pedir releitura. Devolve as caixas, as sobreposi\xE7\xF5es com os tamanhos usados e os n\xF3s sem medida (a altura deles pode crescer quando o board abrir: rode de novo depois). Se a \xE1rea encostar em outro n\xF3 do board, n\xE3o grava nada e sugere uma origem livre. dry_run: true s\xF3 calcula.",
25898
+ description: "Arruma n\xF3s em linhas, uma por vers\xE3o (revis\xE3o de imagem) ou por take (anima\xE7\xE3o), de cima para baixo a partir de origin: title em cima; \xE0 esquerda a coluna left (prompt; m\xEDdia/imagem que alimenta o prompt ou o gerador, como a refer\xEAncia e o frame de entrada, fica alinhada \xE0 direita embaixo dele); \xE0 direita a coluna right (gerador) com o topo no do prompt. Qualquer tipo entra em qualquer coluna. Usa o tamanho medido da tela (ou o gravado, avisando) e p\xF5e a largura pelo tipo real do n\xF3: prompt 1024, gerador de imagem 560, de v\xEDdeo 560, refer\xEAncia (m\xEDdia) 320, frame (m\xEDdia/imagem ligada na porta frame do v\xEDdeo) 320, t\xEDtulo 1024. Grava s\xF3 posi\xE7\xE3o e tamanho num passo, sem pedir releitura. Devolve as caixas, as sobreposi\xE7\xF5es com os tamanhos usados e os n\xF3s sem medida (a altura deles pode crescer quando o board abrir: rode de novo depois). Se a \xE1rea encostar em outro n\xF3 da mesma aba (aba, ou a dos n\xF3s arrumados), n\xE3o grava nada e sugere uma origem livre. dry_run: true s\xF3 calcula.",
25514
25899
  inputSchema: {
25515
25900
  board_id: external_exports.string().uuid(),
25516
25901
  origin: external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() }).describe("Canto de cima \xE0 esquerda da \xE1rea."),
25902
+ aba: abaArg(ABA_ARRANGE_ROWS),
25517
25903
  rows: external_exports.array(
25518
25904
  external_exports.object({
25519
25905
  title: nodeIdArg.optional().describe("N\xF3 do t\xEDtulo da linha (Texto livre)."),
@@ -25542,15 +25928,16 @@ function registerCanvasLayoutTools(server, deps) {
25542
25928
  const gap = args.gap ?? DEFAULT_GAP;
25543
25929
  const plan = (doc) => {
25544
25930
  checkRows(doc, rows);
25545
- const a = arrangeRows(doc, rows, args.origin, gap, widths);
25546
- return { a, ops: layoutOps(doc, a), text: report(doc, a) };
25931
+ const scope = scopeFor(doc, rows, args.aba);
25932
+ const a = arrangeRows(doc, rows, args.origin, gap, widths, scope?.ids);
25933
+ return { a, scoped: scope !== void 0, ops: layoutOps(doc, a), text: report(doc, a, scope) };
25547
25934
  };
25548
25935
  const count = (ops) => new Set(ops.map((o) => o.type === "node.set" ? o.nodeId : "")).size;
25549
- const blocked = (a) => `A \xE1rea encosta em n\xF3s que n\xE3o est\xE3o no arranjo. Nada foi gravado. Origem livre sugerida: {"x":${a.freeOrigin.x},"y":${a.freeOrigin.y}} (abaixo de tudo nessa faixa), ou tire esses n\xF3s do caminho.`;
25936
+ const blocked = (a, scoped) => `A \xE1rea encosta em n\xF3s que n\xE3o est\xE3o no arranjo. Nada foi gravado. Origem livre sugerida: {"x":${a.freeOrigin.x},"y":${a.freeOrigin.y}} (abaixo de tudo${scoped ? " da aba" : ""} nessa faixa), ou tire esses n\xF3s do caminho.`;
25550
25937
  if (args.dry_run) {
25551
- const { a, ops, text: text2 } = plan((await api.snapshot(args.board_id)).doc);
25938
+ const { a, scoped, ops, text: text2 } = plan((await api.snapshot(args.board_id)).doc);
25552
25939
  const head = `Pr\xE9via, nada gravado: ${ops.length ? `${count(ops)} n\xF3(s) mudariam de lugar ou largura` : "tudo j\xE1 est\xE1 assim"}.`;
25553
- return [head, ...a.outer.length ? [blocked(a)] : [], text2].join("\n");
25940
+ return [head, ...a.outer.length ? [blocked(a, scoped)] : [], text2].join("\n");
25554
25941
  }
25555
25942
  let text = "";
25556
25943
  const done = await edit(
@@ -25558,7 +25945,7 @@ function registerCanvasLayoutTools(server, deps) {
25558
25945
  async (doc) => {
25559
25946
  const p = plan(doc);
25560
25947
  text = p.text;
25561
- if (p.a.outer.length) throw new CanvasToolError(`${blocked(p.a)}
25948
+ if (p.a.outer.length) throw new CanvasToolError(`${blocked(p.a, p.scoped)}
25562
25949
  ${p.text}`);
25563
25950
  return {
25564
25951
  ops: p.ops,
@@ -25654,7 +26041,7 @@ function needMediaNode(doc, nodeId3) {
25654
26041
  if (!node) throw new CanvasToolError(`N\xF3 ${nodeId3} n\xE3o existe no board. Releia com canvas_read_board.`);
25655
26042
  if (nodeKind(node) !== "media") {
25656
26043
  throw new CanvasToolError(
25657
- `O n\xF3 ${nodeId3} ("${oneLine(nodeName(node), 40)}") \xE9 ${nodeKind(node) ?? node.type}, n\xE3o M\xEDdia. Passe o id de um n\xF3 media, ou omita node_id para criar um n\xF3 M\xEDdia novo.`
26044
+ `O n\xF3 ${nodeId3} ("${oneLine2(nodeName(node), 40)}") \xE9 ${nodeKind(node) ?? node.type}, n\xE3o M\xEDdia. Passe o id de um n\xF3 media, ou omita node_id para criar um n\xF3 M\xEDdia novo.`
25658
26045
  );
25659
26046
  }
25660
26047
  return node;
@@ -25689,20 +26076,26 @@ function registerCanvasMediaTools(server, deps) {
25689
26076
  "canvas_upload_media",
25690
26077
  {
25691
26078
  title: "Canvas: subir arquivo",
25692
- description: "Sobe um arquivo (caminho local ou URL) pela rota de arquivos da BePlus e devolve o item pronto: url, mime, bytes, dura\xE7\xE3o/largura/altura quando \xE9 m\xEDdia, media_item (data.items do n\xF3 M\xEDdia), sequence_asset e libraryItemId (clipe da timeline da Montagem). Com board_id grava no n\xF3 M\xEDdia node_id, ou cria um n\xF3 M\xEDdia novo sem node_id; a\xED exige canvas_read_board nesta sess\xE3o. Teto de 95 MB. N\xE3o gera m\xEDdia nem gasta diamantes.",
26079
+ description: "Sobe um arquivo (caminho local ou URL) pela rota de arquivos da BePlus e devolve o item pronto: url, mime, bytes, dura\xE7\xE3o/largura/altura quando \xE9 m\xEDdia, media_item (data.items do n\xF3 M\xEDdia), sequence_asset e libraryItemId (clipe da timeline da Montagem). Com board_id grava no n\xF3 M\xEDdia node_id, ou cria um n\xF3 M\xEDdia novo sem node_id (com aba, ele nasce no fim do conte\xFAdo da aba e grava data.abaId); a\xED exige canvas_read_board nesta sess\xE3o. Teto de 95 MB. N\xE3o gera m\xEDdia nem gasta diamantes.",
25693
26080
  inputSchema: {
25694
26081
  source: external_exports.string().trim().min(1).max(2e3).describe("Caminho local (/\u2026, ~/\u2026, file://) ou URL http(s)."),
25695
26082
  name: external_exports.string().trim().min(1).max(120).optional().describe("Nome do arquivo no Canvas (a extens\xE3o \xE9 mantida)."),
25696
26083
  board_id: external_exports.string().uuid().optional().describe("Board onde gravar o item (opcional)."),
25697
26084
  node_id: external_exports.string().min(1).max(128).optional().describe("N\xF3 M\xEDdia que recebe o item. Sem ele, cria um n\xF3 M\xEDdia novo."),
26085
+ aba: abaArg(ABA_UPLOAD_MEDIA),
25698
26086
  label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do n\xF3 M\xEDdia novo (padr\xE3o: nome do arquivo)."),
25699
26087
  replace: external_exports.boolean().optional().describe("true troca a lista do n\xF3 por este arquivo; false acrescenta. Padr\xE3o: a regra do Canvas (n\xF3 de um arquivo s\xF3, ligado, troca).")
25700
26088
  },
25701
26089
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
25702
26090
  },
25703
26091
  async (args) => run(async () => {
25704
- if (!args.board_id && (args.node_id || args.label || args.replace !== void 0)) {
25705
- throw new CanvasToolError("node_id, label e replace s\xF3 valem com board_id.");
26092
+ if (!args.board_id && (args.node_id || args.label || args.replace !== void 0 || args.aba)) {
26093
+ throw new CanvasToolError("node_id, label, replace e aba s\xF3 valem com board_id.");
26094
+ }
26095
+ if (args.aba && args.node_id) {
26096
+ throw new CanvasToolError(
26097
+ "aba s\xF3 vale quando o upload cria um n\xF3 M\xEDdia novo (sem node_id): o n\xF3 que j\xE1 existe n\xE3o muda de aba por aqui."
26098
+ );
25706
26099
  }
25707
26100
  if (args.board_id) {
25708
26101
  if (!seen.has(args.board_id)) {
@@ -25710,12 +26103,13 @@ function registerCanvasMediaTools(server, deps) {
25710
26103
  }
25711
26104
  const snap = await api.snapshot(args.board_id);
25712
26105
  if (snap.role === "viewer") throw new CanvasToolError("Voc\xEA \xE9 s\xF3 leitor neste board: pe\xE7a ao dono para te dar papel de editor.");
26106
+ if (args.aba) resolveTab(snap.doc, args.aba);
25713
26107
  if (args.node_id) {
25714
26108
  const node = needMediaNode(snap.doc, args.node_id);
25715
26109
  const replacing = args.replace ?? holdsOne(snap.doc, node);
25716
26110
  if (!replacing && itemsOf(node).length >= MAX_MEDIA_ITEMS) {
25717
26111
  throw new CanvasToolError(
25718
- `O n\xF3 "${oneLine(nodeName(node), 40)}" j\xE1 tem ${MAX_MEDIA_ITEMS} arquivos (teto do Canvas). Omita node_id para criar outro n\xF3 M\xEDdia.`
26112
+ `O n\xF3 "${oneLine2(nodeName(node), 40)}" j\xE1 tem ${MAX_MEDIA_ITEMS} arquivos (teto do Canvas). Omita node_id para criar outro n\xF3 M\xEDdia.`
25719
26113
  );
25720
26114
  }
25721
26115
  }
@@ -25749,14 +26143,21 @@ URL: ${u.url}`;
25749
26143
  return {
25750
26144
  ops: [{ type: "node.set", nodeId: node.id, path: "data.items", value: setValue(items) }],
25751
26145
  targets: [node.id],
25752
- done: `N\xF3 M\xEDdia "${oneLine(nodeName(node), 40)}" (${node.id}): ` + (replacing ? `arquivo trocado (${before} \u2192 1)` : `arquivo acrescentado, agora com ${items.length}`)
26146
+ done: `N\xF3 M\xEDdia "${oneLine2(nodeName(node), 40)}" (${node.id}): ` + (replacing ? `arquivo trocado (${before} \u2192 1)` : `arquivo acrescentado, agora com ${items.length}`)
25753
26147
  };
25754
26148
  }
25755
26149
  const spec = KINDS.media;
26150
+ const tab = args.aba ? resolveTab(doc, args.aba) : void 0;
25756
26151
  const created = makeNode({
25757
26152
  type: WORKFLOW_NODE_TYPE,
25758
- data: { ...spec.data, [KIND_FIELD]: "media", label: args.label ?? u.name, items: [item] },
25759
- position: placeFor(doc, spec.size),
26153
+ data: {
26154
+ ...spec.data,
26155
+ [KIND_FIELD]: "media",
26156
+ label: args.label ?? u.name,
26157
+ items: [item],
26158
+ ...tab === void 0 ? {} : { [TAB_ID_FIELD]: tab }
26159
+ },
26160
+ position: placeFor(doc, spec.size, void 0, "right", tab),
25760
26161
  size: spec.size,
25761
26162
  zIndex: Object.values(doc.nodes).reduce((t, n) => Math.max(t, n.zIndex), 0) + 1,
25762
26163
  createdBy: await api.me()
@@ -25765,7 +26166,7 @@ URL: ${u.url}`;
25765
26166
  return {
25766
26167
  ops: [{ type: "node.add", nodeId: created.id, node: created }],
25767
26168
  targets: [],
25768
- done: `N\xF3 M\xEDdia "${oneLine(nodeName(created), 40)}" criado: ${created.id}`
26169
+ done: `N\xF3 M\xEDdia "${oneLine2(nodeName(created), 40)}" criado: ${created.id}.${placementNote(doc, created)}`
25769
26170
  };
25770
26171
  });
25771
26172
  } catch (e) {
@@ -26138,6 +26539,65 @@ function registerCanvasFocusTools(server, api, run) {
26138
26539
  );
26139
26540
  }
26140
26541
 
26542
+ // src/canvas/lock.ts
26543
+ var SECTION_LOCK_FIELD = "travada";
26544
+ var LOCKED_TEXT = "\xE1rea travada pelo dono (cadeado); pe\xE7a pra destravar";
26545
+ function isLockedSection(node) {
26546
+ return node !== void 0 && nodeKind(node) === "section" && node.data[SECTION_LOCK_FIELD] === true;
26547
+ }
26548
+ var LAYOUT_PATH = /^(position|size)(\.|$)/;
26549
+ var STAMP_PATH = `data.${TAB_ID_FIELD}`;
26550
+ function rectOf(node, withMeasured) {
26551
+ const h = withMeasured ? Math.max(node.size.h, readMeasured(node)?.h ?? 0) : node.size.h;
26552
+ return { x: node.position.x, y: node.position.y, w: node.size.w, h };
26553
+ }
26554
+ function contains(outer, inner) {
26555
+ return inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.w <= outer.x + outer.w && inner.y + inner.h <= outer.y + outer.h;
26556
+ }
26557
+ function flatten2(ops) {
26558
+ return ops.flatMap((op) => op.type === "batch" ? flatten2(op.ops) : [op]);
26559
+ }
26560
+ function lockViolation(doc, ops) {
26561
+ const locked = Object.values(doc.nodes).filter(isLockedSection);
26562
+ if (!locked.length) return null;
26563
+ const name = (node) => `"${oneLine2(tabTitle(node), 40)}"`;
26564
+ const flat = flatten2(ops);
26565
+ for (const op of flat) {
26566
+ if (op.type !== "node.set" && op.type !== "node.remove") continue;
26567
+ const node = doc.nodes[op.nodeId];
26568
+ if (!isLockedSection(node)) continue;
26569
+ if (op.type === "node.remove") return `${name(node)}: ${LOCKED_TEXT}. Nada foi apagado.`;
26570
+ if (LAYOUT_PATH.test(op.path)) return `${name(node)}: ${LOCKED_TEXT}. Nada foi aplicado.`;
26571
+ }
26572
+ const home = partitionOf(doc).home;
26573
+ const placed = /* @__PURE__ */ new Set();
26574
+ const restamped = /* @__PURE__ */ new Set();
26575
+ for (const op of flat) {
26576
+ if (op.type !== "node.set") continue;
26577
+ const node = doc.nodes[op.nodeId];
26578
+ if (node === void 0 || nodeKind(node) === "section") continue;
26579
+ if (LAYOUT_PATH.test(op.path)) {
26580
+ placed.add(op.nodeId);
26581
+ } else if (op.path === STAMP_PATH) {
26582
+ const sameHome = op.value.kind === "value" && op.value.value === home.get(op.nodeId);
26583
+ if (!sameHome) restamped.add(op.nodeId);
26584
+ }
26585
+ }
26586
+ const moved = /* @__PURE__ */ new Set([...placed, ...restamped]);
26587
+ if (moved.size < 2) return null;
26588
+ for (const section of locked) {
26589
+ const inside = isTabSection(section) ? [...moved].filter((id) => home.get(id) === section.id) : (() => {
26590
+ const outer = rectOf(section, false);
26591
+ return [...placed].filter((id) => contains(outer, rectOf(doc.nodes[id], true)));
26592
+ })();
26593
+ if (inside.length >= 2) {
26594
+ const stamp = inside.some((id) => restamped.has(id)) ? " Trocar a aba (data.abaId) tamb\xE9m conta como mexer na aba." : "";
26595
+ return `${name(section)}: ${LOCKED_TEXT}. Mudar ${inside.length} n\xF3s de dentro dela num pedido s\xF3 n\xE3o vale (um por vez segue valendo).${stamp} Nada foi aplicado.`;
26596
+ }
26597
+ }
26598
+ return null;
26599
+ }
26600
+
26141
26601
  // src/canvas/mentions.ts
26142
26602
  var VIA_MENTION = "viaMention";
26143
26603
  var PROMPT_REFS_INPUT = "refs";
@@ -26337,7 +26797,7 @@ function registerCanvasTools(server, client, cfg) {
26337
26797
  return fail(e);
26338
26798
  }
26339
26799
  }
26340
- async function edit(id, build, opts = {}) {
26800
+ async function edit(id, build2, opts = {}) {
26341
26801
  const last = seen.get(id);
26342
26802
  if (last === void 0 && !opts.layoutOnly) {
26343
26803
  throw new CanvasToolError("Leia o board com canvas_read_board antes de editar (a leitura vale para a sess\xE3o).");
@@ -26345,9 +26805,13 @@ function registerCanvasTools(server, client, cfg) {
26345
26805
  const snap = await api.snapshot(id);
26346
26806
  if (snap.role === "viewer") throw new CanvasToolError("Voc\xEA \xE9 s\xF3 leitor neste board: pe\xE7a ao dono para te dar papel de editor.");
26347
26807
  const doc = snap.doc;
26348
- const { ops, done, targets } = await build(doc, snap);
26808
+ const { ops, done, targets } = await build2(doc, snap);
26349
26809
  if (!ops.length) return `Nada a mudar. ${done}`;
26350
26810
  if (opts.layoutOnly && !ops.every(isLayoutOp)) throw new Error("layout_only com opera\xE7\xE3o fora de posi\xE7\xE3o/tamanho");
26811
+ const locked = lockViolation(doc, ops);
26812
+ if (locked) throw new CanvasToolError(locked);
26813
+ const badAba = abaIdViolation(doc, ops);
26814
+ if (badAba) throw new CanvasToolError(`${badAba} Nada foi aplicado.`);
26351
26815
  let note = "";
26352
26816
  if (!opts.layoutOnly && last !== void 0 && snap.seq > last) {
26353
26817
  const since = await api.opsAfter(id, last);
@@ -26361,7 +26825,7 @@ function registerCanvasTools(server, client, cfg) {
26361
26825
  const clash = [...touchedNodes(theirs)].filter((n) => targets.includes(n));
26362
26826
  if (clash.length) {
26363
26827
  seen.delete(id);
26364
- const names = clash.map((n) => doc.nodes[n] ? `"${oneLine(nodeName(doc.nodes[n]), 40)}"` : n).join(", ");
26828
+ const names = clash.map((n) => doc.nodes[n] ? `"${oneLine2(nodeName(doc.nodes[n]), 40)}"` : n).join(", ");
26365
26829
  throw new CanvasToolError(
26366
26830
  `Outra pessoa mexeu em ${names} depois da sua leitura (seq ${last} \u2192 ${snap.seq}). Nada foi aplicado: releia com canvas_read_board e refa\xE7a a edi\xE7\xE3o em cima do estado atual.`
26367
26831
  );
@@ -26458,15 +26922,17 @@ ${lines.join("\n")}${note}`;
26458
26922
  }
26459
26923
  return { ops, warn };
26460
26924
  }
26461
- function placeFor(doc, size, near, side = "right") {
26925
+ function placeFor(doc, size, near, side = "right", tab) {
26462
26926
  if (near) {
26463
26927
  const x = side === "right" ? near.position.x + near.size.w + GAP : near.position.x - size.w - GAP;
26464
26928
  let y = near.position.y;
26465
- const nodes2 = Object.values(doc.nodes);
26929
+ const scope = tab === void 0 ? null : membersOfTab(doc, tab);
26930
+ const nodes2 = Object.values(doc.nodes).filter((n) => scope === null || scope.has(n.id) && nodeKind(n) !== "section");
26466
26931
  const hits = (yy) => nodes2.some((n) => x < n.position.x + n.size.w && x + size.w > n.position.x && yy < n.position.y + n.size.h && yy + size.h > n.position.y);
26467
26932
  for (let i = 0; i < 50 && hits(y); i++) y += GAP;
26468
26933
  return { x, y };
26469
26934
  }
26935
+ if (tab !== void 0 && tab !== AVULSOS_TAB) return endOfTabContent(doc, tab, size);
26470
26936
  const nodes = Object.values(doc.nodes).filter((n) => nodeKind(n) !== "section");
26471
26937
  if (!nodes.length) return { x: 0, y: 0 };
26472
26938
  return {
@@ -26522,7 +26988,7 @@ ${lines.join("\n")}${note}`;
26522
26988
  for (const c of changed) after.nodes[c.node.id] = { ...c.node, data: c.data };
26523
26989
  for (const e of newEdges) after.edges[e.id] = e;
26524
26990
  const sync = mentionEdgeOps(after, prompts.map((c) => c.node.id), await api.me());
26525
- const name = (id) => after.nodes[id] ? `"${oneLine(nodeName(after.nodes[id]), 40)}"` : id;
26991
+ const name = (id) => after.nodes[id] ? `"${oneLine2(nodeName(after.nodes[id]), 40)}"` : id;
26526
26992
  return { ops: sync.ops, note: describeMentionSync(sync, name) };
26527
26993
  }
26528
26994
  server.registerTool(
@@ -26543,17 +27009,19 @@ ${lines.join("\n")}${note}`;
26543
27009
  "canvas_read_board",
26544
27010
  {
26545
27011
  title: "Canvas: ler board",
26546
- description: 'L\xEA um board: n\xF3s (id, tipo, nome, posi\xE7\xE3o, tamanho, campos principais), liga\xE7\xF5es e v\xEDnculo com o IA Lab, sem m\xEDdia. Tamanho "medido" \xE9 o da tela; "gravado" \xE9 o piso (o cart\xE3o cresce com o conte\xFAdo). Obrigat\xF3rio antes de editar (vale para a sess\xE3o). node_ids devolve o data desses n\xF3s, com cada corte marcado (path=\u2026); o valor inteiro (ex.: data.sequence da Montagem) sai no canvas_read_node.',
27012
+ description: 'L\xEA um board: n\xF3s (id, tipo, nome, posi\xE7\xE3o, tamanho, campos principais), liga\xE7\xF5es e v\xEDnculo com o IA Lab, sem m\xEDdia. Tamanho "medido" \xE9 o da tela; "gravado" \xE9 o piso (o cart\xE3o cresce com o conte\xFAdo). Obrigat\xF3rio antes de editar (vale para a sess\xE3o). node_ids devolve o data desses n\xF3s, com cada corte marcado (path=\u2026); o valor inteiro (ex.: data.sequence da Montagem) sai no canvas_read_node. Cada cart\xE3o diz a aba (aba=<id curto> ou aba=avulsos), o topo conta os cart\xF5es de cada aba e aba l\xEA uma aba s\xF3.',
26547
27013
  inputSchema: {
26548
27014
  board_id: boardId3,
26549
- node_ids: external_exports.array(nodeIdArg2).max(20).optional().describe("N\xF3s para ver o data (sem blobs, cortes marcados).")
27015
+ node_ids: external_exports.array(nodeIdArg2).max(20).optional().describe("N\xF3s para ver o data (sem blobs, cortes marcados)."),
27016
+ aba: abaArg(ABA_READ_BOARD)
26550
27017
  },
26551
27018
  annotations: { readOnlyHint: true, openWorldHint: false }
26552
27019
  },
26553
- async ({ board_id, node_ids }) => run(async () => {
27020
+ async ({ board_id, node_ids, aba }) => run(async () => {
26554
27021
  const snap = await api.snapshot(board_id);
27022
+ const tab = aba ? resolveTab(snap.doc, aba, { allowAvulsos: true }) : void 0;
26555
27023
  seen.set(board_id, snap.seq);
26556
- const parts = [boardSummary(snap)];
27024
+ const parts = [boardSummary(snap, { tab })];
26557
27025
  if (node_ids?.length) {
26558
27026
  parts.push("", "Detalhe:");
26559
27027
  for (const n of node_ids) parts.push(snap.doc.nodes[n] ? nodeDetail(snap.doc.nodes[n]) : `${n}: n\xE3o existe`);
@@ -26613,7 +27081,7 @@ ${lines.join("\n")}${note}`;
26613
27081
  const hash = createHash("sha256").update(json2).digest("hex").slice(0, 12);
26614
27082
  const where = `data${segments.length ? `.${segments.join(".")}` : ""}`;
26615
27083
  const at = readMeasured(node)?.at;
26616
- const head = `${node.id} \xB7 ${nodeKind(node) ?? node.type} \xB7 "${oneLine(nodeName(node), 60)}" \xB7 board seq ${snap.seq} \xB7 tamanho ${sizeLabel(node)}${at ? ` \xB7 medido em ${at}` : ""} \xB7 ${where}${which}: ${total} caracteres (JSON compacto) \xB7 sha256 ${hash}`;
27084
+ const head = `${node.id} \xB7 ${nodeKind(node) ?? node.type} \xB7 "${oneLine2(nodeName(node), 60)}" \xB7 board seq ${snap.seq} \xB7 tamanho ${sizeLabel(node)}${at ? ` \xB7 medido em ${at}` : ""} \xB7 ${where}${which}: ${total} caracteres (JSON compacto) \xB7 sha256 ${hash}`;
26617
27085
  const part = from === 0 && to === total ? "Inteiro, abaixo." : `Parte: caracteres ${from} a ${to} de ${total}. ${to < total ? `Falta o resto: chame de novo com offset=${to}.` : "Esta \xE9 a \xFAltima parte."}`;
26618
27086
  return `${head}
26619
27087
  ${part}
@@ -26674,12 +27142,13 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26674
27142
  "canvas_add_node",
26675
27143
  {
26676
27144
  title: "Canvas: adicionar n\xF3",
26677
- description: `Adiciona um n\xF3 (n\xE3o executa). ${READ}kind: ${kindList()}. data completa o padr\xE3o do tipo; label d\xE1 o nome. Sem position, nasce ao lado do n\xF3 ligado ou \xE0 direita do board. inputs/outputs j\xE1 ligam o n\xF3 no mesmo passo. ` + MENTION_NOTE + MONTAGE_NOTE,
27145
+ description: `Adiciona um n\xF3 (n\xE3o executa). ${READ}kind: ${kindList()}. data completa o padr\xE3o do tipo; label d\xE1 o nome. Sem position, nasce ao lado do n\xF3 ligado ou \xE0 direita do board (com aba, no fim do conte\xFAdo da aba); aba grava data.abaId. inputs/outputs j\xE1 ligam o n\xF3 no mesmo passo. ` + MENTION_NOTE + MONTAGE_NOTE,
26678
27146
  inputSchema: {
26679
27147
  board_id: boardId3,
26680
27148
  kind: external_exports.string().min(1).max(40),
26681
27149
  label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do cart\xE3o (data.label)."),
26682
27150
  data: external_exports.record(external_exports.unknown()).optional().describe("Campos do data. Ex.: narration {blocks:[{id,text}]}, text {text}, prompt {template}."),
27151
+ aba: abaArg(ABA_ADD_NODE),
26683
27152
  position: point.optional(),
26684
27153
  size: sizeArg.optional(),
26685
27154
  inputs: external_exports.array(external_exports.object({ from: nodeIdArg2, from_port: external_exports.string().min(1), to_port: external_exports.string().min(1) })).max(20).optional().describe("Liga\xE7\xF5es que CHEGAM no n\xF3 novo: from.from_port \u2192 novo.to_port."),
@@ -26700,8 +27169,19 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26700
27169
  const b = boxOf(args.size);
26701
27170
  box = { w: b.w ?? spec.size.w, h: b.h ?? spec.size.h };
26702
27171
  }
27172
+ const byParam = args.aba ? resolveTab(doc, args.aba) : void 0;
27173
+ const written = data[TAB_ID_FIELD];
27174
+ const byData = typeof written === "string" && written !== "" ? resolveTab(doc, written) : void 0;
27175
+ if (byParam !== void 0 && byData !== void 0 && byParam !== byData) {
27176
+ throw new CanvasToolError(
27177
+ `aba ("${tabTitle(doc.nodes[byParam])}") e data.abaId ("${tabTitle(doc.nodes[byData])}") apontam para abas diferentes: mande s\xF3 aba.`
27178
+ );
27179
+ }
27180
+ const tab = byParam ?? byData;
27181
+ if (tab !== void 0) data[TAB_ID_FIELD] = tab;
26703
27182
  const near = args.inputs?.[0] ? doc.nodes[args.inputs[0].from] : args.outputs?.[0] ? doc.nodes[args.outputs[0].to] : void 0;
26704
- const position = args.position ?? placeFor(doc, box, near, args.inputs?.[0] ? "right" : "left");
27183
+ const nearTab = near ? homeTabOf(doc, near.id) ?? void 0 : void 0;
27184
+ const position = args.position ?? placeFor(doc, box, tab === void 0 ? near : void 0, args.inputs?.[0] ? "right" : "left", tab ?? nearTab);
26705
27185
  const zIndex = args.kind === "section" ? SECTION_Z : Object.values(doc.nodes).reduce((t, n) => Math.max(t, n.zIndex), 0) + 1;
26706
27186
  const node = makeNode({ type: WORKFLOW_NODE_TYPE, data, position, size: box, zIndex, createdBy: me });
26707
27187
  const links = [
@@ -26722,7 +27202,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26722
27202
  return {
26723
27203
  ops: [{ type: "node.add", nodeId: node.id, node }, ...edges.ops, ...mention.ops],
26724
27204
  targets: links.map((l) => l.source === node.id ? l.target : l.source),
26725
- done: `N\xF3 ${args.kind} "${nodeName(node)}" criado: ${node.id}${edges.ops.length ? ` com ${edges.ops.length} liga\xE7\xE3o(\xF5es)` : ""}.${edges.warn}${mention.note}`
27205
+ done: `N\xF3 ${args.kind} "${nodeName(node)}" criado: ${node.id}${edges.ops.length ? ` com ${edges.ops.length} liga\xE7\xE3o(\xF5es)` : ""}.${placementNote(doc, node)}${edges.warn}${mention.note}`
26726
27206
  };
26727
27207
  })
26728
27208
  )
@@ -26796,7 +27276,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26796
27276
  });
26797
27277
  const mention = args.layout_only ? { ops: [], note: "" } : await mentionOps(doc, changes.map((c) => ({ node: c.node, data: c.data, keys: c.dataKeys })));
26798
27278
  const touched = changes.filter((c) => c.ops.length);
26799
- const list = touched.slice(0, 12).map((c) => `"${oneLine(nodeName(c.node), 30)}" (${c.keys.join(", ")})`).join("; ");
27279
+ const list = touched.slice(0, 12).map((c) => `"${oneLine2(nodeName(c.node), 30)}" (${c.keys.join(", ")})`).join("; ");
26800
27280
  return {
26801
27281
  ops: [...changes.flatMap((c) => c.ops), ...mention.ops],
26802
27282
  targets: ids,
@@ -26899,7 +27379,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26899
27379
  return {
26900
27380
  ops,
26901
27381
  targets: [node.id],
26902
- done: `N\xF3 "${oneLine(nodeName(node), 40)}" (${node.id}): ${written} caminho(s) gravado(s)` + (skipped ? ` (ignorados: ${skipped})` : "") + mention.note
27382
+ done: `N\xF3 "${oneLine2(nodeName(node), 40)}" (${node.id}): ${written} caminho(s) gravado(s)` + (skipped ? ` (ignorados: ${skipped})` : "") + mention.note
26903
27383
  };
26904
27384
  },
26905
27385
  { split: true }
@@ -26969,7 +27449,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26969
27449
  ops.push(op);
26970
27450
  }
26971
27451
  }
26972
- const names = nodeIds.map((n) => `"${oneLine(nodeName(doc.nodes[n]), 40)}"`);
27452
+ const names = nodeIds.map((n) => `"${oneLine2(nodeName(doc.nodes[n]), 40)}"`);
26973
27453
  return {
26974
27454
  ops,
26975
27455
  targets: nodeIds,
@@ -27051,7 +27531,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
27051
27531
  import { createRequire } from "module";
27052
27532
  var require2 = createRequire(import.meta.url);
27053
27533
  function readVersion() {
27054
- if ("0.35.0") return "0.35.0";
27534
+ if ("0.37.0") return "0.37.0";
27055
27535
  try {
27056
27536
  const pkg = require2("../package.json");
27057
27537
  return pkg.version ?? "0.0.0-unknown";
@@ -27067,6 +27547,7 @@ var SERVER_INSTRUCTIONS = [
27067
27547
  'M\xEDdia: generate_* gasta diamantes; confira com estimate_cost antes de lotes grandes. Sem project, a gera\xE7\xE3o cai no projeto ativo; billing:"personal" cobra do usu\xE1rio.',
27068
27548
  "Calls: search_calls acha, get_call traz a transcri\xE7\xE3o.",
27069
27549
  "Canvas online (canvas_*): leia com canvas_read_board antes de editar e siga o estado lido. Cada edi\xE7\xE3o vai num passo s\xF3; se outra pessoa mexeu nos mesmos n\xF3s, a ferramenta recusa e pede releitura. As canvas_* n\xE3o geram m\xEDdia, exceto canvas_run: roda n\xF3s pela aba do Canvas aberta do usu\xE1rio e gasta diamantes; max_diamonds \xE9 o teto que o usu\xE1rio autorizou (dry_run s\xF3 estima). D\xEA nome (label) aos n\xF3s, n\xE3o invente tipos nem portas, e confirme antes de canvas_delete.",
27550
+ "Cada aba \xE9 um plano: ao criar, passe aba (id da se\xE7\xE3o) em canvas_add_node e canvas_upload_media, sen\xE3o o cart\xE3o pode cair em Avulsos. canvas_read_board com aba l\xEA uma aba s\xF3.",
27070
27551
  'Mostrar algo ao usu\xE1rio ("olha aqui"): canvas_focus leva a aba do Canvas que ele est\xE1 olhando at\xE9 os n\xF3s e os seleciona uma vez, sem prender a aba, sem gasto e sem escrita no board. follow: true prende a aba na \xE1rea at\xE9 o \xD7: s\xF3 se ele pedir.',
27071
27552
  'Arrumar o board: o size gravado \xE9 piso (o cart\xE3o cresce com o conte\xFAdo); use o tamanho "medido" do canvas_read_board. \xC1rea de revis\xE3o: canvas_arrange_rows (uma linha por vers\xE3o); posi\xE7\xE3o/tamanho em lote: canvas_update_nodes com layout_only. N\xE3o afirme que nada se sobrep\xF5e com n\xF3 sem medida.',
27072
27553
  '"Aplica os coment\xE1rios" do board: canvas_list_comments (abertos) \u2192 para cada um, leia o n\xF3 ancorado com canvas_read_board (node_ids), aplique a edi\xE7\xE3o pelas canvas_* e feche com canvas_resolve_comment dizendo em uma frase o que fez. Coment\xE1rio amb\xEDguo ou com alvo sumido: responda com pergunta (canvas_reply_comment) e n\xE3o resolva. D\xFAvida sua sobre um n\xF3: canvas_comment ancorado nele. As escritas j\xE1 saem espa\xE7adas (60/min); se ainda vier limite, pare e diga de onde seguir.'