@adea-ai/ui 0.22.2 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/components/account-drawer.d.ts +3 -3
- package/dist/components/account-drawer.js +119 -19
- package/dist/components/on-screen-controls.d.ts +2 -1
- package/dist/components/on-screen-controls.js +158 -46
- package/dist/components/scene-settings.d.ts +3 -3
- package/dist/components/scene-settings.js +247 -28
- package/dist/components/theme-provider.d.ts +27 -5
- package/dist/components/theme-provider.js +96 -7
- package/dist/components/theme-toggle.d.ts +4 -4
- package/dist/components/theme-toggle.js +62 -18
- package/dist/components/ui/badge.d.ts +4 -3
- package/dist/components/ui/badge.js +31 -30
- package/dist/components/ui/button.d.ts +4 -3
- package/dist/components/ui/button.js +49 -32
- package/dist/components/ui/card.d.ts +3 -3
- package/dist/components/ui/card.js +25 -6
- package/dist/components/ui/dialog.d.ts +13 -13
- package/dist/components/ui/dialog.js +94 -26
- package/dist/components/ui/drawer.d.ts +23 -15
- package/dist/components/ui/drawer.js +109 -64
- package/dist/components/ui/dropdown-menu.d.ts +28 -22
- package/dist/components/ui/dropdown-menu.js +165 -36
- package/dist/components/ui/empty.d.ts +7 -6
- package/dist/components/ui/empty.js +78 -26
- package/dist/components/ui/field.d.ts +15 -14
- package/dist/components/ui/field.js +177 -55
- package/dist/components/ui/input.d.ts +2 -2
- package/dist/components/ui/input.js +20 -5
- package/dist/components/ui/label.d.ts +2 -2
- package/dist/components/ui/label.js +15 -5
- package/dist/components/ui/radio-group.d.ts +4 -4
- package/dist/components/ui/radio-group.js +36 -9
- package/dist/components/ui/separator.d.ts +6 -2
- package/dist/components/ui/separator.js +18 -6
- package/dist/components/ui/skeleton.d.ts +2 -1
- package/dist/components/ui/skeleton.js +15 -4
- package/dist/components/ui/spinner.d.ts +4 -3
- package/dist/components/ui/spinner.js +15 -5
- package/dist/components/ui/switch.d.ts +6 -3
- package/dist/components/ui/switch.js +57 -6
- package/dist/components/ui/tabs.d.ts +6 -5
- package/dist/components/ui/tabs.js +52 -24
- package/dist/components/ui/toggle-group.d.ts +10 -8
- package/dist/components/ui/toggle-group.js +91 -20
- package/dist/components/ui/toggle.d.ts +4 -2
- package/dist/components/ui/toggle.js +38 -23
- package/dist/components/ui/tooltip.d.ts +13 -6
- package/dist/components/ui/tooltip.js +51 -14
- package/dist/components/version-dialog.d.ts +2 -2
- package/dist/components/version-dialog.js +322 -129
- package/dist/components/workspace-brand.d.ts +1 -1
- package/dist/components/workspace-brand.js +12 -3
- package/dist/components/workspace-logo.d.ts +2 -2
- package/dist/components/workspace-logo.js +15 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +20 -19
- package/dist/lib/utils.js +7 -4
- package/dist/lib/version-notes.js +14 -29
- package/package.json +18 -16
- package/src/components/account-drawer.tsx +48 -67
- package/src/components/on-screen-controls.tsx +55 -62
- package/src/components/scene-settings.tsx +97 -100
- package/src/components/theme-provider.tsx +122 -22
- package/src/components/theme-toggle.tsx +29 -54
- package/src/components/ui/badge.tsx +14 -23
- package/src/components/ui/button.tsx +9 -11
- package/src/components/ui/card.tsx +9 -6
- package/src/components/ui/dialog.tsx +58 -55
- package/src/components/ui/drawer.tsx +84 -125
- package/src/components/ui/dropdown-menu.tsx +130 -153
- package/src/components/ui/empty.tsx +29 -26
- package/src/components/ui/field.tsx +83 -87
- package/src/components/ui/input.tsx +8 -8
- package/src/components/ui/label.tsx +6 -7
- package/src/components/ui/radio-group.tsx +22 -19
- package/src/components/ui/separator.tsx +16 -9
- package/src/components/ui/skeleton.tsx +6 -3
- package/src/components/ui/spinner.tsx +9 -5
- package/src/components/ui/switch.tsx +47 -20
- package/src/components/ui/tabs.tsx +31 -30
- package/src/components/ui/toggle-group.tsx +65 -54
- package/src/components/ui/toggle.tsx +9 -12
- package/src/components/ui/tooltip.tsx +41 -36
- package/src/components/version-dialog.tsx +144 -144
- package/src/components/workspace-brand.tsx +5 -5
- package/src/components/workspace-logo.tsx +9 -8
- package/src/index.tsx +7 -1
- package/src/styles/base.css +637 -0
- package/src/styles/globals.css +1 -1
- package/tsconfig.json +2 -1
- package/components.json +0 -19
|
@@ -1,31 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
5
|
-
.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
|
|
6
|
-
.replace(/^[ \t]*#{1,6}[ \t]*/gm, '')
|
|
7
|
-
.replace(/^[ \t]*>[ \t]?/gm, '')
|
|
8
|
-
.replace(/^[ \t]*---+[ \t]*$/gm, '')
|
|
9
|
-
.replace(/^[ \t]*[-*+][ \t]+/gm, '• ')
|
|
10
|
-
.replace(/^[ \t]*\d+\.[ \t]+/gm, '')
|
|
11
|
-
.replace(/```[^\n]*\n?/g, '')
|
|
12
|
-
.replace(/`([^`]+)`/g, '$1')
|
|
13
|
-
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
14
|
-
.replace(/__([^_]+)__/g, '$1')
|
|
15
|
-
.replace(/\*([^*]+)\*/g, '$1')
|
|
16
|
-
.replace(/_([^_]+)_/g, '$1')
|
|
17
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
18
|
-
.trim();
|
|
1
|
+
//#region src/lib/version-notes.ts
|
|
2
|
+
function plainTextFromMarkdown(markdown) {
|
|
3
|
+
return markdown.replace(/\r\n/g, "\n").replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/^[ \t]*#{1,6}[ \t]*/gm, "").replace(/^[ \t]*>[ \t]?/gm, "").replace(/^[ \t]*---+[ \t]*$/gm, "").replace(/^[ \t]*[-*+][ \t]+/gm, "• ").replace(/^[ \t]*\d+\.[ \t]+/gm, "").replace(/```[^\n]*\n?/g, "").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/_([^_]+)_/g, "$1").replace(/\n{3,}/g, "\n\n").trim();
|
|
19
4
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
year: 'numeric',
|
|
30
|
-
}).format(date);
|
|
5
|
+
function formatReleaseDate(value) {
|
|
6
|
+
if (!value) return null;
|
|
7
|
+
const date = new Date(value);
|
|
8
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
9
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
10
|
+
day: "numeric",
|
|
11
|
+
month: "short",
|
|
12
|
+
year: "numeric"
|
|
13
|
+
}).format(date);
|
|
31
14
|
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { formatReleaseDate, plainTextFromMarkdown };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adea-ai/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Shared UI kit for Adea:
|
|
3
|
+
"version": "0.23.1",
|
|
4
|
+
"description": "Shared UI kit for Adea: Kobalte/corvu-backed Solid primitives, workspace shells, and theme styles.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"base-ui",
|
|
7
6
|
"components",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
7
|
+
"corvu",
|
|
8
|
+
"kobalte",
|
|
9
|
+
"solid",
|
|
10
10
|
"tailwind",
|
|
11
11
|
"workspace"
|
|
12
12
|
],
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
21
|
"src",
|
|
22
|
-
"components.json",
|
|
23
22
|
"tsconfig.json",
|
|
24
23
|
"README.md"
|
|
25
24
|
],
|
|
@@ -33,16 +32,19 @@
|
|
|
33
32
|
"exports": {
|
|
34
33
|
".": {
|
|
35
34
|
"types": "./dist/index.d.ts",
|
|
35
|
+
"solid": "./src/index.tsx",
|
|
36
36
|
"development": "./src/index.tsx",
|
|
37
37
|
"import": "./dist/index.js"
|
|
38
38
|
},
|
|
39
39
|
"./components/*": {
|
|
40
40
|
"types": "./dist/components/*.d.ts",
|
|
41
|
+
"solid": "./src/components/*",
|
|
41
42
|
"development": "./src/components/*",
|
|
42
43
|
"import": "./dist/components/*.js"
|
|
43
44
|
},
|
|
44
45
|
"./lib/*": {
|
|
45
46
|
"types": "./dist/lib/*.d.ts",
|
|
47
|
+
"solid": "./src/lib/*",
|
|
46
48
|
"development": "./src/lib/*",
|
|
47
49
|
"import": "./dist/lib/*.js"
|
|
48
50
|
},
|
|
@@ -50,27 +52,27 @@
|
|
|
50
52
|
"./auth-shell.css": "./src/styles/auth-shell.css",
|
|
51
53
|
"./conventional-workspace.css": "./src/styles/conventional-workspace.css",
|
|
52
54
|
"./theme.css": "./src/styles/theme.css",
|
|
53
|
-
"./workspace-shell.css": "./src/styles/workspace-shell.css"
|
|
55
|
+
"./workspace-shell.css": "./src/styles/workspace-shell.css",
|
|
56
|
+
"./base.css": "./src/styles/base.css"
|
|
54
57
|
},
|
|
55
58
|
"scripts": {
|
|
56
59
|
"lint": "oxlint",
|
|
57
|
-
"build": "tsc -p tsconfig.json",
|
|
58
|
-
"dev": "
|
|
60
|
+
"build": "vite build && tsc -p tsconfig.json --emitDeclarationOnly",
|
|
61
|
+
"dev": "vite build --watch",
|
|
59
62
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
60
63
|
},
|
|
61
64
|
"dependencies": {
|
|
62
|
-
"@
|
|
65
|
+
"@corvu/drawer": "^0.2.4",
|
|
66
|
+
"@kobalte/core": "^0.13.14",
|
|
63
67
|
"class-variance-authority": "^0.7.1",
|
|
64
68
|
"clsx": "^2.1.1",
|
|
65
|
-
"lucide-
|
|
66
|
-
"
|
|
67
|
-
"react": "19.3.0",
|
|
68
|
-
"react-dom": "19.3.0",
|
|
69
|
+
"lucide-solid": "^1.45.0",
|
|
70
|
+
"solid-js": "^1.9.15",
|
|
69
71
|
"tailwind-merge": "^3.6.0",
|
|
70
72
|
"tw-animate-css": "^1.4.0"
|
|
71
73
|
},
|
|
72
74
|
"devDependencies": {
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
+
"vite": "^8.0.14",
|
|
76
|
+
"vite-plugin-solid": "^2.11.14"
|
|
75
77
|
}
|
|
76
78
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { UserRound } from 'lucide-solid'
|
|
2
|
+
import { createEffect, createSignal, Show, type JSX } from 'solid-js'
|
|
3
|
+
import { Portal } from 'solid-js/web'
|
|
2
4
|
|
|
3
|
-
import {
|
|
4
|
-
import { useEffect, useState, type ReactNode } from 'react'
|
|
5
|
-
import { UserRound } from 'lucide-react'
|
|
6
|
-
import { Button } from '#components/ui/button'
|
|
5
|
+
import { buttonVariants } from '#components/ui/button'
|
|
7
6
|
import {
|
|
8
7
|
Drawer,
|
|
9
8
|
DrawerClose,
|
|
@@ -19,111 +18,93 @@ export type AccountDrawerProps = {
|
|
|
19
18
|
accountLabel?: string
|
|
20
19
|
authenticated?: boolean
|
|
21
20
|
busy?: boolean
|
|
22
|
-
musicControl?:
|
|
21
|
+
musicControl?: JSX.Element
|
|
23
22
|
triggerTargetId?: string
|
|
24
23
|
onSignIn?: () => void
|
|
25
24
|
onSignOut?: () => void
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
export function AccountDrawer({
|
|
29
|
-
accountLabel = 'Sign in'
|
|
30
|
-
|
|
31
|
-
busy = false,
|
|
32
|
-
musicControl,
|
|
33
|
-
triggerTargetId,
|
|
34
|
-
onSignIn,
|
|
35
|
-
onSignOut,
|
|
36
|
-
}: AccountDrawerProps) {
|
|
37
|
-
const [triggerTarget, setTriggerTarget] = useState<HTMLElement | null>(null)
|
|
27
|
+
export function AccountDrawer(props: AccountDrawerProps) {
|
|
28
|
+
const accountLabel = () => props.accountLabel ?? 'Sign in'
|
|
29
|
+
const [triggerTarget, setTriggerTarget] = createSignal<HTMLElement | null>(null)
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
setTriggerTarget(triggerTargetId ? document.getElementById(triggerTargetId) : null)
|
|
41
|
-
|
|
31
|
+
createEffect(() =>
|
|
32
|
+
setTriggerTarget(props.triggerTargetId ? document.getElementById(props.triggerTargetId) : null)
|
|
33
|
+
)
|
|
42
34
|
|
|
43
35
|
const trigger = (
|
|
44
36
|
<DrawerTrigger
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
size="sm"
|
|
49
|
-
className="workspace-account-trigger"
|
|
50
|
-
aria-label={`Open user menu for ${accountLabel}`}
|
|
51
|
-
aria-haspopup="dialog"
|
|
52
|
-
/>
|
|
53
|
-
}
|
|
37
|
+
class={buttonVariants({ variant: 'outline', size: 'sm', class: 'workspace-account-trigger' })}
|
|
38
|
+
aria-label={`Open user menu for ${accountLabel()}`}
|
|
39
|
+
aria-haspopup="dialog"
|
|
54
40
|
>
|
|
55
41
|
<UserRound aria-hidden="true" />
|
|
56
|
-
<span>{accountLabel}</span>
|
|
42
|
+
<span>{accountLabel()}</span>
|
|
57
43
|
</DrawerTrigger>
|
|
58
44
|
)
|
|
59
45
|
|
|
60
46
|
return (
|
|
61
47
|
<Drawer swipeDirection="right">
|
|
62
|
-
{triggerTarget
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<span className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground">
|
|
72
|
-
<UserRound className="size-5" aria-hidden="true" />
|
|
48
|
+
<Show when={triggerTarget()} fallback={<div class="fixed right-4 top-4 z-40">{trigger}</div>}>
|
|
49
|
+
<Portal mount={triggerTarget()!}>{trigger}</Portal>
|
|
50
|
+
</Show>
|
|
51
|
+
<DrawerContent class="w-[min(24rem,90vw)]">
|
|
52
|
+
<DrawerHeader class="border-b px-5 pb-4 pt-5 text-left">
|
|
53
|
+
<div class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4">
|
|
54
|
+
<div class="flex min-w-0 items-center gap-3">
|
|
55
|
+
<span class="flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground">
|
|
56
|
+
<UserRound class="size-5" aria-hidden="true" />
|
|
73
57
|
</span>
|
|
74
|
-
<div
|
|
75
|
-
<DrawerTitle
|
|
58
|
+
<div class="min-w-0">
|
|
59
|
+
<DrawerTitle class="truncate">{accountLabel()}</DrawerTitle>
|
|
76
60
|
<DrawerDescription>
|
|
77
|
-
{authenticated
|
|
61
|
+
{props.authenticated
|
|
78
62
|
? 'Signed in · workspace saved'
|
|
79
63
|
: 'Guest workspace · sign in anytime'}
|
|
80
64
|
</DrawerDescription>
|
|
81
65
|
</div>
|
|
82
66
|
</div>
|
|
83
|
-
<ThemeToggle
|
|
67
|
+
<ThemeToggle class="shrink-0" />
|
|
84
68
|
</div>
|
|
85
69
|
</DrawerHeader>
|
|
86
|
-
<div
|
|
87
|
-
{musicControl
|
|
70
|
+
<div class="flex min-h-0 flex-1 flex-col gap-6 overflow-y-auto p-5">
|
|
71
|
+
<Show when={props.musicControl}>
|
|
88
72
|
<section
|
|
89
|
-
|
|
73
|
+
class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-t pt-5"
|
|
90
74
|
aria-labelledby="account-music-title"
|
|
91
75
|
>
|
|
92
|
-
<div
|
|
93
|
-
<h2 id="account-music-title"
|
|
76
|
+
<div class="min-w-0">
|
|
77
|
+
<h2 id="account-music-title" class="text-sm font-semibold">
|
|
94
78
|
Music
|
|
95
79
|
</h2>
|
|
96
|
-
<p
|
|
80
|
+
<p class="text-sm text-muted-foreground">Control the workspace soundtrack.</p>
|
|
97
81
|
</div>
|
|
98
|
-
<div
|
|
82
|
+
<div class="flex shrink-0 items-center justify-end">{props.musicControl}</div>
|
|
99
83
|
</section>
|
|
100
|
-
|
|
84
|
+
</Show>
|
|
101
85
|
<section
|
|
102
|
-
|
|
86
|
+
class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-t pt-5"
|
|
103
87
|
aria-labelledby="account-session-title"
|
|
104
88
|
>
|
|
105
|
-
<div
|
|
106
|
-
<h2 id="account-session-title"
|
|
89
|
+
<div class="min-w-0">
|
|
90
|
+
<h2 id="account-session-title" class="text-sm font-semibold">
|
|
107
91
|
Account
|
|
108
92
|
</h2>
|
|
109
|
-
<p
|
|
110
|
-
{authenticated
|
|
93
|
+
<p class="text-sm text-muted-foreground">
|
|
94
|
+
{props.authenticated
|
|
111
95
|
? 'Sign out on this device without removing your saved workspace.'
|
|
112
96
|
: 'Sign in or create an account to keep this workspace across devices.'}
|
|
113
97
|
</p>
|
|
114
98
|
</div>
|
|
115
99
|
<DrawerClose
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
onClick={authenticated ? onSignOut : onSignIn}
|
|
123
|
-
/>
|
|
124
|
-
}
|
|
100
|
+
class={buttonVariants({
|
|
101
|
+
variant: props.authenticated ? 'outline' : 'default',
|
|
102
|
+
class: 'shrink-0',
|
|
103
|
+
})}
|
|
104
|
+
disabled={props.busy}
|
|
105
|
+
onClick={props.authenticated ? props.onSignOut : props.onSignIn}
|
|
125
106
|
>
|
|
126
|
-
{authenticated ? 'Sign out' : 'Sign in'}
|
|
107
|
+
{props.authenticated ? 'Sign out' : 'Sign in'}
|
|
127
108
|
</DrawerClose>
|
|
128
109
|
</section>
|
|
129
110
|
</div>
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { ZoomIn, ZoomOut } from 'lucide-solid'
|
|
2
|
+
import { onCleanup, Show, type JSX } from 'solid-js'
|
|
2
3
|
|
|
3
|
-
import { useCallback, useEffect, useRef } from 'react'
|
|
4
|
-
import type { PointerEvent, ReactNode } from 'react'
|
|
5
|
-
import { ZoomIn, ZoomOut } from 'lucide-react'
|
|
6
4
|
import { Button } from '#components/ui/button'
|
|
7
5
|
import { cn } from '#lib/utils'
|
|
6
|
+
|
|
8
7
|
type ControlButtonProps = {
|
|
9
8
|
code: string
|
|
10
9
|
label: string
|
|
11
|
-
children:
|
|
12
|
-
|
|
10
|
+
children: JSX.Element
|
|
11
|
+
class?: string
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
function sendKeyEvent(type: 'keydown' | 'keyup', code: string): void {
|
|
@@ -18,52 +17,49 @@ function sendKeyEvent(type: 'keydown' | 'keyup', code: string): void {
|
|
|
18
17
|
)
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
function ControlButton(
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
function ControlButton(props: ControlButtonProps) {
|
|
21
|
+
let pressed = false
|
|
22
|
+
let pressedPointer: number | null = null
|
|
24
23
|
|
|
25
|
-
const release =
|
|
26
|
-
if (!pressed
|
|
27
|
-
pressed
|
|
28
|
-
pressedPointer
|
|
29
|
-
sendKeyEvent('keyup', code)
|
|
30
|
-
}
|
|
24
|
+
const release = () => {
|
|
25
|
+
if (!pressed) return
|
|
26
|
+
pressed = false
|
|
27
|
+
pressedPointer = null
|
|
28
|
+
sendKeyEvent('keyup', props.code)
|
|
29
|
+
}
|
|
31
30
|
|
|
32
31
|
// Self-heal: release the key if the pointerup lands elsewhere (e.g. the
|
|
33
32
|
// scene DOM changes mid-teleport) or the window loses focus. PointerId
|
|
34
33
|
// matching preserves multi-touch.
|
|
35
|
-
|
|
36
|
-
const onWindowPointerUp = (event:
|
|
37
|
-
if (pressedPointer
|
|
34
|
+
if (typeof window !== 'undefined') {
|
|
35
|
+
const onWindowPointerUp = (event: PointerEvent) => {
|
|
36
|
+
if (pressedPointer === event.pointerId) release()
|
|
38
37
|
}
|
|
39
38
|
const onBlur = () => release()
|
|
40
39
|
window.addEventListener('pointerup', onWindowPointerUp)
|
|
41
40
|
window.addEventListener('blur', onBlur)
|
|
42
|
-
|
|
41
|
+
onCleanup(() => {
|
|
43
42
|
window.removeEventListener('pointerup', onWindowPointerUp)
|
|
44
43
|
window.removeEventListener('blur', onBlur)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
47
46
|
|
|
48
|
-
const press =
|
|
49
|
-
(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
[code]
|
|
58
|
-
)
|
|
47
|
+
const press = (event: PointerEvent & { currentTarget: HTMLButtonElement }) => {
|
|
48
|
+
event.preventDefault()
|
|
49
|
+
if (pressed) return
|
|
50
|
+
pressed = true
|
|
51
|
+
pressedPointer = event.pointerId
|
|
52
|
+
event.currentTarget.setPointerCapture(event.pointerId)
|
|
53
|
+
sendKeyEvent('keydown', props.code)
|
|
54
|
+
}
|
|
59
55
|
|
|
60
56
|
return (
|
|
61
57
|
<button
|
|
62
58
|
type="button"
|
|
63
|
-
aria-label={label}
|
|
64
|
-
|
|
59
|
+
aria-label={props.label}
|
|
60
|
+
class={cn(
|
|
65
61
|
'flex size-14 touch-none select-none items-center justify-center rounded-2xl border border-white/25 bg-slate-950/65 p-2 text-white shadow-lg backdrop-blur-sm transition active:scale-95 [-webkit-touch-callout:none] [-webkit-user-select:none]',
|
|
66
|
-
|
|
62
|
+
props.class
|
|
67
63
|
)}
|
|
68
64
|
onContextMenu={(event) => event.preventDefault()}
|
|
69
65
|
onPointerCancel={release}
|
|
@@ -71,7 +67,7 @@ function ControlButton({ code, label, children, className }: ControlButtonProps)
|
|
|
71
67
|
onPointerLeave={release}
|
|
72
68
|
onPointerUp={release}
|
|
73
69
|
>
|
|
74
|
-
{children}
|
|
70
|
+
{props.children}
|
|
75
71
|
</button>
|
|
76
72
|
)
|
|
77
73
|
}
|
|
@@ -83,41 +79,38 @@ export type OnScreenControlsProps = {
|
|
|
83
79
|
showJumpControl?: boolean
|
|
84
80
|
}
|
|
85
81
|
|
|
86
|
-
export function OnScreenControls({
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
showJumpControl = true,
|
|
91
|
-
}: OnScreenControlsProps = {}) {
|
|
92
|
-
const showZoomControls = Boolean(onZoomIn && onZoomOut)
|
|
82
|
+
export function OnScreenControls(props: OnScreenControlsProps) {
|
|
83
|
+
const showMovementControls = () => props.showMovementControls ?? true
|
|
84
|
+
const showJumpControl = () => props.showJumpControl ?? true
|
|
85
|
+
const showZoomControls = () => Boolean(props.onZoomIn && props.onZoomOut)
|
|
93
86
|
|
|
94
87
|
return (
|
|
95
88
|
<div
|
|
96
89
|
data-agent-hq-on-screen-controls
|
|
97
|
-
|
|
90
|
+
class="pointer-events-none fixed inset-x-4 bottom-4 z-30 flex select-none items-end justify-between gap-4 pb-[env(safe-area-inset-bottom)] sm:inset-x-6 sm:bottom-6 [-webkit-touch-callout:none] [-webkit-user-select:none]"
|
|
98
91
|
>
|
|
99
|
-
{showMovementControls
|
|
100
|
-
<div
|
|
92
|
+
<Show when={showMovementControls()}>
|
|
93
|
+
<div class="pointer-events-auto grid grid-cols-3 gap-1.5 rounded-3xl bg-slate-950/20 p-2 backdrop-blur-[2px]">
|
|
101
94
|
<span />
|
|
102
|
-
<ControlButton code="KeyW" label="Move forward"
|
|
95
|
+
<ControlButton code="KeyW" label="Move forward" class="size-12 rounded-xl text-xl">
|
|
103
96
|
▲
|
|
104
97
|
</ControlButton>
|
|
105
98
|
<span />
|
|
106
|
-
<ControlButton code="KeyA" label="Move left"
|
|
99
|
+
<ControlButton code="KeyA" label="Move left" class="size-12 rounded-xl text-xl">
|
|
107
100
|
◀
|
|
108
101
|
</ControlButton>
|
|
109
|
-
<ControlButton code="KeyS" label="Move backward"
|
|
102
|
+
<ControlButton code="KeyS" label="Move backward" class="size-12 rounded-xl text-xl">
|
|
110
103
|
▼
|
|
111
104
|
</ControlButton>
|
|
112
|
-
<ControlButton code="KeyD" label="Move right"
|
|
105
|
+
<ControlButton code="KeyD" label="Move right" class="size-12 rounded-xl text-xl">
|
|
113
106
|
▶
|
|
114
107
|
</ControlButton>
|
|
115
108
|
</div>
|
|
116
|
-
|
|
117
|
-
<div
|
|
118
|
-
{showZoomControls
|
|
109
|
+
</Show>
|
|
110
|
+
<div class="pointer-events-auto ml-auto flex flex-col items-end gap-2">
|
|
111
|
+
<Show when={showZoomControls()}>
|
|
119
112
|
<div
|
|
120
|
-
|
|
113
|
+
class="flex items-center gap-1.5 rounded-2xl bg-slate-950/20 p-1.5 backdrop-blur-[2px]"
|
|
121
114
|
role="group"
|
|
122
115
|
aria-label="Camera zoom"
|
|
123
116
|
>
|
|
@@ -127,8 +120,8 @@ export function OnScreenControls({
|
|
|
127
120
|
size="icon-lg"
|
|
128
121
|
aria-label="Zoom out"
|
|
129
122
|
title="Zoom out"
|
|
130
|
-
|
|
131
|
-
onClick={onZoomOut}
|
|
123
|
+
class="border border-white/25 bg-slate-950/65 text-white shadow-lg backdrop-blur-sm hover:bg-slate-900/80"
|
|
124
|
+
onClick={() => props.onZoomOut?.()}
|
|
132
125
|
>
|
|
133
126
|
<ZoomOut aria-hidden="true" />
|
|
134
127
|
</Button>
|
|
@@ -138,18 +131,18 @@ export function OnScreenControls({
|
|
|
138
131
|
size="icon-lg"
|
|
139
132
|
aria-label="Zoom in"
|
|
140
133
|
title="Zoom in"
|
|
141
|
-
|
|
142
|
-
onClick={onZoomIn}
|
|
134
|
+
class="border border-white/25 bg-slate-950/65 text-white shadow-lg backdrop-blur-sm hover:bg-slate-900/80"
|
|
135
|
+
onClick={() => props.onZoomIn?.()}
|
|
143
136
|
>
|
|
144
137
|
<ZoomIn aria-hidden="true" />
|
|
145
138
|
</Button>
|
|
146
139
|
</div>
|
|
147
|
-
|
|
148
|
-
{showJumpControl
|
|
149
|
-
<ControlButton code="Space" label="Jump"
|
|
140
|
+
</Show>
|
|
141
|
+
<Show when={showJumpControl()}>
|
|
142
|
+
<ControlButton code="Space" label="Jump" class="text-xs font-semibold">
|
|
150
143
|
JUMP
|
|
151
144
|
</ControlButton>
|
|
152
|
-
|
|
145
|
+
</Show>
|
|
153
146
|
</div>
|
|
154
147
|
</div>
|
|
155
148
|
)
|