@almadar/shell-web 0.0.0-stage → 0.1.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 (274) hide show
  1. package/bundling.d.mts +18 -0
  2. package/bundling.mjs +28 -0
  3. package/dist/ArbitraryClassCompiler-Mzabq-UB.d.ts +100 -0
  4. package/dist/AvlGlyph-JKJOAH23.js +6 -0
  5. package/dist/AvlStateMachine-PI4YGREK.js +8 -0
  6. package/dist/AvlTransitionDetail-ZH7V24UG.js +12 -0
  7. package/dist/AvlTransitionExplainer-ZSRT7L6Y.js +13 -0
  8. package/dist/BehaviorValueFrame-6RUJXT4A.js +29 -0
  9. package/dist/BehaviorValueFrame-SSEMEBTG.css +1756 -0
  10. package/dist/BehaviorView-RFEE5J6E.js +10 -0
  11. package/dist/Canvas3DHost-_Y-5uLM_.d.ts +552 -0
  12. package/dist/EventBusProvider-CVwjkV_2.d.ts +284 -0
  13. package/dist/FileTree-BCFnBEuQ.d.ts +254 -0
  14. package/dist/FlowCanvas-3JRMNJLV.css +1756 -0
  15. package/dist/FlowCanvas-STDC4OWK.js +27 -0
  16. package/dist/GameAudioProvider-Cm9BnvmP.d.ts +93 -0
  17. package/dist/KaTeX_AMS-Regular-CYEKBG2K.woff +0 -0
  18. package/dist/KaTeX_AMS-Regular-JKX5W2C4.ttf +0 -0
  19. package/dist/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  20. package/dist/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  21. package/dist/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff +0 -0
  22. package/dist/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf +0 -0
  23. package/dist/KaTeX_Caligraphic-Regular-3LKEU76G.woff +0 -0
  24. package/dist/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf +0 -0
  25. package/dist/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  26. package/dist/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  27. package/dist/KaTeX_Fraktur-Bold-T4SWXBMT.woff +0 -0
  28. package/dist/KaTeX_Fraktur-Bold-WGHVTYOR.ttf +0 -0
  29. package/dist/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  30. package/dist/KaTeX_Fraktur-Regular-5U4OPH2X.ttf +0 -0
  31. package/dist/KaTeX_Fraktur-Regular-PQMHCIK6.woff +0 -0
  32. package/dist/KaTeX_Main-Bold-2GA4IZIN.woff +0 -0
  33. package/dist/KaTeX_Main-Bold-W5FBVCZM.ttf +0 -0
  34. package/dist/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  35. package/dist/KaTeX_Main-BoldItalic-4P4C7HJH.woff +0 -0
  36. package/dist/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  37. package/dist/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf +0 -0
  38. package/dist/KaTeX_Main-Italic-I43T2HSR.ttf +0 -0
  39. package/dist/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  40. package/dist/KaTeX_Main-Italic-SASNQFN2.woff +0 -0
  41. package/dist/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  42. package/dist/KaTeX_Main-Regular-P5I74A2A.woff +0 -0
  43. package/dist/KaTeX_Main-Regular-W74P5G27.ttf +0 -0
  44. package/dist/KaTeX_Math-BoldItalic-6EBV3DK5.woff +0 -0
  45. package/dist/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  46. package/dist/KaTeX_Math-BoldItalic-VB447A4D.ttf +0 -0
  47. package/dist/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  48. package/dist/KaTeX_Math-Italic-KKK3USB2.woff +0 -0
  49. package/dist/KaTeX_Math-Italic-SON4MRCA.ttf +0 -0
  50. package/dist/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  51. package/dist/KaTeX_SansSerif-Bold-STQ6RXC7.ttf +0 -0
  52. package/dist/KaTeX_SansSerif-Bold-X5M5EMOD.woff +0 -0
  53. package/dist/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  54. package/dist/KaTeX_SansSerif-Italic-PSN4QKYX.woff +0 -0
  55. package/dist/KaTeX_SansSerif-Italic-WTBAZBGY.ttf +0 -0
  56. package/dist/KaTeX_SansSerif-Regular-2TL3USAE.ttf +0 -0
  57. package/dist/KaTeX_SansSerif-Regular-OQCII6EP.woff +0 -0
  58. package/dist/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  59. package/dist/KaTeX_Script-Regular-72OLXYNA.ttf +0 -0
  60. package/dist/KaTeX_Script-Regular-A5IFOEBS.woff +0 -0
  61. package/dist/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  62. package/dist/KaTeX_Size1-Regular-4HRHTS65.woff +0 -0
  63. package/dist/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  64. package/dist/KaTeX_Size1-Regular-7K6AASVL.ttf +0 -0
  65. package/dist/KaTeX_Size2-Regular-222HN3GT.ttf +0 -0
  66. package/dist/KaTeX_Size2-Regular-K5ZHAIS6.woff +0 -0
  67. package/dist/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  68. package/dist/KaTeX_Size3-Regular-TLFPAHDE.woff +0 -0
  69. package/dist/KaTeX_Size3-Regular-UFCO6WCA.ttf +0 -0
  70. package/dist/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  71. package/dist/KaTeX_Size4-Regular-7PGNVPQK.ttf +0 -0
  72. package/dist/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  73. package/dist/KaTeX_Size4-Regular-PKMWZHNC.woff +0 -0
  74. package/dist/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf +0 -0
  75. package/dist/KaTeX_Typewriter-Regular-MJMFSK64.woff +0 -0
  76. package/dist/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  77. package/dist/KernelEngineContext-BOCQoFwk.d.ts +43 -0
  78. package/dist/LayersPanel-BBQ54IVG.js +7 -0
  79. package/dist/ModuleCard-3TT2X7IW.js +7 -0
  80. package/dist/OrbInspector-K6T6O7GI.js +27 -0
  81. package/dist/OrbInspector-MHLJ6OEE.css +1756 -0
  82. package/dist/Select-mDZIwNqN.d.ts +199 -0
  83. package/dist/ServerBridge-Cm1On51b.d.ts +233 -0
  84. package/dist/ThemeContext-DJmO59xd.d.ts +147 -0
  85. package/dist/TraitValueFrame-2PJGLLVI.js +28 -0
  86. package/dist/TraitValueFrame-4G3KOUBR.css +1756 -0
  87. package/dist/UISlotContext-Dl_ewwCI.d.ts +79 -0
  88. package/dist/UISlotRenderer-JKFSX24W.js +27 -0
  89. package/dist/UISlotRenderer-KVFO6J7R.css +1756 -0
  90. package/dist/UserContext-DqWeZkUZ.d.ts +100 -0
  91. package/dist/auth/browser/index.d.ts +2 -0
  92. package/dist/auth/browser/index.js +1 -0
  93. package/dist/auth/react/index.d.ts +49 -0
  94. package/dist/auth/react/index.js +119 -0
  95. package/dist/avl/index.css +1756 -0
  96. package/dist/avl/index.d.ts +2238 -0
  97. package/dist/avl/index.js +2693 -0
  98. package/dist/avl-3d-context-BIRKWpaL.d.ts +204 -0
  99. package/dist/avl-schema-parser-CtJxJ9KU.d.ts +132 -0
  100. package/dist/avl-zoom-state-CKMVuBFg.d.ts +17 -0
  101. package/dist/chunk-2PY54Z44.js +227 -0
  102. package/dist/chunk-2QOH2HWM.js +95 -0
  103. package/dist/chunk-3BBUBJFF.js +339 -0
  104. package/dist/chunk-42FY4RYA.js +191 -0
  105. package/dist/chunk-5QTYQTPS.js +15 -0
  106. package/dist/chunk-6P3MWXYE.js +440 -0
  107. package/dist/chunk-73SRAB4P.js +1053 -0
  108. package/dist/chunk-A2LR6VQQ.js +349 -0
  109. package/dist/chunk-AILY3XPS.js +562 -0
  110. package/dist/chunk-B4BU77LT.js +432 -0
  111. package/dist/chunk-DFNZUYX4.js +378 -0
  112. package/dist/chunk-DJ433ET5.js +1327 -0
  113. package/dist/chunk-DLZF4PVE.js +24 -0
  114. package/dist/chunk-DYEKAA4L.js +787 -0
  115. package/dist/chunk-DZ42OJQC.js +176 -0
  116. package/dist/chunk-EOWMARUB.js +16 -0
  117. package/dist/chunk-EQAMU2ZC.js +14 -0
  118. package/dist/chunk-FAJDTHWC.js +98 -0
  119. package/dist/chunk-FIW6WN23.js +508 -0
  120. package/dist/chunk-FP3WIKTC.js +303 -0
  121. package/dist/chunk-G7DCAZ22.js +19 -0
  122. package/dist/chunk-GAR5TQ5B.js +199 -0
  123. package/dist/chunk-GKIFONTE.js +131 -0
  124. package/dist/chunk-GYTPOFVC.js +47155 -0
  125. package/dist/chunk-KVQS4GXU.js +328 -0
  126. package/dist/chunk-L2DEKY4J.js +97 -0
  127. package/dist/chunk-L677XGY3.js +701 -0
  128. package/dist/chunk-MBQAMMSE.js +11877 -0
  129. package/dist/chunk-P4LNHAFI.js +1051 -0
  130. package/dist/chunk-P4ZFUQFF.js +93 -0
  131. package/dist/chunk-PC7K4SK7.js +1384 -0
  132. package/dist/chunk-R5O5AUPI.js +573 -0
  133. package/dist/chunk-SGM3FASH.js +672 -0
  134. package/dist/chunk-SJGJJKBY.js +502 -0
  135. package/dist/chunk-TESBIVUA.js +11 -0
  136. package/dist/chunk-TZZBINQY.js +82 -0
  137. package/dist/chunk-U6QRZASX.js +280 -0
  138. package/dist/chunk-VFF5OO47.js +319 -0
  139. package/dist/chunk-YDNJLLG6.js +516 -0
  140. package/dist/chunk-YHMRX6YA.js +455 -0
  141. package/dist/chunk-ZO6MXAS3.js +69 -0
  142. package/dist/components/index.css +1756 -0
  143. package/dist/components/index.d.ts +11093 -0
  144. package/dist/components/index.js +1110 -0
  145. package/dist/context/index.d.ts +9 -0
  146. package/dist/context/index.js +7 -0
  147. package/dist/core.d.ts +2523 -0
  148. package/dist/core.js +2 -0
  149. package/dist/design-classes/index.cjs +429 -0
  150. package/dist/design-classes/index.d.cts +165 -0
  151. package/dist/design-classes/index.d.ts +165 -0
  152. package/dist/design-classes/index.js +397 -0
  153. package/dist/emojiItems-QGSNAABW.js +20 -0
  154. package/dist/fx-CmQjnHmT.d.ts +371 -0
  155. package/dist/hooks/index.d.ts +936 -0
  156. package/dist/hooks/index.js +7 -0
  157. package/dist/index.d.ts +486 -0
  158. package/dist/index.js +38 -0
  159. package/dist/katexPlugin-FA7NX4PE.css +1161 -0
  160. package/dist/katexPlugin-NCDKOOQY.js +2 -0
  161. package/dist/kernel-client-ZH4PSMCN.js +367 -0
  162. package/dist/kernel-wire-B4S8pnP3.d.ts +65 -0
  163. package/dist/lib/index.d.ts +602 -0
  164. package/dist/lib/index.js +1288 -0
  165. package/dist/paintDispatch-ChW5mVuK.d.ts +698 -0
  166. package/dist/parseLessonSegments-GpCeMrb2.d.ts +334 -0
  167. package/dist/perf-CKiBKbkw.d.ts +22 -0
  168. package/dist/perf-Dr28-AgT.d.ts +57 -0
  169. package/dist/prism-AIWGN2X5.js +134 -0
  170. package/dist/providers/index.css +1756 -0
  171. package/dist/providers/index.d.ts +340 -0
  172. package/dist/providers/index.js +30 -0
  173. package/dist/renderer/index.d.ts +467 -0
  174. package/dist/renderer/index.js +3 -0
  175. package/dist/runtime/index.css +1756 -0
  176. package/dist/runtime/index.d.ts +440 -0
  177. package/dist/runtime/index.js +31 -0
  178. package/dist/slot-host-CPzrxLo9.d.ts +48 -0
  179. package/dist/ssr/index.cjs +7631 -0
  180. package/dist/ssr/index.d.cts +487 -0
  181. package/dist/ssr/index.d.ts +487 -0
  182. package/dist/ssr/index.js +7599 -0
  183. package/dist/storage/browser/index.d.ts +1 -0
  184. package/dist/storage/browser/index.js +1 -0
  185. package/dist/test-support.d.ts +35 -0
  186. package/dist/test-support.js +114 -0
  187. package/dist/themeTokens-CRd8AEDF.d.ts +125 -0
  188. package/dist/three/index.css +503 -0
  189. package/dist/three/index.d.ts +1185 -0
  190. package/dist/three/index.js +3896 -0
  191. package/dist/useKeyboardRouter-Dsq553Ur.d.ts +42 -0
  192. package/dist/useUISlots-CiobNFGb.d.ts +211 -0
  193. package/dist/verification-C5pTeMJv.d.ts +66 -0
  194. package/dist/verificationRegistry-CabxhfTv.d.ts +122 -0
  195. package/dist/wrapCallbackForEvent-B9QLbhxX.d.ts +19 -0
  196. package/index.css +199 -0
  197. package/package.json +269 -3
  198. package/src/core/locales/ar.json +1263 -0
  199. package/src/core/locales/en.json +1263 -0
  200. package/src/core/locales/index.ts +54 -0
  201. package/src/core/locales/sl.json +1263 -0
  202. package/src/core/tokens/native-tokens.json +369 -0
  203. package/tailwind-compile.cjs +29 -0
  204. package/tailwind-preset.cjs +991 -0
  205. package/themes/_base.css +451 -0
  206. package/themes/_contract.md +307 -0
  207. package/themes/_fonts-kenney.css +11 -0
  208. package/themes/_fonts.css +12 -0
  209. package/themes/_foundation.css +105 -0
  210. package/themes/_head.css +22 -0
  211. package/themes/_presets.css +67 -0
  212. package/themes/almadar-heritage.css +455 -0
  213. package/themes/almadar-website.css +269 -0
  214. package/themes/almadar.css +269 -0
  215. package/themes/aqua.css +455 -0
  216. package/themes/arabesque.css +459 -0
  217. package/themes/art-deco.css +457 -0
  218. package/themes/art-nouveau.css +459 -0
  219. package/themes/atelier.css +463 -0
  220. package/themes/bauhaus.css +461 -0
  221. package/themes/bloomberg-dense.css +463 -0
  222. package/themes/blueprint.css +457 -0
  223. package/themes/clay.css +449 -0
  224. package/themes/comic.css +453 -0
  225. package/themes/constructivist.css +457 -0
  226. package/themes/corporate.css +449 -0
  227. package/themes/cyberpunk.css +453 -0
  228. package/themes/frutiger-aero.css +458 -0
  229. package/themes/game-adventure.css +201 -0
  230. package/themes/game-rpg.css +203 -0
  231. package/themes/game-sci-fi.css +201 -0
  232. package/themes/game-ui-pack.css +200 -0
  233. package/themes/gazette.css +449 -0
  234. package/themes/glass.css +457 -0
  235. package/themes/gothic.css +453 -0
  236. package/themes/index.css +21 -0
  237. package/themes/ink-wash.css +459 -0
  238. package/themes/kawaii.css +459 -0
  239. package/themes/kflow.css +310 -0
  240. package/themes/kiosk.css +449 -0
  241. package/themes/linear-clean.css +463 -0
  242. package/themes/luxury.css +451 -0
  243. package/themes/mac-classic.css +455 -0
  244. package/themes/material.css +463 -0
  245. package/themes/memphis.css +453 -0
  246. package/themes/mid-century.css +455 -0
  247. package/themes/minimalist.css +449 -0
  248. package/themes/mudcloth.css +459 -0
  249. package/themes/nata.css +310 -0
  250. package/themes/neon.css +453 -0
  251. package/themes/neumorphic.css +451 -0
  252. package/themes/newsprint.css +455 -0
  253. package/themes/notion-editorial.css +463 -0
  254. package/themes/orb.css +465 -0
  255. package/themes/pixel.css +449 -0
  256. package/themes/prism.css +309 -0
  257. package/themes/rawi.css +310 -0
  258. package/themes/retro.css +452 -0
  259. package/themes/risograph.css +463 -0
  260. package/themes/scandi.css +469 -0
  261. package/themes/sketch.css +455 -0
  262. package/themes/solarpunk.css +469 -0
  263. package/themes/steampunk.css +451 -0
  264. package/themes/swiss.css +455 -0
  265. package/themes/terminal.css +466 -0
  266. package/themes/trait-wars.css +487 -0
  267. package/themes/ukiyo-e.css +459 -0
  268. package/themes/vaporwave.css +455 -0
  269. package/themes/victorian.css +453 -0
  270. package/themes/wabi-sabi.css +459 -0
  271. package/themes/win95.css +455 -0
  272. package/themes/wireframe.css +439 -0
  273. package/themes/zine.css +455 -0
  274. package/README.md +0 -3
@@ -0,0 +1,502 @@
1
+ import { AvlEffect } from './chunk-DZ42OJQC.js';
2
+ import { effectCategoryOf } from './chunk-DFNZUYX4.js';
3
+ import { useThemeScope, Box, useTapReveal, Typography, cn, domPassthrough, usePresence, useDialogBehavior, SurfaceBoundary, VStack, useTranslate } from './chunk-MBQAMMSE.js';
4
+ import { createPortal } from 'react-dom';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+ import React, { useId, useState, useRef, useLayoutEffect, useEffect } from 'react';
7
+ import { getStdOperatorMeta } from '@almadar/std/registry';
8
+
9
+ // src/lib/portalRoot.ts
10
+ function getOrCreatePortalRoot() {
11
+ let root = document.getElementById("ui-slot-portal-root");
12
+ if (!root) {
13
+ root = document.createElement("div");
14
+ root.id = "ui-slot-portal-root";
15
+ root.style.position = "relative";
16
+ root.style.zIndex = "9999";
17
+ document.body.appendChild(root);
18
+ }
19
+ return root;
20
+ }
21
+ function ThemedPortal({ children, container }) {
22
+ const { theme, mode, vars } = useThemeScope();
23
+ const scoped = theme !== void 0 || vars !== void 0;
24
+ return createPortal(
25
+ scoped ? /* @__PURE__ */ jsx(Box, { "data-theme": theme, className: mode ? `contents ${mode}` : "contents", style: vars, children }) : children,
26
+ container ?? getOrCreatePortalRoot()
27
+ );
28
+ }
29
+ var TRIGGER_GAP = 8;
30
+ var arrowClasses = {
31
+ top: "top-full left-1/2 -translate-x-1/2 border-t-primary border-l-transparent border-r-transparent border-b-transparent",
32
+ bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-primary border-l-transparent border-r-transparent border-t-transparent",
33
+ left: "left-full top-1/2 -translate-y-1/2 border-l-primary border-t-transparent border-b-transparent border-r-transparent",
34
+ right: "right-full top-1/2 -translate-y-1/2 border-r-primary border-t-transparent border-b-transparent border-l-transparent"
35
+ };
36
+ function computeTooltipStyle(position, triggerRect) {
37
+ switch (position) {
38
+ case "bottom":
39
+ return {
40
+ left: triggerRect.left + triggerRect.width / 2,
41
+ top: triggerRect.bottom + TRIGGER_GAP,
42
+ transform: "translateX(-50%)"
43
+ };
44
+ case "left":
45
+ return {
46
+ left: triggerRect.left - TRIGGER_GAP,
47
+ top: triggerRect.top + triggerRect.height / 2,
48
+ transform: "translate(-100%, -50%)"
49
+ };
50
+ case "right":
51
+ return {
52
+ left: triggerRect.right + TRIGGER_GAP,
53
+ top: triggerRect.top + triggerRect.height / 2,
54
+ transform: "translateY(-50%)"
55
+ };
56
+ case "top":
57
+ default:
58
+ return {
59
+ left: triggerRect.left + triggerRect.width / 2,
60
+ top: triggerRect.top - TRIGGER_GAP,
61
+ transform: "translate(-50%, -100%)"
62
+ };
63
+ }
64
+ }
65
+ var Tooltip = ({
66
+ content,
67
+ children,
68
+ position = "top",
69
+ delay = 200,
70
+ hideDelay = 0,
71
+ showArrow = true,
72
+ className,
73
+ ...rest
74
+ }) => {
75
+ const tooltipId = useId();
76
+ const [isVisible, setIsVisible] = useState(false);
77
+ const [triggerRect, setTriggerRect] = useState(null);
78
+ const triggerRef = useRef(null);
79
+ const tooltipRef = useRef(null);
80
+ const showTimeoutRef = useRef(null);
81
+ const hideTimeoutRef = useRef(null);
82
+ const updatePosition = () => {
83
+ if (triggerRef.current) {
84
+ setTriggerRect(triggerRef.current.getBoundingClientRect());
85
+ }
86
+ };
87
+ const handleMouseEnter = () => {
88
+ if (hideTimeoutRef.current) {
89
+ clearTimeout(hideTimeoutRef.current);
90
+ }
91
+ updatePosition();
92
+ showTimeoutRef.current = setTimeout(() => {
93
+ setIsVisible(true);
94
+ }, delay);
95
+ };
96
+ const handleMouseLeave = () => {
97
+ if (showTimeoutRef.current) {
98
+ clearTimeout(showTimeoutRef.current);
99
+ }
100
+ hideTimeoutRef.current = setTimeout(() => {
101
+ setIsVisible(false);
102
+ }, hideDelay);
103
+ };
104
+ const { triggerProps } = useTapReveal({
105
+ onReveal: handleMouseEnter,
106
+ onDismiss: handleMouseLeave,
107
+ refs: [triggerRef, tooltipRef]
108
+ });
109
+ useLayoutEffect(() => {
110
+ if (isVisible) {
111
+ updatePosition();
112
+ }
113
+ }, [isVisible]);
114
+ useEffect(() => {
115
+ if (!isVisible) return;
116
+ const onKeyDown = (e) => {
117
+ if (e.key !== "Escape") return;
118
+ if (showTimeoutRef.current) clearTimeout(showTimeoutRef.current);
119
+ setIsVisible(false);
120
+ };
121
+ document.addEventListener("keydown", onKeyDown);
122
+ return () => document.removeEventListener("keydown", onKeyDown);
123
+ }, [isVisible]);
124
+ useEffect(() => {
125
+ return () => {
126
+ if (showTimeoutRef.current) clearTimeout(showTimeoutRef.current);
127
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
128
+ };
129
+ }, []);
130
+ const triggerElement = React.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
131
+ const child = triggerElement;
132
+ const childRef = child.props.ref ?? child.ref;
133
+ const setTriggerRef = (el) => {
134
+ triggerRef.current = el;
135
+ if (typeof childRef === "function") childRef(el);
136
+ else if (childRef) childRef.current = el;
137
+ };
138
+ const trigger = React.cloneElement(child, {
139
+ ref: setTriggerRef,
140
+ "aria-describedby": isVisible ? [child.props["aria-describedby"], tooltipId].filter(Boolean).join(" ") : child.props["aria-describedby"],
141
+ onMouseEnter: (e) => {
142
+ child.props.onMouseEnter?.(e);
143
+ handleMouseEnter();
144
+ },
145
+ onMouseLeave: (e) => {
146
+ child.props.onMouseLeave?.(e);
147
+ handleMouseLeave();
148
+ },
149
+ onFocus: (e) => {
150
+ child.props.onFocus?.(e);
151
+ handleMouseEnter();
152
+ },
153
+ onBlur: (e) => {
154
+ child.props.onBlur?.(e);
155
+ handleMouseLeave();
156
+ },
157
+ onPointerDown: (e) => {
158
+ triggerProps.onPointerDown(e);
159
+ child.props.onPointerDown?.(e);
160
+ }
161
+ });
162
+ const tooltipContent = isVisible && triggerRect ? /* @__PURE__ */ jsxs(
163
+ "div",
164
+ {
165
+ ref: tooltipRef,
166
+ ...domPassthrough(rest),
167
+ id: tooltipId,
168
+ className: cn(
169
+ "fixed z-50 px-3 py-2 max-w-xs",
170
+ "bg-primary text-primary-foreground",
171
+ "shadow-elevation-popover rounded-container",
172
+ "text-sm pointer-events-none",
173
+ "break-words whitespace-normal",
174
+ "h-auto min-h-fit",
175
+ className
176
+ ),
177
+ style: computeTooltipStyle(position, triggerRect),
178
+ role: "tooltip",
179
+ children: [
180
+ /* @__PURE__ */ jsx("div", { className: "w-full break-words whitespace-normal h-auto", children: typeof content === "string" ? /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-primary-foreground break-words whitespace-normal", children: content }) : /* @__PURE__ */ jsx("div", { className: "break-words whitespace-normal", children: content }) }),
181
+ showArrow && /* @__PURE__ */ jsx(
182
+ "div",
183
+ {
184
+ className: cn(
185
+ "absolute w-0 h-0 border-heavy",
186
+ arrowClasses[position]
187
+ )
188
+ }
189
+ )
190
+ ]
191
+ }
192
+ ) : null;
193
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
194
+ trigger,
195
+ typeof window !== "undefined" && tooltipContent ? /* @__PURE__ */ jsx(ThemedPortal, { children: tooltipContent }) : tooltipContent
196
+ ] });
197
+ };
198
+ Tooltip.displayName = "Tooltip";
199
+ var INTERACTIVE_SELECTOR = 'button, a[href], input, select, textarea, [role="button"], [tabindex]:not([tabindex="-1"])';
200
+ var HOVER_CLOSE_DELAY_MS = 150;
201
+ var arrowClasses2 = {
202
+ top: "top-full left-1/2 -translate-x-1/2 border-t-white border-l-transparent border-r-transparent border-b-transparent",
203
+ bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-white border-l-transparent border-r-transparent border-t-transparent",
204
+ left: "left-full top-1/2 -translate-y-1/2 border-l-white border-t-transparent border-b-transparent border-r-transparent",
205
+ right: "right-full top-1/2 -translate-y-1/2 border-r-white border-t-transparent border-b-transparent border-l-transparent"
206
+ };
207
+ var VIEWPORT_EDGE_PADDING = 8;
208
+ var TRIGGER_GAP2 = 8;
209
+ function placePopover(position, trigger, panel, viewport) {
210
+ const clamp = (value, size, extent) => Math.max(VIEWPORT_EDGE_PADDING, Math.min(value, Math.max(VIEWPORT_EDGE_PADDING, extent - size - VIEWPORT_EDGE_PADDING)));
211
+ if (position === "left" || position === "right") {
212
+ const rightLeft = trigger.right + TRIGGER_GAP2;
213
+ const leftLeft = trigger.left - panel.width - TRIGGER_GAP2;
214
+ const fitsRight = rightLeft + panel.width <= viewport.width - VIEWPORT_EDGE_PADDING;
215
+ const fitsLeft = leftLeft >= VIEWPORT_EDGE_PADDING;
216
+ const side2 = position === "right" ? fitsRight || !fitsLeft ? "right" : "left" : fitsLeft || !fitsRight ? "left" : "right";
217
+ const left2 = clamp(side2 === "right" ? rightLeft : leftLeft, panel.width, viewport.width);
218
+ const top2 = clamp(trigger.top + trigger.height / 2 - panel.height / 2, panel.height, viewport.height);
219
+ return { side: side2, left: left2, top: top2 };
220
+ }
221
+ const aboveTop = trigger.top - TRIGGER_GAP2 - panel.height;
222
+ const belowTop = trigger.bottom + TRIGGER_GAP2;
223
+ const fitsAbove = aboveTop >= VIEWPORT_EDGE_PADDING;
224
+ const fitsBelow = belowTop + panel.height <= viewport.height - VIEWPORT_EDGE_PADDING;
225
+ const side = position === "top" ? fitsAbove || !fitsBelow ? "top" : "bottom" : fitsBelow || !fitsAbove ? "bottom" : "top";
226
+ const left = clamp(trigger.left + trigger.width / 2 - panel.width / 2, panel.width, viewport.width);
227
+ const top = clamp(side === "top" ? aboveTop : belowTop, panel.height, viewport.height);
228
+ return { side, left, top };
229
+ }
230
+ var Popover = ({
231
+ content,
232
+ children,
233
+ position = "bottom",
234
+ trigger = "click",
235
+ showArrow = true,
236
+ open,
237
+ onOpenChange,
238
+ className,
239
+ ...rest
240
+ }) => {
241
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
242
+ const isOpen = open !== void 0 ? open : uncontrolledOpen;
243
+ const setIsOpen = (next) => {
244
+ if (open === void 0) setUncontrolledOpen(next);
245
+ onOpenChange?.(next);
246
+ };
247
+ const [triggerRect, setTriggerRect] = useState(null);
248
+ const [panelSize, setPanelSize] = useState({ width: 0, height: 0 });
249
+ const popoverWidth = panelSize.width;
250
+ const triggerRef = useRef(null);
251
+ const popoverRef = useRef(null);
252
+ const { mounted, className: panelAnim, onAnimationEnd } = usePresence(isOpen, { animation: "popover" });
253
+ const updatePosition = () => {
254
+ if (triggerRef.current) {
255
+ setTriggerRect(triggerRef.current.getBoundingClientRect());
256
+ }
257
+ if (popoverRef.current) {
258
+ setPanelSize({ width: popoverRef.current.offsetWidth, height: popoverRef.current.offsetHeight });
259
+ }
260
+ };
261
+ const handleOpen = () => {
262
+ updatePosition();
263
+ setIsOpen(true);
264
+ };
265
+ const handleClose = () => {
266
+ cancelHoverClose();
267
+ setIsOpen(false);
268
+ };
269
+ const closeTimer = useRef(null);
270
+ const cancelHoverClose = () => {
271
+ if (closeTimer.current !== null) clearTimeout(closeTimer.current);
272
+ closeTimer.current = null;
273
+ };
274
+ const scheduleHoverClose = () => {
275
+ cancelHoverClose();
276
+ closeTimer.current = setTimeout(handleClose, HOVER_CLOSE_DELAY_MS);
277
+ };
278
+ const hoverOpen = () => {
279
+ cancelHoverClose();
280
+ if (!isOpen) handleOpen();
281
+ };
282
+ useEffect(() => cancelHoverClose, []);
283
+ const panelId = useId();
284
+ const focusReturnRef = useRef(null);
285
+ useDialogBehavior({ open: isOpen, containerRef: popoverRef, onEscape: handleClose, modal: false, ...trigger === "click" ? { returnFocusRef: focusReturnRef } : {} });
286
+ const handleToggle = () => {
287
+ if (isOpen) {
288
+ handleClose();
289
+ } else {
290
+ handleOpen();
291
+ }
292
+ };
293
+ useEffect(() => {
294
+ if (isOpen) {
295
+ updatePosition();
296
+ }
297
+ }, [isOpen]);
298
+ useEffect(() => {
299
+ if (!isOpen) return;
300
+ let raf = 0;
301
+ let lastTop = Number.NaN;
302
+ let lastLeft = Number.NaN;
303
+ const track = () => {
304
+ const rect = triggerRef.current?.getBoundingClientRect();
305
+ if (rect && (rect.top !== lastTop || rect.left !== lastLeft)) {
306
+ lastTop = rect.top;
307
+ lastLeft = rect.left;
308
+ updatePosition();
309
+ }
310
+ raf = requestAnimationFrame(track);
311
+ };
312
+ raf = requestAnimationFrame(track);
313
+ return () => cancelAnimationFrame(raf);
314
+ }, [isOpen]);
315
+ useEffect(() => {
316
+ if (!mounted) setPanelSize({ width: 0, height: 0 });
317
+ }, [mounted]);
318
+ useLayoutEffect(() => {
319
+ if (isOpen && popoverRef.current) {
320
+ const width = popoverRef.current.offsetWidth;
321
+ const height = popoverRef.current.offsetHeight;
322
+ if (width !== panelSize.width || height !== panelSize.height) {
323
+ setPanelSize({ width, height });
324
+ }
325
+ }
326
+ });
327
+ useEffect(() => {
328
+ if (trigger !== "click") {
329
+ return;
330
+ }
331
+ const handleClickOutside = (e) => {
332
+ if (isOpen && popoverRef.current && !popoverRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
333
+ handleClose();
334
+ }
335
+ };
336
+ document.addEventListener("mousedown", handleClickOutside);
337
+ return () => document.removeEventListener("mousedown", handleClickOutside);
338
+ }, [isOpen, trigger]);
339
+ const { triggerProps: tapTriggerProps } = useTapReveal({
340
+ enabled: trigger === "hover",
341
+ onReveal: handleOpen,
342
+ onDismiss: handleClose,
343
+ refs: [triggerRef, popoverRef]
344
+ });
345
+ const handlerProps = trigger === "click" ? {
346
+ onClick: handleToggle
347
+ } : {
348
+ onMouseEnter: hoverOpen,
349
+ onMouseLeave: scheduleHoverClose,
350
+ onFocus: hoverOpen,
351
+ onBlur: scheduleHoverClose,
352
+ onPointerDown: tapTriggerProps.onPointerDown,
353
+ "data-popover-open": String(isOpen)
354
+ };
355
+ const wrapsContent = !React.isValidElement(children);
356
+ const [innerControl, setInnerControl] = useState(null);
357
+ useLayoutEffect(() => {
358
+ if (!wrapsContent || trigger !== "click") return;
359
+ setInnerControl(triggerRef.current?.querySelector(INTERACTIVE_SELECTOR) ?? null);
360
+ }, [wrapsContent, trigger, children]);
361
+ useLayoutEffect(() => {
362
+ focusReturnRef.current = innerControl ?? triggerRef.current;
363
+ });
364
+ useLayoutEffect(() => {
365
+ if (innerControl === null) return;
366
+ innerControl.setAttribute("aria-expanded", String(isOpen));
367
+ innerControl.setAttribute("aria-haspopup", "dialog");
368
+ if (mounted) innerControl.setAttribute("aria-controls", panelId);
369
+ else innerControl.removeAttribute("aria-controls");
370
+ }, [innerControl, isOpen, mounted, panelId]);
371
+ const triggerIsWrapper = innerControl === null;
372
+ const childElement = !wrapsContent ? children : trigger === "click" && triggerIsWrapper ? /* @__PURE__ */ jsx(
373
+ "span",
374
+ {
375
+ role: "button",
376
+ tabIndex: 0,
377
+ onKeyDown: (e) => {
378
+ if (e.key === "Enter" || e.key === " ") {
379
+ e.preventDefault();
380
+ handleToggle();
381
+ }
382
+ },
383
+ children
384
+ }
385
+ ) : /* @__PURE__ */ jsx("span", { children });
386
+ const childPointerDown = childElement.props.onPointerDown;
387
+ const triggerElement = React.cloneElement(
388
+ childElement,
389
+ {
390
+ ref: triggerRef,
391
+ ...handlerProps,
392
+ // Dialog semantics belong to a click-opened panel; a hover popover is a tooltip.
393
+ ...trigger === "click" && triggerIsWrapper ? {
394
+ "aria-expanded": isOpen,
395
+ "aria-haspopup": "dialog",
396
+ ...mounted ? { "aria-controls": panelId } : void 0
397
+ } : void 0,
398
+ ...trigger === "hover" ? {
399
+ onPointerDown: (e) => {
400
+ tapTriggerProps.onPointerDown(e);
401
+ childPointerDown?.(e);
402
+ }
403
+ } : void 0
404
+ }
405
+ );
406
+ const placement = triggerRect ? placePopover(position, triggerRect, panelSize, {
407
+ width: typeof window !== "undefined" ? window.innerWidth : 1024,
408
+ height: typeof window !== "undefined" ? window.innerHeight : 768
409
+ }) : null;
410
+ const panel = mounted && placement ? /* @__PURE__ */ jsxs(
411
+ "div",
412
+ {
413
+ ref: popoverRef,
414
+ className: cn(
415
+ "fixed z-50 p-4",
416
+ "bg-card surface-material rounded-container border-heavy border-border shadow-elevation-popover",
417
+ panelAnim,
418
+ className
419
+ ),
420
+ style: {
421
+ left: placement.left,
422
+ top: placement.top,
423
+ ...popoverWidth === 0 ? { visibility: "hidden" } : void 0
424
+ },
425
+ ...domPassthrough(rest),
426
+ id: panelId,
427
+ role: "dialog",
428
+ "aria-label": rest["aria-label"] ?? (typeof content === "string" ? content : void 0),
429
+ onAnimationEnd,
430
+ onMouseEnter: trigger === "hover" ? hoverOpen : void 0,
431
+ onMouseLeave: trigger === "hover" ? scheduleHoverClose : void 0,
432
+ children: [
433
+ typeof content === "string" ? /* @__PURE__ */ jsx(Typography, { variant: "body", children: content }) : /* @__PURE__ */ jsx(SurfaceBoundary, { children: content }),
434
+ showArrow && /* @__PURE__ */ jsx(
435
+ "div",
436
+ {
437
+ className: cn(
438
+ "absolute w-0 h-0 border-heavy",
439
+ arrowClasses2[placement.side]
440
+ )
441
+ }
442
+ )
443
+ ]
444
+ }
445
+ ) : null;
446
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
447
+ triggerElement,
448
+ panel && typeof document !== "undefined" ? /* @__PURE__ */ jsx(ThemedPortal, { children: panel }) : panel
449
+ ] });
450
+ };
451
+ Popover.displayName = "Popover";
452
+ var AvlExplain = ({ lines, note, code, children }) => {
453
+ if (note !== void 0 || code !== void 0) {
454
+ return /* @__PURE__ */ jsx(
455
+ Popover,
456
+ {
457
+ trigger: "hover",
458
+ position: "top",
459
+ content: /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "max-w-sm", children: [
460
+ note?.title ? /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "semibold", children: note.title }) : null,
461
+ note ? /* @__PURE__ */ jsx(Typography, { variant: "small", children: note.body }) : null,
462
+ /* @__PURE__ */ jsx(VStack, { gap: "none", className: note ? "border-t border-border pt-1" : void 0, children: lines.map((line, i) => /* @__PURE__ */ jsx(Typography, { variant: "caption", color: i === 0 ? "inherit" : "muted", weight: i === 0 ? "semibold" : "normal", children: line }, i)) }),
463
+ code === void 0 ? null : /* @__PURE__ */ jsx(
464
+ Box,
465
+ {
466
+ as: "pre",
467
+ dir: "ltr",
468
+ "data-testid": "avl-explain-code",
469
+ className: "m-0 max-h-48 overflow-auto rounded-sm bg-muted px-2 py-1 font-mono text-xs whitespace-pre",
470
+ children: code
471
+ }
472
+ )
473
+ ] }),
474
+ children
475
+ }
476
+ );
477
+ }
478
+ if (lines.length === 0) return children;
479
+ return /* @__PURE__ */ jsx(
480
+ Tooltip,
481
+ {
482
+ position: "top",
483
+ content: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => /* @__PURE__ */ jsx(Box, { as: "span", className: i === 0 ? "font-semibold" : "opacity-80", children: line }, i)) }),
484
+ children
485
+ }
486
+ );
487
+ };
488
+ AvlExplain.displayName = "AvlExplain";
489
+ function useEffectLines(effectType) {
490
+ const { t } = useTranslate();
491
+ const category = effectCategoryOf(effectType);
492
+ const description = getStdOperatorMeta(effectType)?.description;
493
+ return [effectType, ...category ? [t(`avl.effectCategory.${category}`)] : [], ...description ? [description] : []];
494
+ }
495
+ var AvlEffectChip = ({ effectType, size = 16, note, code }) => {
496
+ const lines = useEffectLines(effectType);
497
+ const half = size / 2;
498
+ return /* @__PURE__ */ jsx(AvlExplain, { lines, note, code, children: /* @__PURE__ */ jsx(Box, { as: "span", role: "img", tabIndex: 0, "aria-label": effectType, "data-testid": "avl-effect-chip", className: "inline-flex shrink-0 rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx(AvlEffect, { x: half, y: half, effectType, size: half / 1.2 - 0.5, showBackground: true }) }) }) });
499
+ };
500
+ AvlEffectChip.displayName = "AvlEffectChip";
501
+
502
+ export { AvlEffectChip, AvlExplain, Popover, ThemedPortal, Tooltip, getOrCreatePortalRoot, useEffectLines };
@@ -0,0 +1,11 @@
1
+ import { AvlTransitionDetail } from './chunk-SGM3FASH.js';
2
+ import { Box, domPassthrough } from './chunk-MBQAMMSE.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var AvlTransitionExplainer = ({ orbitalName, traitName, transition, annotations, className, ...rest }) => {
6
+ const detail = { ...transition, guard: transition.guard ?? null };
7
+ return /* @__PURE__ */ jsx(Box, { ...domPassthrough(rest), className, children: /* @__PURE__ */ jsx(AvlTransitionDetail, { orbital: orbitalName, trait: traitName, transition: detail, annotations }) });
8
+ };
9
+ AvlTransitionExplainer.displayName = "AvlTransitionExplainer";
10
+
11
+ export { AvlTransitionExplainer };
@@ -0,0 +1,82 @@
1
+ // src/lib/jazari/svg-paths.ts
2
+ function gearTeethPath(cx, cy, innerRadius, outerRadius, numTeeth) {
3
+ const parts = [];
4
+ const angleStep = Math.PI * 2 / numTeeth;
5
+ const halfTooth = angleStep * 0.25;
6
+ for (let i = 0; i < numTeeth; i++) {
7
+ const baseAngle = i * angleStep;
8
+ const x0 = cx + innerRadius * Math.cos(baseAngle - halfTooth);
9
+ const y0 = cy + innerRadius * Math.sin(baseAngle - halfTooth);
10
+ const x1 = cx + outerRadius * Math.cos(baseAngle - halfTooth * 0.4);
11
+ const y1 = cy + outerRadius * Math.sin(baseAngle - halfTooth * 0.4);
12
+ const x2 = cx + outerRadius * Math.cos(baseAngle + halfTooth * 0.4);
13
+ const y2 = cy + outerRadius * Math.sin(baseAngle + halfTooth * 0.4);
14
+ const x3 = cx + innerRadius * Math.cos(baseAngle + halfTooth);
15
+ const y3 = cy + innerRadius * Math.sin(baseAngle + halfTooth);
16
+ if (i === 0) {
17
+ parts.push(`M ${x0.toFixed(1)} ${y0.toFixed(1)}`);
18
+ } else {
19
+ parts.push(`L ${x0.toFixed(1)} ${y0.toFixed(1)}`);
20
+ }
21
+ parts.push(`L ${x1.toFixed(1)} ${y1.toFixed(1)}`);
22
+ parts.push(`L ${x2.toFixed(1)} ${y2.toFixed(1)}`);
23
+ parts.push(`L ${x3.toFixed(1)} ${y3.toFixed(1)}`);
24
+ }
25
+ parts.push("Z");
26
+ return parts.join(" ");
27
+ }
28
+ function lockIconPath(cx, cy, size) {
29
+ const bodyW = size * 0.7;
30
+ const bodyH = size * 0.5;
31
+ const bodyX = cx - bodyW / 2;
32
+ const bodyY = cy - bodyH / 4;
33
+ const body = `M ${bodyX.toFixed(1)} ${bodyY.toFixed(1)} h ${bodyW.toFixed(1)} v ${bodyH.toFixed(1)} h ${(-bodyW).toFixed(1)} Z`;
34
+ const shR = size * 0.28;
35
+ const shY = bodyY;
36
+ const shackle = `M ${(cx - shR).toFixed(1)} ${shY.toFixed(1)} A ${shR.toFixed(1)} ${(shR * 1.2).toFixed(1)} 0 1 1 ${(cx + shR).toFixed(1)} ${shY.toFixed(1)}`;
37
+ const kR = size * 0.08;
38
+ const kY = bodyY + bodyH * 0.35;
39
+ const keyhole = `M ${cx.toFixed(1)} ${(kY - kR).toFixed(1)} a ${kR.toFixed(1)} ${kR.toFixed(1)} 0 1 1 0 ${(kR * 2).toFixed(1)} a ${kR.toFixed(1)} ${kR.toFixed(1)} 0 1 1 0 ${(-kR * 2).toFixed(1)} Z`;
40
+ return `${body} ${shackle} ${keyhole}`;
41
+ }
42
+ function brainIconPath(cx, cy, size) {
43
+ const s = size / 2;
44
+ return [
45
+ `M ${cx.toFixed(1)} ${(cy - s).toFixed(1)}`,
46
+ `C ${(cx - s * 1.2).toFixed(1)} ${(cy - s).toFixed(1)}, ${(cx - s * 1.3).toFixed(1)} ${(cy + s * 0.3).toFixed(1)}, ${cx.toFixed(1)} ${(cy + s).toFixed(1)}`,
47
+ `C ${(cx + s * 1.3).toFixed(1)} ${(cy + s * 0.3).toFixed(1)}, ${(cx + s * 1.2).toFixed(1)} ${(cy - s).toFixed(1)}, ${cx.toFixed(1)} ${(cy - s).toFixed(1)}`,
48
+ "Z",
49
+ // Dividing line
50
+ `M ${cx.toFixed(1)} ${(cy - s * 0.8).toFixed(1)}`,
51
+ `C ${(cx - s * 0.2).toFixed(1)} ${(cy - s * 0.2).toFixed(1)}, ${(cx + s * 0.2).toFixed(1)} ${(cy + s * 0.2).toFixed(1)}, ${cx.toFixed(1)} ${(cy + s * 0.8).toFixed(1)}`
52
+ ].join(" ");
53
+ }
54
+ function pipeIconPath(cx, cy, size) {
55
+ const w = size * 0.6;
56
+ const h = size * 0.3;
57
+ const flangeW = size * 0.8;
58
+ const flangeH = size * 0.1;
59
+ const tube = `M ${(cx - w / 2).toFixed(1)} ${(cy - h / 2).toFixed(1)} h ${w.toFixed(1)} v ${h.toFixed(1)} h ${(-w).toFixed(1)} Z`;
60
+ const lf = `M ${(cx - flangeW / 2).toFixed(1)} ${(cy - h / 2 - flangeH).toFixed(1)} h ${flangeH.toFixed(1)} v ${(h + flangeH * 2).toFixed(1)} h ${(-flangeH).toFixed(1)} Z`;
61
+ const rf = `M ${(cx + flangeW / 2 - flangeH).toFixed(1)} ${(cy - h / 2 - flangeH).toFixed(1)} h ${flangeH.toFixed(1)} v ${(h + flangeH * 2).toFixed(1)} h ${(-flangeH).toFixed(1)} Z`;
62
+ return `${tube} ${lf} ${rf}`;
63
+ }
64
+ function eightPointedStarPath(cx, cy, outerR, innerR) {
65
+ const points = [];
66
+ const numPoints = 8;
67
+ const angleStep = Math.PI / numPoints;
68
+ for (let i = 0; i < numPoints * 2; i++) {
69
+ const angle = i * angleStep - Math.PI / 2;
70
+ const r = i % 2 === 0 ? outerR : innerR;
71
+ const x = cx + r * Math.cos(angle);
72
+ const y = cy + r * Math.sin(angle);
73
+ points.push(`${i === 0 ? "M" : "L"} ${x.toFixed(1)} ${y.toFixed(1)}`);
74
+ }
75
+ points.push("Z");
76
+ return points.join(" ");
77
+ }
78
+ function arrowheadPath(size) {
79
+ return `M 0 0 L ${size} ${size / 2} L 0 ${size} Z`;
80
+ }
81
+
82
+ export { arrowheadPath, brainIconPath, eightPointedStarPath, gearTeethPath, lockIconPath, pipeIconPath };