@dreamboard-games/cli 0.1.30-alpha.0 → 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-TSJVWTJO.js → chunk-N7XPNNUI.js} +14 -12
- package/dist/chunk-N7XPNNUI.js.map +1 -0
- package/dist/chunk-SEGVTWSK.js +44 -0
- package/dist/{chunk-3XNJT3RK.js → chunk-TAQKH67O.js} +21279 -35845
- package/dist/chunk-TAQKH67O.js.map +1 -0
- package/dist/{global-config-UKSWNDTX.js → global-config-S4ZIPECE.js} +3 -3
- package/dist/index.js +955 -230
- 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-2NZNKIND.mjs +0 -166
- package/dist/agent-verifier/chunk-2QMNAVV4.mjs +0 -14522
- package/dist/agent-verifier/chunk-2SZHMP6F.mjs +0 -264
- 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-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-DTMJCPS4.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-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-PM3SVG6R.mjs +0 -38
- package/dist/agent-verifier/chunk-RBDDIIPM.mjs +0 -19
- package/dist/agent-verifier/chunk-RJBLBYHX.mjs +0 -1681
- 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-WNCQQVOF.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-EWGZIVOY.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-4YEQME5N.mjs +0 -28
- package/dist/agent-verifier/sync-LOQAH4RC.mjs +0 -594
- package/dist/agent-verifier/test-YOJERVHN.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-3XNJT3RK.js.map +0 -1
- package/dist/chunk-7FOO4AJI.js +0 -50
- package/dist/chunk-7FOO4AJI.js.map +0 -1
- package/dist/chunk-TSJVWTJO.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-UKSWNDTX.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,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,194 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
PERF_MARK_NAMES,
|
|
4
|
-
clearPerfEntries,
|
|
5
|
-
getPerfEntries,
|
|
6
|
-
isPerfEnabled,
|
|
7
|
-
type PerfEntry,
|
|
8
|
-
} from "../perf.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Tier-0 input-latency HUD.
|
|
12
|
-
*
|
|
13
|
-
* Enabled when {@link isPerfEnabled} returns true (dev build or
|
|
14
|
-
* `localStorage.dreamboard.perf = 1`) AND the URL contains `?perf=1`.
|
|
15
|
-
* The explicit query flag keeps the overlay off in the normal dev
|
|
16
|
-
* experience; flip it on for a targeted benchmarking session.
|
|
17
|
-
*
|
|
18
|
-
* Shared by `apps/web` (real game sessions) and `apps/dreamboard-cli`
|
|
19
|
-
* (`dreamboard dev` local host) so both surfaces expose the same HUD
|
|
20
|
-
* against the same `clientActionId`-keyed ring buffer.
|
|
21
|
-
*/
|
|
22
|
-
const REFRESH_INTERVAL_MS = 500;
|
|
23
|
-
const MAX_ROWS = 10;
|
|
24
|
-
|
|
25
|
-
interface RowStats {
|
|
26
|
-
clientActionId: string;
|
|
27
|
-
totalMs: number | null;
|
|
28
|
-
serverMs: number | null;
|
|
29
|
-
postAckTailMs: number | null;
|
|
30
|
-
sseMs: number | null;
|
|
31
|
-
renderMs: number | null;
|
|
32
|
-
version: number | undefined;
|
|
33
|
-
syncId: number | undefined;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function firstMarkMs(entry: PerfEntry, name: string): number | null {
|
|
37
|
-
const mark = entry.marks.find((m) => m.name === name);
|
|
38
|
-
return mark ? mark.timestampMs : null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function deltaMs(a: number | null, b: number | null): number | null {
|
|
42
|
-
if (a === null || b === null) return null;
|
|
43
|
-
return Math.round((b - a) * 10) / 10;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function toRowStats(entry: PerfEntry): RowStats {
|
|
47
|
-
const t0 = firstMarkMs(entry, PERF_MARK_NAMES.T0_CLICK);
|
|
48
|
-
const t2 = firstMarkMs(entry, PERF_MARK_NAMES.T2_HTTP_SENT);
|
|
49
|
-
const t3 = firstMarkMs(entry, PERF_MARK_NAMES.T3_HTTP_RESPONSE);
|
|
50
|
-
const t4 = firstMarkMs(entry, PERF_MARK_NAMES.T4_SSE_RECEIVED);
|
|
51
|
-
const t7 = firstMarkMs(entry, PERF_MARK_NAMES.T7_STATE_SYNC_RECEIVED);
|
|
52
|
-
const t8 = firstMarkMs(entry, PERF_MARK_NAMES.T8_RENDER_COMMIT);
|
|
53
|
-
return {
|
|
54
|
-
clientActionId: entry.clientActionId,
|
|
55
|
-
totalMs: deltaMs(t0, t8),
|
|
56
|
-
serverMs: deltaMs(t2, t3),
|
|
57
|
-
postAckTailMs: deltaMs(t3, t8),
|
|
58
|
-
sseMs: deltaMs(t3, t4),
|
|
59
|
-
renderMs: deltaMs(t7, t8),
|
|
60
|
-
version: entry.version,
|
|
61
|
-
syncId: entry.syncId,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function formatMs(value: number | null): string {
|
|
66
|
-
if (value === null) return "—";
|
|
67
|
-
return `${value.toFixed(1)}ms`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function formatActionId(actionId: string): string {
|
|
71
|
-
return actionId.length > 8 ? `${actionId.slice(0, 8)}…` : actionId;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function usePerfOverlayEnabled(): boolean {
|
|
75
|
-
const [enabled, setEnabled] = useState(false);
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
if (typeof window === "undefined") return;
|
|
78
|
-
if (!isPerfEnabled()) {
|
|
79
|
-
setEnabled(false);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const check = () => {
|
|
83
|
-
const params = new URLSearchParams(window.location.search);
|
|
84
|
-
setEnabled(params.get("perf") === "1");
|
|
85
|
-
};
|
|
86
|
-
check();
|
|
87
|
-
window.addEventListener("popstate", check);
|
|
88
|
-
return () => window.removeEventListener("popstate", check);
|
|
89
|
-
}, []);
|
|
90
|
-
return enabled;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export const PerfOverlay: React.FC = () => {
|
|
94
|
-
const enabled = usePerfOverlayEnabled();
|
|
95
|
-
const [rows, setRows] = useState<RowStats[]>([]);
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
if (!enabled) return;
|
|
99
|
-
const refresh = () => {
|
|
100
|
-
const entries = getPerfEntries();
|
|
101
|
-
const sliced = entries.slice(-MAX_ROWS).reverse();
|
|
102
|
-
setRows(sliced.map(toRowStats));
|
|
103
|
-
};
|
|
104
|
-
refresh();
|
|
105
|
-
const handle = window.setInterval(refresh, REFRESH_INTERVAL_MS);
|
|
106
|
-
return () => window.clearInterval(handle);
|
|
107
|
-
}, [enabled]);
|
|
108
|
-
|
|
109
|
-
if (!enabled) return null;
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<div
|
|
113
|
-
className="fixed bottom-4 right-4 z-[9999] max-w-[640px] rounded-md border border-slate-700 bg-slate-900/90 p-3 font-mono text-[11px] text-slate-100 shadow-lg backdrop-blur"
|
|
114
|
-
data-testid="perf-overlay"
|
|
115
|
-
>
|
|
116
|
-
<div className="mb-2 flex items-center justify-between gap-3">
|
|
117
|
-
<span className="font-bold uppercase tracking-wider text-emerald-300">
|
|
118
|
-
Tier-0 Input Perf
|
|
119
|
-
</span>
|
|
120
|
-
<div className="flex items-center gap-2">
|
|
121
|
-
<button
|
|
122
|
-
type="button"
|
|
123
|
-
className="rounded border border-slate-600 px-2 py-0.5 text-[10px] uppercase hover:bg-slate-700"
|
|
124
|
-
onClick={() => {
|
|
125
|
-
clearPerfEntries();
|
|
126
|
-
setRows([]);
|
|
127
|
-
}}
|
|
128
|
-
>
|
|
129
|
-
Clear
|
|
130
|
-
</button>
|
|
131
|
-
<button
|
|
132
|
-
type="button"
|
|
133
|
-
className="rounded border border-slate-600 px-2 py-0.5 text-[10px] uppercase hover:bg-slate-700"
|
|
134
|
-
onClick={() => {
|
|
135
|
-
// eslint-disable-next-line no-console
|
|
136
|
-
console.table(getPerfEntries());
|
|
137
|
-
}}
|
|
138
|
-
>
|
|
139
|
-
Dump
|
|
140
|
-
</button>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
{rows.length === 0 ? (
|
|
144
|
-
<div className="py-2 text-slate-400">
|
|
145
|
-
No samples yet. Submit an action to populate the HUD.
|
|
146
|
-
</div>
|
|
147
|
-
) : (
|
|
148
|
-
<table className="w-full border-collapse">
|
|
149
|
-
<thead className="text-slate-400">
|
|
150
|
-
<tr>
|
|
151
|
-
<th className="pr-2 text-left font-semibold">action</th>
|
|
152
|
-
<th className="pr-2 text-right font-semibold">v</th>
|
|
153
|
-
<th className="pr-2 text-right font-semibold">total</th>
|
|
154
|
-
<th className="pr-2 text-right font-semibold">server</th>
|
|
155
|
-
<th className="pr-2 text-right font-semibold">post-ack</th>
|
|
156
|
-
<th className="pr-2 text-right font-semibold">sse</th>
|
|
157
|
-
<th className="text-right font-semibold">render</th>
|
|
158
|
-
</tr>
|
|
159
|
-
</thead>
|
|
160
|
-
<tbody>
|
|
161
|
-
{rows.map((row) => (
|
|
162
|
-
<tr
|
|
163
|
-
key={row.clientActionId}
|
|
164
|
-
className="border-t border-slate-800 align-top"
|
|
165
|
-
>
|
|
166
|
-
<td className="pr-2 text-slate-300">
|
|
167
|
-
{formatActionId(row.clientActionId)}
|
|
168
|
-
</td>
|
|
169
|
-
<td className="pr-2 text-right text-slate-400">
|
|
170
|
-
{row.version ?? "—"}
|
|
171
|
-
</td>
|
|
172
|
-
<td className="pr-2 text-right text-emerald-200">
|
|
173
|
-
{formatMs(row.totalMs)}
|
|
174
|
-
</td>
|
|
175
|
-
<td className="pr-2 text-right text-slate-200">
|
|
176
|
-
{formatMs(row.serverMs)}
|
|
177
|
-
</td>
|
|
178
|
-
<td className="pr-2 text-right text-amber-200">
|
|
179
|
-
{formatMs(row.postAckTailMs)}
|
|
180
|
-
</td>
|
|
181
|
-
<td className="pr-2 text-right text-slate-200">
|
|
182
|
-
{formatMs(row.sseMs)}
|
|
183
|
-
</td>
|
|
184
|
-
<td className="text-right text-slate-200">
|
|
185
|
-
{formatMs(row.renderMs)}
|
|
186
|
-
</td>
|
|
187
|
-
</tr>
|
|
188
|
-
))}
|
|
189
|
-
</tbody>
|
|
190
|
-
</table>
|
|
191
|
-
)}
|
|
192
|
-
</div>
|
|
193
|
-
);
|
|
194
|
-
};
|