@a2f0/agent-tool 0.1.32 → 0.1.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2f0/agent-tool",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "packageManager": "bun@1.4.3",
5
5
  "type": "module",
6
6
  "description": "Portable cross-agent review, PR workflows, and shared skills",
@@ -1,9 +1,16 @@
1
1
  /*
2
2
  * Styles for <screenshot-gallery> and <screenshot-carousel>
3
- * (@a2f0/agent-tool/screenshots/gallery.css). Every rule sits in :where(), so
4
- * any site selector overrides it; restyle through the --shots-* custom
5
- * properties first, set on the elements or any ancestor. Defaults, which
6
- * follow prefers-color-scheme, live in the internal --_shots-* properties.
3
+ * (@a2f0/agent-tool/screenshots/gallery.css). Restyle through the --shots-*
4
+ * custom properties first, set on the elements or any ancestor. Defaults,
5
+ * which follow prefers-color-scheme, live in the internal --_shots-*
6
+ * properties.
7
+ *
8
+ * Specificity: rules on the host elements are zero (:where), so any site
9
+ * selector overrides them. Every rule on a part the elements render has
10
+ * exactly one class of specificity (0,1,0), with its context in :where(), so
11
+ * element resets such as `button { background: none }` never reach the
12
+ * parts, while any site rule with a class and an element or a second class
13
+ * (`screenshot-gallery .shots-frame`) still overrides them.
7
14
  */
8
15
 
9
16
  :where(screenshot-gallery, screenshot-carousel) {
@@ -55,30 +62,30 @@
55
62
  }
56
63
  }
57
64
 
58
- :where(.shots-host) :where(button) {
65
+ .shots-host :where(button) {
59
66
  font: inherit;
60
67
  color: inherit;
61
68
  }
62
69
 
63
- :where(.shots-host) :where(button:focus-visible, a:focus-visible, [tabindex]:focus-visible) {
70
+ :where(.shots-host) :where(button, a, [tabindex]):focus-visible {
64
71
  outline: 2px solid var(--_shots-focus);
65
72
  outline-offset: 2px;
66
73
  }
67
74
 
68
- :where(.shots-icon) {
75
+ .shots-icon {
69
76
  display: inline-flex;
70
77
  width: 1.25rem;
71
78
  height: 1.25rem;
72
79
  }
73
80
 
74
- :where(.shots-icon svg) {
81
+ .shots-icon :where(svg) {
75
82
  width: 100%;
76
83
  height: 100%;
77
84
  }
78
85
 
79
86
  /* --- States --- */
80
87
 
81
- :where(.shots-status) {
88
+ .shots-status {
82
89
  display: grid;
83
90
  place-items: center;
84
91
  padding: 2rem 1rem;
@@ -90,13 +97,13 @@
90
97
  }
91
98
 
92
99
  /* Loading reserves the gallery's height so the page does not jump when it arrives. */
93
- :where(screenshot-gallery) :where(.shots-status[data-state="loading"]) {
100
+ :where(screenshot-gallery) .shots-status:where([data-state="loading"]) {
94
101
  min-height: calc(var(--_shots-stage-height) + var(--_shots-thumb-height) + 7rem);
95
102
  }
96
103
 
97
104
  /* --- Gallery --- */
98
105
 
99
- :where(.shots-gallery) {
106
+ .shots-gallery {
100
107
  display: flex;
101
108
  flex-direction: column;
102
109
  border: 1px solid var(--_shots-border);
@@ -105,7 +112,7 @@
105
112
  overflow: hidden;
106
113
  }
107
114
 
108
- :where(.shots-gallery[data-expanded]) {
115
+ .shots-gallery:where([data-expanded]) {
109
116
  position: fixed;
110
117
  inset: 0;
111
118
  z-index: var(--_shots-expanded-z);
@@ -113,7 +120,7 @@
113
120
  border-radius: 0;
114
121
  }
115
122
 
116
- :where(.shots-toolbar) {
123
+ .shots-toolbar {
117
124
  display: flex;
118
125
  flex-wrap: wrap;
119
126
  align-items: center;
@@ -122,7 +129,7 @@
122
129
  border-bottom: 1px solid var(--_shots-border);
123
130
  }
124
131
 
125
- :where(.shots-title) {
132
+ .shots-title {
126
133
  flex: 1 1 10rem;
127
134
  margin: 0;
128
135
  font-weight: 600;
@@ -131,14 +138,14 @@
131
138
  white-space: nowrap;
132
139
  }
133
140
 
134
- :where(.shots-toggle) {
141
+ .shots-toggle {
135
142
  display: inline-flex;
136
143
  padding: 2px;
137
144
  border: 1px solid var(--_shots-border);
138
145
  border-radius: 999px;
139
146
  }
140
147
 
141
- :where(.shots-toggle-button) {
148
+ .shots-toggle-button {
142
149
  display: inline-flex;
143
150
  align-items: center;
144
151
  gap: 0.375rem;
@@ -149,17 +156,17 @@
149
156
  cursor: pointer;
150
157
  }
151
158
 
152
- :where(.shots-toggle-button[aria-pressed="true"]) {
159
+ .shots-toggle-button:where([aria-pressed="true"]) {
153
160
  background: var(--_shots-accent);
154
161
  color: var(--_shots-accent-ink);
155
162
  }
156
163
 
157
- :where(.shots-actions) {
164
+ .shots-actions {
158
165
  display: inline-flex;
159
166
  gap: 0.25rem;
160
167
  }
161
168
 
162
- :where(.shots-icon-button) {
169
+ .shots-icon-button {
163
170
  display: inline-grid;
164
171
  place-items: center;
165
172
  width: var(--_shots-button-size);
@@ -171,12 +178,12 @@
171
178
  cursor: pointer;
172
179
  }
173
180
 
174
- :where(.shots-icon-button:disabled) {
181
+ .shots-icon-button:where(:disabled) {
175
182
  opacity: 0.4;
176
183
  cursor: default;
177
184
  }
178
185
 
179
- :where(.shots-body) {
186
+ .shots-body {
180
187
  display: flex;
181
188
  flex: 1 1 auto;
182
189
  flex-direction: column;
@@ -184,7 +191,7 @@
184
191
  }
185
192
 
186
193
  /* The main panel: one screen per page of a horizontal scroll-snap track. */
187
- :where(.shots-track) {
194
+ .shots-track {
188
195
  position: relative;
189
196
  display: flex;
190
197
  height: var(--_shots-stage-height);
@@ -196,16 +203,16 @@
196
203
  background: var(--_shots-surface-raised);
197
204
  }
198
205
 
199
- :where(.shots-gallery[data-expanded]) :where(.shots-track) {
206
+ :where(.shots-gallery[data-expanded]) .shots-track {
200
207
  flex: 1 1 auto;
201
208
  height: auto;
202
209
  }
203
210
 
204
- :where(.shots-track)::-webkit-scrollbar {
211
+ .shots-track::-webkit-scrollbar {
205
212
  display: none;
206
213
  }
207
214
 
208
- :where(.shots-slide) {
215
+ .shots-slide {
209
216
  display: flex;
210
217
  flex: 0 0 100%;
211
218
  align-items: center;
@@ -217,7 +224,7 @@
217
224
  scroll-snap-stop: always;
218
225
  }
219
226
 
220
- :where(.shots-frame) {
227
+ .shots-frame {
221
228
  display: block;
222
229
  box-sizing: content-box;
223
230
  max-width: 100%;
@@ -229,14 +236,14 @@
229
236
  cursor: zoom-in;
230
237
  }
231
238
 
232
- :where(.shots-image) {
239
+ .shots-image {
233
240
  display: block;
234
241
  max-width: 100%;
235
242
  height: auto;
236
243
  }
237
244
 
238
245
  /* Phone and tablet captures sit in a device bezel sized from the capture (fitFrame). */
239
- :where(.shots-gallery[data-frame="phone"], .shots-gallery[data-frame="tablet"]) :where(.shots-frame:not(.shots-missing)) {
246
+ :where(.shots-gallery[data-frame="phone"], .shots-gallery[data-frame="tablet"]) .shots-frame:where(:not(.shots-missing)) {
240
247
  padding: var(--shots-frame-pad, 0.6rem);
241
248
  border: 0;
242
249
  border-radius: calc(var(--shots-frame-radius, 1.5rem) + var(--shots-frame-pad, 0.6rem));
@@ -244,11 +251,11 @@
244
251
  box-shadow: inset 0 0 0 1px var(--_shots-bezel-edge), var(--_shots-shadow);
245
252
  }
246
253
 
247
- :where(.shots-gallery[data-frame="phone"], .shots-gallery[data-frame="tablet"]) :where(.shots-frame:not(.shots-missing) .shots-image) {
254
+ :where(.shots-gallery[data-frame="phone"], .shots-gallery[data-frame="tablet"]) :where(.shots-frame:not(.shots-missing)) .shots-image {
248
255
  border-radius: var(--shots-frame-radius, 1.5rem);
249
256
  }
250
257
 
251
- :where(.shots-missing) {
258
+ .shots-missing {
252
259
  display: grid;
253
260
  place-items: center;
254
261
  max-width: 24rem;
@@ -258,7 +265,7 @@
258
265
  cursor: default;
259
266
  }
260
267
 
261
- :where(.shots-navrow) {
268
+ .shots-navrow {
262
269
  display: flex;
263
270
  align-items: center;
264
271
  justify-content: center;
@@ -266,7 +273,7 @@
266
273
  padding: 0.5rem;
267
274
  }
268
275
 
269
- :where(.shots-counter) {
276
+ .shots-counter {
270
277
  min-width: 4.5rem;
271
278
  margin: 0;
272
279
  color: var(--_shots-ink-muted);
@@ -274,7 +281,7 @@
274
281
  text-align: center;
275
282
  }
276
283
 
277
- :where(.shots-filmstrip) {
284
+ .shots-filmstrip {
278
285
  position: relative;
279
286
  display: flex;
280
287
  gap: 0.5rem;
@@ -284,7 +291,7 @@
284
291
  scroll-snap-type: x proximity;
285
292
  }
286
293
 
287
- :where(.shots-thumb) {
294
+ .shots-thumb {
288
295
  display: flex;
289
296
  flex: 0 0 auto;
290
297
  flex-direction: column;
@@ -298,12 +305,12 @@
298
305
  scroll-snap-align: start;
299
306
  }
300
307
 
301
- :where(.shots-thumb[aria-current="true"]) {
308
+ .shots-thumb:where([aria-current="true"]) {
302
309
  border-color: var(--_shots-accent);
303
310
  }
304
311
 
305
312
  /* The image keeps its capture's aspect ratio at a fixed height, cropped from the top only when wider than the cap. */
306
- :where(.shots-thumb-image) {
313
+ .shots-thumb-image {
307
314
  display: block;
308
315
  height: var(--_shots-thumb-height);
309
316
  min-width: 4.5rem;
@@ -314,7 +321,7 @@
314
321
  overflow: hidden;
315
322
  }
316
323
 
317
- :where(.shots-thumb-image img) {
324
+ .shots-thumb-image :where(img) {
318
325
  display: block;
319
326
  width: 100%;
320
327
  height: 100%;
@@ -322,7 +329,7 @@
322
329
  object-position: top;
323
330
  }
324
331
 
325
- :where(.shots-thumb-label) {
332
+ .shots-thumb-label {
326
333
  max-width: calc(var(--_shots-thumb-max-width) - 0.5rem);
327
334
  overflow: hidden;
328
335
  color: var(--_shots-ink-muted);
@@ -332,11 +339,11 @@
332
339
  white-space: nowrap;
333
340
  }
334
341
 
335
- :where(.shots-thumb[aria-current="true"]) :where(.shots-thumb-label) {
342
+ :where(.shots-thumb[aria-current="true"]) .shots-thumb-label {
336
343
  color: var(--_shots-ink);
337
344
  }
338
345
 
339
- :where(.shots-lightbox) {
346
+ .shots-lightbox {
340
347
  max-width: 100vw;
341
348
  max-height: 100vh;
342
349
  padding: 0;
@@ -345,11 +352,11 @@
345
352
  overflow: visible;
346
353
  }
347
354
 
348
- :where(.shots-lightbox)::backdrop {
355
+ .shots-lightbox::backdrop {
349
356
  background: var(--_shots-backdrop);
350
357
  }
351
358
 
352
- :where(.shots-lightbox-backdrop) {
359
+ .shots-lightbox-backdrop {
353
360
  position: fixed;
354
361
  inset: 0;
355
362
  border: 0;
@@ -357,11 +364,11 @@
357
364
  cursor: zoom-out;
358
365
  }
359
366
 
360
- :where(.shots-lightbox-frame) {
367
+ .shots-lightbox-frame {
361
368
  position: relative;
362
369
  }
363
370
 
364
- :where(.shots-lightbox-image) {
371
+ .shots-lightbox-image {
365
372
  display: block;
366
373
  width: auto;
367
374
  height: auto;
@@ -370,18 +377,18 @@
370
377
  border-radius: var(--shots-lightbox-radius, 8px);
371
378
  }
372
379
 
373
- :where(.shots-lightbox[data-frame="phone"], .shots-lightbox[data-frame="tablet"]) :where(.shots-lightbox-frame) {
380
+ :where(.shots-lightbox[data-frame="phone"], .shots-lightbox[data-frame="tablet"]) .shots-lightbox-frame {
374
381
  padding: var(--shots-frame-pad, 0.75rem);
375
382
  border-radius: calc(var(--shots-frame-radius, 2rem) + var(--shots-frame-pad, 0.75rem));
376
383
  background: var(--_shots-bezel);
377
384
  box-shadow: inset 0 0 0 1px var(--_shots-bezel-edge);
378
385
  }
379
386
 
380
- :where(.shots-lightbox[data-frame="phone"], .shots-lightbox[data-frame="tablet"]) :where(.shots-lightbox-image) {
387
+ :where(.shots-lightbox[data-frame="phone"], .shots-lightbox[data-frame="tablet"]) .shots-lightbox-image {
381
388
  border-radius: var(--shots-frame-radius, 2rem);
382
389
  }
383
390
 
384
- :where(.shots-lightbox-close) {
391
+ .shots-lightbox-close {
385
392
  position: fixed;
386
393
  top: 1rem;
387
394
  right: 1rem;
@@ -397,7 +404,7 @@
397
404
  cursor: pointer;
398
405
  }
399
406
 
400
- :where(.shots-lightbox) :where(.shots-missing) {
407
+ :where(.shots-lightbox) .shots-missing {
401
408
  position: relative;
402
409
  background: var(--_shots-surface);
403
410
  border-radius: var(--_shots-radius);
@@ -405,11 +412,11 @@
405
412
 
406
413
  /* --- Carousel --- */
407
414
 
408
- :where(.shots-carousel) {
415
+ .shots-carousel {
409
416
  position: relative;
410
417
  }
411
418
 
412
- :where(.shots-carousel-track) {
419
+ .shots-carousel-track {
413
420
  position: relative;
414
421
  overflow-x: auto;
415
422
  overscroll-behavior-x: contain;
@@ -419,11 +426,11 @@
419
426
  scrollbar-width: none;
420
427
  }
421
428
 
422
- :where(.shots-carousel-track)::-webkit-scrollbar {
429
+ .shots-carousel-track::-webkit-scrollbar {
423
430
  display: none;
424
431
  }
425
432
 
426
- :where(.shots-carousel-list) {
433
+ .shots-carousel-list {
427
434
  display: flex;
428
435
  gap: var(--_shots-carousel-gap);
429
436
  margin: 0;
@@ -431,33 +438,33 @@
431
438
  list-style: none;
432
439
  }
433
440
 
434
- :where(.shots-carousel-slide) {
441
+ .shots-carousel-slide {
435
442
  flex: 0 0 var(--_shots-carousel-slide-width);
436
443
  scroll-snap-align: start;
437
444
  }
438
445
 
439
- :where(.shots-carousel-slide figure) {
446
+ .shots-carousel-slide :where(figure) {
440
447
  margin: 0;
441
448
  }
442
449
 
443
- :where(.shots-carousel-slide figcaption) {
450
+ .shots-carousel-slide :where(figcaption) {
444
451
  margin-top: 0.75rem;
445
452
  color: var(--_shots-ink-muted);
446
453
  font-size: 0.875rem;
447
454
  text-align: center;
448
455
  }
449
456
 
450
- :where(.shots-device) {
457
+ .shots-device {
451
458
  display: block;
452
459
  color: inherit;
453
460
  container-type: inline-size;
454
461
  }
455
462
 
456
- :where(.shots-device picture) {
463
+ .shots-device :where(picture) {
457
464
  display: block;
458
465
  }
459
466
 
460
- :where(.shots-device img) {
467
+ .shots-device :where(img) {
461
468
  display: block;
462
469
  width: 100%;
463
470
  height: auto;
@@ -465,7 +472,7 @@
465
472
  }
466
473
 
467
474
  /* The bezel scales with the device's width, so it keeps its proportions at every size (FRAME_METRICS). */
468
- :where(.shots-carousel[data-frame="phone"], .shots-carousel[data-frame="tablet"]) :where(.shots-device picture) {
475
+ :where(.shots-carousel[data-frame="phone"], .shots-carousel[data-frame="tablet"]) .shots-device :where(picture) {
469
476
  --shots-frame-pad: 3.5cqi;
470
477
  --shots-frame-radius: calc((100cqi - 2 * var(--shots-frame-pad)) * 0.1214);
471
478
  padding: var(--shots-frame-pad);
@@ -474,32 +481,32 @@
474
481
  box-shadow: inset 0 0 0 1px var(--_shots-bezel-edge), var(--_shots-shadow);
475
482
  }
476
483
 
477
- :where(.shots-carousel[data-frame="tablet"]) :where(.shots-device picture) {
484
+ :where(.shots-carousel[data-frame="tablet"]) .shots-device :where(picture) {
478
485
  --shots-frame-pad: 3cqi;
479
486
  --shots-frame-radius: calc((100cqi - 2 * var(--shots-frame-pad)) * 0.025);
480
487
  }
481
488
 
482
- :where(.shots-carousel[data-frame="phone"], .shots-carousel[data-frame="tablet"]) :where(.shots-device img) {
489
+ :where(.shots-carousel[data-frame="phone"], .shots-carousel[data-frame="tablet"]) .shots-device :where(img) {
483
490
  border-radius: var(--shots-frame-radius);
484
491
  }
485
492
 
486
- :where(a.shots-device:focus-visible) {
493
+ .shots-device:where(a:focus-visible) {
487
494
  outline: none;
488
495
  }
489
496
 
490
- :where(a.shots-device:focus-visible picture) {
497
+ .shots-device:where(a:focus-visible) :where(picture) {
491
498
  outline: 2px solid var(--_shots-focus);
492
499
  outline-offset: 3px;
493
500
  }
494
501
 
495
- :where(.shots-carousel-controls) {
502
+ .shots-carousel-controls {
496
503
  display: flex;
497
504
  justify-content: flex-end;
498
505
  gap: 0.5rem;
499
506
  padding: 0 var(--_shots-carousel-padding);
500
507
  }
501
508
 
502
- :where(.shots-carousel-step) {
509
+ .shots-carousel-step {
503
510
  display: inline-grid;
504
511
  place-items: center;
505
512
  width: var(--_shots-button-size);
@@ -512,16 +519,16 @@
512
519
  }
513
520
 
514
521
  @media (prefers-reduced-motion: no-preference) {
515
- :where(.shots-track, .shots-carousel-track, .shots-filmstrip) {
522
+ :is(.shots-track, .shots-carousel-track, .shots-filmstrip) {
516
523
  scroll-behavior: smooth;
517
524
  }
518
525
 
519
- :where(a.shots-device picture) {
526
+ .shots-device:where(a) :where(picture) {
520
527
  transition: transform 200ms ease;
521
528
  }
522
529
 
523
530
  @media (hover: hover) {
524
- :where(a.shots-device:hover picture) {
531
+ .shots-device:where(a:hover) :where(picture) {
525
532
  transform: translateY(-4px);
526
533
  }
527
534
  }
@@ -156,6 +156,19 @@ function status(root: HTMLElement, state: string, message: string): void {
156
156
  root.replaceChildren(element("div", { class: "shots-status", role: "status", "data-state": state }, [element("p", { text: message })]));
157
157
  }
158
158
 
159
+ /**
160
+ * A page can set `manifestData` before the elements are defined; that value is
161
+ * an own property that shadows the accessor. Take it off, so the constructor
162
+ * can pass it through the setter.
163
+ */
164
+ function takePendingData(element: HTMLElement): { value: unknown } | undefined {
165
+ if (!Object.prototype.hasOwnProperty.call(element, "manifestData")) return undefined;
166
+ const own = element as unknown as Record<string, unknown>;
167
+ const value = own["manifestData"];
168
+ delete own["manifestData"];
169
+ return { value };
170
+ }
171
+
159
172
  function defineGallery(): CustomElementConstructor {
160
173
  return class ScreenshotGallery extends HTMLElement implements ScreenshotGalleryElement {
161
174
  static observedAttributes = ["manifest"];
@@ -179,6 +192,12 @@ function defineGallery(): CustomElementConstructor {
179
192
  #rendered = false;
180
193
  #announced = "";
181
194
 
195
+ constructor() {
196
+ super();
197
+ const pending = takePendingData(this);
198
+ if (pending) this.manifestData = pending.value as ScreenshotManifest | null;
199
+ }
200
+
182
201
  get manifestData(): ScreenshotManifest | null { return this.#data; }
183
202
  set manifestData(value: ScreenshotManifest | null) { this.#data = value && parseScreenshotManifest(value); if (this.#connected) this.#load(); }
184
203
  get project(): string { return this.#project; }
@@ -359,7 +378,7 @@ function defineGallery(): CustomElementConstructor {
359
378
  button.setAttribute("aria-label", label);
360
379
  button.title = label;
361
380
  }
362
- this.#root!.dataset["theme"] = this.#theme;
381
+ this.#root!.dataset["shotsTheme"] = this.#theme;
363
382
  this.#loaded.clear();
364
383
  for (const slide of this.#track!.querySelectorAll<HTMLElement>(".shots-slide")) {
365
384
  const index = Number(slide.dataset["index"]), name = this.#screens[index]!, entry = this.#entry(name);
@@ -559,6 +578,12 @@ function defineCarousel(): CustomElementConstructor {
559
578
  #slides: HTMLElement[] = [];
560
579
  #manifest: ScreenshotManifest | null = null;
561
580
 
581
+ constructor() {
582
+ super();
583
+ const pending = takePendingData(this);
584
+ if (pending) this.manifestData = pending.value as ScreenshotManifest | null;
585
+ }
586
+
562
587
  get manifestData(): ScreenshotManifest | null { return this.#data; }
563
588
  set manifestData(value: ScreenshotManifest | null) { this.#data = value && parseScreenshotManifest(value); if (this.#connected) this.#load(); }
564
589