@astryxdesign/lab 0.7.6-canary.cdab2e6 → 0.7.6-canary.ce052bf

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/CodeEditor/CodeEditor.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAkBlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC;AAuF5D,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,+BAA+B;IAC/B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,uBAAuB;IACvB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAsB,EACtB,cAAsB,EACtB,UAAkB,EAClB,WAAW,EACX,SAAS,EACT,IAAW,EACX,SAAS,EAAE,eAAe,EAC1B,KAAK,EACL,UAAqE,EACrE,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BAsVjB;yBAvWe,UAAU"}
1
+ {"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/CodeEditor/CodeEditor.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAkBlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC;AA2F5D,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,+BAA+B;IAC/B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,uBAAuB;IACvB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAsB,EACtB,cAAsB,EACtB,UAAkB,EAClB,WAAW,EACX,SAAS,EACT,IAAW,EACX,SAAS,EAAE,eAAe,EAC1B,KAAK,EACL,UAAqE,EACrE,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BAsVjB;yBAvWe,UAAU"}
@@ -35,6 +35,9 @@ const styles = {
35
35
  k1xSpc: "x78zum5",
36
36
  kaIpWk: "xh6dtrn",
37
37
  kWkggS: "xwmxj5m",
38
+ kMzoRj: "x1litavf",
39
+ ksu8eU: "x1y0btm7",
40
+ kVAM5u: "x14i3s5s",
38
41
  kVQacm: "xb3r6kr",
39
42
  $$css: true
40
43
  },
@@ -51,6 +54,9 @@ const styles = {
51
54
  k9WMMc: "xp4054r",
52
55
  kfSwDN: "x87ps6o",
53
56
  kMwMTN: "xnbbluu",
57
+ kWqL5O: "x1pcaw5z",
58
+ kSWEuD: "x32b0ac",
59
+ k26BEO: "xtgwc6q",
54
60
  $$css: true
55
61
  },
56
62
  editor: {
package/dist/lab.css CHANGED
@@ -650,6 +650,9 @@
650
650
  .x1q0q8m5:not(#\#):not(#\#):not(#\#):not(#\#){border-bottom-style:solid}
651
651
  .x1qhh985:not(#\#):not(#\#):not(#\#):not(#\#){border-bottom-width:0}
652
652
  .x92x3c3:not(#\#):not(#\#):not(#\#):not(#\#){border-bottom-width:var(--border-width)}
653
+ .xtgwc6q:not(#\#):not(#\#):not(#\#):not(#\#){border-right-color:var(--color-border)}
654
+ .x32b0ac:not(#\#):not(#\#):not(#\#):not(#\#){border-right-style:solid}
655
+ .x1pcaw5z:not(#\#):not(#\#):not(#\#):not(#\#){border-right-width:var(--border-width)}
653
656
  .x5mp61g:not(#\#):not(#\#):not(#\#):not(#\#){border-top-color:var(--color-border-orange)}
654
657
  .x1pc3f07:not(#\#):not(#\#):not(#\#):not(#\#){border-top-color:var(--color-border)}
655
658
  .x13fuv20:not(#\#):not(#\#):not(#\#):not(#\#){border-top-style:solid}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/lab",
3
- "version": "0.7.6-canary.cdab2e6",
3
+ "version": "0.7.6-canary.ce052bf",
4
4
  "description": "Experimental Astryx components — published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
5
5
  "author": "Meta Open Source",
6
6
  "license": "MIT",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "peerDependencies": {
51
51
  "@stylexjs/stylex": ">=0.10.0",
52
- "@astryxdesign/core": "0.7.6-canary.cdab2e6",
52
+ "@astryxdesign/core": "0.7.6-canary.ce052bf",
53
53
  "react": ">=19.0.0",
54
54
  "react-dom": ">=19.0.0"
55
55
  },
@@ -59,9 +59,9 @@
59
59
  "d3-shape": "^3.2.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@astryxdesign/a11y-spec": "0.7.6-canary.cdab2e6",
63
- "@astryxdesign/cli": "0.7.6-canary.cdab2e6",
64
- "@astryxdesign/interaction-harness": "0.7.6-canary.cdab2e6",
62
+ "@astryxdesign/a11y-spec": "0.7.6-canary.ce052bf",
63
+ "@astryxdesign/cli": "0.7.6-canary.ce052bf",
64
+ "@astryxdesign/interaction-harness": "0.7.6-canary.ce052bf",
65
65
  "@babel/cli": "^7.29.7",
66
66
  "@babel/core": "^7.29.7",
67
67
  "@babel/preset-react": "^7.29.7",
@@ -3,7 +3,8 @@
3
3
  /**
4
4
  * @file CircularProgressMotion.a11y.chromium.spec.ts
5
5
  * @input Built CircularProgress stories in real Chromium (Web Animations
6
- * seeking, CDP screencast, tracing)
6
+ * seeking, CDP screencast, tracing), the interaction harness's busy-window
7
+ * control and proof
7
8
  * @output Browser evidence that a determinate value change keeps moving while
8
9
  * a script holds the main thread and paints nothing per frame, that the
9
10
  * ring at rest draws what the stroked circle drew, and that the arc stays
@@ -24,7 +25,14 @@ import {
24
25
  serveStorybook,
25
26
  type StaticServer,
26
27
  } from '@astryxdesign/a11y-spec/storybook';
27
- import {inPresentationOrder} from '@astryxdesign/interaction-harness';
28
+ import {
29
+ busyWindowProof,
30
+ CONTROL_RGB,
31
+ inPresentationOrder,
32
+ startBusyControl,
33
+ trackMarker,
34
+ type TrackedFrame,
35
+ } from '@astryxdesign/interaction-harness';
28
36
 
29
37
  let storybook: StaticServer;
30
38
  test.beforeAll(async () => {
@@ -152,35 +160,59 @@ async function colors(page: Page) {
152
160
 
153
161
  /**
154
162
  * For each degree clockwise from 12 o'clock, whether the ring's centerline is
155
- * drawn in the fill color in the given screenshot (PNGs of the ring box).
163
+ * drawn in the fill color in the given screenshot: PNGs of the ring box, or,
164
+ * with `frame`, PNGs of the whole viewport (`viewportWidth` CSS px wide) read
165
+ * at the ring box, the same pixels a crop to that box would hold.
156
166
  */
157
167
  async function inkByDegree(
158
168
  page: Page,
159
169
  shots: string[],
160
170
  palette: {fill: number[]; track: number[]},
171
+ frame?: {ring: Box; viewportWidth: number},
161
172
  ): Promise<boolean[][]> {
162
173
  const scratch = await page.context().newPage();
163
174
  try {
164
175
  return await scratch.evaluate(
165
- async ({items, fill, track, diameter, stroke}) => {
176
+ async ({items, fill, track, diameter, stroke, region}) => {
166
177
  const out: boolean[][] = [];
167
178
  for (const item of items) {
168
179
  const bitmap = await createImageBitmap(
169
180
  await (await fetch(`data:image/png;base64,${item}`)).blob(),
170
181
  );
171
- const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
182
+ // The ring box in the shot's pixels: the whole shot, or the box's
183
+ // pixels in a viewport frame.
184
+ const frameScale =
185
+ region == null ? 1 : bitmap.width / region.viewportWidth;
186
+ const x0 =
187
+ region == null ? 0 : Math.round(region.ring.x * frameScale);
188
+ const y0 =
189
+ region == null ? 0 : Math.round(region.ring.y * frameScale);
190
+ const boxWidth =
191
+ region == null
192
+ ? bitmap.width
193
+ : Math.round(region.ring.width * frameScale);
194
+ const boxHeight =
195
+ region == null
196
+ ? bitmap.height
197
+ : Math.round(region.ring.height * frameScale);
198
+ const canvas = new OffscreenCanvas(boxWidth, boxHeight);
172
199
  const context = canvas.getContext('2d');
173
200
  if (context == null) {
174
201
  throw new Error('no 2d context');
175
202
  }
176
- context.drawImage(bitmap, 0, 0);
177
- const {data, width} = context.getImageData(
203
+ context.drawImage(
204
+ bitmap,
205
+ x0,
206
+ y0,
207
+ boxWidth,
208
+ boxHeight,
178
209
  0,
179
210
  0,
180
- bitmap.width,
181
- bitmap.height,
211
+ boxWidth,
212
+ boxHeight,
182
213
  );
183
- const scale = bitmap.width / diameter;
214
+ const {data, width} = context.getImageData(0, 0, boxWidth, boxHeight);
215
+ const scale = boxWidth / diameter;
184
216
  const r = ((diameter - stroke) / 2) * scale;
185
217
  const c = (diameter / 2) * scale;
186
218
  const d = (a: number[], b: number[]) =>
@@ -200,7 +232,13 @@ async function inkByDegree(
200
232
  }
201
233
  return out;
202
234
  },
203
- {items: shots, ...palette, diameter: DIAMETER, stroke: STROKE},
235
+ {
236
+ items: shots,
237
+ ...palette,
238
+ diameter: DIAMETER,
239
+ stroke: STROKE,
240
+ region: frame ?? null,
241
+ },
204
242
  );
205
243
  } finally {
206
244
  await scratch.close();
@@ -263,6 +301,10 @@ test('a value change keeps moving while a script holds the main thread', async (
263
301
  everyNthFrame: 1,
264
302
  });
265
303
  await page.waitForTimeout(300);
304
+ // The harness's busy-window control, through the block: a box the
305
+ // compositor moves along the top edge, clear of the ring, in the same
306
+ // screencast.
307
+ const control = await startBusyControl(page);
266
308
  // A value change, one presented frame, then 700 ms of script.
267
309
  const busy = await page.evaluate(
268
310
  async ({storyId}) =>
@@ -298,6 +340,9 @@ test('a value change keeps moving while a script holds the main thread', async (
298
340
  }),
299
341
  {storyId: STORY},
300
342
  );
343
+ // Gone once the block ends, so the page is still again when the
344
+ // screencast stops.
345
+ await control.remove();
301
346
  await page.waitForTimeout(300);
302
347
  await cdp.send('Page.stopScreencast');
303
348
  await Promise.all(acks);
@@ -309,61 +354,54 @@ test('a value change keeps moving while a script holds the main thread', async (
309
354
  // that shows the arc behind an earlier one still has its own later
310
355
  // timestamp, so it stays in place.
311
356
  const presented = inPresentationOrder(raw);
312
- // Crop each frame to the ring, then read how far the arc reaches.
313
- const scratch = await page.context().newPage();
314
- let crops: string[];
315
- try {
316
- crops = await scratch.evaluate(
317
- async ({items, ring}) => {
318
- const out: string[] = [];
319
- for (const item of items) {
320
- const bitmap = await createImageBitmap(
321
- await (await fetch(`data:image/png;base64,${item}`)).blob(),
322
- );
323
- const scale = bitmap.width / 240;
324
- const canvas = new OffscreenCanvas(
325
- Math.round(ring.width * scale),
326
- Math.round(ring.height * scale),
327
- );
328
- const context = canvas.getContext('2d');
329
- if (context == null) {
330
- throw new Error('no 2d context');
331
- }
332
- context.drawImage(
333
- bitmap,
334
- Math.round(ring.x * scale),
335
- Math.round(ring.y * scale),
336
- canvas.width,
337
- canvas.height,
338
- 0,
339
- 0,
340
- canvas.width,
341
- canvas.height,
342
- );
343
- const blob = await canvas.convertToBlob({type: 'image/png'});
344
- const bytes = new Uint8Array(await blob.arrayBuffer());
345
- let binary = '';
346
- for (const byte of bytes) {
347
- binary += String.fromCharCode(byte);
348
- }
349
- out.push(btoa(binary));
350
- }
351
- return out;
352
- },
353
- {items: presented.map(f => f.data), ring: box},
354
- );
355
- } finally {
356
- await scratch.close();
357
- }
358
- const rows = await inkByDegree(page, crops, palette);
357
+ // How far the arc reaches in each frame, read at the ring.
358
+ const rows = await inkByDegree(
359
+ page,
360
+ presented.map(f => f.data),
361
+ palette,
362
+ {ring: box, viewportWidth: 240},
363
+ );
359
364
  const frames = rows.map((row, i) => ({t: presented[i].t, ...span(row)}));
360
365
  const inside = frames.filter(
361
366
  f => f.t >= busy.from + 20 && f.t <= busy.to - 20,
362
367
  );
363
- // The change lasts the theme's 410 ms, so it moves through most of the
364
- // block, one piece the whole way, and lands at 90%.
365
- const reaches = new Set(inside.map(f => f.last));
366
- expect(reaches.size).toBeGreaterThanOrEqual(10);
368
+ // The control, in the frames around the block (the proof reads it from the
369
+ // last frame before the block through the block's end).
370
+ const controlFrames = await trackMarker(
371
+ page,
372
+ presented
373
+ .filter(f => f.t >= busy.from - 250 && f.t <= busy.to)
374
+ .map(f => ({
375
+ t: f.t,
376
+ data: f.data,
377
+ format: 'png' as const,
378
+ viewportWidth: 240,
379
+ viewportHeight: 160,
380
+ })),
381
+ {rgb: CONTROL_RGB},
382
+ );
383
+ // The change runs through the block: the arc's reach shows a new degree on
384
+ // every frame the control moved on, and stalls no longer than the control,
385
+ // until it settles (the harness's busy proof: the screencast's capture rate
386
+ // thins both alike). It settles 3 degrees short of its end: on the change's
387
+ // own clock each 1-degree step of the reading takes 2 to 8 ms up to 324,
388
+ // then 12, 12 and 18 ms for 325, 326 and 327 before it rests at 328, so
389
+ // from 325 two frames can read the same degree while the arc still moves.
390
+ // It stays one piece the whole way, and lands at 90%.
391
+ const arc: TrackedFrame[] = frames.map(f => ({
392
+ t: f.t,
393
+ box: {x: f.last, y: 0, width: 1, height: 1},
394
+ pixels: 1,
395
+ }));
396
+ const proof = busyWindowProof(arc, busy, {
397
+ axis: 'x',
398
+ settledWithin: 3,
399
+ control: controlFrames,
400
+ });
401
+ test
402
+ .info()
403
+ .annotations.push({type: 'busy window', description: proof.summary});
404
+ expect(proof.verdict.failures, proof.summary).toEqual([]);
367
405
  for (const frame of inside) {
368
406
  expect(frame.pieces, `pieces at ${frame.t}`).toBe(1);
369
407
  }
@@ -58,7 +58,9 @@ const styles = stylex.create({
58
58
  display: 'flex',
59
59
  borderRadius: radiusVars['--radius-element'],
60
60
  backgroundColor: colorVars['--color-background-muted'],
61
- border: `${borderVars['--border-width']} solid ${colorVars['--color-border']}`,
61
+ borderWidth: borderVars['--border-width'],
62
+ borderStyle: 'solid',
63
+ borderColor: colorVars['--color-border'],
62
64
  overflow: 'hidden',
63
65
  },
64
66
  rootFocused: {
@@ -73,7 +75,9 @@ const styles = stylex.create({
73
75
  textAlign: 'end',
74
76
  userSelect: 'none',
75
77
  color: colorVars['--color-text-disabled'],
76
- borderRight: `${borderVars['--border-width']} solid ${colorVars['--color-border']}`,
78
+ borderRightWidth: borderVars['--border-width'],
79
+ borderRightStyle: 'solid',
80
+ borderRightColor: colorVars['--color-border'],
77
81
  },
78
82
  gutterLine: {
79
83
  fontFamily: typographyVars['--font-family-code'],