openpencil/scripts/probe-layout.ts
Kayshen-X a7d73ebb62 feat(ai): pencil-style agentic design tool-loop, multi-chat tabs, #27 panel restyle
Built-in design generation now runs as an agentic MCP tool-loop (reusing the
agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the
Settings experimental toggle; the orchestrator stays the default.

- design-agent system prompt + in-process design toolset (parity-locked with
  the MCP surface) + flag-gated Intent::Design routing
- spawn_agents execution as sequential sub-loops + live creation-mode badges
  (per-agent glow + 'N/M designing...' header)
- new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state,
  export_nodes, spawn_agents; style-guide local audit
- #27 AI panel restyle: rounded tool cards + green check-rings, gray user
  bubbles, model-pill bottom toolbar, header, empty-state pills, the
  PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown
- multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI
  (switch / close / + / Cmd+T) with each run bound to its tab

Large checkpoint commit spanning the working tree (Rust shell crates).
2026-07-02 21:21:06 +08:00

102 lines
2.9 KiB
TypeScript

/* eslint-disable no-console */
// Drive `computeLayoutPositions` directly on the 5-card horizontal
// frame from /tmp/op-figma-ts.json and print the children x/y AFTER
// layout, so we can compare with authored values.
import { readFileSync } from 'node:fs';
import { computeLayoutPositions } from '../packages/pen-core/src/layout/engine';
import type { PenNode } from '../packages/pen-types/src';
const doc = JSON.parse(readFileSync('/tmp/op-figma-ts.json', 'utf8'));
function find5CardFrame(node: PenNode): PenNode | null {
if (
node.type === 'frame' &&
(node as any).layout === 'horizontal' &&
Array.isArray((node as any).children)
) {
const flat: string[] = [];
function go(x: any): void {
if (x?.type === 'text') {
const t =
typeof x.content === 'string'
? x.content
: Array.isArray(x.content)
? x.content.map((s: any) => s.text).join('')
: '';
flat.push(t);
}
if (x?.children) for (const c of x.children) go(c);
}
for (const c of (node as any).children) go(c);
if (flat.includes('一级高风险') && flat.includes('其他')) return node;
}
if ('children' in (node as any)) {
for (const c of (node as any).children ?? []) {
const r = find5CardFrame(c);
if (r) return r;
}
}
return null;
}
let parent: PenNode | null = null;
for (const p of doc.pages ?? []) {
for (const c of p.children) {
if (!parent) parent = find5CardFrame(c);
}
}
if (!parent) {
console.error('not found');
process.exit(1);
}
const kids = (parent as any).children;
console.log('AUTHORED:');
kids.forEach((c: any, i: number) => {
const t = (() => {
const out: string[] = [];
function go(x: any): void {
if (x?.type === 'text') {
out.push(
typeof x.content === 'string'
? x.content
: Array.isArray(x.content)
? x.content.map((s: any) => s.text).join('')
: '',
);
}
if (x?.children) for (const c of x.children) go(c);
}
go(c);
return out[0] ?? '';
})();
console.log(
` [${i}] ${c.type} x=${c.x} y=${c.y} w=${JSON.stringify((c as any).width)} text=${JSON.stringify(t)}`,
);
});
console.log('\nAFTER computeLayoutPositions:');
const after = computeLayoutPositions(parent, kids);
after.forEach((c: any, i: number) => {
const t = (() => {
const out: string[] = [];
function go(x: any): void {
if (x?.type === 'text') {
out.push(
typeof x.content === 'string'
? x.content
: Array.isArray(x.content)
? x.content.map((s: any) => s.text).join('')
: '',
);
}
if (x?.children) for (const c of x.children) go(c);
}
go(c);
return out[0] ?? '';
})();
console.log(
` [${i}] ${c.type} x=${c.x} y=${c.y} w=${JSON.stringify((c as any).width)} text=${JSON.stringify(t)}`,
);
});