tassello 0.1.0 → 0.2.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 (51) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -3
  3. package/dist/server/behavior_tree_file.d.ts +17 -0
  4. package/dist/server/behavior_tree_file.js +232 -0
  5. package/dist/server/behavior_tree_files.d.ts +18 -0
  6. package/dist/server/behavior_tree_files.js +405 -0
  7. package/dist/server/behavior_tree_log.d.ts +10 -0
  8. package/dist/server/behavior_tree_log.js +132 -0
  9. package/dist/server/behavior_tree_playback.d.ts +57 -0
  10. package/dist/server/behavior_tree_playback.js +188 -0
  11. package/dist/server/behavior_tree_state.d.ts +38 -0
  12. package/dist/server/behavior_tree_state.js +220 -0
  13. package/dist/server/behavior_trees.d.ts +29 -0
  14. package/dist/server/behavior_trees.js +362 -0
  15. package/dist/server/files.d.ts +5 -2
  16. package/dist/server/files.js +32 -13
  17. package/dist/server/http.js +7 -5
  18. package/dist/server/rosbag.d.ts +25 -0
  19. package/dist/server/rosbag.js +418 -0
  20. package/dist/server/rosout.d.ts +16 -0
  21. package/dist/server/rosout.js +203 -0
  22. package/dist/server/rosout_log.d.ts +20 -0
  23. package/dist/server/rosout_log.js +66 -0
  24. package/dist/server/server.js +20 -4
  25. package/dist/server/session.d.ts +7 -0
  26. package/dist/server/session.js +63 -0
  27. package/dist/server/systemd.js +1 -1
  28. package/dist/server/systemd_format.d.ts +1 -0
  29. package/dist/server/systemd_format.js +19 -3
  30. package/dist/shared/bt_nodes.d.ts +25 -0
  31. package/dist/shared/bt_nodes.js +180 -0
  32. package/dist/shared/protocol.d.ts +157 -2
  33. package/dist/shared/protocol.js +4 -0
  34. package/dist/shared/xml.d.ts +25 -0
  35. package/dist/shared/xml.js +164 -0
  36. package/dist/types/client/api.d.ts +4 -3
  37. package/dist/types/client/connection.d.ts +31 -1
  38. package/dist/types/client/dialogs.d.ts +2 -1
  39. package/dist/types/client/icons.d.ts +24 -0
  40. package/dist/types/client/sdk/index.d.ts +3 -2
  41. package/dist/types/client/widgets/types.d.ts +5 -1
  42. package/dist/types/shared/bt_nodes.d.ts +25 -0
  43. package/dist/types/shared/protocol.d.ts +157 -2
  44. package/dist/www/assets/Joystick.js +1 -1
  45. package/dist/www/assets/RobotModelView.js +2 -2
  46. package/dist/www/assets/icons.js +1 -1
  47. package/dist/www/assets/index.css +1 -1
  48. package/dist/www/assets/index.js +28 -1
  49. package/dist/www/assets/shared/tassello-sdk.js +1 -1
  50. package/docs/plugins.md +8 -0
  51. package/package.json +2 -1
@@ -1,3 +1,4 @@
1
+ import type { BtNodeModel } from './bt_nodes.ts';
1
2
  export type ImageTransport = 'auto' | 'raw' | 'compressed';
2
3
  export type GridStyle = 'map' | 'costmap';
3
4
  export interface TwistCommand {
@@ -146,7 +147,52 @@ export interface PluginSubscribe {
146
147
  stream: string;
147
148
  params: unknown;
148
149
  }
149
- export type ClientMessage = TwistCommand | ImageSubscribe | GridSubscribe | TfSubscribe | PathSubscribe | TopicSubscribe | ServicesSubscribe | ServiceAction | LogsSubscribe | Unsubscribe | FrameAck | PosePublish | CommandRun | CommandStop | ArmSubscribe | ArmJog | RosPublish | RosCall | PluginRequest | PluginSubscribe;
150
+ export interface BehaviorTreeSubscribe {
151
+ type: 'bt.subscribe';
152
+ id: number;
153
+ topic: string;
154
+ tree: string;
155
+ }
156
+ export interface BehaviorTreeRecord {
157
+ type: 'bt.record';
158
+ id: number;
159
+ topic: string;
160
+ path: string | null;
161
+ }
162
+ export interface BehaviorTreePlaybackSubscribe {
163
+ type: 'bt.playback';
164
+ id: number;
165
+ bag: string;
166
+ topic: string;
167
+ tree: string;
168
+ }
169
+ export interface BehaviorTreePlaybackControl {
170
+ type: 'bt.playback.control';
171
+ id: number;
172
+ playing?: boolean;
173
+ speed?: number;
174
+ time?: number;
175
+ }
176
+ export interface BehaviorTreeFileSubscribe {
177
+ type: 'bt.file.subscribe';
178
+ id: number;
179
+ path: string;
180
+ libraries: string[];
181
+ }
182
+ export interface BehaviorTreeFileSave {
183
+ type: 'bt.file.save';
184
+ id: number;
185
+ path: string;
186
+ text: string;
187
+ base: string | null;
188
+ create: boolean;
189
+ }
190
+ export interface RosoutSubscribe {
191
+ type: 'rosout.subscribe';
192
+ id: number;
193
+ topic: string;
194
+ }
195
+ export type ClientMessage = TwistCommand | ImageSubscribe | GridSubscribe | TfSubscribe | PathSubscribe | TopicSubscribe | ServicesSubscribe | ServiceAction | LogsSubscribe | Unsubscribe | FrameAck | PosePublish | CommandRun | CommandStop | ArmSubscribe | ArmJog | RosPublish | RosCall | PluginRequest | PluginSubscribe | BehaviorTreeSubscribe | BehaviorTreeRecord | BehaviorTreePlaybackSubscribe | BehaviorTreePlaybackControl | BehaviorTreeFileSubscribe | BehaviorTreeFileSave | RosoutSubscribe;
150
196
  export interface Hello {
151
197
  type: 'hello';
152
198
  clientId: string;
@@ -277,7 +323,113 @@ export interface PluginData {
277
323
  id: number;
278
324
  data: unknown;
279
325
  }
280
- export type ServerMessage = Hello | CommandStates | CommandUpdate | StreamStatus | TfUpdate | PathUpdate | TopicMessage | ServicesUpdate | ServiceResult | LogsUpdate | ArmUpdate | Result | PluginData | PanelsChanged | ErrorMessage;
326
+ export type BtNodeKind = 'action' | 'condition' | 'control' | 'decorator' | 'subtree';
327
+ export interface BtNode {
328
+ name: string;
329
+ type: string;
330
+ kind: BtNodeKind | null;
331
+ parent: number;
332
+ ports: Record<string, string>;
333
+ }
334
+ export interface BehaviorTreeInfo {
335
+ type: 'bt.tree';
336
+ id: number;
337
+ treeId: string;
338
+ nodes: BtNode[];
339
+ error: string | null;
340
+ warnings: string[];
341
+ }
342
+ export interface BtNodeState {
343
+ node: number;
344
+ status: string | null;
345
+ result: string | null;
346
+ since: number;
347
+ changes: number;
348
+ successes: number;
349
+ failures: number;
350
+ }
351
+ export interface BtTransition {
352
+ time: number;
353
+ node: number;
354
+ from: string;
355
+ to: string;
356
+ }
357
+ export interface BehaviorTreeRecording {
358
+ type: 'bt.recording';
359
+ id: number;
360
+ recording: {
361
+ path: string;
362
+ since: number;
363
+ } | null;
364
+ }
365
+ export interface BehaviorTreePlaybackState {
366
+ type: 'bt.playback.state';
367
+ id: number;
368
+ topic: string;
369
+ start: number;
370
+ end: number;
371
+ time: number;
372
+ playing: boolean;
373
+ speed: number;
374
+ error: string | null;
375
+ }
376
+ export interface BehaviorTreeUpdate {
377
+ type: 'bt';
378
+ id: number;
379
+ reset: boolean;
380
+ added: BtNode[];
381
+ states: BtNodeState[];
382
+ transitions: BtTransition[];
383
+ mismatch: string | null;
384
+ }
385
+ export interface BehaviorTreeFile {
386
+ type: 'bt.file';
387
+ id: number;
388
+ file: string;
389
+ text: string | null;
390
+ version: string | null;
391
+ readOnly: string | null;
392
+ error: string | null;
393
+ }
394
+ export interface BtNodeLibrary {
395
+ name: string;
396
+ file: string;
397
+ models: BtNodeModel[];
398
+ }
399
+ export interface BehaviorTreeModels {
400
+ type: 'bt.models';
401
+ id: number;
402
+ libraries: BtNodeLibrary[];
403
+ trees: {
404
+ id: string;
405
+ file: string;
406
+ }[];
407
+ warnings: string[];
408
+ }
409
+ export interface BehaviorTreeSaved {
410
+ version: string;
411
+ conflict: boolean;
412
+ }
413
+ export interface LogEntry {
414
+ seq: number;
415
+ stamp: number;
416
+ level: number;
417
+ name: string;
418
+ msg: string;
419
+ file: string;
420
+ function: string;
421
+ line: number;
422
+ }
423
+ export interface RosoutUpdate {
424
+ type: 'rosout';
425
+ id: number;
426
+ reset: boolean;
427
+ started: number;
428
+ entries: LogEntry[];
429
+ skipped: number;
430
+ nodes: string[] | null;
431
+ }
432
+ export type ServerMessage = Hello | CommandStates | CommandUpdate | StreamStatus | TfUpdate | PathUpdate | TopicMessage | ServicesUpdate | ServiceResult | LogsUpdate | ArmUpdate | Result | PluginData | BehaviorTreeInfo | BehaviorTreeUpdate | BehaviorTreeRecording | BehaviorTreePlaybackState | BehaviorTreeFile | BehaviorTreeModels | RosoutUpdate | PanelsChanged | ErrorMessage;
281
433
  export declare const FRAME_HEADER_BYTES = 11;
282
434
  export declare const IMAGE_FORMATS: readonly ['image/jpeg', 'image/png'];
283
435
  export interface GridInfo {
@@ -297,6 +449,9 @@ export interface GridInfo {
297
449
  }
298
450
  export declare const TELEOP_WATCHDOG_MS = 500;
299
451
  export declare const ARM_STATE_RATE = 10;
452
+ export declare const BT_RATE = 10;
453
+ export declare const ROSOUT_RATE = 10;
454
+ export declare const ROSOUT_HISTORY = 10000;
300
455
  export declare const DASHBOARD_CLIENT_HEADER = "X-Dashboard-Client";
301
456
  export interface PluginInfo {
302
457
  name: string;
@@ -1 +1 @@
1
- import{n as e}from"./rolldown-runtime.js";import{t}from"./react.js";import{P as n,a as r,z as i}from"./icons.js";import{t as a}from"./jsx-runtime.js";async function o(e,t,r){let a=await fetch(t,{method:e,headers:{"Content-Type":`application/json`,[i]:n.clientId??``},body:r===void 0?void 0:JSON.stringify(r)});if(!a.ok){let e=`${a.status} ${a.statusText}`;try{e=(await a.json()).error??e}catch{}throw Error(e)}return a.status===204?void 0:await a.json()}var s=e=>`/api/panels/${encodeURIComponent(e)}`;async function c(e){let t=await fetch(`/api/uploads`,{method:`POST`,body:e});if(!t.ok){let e=await t.json().catch(()=>null);throw Error(e?.error??`${t.status} ${t.statusText}`)}return await t.json()}function l(e){let t=e.trim();if(t.startsWith(`uploads/`))return`/api/uploads/${encodeURIComponent(t.slice(8))}`;let n=/^package:\/\/([^/]+)\/(.+)$/.exec(t);return n?`/api/packages/${encodeURIComponent(n[1])}/${n[2].split(`/`).map(encodeURIComponent).join(`/`)}`:t.startsWith(`/`)||t.startsWith(`~/`)?`/api/files?path=${encodeURIComponent(t)}`:null}var u={listPanels:()=>o(`GET`,`/api/panels`),getPanel:e=>o(`GET`,s(e)),createPanel:e=>o(`POST`,`/api/panels`,e),savePanel:e=>o(`PUT`,s(e.id),e),deletePanel:e=>o(`DELETE`,s(e)),listTopics:()=>o(`GET`,`/api/topics`),listPlugins:()=>o(`GET`,`/api/plugins`),listFiles:e=>o(`GET`,`/api/files/list?dir=${encodeURIComponent(e)}`),uploadImage:async e=>`uploads/${(await c(e)).name}`},d=e(t(),1),f=e(a(),1);function p({title:e,onClose:t,children:n,wide:i=!1}){return(0,d.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,f.jsx)(`div`,{className:`modal-backdrop`,onPointerDown:e=>e.target===e.currentTarget&&t(),children:(0,f.jsxs)(`div`,{className:`modal${i?` wide`:``}`,role:`dialog`,"aria-modal":`true`,"aria-label":e,children:[(0,f.jsxs)(`header`,{className:`modal-header`,children:[(0,f.jsx)(`h2`,{children:e}),(0,f.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Close`,onClick:t,children:(0,f.jsx)(r,{})})]}),n]})})}var m=1,h=(0,d.createContext)(null);function g(){let e=(0,d.useContext)(h);if(!e)throw Error(`useDialogs() outside of <DialogProvider>`);return e}function _({children:e}){let[t,n]=(0,d.useState)(null),r=(0,d.useMemo)(()=>({confirm:e=>new Promise(t=>n({id:m++,kind:`confirm`,resolve:t,...e})),prompt:e=>new Promise(t=>n({id:m++,kind:`prompt`,resolve:t,...e}))}),[]);return(0,f.jsxs)(h.Provider,{value:r,children:[e,t&&(0,f.jsx)(v,{request:t,onDone:()=>n(null)},t.id)]})}function v({request:e,onDone:t}){let[n,r]=(0,d.useState)(e.kind===`prompt`?e.initial??``:``),i=(0,d.useRef)(null);(0,d.useEffect)(()=>i.current?.select(),[]);let a=r=>{e.kind===`confirm`?e.resolve(r):e.resolve(r&&n.trim()?n.trim():null),t()};return(0,f.jsx)(p,{title:e.title,onClose:()=>a(!1),children:(0,f.jsxs)(`form`,{className:`modal-body`,onSubmit:e=>{e.preventDefault(),a(!0)},children:[e.kind===`confirm`?e.message&&(0,f.jsx)(`div`,{className:`dialog-message`,children:e.message}):(0,f.jsxs)(`label`,{className:`field`,children:[(0,f.jsx)(`span`,{className:`field-label`,children:e.label}),(0,f.jsx)(`input`,{ref:i,value:n,onChange:e=>r(e.target.value),autoFocus:!0})]}),(0,f.jsxs)(`div`,{className:`modal-actions`,children:[(0,f.jsx)(`button`,{type:`button`,className:`button`,onClick:()=>a(!1),children:`Cancel`}),(0,f.jsx)(`button`,{type:`submit`,className:`button ${e.kind===`confirm`&&e.danger?`danger`:`primary`}`,disabled:e.kind===`prompt`&&!n.trim(),autoFocus:e.kind===`confirm`,children:e.confirmLabel??`OK`})]})]})})}var y=5e3,b=[],x=1,S=new Set;function C(e){b=e,S.forEach(e=>e())}function w(e){C(b.filter(t=>t.id!==e))}function T(e,t=`info`,n){let r=x++;C([...b.filter(t=>t.message!==e),{id:r,message:e,kind:t,action:n}].slice(-4)),setTimeout(()=>w(r),y)}function E(e){return S.add(e),()=>S.delete(e)}function D(){let e=(0,d.useSyncExternalStore)(E,()=>b);return(0,f.jsx)(`div`,{className:`notifications`,role:`status`,"aria-live":`polite`,children:e.map(e=>(0,f.jsxs)(`div`,{className:`notification ${e.kind}`,children:[(0,f.jsx)(`span`,{children:e.message}),e.action&&(0,f.jsx)(`button`,{type:`button`,className:`link-button`,onClick:()=>{e.action?.run(),w(e.id)},children:e.action.label}),(0,f.jsx)(`button`,{type:`button`,className:`notification-close`,"aria-label":`Dismiss`,onClick:()=>w(e.id),children:`×`})]},e.id))})}function O(e){return e}function k(e){return e}var A=34,j=.08;function M(e){let t=Math.abs(e);return t<j?0:Math.sign(e)*(t-j)/.92}function N({axes:e,onMove:t,disabled:n,label:r}){let i=(0,d.useRef)(null),a=(0,d.useRef)(null),[o,s]=(0,d.useState)({x:0,y:0}),[c,l]=(0,d.useState)(!1),u=(0,d.useRef)(t);u.current=t;let p=(e,t)=>{s({x:e,y:t}),u.current(M(e),M(t))},m=t=>{let n=i.current.getBoundingClientRect(),r=n.width*A/100,a=e===`y`?0:(t.clientX-(n.left+n.width/2))/r,o=e===`x`?0:-(t.clientY-(n.top+n.height/2))/r,s=Math.hypot(a,o);s>1&&(a/=s,o/=s),p(a,o)},h=e=>{a.current===null||e&&e.pointerId!==a.current||(a.current=null,l(!1),p(0,0))},g=(0,d.useRef)(h);return g.current=h,(0,d.useEffect)(()=>{let e=()=>document.hidden&&g.current();return document.addEventListener(`visibilitychange`,e),()=>document.removeEventListener(`visibilitychange`,e)},[]),(0,d.useEffect)(()=>{n&&g.current()},[n]),(0,f.jsxs)(`div`,{className:`joystick-slot`,children:[(0,f.jsxs)(`div`,{ref:i,className:`joystick${e===`x`?` horizontal`:e===`y`?` vertical`:``}${c?` active`:``}${n?` disabled`:``}`,onPointerDown:e=>{n||a.current!==null||e.button!==0||(a.current=e.pointerId,l(!0),e.currentTarget.setPointerCapture(e.pointerId),m(e))},onPointerMove:e=>e.pointerId===a.current&&m(e),onPointerUp:h,onPointerCancel:h,onLostPointerCapture:h,children:[(0,f.jsx)(`div`,{className:`joystick-track`}),(0,f.jsx)(`div`,{className:`joystick-knob`,style:{left:`${50+o.x*A}%`,top:`${50-o.y*A}%`}})]}),(0,f.jsx)(`span`,{className:`joystick-label`,children:r})]})}export{T as a,g as c,D as i,u as l,k as n,_ as o,O as r,p as s,N as t,l as u};
1
+ import{n as e}from"./rolldown-runtime.js";import{t}from"./react.js";import{it as n,lt as r,u as i}from"./icons.js";import{t as a}from"./jsx-runtime.js";async function o(e,t,i){let a=await fetch(t,{method:e,headers:{"Content-Type":`application/json`,[r]:n.clientId??``},body:i===void 0?void 0:JSON.stringify(i)});if(!a.ok){let e=`${a.status} ${a.statusText}`;try{e=(await a.json()).error??e}catch{}throw Error(e)}return a.status===204?void 0:await a.json()}var s=e=>`/api/panels/${encodeURIComponent(e)}`;async function c(e){let t=await fetch(`/api/uploads`,{method:`POST`,body:e});if(!t.ok){let e=await t.json().catch(()=>null);throw Error(e?.error??`${t.status} ${t.statusText}`)}return await t.json()}function l(e){let t=e.trim();if(t.startsWith(`uploads/`))return`/api/uploads/${encodeURIComponent(t.slice(8))}`;let n=/^package:\/\/([^/]+)\/(.+)$/.exec(t);return n?`/api/packages/${encodeURIComponent(n[1])}/${n[2].split(`/`).map(encodeURIComponent).join(`/`)}`:t.startsWith(`/`)||t.startsWith(`~/`)?`/api/files?path=${encodeURIComponent(t)}`:null}var u={listPanels:()=>o(`GET`,`/api/panels`),getPanel:e=>o(`GET`,s(e)),createPanel:e=>o(`POST`,`/api/panels`,e),savePanel:e=>o(`PUT`,s(e.id),e),deletePanel:e=>o(`DELETE`,s(e)),listTopics:()=>o(`GET`,`/api/topics`),listPlugins:()=>o(`GET`,`/api/plugins`),listFiles:(e,t=`image`)=>o(`GET`,`/api/files/list?dir=${encodeURIComponent(e)}&kind=${t}`),uploadFile:async e=>`uploads/${(await c(e)).name}`},d=e(t(),1),f=e(a(),1);function p({title:e,onClose:t,children:n,wide:r=!1,resizable:a=!1}){return(0,d.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,f.jsx)(`div`,{className:`modal-backdrop`,onPointerDown:e=>e.target===e.currentTarget&&t(),children:(0,f.jsxs)(`div`,{className:`modal${r?` wide`:``}${a?` resizable`:``}`,role:`dialog`,"aria-modal":`true`,"aria-label":e,children:[(0,f.jsxs)(`header`,{className:`modal-header`,children:[(0,f.jsx)(`h2`,{children:e}),(0,f.jsx)(`button`,{type:`button`,className:`icon-button`,"aria-label":`Close`,onClick:t,children:(0,f.jsx)(i,{})})]}),n]})})}var m=1,h=(0,d.createContext)(null);function g(){let e=(0,d.useContext)(h);if(!e)throw Error(`useDialogs() outside of <DialogProvider>`);return e}function _({children:e}){let[t,n]=(0,d.useState)(null),r=(0,d.useMemo)(()=>({confirm:e=>new Promise(t=>n({id:m++,kind:`confirm`,resolve:t,...e})),prompt:e=>new Promise(t=>n({id:m++,kind:`prompt`,resolve:t,...e}))}),[]);return(0,f.jsxs)(h.Provider,{value:r,children:[e,t&&(0,f.jsx)(v,{request:t,onDone:()=>n(null)},t.id)]})}function v({request:e,onDone:t}){let[n,r]=(0,d.useState)(e.kind===`prompt`?e.initial??``:``),i=(0,d.useRef)(null);(0,d.useEffect)(()=>i.current?.select(),[]);let a=r=>{e.kind===`confirm`?e.resolve(r):e.resolve(r&&n.trim()?n.trim():null),t()};return(0,f.jsx)(p,{title:e.title,onClose:()=>a(!1),children:(0,f.jsxs)(`form`,{className:`modal-body`,onSubmit:e=>{e.preventDefault(),a(!0)},children:[e.kind===`confirm`?e.message&&(0,f.jsx)(`div`,{className:`dialog-message`,children:e.message}):(0,f.jsxs)(`label`,{className:`field`,children:[(0,f.jsx)(`span`,{className:`field-label`,children:e.label}),(0,f.jsx)(`input`,{ref:i,value:n,onChange:e=>r(e.target.value),autoFocus:!0})]}),(0,f.jsxs)(`div`,{className:`modal-actions`,children:[(0,f.jsx)(`button`,{type:`button`,className:`button`,onClick:()=>a(!1),children:`Cancel`}),(0,f.jsx)(`button`,{type:`submit`,className:`button ${e.kind===`confirm`&&e.danger?`danger`:`primary`}`,disabled:e.kind===`prompt`&&!n.trim(),autoFocus:e.kind===`confirm`,children:e.confirmLabel??`OK`})]})]})})}var y=5e3,b=[],x=1,S=new Set;function C(e){b=e,S.forEach(e=>e())}function w(e){C(b.filter(t=>t.id!==e))}function T(e,t=`info`,n){let r=x++;C([...b.filter(t=>t.message!==e),{id:r,message:e,kind:t,action:n}].slice(-4)),setTimeout(()=>w(r),y)}function E(e){return S.add(e),()=>S.delete(e)}function D(){let e=(0,d.useSyncExternalStore)(E,()=>b);return(0,f.jsx)(`div`,{className:`notifications`,role:`status`,"aria-live":`polite`,children:e.map(e=>(0,f.jsxs)(`div`,{className:`notification ${e.kind}`,children:[(0,f.jsx)(`span`,{children:e.message}),e.action&&(0,f.jsx)(`button`,{type:`button`,className:`link-button`,onClick:()=>{e.action?.run(),w(e.id)},children:e.action.label}),(0,f.jsx)(`button`,{type:`button`,className:`notification-close`,"aria-label":`Dismiss`,onClick:()=>w(e.id),children:`×`})]},e.id))})}function O(e){return e}function k(e){return e}var A=34,j=.08;function M(e){let t=Math.abs(e);return t<j?0:Math.sign(e)*(t-j)/.92}function N({axes:e,onMove:t,disabled:n,label:r}){let i=(0,d.useRef)(null),a=(0,d.useRef)(null),[o,s]=(0,d.useState)({x:0,y:0}),[c,l]=(0,d.useState)(!1),u=(0,d.useRef)(t);u.current=t;let p=(e,t)=>{s({x:e,y:t}),u.current(M(e),M(t))},m=t=>{let n=i.current.getBoundingClientRect(),r=n.width*A/100,a=e===`y`?0:(t.clientX-(n.left+n.width/2))/r,o=e===`x`?0:-(t.clientY-(n.top+n.height/2))/r,s=Math.hypot(a,o);s>1&&(a/=s,o/=s),p(a,o)},h=e=>{a.current===null||e&&e.pointerId!==a.current||(a.current=null,l(!1),p(0,0))},g=(0,d.useRef)(h);return g.current=h,(0,d.useEffect)(()=>{let e=()=>document.hidden&&g.current();return document.addEventListener(`visibilitychange`,e),()=>document.removeEventListener(`visibilitychange`,e)},[]),(0,d.useEffect)(()=>{n&&g.current()},[n]),(0,f.jsxs)(`div`,{className:`joystick-slot`,children:[(0,f.jsxs)(`div`,{ref:i,className:`joystick${e===`x`?` horizontal`:e===`y`?` vertical`:``}${c?` active`:``}${n?` disabled`:``}`,onPointerDown:e=>{n||a.current!==null||e.button!==0||(a.current=e.pointerId,l(!0),e.currentTarget.setPointerCapture(e.pointerId),m(e))},onPointerMove:e=>e.pointerId===a.current&&m(e),onPointerUp:h,onPointerCancel:h,onLostPointerCapture:h,children:[(0,f.jsx)(`div`,{className:`joystick-track`}),(0,f.jsx)(`div`,{className:`joystick-knob`,style:{left:`${50+o.x*A}%`,top:`${50-o.y*A}%`}})]}),(0,f.jsx)(`span`,{className:`joystick-label`,children:r})]})}export{T as a,g as c,D as i,u as l,k as n,_ as o,O as r,p as s,N as t,l as u};