@dreamboard-games/ui-sdk 0.0.43 → 0.0.46

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 (172) hide show
  1. package/dist/components/ActionButton.d.ts.map +1 -1
  2. package/dist/components/ActionButton.js +2 -1
  3. package/dist/components/Card.d.ts +1 -1
  4. package/dist/components/Card.d.ts.map +1 -1
  5. package/dist/components/DiceRoller.d.ts +3 -2
  6. package/dist/components/DiceRoller.d.ts.map +1 -1
  7. package/dist/components/DiceRoller.js +4 -13
  8. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  9. package/dist/components/ErrorBoundary.js +94 -2
  10. package/dist/components/InteractionForm.d.ts +1 -1
  11. package/dist/components/InteractionForm.d.ts.map +1 -1
  12. package/dist/components/InteractionForm.js +29 -15
  13. package/dist/components/PrimaryActionButton.d.ts.map +1 -1
  14. package/dist/components/PrimaryActionButton.js +7 -6
  15. package/dist/components/ResourceCounter.d.ts +59 -25
  16. package/dist/components/ResourceCounter.d.ts.map +1 -1
  17. package/dist/components/ResourceCounter.js +106 -115
  18. package/dist/components/Toast.d.ts +13 -6
  19. package/dist/components/Toast.d.ts.map +1 -1
  20. package/dist/components/Toast.js +10 -5
  21. package/dist/components/board/HexGrid.js +6 -6
  22. package/dist/components/board/target-layer.d.ts +18 -2
  23. package/dist/components/board/target-layer.d.ts.map +1 -1
  24. package/dist/components/board/target-layer.js +20 -3
  25. package/dist/components/index.d.ts +3 -4
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +3 -4
  28. package/dist/components/surfaces/InboxSurface.d.ts.map +1 -1
  29. package/dist/components/surfaces/InboxSurface.js +2 -6
  30. package/dist/components/surfaces/PlayerCardsSurface.js +2 -2
  31. package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts +7 -0
  32. package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts.map +1 -0
  33. package/dist/components/surfaces/internal/CardZoneRoutedForm.js +9 -0
  34. package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts.map +1 -1
  35. package/dist/components/surfaces/internal/DefaultInteractionButton.js +5 -8
  36. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts +2 -2
  37. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts.map +1 -1
  38. package/dist/components/surfaces/internal/useCardZoneInteractions.js +19 -43
  39. package/dist/context/InteractionDraftContext.d.ts +11 -2
  40. package/dist/context/InteractionDraftContext.d.ts.map +1 -1
  41. package/dist/context/InteractionDraftContext.js +41 -4
  42. package/dist/defaults/components.d.ts +0 -5
  43. package/dist/defaults/components.d.ts.map +1 -1
  44. package/dist/defaults/components.js +7 -11
  45. package/dist/hooks/useBoardInteractions.d.ts +35 -12
  46. package/dist/hooks/useBoardInteractions.d.ts.map +1 -1
  47. package/dist/hooks/useBoardInteractions.js +186 -82
  48. package/dist/hooks/useInteractionHandle.d.ts +1 -1
  49. package/dist/hooks/useInteractionHandle.d.ts.map +1 -1
  50. package/dist/hooks/useInteractionHandle.js +12 -27
  51. package/dist/index.d.ts +11 -17
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +5 -14
  54. package/dist/primitives/board.d.ts +53 -3
  55. package/dist/primitives/board.d.ts.map +1 -1
  56. package/dist/primitives/board.js +65 -41
  57. package/dist/primitives/dialog-lifecycle.d.ts +17 -0
  58. package/dist/primitives/dialog-lifecycle.d.ts.map +1 -0
  59. package/dist/primitives/dialog-lifecycle.js +24 -0
  60. package/dist/primitives/dice.d.ts +31 -0
  61. package/dist/primitives/dice.d.ts.map +1 -0
  62. package/dist/primitives/dice.js +33 -0
  63. package/dist/primitives/game.d.ts +55 -0
  64. package/dist/primitives/game.d.ts.map +1 -0
  65. package/dist/primitives/game.js +101 -0
  66. package/dist/primitives/index.d.ts +7 -4
  67. package/dist/primitives/index.d.ts.map +1 -1
  68. package/dist/primitives/index.js +7 -4
  69. package/dist/primitives/interaction-form-binding.d.ts +12 -0
  70. package/dist/primitives/interaction-form-binding.d.ts.map +1 -0
  71. package/dist/primitives/interaction-form-binding.js +14 -0
  72. package/dist/primitives/interaction-submit.d.ts +23 -0
  73. package/dist/primitives/interaction-submit.d.ts.map +1 -0
  74. package/dist/primitives/interaction-submit.js +41 -0
  75. package/dist/primitives/interaction.d.ts +76 -6
  76. package/dist/primitives/interaction.d.ts.map +1 -1
  77. package/dist/primitives/interaction.js +210 -26
  78. package/dist/primitives/player-roster.d.ts +2 -1
  79. package/dist/primitives/player-roster.d.ts.map +1 -1
  80. package/dist/primitives/prompt.d.ts +36 -11
  81. package/dist/primitives/prompt.d.ts.map +1 -1
  82. package/dist/primitives/prompt.js +29 -17
  83. package/dist/primitives/ui.d.ts +9 -0
  84. package/dist/primitives/ui.d.ts.map +1 -0
  85. package/dist/primitives/ui.js +7 -0
  86. package/dist/primitives/zone.d.ts +111 -5
  87. package/dist/primitives/zone.d.ts.map +1 -1
  88. package/dist/primitives/zone.js +349 -9
  89. package/dist/reducer.d.ts +2 -14
  90. package/dist/reducer.d.ts.map +1 -1
  91. package/dist/reducer.js +1 -14
  92. package/dist/runtime/createPluginRuntimeAPI.js +1 -1
  93. package/dist/types/hex-color.d.ts +7 -0
  94. package/dist/types/hex-color.d.ts.map +1 -0
  95. package/dist/types/hex-color.js +13 -0
  96. package/dist/types/player-state.d.ts +28 -14
  97. package/dist/types/player-state.d.ts.map +1 -1
  98. package/dist/types/plugin-state.d.ts +9 -3
  99. package/dist/types/plugin-state.d.ts.map +1 -1
  100. package/dist/ui-contract.d.ts +119 -14
  101. package/dist/ui-contract.d.ts.map +1 -1
  102. package/dist/ui-contract.js +4 -3
  103. package/dist/ui-sdk.d.ts +1637 -1245
  104. package/dist/utils/interaction-inputs.d.ts +8 -5
  105. package/dist/utils/interaction-inputs.d.ts.map +1 -1
  106. package/dist/utils/interaction-inputs.js +82 -14
  107. package/dist/utils/interaction-router.d.ts +31 -0
  108. package/dist/utils/interaction-router.d.ts.map +1 -0
  109. package/dist/utils/interaction-router.js +114 -0
  110. package/package.json +2 -1
  111. package/src/components/ActionButton.tsx +2 -1
  112. package/src/components/Card.tsx +1 -1
  113. package/src/components/DiceRoller.tsx +13 -22
  114. package/src/components/ErrorBoundary.test.tsx +19 -0
  115. package/src/components/ErrorBoundary.tsx +113 -24
  116. package/src/components/InteractionForm.test.tsx +24 -0
  117. package/src/components/InteractionForm.tsx +48 -23
  118. package/src/components/PrimaryActionButton.tsx +19 -5
  119. package/src/components/ResourceCounter.test.tsx +13 -13
  120. package/src/components/ResourceCounter.tsx +238 -244
  121. package/src/components/Toast.tsx +23 -10
  122. package/src/components/__fixtures__/ResourceCounter.fixture.tsx +70 -169
  123. package/src/components/board/HexGrid.tsx +6 -6
  124. package/src/components/board/target-layer.ts +44 -5
  125. package/src/components/index.ts +17 -10
  126. package/src/components/surfaces/InboxSurface.tsx +7 -5
  127. package/src/components/surfaces/PlayerCardsSurface.tsx +6 -6
  128. package/src/components/surfaces/internal/CardZoneRoutedForm.tsx +35 -0
  129. package/src/components/surfaces/internal/DefaultInteractionButton.tsx +17 -7
  130. package/src/components/surfaces/internal/useCardZoneInteractions.ts +25 -67
  131. package/src/context/InteractionDraftContext.tsx +51 -5
  132. package/src/defaults/components.tsx +12 -50
  133. package/src/defaults/defaults.test.tsx +1 -50
  134. package/src/hooks/useBoardInteractions.test.tsx +240 -17
  135. package/src/hooks/useBoardInteractions.ts +330 -105
  136. package/src/hooks/useInteractionHandle.ts +23 -28
  137. package/src/index.test.ts +60 -40
  138. package/src/index.ts +30 -36
  139. package/src/primitives/board.test.tsx +73 -0
  140. package/src/primitives/board.tsx +191 -40
  141. package/src/primitives/dialog-lifecycle.ts +58 -0
  142. package/src/primitives/dice.test.tsx +47 -0
  143. package/src/primitives/dice.tsx +79 -0
  144. package/src/primitives/game.test.tsx +98 -0
  145. package/src/primitives/game.tsx +213 -0
  146. package/src/primitives/index.ts +84 -0
  147. package/src/primitives/interaction-form-binding.tsx +56 -0
  148. package/src/primitives/interaction-submit.ts +90 -0
  149. package/src/primitives/interaction.test.tsx +396 -0
  150. package/src/primitives/interaction.tsx +451 -31
  151. package/src/primitives/player-roster.tsx +2 -1
  152. package/src/primitives/prompt.test.tsx +94 -3
  153. package/src/primitives/prompt.tsx +87 -48
  154. package/src/primitives/ui.test.tsx +131 -0
  155. package/src/primitives/ui.tsx +13 -0
  156. package/src/primitives/zone.test.tsx +305 -0
  157. package/src/primitives/zone.tsx +660 -12
  158. package/src/reducer.ts +7 -20
  159. package/src/runtime/createPluginRuntimeAPI.ts +1 -1
  160. package/src/types/hex-color.ts +20 -0
  161. package/src/types/player-state.ts +36 -18
  162. package/src/types/plugin-state.ts +10 -3
  163. package/src/ui-contract.ts +253 -21
  164. package/src/utils/interaction-inputs.test.ts +400 -0
  165. package/src/utils/interaction-inputs.ts +113 -11
  166. package/src/utils/interaction-router.ts +200 -0
  167. package/type-stubs/manifest-contract.d.ts +42 -0
  168. package/type-stubs/manifest-contract.d.ts.map +1 -0
  169. package/type-stubs/manifest-contract.js +72 -0
  170. package/type-stubs/ui-contract.d.ts +5 -0
  171. package/type-stubs/ui-contract.d.ts.map +1 -0
  172. package/type-stubs/ui-contract.js +1 -0
@@ -1,17 +1,13 @@
1
- /**
2
- * ResourceCounter component fixtures
3
- * Demonstrates resource display in various configurations
4
- */
5
1
  import React, { useState } from "react";
6
2
  import {
7
3
  Coins,
8
- TreePine,
9
- Gem,
10
- Zap,
11
- Droplet,
12
- Mountain,
13
4
  Cpu,
5
+ Droplet,
6
+ Gem,
14
7
  Leaf,
8
+ Mountain,
9
+ TreePine,
10
+ Zap,
15
11
  } from "lucide-react";
16
12
  import {
17
13
  ResourceCounter,
@@ -20,77 +16,71 @@ import {
20
16
 
21
17
  function Container({ children }: { children: React.ReactNode }) {
22
18
  return (
23
- <div className="min-h-screen bg-gradient-to-br from-slate-800 to-slate-900 p-8">
24
- <div className="max-w-2xl mx-auto space-y-6">{children}</div>
19
+ <div className="min-h-screen bg-slate-950 p-8 text-white">
20
+ <div className="mx-auto max-w-2xl space-y-6">{children}</div>
25
21
  </div>
26
22
  );
27
23
  }
28
24
 
29
- // Fantasy game resources
30
25
  const fantasyResources: ResourceDisplayConfig[] = [
31
- {
32
- type: "gold",
33
- label: "Gold",
34
- icon: Coins,
35
- iconColor: "text-yellow-400",
36
- bgColor: "bg-yellow-900/30",
37
- },
38
- {
39
- type: "wood",
40
- label: "Wood",
41
- icon: TreePine,
42
- iconColor: "text-amber-600",
43
- bgColor: "bg-amber-900/30",
44
- },
45
- {
46
- type: "gems",
47
- label: "Gems",
48
- icon: Gem,
49
- iconColor: "text-purple-400",
50
- bgColor: "bg-purple-900/30",
51
- },
26
+ { type: "gold", label: "Gold", icon: Coins },
27
+ { type: "wood", label: "Wood", icon: TreePine },
28
+ { type: "gems", label: "Gems", icon: Gem },
52
29
  ];
53
30
 
54
- // Sci-fi game resources (Catan-style)
55
31
  const scifiResources: ResourceDisplayConfig[] = [
56
- {
57
- type: "minerals",
58
- label: "Minerals",
59
- icon: Mountain,
60
- iconColor: "text-amber-600",
61
- bgColor: "bg-amber-900/30",
62
- },
63
- {
64
- type: "energy",
65
- label: "Energy",
66
- icon: Zap,
67
- iconColor: "text-blue-400",
68
- bgColor: "bg-blue-900/30",
69
- },
70
- {
71
- type: "water",
72
- label: "Water",
73
- icon: Droplet,
74
- iconColor: "text-cyan-400",
75
- bgColor: "bg-cyan-900/30",
76
- },
77
- {
78
- type: "tech",
79
- label: "Tech Parts",
80
- icon: Cpu,
81
- iconColor: "text-purple-400",
82
- bgColor: "bg-purple-900/30",
83
- },
84
- {
85
- type: "bio",
86
- label: "Bio-matter",
87
- icon: Leaf,
88
- iconColor: "text-green-400",
89
- bgColor: "bg-green-900/30",
90
- },
32
+ { type: "minerals", label: "Minerals", icon: Mountain },
33
+ { type: "energy", label: "Energy", icon: Zap },
34
+ { type: "water", label: "Water", icon: Droplet },
35
+ { type: "tech", label: "Tech Parts", icon: Cpu },
36
+ { type: "bio", label: "Bio-matter", icon: Leaf },
91
37
  ];
92
38
 
93
- // Interactive demo
39
+ const iconClassByResource: Record<string, string> = {
40
+ gold: "text-yellow-300",
41
+ wood: "text-amber-500",
42
+ gems: "text-fuchsia-300",
43
+ minerals: "text-stone-300",
44
+ energy: "text-sky-300",
45
+ water: "text-cyan-300",
46
+ tech: "text-violet-300",
47
+ bio: "text-emerald-300",
48
+ };
49
+
50
+ function DemoResourceCounter({
51
+ resources,
52
+ counts,
53
+ showZero = true,
54
+ onResourceClick,
55
+ }: {
56
+ resources: ResourceDisplayConfig[];
57
+ counts: Record<string, number>;
58
+ showZero?: boolean;
59
+ onResourceClick?: (type: string) => void;
60
+ }) {
61
+ return (
62
+ <ResourceCounter.Root
63
+ resources={resources}
64
+ counts={counts}
65
+ zero={showZero ? "show" : "hide"}
66
+ onResourceClick={onResourceClick}
67
+ className="flex flex-wrap gap-3"
68
+ >
69
+ <ResourceCounter.Item className="inline-flex items-center gap-2 rounded-md border border-slate-700 bg-slate-900 px-3 py-2 font-bold transition data-[interactive=true]:cursor-pointer data-[interactive=true]:border-slate-600 data-[interactive=true]:shadow-sm data-[interactive=true]:hover:border-slate-300 data-[resource-zero=true]:opacity-50">
70
+ {(resource) => (
71
+ <>
72
+ <ResourceCounter.Icon
73
+ className={`h-5 w-5 ${iconClassByResource[resource.type] ?? ""}`}
74
+ />
75
+ <ResourceCounter.Count className="tabular-nums" />
76
+ <ResourceCounter.Label className="text-xs font-medium text-slate-400" />
77
+ </>
78
+ )}
79
+ </ResourceCounter.Item>
80
+ </ResourceCounter.Root>
81
+ );
82
+ }
83
+
94
84
  function InteractiveDemo() {
95
85
  const [counts, setCounts] = useState<Record<string, number>>({
96
86
  gold: 5,
@@ -98,27 +88,16 @@ function InteractiveDemo() {
98
88
  gems: 1,
99
89
  });
100
90
 
101
- const handleClick = (type: string) => {
102
- setCounts((prev) => ({
103
- ...prev,
104
- [type]: (prev[type] ?? 0) + 1,
105
- }));
106
- };
107
-
108
91
  return (
109
92
  <Container>
110
- <h2 className="text-xl font-bold text-white mb-4">
111
- Interactive (Click to Add)
112
- </h2>
113
- <ResourceCounter
93
+ <h2 className="text-xl font-bold">Interactive Author Markup</h2>
94
+ <DemoResourceCounter
114
95
  resources={fantasyResources}
115
96
  counts={counts}
116
- onResourceClick={handleClick}
117
- size="lg"
97
+ onResourceClick={(type) =>
98
+ setCounts((prev) => ({ ...prev, [type]: (prev[type] ?? 0) + 1 }))
99
+ }
118
100
  />
119
- <p className="text-slate-400 text-sm mt-4">
120
- Click any resource to add +1
121
- </p>
122
101
  </Container>
123
102
  );
124
103
  }
@@ -126,81 +105,18 @@ function InteractiveDemo() {
126
105
  export default {
127
106
  default: (
128
107
  <Container>
129
- <h2 className="text-xl font-bold text-white mb-4">
130
- Default (Row Layout)
131
- </h2>
132
- <ResourceCounter
108
+ <h2 className="text-xl font-bold">Author Styled Resources</h2>
109
+ <DemoResourceCounter
133
110
  resources={fantasyResources}
134
111
  counts={{ gold: 5, wood: 3, gems: 1 }}
135
112
  />
136
113
  </Container>
137
114
  ),
138
115
 
139
- gridLayout: (
116
+ hiddenZeros: (
140
117
  <Container>
141
- <h2 className="text-xl font-bold text-white mb-4">
142
- Grid Layout (5 Resources)
143
- </h2>
144
- <ResourceCounter
145
- resources={scifiResources}
146
- counts={{ minerals: 4, energy: 2, water: 3, tech: 1, bio: 5 }}
147
- layout="grid"
148
- columns={5}
149
- />
150
- </Container>
151
- ),
152
-
153
- compactLayout: (
154
- <Container>
155
- <h2 className="text-xl font-bold text-white mb-4">Compact Layout</h2>
156
- <ResourceCounter
157
- resources={fantasyResources}
158
- counts={{ gold: 10, wood: 5, gems: 2 }}
159
- layout="compact"
160
- size="sm"
161
- />
162
- </Container>
163
- ),
164
-
165
- sizes: (
166
- <Container>
167
- <h2 className="text-xl font-bold text-white mb-4">Size Variants</h2>
168
- <div className="space-y-6">
169
- <div>
170
- <p className="text-slate-400 text-sm mb-2">Small</p>
171
- <ResourceCounter
172
- resources={fantasyResources}
173
- counts={{ gold: 5, wood: 3, gems: 1 }}
174
- size="sm"
175
- />
176
- </div>
177
- <div>
178
- <p className="text-slate-400 text-sm mb-2">Medium (Default)</p>
179
- <ResourceCounter
180
- resources={fantasyResources}
181
- counts={{ gold: 5, wood: 3, gems: 1 }}
182
- size="md"
183
- />
184
- </div>
185
- <div>
186
- <p className="text-slate-400 text-sm mb-2">Large</p>
187
- <ResourceCounter
188
- resources={fantasyResources}
189
- counts={{ gold: 5, wood: 3, gems: 1 }}
190
- size="lg"
191
- />
192
- </div>
193
- </div>
194
- </Container>
195
- ),
196
-
197
- hideZeroValues: (
198
- <Container>
199
- <h2 className="text-xl font-bold text-white mb-4">Hide Zero Values</h2>
200
- <p className="text-slate-400 text-sm mb-4">
201
- Only showing resources with count {">"} 0
202
- </p>
203
- <ResourceCounter
118
+ <h2 className="text-xl font-bold">Filtered Resources</h2>
119
+ <DemoResourceCounter
204
120
  resources={scifiResources}
205
121
  counts={{ minerals: 4, energy: 0, water: 3, tech: 0, bio: 5 }}
206
122
  showZero={false}
@@ -209,19 +125,4 @@ export default {
209
125
  ),
210
126
 
211
127
  interactive: <InteractiveDemo />,
212
-
213
- sciFiTheme: (
214
- <Container>
215
- <h2 className="text-xl font-bold text-white mb-4">
216
- Sci-Fi Resources (Catan Style)
217
- </h2>
218
- <ResourceCounter
219
- resources={scifiResources}
220
- counts={{ minerals: 3, energy: 2, water: 1, tech: 4, bio: 2 }}
221
- layout="grid"
222
- columns={5}
223
- size="md"
224
- />
225
- </Container>
226
- ),
227
128
  };
@@ -1053,7 +1053,7 @@ function HexGridImpl(
1053
1053
  onClick={
1054
1054
  isSelectable
1055
1055
  ? () => {
1056
- void interactiveSpaces.selectTargetId?.(space.id);
1056
+ void state.select?.();
1057
1057
  }
1058
1058
  : undefined
1059
1059
  }
@@ -1062,7 +1062,7 @@ function HexGridImpl(
1062
1062
  event,
1063
1063
  isSelectable
1064
1064
  ? () => {
1065
- void interactiveSpaces.selectTargetId?.(space.id);
1065
+ void state.select?.();
1066
1066
  }
1067
1067
  : undefined,
1068
1068
  )
@@ -1151,7 +1151,7 @@ function HexGridImpl(
1151
1151
  onClick={
1152
1152
  isSelectable
1153
1153
  ? () => {
1154
- void interactiveEdges.selectTargetId?.(edge.id);
1154
+ void state.select?.();
1155
1155
  }
1156
1156
  : undefined
1157
1157
  }
@@ -1160,7 +1160,7 @@ function HexGridImpl(
1160
1160
  event,
1161
1161
  isSelectable
1162
1162
  ? () => {
1163
- void interactiveEdges.selectTargetId?.(edge.id);
1163
+ void state.select?.();
1164
1164
  }
1165
1165
  : undefined,
1166
1166
  )
@@ -1226,7 +1226,7 @@ function HexGridImpl(
1226
1226
  onClick={
1227
1227
  isSelectable
1228
1228
  ? () => {
1229
- void interactiveVertices.selectTargetId?.(vertex.id);
1229
+ void state.select?.();
1230
1230
  }
1231
1231
  : undefined
1232
1232
  }
@@ -1235,7 +1235,7 @@ function HexGridImpl(
1235
1235
  event,
1236
1236
  isSelectable
1237
1237
  ? () => {
1238
- void interactiveVertices.selectTargetId?.(vertex.id);
1238
+ void state.select?.();
1239
1239
  }
1240
1240
  : undefined,
1241
1241
  )
@@ -1,10 +1,28 @@
1
+ import type { BoardTargetKind } from "../../utils/interaction-inputs.js";
2
+
3
+ export interface InteractiveTargetState {
4
+ kind?: BoardTargetKind;
5
+ id: string;
6
+ eligible: boolean;
7
+ selectable: boolean;
8
+ hovered: boolean;
9
+ interactionKey?: string;
10
+ interactionId?: string;
11
+ inputKey?: string;
12
+ pending: boolean;
13
+ conflict: boolean;
14
+ unavailableReason?: string;
15
+ select?: () => unknown | Promise<unknown>;
16
+ }
17
+
1
18
  export interface InteractiveTargetLayer {
2
19
  enabled?: boolean;
3
20
  eligible?: ReadonlySet<string>;
4
- selectTargetId?: (targetId: string) => void | Promise<void>;
21
+ selectTargetId?: (targetId: string) => unknown | Promise<unknown>;
22
+ targetState?: (targetId: string) => Partial<InteractiveTargetState>;
5
23
  }
6
24
 
7
- export interface InteractiveTargetRenderState {
25
+ export interface InteractiveTargetRenderState extends InteractiveTargetState {
8
26
  isEnabled: boolean;
9
27
  isEligible: boolean;
10
28
  isHovered: boolean;
@@ -15,9 +33,26 @@ export function interactiveTargetRenderState(
15
33
  targetId: string,
16
34
  isHovered: boolean,
17
35
  ): InteractiveTargetRenderState {
36
+ const enabled = layer.enabled !== false;
37
+ const eligible = layer.eligible?.has(targetId) ?? true;
38
+ const extra = layer.targetState?.(targetId) ?? {};
39
+ const selectable =
40
+ extra.selectable ?? (enabled && eligible && !!layer.selectTargetId);
18
41
  return {
19
- isEnabled: layer.enabled !== false,
20
- isEligible: layer.eligible?.has(targetId) ?? true,
42
+ id: targetId,
43
+ ...extra,
44
+ eligible: extra.eligible ?? eligible,
45
+ selectable,
46
+ hovered: isHovered,
47
+ pending: extra.pending ?? false,
48
+ conflict: extra.conflict ?? false,
49
+ select:
50
+ extra.select ??
51
+ (layer.selectTargetId
52
+ ? () => layer.selectTargetId?.(targetId)
53
+ : undefined),
54
+ isEnabled: enabled,
55
+ isEligible: extra.eligible ?? eligible,
21
56
  isHovered,
22
57
  };
23
58
  }
@@ -26,5 +61,9 @@ export function isInteractiveTargetSelectable(
26
61
  layer: InteractiveTargetLayer,
27
62
  state: InteractiveTargetRenderState,
28
63
  ): boolean {
29
- return state.isEnabled && state.isEligible && !!layer.selectTargetId;
64
+ return (
65
+ state.isEnabled &&
66
+ state.selectable &&
67
+ !!(state.select ?? layer.selectTargetId)
68
+ );
30
69
  }
@@ -16,9 +16,12 @@ export { PlayArea, type PlayAreaProps } from "./PlayArea.js";
16
16
  // Other UI components
17
17
  export { GameSkeleton, type GameSkeletonProps } from "./GameSkeleton.js";
18
18
  export {
19
+ Toast,
20
+ ToastActions,
19
21
  ToastProvider,
20
- useToast,
21
- type Toast,
22
+ type ToastActionsProps,
23
+ type ToastActionsValue,
24
+ type ToastNotification,
22
25
  type ToastType,
23
26
  } from "./Toast.js";
24
27
  export { ErrorBoundary, type ErrorBoundaryProps } from "./ErrorBoundary.js";
@@ -51,8 +54,20 @@ export {
51
54
  // Game UI primitives (SDK v0.1.0+)
52
55
  export {
53
56
  ResourceCounter,
57
+ ResourceCounterCount,
58
+ ResourceCounterIcon,
59
+ ResourceCounterItem,
60
+ ResourceCounterLabel,
61
+ ResourceCounterRoot,
62
+ createResourceCounter,
63
+ type BoundResourceCounterRootProps,
64
+ type ResourceCounterComponents,
65
+ type ResourceCounterItemState,
66
+ type ResourceCounterPartProps,
54
67
  type ResourceCounterProps,
68
+ type ResourceCounterRootProps,
55
69
  type ResourceDisplayConfig,
70
+ type ResourceIconProps,
56
71
  } from "./ResourceCounter.js";
57
72
  export {
58
73
  CostDisplay,
@@ -69,12 +84,8 @@ export {
69
84
  } from "./PrimaryActionButton.js";
70
85
  export {
71
86
  InteractionForm,
72
- InteractionField,
73
87
  defaultFormInputs,
74
88
  hasDefaultInteractionFormFields,
75
- type InteractionFieldProps,
76
- type InteractionFieldRenderMap,
77
- type InteractionFieldRenderProps,
78
89
  type InteractionFormProps,
79
90
  } from "./InteractionForm.js";
80
91
  export {
@@ -91,10 +102,6 @@ export {
91
102
  type GameEndDisplayProps,
92
103
  type PlayerScore,
93
104
  } from "./GameEndDisplay.js";
94
- export {
95
- PromptDialogHost,
96
- type PromptDialogHostProps,
97
- } from "./PromptDialogHost.js";
98
105
 
99
106
  // Board primitives (SDK v0.2.0+)
100
107
  export {
@@ -12,6 +12,7 @@ import type {
12
12
  } from "../../types/plugin-state.js";
13
13
  import { interactionInputKeys } from "../../utils/interaction-inputs.js";
14
14
  import { interactionLabel } from "../../utils/interaction-labels.js";
15
+ import { submitInteractionParams } from "../../primitives/interaction-submit.js";
15
16
  import { DefaultInteractionButton } from "./internal/DefaultInteractionButton.js";
16
17
  import { ThemedButton } from "../ThemedButton.js";
17
18
  import type {
@@ -225,11 +226,12 @@ function PromptOptionsCard<
225
226
  if (disabled || !inputKey) return;
226
227
  setPendingOptionId(option.id);
227
228
  try {
228
- await handle.submit({ [inputKey]: option.id } as Params);
229
- } catch {
230
- // Submission errors flow through the runtime's
231
- // notification channel; we swallow here so the option
232
- // buttons re-enable on failure.
229
+ await submitInteractionParams(
230
+ handle,
231
+ { [inputKey]: option.id } as Params,
232
+ {},
233
+ { unhandledError: "ignore" },
234
+ );
233
235
  } finally {
234
236
  setPendingOptionId(null);
235
237
  }
@@ -10,7 +10,7 @@ import {
10
10
  useCardZoneInteractions,
11
11
  type CardZoneInteractionContext,
12
12
  } from "./internal/useCardZoneInteractions.js";
13
- import { CardZoneFollowUpForm } from "./internal/CardZoneFollowUpForm.js";
13
+ import { CardZoneRoutedForm } from "./internal/CardZoneRoutedForm.js";
14
14
  import type { InteractionParamsByKeyShape } from "./types.js";
15
15
 
16
16
  export type PlayerCardsLayout<ZoneKey extends string = string> =
@@ -511,12 +511,12 @@ function PlayerCardsFan<
511
511
  );
512
512
  }}
513
513
  />
514
- {zone.followUp ? (
514
+ {zone.routedInteraction ? (
515
515
  <Fragment>
516
- <CardZoneFollowUpForm<I, ParamsByKey>
517
- key={zone.followUp.interactionKey}
518
- descriptor={zone.followUp}
519
- onDone={() => zone.setFollowUp(null)}
516
+ <CardZoneRoutedForm<I, ParamsByKey>
517
+ key={zone.routedInteraction.interactionKey}
518
+ descriptor={zone.routedInteraction}
519
+ onDone={() => zone.setRoutedInteraction(null)}
520
520
  />
521
521
  </Fragment>
522
522
  ) : null}
@@ -0,0 +1,35 @@
1
+ import { InteractionForm } from "../../InteractionForm.js";
2
+ import { useInteractionHandle } from "../../../hooks/useInteractionHandle.js";
3
+ import type { InteractionDescriptor } from "../../../types/plugin-state.js";
4
+ import type {
5
+ InteractionDefaultedKeysOf,
6
+ InteractionParamsByKeyShape,
7
+ InteractionParamsOf,
8
+ } from "../types.js";
9
+
10
+ export function CardZoneRoutedForm<
11
+ I extends string,
12
+ ParamsByKey extends Partial<InteractionParamsByKeyShape> = {},
13
+ >({
14
+ descriptor,
15
+ onDone,
16
+ }: {
17
+ descriptor: InteractionDescriptor<I>;
18
+ onDone: () => void;
19
+ }) {
20
+ const handle = useInteractionHandle<
21
+ InteractionParamsOf<ParamsByKey, I>,
22
+ InteractionDefaultedKeysOf<ParamsByKey, I>
23
+ >(descriptor);
24
+ return (
25
+ <InteractionForm
26
+ descriptor={descriptor}
27
+ handle={handle}
28
+ hiddenFields={[
29
+ "cardId" as keyof InteractionParamsOf<ParamsByKey, I> & string,
30
+ ]}
31
+ onCancel={onDone}
32
+ onSubmitSuccess={onDone}
33
+ />
34
+ );
35
+ }
@@ -10,6 +10,10 @@ import {
10
10
  InteractionForm,
11
11
  hasDefaultInteractionFormFields,
12
12
  } from "../../InteractionForm.js";
13
+ import {
14
+ submitInteractionDraft,
15
+ submitInteractionParams,
16
+ } from "../../../primitives/interaction-submit.js";
13
17
  import { interactionLabel } from "../../../utils/interaction-labels.js";
14
18
  import { ThemedButton } from "../../ThemedButton.js";
15
19
 
@@ -160,15 +164,21 @@ export function DefaultInteractionButton<
160
164
  setPending(true);
161
165
  try {
162
166
  if (params !== undefined) {
163
- await handle.submit(params as Params);
167
+ await submitInteractionParams(
168
+ handle,
169
+ params as Params,
170
+ {},
171
+ {
172
+ unhandledError: "ignore",
173
+ },
174
+ );
164
175
  } else {
165
- await handle.submitDraft();
176
+ await submitInteractionDraft(
177
+ handle,
178
+ {},
179
+ { unhandledError: "ignore" },
180
+ );
166
181
  }
167
- } catch {
168
- // Descriptor-level availability is authoritative; submission
169
- // errors flow through the runtime's error channel. We swallow
170
- // here so consumers without an error boundary don't see an
171
- // unhandled rejection warning.
172
182
  } finally {
173
183
  setPending(false);
174
184
  }