stitch2 0.22.9 → 0.22.11

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/canvas.html CHANGED
@@ -385,6 +385,15 @@ const rows = () => {
385
385
 
386
386
  // Screens flow left to right and wrap, like a design canvas; each screen's versions stay together.
387
387
  function layout() {
388
+ // Late page/thumbnail heights move later rows. Keep the frame being reviewed
389
+ // at its current screen position, not at a fresh zoomTo position. A frame the
390
+ // user has panned away from, or an active drag, must not pull the camera back.
391
+ const anchor = (() => {
392
+ if (!selected || selected.el.style.display === 'none' || viewport.classList.contains('panning')) return null
393
+ const a = selected.el.getBoundingClientRect(), b = viewport.getBoundingClientRect()
394
+ if (a.right <= b.left || a.left >= b.right || a.bottom <= b.top || a.top >= b.bottom) return null
395
+ return { frame: selected, x: parseFloat(selected.el.style.left), y: parseFloat(selected.el.style.top) }
396
+ })()
388
397
  for (const n of world.querySelectorAll('.row-label')) n.remove()
389
398
  let x = 0, y = 0, lineH = 0, page = ''
390
399
  const all = rows()
@@ -435,6 +444,11 @@ function layout() {
435
444
  }
436
445
  tree()
437
446
  sendFilter()
447
+ if (anchor && selected === anchor.frame && selected.el.style.display !== 'none') {
448
+ const dx = (anchor.x - parseFloat(selected.el.style.left)) * view.z
449
+ const dy = (anchor.y - parseFloat(selected.el.style.top)) * view.z
450
+ if (dx || dy) { view.x += dx; view.y += dy; apply(); return }
451
+ }
438
452
  scheduleSync()
439
453
  }
440
454
 
package/dist/elements.js CHANGED
@@ -37,14 +37,22 @@ export const screenLocale = () => globalThis.__stitch2?.lang;
37
37
  */
38
38
  let idCount = 0;
39
39
  const ID_REFS = ['for', 'aria-labelledby', 'aria-describedby', 'aria-controls', 'aria-owns', 'aria-activedescendant', 'list'];
40
- function uniqueIds(content) {
40
+ function uniqueIds(content, replacing) {
41
+ // The element's children are about to be replaced, not duplicated. Their IDs may also be
42
+ // referenced from outside the component (labels, descriptions and navigation anchors).
43
+ const occupied = new Set(Array.from(document.querySelectorAll('[id]'))
44
+ .filter((el) => !replacing.contains(el)).map((el) => el.id));
41
45
  const renamed = new Map();
42
46
  for (const el of Array.from(content.querySelectorAll('[id]'))) {
43
- if (!document.getElementById(el.id))
47
+ if (!occupied.has(el.id))
44
48
  continue;
45
- const next = el.id + '-s2-' + ++idCount;
49
+ let next;
50
+ do
51
+ next = el.id + '-s2-' + ++idCount;
52
+ while (occupied.has(next) || content.getElementById(next));
46
53
  renamed.set(el.id, next);
47
54
  el.id = next;
55
+ occupied.add(next);
48
56
  }
49
57
  if (!renamed.size)
50
58
  return;
@@ -95,7 +103,7 @@ export function defineElements(components, options) {
95
103
  if (typeof props[key] === 'string' && !root.hasAttribute(`data-${key}`))
96
104
  root.setAttribute(`data-${key}`, props[key]);
97
105
  }
98
- uniqueIds(t.content);
106
+ uniqueIds(t.content, this);
99
107
  this.replaceWith(t.content);
100
108
  if (options.after && !pending++)
101
109
  queueMicrotask(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitch2",
3
- "version": "0.22.9",
3
+ "version": "0.22.11",
4
4
  "description": "A design canvas, layout and type checks, component consistency and screen versioning for agent-built UI, on Google's DESIGN.md format.",
5
5
  "license": "MIT",
6
6
  "author": "ifokeev",
@@ -90,6 +90,9 @@ References, read before the first screen:
90
90
  States sheet (components.md, States).
91
91
  6. **Look and critique.** View the render in `<design>/renders/` (crop the first screenful). Score it with the
92
92
  rubric in principles.md, fix what fails, repeat 5–6 at most three times.
93
+ On the canvas, select a later row and zoom out and back while earlier pages/thumbnails load. The selected
94
+ visible frame keeps its screen position when row heights settle; this does not recenter a frame you pan
95
+ away from or interrupt a drag. Check the live canvas as well as the saved render before hand-over.
93
96
  7. **Hand over.** Set the version's status to `review`, point the user to it on the canvas, and report what
94
97
  changed, how you answered their notes, and any rubric item you could not satisfy. The page's empty and loading
95
98
  screens go to review with it; for a page without them (a form, a static page), say why. The user approves; only then
@@ -61,6 +61,10 @@ never patch the bundle or restyle the element on a screen. Props are the element
61
61
  an empty attribute is true) and its content is children. A component the screens need but the app lacks is
62
62
  added to the app first, with the same name.
63
63
 
64
+ Keep native field IDs and their external label, ARIA and fragment references stable when nesting components.
65
+ `stitch2/elements` preserves IDs from the element being replaced; only real collisions outside that element
66
+ are renamed, together with references inside the component. Test both cases when changing a shared form.
67
+
64
68
  ## Colours come from DESIGN.md
65
69
 
66
70
  Every colour on a screen and in a component is one of DESIGN.md's: a token class (`bg-surface`,