| 1529 | |
| 1530 | /** Parse layoutCode string into a map of scoped ID → layout entry */ |
| 1531 | export function parseLayoutCode(layoutCode: string): Record<string, { x: number; y: number; w?: number; h?: number; expanded?: boolean }> { |
| 1532 | const map: Record<string, { x: number; y: number; w?: number; h?: number; expanded?: boolean }> = {}; |
| 1533 | if (!layoutCode) return map; |
| 1534 | for (const line of layoutCode.split('\n')) { |
| 1535 | const trimmed = line.trim(); |
| 1536 | if (!trimmed) continue; |
| 1537 | // Format: scopedId @layout x y [WxH] [expanded|collapsed] |
| 1538 | const match = trimmed.match(/^(.+?)\s+@layout\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)(?:\s+(\d+(?:\.\d+)?)x(\d+(?:\.\d+)?))?(?:\s+(collapsed|expanded))?\s*$/); |
| 1539 | if (!match) continue; |
| 1540 | const [, scopedId, xStr, yStr, wStr, hStr, state] = match; |
| 1541 | const entry: { x: number; y: number; w?: number; h?: number; expanded?: boolean } = { |
| 1542 | x: parseFloat(xStr), |
| 1543 | y: parseFloat(yStr), |
| 1544 | }; |
| 1545 | if (wStr && hStr) { |
| 1546 | entry.w = parseFloat(wStr); |
| 1547 | entry.h = parseFloat(hStr); |
| 1548 | } |
| 1549 | if (state === 'expanded') entry.expanded = true; |
| 1550 | if (state === 'collapsed') entry.expanded = false; |
| 1551 | map[scopedId] = entry; |
| 1552 | } |
| 1553 | return map; |
| 1554 | } |
| 1555 | |
| 1556 | /** Update or insert a layout entry in layoutCode. Returns the new layoutCode string. */ |
| 1557 | export function updateLayoutEntry( |