@adea-ai/ui 0.12.0 → 0.13.0
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/LICENSE +201 -0
- package/dist/components/account-drawer.d.ts +1 -1
- package/dist/components/account-drawer.js +12 -12
- package/dist/components/on-screen-controls.js +12 -12
- package/dist/components/scene-settings.d.ts +3 -3
- package/dist/components/scene-settings.js +11 -11
- package/dist/components/theme-provider.d.ts +1 -1
- package/dist/components/theme-provider.js +2 -2
- package/dist/components/theme-toggle.js +8 -8
- package/dist/components/ui/badge.d.ts +3 -3
- package/dist/components/ui/badge.js +15 -15
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/button.js +19 -19
- package/dist/components/ui/card.d.ts +1 -1
- package/dist/components/ui/card.js +3 -3
- package/dist/components/ui/dialog.d.ts +4 -4
- package/dist/components/ui/dialog.js +10 -10
- package/dist/components/ui/drawer.d.ts +5 -5
- package/dist/components/ui/drawer.js +26 -26
- package/dist/components/ui/dropdown-menu.d.ts +5 -5
- package/dist/components/ui/dropdown-menu.js +12 -12
- package/dist/components/ui/empty.d.ts +7 -7
- package/dist/components/ui/empty.js +11 -11
- package/dist/components/ui/field.d.ts +12 -12
- package/dist/components/ui/field.js +22 -22
- package/dist/components/ui/input.d.ts +2 -2
- package/dist/components/ui/input.js +3 -3
- package/dist/components/ui/label.d.ts +2 -2
- package/dist/components/ui/label.js +3 -3
- package/dist/components/ui/radio-group.d.ts +2 -2
- package/dist/components/ui/radio-group.js +6 -6
- package/dist/components/ui/separator.d.ts +1 -1
- package/dist/components/ui/separator.js +5 -5
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/skeleton.js +2 -2
- package/dist/components/ui/spinner.d.ts +2 -2
- package/dist/components/ui/spinner.js +3 -3
- package/dist/components/ui/switch.d.ts +2 -2
- package/dist/components/ui/switch.js +5 -5
- package/dist/components/ui/tabs.d.ts +2 -2
- package/dist/components/ui/tabs.js +13 -13
- package/dist/components/ui/toggle-group.d.ts +6 -6
- package/dist/components/ui/toggle-group.js +13 -13
- package/dist/components/ui/toggle.d.ts +2 -2
- package/dist/components/ui/toggle.js +11 -11
- package/dist/components/ui/tooltip.d.ts +3 -3
- package/dist/components/ui/tooltip.js +5 -5
- package/dist/components/version-dialog.d.ts +1 -1
- package/dist/components/version-dialog.js +33 -33
- package/dist/components/workspace-brand.js +1 -1
- package/dist/components/workspace-logo.d.ts +1 -1
- package/dist/index.d.ts +19 -19
- package/dist/index.js +19 -19
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.js +2 -2
- package/dist/lib/version-notes.js +18 -18
- package/package.json +24 -9
- package/src/components/account-drawer.tsx +27 -27
- package/src/components/on-screen-controls.tsx +45 -45
- package/src/components/scene-settings.tsx +55 -55
- package/src/components/theme-provider.tsx +7 -7
- package/src/components/theme-toggle.tsx +20 -20
- package/src/components/ui/badge.tsx +20 -20
- package/src/components/ui/button.tsx +24 -24
- package/src/components/ui/card.tsx +6 -6
- package/src/components/ui/dialog.tsx +24 -24
- package/src/components/ui/drawer.tsx +55 -55
- package/src/components/ui/dropdown-menu.tsx +39 -39
- package/src/components/ui/empty.tsx +25 -25
- package/src/components/ui/field.tsx +55 -55
- package/src/components/ui/input.tsx +7 -7
- package/src/components/ui/label.tsx +7 -7
- package/src/components/ui/radio-group.tsx +9 -9
- package/src/components/ui/separator.tsx +7 -7
- package/src/components/ui/skeleton.tsx +5 -5
- package/src/components/ui/spinner.tsx +6 -6
- package/src/components/ui/switch.tsx +8 -8
- package/src/components/ui/tabs.tsx +21 -21
- package/src/components/ui/toggle-group.tsx +25 -25
- package/src/components/ui/toggle.tsx +15 -15
- package/src/components/ui/tooltip.tsx +13 -13
- package/src/components/version-dialog.tsx +113 -113
- package/src/components/workspace-brand.tsx +4 -4
- package/src/components/workspace-logo.tsx +2 -2
- package/src/index.tsx +19 -19
- package/src/lib/utils.ts +3 -3
- package/src/lib/version-notes.ts +23 -23
- package/src/styles/conventional-workspace.css +26 -26
- package/src/styles/globals.css +2 -2
- package/src/styles/theme.css +1 -1
- package/src/styles/workspace-shell.css +2 -2
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export function plainTextFromMarkdown(markdown) {
|
|
2
2
|
return markdown
|
|
3
|
-
.replace(/\r\n/g,
|
|
4
|
-
.replace(/!\[([^\]]*)\]\([^)]*\)/g,
|
|
5
|
-
.replace(/\[([^\]]+)\]\([^)]*\)/g,
|
|
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,
|
|
13
|
-
.replace(/\*\*([^*]+)\*\*/g,
|
|
14
|
-
.replace(/__([^_]+)__/g,
|
|
15
|
-
.replace(/\*([^*]+)\*/g,
|
|
16
|
-
.replace(/_([^_]+)_/g,
|
|
17
|
-
.replace(/\n{3,}/g,
|
|
3
|
+
.replace(/\r\n/g, '\n')
|
|
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
18
|
.trim();
|
|
19
19
|
}
|
|
20
20
|
export function formatReleaseDate(value) {
|
|
@@ -24,8 +24,8 @@ export function formatReleaseDate(value) {
|
|
|
24
24
|
if (Number.isNaN(date.getTime()))
|
|
25
25
|
return value;
|
|
26
26
|
return new Intl.DateTimeFormat(undefined, {
|
|
27
|
-
day:
|
|
28
|
-
month:
|
|
29
|
-
year:
|
|
27
|
+
day: 'numeric',
|
|
28
|
+
month: 'short',
|
|
29
|
+
year: 'numeric',
|
|
30
30
|
}).format(date);
|
|
31
31
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adea-ai/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Shared UI kit for Adea: Base UI-backed shadcn primitives, workspace shells, and theme styles.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"base-ui",
|
|
7
|
+
"components",
|
|
8
|
+
"react",
|
|
9
|
+
"shadcn",
|
|
10
|
+
"tailwind",
|
|
11
|
+
"workspace"
|
|
12
|
+
],
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/adea-ai/adea.git",
|
|
17
|
+
"directory": "packages/ui"
|
|
18
|
+
},
|
|
7
19
|
"files": [
|
|
8
20
|
"dist",
|
|
9
21
|
"src",
|
|
@@ -11,6 +23,13 @@
|
|
|
11
23
|
"tsconfig.json",
|
|
12
24
|
"README.md"
|
|
13
25
|
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"imports": {
|
|
30
|
+
"#components/*": "./src/components/*.tsx",
|
|
31
|
+
"#lib/*": "./src/lib/*.ts"
|
|
32
|
+
},
|
|
14
33
|
"exports": {
|
|
15
34
|
".": {
|
|
16
35
|
"types": "./dist/index.d.ts",
|
|
@@ -33,12 +52,8 @@
|
|
|
33
52
|
"./theme.css": "./src/styles/theme.css",
|
|
34
53
|
"./workspace-shell.css": "./src/styles/workspace-shell.css"
|
|
35
54
|
},
|
|
36
|
-
"imports": {
|
|
37
|
-
"#components/*": "./src/components/*.tsx",
|
|
38
|
-
"#lib/*": "./src/lib/*.ts"
|
|
39
|
-
},
|
|
40
55
|
"scripts": {
|
|
41
|
-
"lint": "
|
|
56
|
+
"lint": "oxlint",
|
|
42
57
|
"build": "tsc -p tsconfig.json",
|
|
43
58
|
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
|
|
44
59
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { createPortal } from
|
|
4
|
-
import { useEffect, useState, type ReactNode } from
|
|
5
|
-
import { UserRound } from
|
|
6
|
-
import { Button } from
|
|
3
|
+
import { createPortal } from 'react-dom'
|
|
4
|
+
import { useEffect, useState, type ReactNode } from 'react'
|
|
5
|
+
import { UserRound } from 'lucide-react'
|
|
6
|
+
import { Button } from '#components/ui/button'
|
|
7
7
|
import {
|
|
8
8
|
Drawer,
|
|
9
9
|
DrawerClose,
|
|
@@ -12,21 +12,21 @@ import {
|
|
|
12
12
|
DrawerHeader,
|
|
13
13
|
DrawerTitle,
|
|
14
14
|
DrawerTrigger,
|
|
15
|
-
} from
|
|
16
|
-
import { ThemeToggle } from
|
|
15
|
+
} from '#components/ui/drawer'
|
|
16
|
+
import { ThemeToggle } from './theme-toggle'
|
|
17
17
|
|
|
18
18
|
export type AccountDrawerProps = {
|
|
19
|
-
accountLabel?: string
|
|
20
|
-
authenticated?: boolean
|
|
21
|
-
busy?: boolean
|
|
22
|
-
musicControl?: ReactNode
|
|
23
|
-
triggerTargetId?: string
|
|
24
|
-
onSignIn?: () => void
|
|
25
|
-
onSignOut?: () => void
|
|
26
|
-
}
|
|
19
|
+
accountLabel?: string
|
|
20
|
+
authenticated?: boolean
|
|
21
|
+
busy?: boolean
|
|
22
|
+
musicControl?: ReactNode
|
|
23
|
+
triggerTargetId?: string
|
|
24
|
+
onSignIn?: () => void
|
|
25
|
+
onSignOut?: () => void
|
|
26
|
+
}
|
|
27
27
|
|
|
28
28
|
export function AccountDrawer({
|
|
29
|
-
accountLabel =
|
|
29
|
+
accountLabel = 'Sign in',
|
|
30
30
|
authenticated = false,
|
|
31
31
|
busy = false,
|
|
32
32
|
musicControl,
|
|
@@ -34,11 +34,11 @@ export function AccountDrawer({
|
|
|
34
34
|
onSignIn,
|
|
35
35
|
onSignOut,
|
|
36
36
|
}: AccountDrawerProps) {
|
|
37
|
-
const [triggerTarget, setTriggerTarget] = useState<HTMLElement | null>(null)
|
|
37
|
+
const [triggerTarget, setTriggerTarget] = useState<HTMLElement | null>(null)
|
|
38
38
|
|
|
39
39
|
useEffect(() => {
|
|
40
|
-
setTriggerTarget(triggerTargetId ? document.getElementById(triggerTargetId) : null)
|
|
41
|
-
}, [triggerTargetId])
|
|
40
|
+
setTriggerTarget(triggerTargetId ? document.getElementById(triggerTargetId) : null)
|
|
41
|
+
}, [triggerTargetId])
|
|
42
42
|
|
|
43
43
|
const trigger = (
|
|
44
44
|
<DrawerTrigger
|
|
@@ -55,7 +55,7 @@ export function AccountDrawer({
|
|
|
55
55
|
<UserRound aria-hidden="true" />
|
|
56
56
|
<span>{accountLabel}</span>
|
|
57
57
|
</DrawerTrigger>
|
|
58
|
-
)
|
|
58
|
+
)
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<Drawer swipeDirection="right">
|
|
@@ -75,8 +75,8 @@ export function AccountDrawer({
|
|
|
75
75
|
<DrawerTitle className="truncate">{accountLabel}</DrawerTitle>
|
|
76
76
|
<DrawerDescription>
|
|
77
77
|
{authenticated
|
|
78
|
-
?
|
|
79
|
-
:
|
|
78
|
+
? 'Signed in · workspace saved'
|
|
79
|
+
: 'Guest workspace · sign in anytime'}
|
|
80
80
|
</DrawerDescription>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
@@ -108,8 +108,8 @@ export function AccountDrawer({
|
|
|
108
108
|
</h2>
|
|
109
109
|
<p className="text-sm text-muted-foreground">
|
|
110
110
|
{authenticated
|
|
111
|
-
?
|
|
112
|
-
:
|
|
111
|
+
? 'Sign out on this device without removing your saved workspace.'
|
|
112
|
+
: 'Sign in or create an account to keep this workspace across devices.'}
|
|
113
113
|
</p>
|
|
114
114
|
</div>
|
|
115
115
|
<DrawerClose
|
|
@@ -117,17 +117,17 @@ export function AccountDrawer({
|
|
|
117
117
|
<Button
|
|
118
118
|
type="button"
|
|
119
119
|
className="shrink-0"
|
|
120
|
-
variant={authenticated ?
|
|
120
|
+
variant={authenticated ? 'outline' : 'default'}
|
|
121
121
|
disabled={busy}
|
|
122
122
|
onClick={authenticated ? onSignOut : onSignIn}
|
|
123
123
|
/>
|
|
124
124
|
}
|
|
125
125
|
>
|
|
126
|
-
{authenticated ?
|
|
126
|
+
{authenticated ? 'Sign out' : 'Sign in'}
|
|
127
127
|
</DrawerClose>
|
|
128
128
|
</section>
|
|
129
129
|
</div>
|
|
130
130
|
</DrawerContent>
|
|
131
131
|
</Drawer>
|
|
132
|
-
)
|
|
132
|
+
)
|
|
133
133
|
}
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect, useRef } from
|
|
4
|
-
import type { PointerEvent, ReactNode } from
|
|
5
|
-
import { ZoomIn, ZoomOut } from
|
|
6
|
-
import { Button } from
|
|
7
|
-
import { cn } from
|
|
3
|
+
import { useCallback, useEffect, useRef } from 'react'
|
|
4
|
+
import type { PointerEvent, ReactNode } from 'react'
|
|
5
|
+
import { ZoomIn, ZoomOut } from 'lucide-react'
|
|
6
|
+
import { Button } from '#components/ui/button'
|
|
7
|
+
import { cn } from '#lib/utils'
|
|
8
8
|
type ControlButtonProps = {
|
|
9
|
-
code: string
|
|
10
|
-
label: string
|
|
11
|
-
children: ReactNode
|
|
12
|
-
className?: string
|
|
13
|
-
}
|
|
9
|
+
code: string
|
|
10
|
+
label: string
|
|
11
|
+
children: ReactNode
|
|
12
|
+
className?: string
|
|
13
|
+
}
|
|
14
14
|
|
|
15
|
-
function sendKeyEvent(type:
|
|
15
|
+
function sendKeyEvent(type: 'keydown' | 'keyup', code: string): void {
|
|
16
16
|
window.dispatchEvent(
|
|
17
17
|
new KeyboardEvent(type, { bubbles: true, cancelable: true, code, key: code })
|
|
18
|
-
)
|
|
18
|
+
)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function ControlButton({ code, label, children, className }: ControlButtonProps) {
|
|
22
|
-
const pressed = useRef(false)
|
|
23
|
-
const pressedPointer = useRef<number | null>(null)
|
|
22
|
+
const pressed = useRef(false)
|
|
23
|
+
const pressedPointer = useRef<number | null>(null)
|
|
24
24
|
|
|
25
25
|
const release = useCallback(() => {
|
|
26
|
-
if (!pressed.current) return
|
|
27
|
-
pressed.current = false
|
|
28
|
-
pressedPointer.current = null
|
|
29
|
-
sendKeyEvent(
|
|
30
|
-
}, [code])
|
|
26
|
+
if (!pressed.current) return
|
|
27
|
+
pressed.current = false
|
|
28
|
+
pressedPointer.current = null
|
|
29
|
+
sendKeyEvent('keyup', code)
|
|
30
|
+
}, [code])
|
|
31
31
|
|
|
32
32
|
// Self-heal: release the key if the pointerup lands elsewhere (e.g. the
|
|
33
33
|
// scene DOM changes mid-teleport) or the window loses focus. PointerId
|
|
34
34
|
// matching preserves multi-touch.
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
const onWindowPointerUp = (event: globalThis.PointerEvent) => {
|
|
37
|
-
if (pressedPointer.current === event.pointerId) release()
|
|
38
|
-
}
|
|
39
|
-
const onBlur = () => release()
|
|
40
|
-
window.addEventListener(
|
|
41
|
-
window.addEventListener(
|
|
37
|
+
if (pressedPointer.current === event.pointerId) release()
|
|
38
|
+
}
|
|
39
|
+
const onBlur = () => release()
|
|
40
|
+
window.addEventListener('pointerup', onWindowPointerUp)
|
|
41
|
+
window.addEventListener('blur', onBlur)
|
|
42
42
|
return () => {
|
|
43
|
-
window.removeEventListener(
|
|
44
|
-
window.removeEventListener(
|
|
45
|
-
}
|
|
46
|
-
}, [release])
|
|
43
|
+
window.removeEventListener('pointerup', onWindowPointerUp)
|
|
44
|
+
window.removeEventListener('blur', onBlur)
|
|
45
|
+
}
|
|
46
|
+
}, [release])
|
|
47
47
|
|
|
48
48
|
const press = useCallback(
|
|
49
49
|
(event: PointerEvent<HTMLButtonElement>) => {
|
|
50
|
-
event.preventDefault()
|
|
51
|
-
if (pressed.current) return
|
|
52
|
-
pressed.current = true
|
|
53
|
-
pressedPointer.current = event.pointerId
|
|
54
|
-
event.currentTarget.setPointerCapture(event.pointerId)
|
|
55
|
-
sendKeyEvent(
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
if (pressed.current) return
|
|
52
|
+
pressed.current = true
|
|
53
|
+
pressedPointer.current = event.pointerId
|
|
54
|
+
event.currentTarget.setPointerCapture(event.pointerId)
|
|
55
|
+
sendKeyEvent('keydown', code)
|
|
56
56
|
},
|
|
57
57
|
[code]
|
|
58
|
-
)
|
|
58
|
+
)
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<button
|
|
62
62
|
type="button"
|
|
63
63
|
aria-label={label}
|
|
64
64
|
className={cn(
|
|
65
|
-
|
|
65
|
+
'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
66
|
className
|
|
67
67
|
)}
|
|
68
68
|
onContextMenu={(event) => event.preventDefault()}
|
|
@@ -73,15 +73,15 @@ function ControlButton({ code, label, children, className }: ControlButtonProps)
|
|
|
73
73
|
>
|
|
74
74
|
{children}
|
|
75
75
|
</button>
|
|
76
|
-
)
|
|
76
|
+
)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export type OnScreenControlsProps = {
|
|
80
|
-
onZoomIn?: () => void
|
|
81
|
-
onZoomOut?: () => void
|
|
82
|
-
showMovementControls?: boolean
|
|
83
|
-
showJumpControl?: boolean
|
|
84
|
-
}
|
|
80
|
+
onZoomIn?: () => void
|
|
81
|
+
onZoomOut?: () => void
|
|
82
|
+
showMovementControls?: boolean
|
|
83
|
+
showJumpControl?: boolean
|
|
84
|
+
}
|
|
85
85
|
|
|
86
86
|
export function OnScreenControls({
|
|
87
87
|
onZoomIn,
|
|
@@ -89,7 +89,7 @@ export function OnScreenControls({
|
|
|
89
89
|
showMovementControls = true,
|
|
90
90
|
showJumpControl = true,
|
|
91
91
|
}: OnScreenControlsProps = {}) {
|
|
92
|
-
const showZoomControls = Boolean(onZoomIn && onZoomOut)
|
|
92
|
+
const showZoomControls = Boolean(onZoomIn && onZoomOut)
|
|
93
93
|
|
|
94
94
|
return (
|
|
95
95
|
<div
|
|
@@ -152,5 +152,5 @@ export function OnScreenControls({
|
|
|
152
152
|
) : null}
|
|
153
153
|
</div>
|
|
154
154
|
</div>
|
|
155
|
-
)
|
|
155
|
+
)
|
|
156
156
|
}
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { createPortal } from
|
|
4
|
-
import { useEffect, useState, type ReactNode } from
|
|
5
|
-
import { Box, Camera, Focus, Grid3X3, UserRoundPen } from
|
|
6
|
-
import { AccountDrawer } from
|
|
7
|
-
import { Button } from
|
|
3
|
+
import { createPortal } from 'react-dom'
|
|
4
|
+
import { useEffect, useState, type ReactNode } from 'react'
|
|
5
|
+
import { Box, Camera, Focus, Grid3X3, UserRoundPen } from 'lucide-react'
|
|
6
|
+
import { AccountDrawer } from './account-drawer'
|
|
7
|
+
import { Button } from '#components/ui/button'
|
|
8
8
|
|
|
9
9
|
export type SceneSettingsProps = {
|
|
10
|
-
cameraViewMode:
|
|
11
|
-
onCameraViewModeChange: (value:
|
|
12
|
-
allowCameraViewModeChange?: boolean
|
|
10
|
+
cameraViewMode: 'perspective' | 'orthographic'
|
|
11
|
+
onCameraViewModeChange: (value: 'perspective' | 'orthographic') => void
|
|
12
|
+
allowCameraViewModeChange?: boolean
|
|
13
13
|
/** Standalone character selection and customization menu toggle. */
|
|
14
|
-
characterDesignerEnabled?: boolean
|
|
15
|
-
onCharacterDesignerChange?: (value: boolean) => void
|
|
14
|
+
characterDesignerEnabled?: boolean
|
|
15
|
+
onCharacterDesignerChange?: (value: boolean) => void
|
|
16
16
|
/** DOM target for the character designer control in an app shell toolbar. */
|
|
17
|
-
characterDesignerTargetId?: string
|
|
17
|
+
characterDesignerTargetId?: string
|
|
18
18
|
/** Open the scene-specific room layout and interior prop designer. */
|
|
19
|
-
onOpenRoomDesigner?: () => void
|
|
19
|
+
onOpenRoomDesigner?: () => void
|
|
20
20
|
/** DOM target for the account drawer trigger in an app shell toolbar. */
|
|
21
|
-
accountTargetId?: string
|
|
22
|
-
accountLabel?: string
|
|
23
|
-
accountAuthenticated?: boolean
|
|
24
|
-
accountBusy?: boolean
|
|
25
|
-
accountMusicControl?: ReactNode
|
|
26
|
-
onAccountSignIn?: () => void
|
|
27
|
-
onAccountSignOut?: () => void
|
|
28
|
-
showAccountDrawer?: boolean
|
|
21
|
+
accountTargetId?: string
|
|
22
|
+
accountLabel?: string
|
|
23
|
+
accountAuthenticated?: boolean
|
|
24
|
+
accountBusy?: boolean
|
|
25
|
+
accountMusicControl?: ReactNode
|
|
26
|
+
onAccountSignIn?: () => void
|
|
27
|
+
onAccountSignOut?: () => void
|
|
28
|
+
showAccountDrawer?: boolean
|
|
29
29
|
/** DOM target for the compact camera controls in an app shell toolbar. */
|
|
30
|
-
cameraTargetId?: string
|
|
30
|
+
cameraTargetId?: string
|
|
31
31
|
/** DOM target for the room designer control in an app shell toolbar. */
|
|
32
|
-
roomDesignerTargetId?: string
|
|
32
|
+
roomDesignerTargetId?: string
|
|
33
33
|
/** DOM target for the development scene editor control in an app shell toolbar. */
|
|
34
|
-
sceneEditorTargetId?: string
|
|
34
|
+
sceneEditorTargetId?: string
|
|
35
35
|
/** Development-only scene editor toggle, available in perspective view. */
|
|
36
|
-
sceneEditorEnabled?: boolean
|
|
37
|
-
onSceneEditorChange?: (value: boolean) => void
|
|
38
|
-
}
|
|
36
|
+
sceneEditorEnabled?: boolean
|
|
37
|
+
onSceneEditorChange?: (value: boolean) => void
|
|
38
|
+
}
|
|
39
39
|
|
|
40
40
|
function usePortalTarget(targetId?: string) {
|
|
41
|
-
const [target, setTarget] = useState<HTMLElement | null>(null)
|
|
41
|
+
const [target, setTarget] = useState<HTMLElement | null>(null)
|
|
42
42
|
|
|
43
43
|
useEffect(() => {
|
|
44
|
-
setTarget(targetId ? document.getElementById(targetId) : null)
|
|
45
|
-
}, [targetId])
|
|
44
|
+
setTarget(targetId ? document.getElementById(targetId) : null)
|
|
45
|
+
}, [targetId])
|
|
46
46
|
|
|
47
|
-
return target
|
|
47
|
+
return target
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function renderInTarget(content: ReactNode, target: HTMLElement | null) {
|
|
51
|
-
return target ? createPortal(content, target) : content
|
|
51
|
+
return target ? createPortal(content, target) : content
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/** Compact scene controls shared by the HQ shell and other scene hosts. */
|
|
@@ -74,22 +74,22 @@ export function SceneSettings({
|
|
|
74
74
|
sceneEditorEnabled,
|
|
75
75
|
onSceneEditorChange,
|
|
76
76
|
}: SceneSettingsProps) {
|
|
77
|
-
const cameraTarget = usePortalTarget(cameraTargetId)
|
|
78
|
-
const characterDesignerTarget = usePortalTarget(characterDesignerTargetId)
|
|
79
|
-
const roomDesignerTarget = usePortalTarget(roomDesignerTargetId)
|
|
80
|
-
const sceneEditorTarget = usePortalTarget(sceneEditorTargetId)
|
|
77
|
+
const cameraTarget = usePortalTarget(cameraTargetId)
|
|
78
|
+
const characterDesignerTarget = usePortalTarget(characterDesignerTargetId)
|
|
79
|
+
const roomDesignerTarget = usePortalTarget(roomDesignerTargetId)
|
|
80
|
+
const sceneEditorTarget = usePortalTarget(sceneEditorTargetId)
|
|
81
81
|
|
|
82
82
|
const cameraControl = (
|
|
83
83
|
<div className="workspace-camera-control" role="group" aria-label="Camera view">
|
|
84
84
|
<Button
|
|
85
85
|
type="button"
|
|
86
86
|
size="sm"
|
|
87
|
-
variant={cameraViewMode ===
|
|
87
|
+
variant={cameraViewMode === 'perspective' ? 'default' : 'outline'}
|
|
88
88
|
className="workspace-camera-button"
|
|
89
89
|
aria-label="Perspective camera"
|
|
90
90
|
title="Perspective camera"
|
|
91
|
-
aria-pressed={cameraViewMode ===
|
|
92
|
-
onClick={() => onCameraViewModeChange(
|
|
91
|
+
aria-pressed={cameraViewMode === 'perspective'}
|
|
92
|
+
onClick={() => onCameraViewModeChange('perspective')}
|
|
93
93
|
>
|
|
94
94
|
<Camera aria-hidden="true" />
|
|
95
95
|
Perspective
|
|
@@ -97,34 +97,34 @@ export function SceneSettings({
|
|
|
97
97
|
<Button
|
|
98
98
|
type="button"
|
|
99
99
|
size="sm"
|
|
100
|
-
variant={cameraViewMode ===
|
|
100
|
+
variant={cameraViewMode === 'orthographic' ? 'default' : 'outline'}
|
|
101
101
|
className="workspace-camera-button"
|
|
102
102
|
aria-label="Top-down camera"
|
|
103
103
|
title="Top-down camera"
|
|
104
|
-
aria-pressed={cameraViewMode ===
|
|
105
|
-
onClick={() => onCameraViewModeChange(
|
|
104
|
+
aria-pressed={cameraViewMode === 'orthographic'}
|
|
105
|
+
onClick={() => onCameraViewModeChange('orthographic')}
|
|
106
106
|
>
|
|
107
107
|
<Focus aria-hidden="true" />
|
|
108
108
|
Top-down
|
|
109
109
|
</Button>
|
|
110
110
|
</div>
|
|
111
|
-
)
|
|
111
|
+
)
|
|
112
112
|
|
|
113
113
|
const characterDesignerControl = (
|
|
114
114
|
<Button
|
|
115
115
|
type="button"
|
|
116
|
-
variant={characterDesignerEnabled ?
|
|
116
|
+
variant={characterDesignerEnabled ? 'default' : 'outline'}
|
|
117
117
|
size="sm"
|
|
118
118
|
aria-haspopup="dialog"
|
|
119
|
-
aria-label={characterDesignerEnabled ?
|
|
120
|
-
title={characterDesignerEnabled ?
|
|
119
|
+
aria-label={characterDesignerEnabled ? 'Close character designer' : 'Open character designer'}
|
|
120
|
+
title={characterDesignerEnabled ? 'Close character designer' : 'Open character designer'}
|
|
121
121
|
aria-pressed={characterDesignerEnabled}
|
|
122
122
|
onClick={() => onCharacterDesignerChange?.(!characterDesignerEnabled)}
|
|
123
123
|
>
|
|
124
124
|
<UserRoundPen className="size-4" aria-hidden="true" />
|
|
125
125
|
<span className="workspace-character-designer-label">Character</span>
|
|
126
126
|
</Button>
|
|
127
|
-
)
|
|
127
|
+
)
|
|
128
128
|
|
|
129
129
|
const roomDesignerControl = (
|
|
130
130
|
<Button
|
|
@@ -139,23 +139,23 @@ export function SceneSettings({
|
|
|
139
139
|
<Grid3X3 className="size-4" aria-hidden="true" />
|
|
140
140
|
<span className="workspace-room-designer-label">Room designer</span>
|
|
141
141
|
</Button>
|
|
142
|
-
)
|
|
142
|
+
)
|
|
143
143
|
|
|
144
144
|
const sceneEditorControl = (
|
|
145
145
|
<Button
|
|
146
146
|
type="button"
|
|
147
|
-
variant={sceneEditorEnabled ?
|
|
147
|
+
variant={sceneEditorEnabled ? 'default' : 'outline'}
|
|
148
148
|
size="sm"
|
|
149
149
|
aria-haspopup="dialog"
|
|
150
|
-
aria-label={sceneEditorEnabled ?
|
|
151
|
-
title={sceneEditorEnabled ?
|
|
150
|
+
aria-label={sceneEditorEnabled ? 'Close scene editor' : 'Open scene editor'}
|
|
151
|
+
title={sceneEditorEnabled ? 'Close scene editor' : 'Open scene editor'}
|
|
152
152
|
aria-pressed={sceneEditorEnabled}
|
|
153
153
|
onClick={() => onSceneEditorChange?.(!sceneEditorEnabled)}
|
|
154
154
|
>
|
|
155
155
|
<Box className="size-4" aria-hidden="true" />
|
|
156
156
|
<span className="workspace-scene-editor-label">Scene editor</span>
|
|
157
157
|
</Button>
|
|
158
|
-
)
|
|
158
|
+
)
|
|
159
159
|
|
|
160
160
|
return (
|
|
161
161
|
<>
|
|
@@ -180,7 +180,7 @@ export function SceneSettings({
|
|
|
180
180
|
<div className="fixed right-4 top-16 z-40">{characterDesignerControl}</div>
|
|
181
181
|
)
|
|
182
182
|
) : null}
|
|
183
|
-
{onOpenRoomDesigner && cameraViewMode ===
|
|
183
|
+
{onOpenRoomDesigner && cameraViewMode === 'orthographic' ? (
|
|
184
184
|
roomDesignerTarget ? (
|
|
185
185
|
createPortal(roomDesignerControl, roomDesignerTarget)
|
|
186
186
|
) : (
|
|
@@ -190,7 +190,7 @@ export function SceneSettings({
|
|
|
190
190
|
{sceneEditorEnabled != null &&
|
|
191
191
|
!sceneEditorEnabled &&
|
|
192
192
|
onSceneEditorChange &&
|
|
193
|
-
cameraViewMode ===
|
|
193
|
+
cameraViewMode === 'perspective' ? (
|
|
194
194
|
sceneEditorTarget ? (
|
|
195
195
|
createPortal(sceneEditorControl, sceneEditorTarget)
|
|
196
196
|
) : (
|
|
@@ -198,5 +198,5 @@ export function SceneSettings({
|
|
|
198
198
|
)
|
|
199
199
|
) : null}
|
|
200
200
|
</>
|
|
201
|
-
)
|
|
201
|
+
)
|
|
202
202
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { ThemeProvider as NextThemesProvider } from
|
|
4
|
-
import type { ComponentProps, ReactElement, ReactNode } from
|
|
3
|
+
import { ThemeProvider as NextThemesProvider } from 'next-themes'
|
|
4
|
+
import type { ComponentProps, ReactElement, ReactNode } from 'react'
|
|
5
5
|
|
|
6
6
|
// Bun 1.4 can resolve next-themes against a separate React type instance. The
|
|
7
7
|
// runtime component is compatible, but TypeScript then drops `children` from
|
|
8
8
|
// its JSX props. Keep the shared provider's public contract stable by
|
|
9
9
|
// normalizing the third-party component's props at this boundary.
|
|
10
10
|
type CompatibleNextThemesProviderProps = ComponentProps<typeof NextThemesProvider> & {
|
|
11
|
-
children?: ReactNode
|
|
12
|
-
}
|
|
11
|
+
children?: ReactNode
|
|
12
|
+
}
|
|
13
13
|
|
|
14
14
|
const CompatibleNextThemesProvider = NextThemesProvider as unknown as (
|
|
15
15
|
props: CompatibleNextThemesProviderProps
|
|
16
|
-
) => ReactElement
|
|
16
|
+
) => ReactElement
|
|
17
17
|
|
|
18
18
|
/** Shared theme provider used by each Next app in the workspace. */
|
|
19
19
|
export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
@@ -26,5 +26,5 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
|
26
26
|
>
|
|
27
27
|
{children}
|
|
28
28
|
</CompatibleNextThemesProvider>
|
|
29
|
-
)
|
|
29
|
+
)
|
|
30
30
|
}
|