@adea-ai/ui 0.22.2 → 0.23.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.
Files changed (92) hide show
  1. package/README.md +6 -3
  2. package/dist/components/account-drawer.d.ts +3 -3
  3. package/dist/components/account-drawer.js +119 -19
  4. package/dist/components/on-screen-controls.d.ts +2 -1
  5. package/dist/components/on-screen-controls.js +158 -46
  6. package/dist/components/scene-settings.d.ts +3 -3
  7. package/dist/components/scene-settings.js +247 -28
  8. package/dist/components/theme-provider.d.ts +27 -5
  9. package/dist/components/theme-provider.js +96 -7
  10. package/dist/components/theme-toggle.d.ts +4 -4
  11. package/dist/components/theme-toggle.js +62 -18
  12. package/dist/components/ui/badge.d.ts +4 -3
  13. package/dist/components/ui/badge.js +31 -30
  14. package/dist/components/ui/button.d.ts +4 -3
  15. package/dist/components/ui/button.js +49 -32
  16. package/dist/components/ui/card.d.ts +3 -3
  17. package/dist/components/ui/card.js +25 -6
  18. package/dist/components/ui/dialog.d.ts +13 -13
  19. package/dist/components/ui/dialog.js +94 -26
  20. package/dist/components/ui/drawer.d.ts +23 -15
  21. package/dist/components/ui/drawer.js +109 -64
  22. package/dist/components/ui/dropdown-menu.d.ts +28 -22
  23. package/dist/components/ui/dropdown-menu.js +165 -36
  24. package/dist/components/ui/empty.d.ts +7 -6
  25. package/dist/components/ui/empty.js +78 -26
  26. package/dist/components/ui/field.d.ts +15 -14
  27. package/dist/components/ui/field.js +177 -55
  28. package/dist/components/ui/input.d.ts +2 -2
  29. package/dist/components/ui/input.js +20 -5
  30. package/dist/components/ui/label.d.ts +2 -2
  31. package/dist/components/ui/label.js +15 -5
  32. package/dist/components/ui/radio-group.d.ts +4 -4
  33. package/dist/components/ui/radio-group.js +36 -9
  34. package/dist/components/ui/separator.d.ts +6 -2
  35. package/dist/components/ui/separator.js +18 -6
  36. package/dist/components/ui/skeleton.d.ts +2 -1
  37. package/dist/components/ui/skeleton.js +15 -4
  38. package/dist/components/ui/spinner.d.ts +4 -3
  39. package/dist/components/ui/spinner.js +15 -5
  40. package/dist/components/ui/switch.d.ts +6 -3
  41. package/dist/components/ui/switch.js +57 -6
  42. package/dist/components/ui/tabs.d.ts +6 -5
  43. package/dist/components/ui/tabs.js +52 -24
  44. package/dist/components/ui/toggle-group.d.ts +10 -8
  45. package/dist/components/ui/toggle-group.js +91 -20
  46. package/dist/components/ui/toggle.d.ts +4 -2
  47. package/dist/components/ui/toggle.js +38 -23
  48. package/dist/components/ui/tooltip.d.ts +13 -6
  49. package/dist/components/ui/tooltip.js +51 -14
  50. package/dist/components/version-dialog.d.ts +2 -2
  51. package/dist/components/version-dialog.js +322 -129
  52. package/dist/components/workspace-brand.d.ts +1 -1
  53. package/dist/components/workspace-brand.js +12 -3
  54. package/dist/components/workspace-logo.d.ts +2 -2
  55. package/dist/components/workspace-logo.js +15 -3
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.js +20 -19
  58. package/dist/lib/utils.js +7 -4
  59. package/dist/lib/version-notes.js +14 -29
  60. package/package.json +18 -16
  61. package/src/components/account-drawer.tsx +48 -67
  62. package/src/components/on-screen-controls.tsx +55 -62
  63. package/src/components/scene-settings.tsx +97 -100
  64. package/src/components/theme-provider.tsx +122 -22
  65. package/src/components/theme-toggle.tsx +29 -54
  66. package/src/components/ui/badge.tsx +14 -23
  67. package/src/components/ui/button.tsx +9 -11
  68. package/src/components/ui/card.tsx +9 -6
  69. package/src/components/ui/dialog.tsx +58 -55
  70. package/src/components/ui/drawer.tsx +84 -125
  71. package/src/components/ui/dropdown-menu.tsx +130 -153
  72. package/src/components/ui/empty.tsx +29 -26
  73. package/src/components/ui/field.tsx +83 -87
  74. package/src/components/ui/input.tsx +8 -8
  75. package/src/components/ui/label.tsx +6 -7
  76. package/src/components/ui/radio-group.tsx +22 -19
  77. package/src/components/ui/separator.tsx +16 -9
  78. package/src/components/ui/skeleton.tsx +6 -3
  79. package/src/components/ui/spinner.tsx +9 -5
  80. package/src/components/ui/switch.tsx +47 -20
  81. package/src/components/ui/tabs.tsx +31 -30
  82. package/src/components/ui/toggle-group.tsx +65 -54
  83. package/src/components/ui/toggle.tsx +9 -12
  84. package/src/components/ui/tooltip.tsx +41 -36
  85. package/src/components/version-dialog.tsx +144 -144
  86. package/src/components/workspace-brand.tsx +5 -5
  87. package/src/components/workspace-logo.tsx +9 -8
  88. package/src/index.tsx +7 -1
  89. package/src/styles/base.css +637 -0
  90. package/src/styles/globals.css +1 -1
  91. package/tsconfig.json +2 -1
  92. package/components.json +0 -19
package/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # Shared UI
2
2
 
3
- Reusable Base UI/shadcn primitives and React controls for the HQ shell, scene
4
- controls, room authoring, themes, drawers, loading states, and overlays. Scene
5
- branding and Three.js runtime behavior stay in their owning packages.
3
+ Reusable shadcn-style Solid controls for the HQ shell, scene controls, room
4
+ authoring, themes, drawers, loading states, and overlays. Primitives come from
5
+ Kobalte (headless) and corvu (the swipeable drawer); the component layer is one
6
+ Solid codebase. Scene branding and Three.js runtime behavior stay in their
7
+ owning packages. The primitive selection is recorded in
8
+ [decision 0007](../../docs/decisions/0007-solid-tanstack-start.md).
6
9
 
7
10
  ## Theming contract: CSS custom properties are the only color surface
8
11
 
@@ -1,11 +1,11 @@
1
- import { type ReactNode } from 'react';
1
+ import { type JSX } from 'solid-js';
2
2
  export type AccountDrawerProps = {
3
3
  accountLabel?: string;
4
4
  authenticated?: boolean;
5
5
  busy?: boolean;
6
- musicControl?: ReactNode;
6
+ musicControl?: JSX.Element;
7
7
  triggerTargetId?: string;
8
8
  onSignIn?: () => void;
9
9
  onSignOut?: () => void;
10
10
  };
11
- export declare function AccountDrawer({ accountLabel, authenticated, busy, musicControl, triggerTargetId, onSignIn, onSignOut, }: AccountDrawerProps): import("react").JSX.Element;
11
+ export declare function AccountDrawer(props: AccountDrawerProps): JSX.Element;
@@ -1,20 +1,120 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { createPortal } from 'react-dom';
4
- import { useEffect, useState } from 'react';
5
- import { UserRound } from 'lucide-react';
6
- import { Button } from '#components/ui/button';
7
- import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger, } from '#components/ui/drawer';
8
- import { ThemeToggle } from './theme-toggle';
9
- export function AccountDrawer({ accountLabel = 'Sign in', authenticated = false, busy = false, musicControl, triggerTargetId, onSignIn, onSignOut, }) {
10
- const [triggerTarget, setTriggerTarget] = useState(null);
11
- useEffect(() => {
12
- setTriggerTarget(triggerTargetId ? document.getElementById(triggerTargetId) : null);
13
- }, [triggerTargetId]);
14
- const trigger = (_jsxs(DrawerTrigger, { render: _jsx(Button, { variant: "outline", size: "sm", className: "workspace-account-trigger", "aria-label": `Open user menu for ${accountLabel}`, "aria-haspopup": "dialog" }), children: [_jsx(UserRound, { "aria-hidden": "true" }), _jsx("span", { children: accountLabel })] }));
15
- return (_jsxs(Drawer, { swipeDirection: "right", children: [triggerTarget ? (createPortal(trigger, triggerTarget)) : (_jsx("div", { className: "fixed right-4 top-4 z-40", children: trigger })), _jsxs(DrawerContent, { className: "w-[min(24rem,90vw)]", children: [_jsx(DrawerHeader, { className: "border-b px-5 pb-4 pt-5 text-left", children: _jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx("span", { className: "flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground", children: _jsx(UserRound, { className: "size-5", "aria-hidden": "true" }) }), _jsxs("div", { className: "min-w-0", children: [_jsx(DrawerTitle, { className: "truncate", children: accountLabel }), _jsx(DrawerDescription, { children: authenticated
16
- ? 'Signed in · workspace saved'
17
- : 'Guest workspace · sign in anytime' })] })] }), _jsx(ThemeToggle, { className: "shrink-0" })] }) }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-6 overflow-y-auto p-5", children: [musicControl ? (_jsxs("section", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-t pt-5", "aria-labelledby": "account-music-title", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { id: "account-music-title", className: "text-sm font-semibold", children: "Music" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Control the workspace soundtrack." })] }), _jsx("div", { className: "flex shrink-0 items-center justify-end", children: musicControl })] })) : null, _jsxs("section", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-t pt-5", "aria-labelledby": "account-session-title", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { id: "account-session-title", className: "text-sm font-semibold", children: "Account" }), _jsx("p", { className: "text-sm text-muted-foreground", children: authenticated
18
- ? 'Sign out on this device without removing your saved workspace.'
19
- : 'Sign in or create an account to keep this workspace across devices.' })] }), _jsx(DrawerClose, { render: _jsx(Button, { type: "button", className: "shrink-0", variant: authenticated ? 'outline' : 'default', disabled: busy, onClick: authenticated ? onSignOut : onSignIn }), children: authenticated ? 'Sign out' : 'Sign in' })] })] })] })] }));
1
+ import { buttonVariants } from "./ui/button.js";
2
+ import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger } from "./ui/drawer.js";
3
+ import { ThemeToggle } from "./theme-toggle.js";
4
+ import { Portal, createComponent, insert, memo, template } from "solid-js/web";
5
+ import { UserRound } from "lucide-solid";
6
+ import { Show, createEffect, createSignal } from "solid-js";
7
+ //#region src/components/account-drawer.tsx
8
+ var _tmpl$ = /*#__PURE__*/ template(`<span>`);
9
+ var _tmpl$2 = /*#__PURE__*/ template(`<div class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4"><div class="flex min-w-0 items-center gap-3"><span class="flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground"></span><div class=min-w-0>`);
10
+ var _tmpl$3 = /*#__PURE__*/ template(`<section class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-t pt-5"aria-labelledby=account-music-title><div class=min-w-0><h2 id=account-music-title class="text-sm font-semibold">Music</h2><p class="text-sm text-muted-foreground">Control the workspace soundtrack.</p></div><div class="flex shrink-0 items-center justify-end">`);
11
+ var _tmpl$4 = /*#__PURE__*/ template(`<div class="flex min-h-0 flex-1 flex-col gap-6 overflow-y-auto p-5"><section class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-t pt-5"aria-labelledby=account-session-title><div class=min-w-0><h2 id=account-session-title class="text-sm font-semibold">Account</h2><p class="text-sm text-muted-foreground">`);
12
+ var _tmpl$5 = /*#__PURE__*/ template(`<div class="fixed right-4 top-4 z-40">`);
13
+ function AccountDrawer(props) {
14
+ const accountLabel = () => props.accountLabel ?? "Sign in";
15
+ const [triggerTarget, setTriggerTarget] = createSignal(null);
16
+ createEffect(() => setTriggerTarget(props.triggerTargetId ? document.getElementById(props.triggerTargetId) : null));
17
+ const trigger = createComponent(DrawerTrigger, {
18
+ get ["class"]() {
19
+ return buttonVariants({
20
+ variant: "outline",
21
+ size: "sm",
22
+ class: "workspace-account-trigger"
23
+ });
24
+ },
25
+ get ["aria-label"]() {
26
+ return `Open user menu for ${accountLabel()}`;
27
+ },
28
+ "aria-haspopup": "dialog",
29
+ get children() {
30
+ return [createComponent(UserRound, { "aria-hidden": "true" }), (() => {
31
+ var _el$ = _tmpl$();
32
+ insert(_el$, accountLabel);
33
+ return _el$;
34
+ })()];
35
+ }
36
+ });
37
+ return createComponent(Drawer, {
38
+ swipeDirection: "right",
39
+ get children() {
40
+ return [createComponent(Show, {
41
+ get when() {
42
+ return triggerTarget();
43
+ },
44
+ get fallback() {
45
+ return (() => {
46
+ var _el$12 = _tmpl$5();
47
+ insert(_el$12, trigger);
48
+ return _el$12;
49
+ })();
50
+ },
51
+ get children() {
52
+ return createComponent(Portal, {
53
+ get mount() {
54
+ return triggerTarget();
55
+ },
56
+ children: trigger
57
+ });
58
+ }
59
+ }), createComponent(DrawerContent, {
60
+ "class": "w-[min(24rem,90vw)]",
61
+ get children() {
62
+ return [createComponent(DrawerHeader, {
63
+ "class": "border-b px-5 pb-4 pt-5 text-left",
64
+ get children() {
65
+ var _el$2 = _tmpl$2(), _el$4 = _el$2.firstChild.firstChild, _el$5 = _el$4.nextSibling;
66
+ insert(_el$4, createComponent(UserRound, {
67
+ "class": "size-5",
68
+ "aria-hidden": "true"
69
+ }));
70
+ insert(_el$5, createComponent(DrawerTitle, {
71
+ "class": "truncate",
72
+ get children() {
73
+ return accountLabel();
74
+ }
75
+ }), null);
76
+ insert(_el$5, createComponent(DrawerDescription, { get children() {
77
+ return props.authenticated ? "Signed in · workspace saved" : "Guest workspace · sign in anytime";
78
+ } }), null);
79
+ insert(_el$2, createComponent(ThemeToggle, { "class": "shrink-0" }), null);
80
+ return _el$2;
81
+ }
82
+ }), (() => {
83
+ var _el$6 = _tmpl$4(), _el$0 = _el$6.firstChild, _el$11 = _el$0.firstChild.firstChild.nextSibling;
84
+ insert(_el$6, createComponent(Show, {
85
+ get when() {
86
+ return props.musicControl;
87
+ },
88
+ get children() {
89
+ var _el$7 = _tmpl$3(), _el$9 = _el$7.firstChild.nextSibling;
90
+ insert(_el$9, () => props.musicControl);
91
+ return _el$7;
92
+ }
93
+ }), _el$0);
94
+ insert(_el$11, () => props.authenticated ? "Sign out on this device without removing your saved workspace." : "Sign in or create an account to keep this workspace across devices.");
95
+ insert(_el$0, createComponent(DrawerClose, {
96
+ get ["class"]() {
97
+ return buttonVariants({
98
+ variant: props.authenticated ? "outline" : "default",
99
+ class: "shrink-0"
100
+ });
101
+ },
102
+ get disabled() {
103
+ return props.busy;
104
+ },
105
+ get onClick() {
106
+ return memo(() => !!props.authenticated)() ? props.onSignOut : props.onSignIn;
107
+ },
108
+ get children() {
109
+ return props.authenticated ? "Sign out" : "Sign in";
110
+ }
111
+ }), null);
112
+ return _el$6;
113
+ })()];
114
+ }
115
+ })];
116
+ }
117
+ });
20
118
  }
119
+ //#endregion
120
+ export { AccountDrawer };
@@ -1,7 +1,8 @@
1
+ import { type JSX } from 'solid-js';
1
2
  export type OnScreenControlsProps = {
2
3
  onZoomIn?: () => void;
3
4
  onZoomOut?: () => void;
4
5
  showMovementControls?: boolean;
5
6
  showJumpControl?: boolean;
6
7
  };
7
- export declare function OnScreenControls({ onZoomIn, onZoomOut, showMovementControls, showJumpControl, }?: OnScreenControlsProps): import("react").JSX.Element;
8
+ export declare function OnScreenControls(props: OnScreenControlsProps): JSX.Element;
@@ -1,50 +1,162 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef } from 'react';
4
- import { ZoomIn, ZoomOut } from 'lucide-react';
5
- import { Button } from '#components/ui/button';
6
- import { cn } from '#lib/utils';
1
+ import { cn } from "../lib/utils.js";
2
+ import { Button } from "./ui/button.js";
3
+ import { className, createComponent, delegateEvents, effect, insert, setAttribute, template } from "solid-js/web";
4
+ import { ZoomIn, ZoomOut } from "lucide-solid";
5
+ import { Show, onCleanup } from "solid-js";
6
+ //#region src/components/on-screen-controls.tsx
7
+ var _tmpl$ = /*#__PURE__*/ template(`<button type=button>`);
8
+ var _tmpl$2 = /*#__PURE__*/ template(`<div class="pointer-events-auto grid grid-cols-3 gap-1.5 rounded-3xl bg-slate-950/20 p-2 backdrop-blur-[2px]"><span></span><span>`);
9
+ var _tmpl$3 = /*#__PURE__*/ template(`<div class="flex items-center gap-1.5 rounded-2xl bg-slate-950/20 p-1.5 backdrop-blur-[2px]"role=group aria-label="Camera zoom">`);
10
+ var _tmpl$4 = /*#__PURE__*/ template(`<div data-agent-hq-on-screen-controls 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]"><div class="pointer-events-auto ml-auto flex flex-col items-end gap-2">`);
7
11
  function sendKeyEvent(type, code) {
8
- window.dispatchEvent(new KeyboardEvent(type, { bubbles: true, cancelable: true, code, key: code }));
12
+ window.dispatchEvent(new KeyboardEvent(type, {
13
+ bubbles: true,
14
+ cancelable: true,
15
+ code,
16
+ key: code
17
+ }));
9
18
  }
10
- function ControlButton({ code, label, children, className }) {
11
- const pressed = useRef(false);
12
- const pressedPointer = useRef(null);
13
- const release = useCallback(() => {
14
- if (!pressed.current)
15
- return;
16
- pressed.current = false;
17
- pressedPointer.current = null;
18
- sendKeyEvent('keyup', code);
19
- }, [code]);
20
- // Self-heal: release the key if the pointerup lands elsewhere (e.g. the
21
- // scene DOM changes mid-teleport) or the window loses focus. PointerId
22
- // matching preserves multi-touch.
23
- useEffect(() => {
24
- const onWindowPointerUp = (event) => {
25
- if (pressedPointer.current === event.pointerId)
26
- release();
27
- };
28
- const onBlur = () => release();
29
- window.addEventListener('pointerup', onWindowPointerUp);
30
- window.addEventListener('blur', onBlur);
31
- return () => {
32
- window.removeEventListener('pointerup', onWindowPointerUp);
33
- window.removeEventListener('blur', onBlur);
34
- };
35
- }, [release]);
36
- const press = useCallback((event) => {
37
- event.preventDefault();
38
- if (pressed.current)
39
- return;
40
- pressed.current = true;
41
- pressedPointer.current = event.pointerId;
42
- event.currentTarget.setPointerCapture(event.pointerId);
43
- sendKeyEvent('keydown', code);
44
- }, [code]);
45
- return (_jsx("button", { type: "button", "aria-label": label, className: cn('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]', className), onContextMenu: (event) => event.preventDefault(), onPointerCancel: release, onPointerDown: press, onPointerLeave: release, onPointerUp: release, children: children }));
19
+ function ControlButton(props) {
20
+ let pressed = false;
21
+ let pressedPointer = null;
22
+ const release = () => {
23
+ if (!pressed) return;
24
+ pressed = false;
25
+ pressedPointer = null;
26
+ sendKeyEvent("keyup", props.code);
27
+ };
28
+ if (typeof window !== "undefined") {
29
+ const onWindowPointerUp = (event) => {
30
+ if (pressedPointer === event.pointerId) release();
31
+ };
32
+ const onBlur = () => release();
33
+ window.addEventListener("pointerup", onWindowPointerUp);
34
+ window.addEventListener("blur", onBlur);
35
+ onCleanup(() => {
36
+ window.removeEventListener("pointerup", onWindowPointerUp);
37
+ window.removeEventListener("blur", onBlur);
38
+ });
39
+ }
40
+ const press = (event) => {
41
+ event.preventDefault();
42
+ if (pressed) return;
43
+ pressed = true;
44
+ pressedPointer = event.pointerId;
45
+ event.currentTarget.setPointerCapture(event.pointerId);
46
+ sendKeyEvent("keydown", props.code);
47
+ };
48
+ return (() => {
49
+ var _el$ = _tmpl$();
50
+ _el$.$$pointerup = release;
51
+ _el$.addEventListener("pointerleave", release);
52
+ _el$.$$pointerdown = press;
53
+ _el$.addEventListener("pointercancel", release);
54
+ _el$.$$contextmenu = (event) => event.preventDefault();
55
+ insert(_el$, () => props.children);
56
+ effect((_p$) => {
57
+ var _v$ = props.label, _v$2 = cn("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]", props.class);
58
+ _v$ !== _p$.e && setAttribute(_el$, "aria-label", _p$.e = _v$);
59
+ _v$2 !== _p$.t && className(_el$, _p$.t = _v$2);
60
+ return _p$;
61
+ }, {
62
+ e: void 0,
63
+ t: void 0
64
+ });
65
+ return _el$;
66
+ })();
46
67
  }
47
- export function OnScreenControls({ onZoomIn, onZoomOut, showMovementControls = true, showJumpControl = true, } = {}) {
48
- const showZoomControls = Boolean(onZoomIn && onZoomOut);
49
- return (_jsxs("div", { "data-agent-hq-on-screen-controls": true, className: "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]", children: [showMovementControls ? (_jsxs("div", { className: "pointer-events-auto grid grid-cols-3 gap-1.5 rounded-3xl bg-slate-950/20 p-2 backdrop-blur-[2px]", children: [_jsx("span", {}), _jsx(ControlButton, { code: "KeyW", label: "Move forward", className: "size-12 rounded-xl text-xl", children: "\u25B2" }), _jsx("span", {}), _jsx(ControlButton, { code: "KeyA", label: "Move left", className: "size-12 rounded-xl text-xl", children: "\u25C0" }), _jsx(ControlButton, { code: "KeyS", label: "Move backward", className: "size-12 rounded-xl text-xl", children: "\u25BC" }), _jsx(ControlButton, { code: "KeyD", label: "Move right", className: "size-12 rounded-xl text-xl", children: "\u25B6" })] })) : null, _jsxs("div", { className: "pointer-events-auto ml-auto flex flex-col items-end gap-2", children: [showZoomControls ? (_jsxs("div", { className: "flex items-center gap-1.5 rounded-2xl bg-slate-950/20 p-1.5 backdrop-blur-[2px]", role: "group", "aria-label": "Camera zoom", children: [_jsx(Button, { type: "button", variant: "secondary", size: "icon-lg", "aria-label": "Zoom out", title: "Zoom out", className: "border border-white/25 bg-slate-950/65 text-white shadow-lg backdrop-blur-sm hover:bg-slate-900/80", onClick: onZoomOut, children: _jsx(ZoomOut, { "aria-hidden": "true" }) }), _jsx(Button, { type: "button", variant: "secondary", size: "icon-lg", "aria-label": "Zoom in", title: "Zoom in", className: "border border-white/25 bg-slate-950/65 text-white shadow-lg backdrop-blur-sm hover:bg-slate-900/80", onClick: onZoomIn, children: _jsx(ZoomIn, { "aria-hidden": "true" }) })] })) : null, showJumpControl ? (_jsx(ControlButton, { code: "Space", label: "Jump", className: "text-xs font-semibold", children: "JUMP" })) : null] })] }));
68
+ function OnScreenControls(props) {
69
+ const showMovementControls = () => props.showMovementControls ?? true;
70
+ const showJumpControl = () => props.showJumpControl ?? true;
71
+ const showZoomControls = () => Boolean(props.onZoomIn && props.onZoomOut);
72
+ return (() => {
73
+ var _el$2 = _tmpl$4(), _el$6 = _el$2.firstChild;
74
+ insert(_el$2, createComponent(Show, {
75
+ get when() {
76
+ return showMovementControls();
77
+ },
78
+ get children() {
79
+ var _el$3 = _tmpl$2(), _el$5 = _el$3.firstChild.nextSibling;
80
+ insert(_el$3, createComponent(ControlButton, {
81
+ code: "KeyW",
82
+ label: "Move forward",
83
+ "class": "size-12 rounded-xl text-xl",
84
+ children: "▲"
85
+ }), _el$5);
86
+ insert(_el$3, createComponent(ControlButton, {
87
+ code: "KeyA",
88
+ label: "Move left",
89
+ "class": "size-12 rounded-xl text-xl",
90
+ children: "◀"
91
+ }), null);
92
+ insert(_el$3, createComponent(ControlButton, {
93
+ code: "KeyS",
94
+ label: "Move backward",
95
+ "class": "size-12 rounded-xl text-xl",
96
+ children: "▼"
97
+ }), null);
98
+ insert(_el$3, createComponent(ControlButton, {
99
+ code: "KeyD",
100
+ label: "Move right",
101
+ "class": "size-12 rounded-xl text-xl",
102
+ children: "▶"
103
+ }), null);
104
+ return _el$3;
105
+ }
106
+ }), _el$6);
107
+ insert(_el$6, createComponent(Show, {
108
+ get when() {
109
+ return showZoomControls();
110
+ },
111
+ get children() {
112
+ var _el$7 = _tmpl$3();
113
+ insert(_el$7, createComponent(Button, {
114
+ type: "button",
115
+ variant: "secondary",
116
+ size: "icon-lg",
117
+ "aria-label": "Zoom out",
118
+ title: "Zoom out",
119
+ "class": "border border-white/25 bg-slate-950/65 text-white shadow-lg backdrop-blur-sm hover:bg-slate-900/80",
120
+ onClick: () => props.onZoomOut?.(),
121
+ get children() {
122
+ return createComponent(ZoomOut, { "aria-hidden": "true" });
123
+ }
124
+ }), null);
125
+ insert(_el$7, createComponent(Button, {
126
+ type: "button",
127
+ variant: "secondary",
128
+ size: "icon-lg",
129
+ "aria-label": "Zoom in",
130
+ title: "Zoom in",
131
+ "class": "border border-white/25 bg-slate-950/65 text-white shadow-lg backdrop-blur-sm hover:bg-slate-900/80",
132
+ onClick: () => props.onZoomIn?.(),
133
+ get children() {
134
+ return createComponent(ZoomIn, { "aria-hidden": "true" });
135
+ }
136
+ }), null);
137
+ return _el$7;
138
+ }
139
+ }), null);
140
+ insert(_el$6, createComponent(Show, {
141
+ get when() {
142
+ return showJumpControl();
143
+ },
144
+ get children() {
145
+ return createComponent(ControlButton, {
146
+ code: "Space",
147
+ label: "Jump",
148
+ "class": "text-xs font-semibold",
149
+ children: "JUMP"
150
+ });
151
+ }
152
+ }), null);
153
+ return _el$2;
154
+ })();
50
155
  }
156
+ delegateEvents([
157
+ "contextmenu",
158
+ "pointerdown",
159
+ "pointerup"
160
+ ]);
161
+ //#endregion
162
+ export { OnScreenControls };
@@ -1,4 +1,4 @@
1
- import { type ReactNode } from 'react';
1
+ import { type JSX } from 'solid-js';
2
2
  export type SceneSettingsProps = {
3
3
  cameraViewMode: 'perspective' | 'orthographic';
4
4
  onCameraViewModeChange: (value: 'perspective' | 'orthographic') => void;
@@ -15,7 +15,7 @@ export type SceneSettingsProps = {
15
15
  accountLabel?: string;
16
16
  accountAuthenticated?: boolean;
17
17
  accountBusy?: boolean;
18
- accountMusicControl?: ReactNode;
18
+ accountMusicControl?: JSX.Element;
19
19
  onAccountSignIn?: () => void;
20
20
  onAccountSignOut?: () => void;
21
21
  showAccountDrawer?: boolean;
@@ -30,4 +30,4 @@ export type SceneSettingsProps = {
30
30
  onSceneEditorChange?: (value: boolean) => void;
31
31
  };
32
32
  /** Compact scene controls shared by the HQ shell and other scene hosts. */
33
- export declare function SceneSettings({ cameraViewMode, onCameraViewModeChange, allowCameraViewModeChange, characterDesignerEnabled, onCharacterDesignerChange, characterDesignerTargetId, onOpenRoomDesigner, accountTargetId, accountLabel, accountAuthenticated, accountBusy, accountMusicControl, onAccountSignIn, onAccountSignOut, showAccountDrawer, cameraTargetId, roomDesignerTargetId, sceneEditorTargetId, sceneEditorEnabled, onSceneEditorChange, }: SceneSettingsProps): import("react").JSX.Element;
33
+ export declare function SceneSettings(props: SceneSettingsProps): JSX.Element;