@dreamboard-games/cli 0.1.30-alpha.1 → 0.1.30-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -22
- package/dist/{chunk-C6UAT6EH.js → chunk-N7XPNNUI.js} +9 -12
- package/dist/chunk-N7XPNNUI.js.map +1 -0
- package/dist/chunk-SEGVTWSK.js +44 -0
- package/dist/{chunk-RS7UXJZV.js → chunk-TAQKH67O.js} +21300 -35881
- package/dist/chunk-TAQKH67O.js.map +1 -0
- package/dist/{global-config-AGFBDFYD.js → global-config-S4ZIPECE.js} +3 -3
- package/dist/index.js +415 -37
- package/dist/index.js.map +1 -1
- package/dist/internal.js +3 -4
- package/dist/{agent-verifier/keychain-backend-TNOPQV3Z.mjs → keychain-backend-HDF4TZDL.js} +2 -1
- package/dist/{agent-verifier/prompt-3BAINGAQ.mjs → prompt-NDV3AE5L.js} +2 -1
- package/package.json +6 -6
- package/skills/dreamboard/references/building-your-first-game.md +510 -0
- package/skills/dreamboard/references/cli.md +104 -0
- package/skills/dreamboard/references/game-interface.md +548 -0
- package/skills/dreamboard/references/manifest-authoring.md +597 -0
- package/skills/dreamboard/references/quickstart.md +66 -0
- package/skills/dreamboard/references/reducer.md +864 -0
- package/skills/dreamboard/references/rule-authoring.md +147 -0
- package/skills/dreamboard/references/testing.md +249 -0
- package/skills/dreamboard/scripts/events-extract.mjs +218 -0
- package/dist/agent-verifier/agent-workspace-verifier.mjs +0 -227
- package/dist/agent-verifier/chunk-2E5P5NWG.mjs +0 -835
- package/dist/agent-verifier/chunk-2GBBP27W.mjs +0 -301
- package/dist/agent-verifier/chunk-2QMNAVV4.mjs +0 -14522
- package/dist/agent-verifier/chunk-2SZHMP6F.mjs +0 -264
- package/dist/agent-verifier/chunk-4WD3YU2E.mjs +0 -166
- package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
- package/dist/agent-verifier/chunk-6A5HRJMQ.mjs +0 -3174
- package/dist/agent-verifier/chunk-6UUJEYDV.mjs +0 -213
- package/dist/agent-verifier/chunk-7653FPGJ.mjs +0 -381
- package/dist/agent-verifier/chunk-7E65UQLY.mjs +0 -38
- package/dist/agent-verifier/chunk-BVVNBJM4.mjs +0 -221
- package/dist/agent-verifier/chunk-CEDUHGNH.mjs +0 -74
- package/dist/agent-verifier/chunk-CEQ2VJWN.mjs +0 -149
- package/dist/agent-verifier/chunk-CFU5EWIC.mjs +0 -69
- package/dist/agent-verifier/chunk-CJEEA6NJ.mjs +0 -730
- package/dist/agent-verifier/chunk-EIQWDQWJ.mjs +0 -186
- package/dist/agent-verifier/chunk-EOQIV6PS.mjs +0 -649
- package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
- package/dist/agent-verifier/chunk-HJFQDSTU.mjs +0 -225
- package/dist/agent-verifier/chunk-JH22JNYD.mjs +0 -1681
- package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
- package/dist/agent-verifier/chunk-LM3OZLZG.mjs +0 -48
- package/dist/agent-verifier/chunk-MINCYHXN.mjs +0 -106
- package/dist/agent-verifier/chunk-MRCUP5SW.mjs +0 -128
- package/dist/agent-verifier/chunk-RBDDIIPM.mjs +0 -19
- package/dist/agent-verifier/chunk-SHUMAVAP.mjs +0 -59
- package/dist/agent-verifier/chunk-SYPLYRGB.mjs +0 -2812
- package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
- package/dist/agent-verifier/chunk-VYJTHSYR.mjs +0 -44
- package/dist/agent-verifier/chunk-XYDL7GY6.mjs +0 -10
- package/dist/agent-verifier/compile-5QSPIOUT.mjs +0 -313
- package/dist/agent-verifier/global-config-WX3ZZIVU.mjs +0 -17
- package/dist/agent-verifier/local-files-MTPLP62S.mjs +0 -46
- package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
- package/dist/agent-verifier/materialize-workspace-FKALAE2T.mjs +0 -90
- package/dist/agent-verifier/project-state-7GR6BQTQ.mjs +0 -32
- package/dist/agent-verifier/reducer-bundle-preflight-C73LEXI2.mjs +0 -23
- package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
- package/dist/agent-verifier/reducer-native-test-harness-GMWBUISX.mjs +0 -53
- package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
- package/dist/agent-verifier/sync-3DUQH32H.mjs +0 -594
- package/dist/agent-verifier/test-P4U5INTD.mjs +0 -356
- package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
- package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
- package/dist/agent-verifier/workspace-dependencies-HZ6VVS4G.mjs +0 -14
- package/dist/chunk-2H7UOFLK.js +0 -11
- package/dist/chunk-7FOO4AJI.js +0 -50
- package/dist/chunk-7FOO4AJI.js.map +0 -1
- package/dist/chunk-C6UAT6EH.js.map +0 -1
- package/dist/chunk-RS7UXJZV.js.map +0 -1
- package/dist/internal.d.ts +0 -311
- package/dist/keychain-backend-JHTXAKWC.js +0 -135
- package/dist/prompt-GMZABCJC.js +0 -756
- package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
- package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
- package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
- package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
- package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
- package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
- package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
- package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
- package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
- package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
- package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
- package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
- package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
- package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
- package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
- package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
- package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
- package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
- package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
- package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
- package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
- package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
- package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
- package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
- package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
- package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
- package/dist/testing-KLSV6CPJ.js +0 -674
- package/dist/testing-KLSV6CPJ.js.map +0 -1
- /package/dist/{chunk-2H7UOFLK.js.map → chunk-SEGVTWSK.js.map} +0 -0
- /package/dist/{global-config-AGFBDFYD.js.map → global-config-S4ZIPECE.js.map} +0 -0
- /package/dist/{keychain-backend-JHTXAKWC.js.map → keychain-backend-HDF4TZDL.js.map} +0 -0
- /package/dist/{prompt-GMZABCJC.js.map → prompt-NDV3AE5L.js.map} +0 -0
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
import { Toaster, toast } from "sonner";
|
|
3
|
-
import { AlertTriangle, Bell, Clock3, X } from "lucide-react";
|
|
4
|
-
import {
|
|
5
|
-
intentForVariant,
|
|
6
|
-
surfaceStyle,
|
|
7
|
-
useTheme,
|
|
8
|
-
type ButtonVariant,
|
|
9
|
-
type Theme,
|
|
10
|
-
} from "@dreamboard-games/sdk/ui";
|
|
11
|
-
import type { HostFeedback } from "../unified-session-store.js";
|
|
12
|
-
|
|
13
|
-
export interface HostFeedbackToasterProps {
|
|
14
|
-
feedback?: HostFeedback[];
|
|
15
|
-
onDismiss?: (feedbackId: string) => void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface FeedbackPresentation {
|
|
19
|
-
title: string;
|
|
20
|
-
description: string;
|
|
21
|
-
duration: number;
|
|
22
|
-
variant: Extract<ButtonVariant, "danger" | "warning" | "success" | "info">;
|
|
23
|
-
icon: typeof AlertTriangle;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function describeFeedback(item: HostFeedback): FeedbackPresentation {
|
|
27
|
-
switch (item.type) {
|
|
28
|
-
case "YOUR_TURN": {
|
|
29
|
-
const activePlayerCount = item.payload.activePlayers.length;
|
|
30
|
-
return {
|
|
31
|
-
title: "Your turn",
|
|
32
|
-
description:
|
|
33
|
-
activePlayerCount > 1
|
|
34
|
-
? "You can act with one of your controlled players."
|
|
35
|
-
: "You can act now.",
|
|
36
|
-
duration: 3500,
|
|
37
|
-
variant: "success",
|
|
38
|
-
icon: Bell,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
case "PROMPT_OPENED": {
|
|
42
|
-
const { targetPlayer, title } = item.payload;
|
|
43
|
-
return {
|
|
44
|
-
title: "Response needed",
|
|
45
|
-
description: targetPlayer
|
|
46
|
-
? `${title ?? "A prompt is waiting."} (${targetPlayer})`
|
|
47
|
-
: (title ?? "A prompt is waiting."),
|
|
48
|
-
duration: 5000,
|
|
49
|
-
variant: "warning",
|
|
50
|
-
icon: Clock3,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
case "ACTION_REJECTED": {
|
|
54
|
-
const reason = item.payload.targetPlayer
|
|
55
|
-
? `${item.payload.reason} (${item.payload.targetPlayer})`
|
|
56
|
-
: item.payload.reason;
|
|
57
|
-
return {
|
|
58
|
-
title: "Action rejected",
|
|
59
|
-
description: reason,
|
|
60
|
-
duration: 5000,
|
|
61
|
-
variant: "danger",
|
|
62
|
-
icon: AlertTriangle,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const exhaustive: never = item;
|
|
68
|
-
throw new Error(
|
|
69
|
-
`Unsupported host feedback item type: ${String((exhaustive as { type?: unknown }).type)}`,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface HostFeedbackToastBodyProps {
|
|
74
|
-
presentation: FeedbackPresentation;
|
|
75
|
-
theme: Theme;
|
|
76
|
-
onDismiss: () => void;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Inner toast body. Rendered through sonner's `toast.custom` so the
|
|
81
|
-
* surface is fully owned by the active {@link Theme} — no implicit
|
|
82
|
-
* `richColors` styling, no hardcoded `bg-*` Tailwind classes.
|
|
83
|
-
*/
|
|
84
|
-
function HostFeedbackToastBody({
|
|
85
|
-
presentation,
|
|
86
|
-
theme,
|
|
87
|
-
onDismiss,
|
|
88
|
-
}: HostFeedbackToastBodyProps) {
|
|
89
|
-
const intent = intentForVariant(theme, presentation.variant);
|
|
90
|
-
const Icon = presentation.icon;
|
|
91
|
-
return (
|
|
92
|
-
<div
|
|
93
|
-
role="status"
|
|
94
|
-
aria-live="polite"
|
|
95
|
-
style={{
|
|
96
|
-
...surfaceStyle(theme, { tone: "card", radius: "lg" }),
|
|
97
|
-
background: intent.soft,
|
|
98
|
-
color: intent.onSoft,
|
|
99
|
-
border: `1px solid ${intent.border}`,
|
|
100
|
-
boxShadow: theme.elevation.lifted,
|
|
101
|
-
display: "flex",
|
|
102
|
-
alignItems: "flex-start",
|
|
103
|
-
gap: theme.space[3],
|
|
104
|
-
padding: theme.space[3],
|
|
105
|
-
// Match sonner's default 356px so the toast lines up with
|
|
106
|
-
// sibling toasts that may live inside the same stack.
|
|
107
|
-
minWidth: 320,
|
|
108
|
-
maxWidth: 420,
|
|
109
|
-
fontFamily: theme.typography.fontFamily.body,
|
|
110
|
-
}}
|
|
111
|
-
>
|
|
112
|
-
<Icon
|
|
113
|
-
size={20}
|
|
114
|
-
strokeWidth={2.5}
|
|
115
|
-
aria-hidden="true"
|
|
116
|
-
style={{
|
|
117
|
-
flexShrink: 0,
|
|
118
|
-
marginTop: 2,
|
|
119
|
-
color: intent.solid,
|
|
120
|
-
}}
|
|
121
|
-
/>
|
|
122
|
-
<div style={{ flex: 1, minWidth: 0 }}>
|
|
123
|
-
<div
|
|
124
|
-
style={{
|
|
125
|
-
fontFamily: theme.typography.fontFamily.display,
|
|
126
|
-
fontSize: theme.typography.fontSize.md,
|
|
127
|
-
fontWeight: theme.typography.fontWeight.bold,
|
|
128
|
-
lineHeight: theme.typography.lineHeight.tight,
|
|
129
|
-
color: intent.onSoft,
|
|
130
|
-
}}
|
|
131
|
-
>
|
|
132
|
-
{presentation.title}
|
|
133
|
-
</div>
|
|
134
|
-
<div
|
|
135
|
-
style={{
|
|
136
|
-
marginTop: theme.space[1],
|
|
137
|
-
fontSize: theme.typography.fontSize.sm,
|
|
138
|
-
fontWeight: theme.typography.fontWeight.medium,
|
|
139
|
-
lineHeight: theme.typography.lineHeight.normal,
|
|
140
|
-
color: intent.onSoft,
|
|
141
|
-
opacity: 0.92,
|
|
142
|
-
wordBreak: "break-word",
|
|
143
|
-
}}
|
|
144
|
-
>
|
|
145
|
-
{presentation.description}
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
<button
|
|
149
|
-
type="button"
|
|
150
|
-
aria-label="Dismiss notification"
|
|
151
|
-
onClick={onDismiss}
|
|
152
|
-
style={{
|
|
153
|
-
flexShrink: 0,
|
|
154
|
-
width: 28,
|
|
155
|
-
height: 28,
|
|
156
|
-
display: "inline-flex",
|
|
157
|
-
alignItems: "center",
|
|
158
|
-
justifyContent: "center",
|
|
159
|
-
background: "transparent",
|
|
160
|
-
border: "none",
|
|
161
|
-
borderRadius: theme.radius.pill,
|
|
162
|
-
color: intent.onSoft,
|
|
163
|
-
cursor: "pointer",
|
|
164
|
-
opacity: 0.7,
|
|
165
|
-
transition: `opacity ${theme.motion.duration.fast} ${theme.motion.easing.out}`,
|
|
166
|
-
}}
|
|
167
|
-
onMouseEnter={(event) => {
|
|
168
|
-
event.currentTarget.style.opacity = "1";
|
|
169
|
-
}}
|
|
170
|
-
onMouseLeave={(event) => {
|
|
171
|
-
event.currentTarget.style.opacity = "0.7";
|
|
172
|
-
}}
|
|
173
|
-
>
|
|
174
|
-
<X size={16} aria-hidden="true" />
|
|
175
|
-
</button>
|
|
176
|
-
</div>
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Mounts a {@link Toaster} and dispatches host-feedback events as
|
|
182
|
-
* themed sonner toasts.
|
|
183
|
-
*
|
|
184
|
-
* Implementation notes:
|
|
185
|
-
*
|
|
186
|
-
* - We use `toast.custom(jsx, { id })` with a stable `id` so sonner
|
|
187
|
-
* deduplicates the same feedback id by itself. The previous
|
|
188
|
-
* implementation tracked processed ids in a `useRef`, which reset
|
|
189
|
-
* across remounts (StrictMode in dev, parent re-renders that swap
|
|
190
|
-
* the toaster's key) and caused the "shows / disappears / shows
|
|
191
|
-
* again" flicker. With sonner-owned dedup, remounts no longer
|
|
192
|
-
* replay the queue.
|
|
193
|
-
* - We render a fully themed body so the toast picks up the active
|
|
194
|
-
* `useTheme()` palette, font stack, and elevation tokens. Sonner's
|
|
195
|
-
* default `richColors` theme is intentionally not used because it
|
|
196
|
-
* produces a parallel non-themeable colour scheme.
|
|
197
|
-
* - Host feedback is the canonical home for `YOUR_TURN`,
|
|
198
|
-
* `PROMPT_OPENED` and `ACTION_REJECTED`. The plugin-side
|
|
199
|
-
* `<ToastProvider>` no longer mirrors these; consumers must pass
|
|
200
|
-
* the explicit `feedback` array sourced from the unified session
|
|
201
|
-
* store.
|
|
202
|
-
*/
|
|
203
|
-
export function HostFeedbackToaster({
|
|
204
|
-
feedback = [],
|
|
205
|
-
onDismiss,
|
|
206
|
-
}: HostFeedbackToasterProps) {
|
|
207
|
-
const theme = useTheme();
|
|
208
|
-
|
|
209
|
-
useEffect(() => {
|
|
210
|
-
for (const item of feedback) {
|
|
211
|
-
const presentation = describeFeedback(item);
|
|
212
|
-
const dismiss = () => onDismiss?.(item.id);
|
|
213
|
-
|
|
214
|
-
// `toast.custom(jsx, { id })` is idempotent — calling it again
|
|
215
|
-
// with the same id updates the existing toast in place rather
|
|
216
|
-
// than mounting a duplicate, which is exactly what we want for
|
|
217
|
-
// host-feedback ids that may flow through several store
|
|
218
|
-
// snapshots before being dismissed.
|
|
219
|
-
toast.custom(
|
|
220
|
-
(toastId) => (
|
|
221
|
-
<HostFeedbackToastBody
|
|
222
|
-
presentation={presentation}
|
|
223
|
-
theme={theme}
|
|
224
|
-
onDismiss={() => {
|
|
225
|
-
toast.dismiss(toastId);
|
|
226
|
-
dismiss();
|
|
227
|
-
}}
|
|
228
|
-
/>
|
|
229
|
-
),
|
|
230
|
-
{
|
|
231
|
-
id: item.id,
|
|
232
|
-
duration: presentation.duration,
|
|
233
|
-
unstyled: true,
|
|
234
|
-
onAutoClose: () => dismiss(),
|
|
235
|
-
onDismiss: () => dismiss(),
|
|
236
|
-
},
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
}, [feedback, onDismiss, theme]);
|
|
240
|
-
|
|
241
|
-
return (
|
|
242
|
-
<Toaster
|
|
243
|
-
position="top-center"
|
|
244
|
-
// Disable `richColors` because we render fully themed bodies
|
|
245
|
-
// via `toast.custom` and don't want sonner's default success /
|
|
246
|
-
// error palette competing with the active theme.
|
|
247
|
-
richColors={false}
|
|
248
|
-
// Forward the OS-level theme hint so sonner's container
|
|
249
|
-
// (backdrop, default text colour for any non-custom toasts)
|
|
250
|
-
// matches the resolved Dreamboard theme mode.
|
|
251
|
-
theme={theme.meta.mode === "dark" ? "dark" : "light"}
|
|
252
|
-
toastOptions={{
|
|
253
|
-
// We use unstyled toasts so the inner `HostFeedbackToastBody`
|
|
254
|
-
// has full control. `unstyled: true` removes sonner's
|
|
255
|
-
// built-in surface, padding and shadow.
|
|
256
|
-
unstyled: true,
|
|
257
|
-
// Make the offset match sonner's default but anchor to the
|
|
258
|
-
// viewport with safe-area insets so the toast clears phone
|
|
259
|
-
// notches.
|
|
260
|
-
style: {
|
|
261
|
-
marginTop: "env(safe-area-inset-top, 0px)",
|
|
262
|
-
},
|
|
263
|
-
}}
|
|
264
|
-
/>
|
|
265
|
-
);
|
|
266
|
-
}
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Host-owned inline feedback stack.
|
|
3
|
-
*
|
|
4
|
-
* Visual styling is driven entirely by the active `@dreamboard-games/sdk/ui`
|
|
5
|
-
* `Theme` so the stack re-skins with the rest of the host shell. The
|
|
6
|
-
* earlier implementation relied on raw Tailwind colour classes
|
|
7
|
-
* (`bg-emerald-500/10`, `text-amber-700`, …) which were untouchable
|
|
8
|
-
* by the theme tokens.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { AlertTriangle, Bell, Clock3, X } from "lucide-react";
|
|
12
|
-
import {
|
|
13
|
-
intentForVariant,
|
|
14
|
-
surfaceStyle,
|
|
15
|
-
useTheme,
|
|
16
|
-
type ButtonVariant,
|
|
17
|
-
type Theme,
|
|
18
|
-
} from "@dreamboard-games/sdk/ui";
|
|
19
|
-
import type { HostFeedback } from "../unified-session-store.js";
|
|
20
|
-
|
|
21
|
-
export interface HostFeedbackStackProps {
|
|
22
|
-
feedback: HostFeedback[];
|
|
23
|
-
onDismiss?: (feedbackId: string) => void;
|
|
24
|
-
className?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface StackPresentation {
|
|
28
|
-
title: string;
|
|
29
|
-
description: string;
|
|
30
|
-
variant: Extract<ButtonVariant, "danger" | "warning" | "success" | "info">;
|
|
31
|
-
icon: typeof AlertTriangle;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function describeFeedback(item: HostFeedback): StackPresentation {
|
|
35
|
-
switch (item.type) {
|
|
36
|
-
case "YOUR_TURN": {
|
|
37
|
-
const description =
|
|
38
|
-
item.payload.activePlayers.length > 1
|
|
39
|
-
? "You can act with one of your controlled players."
|
|
40
|
-
: "You can act now.";
|
|
41
|
-
return {
|
|
42
|
-
title: "Your turn",
|
|
43
|
-
description,
|
|
44
|
-
variant: "success",
|
|
45
|
-
icon: Bell,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
case "PROMPT_OPENED": {
|
|
49
|
-
const payload = item.payload;
|
|
50
|
-
const description = payload.targetPlayer
|
|
51
|
-
? `${payload.title ?? "A prompt is waiting."} (${payload.targetPlayer})`
|
|
52
|
-
: (payload.title ?? "A prompt is waiting.");
|
|
53
|
-
return {
|
|
54
|
-
title: "Response needed",
|
|
55
|
-
description,
|
|
56
|
-
variant: "warning",
|
|
57
|
-
icon: Clock3,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
case "ACTION_REJECTED": {
|
|
61
|
-
const payload = item.payload;
|
|
62
|
-
const description = payload.targetPlayer
|
|
63
|
-
? `${payload.reason} (${payload.targetPlayer})`
|
|
64
|
-
: payload.reason;
|
|
65
|
-
return {
|
|
66
|
-
title: "Action rejected",
|
|
67
|
-
description,
|
|
68
|
-
variant: "danger",
|
|
69
|
-
icon: AlertTriangle,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const exhaustive: never = item;
|
|
75
|
-
throw new Error(
|
|
76
|
-
`Unsupported host feedback item type: ${String((exhaustive as { type?: unknown }).type)}`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface FeedbackEntryProps {
|
|
81
|
-
item: HostFeedback;
|
|
82
|
-
presentation: StackPresentation;
|
|
83
|
-
theme: Theme;
|
|
84
|
-
onDismiss?: (feedbackId: string) => void;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function FeedbackEntry({
|
|
88
|
-
item,
|
|
89
|
-
presentation,
|
|
90
|
-
theme,
|
|
91
|
-
onDismiss,
|
|
92
|
-
}: FeedbackEntryProps) {
|
|
93
|
-
const intent = intentForVariant(theme, presentation.variant);
|
|
94
|
-
const Icon = presentation.icon;
|
|
95
|
-
return (
|
|
96
|
-
<div
|
|
97
|
-
role="status"
|
|
98
|
-
aria-live="polite"
|
|
99
|
-
style={{
|
|
100
|
-
...surfaceStyle(theme, { tone: "card", radius: "lg" }),
|
|
101
|
-
background: intent.soft,
|
|
102
|
-
color: intent.onSoft,
|
|
103
|
-
border: `1px solid ${intent.border}`,
|
|
104
|
-
boxShadow: theme.elevation.rest,
|
|
105
|
-
display: "flex",
|
|
106
|
-
alignItems: "flex-start",
|
|
107
|
-
gap: theme.space[3],
|
|
108
|
-
padding: theme.space[3],
|
|
109
|
-
fontFamily: theme.typography.fontFamily.body,
|
|
110
|
-
}}
|
|
111
|
-
>
|
|
112
|
-
<Icon
|
|
113
|
-
size={20}
|
|
114
|
-
strokeWidth={2.5}
|
|
115
|
-
aria-hidden="true"
|
|
116
|
-
style={{
|
|
117
|
-
flexShrink: 0,
|
|
118
|
-
marginTop: 2,
|
|
119
|
-
color: intent.solid,
|
|
120
|
-
}}
|
|
121
|
-
/>
|
|
122
|
-
<div style={{ flex: 1, minWidth: 0 }}>
|
|
123
|
-
<div
|
|
124
|
-
style={{
|
|
125
|
-
fontFamily: theme.typography.fontFamily.display,
|
|
126
|
-
fontSize: theme.typography.fontSize.md,
|
|
127
|
-
fontWeight: theme.typography.fontWeight.bold,
|
|
128
|
-
lineHeight: theme.typography.lineHeight.tight,
|
|
129
|
-
color: intent.onSoft,
|
|
130
|
-
}}
|
|
131
|
-
>
|
|
132
|
-
{presentation.title}
|
|
133
|
-
</div>
|
|
134
|
-
<div
|
|
135
|
-
style={{
|
|
136
|
-
marginTop: theme.space[1],
|
|
137
|
-
fontSize: theme.typography.fontSize.sm,
|
|
138
|
-
fontWeight: theme.typography.fontWeight.medium,
|
|
139
|
-
lineHeight: theme.typography.lineHeight.normal,
|
|
140
|
-
color: intent.onSoft,
|
|
141
|
-
opacity: 0.92,
|
|
142
|
-
wordBreak: "break-word",
|
|
143
|
-
}}
|
|
144
|
-
>
|
|
145
|
-
{presentation.description}
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
{onDismiss ? (
|
|
149
|
-
<button
|
|
150
|
-
type="button"
|
|
151
|
-
aria-label="Dismiss feedback"
|
|
152
|
-
onClick={() => onDismiss(item.id)}
|
|
153
|
-
style={{
|
|
154
|
-
flexShrink: 0,
|
|
155
|
-
width: 28,
|
|
156
|
-
height: 28,
|
|
157
|
-
display: "inline-flex",
|
|
158
|
-
alignItems: "center",
|
|
159
|
-
justifyContent: "center",
|
|
160
|
-
background: "transparent",
|
|
161
|
-
border: "none",
|
|
162
|
-
borderRadius: theme.radius.pill,
|
|
163
|
-
color: intent.onSoft,
|
|
164
|
-
cursor: "pointer",
|
|
165
|
-
opacity: 0.7,
|
|
166
|
-
transition: `opacity ${theme.motion.duration.fast} ${theme.motion.easing.out}`,
|
|
167
|
-
}}
|
|
168
|
-
onMouseEnter={(event) => {
|
|
169
|
-
event.currentTarget.style.opacity = "1";
|
|
170
|
-
}}
|
|
171
|
-
onMouseLeave={(event) => {
|
|
172
|
-
event.currentTarget.style.opacity = "0.7";
|
|
173
|
-
}}
|
|
174
|
-
>
|
|
175
|
-
<X size={16} aria-hidden="true" />
|
|
176
|
-
</button>
|
|
177
|
-
) : null}
|
|
178
|
-
</div>
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function HostFeedbackStack({
|
|
183
|
-
feedback,
|
|
184
|
-
onDismiss,
|
|
185
|
-
className,
|
|
186
|
-
}: HostFeedbackStackProps) {
|
|
187
|
-
const theme = useTheme();
|
|
188
|
-
if (feedback.length === 0) {
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return (
|
|
193
|
-
<div
|
|
194
|
-
className={className}
|
|
195
|
-
style={{
|
|
196
|
-
display: "flex",
|
|
197
|
-
flexDirection: "column",
|
|
198
|
-
gap: theme.space[2],
|
|
199
|
-
}}
|
|
200
|
-
>
|
|
201
|
-
{[...feedback].reverse().map((item) => (
|
|
202
|
-
<FeedbackEntry
|
|
203
|
-
key={item.id}
|
|
204
|
-
item={item}
|
|
205
|
-
presentation={describeFeedback(item)}
|
|
206
|
-
theme={theme}
|
|
207
|
-
onDismiss={onDismiss}
|
|
208
|
-
/>
|
|
209
|
-
))}
|
|
210
|
-
</div>
|
|
211
|
-
);
|
|
212
|
-
}
|