@dreamboard-games/cli 0.1.30-alpha.1 → 0.1.30-alpha.11

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 (156) hide show
  1. package/README.md +179 -22
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +31 -30
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
  4. package/dist/agent-verifier/{chunk-4WD3YU2E.mjs → chunk-3IJBOLGT.mjs} +4 -12
  5. package/dist/agent-verifier/chunk-3IJBOLGT.mjs.map +1 -0
  6. package/dist/agent-verifier/{chunk-6A5HRJMQ.mjs → chunk-4GU3PCHV.mjs} +62 -99
  7. package/dist/agent-verifier/chunk-4GU3PCHV.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-SYPLYRGB.mjs → chunk-6XRC5PWB.mjs} +119 -310
  9. package/dist/agent-verifier/chunk-6XRC5PWB.mjs.map +1 -0
  10. package/dist/agent-verifier/{chunk-BVVNBJM4.mjs → chunk-COB56ESI.mjs} +2 -1
  11. package/dist/agent-verifier/chunk-COB56ESI.mjs.map +1 -0
  12. package/dist/agent-verifier/{chunk-2GBBP27W.mjs → chunk-F2DIOJJZ.mjs} +1 -0
  13. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
  14. package/dist/agent-verifier/{chunk-CFU5EWIC.mjs → chunk-G42BGGG2.mjs} +7 -6
  15. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-XYDL7GY6.mjs → chunk-H6XDQJ3N.mjs} +1 -0
  17. package/dist/agent-verifier/{chunk-LM3OZLZG.mjs → chunk-IAYRNVUC.mjs} +1 -0
  18. package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
  19. package/dist/agent-verifier/{chunk-2QMNAVV4.mjs → chunk-JZTH3EMV.mjs} +2 -1
  20. package/dist/agent-verifier/chunk-JZTH3EMV.mjs.map +1 -0
  21. package/dist/agent-verifier/chunk-KK47X7RV.mjs +14 -0
  22. package/dist/agent-verifier/chunk-KK47X7RV.mjs.map +1 -0
  23. package/dist/agent-verifier/{chunk-SHUMAVAP.mjs → chunk-M7UVBANQ.mjs} +8 -9
  24. package/dist/agent-verifier/chunk-M7UVBANQ.mjs.map +1 -0
  25. package/dist/agent-verifier/{chunk-2E5P5NWG.mjs → chunk-NAK77WXW.mjs} +58 -126
  26. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
  27. package/dist/agent-verifier/{chunk-CEQ2VJWN.mjs → chunk-POBFNXD4.mjs} +2 -1
  28. package/dist/agent-verifier/chunk-POBFNXD4.mjs.map +1 -0
  29. package/dist/agent-verifier/{chunk-6UUJEYDV.mjs → chunk-QBAF7EYR.mjs} +1 -0
  30. package/dist/agent-verifier/chunk-QBAF7EYR.mjs.map +1 -0
  31. package/dist/agent-verifier/{chunk-7653FPGJ.mjs → chunk-RHI6S4SU.mjs} +3 -2
  32. package/dist/agent-verifier/chunk-RHI6S4SU.mjs.map +1 -0
  33. package/dist/agent-verifier/{chunk-MINCYHXN.mjs → chunk-TAEQKBJB.mjs} +1 -0
  34. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
  35. package/dist/agent-verifier/{chunk-7E65UQLY.mjs → chunk-TLYGTHXU.mjs} +3 -2
  36. package/dist/agent-verifier/chunk-TLYGTHXU.mjs.map +1 -0
  37. package/dist/agent-verifier/{chunk-JH22JNYD.mjs → chunk-UIJ2NDG6.mjs} +93 -24
  38. package/dist/agent-verifier/chunk-UIJ2NDG6.mjs.map +1 -0
  39. package/dist/agent-verifier/{chunk-EIQWDQWJ.mjs → chunk-UWJIZML3.mjs} +13 -14
  40. package/dist/agent-verifier/chunk-UWJIZML3.mjs.map +1 -0
  41. package/dist/agent-verifier/{chunk-CJEEA6NJ.mjs → chunk-VLOIZDR6.mjs} +15 -31
  42. package/dist/agent-verifier/chunk-VLOIZDR6.mjs.map +1 -0
  43. package/dist/agent-verifier/{chunk-HJFQDSTU.mjs → chunk-W2MDP5ZN.mjs} +6 -5
  44. package/dist/agent-verifier/chunk-W2MDP5ZN.mjs.map +1 -0
  45. package/dist/agent-verifier/{chunk-CEDUHGNH.mjs → chunk-XKCJBIRY.mjs} +2 -1
  46. package/dist/agent-verifier/chunk-XKCJBIRY.mjs.map +1 -0
  47. package/dist/agent-verifier/{chunk-VYJTHSYR.mjs → chunk-YDIOW2BO.mjs} +2 -1
  48. package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +1 -0
  49. package/dist/agent-verifier/{chunk-MRCUP5SW.mjs → chunk-YE7UAO3T.mjs} +1 -0
  50. package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
  51. package/dist/agent-verifier/{chunk-EOQIV6PS.mjs → chunk-YR664DJX.mjs} +111 -116
  52. package/dist/agent-verifier/chunk-YR664DJX.mjs.map +1 -0
  53. package/dist/agent-verifier/{chunk-2SZHMP6F.mjs → chunk-Z6OZWUIZ.mjs} +6 -9
  54. package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +1 -0
  55. package/dist/agent-verifier/{chunk-RBDDIIPM.mjs → chunk-ZEELHSY3.mjs} +1 -0
  56. package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
  57. package/dist/agent-verifier/{compile-5QSPIOUT.mjs → compile-WZ7X6I2A.mjs} +27 -27
  58. package/dist/agent-verifier/compile-WZ7X6I2A.mjs.map +1 -0
  59. package/dist/agent-verifier/{global-config-WX3ZZIVU.mjs → global-config-XHL7BCKN.mjs} +6 -5
  60. package/dist/agent-verifier/global-config-XHL7BCKN.mjs.map +1 -0
  61. package/dist/agent-verifier/{keychain-backend-TNOPQV3Z.mjs → keychain-backend-A3MRWLPF.mjs} +2 -1
  62. package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +1 -0
  63. package/dist/agent-verifier/{local-files-MTPLP62S.mjs → local-files-ZW52HSVT.mjs} +10 -11
  64. package/dist/agent-verifier/local-files-ZW52HSVT.mjs.map +1 -0
  65. package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs +10 -0
  66. package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs.map +1 -0
  67. package/dist/agent-verifier/{materialize-workspace-FKALAE2T.mjs → materialize-workspace-BKZLLFI4.mjs} +20 -20
  68. package/dist/agent-verifier/materialize-workspace-BKZLLFI4.mjs.map +1 -0
  69. package/dist/agent-verifier/{project-state-7GR6BQTQ.mjs → project-state-XKUSCFSV.mjs} +3 -2
  70. package/dist/agent-verifier/project-state-XKUSCFSV.mjs.map +1 -0
  71. package/dist/agent-verifier/{prompt-3BAINGAQ.mjs → prompt-VKHMCQT6.mjs} +2 -1
  72. package/dist/agent-verifier/prompt-VKHMCQT6.mjs.map +1 -0
  73. package/dist/agent-verifier/{reducer-bundle-preflight-C73LEXI2.mjs → reducer-bundle-preflight-7NYZF5ZT.mjs} +6 -9
  74. package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs.map +1 -0
  75. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +11 -0
  76. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs.map +1 -0
  77. package/dist/agent-verifier/{reducer-native-test-harness-GMWBUISX.mjs → reducer-native-test-harness-D4VWPIAC.mjs} +14 -17
  78. package/dist/agent-verifier/reducer-native-test-harness-D4VWPIAC.mjs.map +1 -0
  79. package/dist/agent-verifier/static-scaffold-JCRBDKEH.mjs +26 -0
  80. package/dist/agent-verifier/static-scaffold-JCRBDKEH.mjs.map +1 -0
  81. package/dist/agent-verifier/{sync-3DUQH32H.mjs → sync-ELLJEWMB.mjs} +41 -39
  82. package/dist/agent-verifier/sync-ELLJEWMB.mjs.map +1 -0
  83. package/dist/agent-verifier/{test-P4U5INTD.mjs → test-OSXBPLSP.mjs} +29 -31
  84. package/dist/agent-verifier/test-OSXBPLSP.mjs.map +1 -0
  85. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs +10 -0
  86. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +1 -0
  87. package/dist/agent-verifier/{workspace-dependencies-HZ6VVS4G.mjs → workspace-dependencies-ULZZZPNX.mjs} +5 -4
  88. package/dist/agent-verifier/workspace-dependencies-ULZZZPNX.mjs.map +1 -0
  89. package/dist/{chunk-C6UAT6EH.js → chunk-GXM7RRZJ.js} +9 -11
  90. package/dist/chunk-GXM7RRZJ.js.map +1 -0
  91. package/dist/{chunk-RS7UXJZV.js → chunk-P5TITCD3.js} +790 -17875
  92. package/dist/chunk-P5TITCD3.js.map +1 -0
  93. package/dist/{global-config-AGFBDFYD.js → global-config-WPJRXVDO.js} +2 -2
  94. package/dist/global-config-WPJRXVDO.js.map +1 -0
  95. package/dist/index.js +455 -54
  96. package/dist/index.js.map +1 -1
  97. package/dist/internal.js +2 -3
  98. package/package.json +8 -7
  99. package/skills/dreamboard/references/building-your-first-game.md +510 -0
  100. package/skills/dreamboard/references/cli.md +104 -0
  101. package/skills/dreamboard/references/game-interface.md +548 -0
  102. package/skills/dreamboard/references/manifest-authoring.md +597 -0
  103. package/skills/dreamboard/references/quickstart.md +66 -0
  104. package/skills/dreamboard/references/reducer.md +864 -0
  105. package/skills/dreamboard/references/rule-authoring.md +147 -0
  106. package/skills/dreamboard/references/testing.md +249 -0
  107. package/skills/dreamboard/scripts/events-extract.mjs +218 -0
  108. package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
  109. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  110. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  111. package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
  112. package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
  113. package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
  114. package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
  115. package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
  116. package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
  117. package/dist/chunk-7FOO4AJI.js +0 -50
  118. package/dist/chunk-7FOO4AJI.js.map +0 -1
  119. package/dist/chunk-C6UAT6EH.js.map +0 -1
  120. package/dist/chunk-RS7UXJZV.js.map +0 -1
  121. package/dist/internal.d.ts +0 -311
  122. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
  123. package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
  124. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
  125. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
  126. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
  127. package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
  128. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
  129. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
  130. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
  131. package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
  132. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
  133. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
  134. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
  135. package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
  136. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
  137. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
  138. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
  139. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
  140. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
  141. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
  142. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
  143. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
  144. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
  145. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
  146. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
  147. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
  148. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
  149. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
  150. package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
  151. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
  152. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
  153. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
  154. package/dist/testing-KLSV6CPJ.js +0 -674
  155. package/dist/testing-KLSV6CPJ.js.map +0 -1
  156. /package/dist/{global-config-AGFBDFYD.js.map → agent-verifier/chunk-H6XDQJ3N.mjs.map} +0 -0
@@ -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
- }
@@ -1,271 +0,0 @@
1
- import * as React from "react";
2
- import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
3
- import * as PopoverPrimitive from "@radix-ui/react-popover";
4
- import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
5
- import { Slot } from "@radix-ui/react-slot";
6
- import { cva, type VariantProps } from "class-variance-authority";
7
- import { CircleIcon } from "lucide-react";
8
- import { clsx, type ClassValue } from "clsx";
9
- import { twMerge } from "tailwind-merge";
10
-
11
- export function cn(...inputs: ClassValue[]) {
12
- return twMerge(clsx(inputs));
13
- }
14
-
15
- const buttonVariants = cva(
16
- "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive border-[3px] wobbly-border-md",
17
- {
18
- variants: {
19
- variant: {
20
- default:
21
- "border-border hard-shadow hard-shadow-hover hard-shadow-active bg-white text-foreground hover:bg-primary hover:text-white",
22
- outline:
23
- "border-border hard-shadow hard-shadow-hover hard-shadow-active bg-transparent hover:bg-muted",
24
- secondary:
25
- "border-border hard-shadow hard-shadow-hover hard-shadow-active bg-secondary text-secondary-foreground hover:bg-ring hover:text-white",
26
- },
27
- size: {
28
- default: "h-12 px-6 py-2 has-[>svg]:px-4 text-lg",
29
- sm: "h-10 gap-1.5 px-4 has-[>svg]:px-3 text-base",
30
- },
31
- },
32
- defaultVariants: {
33
- variant: "default",
34
- size: "default",
35
- },
36
- },
37
- );
38
-
39
- export function Button({
40
- className,
41
- variant,
42
- size,
43
- asChild = false,
44
- ...props
45
- }: React.ComponentProps<"button"> &
46
- VariantProps<typeof buttonVariants> & {
47
- asChild?: boolean;
48
- }) {
49
- const Comp = asChild ? Slot : "button";
50
-
51
- return (
52
- <Comp
53
- data-slot="button"
54
- className={cn(buttonVariants({ variant, size, className }))}
55
- {...props}
56
- />
57
- );
58
- }
59
-
60
- const badgeVariants = cva(
61
- "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
62
- {
63
- variants: {
64
- variant: {
65
- default:
66
- "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
67
- secondary:
68
- "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
69
- },
70
- },
71
- defaultVariants: {
72
- variant: "default",
73
- },
74
- },
75
- );
76
-
77
- export function Badge({
78
- className,
79
- variant,
80
- asChild = false,
81
- ...props
82
- }: React.ComponentProps<"span"> &
83
- VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
84
- const Comp = asChild ? Slot : "span";
85
-
86
- return (
87
- <Comp
88
- data-slot="badge"
89
- className={cn(badgeVariants({ variant }), className)}
90
- {...props}
91
- />
92
- );
93
- }
94
-
95
- export function DropdownMenu({
96
- ...props
97
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
98
- return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
99
- }
100
-
101
- export function DropdownMenuTrigger({
102
- ...props
103
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
104
- return (
105
- <DropdownMenuPrimitive.Trigger
106
- data-slot="dropdown-menu-trigger"
107
- {...props}
108
- />
109
- );
110
- }
111
-
112
- export function DropdownMenuContent({
113
- className,
114
- sideOffset = 4,
115
- ...props
116
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
117
- return (
118
- <DropdownMenuPrimitive.Portal>
119
- <DropdownMenuPrimitive.Content
120
- data-slot="dropdown-menu-content"
121
- sideOffset={sideOffset}
122
- className={cn(
123
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
124
- className,
125
- )}
126
- {...props}
127
- />
128
- </DropdownMenuPrimitive.Portal>
129
- );
130
- }
131
-
132
- export function DropdownMenuLabel({
133
- className,
134
- inset,
135
- ...props
136
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
137
- inset?: boolean;
138
- }) {
139
- return (
140
- <DropdownMenuPrimitive.Label
141
- data-slot="dropdown-menu-label"
142
- data-inset={inset}
143
- className={cn(
144
- "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
145
- className,
146
- )}
147
- {...props}
148
- />
149
- );
150
- }
151
-
152
- export function DropdownMenuRadioGroup({
153
- ...props
154
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
155
- return (
156
- <DropdownMenuPrimitive.RadioGroup
157
- data-slot="dropdown-menu-radio-group"
158
- {...props}
159
- />
160
- );
161
- }
162
-
163
- export function DropdownMenuRadioItem({
164
- className,
165
- children,
166
- ...props
167
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
168
- return (
169
- <DropdownMenuPrimitive.RadioItem
170
- data-slot="dropdown-menu-radio-item"
171
- className={cn(
172
- "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
173
- className,
174
- )}
175
- {...props}
176
- >
177
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
178
- <DropdownMenuPrimitive.ItemIndicator>
179
- <CircleIcon className="size-2 fill-current" />
180
- </DropdownMenuPrimitive.ItemIndicator>
181
- </span>
182
- {children}
183
- </DropdownMenuPrimitive.RadioItem>
184
- );
185
- }
186
-
187
- export function DropdownMenuSeparator({
188
- className,
189
- ...props
190
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
191
- return (
192
- <DropdownMenuPrimitive.Separator
193
- data-slot="dropdown-menu-separator"
194
- className={cn("bg-border -mx-1 my-1 h-px", className)}
195
- {...props}
196
- />
197
- );
198
- }
199
-
200
- export function Popover({
201
- ...props
202
- }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
203
- return <PopoverPrimitive.Root data-slot="popover" {...props} />;
204
- }
205
-
206
- export function PopoverTrigger({
207
- ...props
208
- }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
209
- return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
210
- }
211
-
212
- type PopoverContentProps = React.ComponentProps<
213
- typeof PopoverPrimitive.Content
214
- > & {
215
- container?: HTMLElement | null;
216
- };
217
-
218
- export function PopoverContent({
219
- className,
220
- align = "center",
221
- sideOffset = 4,
222
- container,
223
- ...props
224
- }: PopoverContentProps) {
225
- return (
226
- <PopoverPrimitive.Portal container={container ?? undefined}>
227
- <PopoverPrimitive.Content
228
- data-slot="popover-content"
229
- align={align}
230
- sideOffset={sideOffset}
231
- className={cn(
232
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
233
- className,
234
- )}
235
- {...props}
236
- />
237
- </PopoverPrimitive.Portal>
238
- );
239
- }
240
-
241
- export function ScrollArea({
242
- className,
243
- children,
244
- ...props
245
- }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
246
- return (
247
- <ScrollAreaPrimitive.Root
248
- data-slot="scroll-area"
249
- className={cn("relative", className)}
250
- {...props}
251
- >
252
- <ScrollAreaPrimitive.Viewport
253
- data-slot="scroll-area-viewport"
254
- className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
255
- >
256
- {children}
257
- </ScrollAreaPrimitive.Viewport>
258
- <ScrollAreaPrimitive.ScrollAreaScrollbar
259
- data-slot="scroll-area-scrollbar"
260
- orientation="vertical"
261
- className="flex h-full w-2.5 touch-none border-l border-l-transparent p-px transition-colors select-none"
262
- >
263
- <ScrollAreaPrimitive.ScrollAreaThumb
264
- data-slot="scroll-area-thumb"
265
- className="bg-border relative flex-1 rounded-full"
266
- />
267
- </ScrollAreaPrimitive.ScrollAreaScrollbar>
268
- <ScrollAreaPrimitive.Corner />
269
- </ScrollAreaPrimitive.Root>
270
- );
271
- }
@@ -1,135 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { Check, Copy } from "lucide-react";
3
-
4
- type HostMetaField = "gameId" | "sessionId" | "shortCode";
5
-
6
- export interface HostSessionMetadataProps {
7
- gameId: string;
8
- sessionId: string;
9
- shortCode: string;
10
- className?: string;
11
- }
12
-
13
- export function HostSessionMetadata({
14
- gameId,
15
- sessionId,
16
- shortCode,
17
- className,
18
- }: HostSessionMetadataProps) {
19
- const [copiedField, setCopiedField] = useState<HostMetaField | null>(null);
20
-
21
- useEffect(() => {
22
- if (!copiedField) {
23
- return;
24
- }
25
-
26
- const timeoutId = window.setTimeout(() => {
27
- setCopiedField(null);
28
- }, 1600);
29
-
30
- return () => {
31
- window.clearTimeout(timeoutId);
32
- };
33
- }, [copiedField]);
34
-
35
- const handleCopyMeta = async (field: HostMetaField, value: string) => {
36
- if (!value || !navigator.clipboard?.writeText) {
37
- return;
38
- }
39
-
40
- try {
41
- await navigator.clipboard.writeText(value);
42
- setCopiedField(field);
43
- } catch {
44
- // Ignore clipboard failures; the button remains readable.
45
- }
46
- };
47
-
48
- return (
49
- <dl className={className ?? "space-y-3 text-sm"}>
50
- <HostSessionMetaRow
51
- label="Game ID"
52
- value={gameId}
53
- copied={copiedField === "gameId"}
54
- onCopy={() => void handleCopyMeta("gameId", gameId)}
55
- truncate
56
- />
57
- <HostSessionMetaRow
58
- label="Session ID"
59
- value={sessionId}
60
- copied={copiedField === "sessionId"}
61
- onCopy={() => void handleCopyMeta("sessionId", sessionId)}
62
- truncate
63
- />
64
- <HostSessionMetaRow
65
- label="Short code"
66
- value={shortCode}
67
- copied={copiedField === "shortCode"}
68
- onCopy={() => void handleCopyMeta("shortCode", shortCode)}
69
- />
70
- </dl>
71
- );
72
- }
73
-
74
- interface HostSessionMetaRowProps {
75
- label: string;
76
- value: string;
77
- copied: boolean;
78
- onCopy: () => void;
79
- truncate?: boolean;
80
- }
81
-
82
- function HostSessionMetaRow({
83
- label,
84
- value,
85
- copied,
86
- onCopy,
87
- truncate = false,
88
- }: HostSessionMetaRowProps) {
89
- const displayValue = truncate ? truncateMiddle(value) : value;
90
-
91
- return (
92
- <div className="grid grid-cols-[88px_minmax(0,1fr)] items-start gap-3">
93
- <dt className="pt-0.5 text-[11px] font-bold uppercase tracking-[0.14em] text-muted-foreground">
94
- {label}
95
- </dt>
96
- <dd className="min-w-0">
97
- <button
98
- type="button"
99
- onClick={onCopy}
100
- className={`flex w-full items-center justify-end gap-2 border-[2px] px-2.5 py-2 text-right transition-all ${
101
- copied
102
- ? "border-[#2d5da1] bg-[#2d5da1] text-white shadow-[2px_2px_0px_0px_#1d3f72]"
103
- : "border-border bg-white/85 text-foreground shadow-[2px_2px_0px_0px_#2d2d2d] hover:-translate-y-[1px] hover:bg-[#fff9c4]"
104
- } wobbly-border-md`}
105
- title={`Copy ${label}`}
106
- >
107
- <span className="min-w-0 flex-1 truncate font-mono text-[12px] font-bold sm:text-[13px]">
108
- {displayValue}
109
- </span>
110
- <span className="inline-flex shrink-0 items-center gap-1 text-[10px] font-bold uppercase tracking-[0.14em]">
111
- {copied ? (
112
- <>
113
- <Check className="h-3.5 w-3.5" />
114
- Copied
115
- </>
116
- ) : (
117
- <>
118
- <Copy className="h-3.5 w-3.5" />
119
- Copy
120
- </>
121
- )}
122
- </span>
123
- </button>
124
- </dd>
125
- </div>
126
- );
127
- }
128
-
129
- function truncateMiddle(value: string, leading = 12, trailing = 8): string {
130
- if (value.length <= leading + trailing + 3) {
131
- return value;
132
- }
133
-
134
- return `${value.slice(0, leading)}...${value.slice(-trailing)}`;
135
- }
@@ -1,5 +0,0 @@
1
- export * from "./host-controls.js";
2
- export * from "./host-feedback.js";
3
- export * from "./host-feedback-toaster.js";
4
- export * from "./host-session-metadata.js";
5
- export * from "./perf-overlay.js";