@dreamboard-games/sdk 0.2.0 → 0.2.1-alpha.1

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 (90) hide show
  1. package/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-BBMVT3KG.d.ts} +1 -1
  2. package/dist/attributes-BeRyboMS.d.ts +279 -0
  3. package/dist/browser-interaction.d.ts +708 -0
  4. package/dist/browser-interaction.js +106 -0
  5. package/dist/browser-interaction.js.map +1 -0
  6. package/dist/{bundle-TIZcw8LB.d.ts → bundle-CDd5FKeD.d.ts} +3 -1
  7. package/dist/{chunk-U5C6BONG.js → chunk-326PGVAA.js} +2 -2
  8. package/dist/{chunk-VFTAA4WO.js → chunk-MKXPVOUT.js} +4 -2
  9. package/dist/chunk-MKXPVOUT.js.map +1 -0
  10. package/dist/{chunk-GKKBPPSW.js → chunk-MZNVHMJ5.js} +4 -4
  11. package/dist/{chunk-KAELH4KC.js → chunk-NKCRKGR2.js} +2 -2
  12. package/dist/{chunk-WN74KVNY.js → chunk-PEI3FIL2.js} +2 -2
  13. package/dist/chunk-PEI3FIL2.js.map +1 -0
  14. package/dist/chunk-QLG6VEMW.js +1691 -0
  15. package/dist/chunk-QLG6VEMW.js.map +1 -0
  16. package/dist/{chunk-WYPQ3GG5.js → chunk-WG4JQL3S.js} +4 -1
  17. package/dist/{chunk-WYPQ3GG5.js.map → chunk-WG4JQL3S.js.map} +1 -1
  18. package/dist/{chunk-7YAHLYBR.js → chunk-XV6D3ET4.js} +8 -4
  19. package/dist/{chunk-7YAHLYBR.js.map → chunk-XV6D3ET4.js.map} +1 -1
  20. package/dist/{chunk-TDSWKVZ4.js → chunk-ZABVH7AO.js} +1236 -17
  21. package/dist/chunk-ZABVH7AO.js.map +1 -0
  22. package/dist/{components-D5ZRE2Hl.d.ts → components-BoiVSYqx.d.ts} +1 -1
  23. package/dist/generated/runtime/primitives.d.ts +5 -4
  24. package/dist/generated/runtime/primitives.js +4 -3
  25. package/dist/generated/runtime-api.d.ts +1 -1
  26. package/dist/generated/runtime.d.ts +5 -4
  27. package/dist/generated/runtime.js +7 -6
  28. package/dist/generated/workspace-contract.d.ts +5 -4
  29. package/dist/generated/workspace-contract.js +6 -5
  30. package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-1iAyJRFn.d.ts} +1 -0
  31. package/dist/index.js +1 -1
  32. package/dist/infrastructure/reducer-bundle-abi.d.ts +113 -113
  33. package/dist/infrastructure/reducer-bundle-abi.js +1 -1
  34. package/dist/package-set.d.ts +2 -2
  35. package/dist/package-set.js +1 -1
  36. package/dist/reducer.d.ts +1 -1
  37. package/dist/reducer.js +305 -12
  38. package/dist/reducer.js.map +1 -1
  39. package/dist/runtime/primitives.d.ts +6 -5
  40. package/dist/runtime/primitives.js +4 -3
  41. package/dist/runtime/workspace-contract.d.ts +6 -5
  42. package/dist/runtime/workspace-contract.js +6 -5
  43. package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-CPLm_XDG.d.ts} +6 -0
  44. package/dist/runtime.d.ts +5 -4
  45. package/dist/runtime.js +6 -5
  46. package/dist/testing.d.ts +2 -2
  47. package/dist/ui/components.d.ts +2 -2
  48. package/dist/ui/components.js +1 -1
  49. package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-rzKBwOLC.d.ts} +5 -3
  50. package/dist/ui.d.ts +5 -5
  51. package/dist/ui.js +2 -2
  52. package/package.json +15 -9
  53. package/src/browser-interaction/attributes.ts +211 -0
  54. package/src/browser-interaction/canonical.ts +77 -0
  55. package/src/browser-interaction/constants.ts +77 -0
  56. package/src/browser-interaction/effects.ts +176 -0
  57. package/src/browser-interaction/index.ts +111 -0
  58. package/src/browser-interaction/normalize.ts +997 -0
  59. package/src/browser-interaction/registry.ts +70 -0
  60. package/src/browser-interaction/resolve.ts +596 -0
  61. package/src/browser-interaction/schemas.ts +152 -0
  62. package/src/browser-interaction/types.ts +304 -0
  63. package/src/browser-interaction.ts +1 -0
  64. package/src/generated/reducer-contract/wire.ts +1 -1
  65. package/src/generated/reducer-contract/zod.ts +3 -1
  66. package/src/package-set.ts +1 -1
  67. package/src/reducer/bundle/ingress-bundle.ts +1 -1
  68. package/src/reducer/bundle/trusted/interaction-types.ts +3 -0
  69. package/src/reducer/bundle/trusted/projection-builder.ts +337 -13
  70. package/src/reducer/ingress/input-codec.ts +1 -1
  71. package/src/reducer/ingress/session-codec.ts +1 -1
  72. package/src/runtime-internal/components/InteractionForm.tsx +345 -7
  73. package/src/runtime-internal/components/PluginRuntime.tsx +2 -0
  74. package/src/runtime-internal/components/board/target-layer.ts +2 -0
  75. package/src/runtime-internal/context/PluginStateContext.tsx +41 -0
  76. package/src/runtime-internal/hooks/useBoardInteractions.ts +73 -11
  77. package/src/runtime-internal/primitives/board.tsx +71 -0
  78. package/src/runtime-internal/primitives/interaction.tsx +160 -1
  79. package/src/runtime-internal/types/plugin-state.ts +6 -0
  80. package/src/runtime-internal/utils/browser-interaction-effects.ts +240 -0
  81. package/src/runtime-internal/utils/interaction-draft-digest.ts +252 -0
  82. package/src/runtime-internal/utils/semantic-projection-digest.ts +407 -0
  83. package/src/ui/components/board/HexGrid.tsx +3 -0
  84. package/src/ui/components/board/target-layer.ts +1 -0
  85. package/dist/chunk-TDSWKVZ4.js.map +0 -1
  86. package/dist/chunk-VFTAA4WO.js.map +0 -1
  87. package/dist/chunk-WN74KVNY.js.map +0 -1
  88. /package/dist/{chunk-U5C6BONG.js.map → chunk-326PGVAA.js.map} +0 -0
  89. /package/dist/{chunk-GKKBPPSW.js.map → chunk-MZNVHMJ5.js.map} +0 -0
  90. /package/dist/{chunk-KAELH4KC.js.map → chunk-NKCRKGR2.js.map} +0 -0
@@ -1,3 +1,15 @@
1
+ import {
2
+ BROWSER_INTERACTION_ATTRIBUTES,
3
+ DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION,
4
+ GAMEPLAY_BROWSER_INTERACTION_SURFACE,
5
+ createGameplayActuatorAttributes,
6
+ createGameplayInteractionRootAttributes,
7
+ gameplayAdjustResourceEffect,
8
+ gameplayCommitEffect,
9
+ gameplayInvokeEffect,
10
+ gameplaySetCandidateEffect,
11
+ gameplaySetScalarEffect
12
+ } from "./chunk-QLG6VEMW.js";
1
13
  import {
2
14
  CardDragSurface,
3
15
  CardDropTargetView,
@@ -17,7 +29,7 @@ import {
17
29
  useChromeSuppression,
18
30
  useTheme,
19
31
  useThemeCssVars
20
- } from "./chunk-WYPQ3GG5.js";
32
+ } from "./chunk-WG4JQL3S.js";
21
33
 
22
34
  // src/runtime-internal/primitives/ui.tsx
23
35
  import { Fragment, jsx } from "react/jsx-runtime";
@@ -293,9 +305,376 @@ function RuntimeProvider({
293
305
  return /* @__PURE__ */ jsx3(RuntimeContext.Provider, { value: runtime, children: /* @__PURE__ */ jsx3(PluginSessionContext.Provider, { value: sessionState, children }) });
294
306
  }
295
307
 
308
+ // src/runtime-internal/utils/semantic-projection-digest.ts
309
+ var AUTHORIZED_SEAT_PROJECTION_DIGEST_VERSION = "authorized-seat-projection@2";
310
+ function semanticProjectionDigestForState(state) {
311
+ const actorPlayerId = state.session.controllingPlayerId;
312
+ if (!actorPlayerId) {
313
+ return null;
314
+ }
315
+ const seatOrder = state.lobby?.seats.map((seat) => seat.playerId) ?? [];
316
+ if (!seatOrder.includes(actorPlayerId)) {
317
+ return null;
318
+ }
319
+ return hashJson(semanticSeatProjection(state, state.gameplay, seatOrder));
320
+ }
321
+ function semanticSeatProjection(state, gameplay, seatOrder) {
322
+ const playerToSeat = new Map(
323
+ seatOrder.map((playerId, index) => [playerId, index])
324
+ );
325
+ const actorPlayerId = state.session.controllingPlayerId;
326
+ const actorSeat = actorPlayerId ? playerToSeat.get(actorPlayerId) : void 0;
327
+ if (actorSeat === void 0) {
328
+ throw new Error(
329
+ `authorized projection player ${actorPlayerId ?? "__none__"} is not present in seat order`
330
+ );
331
+ }
332
+ return {
333
+ digestVersion: AUTHORIZED_SEAT_PROJECTION_DIGEST_VERSION,
334
+ actorSeat,
335
+ currentStage: gameplay.currentStage ?? null,
336
+ stageSeats: gameplay.activePlayers.map(
337
+ (playerId) => canonicalizeSeatReference(playerId, playerToSeat)
338
+ ),
339
+ view: canonicalizeSemanticProjectionValue(state.view, playerToSeat),
340
+ zones: canonicalizeSemanticProjectionValue(gameplay.zones ?? {}, playerToSeat),
341
+ availableInteractions: gameplay.availableInteractions.map(
342
+ (descriptor) => semanticInteractionDescriptor(descriptor, playerToSeat)
343
+ )
344
+ };
345
+ }
346
+ function semanticInteractionDescriptor(descriptor, playerToSeat) {
347
+ const inputs = descriptor.inputs.map(
348
+ (input) => semanticInteractionInput(input, playerToSeat)
349
+ );
350
+ const { defaults, defaultProvenance } = collectDefaults(descriptor.inputs);
351
+ return {
352
+ interactionKey: descriptor.interactionKey,
353
+ interactionId: descriptor.interactionId,
354
+ stableIdentity: canonicalizeSemanticProjectionValue(
355
+ `${descriptor.interactionKey}:${descriptor.interactionId}`,
356
+ playerToSeat
357
+ ),
358
+ kind: descriptor.kind,
359
+ surface: descriptor.zoneId ?? descriptor.zoneIds?.find((zoneId) => typeof zoneId === "string") ?? null,
360
+ commitMode: descriptor.commit.mode === "autoWhenReady" ? "autoWhenReady" : "manual",
361
+ status: descriptor.availability.status ?? null,
362
+ available: descriptor.availability.status === "available",
363
+ inputs,
364
+ defaults: canonicalizeSemanticProjectionValue(defaults, playerToSeat),
365
+ defaultProvenance
366
+ };
367
+ }
368
+ function semanticInteractionInput(input, playerToSeat) {
369
+ const domain = asRecord(input.domain);
370
+ const selection = asRecord(domain.selection);
371
+ return {
372
+ key: input.key,
373
+ kind: input.kind,
374
+ domain: canonicalizeSemanticProjectionValue(input.domain, playerToSeat),
375
+ defaultValue: input.defaultValue === void 0 ? null : canonicalizeSemanticProjectionValue(input.defaultValue, playerToSeat),
376
+ defaultProvenance: input.defaultValue === void 0 ? null : "input",
377
+ selectionMode: stringField(selection, "mode"),
378
+ min: numberField(domain),
379
+ max: numberField(domain, "max")
380
+ };
381
+ }
382
+ function collectDefaults(inputs) {
383
+ const defaults = {};
384
+ const defaultProvenance = {};
385
+ for (const input of inputs) {
386
+ if (input.defaultValue !== void 0) {
387
+ defaults[input.key] = toCanonicalJson(input.defaultValue);
388
+ defaultProvenance[input.key] = "input";
389
+ }
390
+ }
391
+ return { defaults, defaultProvenance };
392
+ }
393
+ function canonicalizeSemanticProjectionValue(value, playerToSeat) {
394
+ if (value === null || typeof value === "boolean" || typeof value === "number") {
395
+ return toCanonicalJson(value);
396
+ }
397
+ if (typeof value === "string") {
398
+ return canonicalizeSeatReference(value, playerToSeat);
399
+ }
400
+ if (Array.isArray(value)) {
401
+ return value.map(
402
+ (item) => canonicalizeSemanticProjectionValue(item, playerToSeat)
403
+ );
404
+ }
405
+ if (isRecord(value)) {
406
+ return Object.fromEntries(
407
+ Object.entries(value).filter(
408
+ ([key, item]) => item !== void 0 && !SEMANTIC_PROJECTION_TRANSPORT_FIELDS.has(key)
409
+ ).map(([key, item]) => [
410
+ canonicalizeObjectKey(key, playerToSeat),
411
+ canonicalizeSemanticProjectionProperty(key, item, playerToSeat)
412
+ ])
413
+ );
414
+ }
415
+ return null;
416
+ }
417
+ function canonicalizeSemanticProjectionProperty(key, value, playerToSeat) {
418
+ const canonicalValue = canonicalizeSemanticProjectionValue(value, playerToSeat);
419
+ if ((key === "eligibleTargets" || key === "dependentCases") && Array.isArray(canonicalValue)) {
420
+ return [...canonicalValue].sort(
421
+ (left, right) => compareJson(canonicalJson(left), canonicalJson(right))
422
+ );
423
+ }
424
+ return canonicalValue;
425
+ }
426
+ function canonicalizeSeatReference(value, playerToSeat) {
427
+ const seat = playerToSeat.get(value);
428
+ return seat === void 0 ? value : { $seat: seat };
429
+ }
430
+ function canonicalizeObjectKey(key, playerToSeat) {
431
+ const seat = playerToSeat.get(key);
432
+ return seat === void 0 ? key : `$seat:${seat}`;
433
+ }
434
+ var SEMANTIC_PROJECTION_TRANSPORT_FIELDS = /* @__PURE__ */ new Set([
435
+ "availableInteractionRefs",
436
+ "board",
437
+ "cursor",
438
+ "etag",
439
+ "hydratedAt",
440
+ "hydrationCursor",
441
+ "hydrationSequence",
442
+ "interactionRef",
443
+ "interactionRefs",
444
+ "lastEventId",
445
+ "receivedAt",
446
+ "sequence",
447
+ "serverTime",
448
+ "snapshotVersion",
449
+ "transportSequence",
450
+ "transportVersion",
451
+ "updatedAt"
452
+ ]);
453
+ function toCanonicalJson(value) {
454
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
455
+ return value;
456
+ }
457
+ if (typeof value === "number") {
458
+ return value;
459
+ }
460
+ if (Array.isArray(value)) {
461
+ return value.map((item) => toCanonicalJson(item));
462
+ }
463
+ if (isRecord(value)) {
464
+ return Object.fromEntries(
465
+ Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => [key, toCanonicalJson(item)])
466
+ );
467
+ }
468
+ return null;
469
+ }
470
+ function hashJson(value) {
471
+ return `sha256:${sha256Hex(canonicalJson(value))}`;
472
+ }
473
+ function canonicalJson(value) {
474
+ return JSON.stringify(canonicalizeJson(value));
475
+ }
476
+ function canonicalizeJson(value) {
477
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
478
+ return value;
479
+ }
480
+ if (typeof value === "number") {
481
+ if (!Number.isFinite(value)) {
482
+ throw new Error("canonical JSON contains a non-finite number");
483
+ }
484
+ return value;
485
+ }
486
+ if (Array.isArray(value)) {
487
+ return value.map((item) => canonicalizeJson(item));
488
+ }
489
+ return Object.fromEntries(
490
+ Object.entries(value).sort(([left], [right]) => compareJson(left, right)).map(([key, item]) => [key, canonicalizeJson(item)])
491
+ );
492
+ }
493
+ function compareJson(left, right) {
494
+ return left < right ? -1 : left > right ? 1 : 0;
495
+ }
496
+ function isRecord(value) {
497
+ return !!value && typeof value === "object" && !Array.isArray(value);
498
+ }
499
+ function asRecord(value) {
500
+ return isRecord(value) ? value : {};
501
+ }
502
+ function stringField(record, key) {
503
+ const value = record[key];
504
+ return typeof value === "string" && value.trim() ? value : null;
505
+ }
506
+ function numberField(record, key = "min") {
507
+ const value = record[key];
508
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
509
+ }
510
+ var SHA256_K = [
511
+ 1116352408,
512
+ 1899447441,
513
+ 3049323471,
514
+ 3921009573,
515
+ 961987163,
516
+ 1508970993,
517
+ 2453635748,
518
+ 2870763221,
519
+ 3624381080,
520
+ 310598401,
521
+ 607225278,
522
+ 1426881987,
523
+ 1925078388,
524
+ 2162078206,
525
+ 2614888103,
526
+ 3248222580,
527
+ 3835390401,
528
+ 4022224774,
529
+ 264347078,
530
+ 604807628,
531
+ 770255983,
532
+ 1249150122,
533
+ 1555081692,
534
+ 1996064986,
535
+ 2554220882,
536
+ 2821834349,
537
+ 2952996808,
538
+ 3210313671,
539
+ 3336571891,
540
+ 3584528711,
541
+ 113926993,
542
+ 338241895,
543
+ 666307205,
544
+ 773529912,
545
+ 1294757372,
546
+ 1396182291,
547
+ 1695183700,
548
+ 1986661051,
549
+ 2177026350,
550
+ 2456956037,
551
+ 2730485921,
552
+ 2820302411,
553
+ 3259730800,
554
+ 3345764771,
555
+ 3516065817,
556
+ 3600352804,
557
+ 4094571909,
558
+ 275423344,
559
+ 430227734,
560
+ 506948616,
561
+ 659060556,
562
+ 883997877,
563
+ 958139571,
564
+ 1322822218,
565
+ 1537002063,
566
+ 1747873779,
567
+ 1955562222,
568
+ 2024104815,
569
+ 2227730452,
570
+ 2361852424,
571
+ 2428436474,
572
+ 2756734187,
573
+ 3204031479,
574
+ 3329325298
575
+ ];
576
+ function sha256Hex(input) {
577
+ const bytes = utf8Bytes(input);
578
+ const bitLength = bytes.length * 8;
579
+ bytes.push(128);
580
+ while (bytes.length % 64 !== 56) {
581
+ bytes.push(0);
582
+ }
583
+ for (let shift = 56; shift >= 0; shift -= 8) {
584
+ bytes.push(Math.floor(bitLength / 2 ** shift) & 255);
585
+ }
586
+ let h0 = 1779033703;
587
+ let h1 = 3144134277;
588
+ let h2 = 1013904242;
589
+ let h3 = 2773480762;
590
+ let h4 = 1359893119;
591
+ let h5 = 2600822924;
592
+ let h6 = 528734635;
593
+ let h7 = 1541459225;
594
+ const words = new Array(64);
595
+ for (let offset = 0; offset < bytes.length; offset += 64) {
596
+ for (let index = 0; index < 16; index += 1) {
597
+ const base = offset + index * 4;
598
+ words[index] = (bytes[base] << 24 | bytes[base + 1] << 16 | bytes[base + 2] << 8 | bytes[base + 3]) >>> 0;
599
+ }
600
+ for (let index = 16; index < 64; index += 1) {
601
+ const s0 = rotateRight(words[index - 15], 7) ^ rotateRight(words[index - 15], 18) ^ words[index - 15] >>> 3;
602
+ const s1 = rotateRight(words[index - 2], 17) ^ rotateRight(words[index - 2], 19) ^ words[index - 2] >>> 10;
603
+ words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
604
+ }
605
+ let a = h0;
606
+ let b = h1;
607
+ let c = h2;
608
+ let d = h3;
609
+ let e = h4;
610
+ let f = h5;
611
+ let g = h6;
612
+ let h = h7;
613
+ for (let index = 0; index < 64; index += 1) {
614
+ const s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
615
+ const ch2 = e & f ^ ~e & g;
616
+ const temp1 = h + s1 + ch2 + SHA256_K[index] + words[index] >>> 0;
617
+ const s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
618
+ const maj2 = a & b ^ a & c ^ b & c;
619
+ const temp2 = s0 + maj2 >>> 0;
620
+ h = g;
621
+ g = f;
622
+ f = e;
623
+ e = d + temp1 >>> 0;
624
+ d = c;
625
+ c = b;
626
+ b = a;
627
+ a = temp1 + temp2 >>> 0;
628
+ }
629
+ h0 = h0 + a >>> 0;
630
+ h1 = h1 + b >>> 0;
631
+ h2 = h2 + c >>> 0;
632
+ h3 = h3 + d >>> 0;
633
+ h4 = h4 + e >>> 0;
634
+ h5 = h5 + f >>> 0;
635
+ h6 = h6 + g >>> 0;
636
+ h7 = h7 + h >>> 0;
637
+ }
638
+ return [h0, h1, h2, h3, h4, h5, h6, h7].map((value) => value.toString(16).padStart(8, "0")).join("");
639
+ }
640
+ function rotateRight(value, bits) {
641
+ return value >>> bits | value << 32 - bits;
642
+ }
643
+ function utf8Bytes(input) {
644
+ return Array.from(new TextEncoder().encode(input));
645
+ }
646
+
296
647
  // src/runtime-internal/context/PluginStateContext.tsx
297
648
  import { Fragment as Fragment2, jsx as jsx4, jsxs } from "react/jsx-runtime";
298
649
  var PluginStateContext = createContext4(null);
650
+ var GAMEPLAY_BROWSER_SCOPE_ID = "runtime";
651
+ var BROWSER_PROJECTION_DIGEST_ATTRIBUTE = "data-dreamboard-projection-digest";
652
+ function RuntimeSemanticProjectionMarker() {
653
+ const state = usePluginState((snapshot) => snapshot);
654
+ return /* @__PURE__ */ jsx4(SemanticProjectionMarker, { state });
655
+ }
656
+ function SemanticProjectionMarker({
657
+ state
658
+ }) {
659
+ const digest = semanticProjectionDigestForState(state);
660
+ if (!digest) {
661
+ return null;
662
+ }
663
+ return /* @__PURE__ */ jsx4(
664
+ "span",
665
+ {
666
+ "aria-hidden": "true",
667
+ style: { display: "none" },
668
+ ...{
669
+ [BROWSER_INTERACTION_ATTRIBUTES.protocol]: DREAMBOARD_BROWSER_INTERACTION_PROTOCOL_VERSION,
670
+ [BROWSER_INTERACTION_ATTRIBUTES.surface]: GAMEPLAY_BROWSER_INTERACTION_SURFACE,
671
+ [BROWSER_INTERACTION_ATTRIBUTES.scope]: GAMEPLAY_BROWSER_SCOPE_ID,
672
+ [BROWSER_INTERACTION_ATTRIBUTES.role]: "projection",
673
+ [BROWSER_PROJECTION_DIGEST_ATTRIBUTE]: digest
674
+ }
675
+ }
676
+ );
677
+ }
299
678
  function usePluginState(selector) {
300
679
  const runtime = useRuntimeContext();
301
680
  const subscribe = (onStoreChange) => {
@@ -2373,6 +2752,373 @@ function validationErrorFromUnknown(error, fallbackMessage = "Interaction submis
2373
2752
  return new ValidationError(errorCode, message);
2374
2753
  }
2375
2754
 
2755
+ // src/runtime-internal/utils/interaction-draft-digest.ts
2756
+ function interactionDraftDigestForValues(descriptor, values) {
2757
+ if (descriptor.descriptorDigest === void 0 || descriptor.actorSeat === void 0 || descriptor.draftDigest === void 0) {
2758
+ return void 0;
2759
+ }
2760
+ const defaults = defaultsForDescriptor(descriptor);
2761
+ return draftDigest({
2762
+ actorSeat: descriptor.actorSeat,
2763
+ descriptorDigest: descriptor.descriptorDigest,
2764
+ interactionId: descriptor.interactionId,
2765
+ interactionKey: descriptor.interactionKey,
2766
+ values: {
2767
+ ...defaults,
2768
+ ...toCanonicalJson2(values)
2769
+ }
2770
+ });
2771
+ }
2772
+ function draftDigest({
2773
+ actorSeat,
2774
+ descriptorDigest,
2775
+ interactionId,
2776
+ interactionKey,
2777
+ values
2778
+ }) {
2779
+ return hashJson2({
2780
+ digestVersion: "interaction-draft@2",
2781
+ actorSeat,
2782
+ descriptorDigest,
2783
+ emitted: false,
2784
+ interactionId,
2785
+ interactionKey,
2786
+ values
2787
+ });
2788
+ }
2789
+ function defaultsForDescriptor(descriptor) {
2790
+ return Object.fromEntries(
2791
+ descriptor.inputs.flatMap(
2792
+ (input) => input.defaultValue === void 0 ? [] : [[input.key, toCanonicalJson2(input.defaultValue)]]
2793
+ )
2794
+ );
2795
+ }
2796
+ function toCanonicalJson2(value) {
2797
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
2798
+ return value;
2799
+ }
2800
+ if (typeof value === "number") {
2801
+ return value;
2802
+ }
2803
+ if (Array.isArray(value)) {
2804
+ return value.map((item) => toCanonicalJson2(item));
2805
+ }
2806
+ if (typeof value === "object") {
2807
+ return Object.fromEntries(
2808
+ Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => [key, toCanonicalJson2(item)])
2809
+ );
2810
+ }
2811
+ return null;
2812
+ }
2813
+ function hashJson2(value) {
2814
+ return `sha256:${sha256Hex2(canonicalJson2(value))}`;
2815
+ }
2816
+ function canonicalJson2(value) {
2817
+ return JSON.stringify(canonicalizeJson2(value));
2818
+ }
2819
+ function canonicalizeJson2(value) {
2820
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
2821
+ return value;
2822
+ }
2823
+ if (typeof value === "number") {
2824
+ if (!Number.isFinite(value)) {
2825
+ throw new Error("canonical JSON contains a non-finite number");
2826
+ }
2827
+ return value;
2828
+ }
2829
+ if (Array.isArray(value)) {
2830
+ return value.map((item) => canonicalizeJson2(item));
2831
+ }
2832
+ return Object.fromEntries(
2833
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, canonicalizeJson2(item)])
2834
+ );
2835
+ }
2836
+ function sha256Hex2(input) {
2837
+ const bytes = utf8Bytes2(input);
2838
+ const bitLength = bytes.length * 8;
2839
+ bytes.push(128);
2840
+ while (bytes.length % 64 !== 56) {
2841
+ bytes.push(0);
2842
+ }
2843
+ for (let shift = 56; shift >= 0; shift -= 8) {
2844
+ bytes.push(Math.floor(bitLength / 2 ** shift) & 255);
2845
+ }
2846
+ let h0 = 1779033703;
2847
+ let h1 = 3144134277;
2848
+ let h2 = 1013904242;
2849
+ let h3 = 2773480762;
2850
+ let h4 = 1359893119;
2851
+ let h5 = 2600822924;
2852
+ let h6 = 528734635;
2853
+ let h7 = 1541459225;
2854
+ const words = new Array(64);
2855
+ for (let offset = 0; offset < bytes.length; offset += 64) {
2856
+ for (let i = 0; i < 16; i += 1) {
2857
+ words[i] = (bytes[offset + i * 4] ?? 0) << 24 | (bytes[offset + i * 4 + 1] ?? 0) << 16 | (bytes[offset + i * 4 + 2] ?? 0) << 8 | (bytes[offset + i * 4 + 3] ?? 0);
2858
+ }
2859
+ for (let i = 16; i < 64; i += 1) {
2860
+ words[i] = smallSigma1(words[i - 2] ?? 0) + (words[i - 7] ?? 0) + smallSigma0(words[i - 15] ?? 0) + (words[i - 16] ?? 0) >>> 0;
2861
+ }
2862
+ let a = h0;
2863
+ let b = h1;
2864
+ let c = h2;
2865
+ let d = h3;
2866
+ let e = h4;
2867
+ let f = h5;
2868
+ let g = h6;
2869
+ let h = h7;
2870
+ for (let i = 0; i < 64; i += 1) {
2871
+ const t1 = h + bigSigma1(e) + ch(e, f, g) + K[i] + (words[i] ?? 0) >>> 0;
2872
+ const t2 = bigSigma0(a) + maj(a, b, c) >>> 0;
2873
+ h = g;
2874
+ g = f;
2875
+ f = e;
2876
+ e = d + t1 >>> 0;
2877
+ d = c;
2878
+ c = b;
2879
+ b = a;
2880
+ a = t1 + t2 >>> 0;
2881
+ }
2882
+ h0 = h0 + a >>> 0;
2883
+ h1 = h1 + b >>> 0;
2884
+ h2 = h2 + c >>> 0;
2885
+ h3 = h3 + d >>> 0;
2886
+ h4 = h4 + e >>> 0;
2887
+ h5 = h5 + f >>> 0;
2888
+ h6 = h6 + g >>> 0;
2889
+ h7 = h7 + h >>> 0;
2890
+ }
2891
+ return [h0, h1, h2, h3, h4, h5, h6, h7].map((word) => word.toString(16).padStart(8, "0")).join("");
2892
+ }
2893
+ function utf8Bytes2(input) {
2894
+ return Array.from(new TextEncoder().encode(input));
2895
+ }
2896
+ function rightRotate(value, bits) {
2897
+ return value >>> bits | value << 32 - bits;
2898
+ }
2899
+ function ch(x, y, z) {
2900
+ return x & y ^ ~x & z;
2901
+ }
2902
+ function maj(x, y, z) {
2903
+ return x & y ^ x & z ^ y & z;
2904
+ }
2905
+ function bigSigma0(x) {
2906
+ return rightRotate(x, 2) ^ rightRotate(x, 13) ^ rightRotate(x, 22);
2907
+ }
2908
+ function bigSigma1(x) {
2909
+ return rightRotate(x, 6) ^ rightRotate(x, 11) ^ rightRotate(x, 25);
2910
+ }
2911
+ function smallSigma0(x) {
2912
+ return rightRotate(x, 7) ^ rightRotate(x, 18) ^ x >>> 3;
2913
+ }
2914
+ function smallSigma1(x) {
2915
+ return rightRotate(x, 17) ^ rightRotate(x, 19) ^ x >>> 10;
2916
+ }
2917
+ var K = [
2918
+ 1116352408,
2919
+ 1899447441,
2920
+ 3049323471,
2921
+ 3921009573,
2922
+ 961987163,
2923
+ 1508970993,
2924
+ 2453635748,
2925
+ 2870763221,
2926
+ 3624381080,
2927
+ 310598401,
2928
+ 607225278,
2929
+ 1426881987,
2930
+ 1925078388,
2931
+ 2162078206,
2932
+ 2614888103,
2933
+ 3248222580,
2934
+ 3835390401,
2935
+ 4022224774,
2936
+ 264347078,
2937
+ 604807628,
2938
+ 770255983,
2939
+ 1249150122,
2940
+ 1555081692,
2941
+ 1996064986,
2942
+ 2554220882,
2943
+ 2821834349,
2944
+ 2952996808,
2945
+ 3210313671,
2946
+ 3336571891,
2947
+ 3584528711,
2948
+ 113926993,
2949
+ 338241895,
2950
+ 666307205,
2951
+ 773529912,
2952
+ 1294757372,
2953
+ 1396182291,
2954
+ 1695183700,
2955
+ 1986661051,
2956
+ 2177026350,
2957
+ 2456956037,
2958
+ 2730485921,
2959
+ 2820302411,
2960
+ 3259730800,
2961
+ 3345764771,
2962
+ 3516065817,
2963
+ 3600352804,
2964
+ 4094571909,
2965
+ 275423344,
2966
+ 430227734,
2967
+ 506948616,
2968
+ 659060556,
2969
+ 883997877,
2970
+ 958139571,
2971
+ 1322822218,
2972
+ 1537002063,
2973
+ 1747873779,
2974
+ 1955562222,
2975
+ 2024104815,
2976
+ 2227730452,
2977
+ 2361852424,
2978
+ 2428436474,
2979
+ 2756734187,
2980
+ 3204031479,
2981
+ 3329325298
2982
+ ];
2983
+
2984
+ // src/runtime-internal/utils/browser-interaction-effects.ts
2985
+ function gameplayCandidateMetadata(input) {
2986
+ const inputDescriptor = resolvedInputByKey(
2987
+ input.descriptor,
2988
+ input.inputKey,
2989
+ input.draftValues
2990
+ );
2991
+ if (!inputDescriptor) return { semanticEffects: [] };
2992
+ const currentValue = input.draftValues[input.inputKey];
2993
+ const beforeSelected = isCandidateSelected(
2994
+ currentValue,
2995
+ input.candidateValue,
2996
+ inputDescriptor
2997
+ );
2998
+ const afterSelected = input.intent === "toggle" && isManySelection(inputDescriptor) ? !beforeSelected : true;
2999
+ return {
3000
+ semanticEffects: [
3001
+ gameplaySetCandidateEffect({
3002
+ inputKey: input.inputKey,
3003
+ candidateValue: input.candidateValue,
3004
+ beforeSelected,
3005
+ afterSelected
3006
+ })
3007
+ ]
3008
+ };
3009
+ }
3010
+ function gameplayResourceMetadata(input) {
3011
+ return {
3012
+ semanticEffects: [
3013
+ gameplayAdjustResourceEffect({
3014
+ inputKey: input.inputKey,
3015
+ resourceKey: input.resourceKey,
3016
+ delta: input.delta
3017
+ })
3018
+ ]
3019
+ };
3020
+ }
3021
+ function gameplayScalarStepMetadata(input) {
3022
+ return {
3023
+ semanticEffects: [
3024
+ gameplaySetScalarEffect({
3025
+ inputKey: input.inputKey,
3026
+ value: input.value
3027
+ })
3028
+ ]
3029
+ };
3030
+ }
3031
+ function gameplayScalarFillMetadata(input) {
3032
+ return {
3033
+ acceptedEffectPatterns: [
3034
+ boundedScalarPattern(input.inputKey, input.domain)
3035
+ ]
3036
+ };
3037
+ }
3038
+ function gameplaySubmitMetadata(input) {
3039
+ const invoke = input.explicitParams === true || input.descriptor.commit.mode !== "manual";
3040
+ return invoke ? { intent: "invoke", semanticEffects: [gameplayInvokeEffect()] } : { intent: "submit", semanticEffects: [gameplayCommitEffect()] };
3041
+ }
3042
+ function gameplayPreparationPatternsForDescriptor(descriptor, draftValues) {
3043
+ const patterns = [];
3044
+ for (const rawInput of descriptor.inputs) {
3045
+ const input = resolveInputDomain(rawInput, draftValues);
3046
+ switch (input.domain.type) {
3047
+ case "choice":
3048
+ case "choiceList":
3049
+ case "cardTarget":
3050
+ case "boardTarget":
3051
+ patterns.push({
3052
+ kind: "match",
3053
+ effectKind: "setCandidate",
3054
+ fields: { inputKey: input.key }
3055
+ });
3056
+ break;
3057
+ case "resourceMap":
3058
+ patterns.push({
3059
+ kind: "match",
3060
+ effectKind: "adjustResource",
3061
+ fields: { inputKey: input.key }
3062
+ });
3063
+ break;
3064
+ case "boundedNumber":
3065
+ patterns.push(boundedScalarPattern(input.key, input.domain));
3066
+ break;
3067
+ }
3068
+ }
3069
+ return dedupePatterns(patterns);
3070
+ }
3071
+ function boundedScalarPattern(inputKey, domain) {
3072
+ const min = domain.min ?? 0;
3073
+ const max = domain.max;
3074
+ return {
3075
+ kind: "match",
3076
+ effectKind: "setScalar",
3077
+ fields: { inputKey },
3078
+ scalar: {
3079
+ field: "value",
3080
+ min,
3081
+ ...max !== void 0 && Number.isFinite(max) ? { max } : {},
3082
+ ...scalarIsInteger(domain) ? { integer: true } : {}
3083
+ }
3084
+ };
3085
+ }
3086
+ function resolvedInputByKey(descriptor, inputKey, draftValues) {
3087
+ const input = inputByKey(descriptor, inputKey);
3088
+ return input ? resolveInputDomain(input, draftValues) : void 0;
3089
+ }
3090
+ function isCandidateSelected(currentValue, candidateValue, input) {
3091
+ if (isTargetDomain(input.domain) && input.domain.selection?.mode === "many") {
3092
+ return valueArrayIncludes(currentValue, candidateValue);
3093
+ }
3094
+ if (input.domain.type === "choiceList") {
3095
+ return valueArrayIncludes(currentValue, candidateValue);
3096
+ }
3097
+ return String(currentValue) === String(candidateValue);
3098
+ }
3099
+ function isManySelection(input) {
3100
+ if (input.domain.type === "choiceList") return true;
3101
+ return isTargetDomain(input.domain) && input.domain.selection?.mode === "many";
3102
+ }
3103
+ function valueArrayIncludes(currentValue, candidateValue) {
3104
+ return Array.isArray(currentValue) && currentValue.some((item) => String(item) === String(candidateValue));
3105
+ }
3106
+ function scalarIsInteger(domain) {
3107
+ const step = domain.step ?? 1;
3108
+ return Number.isInteger(step) && Number.isInteger(domain.min ?? 0) && (domain.max === void 0 || Number.isInteger(domain.max));
3109
+ }
3110
+ function dedupePatterns(patterns) {
3111
+ const seen = /* @__PURE__ */ new Set();
3112
+ const result = [];
3113
+ for (const pattern of patterns) {
3114
+ const key = JSON.stringify(pattern);
3115
+ if (seen.has(key)) continue;
3116
+ seen.add(key);
3117
+ result.push(pattern);
3118
+ }
3119
+ return result;
3120
+ }
3121
+
2376
3122
  // src/runtime-internal/hooks/useBoardInteractions.ts
2377
3123
  var BoardInteractionConflictError = class extends Error {
2378
3124
  name = "BoardInteractionConflictError";
@@ -2527,7 +3273,7 @@ function useBoardInteractions(options = {}) {
2527
3273
  return [];
2528
3274
  }
2529
3275
  if (!isInteractionAvailable(descriptor)) return [];
2530
- const draft = store.getDraft(descriptor.interactionKey);
3276
+ const draft = drafts[descriptor.interactionKey] ?? {};
2531
3277
  const inputKey = inputKeyForTarget(
2532
3278
  descriptor,
2533
3279
  targetKind,
@@ -2536,11 +3282,7 @@ function useBoardInteractions(options = {}) {
2536
3282
  );
2537
3283
  if (!inputKey) return [];
2538
3284
  const input = inputByTarget(descriptor, targetKind, targetId, draft);
2539
- if (input && !isTargetSelectable(
2540
- input,
2541
- store.getDraft(descriptor.interactionKey),
2542
- targetId
2543
- )) {
3285
+ if (input && !isTargetSelectable(input, draft, targetId)) {
2544
3286
  return [];
2545
3287
  }
2546
3288
  const targets = eligibleTargetsForInput(descriptor, inputKey, draft);
@@ -2555,7 +3297,7 @@ function useBoardInteractions(options = {}) {
2555
3297
  });
2556
3298
  return matches;
2557
3299
  },
2558
- [armedIds, interactions, store]
3300
+ [armedIds, drafts, interactions]
2559
3301
  );
2560
3302
  const selectByKind = useCallback3(
2561
3303
  async (targetKind, targetId, extraInputs, interactionKeys) => {
@@ -2605,10 +3347,18 @@ function useBoardInteractions(options = {}) {
2605
3347
  pending,
2606
3348
  conflict,
2607
3349
  conflictInteractionKeys: conflict ? candidates.map((candidate) => candidate.descriptor.interactionKey) : void 0,
2608
- unavailableReason: candidateUnavailableReason(selected, conflict)
3350
+ unavailableReason: candidateUnavailableReason(selected, conflict),
3351
+ browserAttributes: selected ? boardTargetBrowserAttributes({
3352
+ descriptor: selected.descriptor,
3353
+ inputKey: selected.inputKey,
3354
+ targetKind,
3355
+ targetId,
3356
+ enabled: eligible2 && !!controllingPlayerId,
3357
+ draft: drafts[selected.descriptor.interactionKey] ?? {}
3358
+ }) : void 0
2609
3359
  };
2610
3360
  },
2611
- [controllingPlayerId, pendingInteractionKey, resolveTargetMatches]
3361
+ [controllingPlayerId, drafts, pendingInteractionKey, resolveTargetMatches]
2612
3362
  );
2613
3363
  const targetLayers = useMemo8(() => {
2614
3364
  const createLayer = (targetKind) => (layerOptions = {}) => {
@@ -2721,6 +3471,43 @@ function isTargetSelectable(input, draft, targetId) {
2721
3471
  function missingInputsForDraft(descriptor, draft) {
2722
3472
  return [...getInteractionDraftReadiness(descriptor, draft).missingInputs];
2723
3473
  }
3474
+ var GAMEPLAY_BROWSER_SCOPE_ID2 = "runtime";
3475
+ function boardTargetBrowserAttributes({
3476
+ descriptor,
3477
+ inputKey,
3478
+ targetKind,
3479
+ targetId,
3480
+ enabled,
3481
+ draft
3482
+ }) {
3483
+ return createGameplayActuatorAttributes({
3484
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID2,
3485
+ interactionKey: descriptor.interactionKey,
3486
+ interactionId: descriptor.interactionId,
3487
+ intent: "select",
3488
+ enabled,
3489
+ actuatorKind: "click",
3490
+ actuatorId: `board:${targetKind}:${targetId}`,
3491
+ ...descriptor.descriptorDigest !== void 0 ? { descriptorDigest: descriptor.descriptorDigest } : {},
3492
+ ...descriptor.draftDigest !== void 0 ? { draftDigest: interactionDraftDigestForValues(descriptor, draft) } : {},
3493
+ inputKey,
3494
+ candidateValue: targetId,
3495
+ candidateState: isBoardTargetSelected(draft[inputKey], targetId) ? "selected" : "unselected",
3496
+ semanticEffects: gameplayCandidateMetadata({
3497
+ descriptor,
3498
+ draftValues: draft,
3499
+ inputKey,
3500
+ candidateValue: targetId,
3501
+ intent: "select"
3502
+ }).semanticEffects
3503
+ });
3504
+ }
3505
+ function isBoardTargetSelected(value, targetId) {
3506
+ if (Array.isArray(value)) {
3507
+ return value.some((item) => String(item) === targetId);
3508
+ }
3509
+ return value !== void 0 && String(value) === targetId;
3510
+ }
2724
3511
  function selectDispatchCandidate(matches, targetKind, targetId) {
2725
3512
  if (matches.length === 0) return null;
2726
3513
  const armed = matches.filter((match) => match.armed);
@@ -2882,6 +3669,7 @@ function UnambiguousBoardTarget({
2882
3669
  return renderPrimitive("button", {
2883
3670
  type: "button",
2884
3671
  ...props,
3672
+ ...targetState.browserAttributes,
2885
3673
  children,
2886
3674
  disabled: isDisabled,
2887
3675
  "aria-disabled": isDisabled,
@@ -2929,9 +3717,18 @@ function ExplicitBoardTarget({
2929
3717
  const eligibleTargets = inputKey ? eligibleTargetsForInput(descriptor, inputKey, draft) : void 0;
2930
3718
  const eligible = inputDescriptor !== void 0 && inputDescriptor.domain.type === "boardTarget" && isResolvedTargetDomain(inputDescriptor.domain) && inputDescriptor.domain.targetKind === kind && (eligibleTargets ?? inputDescriptor.domain.eligibleTargets).includes(value);
2931
3719
  const isDisabled = disabled ?? (!isInteractionAvailable(descriptor) || !eligible);
3720
+ const browserAttributes = inputKey ? boardTargetBrowserAttributes2({
3721
+ descriptor,
3722
+ inputKey,
3723
+ targetKind: kind,
3724
+ targetId: value,
3725
+ enabled: !isDisabled,
3726
+ draft
3727
+ }) : void 0;
2932
3728
  return renderPrimitive("button", {
2933
3729
  type: "button",
2934
3730
  ...props,
3731
+ ...browserAttributes,
2935
3732
  children,
2936
3733
  disabled: isDisabled,
2937
3734
  "aria-disabled": isDisabled,
@@ -2990,6 +3787,43 @@ function UnavailableBoardTarget({
2990
3787
  function resolveExtraInputs(extraInputs, targetId) {
2991
3788
  return typeof extraInputs === "function" ? extraInputs(targetId) : extraInputs ?? {};
2992
3789
  }
3790
+ var GAMEPLAY_BROWSER_SCOPE_ID3 = "runtime";
3791
+ function boardTargetBrowserAttributes2({
3792
+ descriptor,
3793
+ inputKey,
3794
+ targetKind,
3795
+ targetId,
3796
+ enabled,
3797
+ draft
3798
+ }) {
3799
+ return createGameplayActuatorAttributes({
3800
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID3,
3801
+ interactionKey: descriptor.interactionKey,
3802
+ interactionId: descriptor.interactionId,
3803
+ intent: "select",
3804
+ enabled,
3805
+ actuatorKind: "click",
3806
+ actuatorId: `board:${targetKind}:${targetId}`,
3807
+ ...descriptor.descriptorDigest !== void 0 ? { descriptorDigest: descriptor.descriptorDigest } : {},
3808
+ ...descriptor.draftDigest !== void 0 ? { draftDigest: interactionDraftDigestForValues(descriptor, draft) } : {},
3809
+ inputKey,
3810
+ candidateValue: targetId,
3811
+ candidateState: isBoardTargetSelected2(draft[inputKey], targetId) ? "selected" : "unselected",
3812
+ semanticEffects: gameplayCandidateMetadata({
3813
+ descriptor,
3814
+ draftValues: draft,
3815
+ inputKey,
3816
+ candidateValue: targetId,
3817
+ intent: "select"
3818
+ }).semanticEffects
3819
+ });
3820
+ }
3821
+ function isBoardTargetSelected2(value, targetId) {
3822
+ if (Array.isArray(value)) {
3823
+ return value.some((item) => String(item) === targetId);
3824
+ }
3825
+ return value !== void 0 && String(value) === targetId;
3826
+ }
2993
3827
  var Board = {
2994
3828
  Root: BoardRoot,
2995
3829
  State: BoardState,
@@ -3285,6 +4119,64 @@ import {
3285
4119
  import * as AccordionPrimitive from "@radix-ui/react-accordion";
3286
4120
  import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
3287
4121
  var EMPTY_FIELD_ERRORS = [];
4122
+ var GAMEPLAY_BROWSER_SCOPE_ID4 = "runtime";
4123
+ function gameplayInteractionRootAttributes({
4124
+ descriptor,
4125
+ draftValues,
4126
+ ready,
4127
+ available
4128
+ }) {
4129
+ return createGameplayInteractionRootAttributes({
4130
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID4,
4131
+ interactionKey: descriptor.interactionKey,
4132
+ interactionId: descriptor.interactionId,
4133
+ ...descriptor.descriptorDigest !== void 0 ? { descriptorDigest: descriptor.descriptorDigest } : {},
4134
+ ...descriptor.draftDigest !== void 0 ? {
4135
+ draftDigest: interactionDraftDigestForValues(
4136
+ descriptor,
4137
+ draftValues ?? {}
4138
+ )
4139
+ } : {},
4140
+ readiness: available ? ready ? "ready" : "blocked" : "unavailable"
4141
+ });
4142
+ }
4143
+ function gameplayActuatorAttributes({
4144
+ descriptor,
4145
+ inputKey,
4146
+ intent,
4147
+ candidateValue,
4148
+ candidateState,
4149
+ semanticEffects,
4150
+ acceptedEffectPatterns,
4151
+ preparationPatterns,
4152
+ draftValues,
4153
+ enabled,
4154
+ actuatorKind,
4155
+ actuatorId
4156
+ }) {
4157
+ return createGameplayActuatorAttributes({
4158
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID4,
4159
+ interactionKey: descriptor.interactionKey,
4160
+ interactionId: descriptor.interactionId,
4161
+ intent,
4162
+ enabled,
4163
+ actuatorKind,
4164
+ actuatorId,
4165
+ ...descriptor.descriptorDigest !== void 0 ? { descriptorDigest: descriptor.descriptorDigest } : {},
4166
+ ...descriptor.draftDigest !== void 0 ? {
4167
+ draftDigest: interactionDraftDigestForValues(
4168
+ descriptor,
4169
+ draftValues ?? {}
4170
+ )
4171
+ } : {},
4172
+ ...inputKey !== void 0 ? { inputKey } : {},
4173
+ ...candidateValue !== void 0 ? { candidateValue } : {},
4174
+ ...candidateState !== void 0 ? { candidateState } : {},
4175
+ ...semanticEffects !== void 0 ? { semanticEffects } : {},
4176
+ ...acceptedEffectPatterns !== void 0 ? { acceptedEffectPatterns } : {},
4177
+ ...preparationPatterns !== void 0 ? { preparationPatterns } : {}
4178
+ });
4179
+ }
3288
4180
  function InteractionForm({
3289
4181
  descriptor,
3290
4182
  handle,
@@ -3342,6 +4234,34 @@ function InteractionForm({
3342
4234
  ];
3343
4235
  const isDisabled = disabled || pending || !isInteractionAvailable(descriptor);
3344
4236
  const useAccordion = accordion && visibleInputs.length > 0;
4237
+ const rootBrowserAttributes = gameplayInteractionRootAttributes({
4238
+ descriptor,
4239
+ draftValues: handle.values,
4240
+ ready: handle.isReady,
4241
+ available: !isDisabled
4242
+ });
4243
+ const armBrowserAttributes = gameplayActuatorAttributes({
4244
+ descriptor,
4245
+ draftValues: handle.values,
4246
+ intent: "arm",
4247
+ enabled: !isDisabled,
4248
+ actuatorKind: "click",
4249
+ actuatorId: "arm",
4250
+ preparationPatterns: gameplayPreparationPatternsForDescriptor(
4251
+ descriptor,
4252
+ handle.values
4253
+ )
4254
+ });
4255
+ const submitMetadata = gameplaySubmitMetadata({ descriptor });
4256
+ const submitBrowserAttributes = gameplayActuatorAttributes({
4257
+ descriptor,
4258
+ draftValues: handle.values,
4259
+ intent: submitMetadata.intent,
4260
+ enabled: !isDisabled && handle.isReady,
4261
+ actuatorKind: "click",
4262
+ actuatorId: "submit",
4263
+ semanticEffects: submitMetadata.semanticEffects
4264
+ });
3345
4265
  useEffect6(() => {
3346
4266
  setAccordionOpen(defaultOpen);
3347
4267
  }, [defaultOpen, descriptor.interactionId]);
@@ -3489,6 +4409,7 @@ function InteractionForm({
3489
4409
  size: "sm",
3490
4410
  disabled: isDisabled,
3491
4411
  className: "h-9 px-3 text-sm",
4412
+ ...submitBrowserAttributes,
3492
4413
  ...buttonProps,
3493
4414
  children: pending ? "Submitting..." : children ?? submitLabel ?? fallbackLabel
3494
4415
  }
@@ -3503,6 +4424,7 @@ function InteractionForm({
3503
4424
  size: "sm",
3504
4425
  disabled: isDisabled,
3505
4426
  className: "h-9 px-3 text-sm",
4427
+ ...submitBrowserAttributes,
3506
4428
  children: pending ? "Submitting..." : submitLabel ?? fallbackLabel
3507
4429
  }
3508
4430
  )
@@ -3521,6 +4443,7 @@ function InteractionForm({
3521
4443
  "data-interaction-id": descriptor.interactionId,
3522
4444
  onSubmit: (event) => void submit(event),
3523
4445
  style: containerStyle,
4446
+ ...rootBrowserAttributes,
3524
4447
  children: useAccordion ? /* @__PURE__ */ jsx11(
3525
4448
  AccordionPrimitive.Root,
3526
4449
  {
@@ -3532,6 +4455,7 @@ function InteractionForm({
3532
4455
  /* @__PURE__ */ jsx11(AccordionPrimitive.Header, { style: { margin: 0 }, children: /* @__PURE__ */ jsxs3(
3533
4456
  AccordionPrimitive.Trigger,
3534
4457
  {
4458
+ ...armBrowserAttributes,
3535
4459
  style: {
3536
4460
  alignItems: "center",
3537
4461
  appearance: "none",
@@ -3613,6 +4537,24 @@ function createInteractionInputSlot({
3613
4537
  const selected = selection?.mode === "many" ? Array.isArray(currentValue) && currentValue.map(String).includes(targetValue) : currentValue !== void 0 && String(currentValue) === targetValue;
3614
4538
  const isDisabled = disabled || !eligible;
3615
4539
  const dataAttribute = kind === "card" ? { "data-dreamboard-interaction-card-slot": "" } : { "data-dreamboard-interaction-target-slot": "" };
4540
+ const browserAttributes = gameplayActuatorAttributes({
4541
+ descriptor,
4542
+ draftValues: handle.values,
4543
+ inputKey: input.key,
4544
+ intent: selection?.mode === "many" ? "toggle" : "select",
4545
+ candidateValue: targetValue,
4546
+ candidateState: selected ? "selected" : "unselected",
4547
+ enabled: !isDisabled,
4548
+ actuatorKind: "click",
4549
+ actuatorId: `${kind}:${input.key}:${targetValue}`,
4550
+ semanticEffects: gameplayCandidateMetadata({
4551
+ descriptor,
4552
+ draftValues: handle.values,
4553
+ inputKey: input.key,
4554
+ candidateValue: targetValue,
4555
+ intent: selection?.mode === "many" ? "toggle" : "select"
4556
+ }).semanticEffects
4557
+ });
3616
4558
  return /* @__PURE__ */ jsx11(
3617
4559
  "button",
3618
4560
  {
@@ -3627,6 +4569,7 @@ function createInteractionInputSlot({
3627
4569
  "data-selected": selected || void 0,
3628
4570
  "data-disabled": isDisabled || void 0,
3629
4571
  ...dataAttribute,
4572
+ ...browserAttributes,
3630
4573
  ...buttonProps,
3631
4574
  onClick: () => {
3632
4575
  if (isDisabled) return;
@@ -3655,6 +4598,24 @@ function createInteractionInputSlot({
3655
4598
  Default: ({ children }) => {
3656
4599
  const hasDefault = "defaultValue" in input;
3657
4600
  const isDisabled = disabled || !hasDefault;
4601
+ const browserAttributes = gameplayActuatorAttributes({
4602
+ descriptor,
4603
+ draftValues: handle.values,
4604
+ inputKey: input.key,
4605
+ intent: "select",
4606
+ candidateValue: input.defaultValue,
4607
+ candidateState: "unselected",
4608
+ enabled: !isDisabled,
4609
+ actuatorKind: "click",
4610
+ actuatorId: `default:${input.key}`,
4611
+ semanticEffects: hasDefault ? gameplayCandidateMetadata({
4612
+ descriptor,
4613
+ draftValues: handle.values,
4614
+ inputKey: input.key,
4615
+ candidateValue: input.defaultValue,
4616
+ intent: "select"
4617
+ }).semanticEffects : void 0
4618
+ });
3658
4619
  return /* @__PURE__ */ jsx11(
3659
4620
  "button",
3660
4621
  {
@@ -3664,6 +4625,7 @@ function createInteractionInputSlot({
3664
4625
  "data-dreamboard-interaction-default-slot": "",
3665
4626
  "data-input-name": input.key,
3666
4627
  "data-disabled": isDisabled || void 0,
4628
+ ...browserAttributes,
3667
4629
  onClick: () => {
3668
4630
  if (isDisabled) return;
3669
4631
  handle.setInput(input.key, input.defaultValue);
@@ -3893,6 +4855,8 @@ function decodeChoiceSelectValue(value) {
3893
4855
  return value === NULL_CHOICE_SELECT_VALUE ? null : value;
3894
4856
  }
3895
4857
  function ChoiceField({
4858
+ descriptor,
4859
+ handle,
3896
4860
  input,
3897
4861
  value,
3898
4862
  setValue,
@@ -3923,15 +4887,34 @@ function ChoiceField({
3923
4887
  },
3924
4888
  children: choices.map((choice) => {
3925
4889
  const selected = value === choice.value;
4890
+ const isDisabled = disabled || choice.disabled;
3926
4891
  return /* @__PURE__ */ jsx11(
3927
4892
  ThemedButton,
3928
4893
  {
3929
4894
  type: "button",
3930
4895
  variant: selected ? "primary" : "secondary",
3931
4896
  size: "sm",
3932
- disabled: disabled || choice.disabled,
4897
+ disabled: isDisabled,
3933
4898
  "aria-pressed": selected,
3934
4899
  title: choice.disabledReason ?? choice.description,
4900
+ ...gameplayActuatorAttributes({
4901
+ descriptor,
4902
+ draftValues: handle.values,
4903
+ inputKey: input.key,
4904
+ intent: "select",
4905
+ candidateValue: choice.value,
4906
+ candidateState: selected ? "selected" : "unselected",
4907
+ enabled: !isDisabled,
4908
+ actuatorKind: "click",
4909
+ actuatorId: `choice:${input.key}:${choiceRenderKey(choice)}`,
4910
+ semanticEffects: gameplayCandidateMetadata({
4911
+ descriptor,
4912
+ draftValues: handle.values,
4913
+ inputKey: input.key,
4914
+ candidateValue: choice.value,
4915
+ intent: "select"
4916
+ }).semanticEffects
4917
+ }),
3935
4918
  onClick: () => setValue(choice.value),
3936
4919
  className: "h-8 px-3 text-sm",
3937
4920
  children: /* @__PURE__ */ jsx11(ChoiceOptionLabel, { choice })
@@ -3959,7 +4942,28 @@ function ChoiceField({
3959
4942
  value: encodeChoiceSelectValue(value),
3960
4943
  onValueChange: (next) => setValue(decodeChoiceSelectValue(next)),
3961
4944
  children: [
3962
- /* @__PURE__ */ jsx11(SelectTrigger, { id: controlId, size: "sm", className: "w-full bg-white", children: /* @__PURE__ */ jsx11("span", { "data-slot": "select-value", children: selectedChoice ? /* @__PURE__ */ jsx11(ChoiceOptionLabel, { choice: selectedChoice }) : /* @__PURE__ */ jsx11("span", { style: { color: theme.semantic.text.muted }, children: "Choose..." }) }) }),
4945
+ /* @__PURE__ */ jsx11(
4946
+ SelectTrigger,
4947
+ {
4948
+ id: controlId,
4949
+ size: "sm",
4950
+ className: "w-full bg-white",
4951
+ ...gameplayActuatorAttributes({
4952
+ descriptor,
4953
+ draftValues: handle.values,
4954
+ inputKey: input.key,
4955
+ intent: "reveal",
4956
+ enabled: !disabled,
4957
+ actuatorKind: "click",
4958
+ actuatorId: `choice-reveal:${input.key}`,
4959
+ preparationPatterns: gameplayPreparationPatternsForDescriptor(
4960
+ { inputs: [input] },
4961
+ handle.values
4962
+ )
4963
+ }),
4964
+ children: /* @__PURE__ */ jsx11("span", { "data-slot": "select-value", children: selectedChoice ? /* @__PURE__ */ jsx11(ChoiceOptionLabel, { choice: selectedChoice }) : /* @__PURE__ */ jsx11("span", { style: { color: theme.semantic.text.muted }, children: "Choose..." }) })
4965
+ }
4966
+ ),
3963
4967
  /* @__PURE__ */ jsx11(
3964
4968
  SelectContent,
3965
4969
  {
@@ -3973,6 +4977,24 @@ function ChoiceField({
3973
4977
  value: choiceRenderKey(choice),
3974
4978
  textValue: choice.label,
3975
4979
  disabled: choice.disabled,
4980
+ ...gameplayActuatorAttributes({
4981
+ descriptor,
4982
+ draftValues: handle.values,
4983
+ inputKey: input.key,
4984
+ intent: "select",
4985
+ candidateValue: choice.value,
4986
+ candidateState: value === choice.value ? "selected" : "unselected",
4987
+ enabled: !disabled && !choice.disabled,
4988
+ actuatorKind: "click",
4989
+ actuatorId: `choice:${input.key}:${choiceRenderKey(choice)}`,
4990
+ semanticEffects: gameplayCandidateMetadata({
4991
+ descriptor,
4992
+ draftValues: handle.values,
4993
+ inputKey: input.key,
4994
+ candidateValue: choice.value,
4995
+ intent: "select"
4996
+ }).semanticEffects
4997
+ }),
3976
4998
  children: /* @__PURE__ */ jsx11(ChoiceOptionLabel, { choice })
3977
4999
  },
3978
5000
  choiceRenderKey(choice)
@@ -3988,6 +5010,8 @@ function ChoiceField({
3988
5010
  );
3989
5011
  }
3990
5012
  function ChoiceListField({
5013
+ descriptor,
5014
+ handle,
3991
5015
  input,
3992
5016
  value,
3993
5017
  setValue,
@@ -4029,15 +5053,34 @@ function ChoiceListField({
4029
5053
  children: /* @__PURE__ */ jsx11("span", { style: { display: "flex", flexWrap: "wrap", gap: theme.space[1] }, children: (domain.choices ?? []).map((choice) => {
4030
5054
  const value2 = choice.value;
4031
5055
  const checked = selected.has(value2);
5056
+ const isDisabled = disabled || choice.disabled || !checked && selected.size >= max;
4032
5057
  return /* @__PURE__ */ jsx11(
4033
5058
  ThemedButton,
4034
5059
  {
4035
5060
  type: "button",
4036
5061
  variant: checked ? "primary" : "secondary",
4037
5062
  size: "sm",
4038
- disabled: disabled || choice.disabled || !checked && selected.size >= max,
5063
+ disabled: isDisabled,
4039
5064
  "aria-pressed": checked,
4040
5065
  title: choice.disabledReason ?? choice.description,
5066
+ ...gameplayActuatorAttributes({
5067
+ descriptor,
5068
+ draftValues: handle.values,
5069
+ inputKey: input.key,
5070
+ intent: "toggle",
5071
+ candidateValue: value2,
5072
+ candidateState: checked ? "selected" : "unselected",
5073
+ enabled: !isDisabled,
5074
+ actuatorKind: "click",
5075
+ actuatorId: `choice-list:${input.key}:${value2}`,
5076
+ semanticEffects: gameplayCandidateMetadata({
5077
+ descriptor,
5078
+ draftValues: handle.values,
5079
+ inputKey: input.key,
5080
+ candidateValue: value2,
5081
+ intent: "toggle"
5082
+ }).semanticEffects
5083
+ }),
4041
5084
  onClick: () => toggle(value2),
4042
5085
  className: "h-8 px-3 text-sm",
4043
5086
  children: /* @__PURE__ */ jsx11(ChoiceOptionLabel, { choice })
@@ -4049,6 +5092,8 @@ function ChoiceListField({
4049
5092
  );
4050
5093
  }
4051
5094
  function ResourceMapField({
5095
+ descriptor,
5096
+ handle,
4052
5097
  input,
4053
5098
  value,
4054
5099
  setValue,
@@ -4058,7 +5103,7 @@ function ResourceMapField({
4058
5103
  domain
4059
5104
  }) {
4060
5105
  const theme = useTheme();
4061
- const current = isRecord(value) ? value : {};
5106
+ const current = isRecord2(value) ? value : {};
4062
5107
  const update = (resourceId, delta, min, max) => {
4063
5108
  const previous = numberOrZero(current[resourceId]);
4064
5109
  const next = Math.max(min, Math.min(max, previous + delta));
@@ -4109,6 +5154,21 @@ function ResourceMapField({
4109
5154
  {
4110
5155
  label: `Decrease ${resource.label ?? resource.resourceId}`,
4111
5156
  disabled: disabled || amount <= min,
5157
+ browserAttributes: gameplayActuatorAttributes({
5158
+ descriptor,
5159
+ draftValues: handle.values,
5160
+ inputKey: input.key,
5161
+ intent: "decrement",
5162
+ candidateValue: resource.resourceId,
5163
+ enabled: !(disabled || amount <= min),
5164
+ actuatorKind: "click",
5165
+ actuatorId: `resource-decrement:${input.key}:${resource.resourceId}`,
5166
+ semanticEffects: gameplayResourceMetadata({
5167
+ inputKey: input.key,
5168
+ resourceKey: resource.resourceId,
5169
+ delta: -1
5170
+ }).semanticEffects
5171
+ }),
4112
5172
  onClick: () => update(resource.resourceId, -1, min, max),
4113
5173
  children: "-"
4114
5174
  }
@@ -4129,6 +5189,21 @@ function ResourceMapField({
4129
5189
  {
4130
5190
  label: `Increase ${resource.label ?? resource.resourceId}`,
4131
5191
  disabled: disabled || amount >= max,
5192
+ browserAttributes: gameplayActuatorAttributes({
5193
+ descriptor,
5194
+ draftValues: handle.values,
5195
+ inputKey: input.key,
5196
+ intent: "increment",
5197
+ candidateValue: resource.resourceId,
5198
+ enabled: !(disabled || amount >= max),
5199
+ actuatorKind: "click",
5200
+ actuatorId: `resource-increment:${input.key}:${resource.resourceId}`,
5201
+ semanticEffects: gameplayResourceMetadata({
5202
+ inputKey: input.key,
5203
+ resourceKey: resource.resourceId,
5204
+ delta: 1
5205
+ }).semanticEffects
5206
+ }),
4132
5207
  onClick: () => update(resource.resourceId, 1, min, max),
4133
5208
  children: "+"
4134
5209
  }
@@ -4142,6 +5217,8 @@ function ResourceMapField({
4142
5217
  ) });
4143
5218
  }
4144
5219
  function BoundedNumberField({
5220
+ descriptor,
5221
+ handle,
4145
5222
  input,
4146
5223
  value,
4147
5224
  setValue,
@@ -4178,6 +5255,19 @@ function BoundedNumberField({
4178
5255
  {
4179
5256
  label: `Decrease ${input.key}`,
4180
5257
  disabled: disabled || current <= min,
5258
+ browserAttributes: gameplayActuatorAttributes({
5259
+ descriptor,
5260
+ draftValues: handle.values,
5261
+ inputKey: input.key,
5262
+ intent: "decrement",
5263
+ enabled: !(disabled || current <= min),
5264
+ actuatorKind: "click",
5265
+ actuatorId: `bounded-decrement:${input.key}`,
5266
+ semanticEffects: gameplayScalarStepMetadata({
5267
+ inputKey: input.key,
5268
+ value: Math.max(min, Math.min(max, current - step))
5269
+ }).semanticEffects
5270
+ }),
4181
5271
  onClick: () => update(current - step),
4182
5272
  children: "-"
4183
5273
  }
@@ -4192,6 +5282,19 @@ function BoundedNumberField({
4192
5282
  step,
4193
5283
  value: current,
4194
5284
  disabled,
5285
+ ...gameplayActuatorAttributes({
5286
+ descriptor,
5287
+ draftValues: handle.values,
5288
+ inputKey: input.key,
5289
+ intent: "fill",
5290
+ enabled: !disabled,
5291
+ actuatorKind: "fill",
5292
+ actuatorId: `bounded-fill:${input.key}`,
5293
+ acceptedEffectPatterns: gameplayScalarFillMetadata({
5294
+ inputKey: input.key,
5295
+ domain
5296
+ }).acceptedEffectPatterns
5297
+ }),
4195
5298
  onChange: (event) => update(Number(event.target.value)),
4196
5299
  className: "h-9 w-[8ch] px-2 text-center text-sm md:text-sm"
4197
5300
  }
@@ -4201,6 +5304,19 @@ function BoundedNumberField({
4201
5304
  {
4202
5305
  label: `Increase ${input.key}`,
4203
5306
  disabled: disabled || current >= max,
5307
+ browserAttributes: gameplayActuatorAttributes({
5308
+ descriptor,
5309
+ draftValues: handle.values,
5310
+ inputKey: input.key,
5311
+ intent: "increment",
5312
+ enabled: !(disabled || current >= max),
5313
+ actuatorKind: "click",
5314
+ actuatorId: `bounded-increment:${input.key}`,
5315
+ semanticEffects: gameplayScalarStepMetadata({
5316
+ inputKey: input.key,
5317
+ value: Math.max(min, Math.min(max, current + step))
5318
+ }).semanticEffects
5319
+ }),
4204
5320
  onClick: () => update(current + step),
4205
5321
  children: "+"
4206
5322
  }
@@ -4234,6 +5350,7 @@ function targetSelectionLabel(domain) {
4234
5350
  function StepperButton({
4235
5351
  label,
4236
5352
  disabled,
5353
+ browserAttributes,
4237
5354
  onClick,
4238
5355
  children
4239
5356
  }) {
@@ -4245,6 +5362,7 @@ function StepperButton({
4245
5362
  size: "sm",
4246
5363
  "aria-label": label,
4247
5364
  disabled,
5365
+ ...browserAttributes,
4248
5366
  onClick,
4249
5367
  className: "h-8 w-8 text-sm",
4250
5368
  children
@@ -4263,7 +5381,7 @@ function labelForInput(input) {
4263
5381
  function humanize(key) {
4264
5382
  return key.replace(/Id$/, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
4265
5383
  }
4266
- function isRecord(value) {
5384
+ function isRecord2(value) {
4267
5385
  return typeof value === "object" && value !== null && !Array.isArray(value);
4268
5386
  }
4269
5387
  function numberOrZero(value) {
@@ -4341,6 +5459,38 @@ function humanizeInteraction(value) {
4341
5459
  const leaf = parts[parts.length - 1] ?? value;
4342
5460
  return leaf.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim().replace(/^./, (first) => first.toUpperCase());
4343
5461
  }
5462
+ var GAMEPLAY_BROWSER_SCOPE_ID5 = "runtime";
5463
+ function gameplayActuatorAttributes2({
5464
+ descriptor,
5465
+ inputKey,
5466
+ intent,
5467
+ candidateValue,
5468
+ candidateState,
5469
+ semanticEffects,
5470
+ acceptedEffectPatterns,
5471
+ preparationPatterns,
5472
+ enabled,
5473
+ actuatorKind,
5474
+ actuatorId
5475
+ }) {
5476
+ return createGameplayActuatorAttributes({
5477
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID5,
5478
+ interactionKey: descriptor.interactionKey,
5479
+ interactionId: descriptor.interactionId,
5480
+ intent,
5481
+ enabled,
5482
+ actuatorKind,
5483
+ actuatorId,
5484
+ ...descriptor.descriptorDigest !== void 0 ? { descriptorDigest: descriptor.descriptorDigest } : {},
5485
+ ...descriptor.draftDigest !== void 0 ? { draftDigest: descriptor.draftDigest } : {},
5486
+ ...inputKey !== void 0 ? { inputKey } : {},
5487
+ ...candidateValue !== void 0 ? { candidateValue } : {},
5488
+ ...candidateState !== void 0 ? { candidateState } : {},
5489
+ ...semanticEffects !== void 0 ? { semanticEffects } : {},
5490
+ ...acceptedEffectPatterns !== void 0 ? { acceptedEffectPatterns } : {},
5491
+ ...preparationPatterns !== void 0 ? { preparationPatterns } : {}
5492
+ });
5493
+ }
4344
5494
  function useInteractionPrimitiveContext() {
4345
5495
  const value = useContext9(InteractionContext);
4346
5496
  if (!value) {
@@ -4378,7 +5528,20 @@ function ResolvedInteractionRoot({
4378
5528
  () => ({ interaction, descriptor, handle }),
4379
5529
  [descriptor, handle, interaction]
4380
5530
  );
4381
- return /* @__PURE__ */ jsx13(InteractionContext.Provider, { value, children });
5531
+ const available = isInteractionAvailable(descriptor);
5532
+ const rootAttributes = createGameplayInteractionRootAttributes({
5533
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID5,
5534
+ interactionKey: descriptor.interactionKey,
5535
+ interactionId: descriptor.interactionId,
5536
+ ...descriptor.descriptorDigest !== void 0 ? { descriptorDigest: descriptor.descriptorDigest } : {},
5537
+ ...descriptor.draftDigest !== void 0 ? { draftDigest: descriptor.draftDigest } : {},
5538
+ readiness: available ? handle.isReady ? "ready" : "blocked" : "unavailable"
5539
+ });
5540
+ return /* @__PURE__ */ jsx13(InteractionContext.Provider, { value, children: renderPrimitive("span", {
5541
+ ...rootAttributes,
5542
+ style: { display: "contents" },
5543
+ children
5544
+ }) });
4382
5545
  }
4383
5546
  function InteractionRoot({
4384
5547
  interaction,
@@ -4554,6 +5717,17 @@ function InteractionTrigger({
4554
5717
  return renderPrimitive("button", {
4555
5718
  type: "button",
4556
5719
  ...props,
5720
+ ...descriptor ? gameplayActuatorAttributes2({
5721
+ descriptor,
5722
+ intent: "arm",
5723
+ enabled: !isDisabled,
5724
+ actuatorKind: "click",
5725
+ actuatorId: "primitive-trigger",
5726
+ preparationPatterns: gameplayPreparationPatternsForDescriptor(
5727
+ descriptor,
5728
+ handle?.values ?? {}
5729
+ )
5730
+ }) : {},
4557
5731
  disabled: isDisabled,
4558
5732
  "aria-disabled": isDisabled,
4559
5733
  "data-dreamboard-interaction-trigger": "",
@@ -4633,11 +5807,20 @@ function InteractionSubmit({
4633
5807
  const gameActionError = useGameActionError();
4634
5808
  const isSubmitting = handle?.status === "submitting";
4635
5809
  const hasExplicitParams = params !== void 0;
5810
+ const submitMetadata = descriptor ? gameplaySubmitMetadata({ descriptor, explicitParams: hasExplicitParams }) : null;
4636
5811
  const available = isInteractionAvailable(descriptor);
4637
5812
  const isDisabled = disabled === true || !available || !hasExplicitParams && !handle?.isReady || isSubmitting;
4638
5813
  return renderPrimitive("button", {
4639
5814
  type: "button",
4640
5815
  ...props,
5816
+ ...descriptor ? gameplayActuatorAttributes2({
5817
+ descriptor,
5818
+ intent: submitMetadata?.intent ?? "submit",
5819
+ enabled: !isDisabled,
5820
+ actuatorKind: "click",
5821
+ actuatorId: "primitive-submit",
5822
+ semanticEffects: submitMetadata?.semanticEffects
5823
+ }) : {},
4641
5824
  disabled: isDisabled,
4642
5825
  "aria-disabled": isDisabled,
4643
5826
  "data-dreamboard-interaction-submit": "",
@@ -4677,9 +5860,27 @@ function InteractionInput({
4677
5860
  const { descriptor, handle } = useInteractionPrimitiveContext();
4678
5861
  const value = handle?.draft[name];
4679
5862
  const isDisabled = disabled === true || !isInteractionAvailable(descriptor);
5863
+ const inputDescriptor = descriptor ? inputByKey(descriptor, name) : void 0;
5864
+ const resolvedInputDescriptor = inputDescriptor && handle ? resolveInputDomain(
5865
+ inputDescriptor,
5866
+ handle.values
5867
+ ) : void 0;
5868
+ const scalarFillMetadata = resolvedInputDescriptor?.domain.type === "boundedNumber" ? gameplayScalarFillMetadata({
5869
+ inputKey: name,
5870
+ domain: resolvedInputDescriptor.domain
5871
+ }) : void 0;
4680
5872
  return renderPrimitive("input", {
4681
5873
  ...props,
4682
5874
  name,
5875
+ ...descriptor ? gameplayActuatorAttributes2({
5876
+ descriptor,
5877
+ inputKey: name,
5878
+ intent: "fill",
5879
+ enabled: !isDisabled,
5880
+ actuatorKind: "fill",
5881
+ actuatorId: `primitive-input:${name}`,
5882
+ acceptedEffectPatterns: scalarFillMetadata?.acceptedEffectPatterns
5883
+ }) : {},
4683
5884
  disabled: isDisabled,
4684
5885
  "aria-disabled": isDisabled,
4685
5886
  "data-dreamboard-interaction-input": "",
@@ -4802,6 +6003,23 @@ function InteractionCardInput({
4802
6003
  type: "button",
4803
6004
  ...props,
4804
6005
  children: renderedChildren,
6006
+ ...descriptor ? gameplayActuatorAttributes2({
6007
+ descriptor,
6008
+ inputKey: input,
6009
+ intent: selection?.mode === "many" ? "toggle" : "select",
6010
+ candidateValue: cardId,
6011
+ candidateState: isSelected ? "selected" : "unselected",
6012
+ enabled: !isDisabled,
6013
+ actuatorKind: "click",
6014
+ actuatorId: `primitive-card:${input}:${cardId ?? "missing"}`,
6015
+ semanticEffects: cardId !== void 0 ? gameplayCandidateMetadata({
6016
+ descriptor,
6017
+ draftValues: liveDraft,
6018
+ inputKey: input,
6019
+ candidateValue: cardId,
6020
+ intent: selection?.mode === "many" ? "toggle" : "select"
6021
+ }).semanticEffects : void 0
6022
+ }) : {},
4805
6023
  disabled: isDisabled,
4806
6024
  "aria-disabled": isDisabled,
4807
6025
  "aria-pressed": isSelected,
@@ -5309,6 +6527,7 @@ export {
5309
6527
  UI,
5310
6528
  usePluginSession,
5311
6529
  RuntimeProvider,
6530
+ RuntimeSemanticProjectionMarker,
5312
6531
  usePluginState,
5313
6532
  InteractionUiProvider,
5314
6533
  isInteractionAvailable,
@@ -5398,4 +6617,4 @@ export {
5398
6617
  DiceValues,
5399
6618
  Dice
5400
6619
  };
5401
- //# sourceMappingURL=chunk-TDSWKVZ4.js.map
6620
+ //# sourceMappingURL=chunk-ZABVH7AO.js.map