@ptengine/inline-editor 0.1.4 → 0.1.6

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 (51) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/InlineEditor.d.ts +15 -1
  3. package/dist/InlineEditor.js +85 -54
  4. package/dist/canvas-ui/tree/TreePanel.d.ts +2 -1
  5. package/dist/canvas-ui/tree/TreePanel.js +79 -39
  6. package/dist/chrome/EditorChrome.d.ts +2 -0
  7. package/dist/chrome/EditorChrome.js +1 -1
  8. package/dist/chrome/VersionMenu.d.ts +3 -1
  9. package/dist/chrome/VersionMenu.js +24 -12
  10. package/dist/chrome/topbarPrimitives.d.ts +5 -52
  11. package/dist/chrome/topbarPrimitives.js +13 -31
  12. package/dist/dialogs/ClickActionDialog.js +4 -5
  13. package/dist/dialogs/CodeField.d.ts +4 -0
  14. package/dist/dialogs/CodeField.js +10 -0
  15. package/dist/dialogs/HtmlEditDialog.js +3 -2
  16. package/dist/dialogs/ImageEditDialog.js +53 -15
  17. package/dist/dialogs/ImageSettingDialog.js +3 -3
  18. package/dist/dialogs/InsertElementDialog.js +6 -6
  19. package/dist/dialogs/MediaPreview.d.ts +5 -0
  20. package/dist/dialogs/MediaPreview.js +34 -0
  21. package/dist/dialogs/ReplaceVideoDialog.js +8 -4
  22. package/dist/index.d.ts +1 -0
  23. package/dist/kernel/blocks/BlockOverlay.js +1 -1
  24. package/dist/kernel/blocks/resolveBlocks.d.ts +16 -0
  25. package/dist/kernel/blocks/resolveBlocks.js +39 -6
  26. package/dist/kernel/canvasCommands.d.ts +7 -1
  27. package/dist/kernel/canvasCommands.js +3 -0
  28. package/dist/kernel/changelist/ChangeListPanel.js +11 -9
  29. package/dist/kernel/changelist/useChangeList.d.ts +4 -0
  30. package/dist/kernel/changelist/useChangeList.js +84 -10
  31. package/dist/kernel/selection/SelectionOverlay.js +1 -1
  32. package/dist/kernel/selection/SelectionToolbar.js +26 -8
  33. package/dist/kernel/selection/buildRootShelf.d.ts +49 -2
  34. package/dist/kernel/selection/buildRootShelf.js +156 -41
  35. package/dist/kernel/selection/elementAiContext.d.ts +11 -3
  36. package/dist/kernel/selection/resolveAnchors.d.ts +17 -0
  37. package/dist/kernel/selection/resolveAnchors.js +59 -11
  38. package/dist/locales.d.ts +186 -48
  39. package/dist/locales.js +10 -9
  40. package/dist/panels/EditorStatus.d.ts +5 -0
  41. package/dist/panels/EditorStatus.js +4 -0
  42. package/dist/panels/ScreenshotCard.d.ts +7 -0
  43. package/dist/panels/ScreenshotCard.js +57 -0
  44. package/dist/panels/StylePanel.d.ts +7 -1
  45. package/dist/panels/StylePanel.js +105 -12
  46. package/dist/reviewLocales.d.ts +140 -0
  47. package/dist/reviewLocales.js +134 -0
  48. package/dist/tree/LayersPanel.js +4 -2
  49. package/dist/useInlineEditor.d.ts +12 -1
  50. package/dist/useInlineEditor.js +21 -4
  51. package/package.json +1 -1
@@ -1,16 +1,25 @@
1
1
  import { buildDescendantShelfWithPaths } from './buildDescendantShelf.js';
2
2
  import { AI_DESCENDANT_TEXT_CHARS } from './elementAiContext.js';
3
- import { rowPositions } from './rowLayout.js';
3
+ import { SHELF_LAYOUT_FIELDS, laysOutInRow, rowPositions } from './rowLayout.js';
4
+ export const ROOT_SHELF_TREE_PARAMS = {
5
+ rootSelector: 'body',
6
+ maxDepth: 16,
7
+ maxChildren: 40,
8
+ maxNodes: 1500,
9
+ visibleOnly: true,
10
+ includeRect: true,
11
+ fields: [...SHELF_LAYOUT_FIELDS]
12
+ };
4
13
  const LANDMARK_MIN_HEIGHT = 120;
5
14
  const LANDMARK_MIN_WIDTH_RATIO = 0.5;
6
15
  const DEFAULT_VIEWPORT_WIDTH = 375;
7
16
  const LEAVES_PER_LANDMARK = 6;
17
+ const PAGE_SCALE_RATIO = 0.6;
18
+ const SAME_TOP_PX = 2;
8
19
  const CHROME_ROWS_CAP = 5;
9
- const PREFERRED_LANDMARK_TAGS = new Set(['main', 'section', 'article']);
10
20
  const CHROME_TAGS = new Set(['header', 'nav', 'footer']);
11
21
  const TEXT_LEAF_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', 'button', 'span']);
12
- export function buildRootShelf(root, opts) {
13
- const { limit } = opts;
22
+ function analyzeBlockRoots(root, opts) {
14
23
  const viewportWidth = typeof opts.viewportWidth === 'number' && opts.viewportWidth > 0
15
24
  ? opts.viewportWidth
16
25
  : DEFAULT_VIEWPORT_WIDTH;
@@ -19,7 +28,7 @@ export function buildRootShelf(root, opts) {
19
28
  const entryOf = new Map();
20
29
  let sawRect = root.rect !== undefined;
21
30
  const isLandmarkNode = (n) => n.rect !== undefined && n.rect.height >= LANDMARK_MIN_HEIGHT && n.rect.width >= minWidth;
22
- const visit = (n, ancestors, chromeKey, nearestLandmark) => {
31
+ const visit = (n, parent, ancestors, chromeKey) => {
23
32
  for (const child of n.children) {
24
33
  if (child.hidden)
25
34
  continue;
@@ -28,53 +37,154 @@ export function buildRootShelf(root, opts) {
28
37
  const ownChrome = CHROME_TAGS.has(child.tag) ? child.selector : chromeKey;
29
38
  const entry = {
30
39
  node: child,
40
+ parent,
31
41
  ancestors,
32
42
  order: entries.length,
33
43
  chromeKey: ownChrome,
34
- nearestLandmark,
35
- isLandmark: isLandmarkNode(child)
44
+ landmark: isLandmarkNode(child),
45
+ demoted: false
36
46
  };
37
47
  entries.push(entry);
38
48
  entryOf.set(child, entry);
39
- visit(child, [child.selector, ...ancestors], ownChrome, entry.isLandmark ? entry : nearestLandmark);
49
+ visit(child, entry, [child.selector, ...ancestors], ownChrome);
40
50
  }
41
51
  };
42
- visit(root, [root.selector], null, null);
52
+ visit(root, null, [root.selector], null);
43
53
  if (!sawRect)
44
- return buildDescendantShelfWithPaths(root, limit);
45
- const beaten = new Set();
46
- const absorbed = new Set();
47
- for (const e of entries) {
48
- if (!e.isLandmark || !e.nearestLandmark)
49
- continue;
50
- const outer = e.nearestLandmark;
51
- const wins = !!e.node.heading ||
52
- (PREFERRED_LANDMARK_TAGS.has(e.node.tag) && !PREFERRED_LANDMARK_TAGS.has(outer.node.tag));
53
- if (wins)
54
- beaten.add(outer);
54
+ return null;
55
+ const pageHeight = entries.reduce((h, e) => (e.node.rect ? Math.max(h, e.node.rect.top + e.node.rect.height) : h), root.rect?.height ?? 0);
56
+ const isPageScale = (n) => n.rect !== undefined && pageHeight > 0 && n.rect.height >= PAGE_SCALE_RATIO * pageHeight;
57
+ const isLandmarkRow = (parent) => {
58
+ const items = parent.children.filter(c => !c.hidden && isLandmarkNode(c));
59
+ if (items.length < 2 || items.some(isPageScale))
60
+ return false;
61
+ if (laysOutInRow(parent))
62
+ return true;
63
+ const top0 = items[0].rect.top;
64
+ return items.every(c => Math.abs(c.rect.top - top0) <= SAME_TOP_PX);
65
+ };
66
+ const rowParents = new Map();
67
+ const inLandmarkRow = (parent) => {
68
+ let v = rowParents.get(parent);
69
+ if (v === undefined)
70
+ rowParents.set(parent, (v = isLandmarkRow(parent)));
71
+ return v;
72
+ };
73
+ for (const e of entries)
74
+ e.demoted = (e.parent?.demoted ?? false) || inLandmarkRow(e.parent?.node ?? root);
75
+ if (entries.some(e => e.landmark) && !entries.some(e => e.landmark && !e.demoted)) {
76
+ for (const e of entries)
77
+ e.demoted = false;
78
+ }
79
+ const isCandidate = (e) => e.landmark && !e.demoted;
80
+ const entryBySelector = new Map();
81
+ for (const e of entries)
82
+ if (!entryBySelector.has(e.node.selector))
83
+ entryBySelector.set(e.node.selector, e);
84
+ const claimed = new Map();
85
+ const blocksIn = new Map();
86
+ const addTo = (m, e, id) => {
87
+ let set = m.get(e);
88
+ if (!set)
89
+ m.set(e, (set = new Set()));
90
+ set.add(id);
91
+ };
92
+ for (const b of opts.blocks ?? []) {
93
+ for (const member of b.members) {
94
+ let e = entryBySelector.get(member.selector) ?? null;
95
+ while (e && !isCandidate(e))
96
+ e = e.parent;
97
+ if (!e)
98
+ continue;
99
+ addTo(claimed, e, b.id);
100
+ for (let a = e; a; a = a.parent)
101
+ addTo(blocksIn, a, b.id);
102
+ }
103
+ }
104
+ const modelBlockCount = (e) => blocksIn.get(e)?.size ?? 0;
105
+ const isAtomic = (e) => claimed.get(e)?.size === 1 && modelBlockCount(e) === 1;
106
+ const nearestCandidateAncestor = (e) => {
107
+ for (let a = e.parent; a; a = a.parent)
108
+ if (isCandidate(a))
109
+ return a;
110
+ return null;
111
+ };
112
+ const kids = new Map();
113
+ const tops = [];
114
+ const candidates = entries.filter(isCandidate);
115
+ for (const e of candidates) {
116
+ const p = nearestCandidateAncestor(e);
117
+ if (p) {
118
+ const list = kids.get(p) ?? [];
119
+ list.push(e);
120
+ kids.set(p, list);
121
+ }
55
122
  else
56
- absorbed.add(e);
123
+ tops.push(e);
57
124
  }
58
- const surviving = new Set();
59
- for (const e of entries) {
60
- if (!e.isLandmark || beaten.has(e))
61
- continue;
62
- if (absorbed.has(e)) {
63
- let a = e.nearestLandmark;
64
- let hasSurvivingAncestor = false;
65
- while (a) {
66
- if (surviving.has(a)) {
67
- hasSurvivingAncestor = true;
68
- break;
125
+ const isUnit = new Map();
126
+ for (const e of [...candidates].reverse()) {
127
+ const ks = kids.get(e) ?? [];
128
+ let unit;
129
+ if (isAtomic(e))
130
+ unit = true;
131
+ else if (modelBlockCount(e) >= 2)
132
+ unit = ks.length === 0;
133
+ else if (ks.length === 0)
134
+ unit = true;
135
+ else if (ks.length >= 2)
136
+ unit = false;
137
+ else
138
+ unit = (isUnit.get(ks[0]) ?? false) && !isPageScale(e.node);
139
+ isUnit.set(e, unit);
140
+ }
141
+ const blockRoots = [];
142
+ const takeRoots = (list) => {
143
+ for (const e of list) {
144
+ if (isUnit.get(e))
145
+ blockRoots.push(e);
146
+ else
147
+ takeRoots(kids.get(e) ?? []);
148
+ }
149
+ };
150
+ takeRoots(tops);
151
+ const blockIdOf = (e) => {
152
+ const ids = blocksIn.get(e);
153
+ return ids && ids.size === 1 ? [...ids][0] : undefined;
154
+ };
155
+ return { entryOf, entryBySelector, blockRoots, blockIdOf };
156
+ }
157
+ export function liftToBlockRoots(root, opts) {
158
+ const out = new Map();
159
+ const analysis = analyzeBlockRoots(root, opts);
160
+ if (!analysis)
161
+ return out;
162
+ const rootSet = new Set(analysis.blockRoots);
163
+ for (const b of opts.blocks) {
164
+ for (const member of b.members) {
165
+ for (let e = analysis.entryBySelector.get(member.selector) ?? null; e; e = e.parent) {
166
+ if (!rootSet.has(e))
167
+ continue;
168
+ if (analysis.blockIdOf(e) === b.id) {
169
+ let byMember = out.get(b.id);
170
+ if (!byMember)
171
+ out.set(b.id, (byMember = new Map()));
172
+ byMember.set(member.selector, e.node);
69
173
  }
70
- a = a.nearestLandmark;
174
+ break;
71
175
  }
72
- if (hasSurvivingAncestor)
73
- continue;
74
176
  }
75
- surviving.add(e);
76
177
  }
77
- const landmarks = [...surviving].sort((a, b) => {
178
+ return out;
179
+ }
180
+ export function buildRootShelf(root, opts) {
181
+ const { limit } = opts;
182
+ const analysis = analyzeBlockRoots(root, opts);
183
+ if (!analysis)
184
+ return buildDescendantShelfWithPaths(root, limit);
185
+ const { entryOf, blockRoots, blockIdOf } = analysis;
186
+ const rootSet = new Set(blockRoots);
187
+ const landmarks = [...blockRoots].sort((a, b) => {
78
188
  const dt = a.node.rect.top - b.node.rect.top;
79
189
  return dt !== 0 ? dt : a.order - b.order;
80
190
  });
@@ -83,7 +193,7 @@ export function buildRootShelf(root, opts) {
83
193
  const ancestorsOf = new Map();
84
194
  const emittedSelectors = new Set();
85
195
  const chromeRows = new Map();
86
- const emit = (e) => {
196
+ const emit = (e, blockId) => {
87
197
  if (shelf.length >= limit)
88
198
  return false;
89
199
  if (emittedSelectors.has(e.node.selector))
@@ -107,6 +217,8 @@ export function buildRootShelf(root, opts) {
107
217
  const inRow = inRowOf.get(n.selector);
108
218
  if (inRow)
109
219
  row.inRow = inRow;
220
+ if (blockId)
221
+ row.blockId = blockId;
110
222
  shelf.push(row);
111
223
  ancestorsOf.set(n.selector, e.ancestors);
112
224
  emittedSelectors.add(n.selector);
@@ -115,21 +227,24 @@ export function buildRootShelf(root, opts) {
115
227
  return true;
116
228
  };
117
229
  const isTextLeaf = (n) => n.tag === 'img' || (TEXT_LEAF_TAGS.has(n.tag) && n.textSummary.trim().length > 0);
230
+ const leavesPerBlock = landmarks.length
231
+ ? Math.min(LEAVES_PER_LANDMARK, Math.max(1, Math.floor((limit - landmarks.length) / landmarks.length)))
232
+ : LEAVES_PER_LANDMARK;
118
233
  for (const lm of landmarks) {
119
234
  if (shelf.length >= limit)
120
235
  break;
121
- emit(lm);
236
+ emit(lm, blockIdOf(lm));
122
237
  let taken = 0;
123
238
  const collect = (n) => {
124
239
  for (const child of n.children) {
125
- if (taken >= LEAVES_PER_LANDMARK || shelf.length >= limit)
240
+ if (taken >= leavesPerBlock || shelf.length >= limit)
126
241
  return;
127
242
  if (child.hidden)
128
243
  continue;
129
244
  const ce = entryOf.get(child);
130
245
  if (!ce)
131
246
  continue;
132
- if (surviving.has(ce) && ce !== lm)
247
+ if (rootSet.has(ce) && ce !== lm)
133
248
  continue;
134
249
  if (isTextLeaf(child) && emit(ce))
135
250
  taken += 1;
@@ -77,11 +77,19 @@ export interface AiDescendant {
77
77
  */
78
78
  inRow?: RowPosition;
79
79
  /**
80
- * 区块身份(宿主按页面模型的 member 容器打的,如 `B2 首屏 h:740`),写成行尾的 `[…]`。
80
+ * 区块身份(宿主打的,如 `B2 首屏 h:740`),写成行尾的 `[…]`。
81
81
  * 没选中元素时「首屏下面」要有确定的锚:同一 `top`、同一段文字的包装行常有十几个,没有它
82
- * 「最外层容器」会被选成整页包装层(真机 2026-10-16)。只有恰好是区块容器的那一行才有。
82
+ * 「最外层容器」会被选成整页包装层(真机 2026-10-16)。宿主按 `blockId`(新内核)打;
83
+ * 没有 `blockId` 的老内核货架,退回「selector 与页面模型 member 逐字相等」。
83
84
  */
84
85
  block?: string;
86
+ /**
87
+ * 页面模型区块 id(内核打的):这一行是**只装着这一个区块的最外层容器**(块根),由
88
+ * `buildRootShelf` 的 `blocks` 认领得出 —— 热图成员是轮播第 1 张时,id 落在整个首屏 section
89
+ * 上,而不是那一张上(真机 2026-10-20)。宿主据此格式化成 `block`。没传 `blocks`、或这一行
90
+ * 不是某个区块的块根时没有这个键。
91
+ */
92
+ blockId?: string;
85
93
  }
86
94
  /**
87
95
  * 区块级货架取树的口径(`<page-block>`、方案锚点子树、选中元素货架三处共用)。
@@ -292,7 +300,7 @@ export declare const AI_DESCENDANT_LIMIT = 30;
292
300
  * 60 → 100:货架改成「区块优先」(`buildRootShelf`)之后,每个区块是 1 行容器 + 最多 6 行
293
301
  * 可改点,一个 10 个区块的落地页就是 70 行;60 会把最后几个区块整段切掉,AI 又回到
294
302
  * 「找不到要改的那一块」。100 = 约 13 个区块,长落地页也够;header/nav/footer 各封 5 行,
295
- * 不会被它们吃掉。
303
+ * 不会被它们吃掉。区块再多时 `buildRootShelf` 按块数压低每块的叶子数,块根行一个不落。
296
304
  *
297
305
  * 也不能顺手把 `AI_DESCENDANT_LIMIT` 一起调大:那份是**每轮对话**的固定开销,
298
306
  * 整页这份一次会话只算一次。
@@ -35,6 +35,15 @@ export interface InlinePlanAnchor {
35
35
  op?: string;
36
36
  mode?: string;
37
37
  placement?: string;
38
+ /**
39
+ * 只给 MOVE_ELEMENT:移到哪(`mode` 相对它:before / after / top / bottom)。移动的落点是**一对**
40
+ * (移谁 + 移到哪),只存 selector 的话首版只知道要挪、不知道挪到哪(2026-10-21 trace 973f6deb:
41
+ * 「新闻区挪到畅销榜后面」)。编辑器打开时与锚点一样在活页面上重新解析,命中的目标并进货架 ——
42
+ * 整页模式下 MOVE 的 target 只认货架行(`pageMoveTargetProblem`)。
43
+ */
44
+ targetSelector?: string;
45
+ /** 目标那一行的标题 / 首句 —— `targetSelector` 失效时按它找回来(与 `label` 同一套规则)。 */
46
+ targetLabel?: string;
38
47
  /**
39
48
  * 该区块(B{n})在活页面上的 member 容器 selector,外层优先。**只在内存里**(宿主从页面模型
40
49
  * 补进来),不落库、不与 backend 同形。
@@ -55,6 +64,14 @@ export interface ResolvedAnchor extends InlinePlanAnchor {
55
64
  resolved: boolean;
56
65
  /** 哪条路成功的。`label` = 原 selector 已失效(客户改版),值得让 agent 知道。 */
57
66
  via?: 'selector' | 'label';
67
+ /**
68
+ * MOVE 目标(`targetSelector`)的解析结果;锚点没有目标、或锚点自己没解析到时不写。
69
+ * `true` = 目标在活页面上唯一命中、且已作为一行并进货架与祖先表(agent 抄它就过得了 MOVE 的 target 闸)。
70
+ */
71
+ targetResolved?: boolean;
72
+ targetVia?: 'selector' | 'label';
73
+ /** 目标在活页面上的 selector。`targetVia: 'label'` 时与存下来的 `targetSelector` 不同 —— 宿主打印这一个。 */
74
+ resolvedTargetSelector?: string;
58
75
  }
59
76
  export interface AnchorShelfResult {
60
77
  /** 锚点容器行在前、其子树行在后,按锚点顺序。 */
@@ -64,6 +64,17 @@ export function withImageRows(shelf, anchorRowCount, images) {
64
64
  function normalizeLabel(s) {
65
65
  return s.replace(/\s+/g, ' ').trim().toLowerCase();
66
66
  }
67
+ function indexNodes(root) {
68
+ const map = new Map();
69
+ const walk = (n) => {
70
+ if (!map.has(n.selector))
71
+ map.set(n.selector, n);
72
+ for (const child of n.children)
73
+ walk(child);
74
+ };
75
+ walk(root);
76
+ return map;
77
+ }
67
78
  function indexAncestors(root) {
68
79
  const map = new Map();
69
80
  const walk = (n, ancestors) => {
@@ -117,6 +128,7 @@ export async function resolveInlinePlanAnchors(input) {
117
128
  const outcomes = [];
118
129
  const seen = new Set();
119
130
  const rootAncestors = indexAncestors(rootTree);
131
+ const rootNodes = indexNodes(rootTree);
120
132
  const rootInRow = rowPositions(rootTree);
121
133
  const push = (row, chain) => {
122
134
  if (seen.has(row.selector))
@@ -125,6 +137,37 @@ export async function resolveInlinePlanAnchors(input) {
125
137
  shelf.push(row);
126
138
  ancestorsOf.set(row.selector, chain);
127
139
  };
140
+ const resolveTarget = async (selector, label) => {
141
+ let found = null;
142
+ let targetVia;
143
+ try {
144
+ const probe = await resolveSelector(selector);
145
+ if (probe.matched && probe.matchCount === 1) {
146
+ found = selector;
147
+ targetVia = 'selector';
148
+ }
149
+ }
150
+ catch {
151
+ }
152
+ if (!found && label) {
153
+ const byLabel = findUniqueByLabel(rootTree, label);
154
+ if (byLabel) {
155
+ found = byLabel.selector;
156
+ targetVia = 'label';
157
+ }
158
+ }
159
+ if (!found)
160
+ return { targetResolved: false };
161
+ if (!seen.has(found)) {
162
+ const node = rootNodes.get(found);
163
+ if (!node)
164
+ return { targetResolved: false };
165
+ const inRow = rootInRow.get(found);
166
+ const row = rowOf(node);
167
+ push(inRow ? { ...row, inRow } : row, rootAncestors.get(found) ?? []);
168
+ }
169
+ return { targetResolved: true, targetVia, resolvedTargetSelector: found };
170
+ };
128
171
  for (const anchor of anchors) {
129
172
  let resolvedSelector = null;
130
173
  let via;
@@ -148,7 +191,8 @@ export async function resolveInlinePlanAnchors(input) {
148
191
  outcomes.push({ ...anchor, resolved: false });
149
192
  continue;
150
193
  }
151
- outcomes.push({ ...anchor, resolved: true, via });
194
+ const outcome = { ...anchor, resolved: true, via };
195
+ outcomes.push(outcome);
152
196
  const ownAncestors = rootAncestors.get(resolvedSelector) ?? [];
153
197
  const ownInRow = rootInRow.get(resolvedSelector);
154
198
  const withOwnInRow = (row) => ownInRow ? { ...row, inRow: ownInRow } : row;
@@ -160,17 +204,21 @@ export async function resolveInlinePlanAnchors(input) {
160
204
  }
161
205
  if (!subtree) {
162
206
  push(withOwnInRow({ selector: resolvedSelector, tag: '', text: '', hasChildren: true }), ownAncestors);
163
- continue;
164
207
  }
165
- const anchorSelector = subtree.selector || resolvedSelector;
166
- push(withOwnInRow(rowOf(subtree)), ownAncestors);
167
- const { shelf: rows, ancestorsOf: within } = buildDescendantShelfWithPaths(subtree, limitPerAnchor);
168
- for (const row of rows) {
169
- const chainWithin = within.get(row.selector) ?? [];
170
- const chain = chainWithin.includes(anchorSelector)
171
- ? [...chainWithin, ...ownAncestors]
172
- : [...chainWithin, anchorSelector, ...ownAncestors];
173
- push({ ...row, depth: (row.depth ?? 0) + 1 }, chain);
208
+ else {
209
+ const anchorSelector = subtree.selector || resolvedSelector;
210
+ push(withOwnInRow(rowOf(subtree)), ownAncestors);
211
+ const { shelf: rows, ancestorsOf: within } = buildDescendantShelfWithPaths(subtree, limitPerAnchor);
212
+ for (const row of rows) {
213
+ const chainWithin = within.get(row.selector) ?? [];
214
+ const chain = chainWithin.includes(anchorSelector)
215
+ ? [...chainWithin, ...ownAncestors]
216
+ : [...chainWithin, anchorSelector, ...ownAncestors];
217
+ push({ ...row, depth: (row.depth ?? 0) + 1 }, chain);
218
+ }
219
+ }
220
+ if (anchor.targetSelector) {
221
+ Object.assign(outcome, await resolveTarget(anchor.targetSelector, anchor.targetLabel));
174
222
  }
175
223
  }
176
224
  return { shelf, ancestorsOf, anchors: outcomes };