@moldable-ai/ui 0.2.18 → 0.2.19

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 (228) hide show
  1. package/dist/components/chat/chat-panel.d.ts +18 -12
  2. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  3. package/dist/components/chat/chat-panel.js +73 -19
  4. package/dist/components/chat/index.d.ts +2 -1
  5. package/dist/components/chat/index.d.ts.map +1 -1
  6. package/dist/components/chat/index.js +2 -1
  7. package/dist/components/chat/model-effort-power-effects.d.ts +7 -0
  8. package/dist/components/chat/model-effort-power-effects.d.ts.map +1 -0
  9. package/dist/components/chat/model-effort-power-effects.js +54 -0
  10. package/dist/components/chat/model-effort-selector.d.ts +26 -0
  11. package/dist/components/chat/model-effort-selector.d.ts.map +1 -0
  12. package/dist/components/chat/model-effort-selector.js +179 -0
  13. package/dist/components/chat/model-effort-ultra-effects.d.ts +5 -0
  14. package/dist/components/chat/model-effort-ultra-effects.d.ts.map +1 -0
  15. package/dist/components/chat/model-effort-ultra-effects.js +56 -0
  16. package/dist/components/hardware/bluetooth-panel-helpers.d.ts +9 -0
  17. package/dist/components/hardware/bluetooth-panel-helpers.d.ts.map +1 -0
  18. package/dist/components/hardware/bluetooth-panel-helpers.js +37 -0
  19. package/dist/components/hardware/bluetooth-panel.d.ts +56 -0
  20. package/dist/components/hardware/bluetooth-panel.d.ts.map +1 -0
  21. package/dist/components/hardware/bluetooth-panel.js +470 -0
  22. package/dist/components/hardware/camera-preview.d.ts +39 -0
  23. package/dist/components/hardware/camera-preview.d.ts.map +1 -0
  24. package/dist/components/hardware/camera-preview.js +228 -0
  25. package/dist/components/hardware/capability-badge.d.ts +23 -0
  26. package/dist/components/hardware/capability-badge.d.ts.map +1 -0
  27. package/dist/components/hardware/capability-badge.js +39 -0
  28. package/dist/components/hardware/capability-matrix.d.ts +14 -0
  29. package/dist/components/hardware/capability-matrix.d.ts.map +1 -0
  30. package/dist/components/hardware/capability-matrix.js +97 -0
  31. package/dist/components/hardware/clipboard-button.d.ts +26 -0
  32. package/dist/components/hardware/clipboard-button.d.ts.map +1 -0
  33. package/dist/components/hardware/clipboard-button.js +114 -0
  34. package/dist/components/hardware/device-io-controls.d.ts +4 -0
  35. package/dist/components/hardware/device-io-controls.d.ts.map +1 -0
  36. package/dist/components/hardware/device-io-controls.js +28 -0
  37. package/dist/components/hardware/device-list.d.ts +20 -0
  38. package/dist/components/hardware/device-list.d.ts.map +1 -0
  39. package/dist/components/hardware/device-list.js +14 -0
  40. package/dist/components/hardware/display-map.d.ts +23 -0
  41. package/dist/components/hardware/display-map.d.ts.map +1 -0
  42. package/dist/components/hardware/display-map.js +114 -0
  43. package/dist/components/hardware/haptic-button.d.ts +26 -0
  44. package/dist/components/hardware/haptic-button.d.ts.map +1 -0
  45. package/dist/components/hardware/haptic-button.js +94 -0
  46. package/dist/components/hardware/hardware-panel.d.ts +11 -0
  47. package/dist/components/hardware/hardware-panel.d.ts.map +1 -0
  48. package/dist/components/hardware/hardware-panel.js +28 -0
  49. package/dist/components/hardware/hid-panel.d.ts +40 -0
  50. package/dist/components/hardware/hid-panel.d.ts.map +1 -0
  51. package/dist/components/hardware/hid-panel.js +373 -0
  52. package/dist/components/hardware/index.d.ts +27 -0
  53. package/dist/components/hardware/index.d.ts.map +1 -0
  54. package/dist/components/hardware/index.js +26 -0
  55. package/dist/components/hardware/live-indicator.d.ts +15 -0
  56. package/dist/components/hardware/live-indicator.d.ts.map +1 -0
  57. package/dist/components/hardware/live-indicator.js +26 -0
  58. package/dist/components/hardware/local-auth-button.d.ts +33 -0
  59. package/dist/components/hardware/local-auth-button.d.ts.map +1 -0
  60. package/dist/components/hardware/local-auth-button.js +115 -0
  61. package/dist/components/hardware/location-panel.d.ts +29 -0
  62. package/dist/components/hardware/location-panel.d.ts.map +1 -0
  63. package/dist/components/hardware/location-panel.js +154 -0
  64. package/dist/components/hardware/microphone-meter.d.ts +35 -0
  65. package/dist/components/hardware/microphone-meter.d.ts.map +1 -0
  66. package/dist/components/hardware/microphone-meter.js +244 -0
  67. package/dist/components/hardware/midi-monitor.d.ts +54 -0
  68. package/dist/components/hardware/midi-monitor.d.ts.map +1 -0
  69. package/dist/components/hardware/midi-monitor.js +534 -0
  70. package/dist/components/hardware/notification-panel.d.ts +24 -0
  71. package/dist/components/hardware/notification-panel.d.ts.map +1 -0
  72. package/dist/components/hardware/notification-panel.js +124 -0
  73. package/dist/components/hardware/power-blocker-controller.d.ts +19 -0
  74. package/dist/components/hardware/power-blocker-controller.d.ts.map +1 -0
  75. package/dist/components/hardware/power-blocker-controller.js +83 -0
  76. package/dist/components/hardware/power-panel.d.ts +67 -0
  77. package/dist/components/hardware/power-panel.d.ts.map +1 -0
  78. package/dist/components/hardware/power-panel.js +309 -0
  79. package/dist/components/hardware/readout.d.ts +10 -0
  80. package/dist/components/hardware/readout.d.ts.map +1 -0
  81. package/dist/components/hardware/readout.js +11 -0
  82. package/dist/components/hardware/screen-share-preview.d.ts +36 -0
  83. package/dist/components/hardware/screen-share-preview.d.ts.map +1 -0
  84. package/dist/components/hardware/screen-share-preview.js +191 -0
  85. package/dist/components/hardware/secure-storage-panel.d.ts +36 -0
  86. package/dist/components/hardware/secure-storage-panel.d.ts.map +1 -0
  87. package/dist/components/hardware/secure-storage-panel.js +193 -0
  88. package/dist/components/hardware/serial-console.d.ts +64 -0
  89. package/dist/components/hardware/serial-console.d.ts.map +1 -0
  90. package/dist/components/hardware/serial-console.js +399 -0
  91. package/dist/components/hardware/shortcut-recorder.d.ts +35 -0
  92. package/dist/components/hardware/shortcut-recorder.d.ts.map +1 -0
  93. package/dist/components/hardware/shortcut-recorder.js +217 -0
  94. package/dist/components/hardware/stream-log.d.ts +35 -0
  95. package/dist/components/hardware/stream-log.d.ts.map +1 -0
  96. package/dist/components/hardware/stream-log.js +65 -0
  97. package/dist/components/hardware/system-audio-monitor.d.ts +34 -0
  98. package/dist/components/hardware/system-audio-monitor.d.ts.map +1 -0
  99. package/dist/components/hardware/system-audio-monitor.js +275 -0
  100. package/dist/components/hardware/usb-panel.d.ts +54 -0
  101. package/dist/components/hardware/usb-panel.d.ts.map +1 -0
  102. package/dist/components/hardware/usb-panel.js +363 -0
  103. package/dist/components/hardware/use-capability.d.ts +20 -0
  104. package/dist/components/hardware/use-capability.d.ts.map +1 -0
  105. package/dist/components/hardware/use-capability.js +61 -0
  106. package/dist/components/ui/input-group.d.ts +1 -1
  107. package/dist/components/ui/slider.d.ts +4 -1
  108. package/dist/components/ui/slider.d.ts.map +1 -1
  109. package/dist/components/ui/slider.js +2 -2
  110. package/dist/index.d.ts +2 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +4 -0
  113. package/dist/lib/native-hardware/bluetooth-access.d.ts +14 -0
  114. package/dist/lib/native-hardware/bluetooth-access.d.ts.map +1 -0
  115. package/dist/lib/native-hardware/bluetooth-access.js +40 -0
  116. package/dist/lib/native-hardware/bluetooth-connection.d.ts +19 -0
  117. package/dist/lib/native-hardware/bluetooth-connection.d.ts.map +1 -0
  118. package/dist/lib/native-hardware/bluetooth-connection.js +88 -0
  119. package/dist/lib/native-hardware/bluetooth-gatt.d.ts +7 -0
  120. package/dist/lib/native-hardware/bluetooth-gatt.d.ts.map +1 -0
  121. package/dist/lib/native-hardware/bluetooth-gatt.js +77 -0
  122. package/dist/lib/native-hardware/bluetooth-internal.d.ts +10 -0
  123. package/dist/lib/native-hardware/bluetooth-internal.d.ts.map +1 -0
  124. package/dist/lib/native-hardware/bluetooth-internal.js +105 -0
  125. package/dist/lib/native-hardware/bluetooth-native-fallback.d.ts +33 -0
  126. package/dist/lib/native-hardware/bluetooth-native-fallback.d.ts.map +1 -0
  127. package/dist/lib/native-hardware/bluetooth-native-fallback.js +214 -0
  128. package/dist/lib/native-hardware/bluetooth-types.d.ts +85 -0
  129. package/dist/lib/native-hardware/bluetooth-types.d.ts.map +1 -0
  130. package/dist/lib/native-hardware/bluetooth-types.js +2 -0
  131. package/dist/lib/native-hardware/bluetooth.d.ts +7 -0
  132. package/dist/lib/native-hardware/bluetooth.d.ts.map +1 -0
  133. package/dist/lib/native-hardware/bluetooth.js +4 -0
  134. package/dist/lib/native-hardware/bridge.d.ts +23 -0
  135. package/dist/lib/native-hardware/bridge.d.ts.map +1 -0
  136. package/dist/lib/native-hardware/bridge.js +266 -0
  137. package/dist/lib/native-hardware/capabilities.d.ts +7 -0
  138. package/dist/lib/native-hardware/capabilities.d.ts.map +1 -0
  139. package/dist/lib/native-hardware/capabilities.js +95 -0
  140. package/dist/lib/native-hardware/clipboard.d.ts +11 -0
  141. package/dist/lib/native-hardware/clipboard.d.ts.map +1 -0
  142. package/dist/lib/native-hardware/clipboard.js +55 -0
  143. package/dist/lib/native-hardware/device-io-errors.d.ts +11 -0
  144. package/dist/lib/native-hardware/device-io-errors.d.ts.map +1 -0
  145. package/dist/lib/native-hardware/device-io-errors.js +82 -0
  146. package/dist/lib/native-hardware/displays.d.ts +38 -0
  147. package/dist/lib/native-hardware/displays.d.ts.map +1 -0
  148. package/dist/lib/native-hardware/displays.js +12 -0
  149. package/dist/lib/native-hardware/global-shortcuts.d.ts +86 -0
  150. package/dist/lib/native-hardware/global-shortcuts.d.ts.map +1 -0
  151. package/dist/lib/native-hardware/global-shortcuts.js +351 -0
  152. package/dist/lib/native-hardware/haptics.d.ts +25 -0
  153. package/dist/lib/native-hardware/haptics.d.ts.map +1 -0
  154. package/dist/lib/native-hardware/haptics.js +18 -0
  155. package/dist/lib/native-hardware/hid-native.d.ts +26 -0
  156. package/dist/lib/native-hardware/hid-native.d.ts.map +1 -0
  157. package/dist/lib/native-hardware/hid-native.js +229 -0
  158. package/dist/lib/native-hardware/hid.d.ts +52 -0
  159. package/dist/lib/native-hardware/hid.d.ts.map +1 -0
  160. package/dist/lib/native-hardware/hid.js +160 -0
  161. package/dist/lib/native-hardware/index.d.ts +38 -0
  162. package/dist/lib/native-hardware/index.d.ts.map +1 -0
  163. package/dist/lib/native-hardware/index.js +20 -0
  164. package/dist/lib/native-hardware/local-authentication.d.ts +32 -0
  165. package/dist/lib/native-hardware/local-authentication.d.ts.map +1 -0
  166. package/dist/lib/native-hardware/local-authentication.js +46 -0
  167. package/dist/lib/native-hardware/location.d.ts +49 -0
  168. package/dist/lib/native-hardware/location.d.ts.map +1 -0
  169. package/dist/lib/native-hardware/location.js +114 -0
  170. package/dist/lib/native-hardware/media-permissions.d.ts +28 -0
  171. package/dist/lib/native-hardware/media-permissions.d.ts.map +1 -0
  172. package/dist/lib/native-hardware/media-permissions.js +37 -0
  173. package/dist/lib/native-hardware/media.d.ts +17 -0
  174. package/dist/lib/native-hardware/media.d.ts.map +1 -0
  175. package/dist/lib/native-hardware/media.js +99 -0
  176. package/dist/lib/native-hardware/midi-access.d.ts +13 -0
  177. package/dist/lib/native-hardware/midi-access.d.ts.map +1 -0
  178. package/dist/lib/native-hardware/midi-access.js +66 -0
  179. package/dist/lib/native-hardware/midi-internal.d.ts +12 -0
  180. package/dist/lib/native-hardware/midi-internal.d.ts.map +1 -0
  181. package/dist/lib/native-hardware/midi-internal.js +88 -0
  182. package/dist/lib/native-hardware/midi-native-fallback.d.ts +27 -0
  183. package/dist/lib/native-hardware/midi-native-fallback.d.ts.map +1 -0
  184. package/dist/lib/native-hardware/midi-native-fallback.js +127 -0
  185. package/dist/lib/native-hardware/midi-ports.d.ts +6 -0
  186. package/dist/lib/native-hardware/midi-ports.d.ts.map +1 -0
  187. package/dist/lib/native-hardware/midi-ports.js +115 -0
  188. package/dist/lib/native-hardware/midi-types.d.ts +25 -0
  189. package/dist/lib/native-hardware/midi-types.d.ts.map +1 -0
  190. package/dist/lib/native-hardware/midi-types.js +1 -0
  191. package/dist/lib/native-hardware/midi.d.ts +6 -0
  192. package/dist/lib/native-hardware/midi.d.ts.map +1 -0
  193. package/dist/lib/native-hardware/midi.js +3 -0
  194. package/dist/lib/native-hardware/native-device-io.d.ts +28 -0
  195. package/dist/lib/native-hardware/native-device-io.d.ts.map +1 -0
  196. package/dist/lib/native-hardware/native-device-io.js +91 -0
  197. package/dist/lib/native-hardware/native-device-lifecycle.d.ts +13 -0
  198. package/dist/lib/native-hardware/native-device-lifecycle.d.ts.map +1 -0
  199. package/dist/lib/native-hardware/native-device-lifecycle.js +40 -0
  200. package/dist/lib/native-hardware/notifications.d.ts +38 -0
  201. package/dist/lib/native-hardware/notifications.d.ts.map +1 -0
  202. package/dist/lib/native-hardware/notifications.js +50 -0
  203. package/dist/lib/native-hardware/power-session.d.ts +113 -0
  204. package/dist/lib/native-hardware/power-session.d.ts.map +1 -0
  205. package/dist/lib/native-hardware/power-session.js +264 -0
  206. package/dist/lib/native-hardware/secure-storage.d.ts +36 -0
  207. package/dist/lib/native-hardware/secure-storage.d.ts.map +1 -0
  208. package/dist/lib/native-hardware/secure-storage.js +64 -0
  209. package/dist/lib/native-hardware/serial-native.d.ts +23 -0
  210. package/dist/lib/native-hardware/serial-native.d.ts.map +1 -0
  211. package/dist/lib/native-hardware/serial-native.js +181 -0
  212. package/dist/lib/native-hardware/serial.d.ts +76 -0
  213. package/dist/lib/native-hardware/serial.d.ts.map +1 -0
  214. package/dist/lib/native-hardware/serial.js +212 -0
  215. package/dist/lib/native-hardware/system-audio.d.ts +127 -0
  216. package/dist/lib/native-hardware/system-audio.d.ts.map +1 -0
  217. package/dist/lib/native-hardware/system-audio.js +330 -0
  218. package/dist/lib/native-hardware/types.d.ts +62 -0
  219. package/dist/lib/native-hardware/types.d.ts.map +1 -0
  220. package/dist/lib/native-hardware/types.js +27 -0
  221. package/dist/lib/native-hardware/usb-native.d.ts +39 -0
  222. package/dist/lib/native-hardware/usb-native.d.ts.map +1 -0
  223. package/dist/lib/native-hardware/usb-native.js +217 -0
  224. package/dist/lib/native-hardware/usb.d.ts +85 -0
  225. package/dist/lib/native-hardware/usb.d.ts.map +1 -0
  226. package/dist/lib/native-hardware/usb.js +222 -0
  227. package/package.json +1 -1
  228. package/src/styles/index.css +377 -0
@@ -0,0 +1,363 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { RefreshCw, RotateCcw, Trash2, Usb } from 'lucide-react';
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+ import { NativeHardwareBridgeError, clearMoldableUsbHalt, closeMoldableUsbDevice, forgetMoldableUsbDevice, getMoldableUsbDevices, openMoldableUsbDevice, readMoldableUsbControlTransfer, readMoldableUsbEndpoint, requestMoldableUsbDevice, resetMoldableUsbDevice, writeMoldableUsbControlTransfer, writeMoldableUsbEndpoint, } from '../../lib/native-hardware';
6
+ import { Button } from '../ui/button';
7
+ import { Input } from '../ui/input';
8
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../ui/select';
9
+ import { Spinner } from '../ui/spinner';
10
+ import { CapabilityBadge } from './capability-badge';
11
+ import { formatHexBytes, parseHexBytes, parseIntegerInput, } from './device-io-controls';
12
+ import { DeviceList, DeviceListEmpty, DeviceListItem } from './device-list';
13
+ import { HardwarePanel, HardwarePanelAction, HardwarePanelContent, HardwarePanelFooter, HardwarePanelHeader, HardwarePanelIcon, HardwarePanelTitle, } from './hardware-panel';
14
+ import { Readout, ReadoutItem } from './readout';
15
+ function toError(cause) {
16
+ return cause instanceof Error ? cause : new Error(String(cause));
17
+ }
18
+ function formatHexId(value) {
19
+ return `0x${value.toString(16).padStart(4, '0')}`;
20
+ }
21
+ function usbDeviceDescriptionId(device) {
22
+ return [
23
+ device.vendorId,
24
+ device.productId,
25
+ device.serialNumber ?? '',
26
+ device.productName ?? '',
27
+ ].join(':');
28
+ }
29
+ /**
30
+ * USB state machine behind `UsbDevicePanel`: granted-device enumeration,
31
+ * chooser requests, minimal-claim open/close tracking. Use directly to build
32
+ * custom USB UI on the same best-effort error handling.
33
+ */
34
+ export function useMoldableUsb(options = {}) {
35
+ const [devices, setDevices] = useState([]);
36
+ const [status, setStatus] = useState('idle');
37
+ const [error, setError] = useState(null);
38
+ const [openDeviceIds, setOpenDeviceIds] = useState([]);
39
+ const [nativeDeviceChoices, setNativeDeviceChoices] = useState([]);
40
+ const openDevicesRef = useRef(new Map());
41
+ const idsRef = useRef(new WeakMap());
42
+ const idCounterRef = useRef(0);
43
+ const pendingSelectionRef = useRef(null);
44
+ const generationRef = useRef(0);
45
+ const unmountedRef = useRef(false);
46
+ const optionsRef = useRef(options);
47
+ optionsRef.current = options;
48
+ const deviceId = useCallback((device) => {
49
+ let id = idsRef.current.get(device);
50
+ if (!id) {
51
+ id = `usb-${++idCounterRef.current}`;
52
+ idsRef.current.set(device, id);
53
+ }
54
+ return id;
55
+ }, []);
56
+ const refresh = useCallback(async () => {
57
+ const generation = ++generationRef.current;
58
+ setStatus('loading');
59
+ setError(null);
60
+ try {
61
+ const known = await getMoldableUsbDevices();
62
+ if (generationRef.current !== generation)
63
+ return;
64
+ setDevices((previous) => [
65
+ ...known,
66
+ ...previous.filter((device) => openDevicesRef.current.has(deviceId(device)) &&
67
+ !known.includes(device)),
68
+ ]);
69
+ setStatus('idle');
70
+ }
71
+ catch (cause) {
72
+ if (generationRef.current !== generation)
73
+ return;
74
+ if (cause instanceof NativeHardwareBridgeError &&
75
+ cause.code === 'unsupported') {
76
+ // No WebUSB enumeration here (native fallback runtime). Devices
77
+ // granted through `request` stay listed; this is not an error.
78
+ setStatus('idle');
79
+ return;
80
+ }
81
+ setError(toError(cause));
82
+ setStatus('error');
83
+ }
84
+ }, [deviceId]);
85
+ const handleDisconnect = useCallback((device) => {
86
+ if (unmountedRef.current)
87
+ return;
88
+ const id = deviceId(device);
89
+ const record = openDevicesRef.current.get(id);
90
+ if (record) {
91
+ device.removeEventListener('disconnect', record.onDisconnect);
92
+ }
93
+ openDevicesRef.current.delete(id);
94
+ setOpenDeviceIds((previous) => previous.filter((existing) => existing !== id));
95
+ setDevices((previous) => previous.filter((existing) => existing !== device));
96
+ }, [deviceId]);
97
+ const chooseNativeDevice = useCallback((devices) => new Promise((resolve, reject) => {
98
+ pendingSelectionRef.current = { devices, resolve, reject };
99
+ setNativeDeviceChoices(devices);
100
+ }), []);
101
+ const selectNativeDevice = useCallback((device) => {
102
+ const pending = pendingSelectionRef.current;
103
+ if (!pending || !pending.devices.includes(device))
104
+ return;
105
+ pendingSelectionRef.current = null;
106
+ setNativeDeviceChoices([]);
107
+ pending.resolve(device);
108
+ }, []);
109
+ const cancelNativeSelection = useCallback(() => {
110
+ const pending = pendingSelectionRef.current;
111
+ if (!pending)
112
+ return;
113
+ pendingSelectionRef.current = null;
114
+ setNativeDeviceChoices([]);
115
+ pending.reject(new NativeHardwareBridgeError({
116
+ code: 'cancelled',
117
+ message: 'USB device selection was cancelled.',
118
+ retryable: true,
119
+ }));
120
+ }, []);
121
+ const request = useCallback(async (filters) => {
122
+ const generation = ++generationRef.current;
123
+ setStatus('requesting');
124
+ setError(null);
125
+ try {
126
+ const device = await requestMoldableUsbDevice({
127
+ filters: filters ?? optionsRef.current.filters ?? [],
128
+ selectNativeDevice: optionsRef.current.selectNativeDevice ?? chooseNativeDevice,
129
+ });
130
+ if (generationRef.current !== generation)
131
+ return;
132
+ setDevices((previous) => previous.includes(device) ? previous : [...previous, device]);
133
+ setStatus('idle');
134
+ return device;
135
+ }
136
+ catch (cause) {
137
+ if (generationRef.current !== generation)
138
+ return;
139
+ if (cause instanceof NativeHardwareBridgeError &&
140
+ (cause.code === 'cancelled' || cause.code === 'aborted')) {
141
+ // The user dismissed the chooser; that is not an error state.
142
+ setStatus('idle');
143
+ return;
144
+ }
145
+ setError(toError(cause));
146
+ setStatus('error');
147
+ }
148
+ }, [chooseNativeDevice]);
149
+ const open = useCallback(async (device) => {
150
+ const id = deviceId(device);
151
+ if (openDevicesRef.current.has(id))
152
+ return;
153
+ try {
154
+ await openMoldableUsbDevice(device, optionsRef.current.openOptions);
155
+ if (unmountedRef.current) {
156
+ void closeMoldableUsbDevice(device, optionsRef.current.openOptions?.claimInterfaces).catch(() => undefined);
157
+ return;
158
+ }
159
+ const onDisconnect = () => handleDisconnect(device);
160
+ device.addEventListener('disconnect', onDisconnect, { once: true });
161
+ openDevicesRef.current.set(id, {
162
+ device,
163
+ onDisconnect,
164
+ releaseInterfaces: optionsRef.current.openOptions?.claimInterfaces ?? [],
165
+ });
166
+ setOpenDeviceIds((previous) => previous.includes(id) ? previous : [...previous, id]);
167
+ setError(null);
168
+ setStatus('idle');
169
+ optionsRef.current.onDeviceOpen?.(device);
170
+ }
171
+ catch (cause) {
172
+ if (unmountedRef.current)
173
+ return;
174
+ setError(toError(cause));
175
+ setStatus('error');
176
+ }
177
+ }, [deviceId, handleDisconnect]);
178
+ const close = useCallback(async (device) => {
179
+ const id = deviceId(device);
180
+ const record = openDevicesRef.current.get(id);
181
+ try {
182
+ await closeMoldableUsbDevice(device, record?.releaseInterfaces);
183
+ if (record) {
184
+ device.removeEventListener('disconnect', record.onDisconnect);
185
+ }
186
+ openDevicesRef.current.delete(id);
187
+ setOpenDeviceIds((previous) => previous.filter((existing) => existing !== id));
188
+ setError(null);
189
+ setStatus('idle');
190
+ return true;
191
+ }
192
+ catch (cause) {
193
+ if (record)
194
+ record.releaseInterfaces = [];
195
+ if (unmountedRef.current)
196
+ return false;
197
+ setError(toError(cause));
198
+ setStatus('error');
199
+ return false;
200
+ }
201
+ }, [deviceId]);
202
+ const runOperation = useCallback(async (operation) => {
203
+ setError(null);
204
+ try {
205
+ const result = await operation();
206
+ if (!unmountedRef.current)
207
+ setStatus('idle');
208
+ return result;
209
+ }
210
+ catch (cause) {
211
+ if (!unmountedRef.current) {
212
+ setError(toError(cause));
213
+ setStatus('error');
214
+ }
215
+ throw cause;
216
+ }
217
+ }, []);
218
+ const forget = useCallback(async (device) => {
219
+ const id = deviceId(device);
220
+ if ((openDevicesRef.current.has(id) || device.opened) &&
221
+ !(await close(device))) {
222
+ return false;
223
+ }
224
+ try {
225
+ await runOperation(() => forgetMoldableUsbDevice(device));
226
+ if (unmountedRef.current)
227
+ return false;
228
+ setDevices((previous) => previous.filter((known) => known !== device));
229
+ return true;
230
+ }
231
+ catch {
232
+ return false;
233
+ }
234
+ }, [close, deviceId, runOperation]);
235
+ useEffect(() => {
236
+ unmountedRef.current = false;
237
+ void refresh();
238
+ const liveGeneration = generationRef;
239
+ const openDevices = openDevicesRef.current;
240
+ return () => {
241
+ unmountedRef.current = true;
242
+ liveGeneration.current++;
243
+ const pending = pendingSelectionRef.current;
244
+ pendingSelectionRef.current = null;
245
+ pending?.reject(new NativeHardwareBridgeError({
246
+ code: 'aborted',
247
+ message: 'USB device selection was interrupted.',
248
+ retryable: true,
249
+ }));
250
+ const stillOpen = [...openDevices.values()];
251
+ openDevices.clear();
252
+ for (const { device, onDisconnect, releaseInterfaces } of stillOpen) {
253
+ device.removeEventListener('disconnect', onDisconnect);
254
+ void closeMoldableUsbDevice(device, releaseInterfaces).catch(() => undefined);
255
+ }
256
+ };
257
+ }, [refresh]);
258
+ return {
259
+ devices,
260
+ status,
261
+ error,
262
+ openDeviceIds,
263
+ nativeDeviceChoices,
264
+ deviceId,
265
+ refresh,
266
+ request,
267
+ selectNativeDevice,
268
+ cancelNativeSelection,
269
+ open,
270
+ close,
271
+ forget,
272
+ readEndpoint: (device, endpoint, length) => runOperation(() => readMoldableUsbEndpoint(device, endpoint, length)),
273
+ writeEndpoint: (device, endpoint, data) => runOperation(() => writeMoldableUsbEndpoint(device, endpoint, data)),
274
+ readControl: (device, setup, length) => runOperation(() => readMoldableUsbControlTransfer(device, setup, length)),
275
+ writeControl: (device, setup, data) => runOperation(() => writeMoldableUsbControlTransfer(device, setup, data)),
276
+ clearHalt: (device, direction, endpoint) => runOperation(() => clearMoldableUsbHalt(device, direction, endpoint)),
277
+ reset: (device) => runOperation(() => resetMoldableUsbDevice(device)),
278
+ };
279
+ }
280
+ function UsbDeviceControls({ device, usb }) {
281
+ const [endpoint, setEndpoint] = useState('1');
282
+ const [length, setLength] = useState('64');
283
+ const [payload, setPayload] = useState('');
284
+ const [requestType, setRequestType] = useState('vendor');
285
+ const [recipient, setRecipient] = useState('device');
286
+ const [request, setRequest] = useState('0');
287
+ const [value, setValue] = useState('0');
288
+ const [index, setIndex] = useState('0');
289
+ const [result, setResult] = useState('Ready');
290
+ const [busy, setBusy] = useState(false);
291
+ const run = async (operation) => {
292
+ setBusy(true);
293
+ try {
294
+ setResult(await operation());
295
+ }
296
+ catch (cause) {
297
+ setResult(toError(cause).message);
298
+ }
299
+ finally {
300
+ setBusy(false);
301
+ }
302
+ };
303
+ const endpointNumber = () => parseIntegerInput(endpoint, 'Endpoint', 1, 15);
304
+ const transferLength = () => parseIntegerInput(length, 'Length', 1, 65_535);
305
+ const setup = () => ({
306
+ requestType,
307
+ recipient,
308
+ request: parseIntegerInput(request, 'Request', 0, 255),
309
+ value: parseIntegerInput(value, 'Value', 0, 65_535),
310
+ index: parseIntegerInput(index, 'Index', 0, 65_535),
311
+ });
312
+ return (_jsxs("div", { "data-slot": "usb-device-controls", className: "bg-muted/30 mt-2 space-y-3 rounded-md border p-2.5", children: [_jsxs("div", { className: "grid gap-2 sm:grid-cols-3", children: [_jsx(Input, { "aria-label": "USB endpoint number", value: endpoint, onChange: (event) => setEndpoint(event.target.value), placeholder: "Endpoint", className: "h-8 font-mono text-xs" }), _jsx(Input, { "aria-label": "USB transfer length", value: length, onChange: (event) => setLength(event.target.value), placeholder: "Length", className: "h-8 font-mono text-xs" }), _jsx(Input, { "aria-label": "USB endpoint payload", value: payload, onChange: (event) => setPayload(event.target.value), placeholder: "Hex payload", className: "h-8 font-mono text-xs" })] }), _jsxs("div", { className: "flex flex-wrap gap-1.5", children: [_jsx(Button, { size: "xs", variant: "outline", disabled: busy, onClick: () => void run(async () => {
313
+ const transfer = await usb.readEndpoint(device, endpointNumber(), transferLength());
314
+ return `Endpoint IN · ${transfer.status} · ${formatHexBytes(transfer.data)}`;
315
+ }), children: "Endpoint IN" }), _jsx(Button, { size: "xs", variant: "outline", disabled: busy, onClick: () => void run(async () => {
316
+ const transfer = await usb.writeEndpoint(device, endpointNumber(), parseHexBytes(payload));
317
+ return `Endpoint OUT · ${transfer.status} · ${transfer.bytesWritten ?? 0} bytes`;
318
+ }), children: "Endpoint OUT" }), _jsx(Button, { size: "xs", variant: "ghost", disabled: busy, onClick: () => void run(async () => {
319
+ await usb.clearHalt(device, 'in', endpointNumber());
320
+ return 'IN halt cleared';
321
+ }), children: "Clear IN halt" }), _jsx(Button, { size: "xs", variant: "ghost", disabled: busy, onClick: () => void run(async () => {
322
+ await usb.clearHalt(device, 'out', endpointNumber());
323
+ return 'OUT halt cleared';
324
+ }), children: "Clear OUT halt" })] }), _jsxs("div", { className: "grid gap-2 sm:grid-cols-5", children: [_jsxs(Select, { value: requestType, onValueChange: (next) => setRequestType(next), children: [_jsx(SelectTrigger, { size: "sm", "aria-label": "USB control request type", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "standard", children: "Standard" }), _jsx(SelectItem, { value: "class", children: "Class" }), _jsx(SelectItem, { value: "vendor", children: "Vendor" })] })] }), _jsxs(Select, { value: recipient, onValueChange: (next) => setRecipient(next), children: [_jsx(SelectTrigger, { size: "sm", "aria-label": "USB control recipient", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "device", children: "Device" }), _jsx(SelectItem, { value: "interface", children: "Interface" }), _jsx(SelectItem, { value: "endpoint", children: "Endpoint" }), _jsx(SelectItem, { value: "other", children: "Other" })] })] }), [
325
+ ['Request', request, setRequest],
326
+ ['Value', value, setValue],
327
+ ['Index', index, setIndex],
328
+ ].map(([label, current, update]) => (_jsx(Input, { "aria-label": `USB control ${String(label).toLowerCase()}`, value: current, onChange: (event) => update(event.target.value), placeholder: label, className: "h-8 font-mono text-xs" }, label)))] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Button, { size: "xs", variant: "outline", disabled: busy, onClick: () => void run(async () => {
329
+ const transfer = await usb.readControl(device, setup(), transferLength());
330
+ return `Control IN · ${transfer.status} · ${formatHexBytes(transfer.data)}`;
331
+ }), children: "Control IN" }), _jsx(Button, { size: "xs", variant: "outline", disabled: busy, onClick: () => void run(async () => {
332
+ const transfer = await usb.writeControl(device, setup(), parseHexBytes(payload));
333
+ return `Control OUT · ${transfer.status} · ${transfer.bytesWritten ?? 0} bytes`;
334
+ }), children: "Control OUT" }), _jsxs(Button, { size: "xs", variant: "ghost", disabled: busy, onClick: () => void run(async () => {
335
+ await usb.reset(device);
336
+ return 'Device reset';
337
+ }), children: [_jsx(RotateCcw, {}), "Reset"] }), _jsx("span", { className: "text-muted-foreground ml-auto truncate font-mono text-[10px]", children: busy ? 'Working…' : result })] })] }));
338
+ }
339
+ /**
340
+ * USB device panel: request access, list granted devices, open/close each
341
+ * with minimal claims, and read out live device details while open.
342
+ */
343
+ function UsbDevicePanel({ className, filters, selectNativeDevice, openOptions, onDeviceOpen, ...props }) {
344
+ const usb = useMoldableUsb({
345
+ filters,
346
+ selectNativeDevice,
347
+ openOptions,
348
+ onDeviceOpen,
349
+ });
350
+ const requesting = usb.status === 'requesting';
351
+ const loading = usb.status === 'loading';
352
+ return (_jsxs(HardwarePanel, { "data-slot": "usb-device-panel", "data-status": usb.status, className: className, ...props, children: [_jsxs(HardwarePanelHeader, { children: [_jsx(HardwarePanelIcon, { children: _jsx(Usb, {}) }), _jsx(HardwarePanelTitle, { children: "USB" }), _jsx(CapabilityBadge, { capability: "usb" }), _jsxs(HardwarePanelAction, { children: [_jsxs(Button, { size: "sm", disabled: requesting, onClick: () => void usb.request(), children: [requesting ? _jsx(Spinner, {}) : _jsx(Usb, {}), requesting ? 'Requesting…' : 'Request device'] }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Refresh devices", disabled: loading || requesting, onClick: () => void usb.refresh(), children: _jsx(RefreshCw, { className: loading ? 'animate-spin motion-reduce:animate-none' : undefined }) })] })] }), _jsxs(HardwarePanelContent, { children: [usb.nativeDeviceChoices.length > 0 ? (_jsxs("div", { "data-slot": "usb-native-device-picker", className: "bg-muted/30 mb-4 space-y-2 rounded-lg border p-3", children: [_jsx("p", { className: "text-sm font-medium", children: "Choose a USB device" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "More than one native device matched. Select the device explicitly." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [usb.nativeDeviceChoices.map((device, index) => (_jsxs(Button, { size: "xs", variant: "outline", onClick: () => usb.selectNativeDevice(device), children: [device.productName || 'USB device', " \u00B7", ' ', formatHexId(device.vendorId), ":", formatHexId(device.productId)] }, `${usbDeviceDescriptionId(device)}:${index}`))), _jsx(Button, { size: "xs", variant: "ghost", onClick: usb.cancelNativeSelection, children: "Cancel" })] })] })) : null, _jsx(DeviceList, { children: usb.devices.length === 0 ? (_jsxs(DeviceListEmpty, { icon: _jsx(Usb, {}), children: [_jsx("span", { className: "font-medium", children: "No devices yet" }), _jsx("span", { className: "text-muted-foreground/70 text-xs", children: "Request access to a USB device to get started." })] })) : (usb.devices.map((device, index) => {
353
+ const id = usb.deviceId(device);
354
+ const open = usb.openDeviceIds.includes(id) || device.opened;
355
+ return (_jsx(DeviceListItem, { icon: _jsx(Usb, {}), name: device.productName || 'USB device', description: `${formatHexId(device.vendorId)}:${formatHexId(device.productId)}` +
356
+ (device.manufacturerName
357
+ ? ` · ${device.manufacturerName}`
358
+ : ''), connected: open, action: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { size: "xs", variant: "outline", onClick: () => void (open ? usb.close(device) : usb.open(device)), children: open ? 'Close' : 'Open' }), _jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": `Forget ${device.productName || 'USB device'}`, onClick: () => void usb.forget(device), children: _jsx(Trash2, {}) })] }), children: open ? (_jsxs("div", { "data-slot": "usb-device-details", className: "animate-in fade-in slide-in-from-top-1 bg-muted/30 mt-2 rounded-md border p-2.5 duration-200 motion-reduce:animate-none", children: [_jsxs(Readout, { className: "gap-y-2 sm:grid-cols-4", children: [_jsx(ReadoutItem, { label: "Configuration", value: device.configuration?.configurationValue ?? null }), _jsx(ReadoutItem, { label: "Vendor ID", value: formatHexId(device.vendorId) }), _jsx(ReadoutItem, { label: "Product ID", value: formatHexId(device.productId) }), _jsx(ReadoutItem, { label: "Serial", value: device.serialNumber ?? null })] }), _jsx(UsbDeviceControls, { device: device, usb: usb })] })) : null }, `${id}:${index}`));
359
+ })) })] }), _jsxs(HardwarePanelFooter, { children: [usb.error ? (_jsx("span", { className: "text-status-error truncate", children: usb.error.message })) : (_jsx("span", { className: "truncate", children: usb.devices.length === 0
360
+ ? 'No devices authorized'
361
+ : `${usb.devices.length} device${usb.devices.length === 1 ? '' : 's'} known` })), _jsxs("span", { className: "ml-auto shrink-0 font-mono tabular-nums", children: [usb.openDeviceIds.length, " open"] })] })] }));
362
+ }
363
+ export { UsbDevicePanel };
@@ -0,0 +1,20 @@
1
+ import { type NativeHardwareCapabilityDescriptor, type NativeHardwareCapabilityId, type NativeHardwareCapabilityManifest } from '../../lib/native-hardware';
2
+ export type MoldableCapabilityStatus = 'checking' | 'supported' | 'partial' | 'unsupported' | 'unavailable';
3
+ /**
4
+ * Loads the native capability manifest once per page and shares it across all
5
+ * hardware components. Pass `force` to bypass the cache (e.g. after a grant).
6
+ */
7
+ export declare function loadMoldableCapabilities(force?: boolean): Promise<NativeHardwareCapabilityManifest>;
8
+ export interface MoldableCapability {
9
+ status: MoldableCapabilityStatus;
10
+ descriptor: NativeHardwareCapabilityDescriptor | null;
11
+ manifest: NativeHardwareCapabilityManifest | null;
12
+ error: Error | null;
13
+ refresh: () => void;
14
+ }
15
+ /**
16
+ * Reads the support level for one native capability. `unavailable` means the
17
+ * manifest could not be loaded at all (typically: not running inside Moldable).
18
+ */
19
+ export declare function useMoldableCapability(id: NativeHardwareCapabilityId): MoldableCapability;
20
+ //# sourceMappingURL=use-capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-capability.d.ts","sourceRoot":"","sources":["../../../src/components/hardware/use-capability.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,kCAAkC,EACvC,KAAK,0BAA0B,EAC/B,KAAK,gCAAgC,EAEtC,MAAM,2BAA2B,CAAA;AAElC,MAAM,MAAM,wBAAwB,GAChC,UAAU,GACV,WAAW,GACX,SAAS,GACT,aAAa,GACb,aAAa,CAAA;AAIjB;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,UAAQ,GACZ,OAAO,CAAC,gCAAgC,CAAC,CAU3C;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,wBAAwB,CAAA;IAChC,UAAU,EAAE,kCAAkC,GAAG,IAAI,CAAA;IACrD,QAAQ,EAAE,gCAAgC,GAAG,IAAI,CAAA;IACjD,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,0BAA0B,GAC7B,kBAAkB,CA2CpB"}
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { getMoldableNativeCapabilities, } from '../../lib/native-hardware';
4
+ let manifestPromise = null;
5
+ /**
6
+ * Loads the native capability manifest once per page and shares it across all
7
+ * hardware components. Pass `force` to bypass the cache (e.g. after a grant).
8
+ */
9
+ export function loadMoldableCapabilities(force = false) {
10
+ if (force || !manifestPromise) {
11
+ manifestPromise = getMoldableNativeCapabilities().catch((error) => {
12
+ manifestPromise = null;
13
+ throw error;
14
+ });
15
+ }
16
+ return manifestPromise;
17
+ }
18
+ /**
19
+ * Reads the support level for one native capability. `unavailable` means the
20
+ * manifest could not be loaded at all (typically: not running inside Moldable).
21
+ */
22
+ export function useMoldableCapability(id) {
23
+ const [manifest, setManifest] = useState(null);
24
+ const [error, setError] = useState(null);
25
+ const [checking, setChecking] = useState(true);
26
+ const generation = useRef(0);
27
+ const load = useCallback((force) => {
28
+ const current = ++generation.current;
29
+ setChecking(true);
30
+ setError(null);
31
+ loadMoldableCapabilities(force)
32
+ .then((value) => {
33
+ if (generation.current !== current)
34
+ return;
35
+ setManifest(value);
36
+ setChecking(false);
37
+ })
38
+ .catch((cause) => {
39
+ if (generation.current !== current)
40
+ return;
41
+ setManifest(null);
42
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
43
+ setChecking(false);
44
+ });
45
+ }, []);
46
+ useEffect(() => {
47
+ load(false);
48
+ const liveGeneration = generation;
49
+ return () => {
50
+ liveGeneration.current++;
51
+ };
52
+ }, [load]);
53
+ const refresh = useCallback(() => load(true), [load]);
54
+ const descriptor = manifest ? manifest.capabilities[id] : null;
55
+ const status = checking
56
+ ? 'checking'
57
+ : descriptor
58
+ ? descriptor.support
59
+ : 'unavailable';
60
+ return { status, descriptor, manifest, error, refresh };
61
+ }
@@ -3,7 +3,7 @@ import { Button } from './button';
3
3
  import { type VariantProps } from 'class-variance-authority';
4
4
  declare function InputGroup({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
5
5
  declare const inputGroupAddonVariants: (props?: ({
6
- align?: "inline-end" | "inline-start" | "block-start" | "block-end" | null | undefined;
6
+ align?: "inline-end" | "inline-start" | "block-end" | "block-start" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  declare function InputGroupAddon({ className, align, ...props }: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>): import("react/jsx-runtime").JSX.Element;
9
9
  declare const inputGroupButtonVariants: (props?: ({
@@ -1,5 +1,8 @@
1
1
  import * as SliderPrimitive from '@radix-ui/react-slider';
2
2
  import * as React from 'react';
3
- declare function Slider({ className, defaultValue, value, min, max, ...props }: React.ComponentProps<typeof SliderPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
3
+ declare function Slider({ className, defaultValue, value, min, max, rangeContent, thumbContent, ...props }: React.ComponentProps<typeof SliderPrimitive.Root> & {
4
+ rangeContent?: React.ReactNode;
5
+ thumbContent?: React.ReactNode;
6
+ }): import("react/jsx-runtime").JSX.Element;
4
7
  export { Slider };
5
8
  //# sourceMappingURL=slider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../../../src/components/ui/slider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,YAAY,EACZ,KAAK,EACL,GAAO,EACP,GAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,2CA8CnD;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../../../src/components/ui/slider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,YAAY,EACZ,KAAK,EACL,GAAO,EACP,GAAS,EACT,YAAY,EACZ,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG;IACrD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC/B,2CAkDA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as SliderPrimitive from '@radix-ui/react-slider';
4
4
  import * as React from 'react';
5
5
  import { cn } from '../../lib/utils';
6
- function Slider({ className, defaultValue, value, min = 0, max = 100, ...props }) {
6
+ function Slider({ className, defaultValue, value, min = 0, max = 100, rangeContent, thumbContent, ...props }) {
7
7
  const _values = React.useMemo(() => Array.isArray(value)
8
8
  ? value
9
9
  : Array.isArray(defaultValue)
10
10
  ? defaultValue
11
11
  : [min, max], [value, defaultValue, min, max]);
12
- return (_jsxs(SliderPrimitive.Root, { "data-slot": "slider", defaultValue: defaultValue, value: value, min: min, max: max, className: cn('relative flex w-full touch-none select-none items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col data-[disabled]:opacity-50', className), ...props, children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: cn('bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-1.5'), children: _jsx(SliderPrimitive.Range, { "data-slot": "slider-range", className: cn('bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full') }) }), Array.from({ length: _values.length }, (_, index) => (_jsx(SliderPrimitive.Thumb, { "data-slot": "slider-thumb", className: "border-primary ring-ring/50 focus-visible:outline-hidden block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50" }, index)))] }));
12
+ return (_jsxs(SliderPrimitive.Root, { "data-slot": "slider", defaultValue: defaultValue, value: value, min: min, max: max, className: cn('relative flex w-full touch-none select-none items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col data-[disabled]:opacity-50', className), ...props, children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: cn('bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-1.5'), children: _jsx(SliderPrimitive.Range, { "data-slot": "slider-range", className: cn('bg-primary absolute overflow-hidden rounded-full data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full'), children: rangeContent }) }), Array.from({ length: _values.length }, (_, index) => (_jsx(SliderPrimitive.Thumb, { "data-slot": "slider-thumb", className: "border-primary ring-ring/50 focus-visible:outline-hidden block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50", children: thumbContent }, index)))] }));
13
13
  }
14
14
  export { Slider };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,9 @@ export { cn } from './lib/utils';
2
2
  export { ThemeProvider, useTheme, themeScript, type Theme } from './lib/theme';
3
3
  export { WorkspaceProvider, useWorkspace, WORKSPACE_HEADER, } from './lib/workspace';
4
4
  export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, useMoldableNavigationPop, useMoldableNavigationState, isInMoldable, sendToMoldable, pushMoldableNavigation, popMoldableNavigation, resetMoldableNavigation, downloadFile, publishMoldableArtifact, setMoldableWindowFullscreen, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type MoldableNavigationEntry, type MoldableNavigationPopMessage, type MoldableNavigationStateMessage, type DownloadFileOptions, type MoldableArtifactFile, type MoldableArtifactPublishOptions, type MoldableArtifactPublishResult, type MoldableWindowFullscreenMode, type MoldableWindowFullscreenOptions, type MoldableWindowFullscreenResult, type MoldableWindowFullscreenMessage, type MoldableWindowFullscreenResultMessage, } from './lib/commands';
5
+ export * from './lib/native-hardware';
5
6
  export * from './components/ui';
7
+ export * from './components/hardware';
6
8
  export { TODAY_KIND_RANK, TODAY_KIND_TTL_MS, makeTodayItem, type TodayItem, type TodayItemKind, type TodaySurface, type TodayAction, type TodayResponse, type ResumeState, type HydratedTodayItem, type HydratedResume, } from './lib/today';
7
9
  export { useIsMobile } from './hooks/use-mobile';
8
10
  export { Markdown } from './components/markdown';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAG9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,qCAAqC,GAC3C,MAAM,gBAAgB,CAAA;AAGvB,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAA;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAA;AAGrE,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAG9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,YAAY,EACZ,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,EACZ,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,qCAAqC,GAC3C,MAAM,gBAAgB,CAAA;AAGvB,cAAc,uBAAuB,CAAA;AAGrC,cAAc,iBAAiB,CAAA;AAG/B,cAAc,uBAAuB,CAAA;AAGrC,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,gCAAgC,CAAA;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAA;AAGrE,cAAc,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -6,8 +6,12 @@ export { ThemeProvider, useTheme, themeScript } from './lib/theme';
6
6
  export { WorkspaceProvider, useWorkspace, WORKSPACE_HEADER, } from './lib/workspace';
7
7
  // Export commands
8
8
  export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, useMoldableNavigationPop, useMoldableNavigationState, isInMoldable, sendToMoldable, pushMoldableNavigation, popMoldableNavigation, resetMoldableNavigation, downloadFile, publishMoldableArtifact, setMoldableWindowFullscreen, } from './lib/commands';
9
+ // Export typed desktop/native hardware APIs
10
+ export * from './lib/native-hardware';
9
11
  // Export UI components
10
12
  export * from './components/ui';
13
+ // Export native hardware components (polished wrappers over lib/native-hardware)
14
+ export * from './components/hardware';
11
15
  // Export Today contract
12
16
  export { TODAY_KIND_RANK, TODAY_KIND_TTL_MS, makeTodayItem, } from './lib/today';
13
17
  // Export hooks
@@ -0,0 +1,14 @@
1
+ import type { MoldableBluetoothAvailability, MoldableBluetoothDevice, MoldableBluetoothOperationOptions, MoldableBluetoothRequestDeviceOptions, MoldableBluetoothRequestOptions } from './bluetooth-types';
2
+ /** Checks only for the standards-based Web Bluetooth entry point. */
3
+ export declare function isMoldableBluetoothSupported(): boolean;
4
+ /**
5
+ * Returns adapter availability when the runtime exposes it. `unknown` means the
6
+ * API exists but cannot report adapter state, not that Bluetooth is available.
7
+ */
8
+ export declare function getMoldableBluetoothAvailability(options?: MoldableBluetoothOperationOptions): Promise<MoldableBluetoothAvailability>;
9
+ /**
10
+ * Opens the browser's device chooser. This must be called directly from a
11
+ * trusted user action; it never silently scans for or selects devices.
12
+ */
13
+ export declare function requestMoldableBluetoothDevice(deviceOptions: MoldableBluetoothRequestDeviceOptions, options?: MoldableBluetoothRequestOptions): Promise<MoldableBluetoothDevice>;
14
+ //# sourceMappingURL=bluetooth-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bluetooth-access.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-access.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,6BAA6B,EAC7B,uBAAuB,EACvB,iCAAiC,EACjC,qCAAqC,EACrC,+BAA+B,EAChC,MAAM,mBAAmB,CAAA;AAE1B,qEAAqE;AACrE,wBAAgB,4BAA4B,IAAI,OAAO,CAGtD;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,6BAA6B,CAAC,CAexC;AAED;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,aAAa,EAAE,qCAAqC,EACpD,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,uBAAuB,CAAC,CAelC"}
@@ -0,0 +1,40 @@
1
+ import { abortableBluetooth, assertBluetoothUserGesture, bluetoothError, getBluetoothAPI, getBluetoothNavigator, validateBluetoothRequestOptions, } from './bluetooth-internal';
2
+ /** Checks only for the standards-based Web Bluetooth entry point. */
3
+ export function isMoldableBluetoothSupported() {
4
+ const api = getBluetoothNavigator()?.bluetooth;
5
+ return Boolean(api && typeof api.requestDevice === 'function');
6
+ }
7
+ /**
8
+ * Returns adapter availability when the runtime exposes it. `unknown` means the
9
+ * API exists but cannot report adapter state, not that Bluetooth is available.
10
+ */
11
+ export async function getMoldableBluetoothAvailability(options = {}) {
12
+ const operation = 'check Bluetooth availability';
13
+ const api = getBluetoothAPI(operation);
14
+ if (typeof api.getAvailability !== 'function')
15
+ return 'unknown';
16
+ try {
17
+ return (await abortableBluetooth(api.getAvailability(), options.signal, operation))
18
+ ? 'available'
19
+ : 'unavailable';
20
+ }
21
+ catch (error) {
22
+ throw bluetoothError(error, operation);
23
+ }
24
+ }
25
+ /**
26
+ * Opens the browser's device chooser. This must be called directly from a
27
+ * trusted user action; it never silently scans for or selects devices.
28
+ */
29
+ export async function requestMoldableBluetoothDevice(deviceOptions, options = {}) {
30
+ const operation = 'request a Bluetooth device';
31
+ const api = getBluetoothAPI(operation);
32
+ validateBluetoothRequestOptions(deviceOptions);
33
+ assertBluetoothUserGesture();
34
+ try {
35
+ return await abortableBluetooth(api.requestDevice(deviceOptions), options.signal, operation, (device) => device.gatt?.disconnect());
36
+ }
37
+ catch (error) {
38
+ throw bluetoothError(error, operation);
39
+ }
40
+ }
@@ -0,0 +1,19 @@
1
+ import type { MoldableBluetoothConnectionOptions, MoldableBluetoothDevice, MoldableBluetoothOperationOptions, MoldableBluetoothRemoteGATTServer, MoldableBluetoothRemoteGATTService, MoldableBluetoothUUID } from './bluetooth-types';
2
+ export declare class MoldableBluetoothConnection {
3
+ readonly device: MoldableBluetoothDevice;
4
+ readonly server: MoldableBluetoothRemoteGATTServer;
5
+ private readonly abortSignal?;
6
+ private readonly disconnectListeners;
7
+ private isClosed;
8
+ constructor(device: MoldableBluetoothDevice, server: MoldableBluetoothRemoteGATTServer, options?: MoldableBluetoothConnectionOptions);
9
+ get connected(): boolean;
10
+ onDisconnected(listener: () => void, signal?: AbortSignal): () => void;
11
+ getPrimaryService(service: MoldableBluetoothUUID, options?: MoldableBluetoothOperationOptions): Promise<MoldableBluetoothRemoteGATTService>;
12
+ disconnect(): void;
13
+ private readonly handleAbort;
14
+ private readonly handleDisconnected;
15
+ private finish;
16
+ }
17
+ /** Connects a previously user-selected device and owns disconnect cleanup. */
18
+ export declare function connectMoldableBluetoothDevice(device: MoldableBluetoothDevice, options?: MoldableBluetoothConnectionOptions): Promise<MoldableBluetoothConnection>;
19
+ //# sourceMappingURL=bluetooth-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bluetooth-connection.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-connection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,kCAAkC,EAClC,uBAAuB,EACvB,iCAAiC,EACjC,iCAAiC,EACjC,kCAAkC,EAClC,qBAAqB,EACtB,MAAM,mBAAmB,CAAA;AAE1B,qBAAa,2BAA2B;IACtC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAA;IACxC,QAAQ,CAAC,MAAM,EAAE,iCAAiC,CAAA;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAa;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAQ;gBAGtB,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,iCAAiC,EACzC,OAAO,GAAE,kCAAuC;IASlD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,IAAI;IAWhE,iBAAiB,CACrB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,kCAAkC,CAAC;IAmB9C,UAAU,IAAI,IAAI;IAMlB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA0B;IAE7D,OAAO,CAAC,MAAM;CAqBf;AAED,8EAA8E;AAC9E,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,uBAAuB,EAC/B,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,2BAA2B,CAAC,CA0BtC"}