@abbox/inspector 0.4.20 → 0.4.22

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.
@@ -12,10 +12,24 @@ export declare function hideHighlight(highlight: HTMLDivElement): void;
12
12
  export declare function hideSelectedHighlight(selectedHighlight: HTMLDivElement): void;
13
13
  export declare function rectForElement(el: Element): ElementRect;
14
14
  export declare function renderChangeMarkers(layer: HTMLDivElement, markers: ChangeMarker[], resolveElement: (refId: string) => Element | null, onSelect?: (changeId: string) => void): void;
15
+ type PlacedBadge = {
16
+ node: HTMLElement;
17
+ left: number;
18
+ top: number;
19
+ focused: boolean;
20
+ };
21
+ /**
22
+ * Keeps labelled badges readable: each stays inside the page, and one that
23
+ * would cover another moves up a row, keeping its left edge at its own element
24
+ * so it still reads as belonging there. The focused badge is placed first, so
25
+ * it stays exactly on its element.
26
+ */
27
+ export declare function arrangeBadges(badges: PlacedBadge[], viewportWidth?: number): void;
15
28
  /**
16
29
  * A badge sits just above its element and moves with it. With no room above
17
30
  * (the element starts at the top of the screen) it tucks inside the element's
18
31
  * top edge instead, and scrolls away with the element rather than sticking.
19
32
  */
20
33
  export declare function badgeTop(elementTop: number): number;
34
+ export {};
21
35
  //# sourceMappingURL=overlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/runtime/overlay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAMhE,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,iBAAiB,EAAE,cAAc,CAAC;IAClC,WAAW,EAAE,cAAc,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAgD5D;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,cAAc,EACzB,IAAI,EAAE,WAAW,GAChB,IAAI,CAQN;AAED,wBAAgB,yBAAyB,CACvC,iBAAiB,EAAE,cAAc,EACjC,IAAI,EAAE,WAAW,GAChB,IAAI,CAQN;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAE7D;AAED,wBAAgB,qBAAqB,CAAC,iBAAiB,EAAE,cAAc,GAAG,IAAI,CAE7E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,WAAW,CAQvD;AAOD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,YAAY,EAAE,EACvB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,EACjD,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACpC,IAAI,CAyFN;AAID;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGnD"}
1
+ {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/runtime/overlay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAMhE,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,iBAAiB,EAAE,cAAc,CAAC;IAClC,WAAW,EAAE,cAAc,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAgD5D;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,cAAc,EACzB,IAAI,EAAE,WAAW,GAChB,IAAI,CAQN;AAED,wBAAgB,yBAAyB,CACvC,iBAAiB,EAAE,cAAc,EACjC,IAAI,EAAE,WAAW,GAChB,IAAI,CAQN;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAE7D;AAED,wBAAgB,qBAAqB,CAAC,iBAAiB,EAAE,cAAc,GAAG,IAAI,CAE7E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,WAAW,CAQvD;AAOD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,YAAY,EAAE,EACvB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,EACjD,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACpC,IAAI,CAqGN;AAED,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAKF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,aAAa,CAAC,EAAE,MAAM,QAkC1E;AAID;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGnD"}
@@ -92,6 +92,7 @@ export function renderChangeMarkers(layer, markers, resolveElement, onSelect) {
92
92
  markerNodes.delete(id);
93
93
  }
94
94
  }
95
+ const badges = [];
95
96
  for (const marker of markers) {
96
97
  const el = resolveElement(marker.elementRefId);
97
98
  const rect = el?.getBoundingClientRect();
@@ -147,11 +148,12 @@ export function renderChangeMarkers(layer, markers, resolveElement, onSelect) {
147
148
  event.stopPropagation();
148
149
  onSelect?.(marker.elementRefId);
149
150
  };
151
+ // A labelled pill is far wider than a dot, so it needs much less movement.
150
152
  dot.style.animation = marker.pulse
151
- ? "abbox-inspector-pulse 1.4s ease-in-out infinite"
153
+ ? `abbox-inspector-pulse${marker.badge ? "-soft" : ""} 1.4s ease-in-out infinite`
152
154
  : "";
153
155
  dot.style.boxShadow = marker.pulse
154
- ? "0 0 0 4px rgba(255,255,255,0.35)"
156
+ ? `0 0 0 ${marker.badge ? 2 : 4}px rgba(255,255,255,0.35)`
155
157
  : "0 0 0 1px rgba(0,0,0,0.2)";
156
158
  if (marker.pulse)
157
159
  ensurePulseStyle(layer.ownerDocument);
@@ -165,6 +167,48 @@ export function renderChangeMarkers(layer, markers, resolveElement, onSelect) {
165
167
  dot.style.display = onScreen ? "" : "none";
166
168
  dot.style.left = `${rect.x - 6}px`;
167
169
  dot.style.top = `${marker.badge ? badgeTop(rect.top) : rect.top - 6}px`;
170
+ dot.style.zIndex = marker.pulse ? "1" : "";
171
+ if (marker.badge && onScreen) {
172
+ badges.push({
173
+ node: dot,
174
+ left: rect.x - 6,
175
+ top: badgeTop(rect.top),
176
+ focused: Boolean(marker.pulse),
177
+ });
178
+ }
179
+ }
180
+ arrangeBadges(badges, layer.ownerDocument.defaultView?.innerWidth);
181
+ }
182
+ const BADGE_GAP = 2;
183
+ const EDGE_MARGIN = 4;
184
+ /**
185
+ * Keeps labelled badges readable: each stays inside the page, and one that
186
+ * would cover another moves up a row, keeping its left edge at its own element
187
+ * so it still reads as belonging there. The focused badge is placed first, so
188
+ * it stays exactly on its element.
189
+ */
190
+ export function arrangeBadges(badges, viewportWidth) {
191
+ const taken = [];
192
+ const ordered = [...badges].sort((a, b) => Number(b.focused) - Number(a.focused) || a.top - b.top || a.left - b.left);
193
+ for (const badge of ordered) {
194
+ const width = badge.node.offsetWidth;
195
+ const height = badge.node.offsetHeight;
196
+ const maxLeft = (viewportWidth ?? Number.POSITIVE_INFINITY) - width - EDGE_MARGIN;
197
+ const left = Math.max(EDGE_MARGIN, Math.min(badge.left, maxLeft));
198
+ const collides = (top) => taken.some((other) => left < other.right &&
199
+ left + width > other.left &&
200
+ top < other.bottom &&
201
+ top + height > other.top);
202
+ const step = height + BADGE_GAP;
203
+ let top = badge.top;
204
+ while (collides(top) && top - step >= 0)
205
+ top -= step;
206
+ // Out of room above: go below the row instead.
207
+ while (collides(top))
208
+ top += step;
209
+ taken.push({ left, top, right: left + width, bottom: top + height });
210
+ badge.node.style.left = `${left}px`;
211
+ badge.node.style.top = `${top}px`;
168
212
  }
169
213
  }
170
214
  const BADGE_HEIGHT = 18;
@@ -208,6 +252,10 @@ function ensurePulseStyle(doc) {
208
252
  style.textContent = `@keyframes abbox-inspector-pulse {
209
253
  0%, 100% { transform: scale(1); opacity: 1; }
210
254
  50% { transform: scale(1.35); opacity: 0.75; }
255
+ }
256
+ @keyframes abbox-inspector-pulse-soft {
257
+ 0%, 100% { transform: scale(1); opacity: 1; }
258
+ 50% { transform: scale(1.04); opacity: 0.9; }
211
259
  }`;
212
260
  doc.head.append(style);
213
261
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abbox/inspector",
3
- "version": "0.4.20",
3
+ "version": "0.4.22",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },