@hyperfixi/behaviors 3.2.0 → 4.0.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 (232) hide show
  1. package/CHANGELOG.md +203 -1
  2. package/README.md +29 -9
  3. package/dist/all.browser.global.js +47 -102
  4. package/dist/all.browser.global.js.map +1 -1
  5. package/dist/autodismiss.browser.global.js +5 -11
  6. package/dist/autodismiss.browser.global.js.map +1 -1
  7. package/dist/behaviors/autodismiss.cjs +19 -22
  8. package/dist/behaviors/autodismiss.cjs.map +1 -1
  9. package/dist/behaviors/autodismiss.d.cts +4 -4
  10. package/dist/behaviors/autodismiss.d.ts +4 -4
  11. package/dist/behaviors/autodismiss.js +3 -3
  12. package/dist/behaviors/clickoutside.cjs +19 -18
  13. package/dist/behaviors/clickoutside.cjs.map +1 -1
  14. package/dist/behaviors/clickoutside.d.cts +4 -4
  15. package/dist/behaviors/clickoutside.d.ts +4 -4
  16. package/dist/behaviors/clickoutside.js +3 -3
  17. package/dist/behaviors/clipboard.cjs +18 -19
  18. package/dist/behaviors/clipboard.cjs.map +1 -1
  19. package/dist/behaviors/clipboard.d.cts +4 -4
  20. package/dist/behaviors/clipboard.d.ts +4 -4
  21. package/dist/behaviors/clipboard.js +3 -3
  22. package/dist/behaviors/draggable.cjs +20 -25
  23. package/dist/behaviors/draggable.cjs.map +1 -1
  24. package/dist/behaviors/draggable.d.cts +4 -4
  25. package/dist/behaviors/draggable.d.ts +4 -4
  26. package/dist/behaviors/draggable.js +3 -3
  27. package/dist/behaviors/focustrap.cjs +18 -19
  28. package/dist/behaviors/focustrap.cjs.map +1 -1
  29. package/dist/behaviors/focustrap.d.cts +4 -4
  30. package/dist/behaviors/focustrap.d.ts +4 -4
  31. package/dist/behaviors/focustrap.js +3 -3
  32. package/dist/behaviors/removable.cjs +18 -21
  33. package/dist/behaviors/removable.cjs.map +1 -1
  34. package/dist/behaviors/removable.d.cts +4 -3
  35. package/dist/behaviors/removable.d.ts +4 -3
  36. package/dist/behaviors/removable.js +3 -3
  37. package/dist/behaviors/resizable.cjs +21 -21
  38. package/dist/behaviors/resizable.cjs.map +1 -1
  39. package/dist/behaviors/resizable.d.cts +4 -4
  40. package/dist/behaviors/resizable.d.ts +4 -4
  41. package/dist/behaviors/resizable.js +3 -3
  42. package/dist/behaviors/scrollreveal.cjs +19 -22
  43. package/dist/behaviors/scrollreveal.cjs.map +1 -1
  44. package/dist/behaviors/scrollreveal.d.cts +4 -4
  45. package/dist/behaviors/scrollreveal.d.ts +4 -4
  46. package/dist/behaviors/scrollreveal.js +3 -3
  47. package/dist/behaviors/sortable.cjs +19 -20
  48. package/dist/behaviors/sortable.cjs.map +1 -1
  49. package/dist/behaviors/sortable.d.cts +4 -4
  50. package/dist/behaviors/sortable.d.ts +4 -4
  51. package/dist/behaviors/sortable.js +3 -3
  52. package/dist/behaviors/tabs.cjs +20 -25
  53. package/dist/behaviors/tabs.cjs.map +1 -1
  54. package/dist/behaviors/tabs.d.cts +4 -4
  55. package/dist/behaviors/tabs.d.ts +4 -4
  56. package/dist/behaviors/tabs.js +3 -3
  57. package/dist/behaviors/toggleable.cjs +25 -26
  58. package/dist/behaviors/toggleable.cjs.map +1 -1
  59. package/dist/behaviors/toggleable.d.cts +4 -4
  60. package/dist/behaviors/toggleable.d.ts +4 -4
  61. package/dist/behaviors/toggleable.js +3 -3
  62. package/dist/chunk-2GIS4DCY.js +30 -0
  63. package/dist/chunk-2GIS4DCY.js.map +1 -0
  64. package/dist/chunk-3BCBZR6R.js +30 -0
  65. package/dist/chunk-3BCBZR6R.js.map +1 -0
  66. package/dist/chunk-4YUIDZAO.js +30 -0
  67. package/dist/chunk-4YUIDZAO.js.map +1 -0
  68. package/dist/{chunk-XYZ7FOXJ.js → chunk-AQCOK5J5.js} +10 -14
  69. package/dist/chunk-AQCOK5J5.js.map +1 -0
  70. package/dist/chunk-ATGHQQC7.js +30 -0
  71. package/dist/chunk-ATGHQQC7.js.map +1 -0
  72. package/dist/{chunk-HQDYH45I.js → chunk-CWLAXLWR.js} +5 -13
  73. package/dist/chunk-CWLAXLWR.js.map +1 -0
  74. package/dist/chunk-DMWN52SV.js +30 -0
  75. package/dist/chunk-DMWN52SV.js.map +1 -0
  76. package/dist/{chunk-PDMOOWFW.js → chunk-EIVPVTUI.js} +4 -8
  77. package/dist/{chunk-PDMOOWFW.js.map → chunk-EIVPVTUI.js.map} +1 -1
  78. package/dist/chunk-HHTEMPIT.js +30 -0
  79. package/dist/chunk-HHTEMPIT.js.map +1 -0
  80. package/dist/{chunk-26IX7QU7.js → chunk-HJESSMKB.js} +4 -6
  81. package/dist/{chunk-26IX7QU7.js.map → chunk-HJESSMKB.js.map} +1 -1
  82. package/dist/{chunk-5LFOFVZ3.js → chunk-IFAH652L.js} +6 -9
  83. package/dist/chunk-IFAH652L.js.map +1 -0
  84. package/dist/chunk-IUGJG4RA.js +30 -0
  85. package/dist/chunk-IUGJG4RA.js.map +1 -0
  86. package/dist/{chunk-6EG2MNC7.js → chunk-JNLVXPHR.js} +13 -13
  87. package/dist/chunk-KEZEGPEX.js +30 -0
  88. package/dist/chunk-KEZEGPEX.js.map +1 -0
  89. package/dist/{chunk-66U2RSYJ.js → chunk-KP4GNXYS.js} +4 -10
  90. package/dist/chunk-KP4GNXYS.js.map +1 -0
  91. package/dist/chunk-KXQMCN4O.js +30 -0
  92. package/dist/chunk-KXQMCN4O.js.map +1 -0
  93. package/dist/{chunk-YXS27XM6.js → chunk-KYBOZ7C4.js} +3 -7
  94. package/dist/chunk-KYBOZ7C4.js.map +1 -0
  95. package/dist/chunk-LBWM6TTU.js +30 -0
  96. package/dist/chunk-LBWM6TTU.js.map +1 -0
  97. package/dist/{chunk-3DVDS7CT.js → chunk-LCDONZBX.js} +5 -5
  98. package/dist/chunk-LCDONZBX.js.map +1 -0
  99. package/dist/{chunk-OJ3YWO3W.js → chunk-NE76IBV2.js} +3 -7
  100. package/dist/chunk-NE76IBV2.js.map +1 -0
  101. package/dist/{chunk-HMU342XE.js → chunk-PVO6TZCZ.js} +3 -9
  102. package/dist/{chunk-HMU342XE.js.map → chunk-PVO6TZCZ.js.map} +1 -1
  103. package/dist/{chunk-URZGNPTQ.js → chunk-RILYRQ72.js} +4 -10
  104. package/dist/chunk-RILYRQ72.js.map +1 -0
  105. package/dist/{chunk-FBQ3X3UY.js → chunk-U4QMFVAM.js} +5 -13
  106. package/dist/chunk-U4QMFVAM.js.map +1 -0
  107. package/dist/chunk-VWDTOSE3.js +26 -0
  108. package/dist/chunk-VWDTOSE3.js.map +1 -0
  109. package/dist/chunk-YRQBU2OW.js +30 -0
  110. package/dist/chunk-YRQBU2OW.js.map +1 -0
  111. package/dist/clickoutside.browser.global.js +5 -7
  112. package/dist/clickoutside.browser.global.js.map +1 -1
  113. package/dist/clipboard.browser.global.js +4 -8
  114. package/dist/clipboard.browser.global.js.map +1 -1
  115. package/dist/core.browser.global.js +13 -25
  116. package/dist/core.browser.global.js.map +1 -1
  117. package/dist/draggable.browser.global.js +5 -13
  118. package/dist/draggable.browser.global.js.map +1 -1
  119. package/dist/focustrap.browser.global.js +4 -8
  120. package/dist/focustrap.browser.global.js.map +1 -1
  121. package/dist/index.cjs +98 -273
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.d.cts +10 -10
  124. package/dist/index.d.ts +10 -10
  125. package/dist/index.js +42 -55
  126. package/dist/index.js.map +1 -1
  127. package/dist/loaders.cjs +77 -239
  128. package/dist/loaders.cjs.map +1 -1
  129. package/dist/loaders.js +13 -13
  130. package/dist/registry.cjs +4 -4
  131. package/dist/registry.cjs.map +1 -1
  132. package/dist/registry.d.cts +5 -6
  133. package/dist/registry.d.ts +5 -6
  134. package/dist/registry.js +1 -1
  135. package/dist/removable.browser.global.js +3 -9
  136. package/dist/removable.browser.global.js.map +1 -1
  137. package/dist/resizable.browser.global.js +6 -9
  138. package/dist/resizable.browser.global.js.map +1 -1
  139. package/dist/resolver.browser.global.js +47 -102
  140. package/dist/resolver.browser.global.js.map +1 -1
  141. package/dist/schemas/index.cjs +40 -95
  142. package/dist/schemas/index.cjs.map +1 -1
  143. package/dist/schemas/index.d.cts +2 -2
  144. package/dist/schemas/index.d.ts +2 -2
  145. package/dist/schemas/index.js +11 -11
  146. package/dist/schemas/index.js.map +1 -1
  147. package/dist/scrollreveal.browser.global.js +5 -11
  148. package/dist/scrollreveal.browser.global.js.map +1 -1
  149. package/dist/sortable.browser.global.js +5 -9
  150. package/dist/sortable.browser.global.js.map +1 -1
  151. package/dist/tabs.browser.global.js +6 -14
  152. package/dist/tabs.browser.global.js.map +1 -1
  153. package/dist/toggleable.browser.global.js +9 -13
  154. package/dist/toggleable.browser.global.js.map +1 -1
  155. package/dist/{types-DCCtVQS5.d.cts → types-CbA4L8XJ.d.cts} +16 -23
  156. package/dist/{types-DCCtVQS5.d.ts → types-CbA4L8XJ.d.ts} +16 -23
  157. package/package.json +5 -5
  158. package/src/behavior-resolver.ts +24 -56
  159. package/src/behaviors/autodismiss.test.ts +12 -20
  160. package/src/behaviors/autodismiss.ts +8 -23
  161. package/src/behaviors/clickoutside.test.ts +13 -21
  162. package/src/behaviors/clickoutside.ts +8 -23
  163. package/src/behaviors/clipboard.test.ts +12 -34
  164. package/src/behaviors/clipboard.ts +8 -23
  165. package/src/behaviors/curated-runtime.test.ts +22 -24
  166. package/src/behaviors/draggable.ts +10 -24
  167. package/src/behaviors/experimental-runtime.test.ts +19 -18
  168. package/src/behaviors/focustrap.test.ts +12 -20
  169. package/src/behaviors/focustrap.ts +8 -23
  170. package/src/behaviors/integration.test.ts +19 -54
  171. package/src/behaviors/optional-runtime.test.ts +17 -22
  172. package/src/behaviors/removable.ts +6 -20
  173. package/src/behaviors/resizable.ts +9 -24
  174. package/src/behaviors/scrollreveal.test.ts +13 -21
  175. package/src/behaviors/scrollreveal.ts +8 -23
  176. package/src/behaviors/sortable.ts +7 -22
  177. package/src/behaviors/tabs.test.ts +12 -34
  178. package/src/behaviors/tabs.ts +8 -23
  179. package/src/behaviors/toggleable.ts +10 -26
  180. package/src/browser-resolver-bundle.ts +20 -30
  181. package/src/core-bundle.ts +11 -11
  182. package/src/index.test.ts +2 -2
  183. package/src/index.ts +30 -47
  184. package/src/registry.test.ts +9 -34
  185. package/src/registry.ts +7 -11
  186. package/src/schemas/autodismiss.schema.ts +3 -9
  187. package/src/schemas/clickoutside.schema.ts +3 -5
  188. package/src/schemas/clipboard.schema.ts +2 -6
  189. package/src/schemas/draggable.schema.ts +4 -12
  190. package/src/schemas/focustrap.schema.ts +2 -6
  191. package/src/schemas/index.ts +2 -1
  192. package/src/schemas/removable.schema.ts +2 -8
  193. package/src/schemas/resizable.schema.ts +5 -8
  194. package/src/schemas/scrollreveal.schema.ts +3 -9
  195. package/src/schemas/sortable.schema.ts +3 -7
  196. package/src/schemas/tabs.schema.ts +4 -12
  197. package/src/schemas/toggleable.schema.ts +10 -13
  198. package/src/schemas/types.ts +37 -30
  199. package/dist/chunk-2HUF6RMI.js +0 -40
  200. package/dist/chunk-2HUF6RMI.js.map +0 -1
  201. package/dist/chunk-3DVDS7CT.js.map +0 -1
  202. package/dist/chunk-5LFOFVZ3.js.map +0 -1
  203. package/dist/chunk-66U2RSYJ.js.map +0 -1
  204. package/dist/chunk-6TJXDCAQ.js +0 -11
  205. package/dist/chunk-6TJXDCAQ.js.map +0 -1
  206. package/dist/chunk-7NGOA3T5.js +0 -40
  207. package/dist/chunk-7NGOA3T5.js.map +0 -1
  208. package/dist/chunk-7TPDLB7W.js +0 -40
  209. package/dist/chunk-7TPDLB7W.js.map +0 -1
  210. package/dist/chunk-CR3J7VV6.js +0 -40
  211. package/dist/chunk-CR3J7VV6.js.map +0 -1
  212. package/dist/chunk-EDBG2F57.js +0 -40
  213. package/dist/chunk-EDBG2F57.js.map +0 -1
  214. package/dist/chunk-FBQ3X3UY.js.map +0 -1
  215. package/dist/chunk-HQDYH45I.js.map +0 -1
  216. package/dist/chunk-HX663BJ4.js +0 -40
  217. package/dist/chunk-HX663BJ4.js.map +0 -1
  218. package/dist/chunk-KROW43CG.js +0 -40
  219. package/dist/chunk-KROW43CG.js.map +0 -1
  220. package/dist/chunk-L52AGCMP.js +0 -40
  221. package/dist/chunk-L52AGCMP.js.map +0 -1
  222. package/dist/chunk-OJ3YWO3W.js.map +0 -1
  223. package/dist/chunk-ORBJ7MRW.js +0 -40
  224. package/dist/chunk-ORBJ7MRW.js.map +0 -1
  225. package/dist/chunk-SLSCZV4A.js +0 -40
  226. package/dist/chunk-SLSCZV4A.js.map +0 -1
  227. package/dist/chunk-URZGNPTQ.js.map +0 -1
  228. package/dist/chunk-WKBRZGGL.js +0 -40
  229. package/dist/chunk-WKBRZGGL.js.map +0 -1
  230. package/dist/chunk-XYZ7FOXJ.js.map +0 -1
  231. package/dist/chunk-YXS27XM6.js.map +0 -1
  232. /package/dist/{chunk-6EG2MNC7.js.map → chunk-JNLVXPHR.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/behaviors/tabs.ts","../src/schemas/tabs.schema.ts","../src/schemas/types.ts"],"sourcesContent":["/**\n * Tabs Behavior\n *\n * WAI-ARIA compliant tabs with roving tabindex keyboard navigation.\n * Auto-wires role, aria-selected, aria-controls, and aria-labelledby. The ARIA\n * wiring / keyboard-navigation logic lives in the schema's hyperscript `source`\n * (an `init`-block `js()` body).\n *\n * Compiled from its hyperscript `source` — the single source of truth shared with\n * the CDN resolver bundle and patterns-reference. This collapses Tabs onto the one\n * runtime path (no more imperative-installer fork between CDN and npm);\n * see docs-internal/BEHAVIORS_CONSOLIDATION_PLAN.md §3d.\n */\n\nimport { tabsSchema } from '../schemas/tabs.schema';\nimport type { LokaScriptInstance } from '../schemas/types';\nimport { resolveRuntime } from '../schemas/types';\n\nexport const tabsSource = tabsSchema.source;\nexport const tabsMetadata = tabsSchema;\n\n/**\n * Register the Tabs behavior with LokaScript by compiling its\n * hyperscript source and executing the resulting behavior definition.\n */\nexport async function registerTabs(hyperfixi?: LokaScriptInstance): Promise<void> {\n const hf = hyperfixi || resolveRuntime();\n\n if (!hf) {\n throw new Error(\n 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'\n );\n }\n\n const result = hf.compileSync(tabsSchema.source, { traditional: true });\n\n if (!result.ok) {\n throw new Error(`Failed to compile Tabs behavior: ${JSON.stringify(result.errors)}`);\n }\n\n const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };\n await hf.execute(result.ast, ctx);\n}\n\n// Auto-register when loaded as a script tag\nif (resolveRuntime()) {\n registerTabs().catch(console.error);\n}\n\nexport default {\n source: tabsSchema.source,\n metadata: tabsSchema,\n register: registerTabs,\n};\n","import type { BehaviorSchema } from './types';\n\nexport const tabsSchema: BehaviorSchema = {\n name: 'Tabs',\n category: 'ui',\n tier: 'core',\n version: '1.0.0',\n description:\n 'WAI-ARIA compliant tabs with roving tabindex keyboard navigation. ' +\n 'Auto-wires role, aria-selected, aria-controls, and aria-labelledby attributes.',\n parameters: [\n {\n name: 'orientation',\n type: 'string',\n optional: true,\n default: 'horizontal',\n enum: ['horizontal', 'vertical'],\n description: 'Arrow key direction: horizontal (Left/Right) or vertical (Up/Down)',\n },\n {\n name: 'activeTab',\n type: 'number',\n optional: true,\n default: 0,\n description: 'Initial active tab index (zero-based)',\n },\n {\n name: 'wrap',\n type: 'boolean',\n optional: true,\n default: true,\n description: 'Wrap to first/last tab when navigating past the end',\n },\n {\n name: 'activeClass',\n type: 'string',\n optional: true,\n default: 'active',\n description: 'CSS class applied to the active tab and its panel',\n },\n ],\n events: [\n {\n name: 'tabs:change',\n description: 'Fired before tab selection changes (cancelable via preventDefault)',\n },\n { name: 'tabs:changed', description: 'Fired after tab selection has changed' },\n ],\n requirements: [\n 'Sets role=\"tablist\" on the tab container',\n 'Sets role=\"tab\" on tab buttons and role=\"tabpanel\" on content panels',\n 'Manages aria-selected, aria-controls, aria-labelledby, and tabindex',\n 'Keyboard: Arrow keys navigate, Home/End jump to first/last, Tab exits tablist',\n ],\n source: `\nbehavior Tabs(orientation, activeTab, wrap, activeClass)\n init\n if orientation is undefined\n set orientation to \"horizontal\"\n end\n if activeTab is undefined\n set activeTab to 0\n end\n if wrap is undefined\n set wrap to true\n end\n if activeClass is undefined\n set activeClass to \"active\"\n end\n js(me, orientation, activeTab, wrap, activeClass)\n var tabs = Array.from(me.querySelectorAll('[role=\"tab\"]'));\n var panels = Array.from(me.querySelectorAll('[role=\"tabpanel\"]'));\n if (tabs.length === 0 || panels.length === 0) return;\n var tablist = me.querySelector('[role=\"tablist\"]') || tabs[0].parentElement;\n if (tablist) tablist.setAttribute('aria-orientation', orientation);\n var count = Math.min(tabs.length, panels.length);\n var idN = 0;\n for (var i = 0; i < count; i++) {\n if (!tabs[i].id) tabs[i].id = 'tab-' + (++idN);\n if (!panels[i].id) panels[i].id = 'tabpanel-' + idN;\n tabs[i].setAttribute('aria-controls', panels[i].id);\n panels[i].setAttribute('aria-labelledby', tabs[i].id);\n }\n var cur = Math.max(0, Math.min(activeTab, count - 1));\n function activate(idx, prev) {\n if (idx === prev || idx < 0 || idx >= count) return false;\n var ev = new CustomEvent('tabs:change', { bubbles: true, cancelable: true, detail: { index: idx, previousIndex: prev } });\n me.dispatchEvent(ev);\n if (ev.defaultPrevented) return false;\n if (prev >= 0 && prev < count) {\n tabs[prev].setAttribute('aria-selected', 'false');\n tabs[prev].setAttribute('tabindex', '-1');\n tabs[prev].classList.remove(activeClass);\n panels[prev].setAttribute('aria-hidden', 'true');\n panels[prev].classList.remove(activeClass);\n }\n tabs[idx].setAttribute('aria-selected', 'true');\n tabs[idx].setAttribute('tabindex', '0');\n tabs[idx].classList.add(activeClass);\n panels[idx].setAttribute('aria-hidden', 'false');\n panels[idx].classList.add(activeClass);\n cur = idx;\n me.dispatchEvent(new CustomEvent('tabs:changed', { bubbles: true, detail: { index: idx } }));\n return true;\n }\n for (var j = 0; j < count; j++) {\n tabs[j].setAttribute('aria-selected', 'false');\n tabs[j].setAttribute('tabindex', '-1');\n panels[j].setAttribute('aria-hidden', 'true');\n panels[j].classList.remove(activeClass);\n }\n tabs[cur].setAttribute('aria-selected', 'true');\n tabs[cur].setAttribute('tabindex', '0');\n tabs[cur].classList.add(activeClass);\n panels[cur].setAttribute('aria-hidden', 'false');\n panels[cur].classList.add(activeClass);\n for (var k = 0; k < count; k++) {\n (function(idx) {\n tabs[idx].addEventListener('click', function() { if (activate(idx, cur)) tabs[idx].focus(); });\n })(k);\n }\n (tablist || me).addEventListener('keydown', function(e) {\n var nk = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';\n var pk = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';\n var next = null;\n if (e.key === nk) { next = cur + 1; if (next >= count) next = wrap ? 0 : null; }\n else if (e.key === pk) { next = cur - 1; if (next < 0) next = wrap ? count - 1 : null; }\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = count - 1;\n if (next !== null) { e.preventDefault(); if (activate(next, cur)) tabs[next].focus(); }\n });\n end\n end\nend`.trim(),\n};\n","/**\n * Behavior Schema Types\n *\n * Defines the structure for behavior metadata used for:\n * - Code generation (TypeScript types, documentation)\n * - Runtime categorization and lazy loading\n * - Tooling and IDE integration\n */\n\n/**\n * Behavior categories for organization.\n * - ui: Visual interaction behaviors (drag, resize, sort)\n * - data: Data manipulation behaviors (remove, transform)\n * - animation: Animation and transition behaviors\n * - form: Form-related behaviors (validation, toggle states)\n * - layout: Layout behaviors (sticky, scrollspy)\n */\nexport type BehaviorCategory = 'ui' | 'data' | 'animation' | 'form' | 'layout';\n\n/**\n * Loading tiers for lazy loading optimization.\n * - core: Always loaded (essential behaviors)\n * - common: High usage (loaded for typical apps)\n * - optional: Low usage (loaded on demand)\n */\nexport type BehaviorTier = 'core' | 'common' | 'optional';\n\n/**\n * Schema for a behavior parameter.\n */\nexport interface ParameterSchema {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'selector' | 'element';\n optional?: boolean;\n default?: string | number | boolean;\n /** Allowed values for constrained parameters */\n enum?: string[];\n description: string;\n}\n\n/**\n * Schema for a behavior event.\n */\nexport interface EventSchema {\n name: string;\n description: string;\n}\n\n/**\n * Complete schema for a behavior.\n * This is the source of truth for all behavior metadata.\n */\nexport interface BehaviorSchema {\n /** Behavior name (PascalCase) */\n name: string;\n /** Category for organization */\n category: BehaviorCategory;\n /** Loading tier for lazy loading */\n tier: BehaviorTier;\n /** Semantic version */\n version: string;\n /** Human-readable description */\n description: string;\n /** Parameter definitions */\n parameters: ParameterSchema[];\n /** Event definitions */\n events: EventSchema[];\n /** The hyperscript behavior source code */\n source: string;\n /** Optional requirements or notes */\n requirements?: string[];\n}\n\n/**\n * Runtime behavior module interface.\n * This is what gets registered with the hyperfixi runtime.\n */\nexport interface BehaviorModule {\n source: string;\n metadata: BehaviorSchema;\n register: (hyperfixi?: LokaScriptInstance) => Promise<void>;\n}\n\n/**\n * Minimal LokaScript instance interface for behavior registration.\n */\nexport interface LokaScriptInstance {\n compileSync: (\n code: string,\n options?: { traditional?: boolean; language?: string }\n ) => {\n ok: boolean;\n ast?: unknown;\n errors?: Array<{ message: string; line?: number; column?: number }>;\n };\n execute: (ast: unknown, ctx: unknown) => Promise<unknown>;\n createContext?: () => { locals: Map<string, unknown>; globals: Map<string, unknown> };\n}\n\n/**\n * Typed window interface for behavior auto-registration.\n * Avoids `(window as any)` casts throughout the codebase.\n * Checks both `window.lokascript` and `window.hyperfixi` for the runtime instance.\n */\nexport interface LokaScriptWindow {\n lokascript?: LokaScriptInstance;\n hyperfixi?: LokaScriptInstance;\n __hyperfixi_behaviors_ready?: Promise<void> | undefined;\n}\n\n/**\n * Resolve the LokaScript runtime from window globals.\n * Checks both `window.lokascript` and `window.hyperfixi`.\n */\nexport function resolveRuntime(): LokaScriptInstance | null {\n if (typeof window === 'undefined') return null;\n const win = window as unknown as LokaScriptWindow;\n return win.lokascript || win.hyperfixi || null;\n}\n"],"mappings":"scAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,iBAAAC,EAAA,iBAAAC,EAAA,eAAAC,ICEO,IAAMC,EAA6B,CACxC,KAAM,OACN,SAAU,KACV,KAAM,OACN,QAAS,QACT,YACE,mJAEF,WAAY,CACV,CACE,KAAM,cACN,KAAM,SACN,SAAU,GACV,QAAS,aACT,KAAM,CAAC,aAAc,UAAU,EAC/B,YAAa,oEACf,EACA,CACE,KAAM,YACN,KAAM,SACN,SAAU,GACV,QAAS,EACT,YAAa,uCACf,EACA,CACE,KAAM,OACN,KAAM,UACN,SAAU,GACV,QAAS,GACT,YAAa,qDACf,EACA,CACE,KAAM,cACN,KAAM,SACN,SAAU,GACV,QAAS,SACT,YAAa,mDACf,CACF,EACA,OAAQ,CACN,CACE,KAAM,cACN,YAAa,oEACf,EACA,CAAE,KAAM,eAAgB,YAAa,uCAAwC,CAC/E,EACA,aAAc,CACZ,2CACA,uEACA,sEACA,+EACF,EACA,OAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KA+EL,KAAK,CACV,ECpBO,SAASC,GAA4C,CAC1D,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,EAAM,OACZ,OAAOA,EAAI,YAAcA,EAAI,WAAa,IAC5C,CFpGO,IAAMC,EAAaC,EAAW,OACxBC,EAAeD,EAM5B,eAAsBE,EAAaC,EAA+C,CAChF,IAAMC,EAAKD,GAAaE,EAAe,EAEvC,GAAI,CAACD,EACH,MAAM,IAAI,MACR,yFACF,EAGF,IAAME,EAASF,EAAG,YAAYJ,EAAW,OAAQ,CAAE,YAAa,EAAK,CAAC,EAEtE,GAAI,CAACM,EAAO,GACV,MAAM,IAAI,MAAM,oCAAoC,KAAK,UAAUA,EAAO,MAAM,CAAC,EAAE,EAGrF,IAAMC,EAAMH,EAAG,cAAgBA,EAAG,cAAc,EAAI,CAAE,OAAQ,IAAI,IAAO,QAAS,IAAI,GAAM,EAC5F,MAAMA,EAAG,QAAQE,EAAO,IAAKC,CAAG,CAClC,CAGIF,EAAe,GACjBH,EAAa,EAAE,MAAM,QAAQ,KAAK,EAGpC,IAAOM,EAAQ,CACb,OAAQR,EAAW,OACnB,SAAUA,EACV,SAAUE,CACZ","names":["tabs_exports","__export","tabs_default","registerTabs","tabsMetadata","tabsSource","tabsSchema","resolveRuntime","win","tabsSource","tabsSchema","tabsMetadata","registerTabs","hyperfixi","hf","resolveRuntime","result","ctx","tabs_default"]}
1
+ {"version":3,"sources":["../src/behaviors/tabs.ts","../src/schemas/tabs.schema.ts","../src/schemas/types.ts"],"sourcesContent":["/**\n * Tabs Behavior\n *\n * WAI-ARIA compliant tabs with roving tabindex keyboard navigation.\n * Auto-wires role, aria-selected, aria-controls, and aria-labelledby. The ARIA\n * wiring / keyboard-navigation logic lives in the schema's hyperscript `source`\n * (an `init`-block `js()` body).\n *\n * Defined from its hyperscript `source` — the single source of truth shared with\n * the browser bundle and patterns-reference. This collapses Tabs onto the one\n * runtime path (no more imperative-installer fork between CDN and npm);\n * see docs-internal/BEHAVIORS_CONSOLIDATION_PLAN.md §3d.\n */\n\nimport { tabsSchema } from '../schemas/tabs.schema';\nimport { defineBehavior, resolveRuntime } from '../schemas/types';\nimport type { HyperscriptHost } from '../schemas/types';\n\nexport const tabsSource = tabsSchema.source;\nexport const tabsMetadata = tabsSchema;\n\n/**\n * Define the Tabs behavior on a hyperscript host (`window.hyperfixi` when none is given)\n * from its source, as a `<script type=\"text/hyperscript\">` would.\n */\nexport async function registerTabs(host?: HyperscriptHost): Promise<void> {\n defineBehavior(tabsSchema, host);\n}\n\n// Auto-register when loaded as a script tag\nif (resolveRuntime()) {\n registerTabs().catch(console.error);\n}\n\nexport default {\n source: tabsSchema.source,\n metadata: tabsSchema,\n register: registerTabs,\n};\n","import type { BehaviorSchema } from './types';\n\nexport const tabsSchema: BehaviorSchema = {\n name: 'Tabs',\n category: 'ui',\n tier: 'core',\n version: '1.0.0',\n description:\n 'WAI-ARIA compliant tabs with roving tabindex keyboard navigation. ' +\n 'Auto-wires role, aria-selected, aria-controls, and aria-labelledby attributes.',\n parameters: [\n {\n name: 'orientation',\n type: 'string',\n optional: true,\n default: 'horizontal',\n enum: ['horizontal', 'vertical'],\n description: 'Arrow key direction: horizontal (Left/Right) or vertical (Up/Down)',\n },\n {\n name: 'activeTab',\n type: 'number',\n optional: true,\n default: 0,\n description: 'Initial active tab index (zero-based)',\n },\n {\n name: 'wrap',\n type: 'boolean',\n optional: true,\n default: true,\n description: 'Wrap to first/last tab when navigating past the end',\n },\n {\n name: 'activeClass',\n type: 'string',\n optional: true,\n default: 'active',\n description: 'CSS class applied to the active tab and its panel',\n },\n ],\n events: [\n {\n name: 'tabs:change',\n description: 'Fired before tab selection changes (cancelable via preventDefault)',\n },\n { name: 'tabs:changed', description: 'Fired after tab selection has changed' },\n ],\n requirements: [\n 'Sets role=\"tablist\" on the tab container',\n 'Sets role=\"tab\" on tab buttons and role=\"tabpanel\" on content panels',\n 'Manages aria-selected, aria-controls, aria-labelledby, and tabindex',\n 'Keyboard: Arrow keys navigate, Home/End jump to first/last, Tab exits tablist',\n ],\n source: `\nbehavior Tabs(orientation, activeTab, wrap, activeClass)\n init\n if orientation is undefined set element's orientation to \"horizontal\" end\n if activeTab is undefined set element's activeTab to 0 end\n if wrap is undefined set element's wrap to true end\n if activeClass is undefined set element's activeClass to \"active\" end\n js(me, orientation, activeTab, wrap, activeClass)\n var tabs = Array.from(me.querySelectorAll('[role=\"tab\"]'));\n var panels = Array.from(me.querySelectorAll('[role=\"tabpanel\"]'));\n if (tabs.length === 0 || panels.length === 0) return;\n var tablist = me.querySelector('[role=\"tablist\"]') || tabs[0].parentElement;\n if (tablist) tablist.setAttribute('aria-orientation', orientation);\n var count = Math.min(tabs.length, panels.length);\n var idN = 0;\n for (var i = 0; i < count; i++) {\n if (!tabs[i].id) tabs[i].id = 'tab-' + (++idN);\n if (!panels[i].id) panels[i].id = 'tabpanel-' + idN;\n tabs[i].setAttribute('aria-controls', panels[i].id);\n panels[i].setAttribute('aria-labelledby', tabs[i].id);\n }\n var cur = Math.max(0, Math.min(activeTab, count - 1));\n function activate(idx, prev) {\n if (idx === prev || idx < 0 || idx >= count) return false;\n var ev = new CustomEvent('tabs:change', { bubbles: true, cancelable: true, detail: { index: idx, previousIndex: prev } });\n me.dispatchEvent(ev);\n if (ev.defaultPrevented) return false;\n if (prev >= 0 && prev < count) {\n tabs[prev].setAttribute('aria-selected', 'false');\n tabs[prev].setAttribute('tabindex', '-1');\n tabs[prev].classList.remove(activeClass);\n panels[prev].setAttribute('aria-hidden', 'true');\n panels[prev].classList.remove(activeClass);\n }\n tabs[idx].setAttribute('aria-selected', 'true');\n tabs[idx].setAttribute('tabindex', '0');\n tabs[idx].classList.add(activeClass);\n panels[idx].setAttribute('aria-hidden', 'false');\n panels[idx].classList.add(activeClass);\n cur = idx;\n me.dispatchEvent(new CustomEvent('tabs:changed', { bubbles: true, detail: { index: idx } }));\n return true;\n }\n for (var j = 0; j < count; j++) {\n tabs[j].setAttribute('aria-selected', 'false');\n tabs[j].setAttribute('tabindex', '-1');\n panels[j].setAttribute('aria-hidden', 'true');\n panels[j].classList.remove(activeClass);\n }\n tabs[cur].setAttribute('aria-selected', 'true');\n tabs[cur].setAttribute('tabindex', '0');\n tabs[cur].classList.add(activeClass);\n panels[cur].setAttribute('aria-hidden', 'false');\n panels[cur].classList.add(activeClass);\n for (var k = 0; k < count; k++) {\n (function(idx) {\n tabs[idx].addEventListener('click', function() { if (activate(idx, cur)) tabs[idx].focus(); });\n })(k);\n }\n (tablist || me).addEventListener('keydown', function(e) {\n var nk = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';\n var pk = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';\n var next = null;\n if (e.key === nk) { next = cur + 1; if (next >= count) next = wrap ? 0 : null; }\n else if (e.key === pk) { next = cur - 1; if (next < 0) next = wrap ? count - 1 : null; }\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = count - 1;\n if (next !== null) { e.preventDefault(); if (activate(next, cur)) tabs[next].focus(); }\n });\n end\n end\nend`.trim(),\n};\n","/**\n * Behavior Schema Types\n *\n * Defines the structure for behavior metadata used for:\n * - Code generation (TypeScript types, documentation)\n * - Runtime categorization and lazy loading\n * - Tooling and IDE integration\n */\n\n/**\n * Behavior categories for organization.\n * - ui: Visual interaction behaviors (drag, resize, sort)\n * - data: Data manipulation behaviors (remove, transform)\n * - animation: Animation and transition behaviors\n * - form: Form-related behaviors (validation, toggle states)\n * - layout: Layout behaviors (sticky, scrollspy)\n */\nexport type BehaviorCategory = 'ui' | 'data' | 'animation' | 'form' | 'layout';\n\n/**\n * Loading tiers for lazy loading optimization.\n * - core: Always loaded (essential behaviors)\n * - common: High usage (loaded for typical apps)\n * - optional: Low usage (loaded on demand)\n */\nexport type BehaviorTier = 'core' | 'common' | 'optional';\n\n/**\n * Schema for a behavior parameter.\n */\nexport interface ParameterSchema {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'selector' | 'element';\n optional?: boolean;\n default?: string | number | boolean;\n /** Allowed values for constrained parameters */\n enum?: string[];\n description: string;\n}\n\n/**\n * Schema for a behavior event.\n */\nexport interface EventSchema {\n name: string;\n description: string;\n}\n\n/**\n * Complete schema for a behavior.\n * This is the source of truth for all behavior metadata.\n */\nexport interface BehaviorSchema {\n /** Behavior name (PascalCase) */\n name: string;\n /** Category for organization */\n category: BehaviorCategory;\n /** Loading tier for lazy loading */\n tier: BehaviorTier;\n /** Semantic version */\n version: string;\n /** Human-readable description */\n description: string;\n /** Parameter definitions */\n parameters: ParameterSchema[];\n /** Event definitions */\n events: EventSchema[];\n /** The hyperscript behavior source code */\n source: string;\n /** Optional requirements or notes */\n requirements?: string[];\n}\n\n/**\n * Runtime behavior module interface: a behavior's source, its schema, and the function that\n * defines it on a hyperscript host.\n */\nexport interface BehaviorModule {\n source: string;\n metadata: BehaviorSchema;\n register: (host?: HyperscriptHost) => Promise<void>;\n}\n\n/**\n * A hyperscript host, in upstream `_hyperscript`'s shape: `@hyperfixi/engine` (the\n * `hyperfixi-hs.js` bundle, `window.hyperfixi` / `window._hyperscript`) or upstream itself.\n * `evaluate(source)` of a `behavior … end` program defines the behavior; `processNode`\n * initialises the scripted elements under a node.\n */\nexport interface HyperscriptHost {\n evaluate: (source: string) => unknown;\n processNode?: (node: Node) => void;\n}\n\n/** The window globals a host installs itself as. */\nexport interface HyperscriptWindow {\n hyperfixi?: HyperscriptHost;\n _hyperscript?: HyperscriptHost;\n}\n\n/** The host on `window`: `hyperfixi` (the engine's bundle) or `_hyperscript` (upstream's name). */\nexport function resolveRuntime(): HyperscriptHost | null {\n if (typeof window === 'undefined') return null;\n const win = window as unknown as HyperscriptWindow;\n const host = win.hyperfixi ?? win._hyperscript;\n return host && typeof host.evaluate === 'function' ? host : null;\n}\n\n/**\n * Define a behavior on a host from its schema's source, as a page's\n * `<script type=\"text/hyperscript\">` would. Throws when there is no host or the source\n * does not parse.\n */\nexport function defineBehavior(schema: BehaviorSchema, host?: HyperscriptHost): void {\n const hf = host ?? resolveRuntime();\n if (!hf) {\n throw new Error(\n `No hyperscript host found: load @hyperfixi/engine (hyperfixi-hs.js) before defining ${schema.name}.`\n );\n }\n try {\n hf.evaluate(schema.source);\n } catch (e) {\n throw new Error(`Failed to define ${schema.name} behavior: ${(e as Error).message}`);\n }\n}\n"],"mappings":"scAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,iBAAAC,EAAA,iBAAAC,EAAA,eAAAC,ICEO,IAAMC,EAA6B,CACxC,KAAM,OACN,SAAU,KACV,KAAM,OACN,QAAS,QACT,YACE,mJAEF,WAAY,CACV,CACE,KAAM,cACN,KAAM,SACN,SAAU,GACV,QAAS,aACT,KAAM,CAAC,aAAc,UAAU,EAC/B,YAAa,oEACf,EACA,CACE,KAAM,YACN,KAAM,SACN,SAAU,GACV,QAAS,EACT,YAAa,uCACf,EACA,CACE,KAAM,OACN,KAAM,UACN,SAAU,GACV,QAAS,GACT,YAAa,qDACf,EACA,CACE,KAAM,cACN,KAAM,SACN,SAAU,GACV,QAAS,SACT,YAAa,mDACf,CACF,EACA,OAAQ,CACN,CACE,KAAM,cACN,YAAa,oEACf,EACA,CAAE,KAAM,eAAgB,YAAa,uCAAwC,CAC/E,EACA,aAAc,CACZ,2CACA,uEACA,sEACA,+EACF,EACA,OAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAuEL,KAAK,CACV,ECzBO,SAASC,GAAyC,CACvD,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,EAAM,OACNC,EAAOD,EAAI,WAAaA,EAAI,aAClC,OAAOC,GAAQ,OAAOA,EAAK,UAAa,WAAaA,EAAO,IAC9D,CAOO,SAASC,EAAeC,EAAwBF,EAA8B,CACnF,IAAMG,EAAKH,GAAQF,EAAe,EAClC,GAAI,CAACK,EACH,MAAM,IAAI,MACR,uFAAuFD,EAAO,IAAI,GACpG,EAEF,GAAI,CACFC,EAAG,SAASD,EAAO,MAAM,CAC3B,OAASE,EAAG,CACV,MAAM,IAAI,MAAM,oBAAoBF,EAAO,IAAI,cAAeE,EAAY,OAAO,EAAE,CACrF,CACF,CF3GO,IAAMC,EAAaC,EAAW,OACxBC,EAAeD,EAM5B,eAAsBE,EAAaC,EAAuC,CACxEC,EAAeJ,EAAYG,CAAI,CACjC,CAGIE,EAAe,GACjBH,EAAa,EAAE,MAAM,QAAQ,KAAK,EAGpC,IAAOI,EAAQ,CACb,OAAQN,EAAW,OACnB,SAAUA,EACV,SAAUE,CACZ","names":["tabs_exports","__export","tabs_default","registerTabs","tabsMetadata","tabsSource","tabsSchema","resolveRuntime","win","host","defineBehavior","schema","hf","e","tabsSource","tabsSchema","tabsMetadata","registerTabs","host","defineBehavior","resolveRuntime","tabs_default"]}
@@ -1,19 +1,15 @@
1
- "use strict";var HyperFixiBehaviors=(()=>{var i=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var m=(t,e)=>{for(var o in e)i(t,o,{get:e[o],enumerable:!0})},u=(t,e,o,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of g(e))!p.call(t,n)&&n!==o&&i(t,n,{get:()=>e[n],enumerable:!(a=l(e,n))||a.enumerable});return t};var d=t=>u(i({},"__esModule",{value:!0}),t);var b={};m(b,{default:()=>v,registerToggleable:()=>c,toggleableMetadata:()=>h,toggleableSource:()=>f});var r={name:"Toggleable",category:"form",tier:"core",version:"1.0.0",description:"Toggles a CSS class on click",parameters:[{name:"cls",type:"string",optional:!0,default:"active",description:"CSS class to toggle"},{name:"target",type:"selector",optional:!0,default:"me",description:"Element to toggle the class on"}],events:[{name:"toggleable:on",description:"Fired when class is added"},{name:"toggleable:off",description:"Fired when class is removed"}],source:`
2
- behavior Toggleable(cls, target)
1
+ "use strict";var HyperFixiBehaviors=(()=>{var a=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var d=(e,t)=>{for(var r in t)a(e,r,{get:t[r],enumerable:!0})},h=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of g(t))!m.call(e,i)&&i!==r&&a(e,i,{get:()=>t[i],enumerable:!(n=p(t,i))||n.enumerable});return e};var f=e=>h(a({},"__esModule",{value:!0}),e);var b={};d(b,{default:()=>y,registerToggleable:()=>c,toggleableMetadata:()=>v,toggleableSource:()=>u});var o={name:"Toggleable",category:"form",tier:"core",version:"1.0.0",description:"Toggles a CSS class on click",parameters:[{name:"cls",type:"string",optional:!0,default:"active",description:"CSS class to toggle"},{name:"targetEl",type:"selector",optional:!0,default:"me",description:"Element to toggle the class on (not `target`: that is hyperscript's name for the event target)"}],events:[{name:"toggleable:on",description:"Fired when class is added"},{name:"toggleable:off",description:"Fired when class is removed"}],source:`
2
+ behavior Toggleable(cls, targetEl)
3
3
  init
4
- if cls is undefined
5
- set cls to "active"
6
- end
7
- if target is undefined
8
- set target to me
9
- end
4
+ if cls is undefined set element's cls to "active" end
5
+ if targetEl is undefined set element's targetEl to me end
10
6
  end
11
7
  on click
12
- toggle .{cls} on target
13
- js(target, cls)
14
- var eventName = target.classList.contains(cls) ? 'toggleable:on' : 'toggleable:off';
15
- target.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
8
+ toggle .{cls} on targetEl
9
+ js(targetEl, cls)
10
+ var eventName = targetEl.classList.contains(cls) ? 'toggleable:on' : 'toggleable:off';
11
+ targetEl.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
16
12
  end
17
13
  end
18
- end`.trim()};function s(){if(typeof window>"u")return null;let t=window;return t.lokascript||t.hyperfixi||null}var f=r.source,h=r;async function c(t){let e=t||s();if(!e)throw new Error("LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.");let o=e.compileSync(r.source,{traditional:!0});if(!o.ok)throw new Error(`Failed to compile Toggleable behavior: ${JSON.stringify(o.errors)}`);let a=e.createContext?e.createContext():{locals:new Map,globals:new Map};await e.execute(o.ast,a)}s()&&c().catch(console.error);var v={source:r.source,metadata:r,register:c};return d(b);})();
14
+ end`.trim()};function s(){if(typeof window>"u")return null;let e=window,t=e.hyperfixi??e._hyperscript;return t&&typeof t.evaluate=="function"?t:null}function l(e,t){let r=t??s();if(!r)throw new Error(`No hyperscript host found: load @hyperfixi/engine (hyperfixi-hs.js) before defining ${e.name}.`);try{r.evaluate(e.source)}catch(n){throw new Error(`Failed to define ${e.name} behavior: ${n.message}`)}}var u=o.source,v=o;async function c(e){l(o,e)}s()&&c().catch(console.error);var y={source:o.source,metadata:o,register:c};return f(b);})();
19
15
  //# sourceMappingURL=toggleable.browser.global.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/behaviors/toggleable.ts","../src/schemas/toggleable.schema.ts","../src/schemas/types.ts"],"sourcesContent":["/**\n * Toggleable Behavior\n *\n * A behavior that toggles a CSS class on click.\n * Useful for accordions, dropdowns, and toggle buttons.\n *\n * Compiled from its hyperscript `source` (the single source of truth shared with\n * the CDN resolver bundle and patterns-reference). The `.{cls}` dynamic class\n * selector resolves the behavior's `cls` parameter at runtime — see\n * `packages/core/src/commands/behaviors/__tests__/template-interpolation.test.ts`.\n *\n * @example\n * ```html\n * <button _=\"install Toggleable\">Toggle</button>\n *\n * <button _=\"install Toggleable(cls: expanded, target: #menu)\">Menu</button>\n * ```\n */\n\nimport { toggleableSchema } from '../schemas/toggleable.schema';\nimport type { LokaScriptInstance } from '../schemas/types';\nimport { resolveRuntime } from '../schemas/types';\n\n// Re-export schema-derived values for backwards compatibility\nexport const toggleableSource = toggleableSchema.source;\nexport const toggleableMetadata = toggleableSchema;\n\n/**\n * Register the Toggleable behavior with LokaScript by compiling its\n * hyperscript source and executing the resulting behavior definition.\n */\nexport async function registerToggleable(hyperfixi?: LokaScriptInstance): Promise<void> {\n const hf = hyperfixi || resolveRuntime();\n\n if (!hf) {\n throw new Error(\n 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'\n );\n }\n\n const result = hf.compileSync(toggleableSchema.source, { traditional: true });\n\n if (!result.ok) {\n throw new Error(`Failed to compile Toggleable behavior: ${JSON.stringify(result.errors)}`);\n }\n\n const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };\n await hf.execute(result.ast, ctx);\n}\n\n// Auto-register when loaded as a script tag\nif (resolveRuntime()) {\n registerToggleable().catch(console.error);\n}\n\nexport default {\n source: toggleableSchema.source,\n metadata: toggleableSchema,\n register: registerToggleable,\n};\n","import type { BehaviorSchema } from './types';\n\n/**\n * Toggleable Behavior Schema\n *\n * Toggles a CSS class on click.\n * Useful for accordions, dropdowns, and toggle buttons.\n */\nexport const toggleableSchema: BehaviorSchema = {\n name: 'Toggleable',\n category: 'form',\n tier: 'core',\n version: '1.0.0',\n description: 'Toggles a CSS class on click',\n parameters: [\n {\n name: 'cls',\n type: 'string',\n optional: true,\n default: 'active',\n description: 'CSS class to toggle',\n },\n {\n name: 'target',\n type: 'selector',\n optional: true,\n default: 'me',\n description: 'Element to toggle the class on',\n },\n ],\n events: [\n { name: 'toggleable:on', description: 'Fired when class is added' },\n { name: 'toggleable:off', description: 'Fired when class is removed' },\n ],\n source: `\nbehavior Toggleable(cls, target)\n init\n if cls is undefined\n set cls to \"active\"\n end\n if target is undefined\n set target to me\n end\n end\n on click\n toggle .{cls} on target\n js(target, cls)\n var eventName = target.classList.contains(cls) ? 'toggleable:on' : 'toggleable:off';\n target.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));\n end\n end\nend`.trim(),\n};\n","/**\n * Behavior Schema Types\n *\n * Defines the structure for behavior metadata used for:\n * - Code generation (TypeScript types, documentation)\n * - Runtime categorization and lazy loading\n * - Tooling and IDE integration\n */\n\n/**\n * Behavior categories for organization.\n * - ui: Visual interaction behaviors (drag, resize, sort)\n * - data: Data manipulation behaviors (remove, transform)\n * - animation: Animation and transition behaviors\n * - form: Form-related behaviors (validation, toggle states)\n * - layout: Layout behaviors (sticky, scrollspy)\n */\nexport type BehaviorCategory = 'ui' | 'data' | 'animation' | 'form' | 'layout';\n\n/**\n * Loading tiers for lazy loading optimization.\n * - core: Always loaded (essential behaviors)\n * - common: High usage (loaded for typical apps)\n * - optional: Low usage (loaded on demand)\n */\nexport type BehaviorTier = 'core' | 'common' | 'optional';\n\n/**\n * Schema for a behavior parameter.\n */\nexport interface ParameterSchema {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'selector' | 'element';\n optional?: boolean;\n default?: string | number | boolean;\n /** Allowed values for constrained parameters */\n enum?: string[];\n description: string;\n}\n\n/**\n * Schema for a behavior event.\n */\nexport interface EventSchema {\n name: string;\n description: string;\n}\n\n/**\n * Complete schema for a behavior.\n * This is the source of truth for all behavior metadata.\n */\nexport interface BehaviorSchema {\n /** Behavior name (PascalCase) */\n name: string;\n /** Category for organization */\n category: BehaviorCategory;\n /** Loading tier for lazy loading */\n tier: BehaviorTier;\n /** Semantic version */\n version: string;\n /** Human-readable description */\n description: string;\n /** Parameter definitions */\n parameters: ParameterSchema[];\n /** Event definitions */\n events: EventSchema[];\n /** The hyperscript behavior source code */\n source: string;\n /** Optional requirements or notes */\n requirements?: string[];\n}\n\n/**\n * Runtime behavior module interface.\n * This is what gets registered with the hyperfixi runtime.\n */\nexport interface BehaviorModule {\n source: string;\n metadata: BehaviorSchema;\n register: (hyperfixi?: LokaScriptInstance) => Promise<void>;\n}\n\n/**\n * Minimal LokaScript instance interface for behavior registration.\n */\nexport interface LokaScriptInstance {\n compileSync: (\n code: string,\n options?: { traditional?: boolean; language?: string }\n ) => {\n ok: boolean;\n ast?: unknown;\n errors?: Array<{ message: string; line?: number; column?: number }>;\n };\n execute: (ast: unknown, ctx: unknown) => Promise<unknown>;\n createContext?: () => { locals: Map<string, unknown>; globals: Map<string, unknown> };\n}\n\n/**\n * Typed window interface for behavior auto-registration.\n * Avoids `(window as any)` casts throughout the codebase.\n * Checks both `window.lokascript` and `window.hyperfixi` for the runtime instance.\n */\nexport interface LokaScriptWindow {\n lokascript?: LokaScriptInstance;\n hyperfixi?: LokaScriptInstance;\n __hyperfixi_behaviors_ready?: Promise<void> | undefined;\n}\n\n/**\n * Resolve the LokaScript runtime from window globals.\n * Checks both `window.lokascript` and `window.hyperfixi`.\n */\nexport function resolveRuntime(): LokaScriptInstance | null {\n if (typeof window === 'undefined') return null;\n const win = window as unknown as LokaScriptWindow;\n return win.lokascript || win.hyperfixi || null;\n}\n"],"mappings":"scAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,uBAAAC,EAAA,uBAAAC,EAAA,qBAAAC,ICQO,IAAMC,EAAmC,CAC9C,KAAM,aACN,SAAU,OACV,KAAM,OACN,QAAS,QACT,YAAa,+BACb,WAAY,CACV,CACE,KAAM,MACN,KAAM,SACN,SAAU,GACV,QAAS,SACT,YAAa,qBACf,EACA,CACE,KAAM,SACN,KAAM,WACN,SAAU,GACV,QAAS,KACT,YAAa,gCACf,CACF,EACA,OAAQ,CACN,CAAE,KAAM,gBAAiB,YAAa,2BAA4B,EAClE,CAAE,KAAM,iBAAkB,YAAa,6BAA8B,CACvE,EACA,OAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAiBL,KAAK,CACV,EC8DO,SAASC,GAA4C,CAC1D,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,EAAM,OACZ,OAAOA,EAAI,YAAcA,EAAI,WAAa,IAC5C,CF9FO,IAAMC,EAAmBC,EAAiB,OACpCC,EAAqBD,EAMlC,eAAsBE,EAAmBC,EAA+C,CACtF,IAAMC,EAAKD,GAAaE,EAAe,EAEvC,GAAI,CAACD,EACH,MAAM,IAAI,MACR,yFACF,EAGF,IAAME,EAASF,EAAG,YAAYJ,EAAiB,OAAQ,CAAE,YAAa,EAAK,CAAC,EAE5E,GAAI,CAACM,EAAO,GACV,MAAM,IAAI,MAAM,0CAA0C,KAAK,UAAUA,EAAO,MAAM,CAAC,EAAE,EAG3F,IAAMC,EAAMH,EAAG,cAAgBA,EAAG,cAAc,EAAI,CAAE,OAAQ,IAAI,IAAO,QAAS,IAAI,GAAM,EAC5F,MAAMA,EAAG,QAAQE,EAAO,IAAKC,CAAG,CAClC,CAGIF,EAAe,GACjBH,EAAmB,EAAE,MAAM,QAAQ,KAAK,EAG1C,IAAOM,EAAQ,CACb,OAAQR,EAAiB,OACzB,SAAUA,EACV,SAAUE,CACZ","names":["toggleable_exports","__export","toggleable_default","registerToggleable","toggleableMetadata","toggleableSource","toggleableSchema","resolveRuntime","win","toggleableSource","toggleableSchema","toggleableMetadata","registerToggleable","hyperfixi","hf","resolveRuntime","result","ctx","toggleable_default"]}
1
+ {"version":3,"sources":["../src/behaviors/toggleable.ts","../src/schemas/toggleable.schema.ts","../src/schemas/types.ts"],"sourcesContent":["/**\n * Toggleable Behavior\n *\n * A behavior that toggles a CSS class on click.\n * Useful for accordions, dropdowns, and toggle buttons.\n *\n * Defined from its hyperscript `source` (the single source of truth shared with\n * the browser bundle and patterns-reference). The `.{cls}` dynamic class\n * selector resolves the behavior's `cls` parameter at runtime.\n *\n * @example\n * ```html\n * <button _=\"install Toggleable\">Toggle</button>\n *\n * <button _=\"install Toggleable(cls: 'expanded', targetEl: #menu)\">Menu</button>\n * ```\n */\n\nimport { toggleableSchema } from '../schemas/toggleable.schema';\nimport { defineBehavior, resolveRuntime } from '../schemas/types';\nimport type { HyperscriptHost } from '../schemas/types';\n\n// Re-export schema-derived values for backwards compatibility\nexport const toggleableSource = toggleableSchema.source;\nexport const toggleableMetadata = toggleableSchema;\n\n/**\n * Define the Toggleable behavior on a hyperscript host (`window.hyperfixi` when none is given)\n * from its source, as a `<script type=\"text/hyperscript\">` would.\n */\nexport async function registerToggleable(host?: HyperscriptHost): Promise<void> {\n defineBehavior(toggleableSchema, host);\n}\n\n// Auto-register when loaded as a script tag\nif (resolveRuntime()) {\n registerToggleable().catch(console.error);\n}\n\nexport default {\n source: toggleableSchema.source,\n metadata: toggleableSchema,\n register: registerToggleable,\n};\n","import type { BehaviorSchema } from './types';\n\n/**\n * Toggleable Behavior Schema\n *\n * Toggles a CSS class on click.\n * Useful for accordions, dropdowns, and toggle buttons.\n */\nexport const toggleableSchema: BehaviorSchema = {\n name: 'Toggleable',\n category: 'form',\n tier: 'core',\n version: '1.0.0',\n description: 'Toggles a CSS class on click',\n parameters: [\n {\n name: 'cls',\n type: 'string',\n optional: true,\n default: 'active',\n description: 'CSS class to toggle',\n },\n {\n name: 'targetEl',\n type: 'selector',\n optional: true,\n default: 'me',\n description:\n \"Element to toggle the class on (not `target`: that is hyperscript's name for the event target)\",\n },\n ],\n events: [\n { name: 'toggleable:on', description: 'Fired when class is added' },\n { name: 'toggleable:off', description: 'Fired when class is removed' },\n ],\n source: `\nbehavior Toggleable(cls, targetEl)\n init\n if cls is undefined set element's cls to \"active\" end\n if targetEl is undefined set element's targetEl to me end\n end\n on click\n toggle .{cls} on targetEl\n js(targetEl, cls)\n var eventName = targetEl.classList.contains(cls) ? 'toggleable:on' : 'toggleable:off';\n targetEl.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));\n end\n end\nend`.trim(),\n};\n","/**\n * Behavior Schema Types\n *\n * Defines the structure for behavior metadata used for:\n * - Code generation (TypeScript types, documentation)\n * - Runtime categorization and lazy loading\n * - Tooling and IDE integration\n */\n\n/**\n * Behavior categories for organization.\n * - ui: Visual interaction behaviors (drag, resize, sort)\n * - data: Data manipulation behaviors (remove, transform)\n * - animation: Animation and transition behaviors\n * - form: Form-related behaviors (validation, toggle states)\n * - layout: Layout behaviors (sticky, scrollspy)\n */\nexport type BehaviorCategory = 'ui' | 'data' | 'animation' | 'form' | 'layout';\n\n/**\n * Loading tiers for lazy loading optimization.\n * - core: Always loaded (essential behaviors)\n * - common: High usage (loaded for typical apps)\n * - optional: Low usage (loaded on demand)\n */\nexport type BehaviorTier = 'core' | 'common' | 'optional';\n\n/**\n * Schema for a behavior parameter.\n */\nexport interface ParameterSchema {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'selector' | 'element';\n optional?: boolean;\n default?: string | number | boolean;\n /** Allowed values for constrained parameters */\n enum?: string[];\n description: string;\n}\n\n/**\n * Schema for a behavior event.\n */\nexport interface EventSchema {\n name: string;\n description: string;\n}\n\n/**\n * Complete schema for a behavior.\n * This is the source of truth for all behavior metadata.\n */\nexport interface BehaviorSchema {\n /** Behavior name (PascalCase) */\n name: string;\n /** Category for organization */\n category: BehaviorCategory;\n /** Loading tier for lazy loading */\n tier: BehaviorTier;\n /** Semantic version */\n version: string;\n /** Human-readable description */\n description: string;\n /** Parameter definitions */\n parameters: ParameterSchema[];\n /** Event definitions */\n events: EventSchema[];\n /** The hyperscript behavior source code */\n source: string;\n /** Optional requirements or notes */\n requirements?: string[];\n}\n\n/**\n * Runtime behavior module interface: a behavior's source, its schema, and the function that\n * defines it on a hyperscript host.\n */\nexport interface BehaviorModule {\n source: string;\n metadata: BehaviorSchema;\n register: (host?: HyperscriptHost) => Promise<void>;\n}\n\n/**\n * A hyperscript host, in upstream `_hyperscript`'s shape: `@hyperfixi/engine` (the\n * `hyperfixi-hs.js` bundle, `window.hyperfixi` / `window._hyperscript`) or upstream itself.\n * `evaluate(source)` of a `behavior … end` program defines the behavior; `processNode`\n * initialises the scripted elements under a node.\n */\nexport interface HyperscriptHost {\n evaluate: (source: string) => unknown;\n processNode?: (node: Node) => void;\n}\n\n/** The window globals a host installs itself as. */\nexport interface HyperscriptWindow {\n hyperfixi?: HyperscriptHost;\n _hyperscript?: HyperscriptHost;\n}\n\n/** The host on `window`: `hyperfixi` (the engine's bundle) or `_hyperscript` (upstream's name). */\nexport function resolveRuntime(): HyperscriptHost | null {\n if (typeof window === 'undefined') return null;\n const win = window as unknown as HyperscriptWindow;\n const host = win.hyperfixi ?? win._hyperscript;\n return host && typeof host.evaluate === 'function' ? host : null;\n}\n\n/**\n * Define a behavior on a host from its schema's source, as a page's\n * `<script type=\"text/hyperscript\">` would. Throws when there is no host or the source\n * does not parse.\n */\nexport function defineBehavior(schema: BehaviorSchema, host?: HyperscriptHost): void {\n const hf = host ?? resolveRuntime();\n if (!hf) {\n throw new Error(\n `No hyperscript host found: load @hyperfixi/engine (hyperfixi-hs.js) before defining ${schema.name}.`\n );\n }\n try {\n hf.evaluate(schema.source);\n } catch (e) {\n throw new Error(`Failed to define ${schema.name} behavior: ${(e as Error).message}`);\n }\n}\n"],"mappings":"scAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,uBAAAC,EAAA,uBAAAC,EAAA,qBAAAC,ICQO,IAAMC,EAAmC,CAC9C,KAAM,aACN,SAAU,OACV,KAAM,OACN,QAAS,QACT,YAAa,+BACb,WAAY,CACV,CACE,KAAM,MACN,KAAM,SACN,SAAU,GACV,QAAS,SACT,YAAa,qBACf,EACA,CACE,KAAM,WACN,KAAM,WACN,SAAU,GACV,QAAS,KACT,YACE,gGACJ,CACF,EACA,OAAQ,CACN,CAAE,KAAM,gBAAiB,YAAa,2BAA4B,EAClE,CAAE,KAAM,iBAAkB,YAAa,6BAA8B,CACvE,EACA,OAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAaL,KAAK,CACV,ECoDO,SAASC,GAAyC,CACvD,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,EAAM,OACNC,EAAOD,EAAI,WAAaA,EAAI,aAClC,OAAOC,GAAQ,OAAOA,EAAK,UAAa,WAAaA,EAAO,IAC9D,CAOO,SAASC,EAAeC,EAAwBF,EAA8B,CACnF,IAAMG,EAAKH,GAAQF,EAAe,EAClC,GAAI,CAACK,EACH,MAAM,IAAI,MACR,uFAAuFD,EAAO,IAAI,GACpG,EAEF,GAAI,CACFC,EAAG,SAASD,EAAO,MAAM,CAC3B,OAASE,EAAG,CACV,MAAM,IAAI,MAAM,oBAAoBF,EAAO,IAAI,cAAeE,EAAY,OAAO,EAAE,CACrF,CACF,CFtGO,IAAMC,EAAmBC,EAAiB,OACpCC,EAAqBD,EAMlC,eAAsBE,EAAmBC,EAAuC,CAC9EC,EAAeJ,EAAkBG,CAAI,CACvC,CAGIE,EAAe,GACjBH,EAAmB,EAAE,MAAM,QAAQ,KAAK,EAG1C,IAAOI,EAAQ,CACb,OAAQN,EAAiB,OACzB,SAAUA,EACV,SAAUE,CACZ","names":["toggleable_exports","__export","toggleable_default","registerToggleable","toggleableMetadata","toggleableSource","toggleableSchema","resolveRuntime","win","host","defineBehavior","schema","hf","e","toggleableSource","toggleableSchema","toggleableMetadata","registerToggleable","host","defineBehavior","resolveRuntime","toggleable_default"]}
@@ -66,35 +66,28 @@ interface BehaviorSchema {
66
66
  requirements?: string[];
67
67
  }
68
68
  /**
69
- * Runtime behavior module interface.
70
- * This is what gets registered with the hyperfixi runtime.
69
+ * Runtime behavior module interface: a behavior's source, its schema, and the function that
70
+ * defines it on a hyperscript host.
71
71
  */
72
72
  interface BehaviorModule {
73
73
  source: string;
74
74
  metadata: BehaviorSchema;
75
- register: (hyperfixi?: LokaScriptInstance) => Promise<void>;
75
+ register: (host?: HyperscriptHost) => Promise<void>;
76
76
  }
77
77
  /**
78
- * Minimal LokaScript instance interface for behavior registration.
78
+ * A hyperscript host, in upstream `_hyperscript`'s shape: `@hyperfixi/engine` (the
79
+ * `hyperfixi-hs.js` bundle, `window.hyperfixi` / `window._hyperscript`) or upstream itself.
80
+ * `evaluate(source)` of a `behavior … end` program defines the behavior; `processNode`
81
+ * initialises the scripted elements under a node.
79
82
  */
80
- interface LokaScriptInstance {
81
- compileSync: (code: string, options?: {
82
- traditional?: boolean;
83
- language?: string;
84
- }) => {
85
- ok: boolean;
86
- ast?: unknown;
87
- errors?: Array<{
88
- message: string;
89
- line?: number;
90
- column?: number;
91
- }>;
92
- };
93
- execute: (ast: unknown, ctx: unknown) => Promise<unknown>;
94
- createContext?: () => {
95
- locals: Map<string, unknown>;
96
- globals: Map<string, unknown>;
97
- };
83
+ interface HyperscriptHost {
84
+ evaluate: (source: string) => unknown;
85
+ processNode?: (node: Node) => void;
86
+ }
87
+ /** The window globals a host installs itself as. */
88
+ interface HyperscriptWindow {
89
+ hyperfixi?: HyperscriptHost;
90
+ _hyperscript?: HyperscriptHost;
98
91
  }
99
92
 
100
- export type { BehaviorSchema as B, EventSchema as E, LokaScriptInstance as L, ParameterSchema as P, BehaviorModule as a, BehaviorCategory as b, BehaviorTier as c };
93
+ export type { BehaviorSchema as B, EventSchema as E, HyperscriptHost as H, ParameterSchema as P, BehaviorModule as a, BehaviorCategory as b, BehaviorTier as c, HyperscriptWindow as d };
@@ -66,35 +66,28 @@ interface BehaviorSchema {
66
66
  requirements?: string[];
67
67
  }
68
68
  /**
69
- * Runtime behavior module interface.
70
- * This is what gets registered with the hyperfixi runtime.
69
+ * Runtime behavior module interface: a behavior's source, its schema, and the function that
70
+ * defines it on a hyperscript host.
71
71
  */
72
72
  interface BehaviorModule {
73
73
  source: string;
74
74
  metadata: BehaviorSchema;
75
- register: (hyperfixi?: LokaScriptInstance) => Promise<void>;
75
+ register: (host?: HyperscriptHost) => Promise<void>;
76
76
  }
77
77
  /**
78
- * Minimal LokaScript instance interface for behavior registration.
78
+ * A hyperscript host, in upstream `_hyperscript`'s shape: `@hyperfixi/engine` (the
79
+ * `hyperfixi-hs.js` bundle, `window.hyperfixi` / `window._hyperscript`) or upstream itself.
80
+ * `evaluate(source)` of a `behavior … end` program defines the behavior; `processNode`
81
+ * initialises the scripted elements under a node.
79
82
  */
80
- interface LokaScriptInstance {
81
- compileSync: (code: string, options?: {
82
- traditional?: boolean;
83
- language?: string;
84
- }) => {
85
- ok: boolean;
86
- ast?: unknown;
87
- errors?: Array<{
88
- message: string;
89
- line?: number;
90
- column?: number;
91
- }>;
92
- };
93
- execute: (ast: unknown, ctx: unknown) => Promise<unknown>;
94
- createContext?: () => {
95
- locals: Map<string, unknown>;
96
- globals: Map<string, unknown>;
97
- };
83
+ interface HyperscriptHost {
84
+ evaluate: (source: string) => unknown;
85
+ processNode?: (node: Node) => void;
86
+ }
87
+ /** The window globals a host installs itself as. */
88
+ interface HyperscriptWindow {
89
+ hyperfixi?: HyperscriptHost;
90
+ _hyperscript?: HyperscriptHost;
98
91
  }
99
92
 
100
- export type { BehaviorSchema as B, EventSchema as E, LokaScriptInstance as L, ParameterSchema as P, BehaviorModule as a, BehaviorCategory as b, BehaviorTier as c };
93
+ export type { BehaviorSchema as B, EventSchema as E, HyperscriptHost as H, ParameterSchema as P, BehaviorModule as a, BehaviorCategory as b, BehaviorTier as c, HyperscriptWindow as d };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hyperfixi/behaviors",
3
- "version": "3.2.0",
4
- "description": "Reusable hyperscript behaviors for LokaScript",
3
+ "version": "4.0.0",
4
+ "description": "Reusable hyperscript behaviors for @hyperfixi/engine (hyperfixi-hs.js) and upstream _hyperscript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -108,16 +108,16 @@
108
108
  "build:watch": "tsup --watch",
109
109
  "pretypecheck": "npm run generate",
110
110
  "typecheck": "tsc --noEmit",
111
- "pretest": "npm run generate",
111
+ "pretest": "npm run generate && bash ../../scripts/ensure-fresh.sh engine",
112
112
  "test": "vitest run",
113
113
  "test:watch": "vitest",
114
114
  "test:check": "VITEST_QUIET=1 bash ../../scripts/vitest-run.sh --reporter=dot"
115
115
  },
116
116
  "peerDependencies": {
117
- "@hyperfixi/core": "^3.2.0"
117
+ "@hyperfixi/engine": "^4.0.0"
118
118
  },
119
119
  "devDependencies": {
120
- "@hyperfixi/core": "^3.2.0",
120
+ "@hyperfixi/engine": "^4.0.0",
121
121
  "tsup": "^8.0.0",
122
122
  "typescript": "^5.0.0",
123
123
  "vitest": "^5.0.2"
@@ -1,20 +1,10 @@
1
1
  /**
2
- * Behavior Resolver — Lazy compilation of hyperscript behaviors
2
+ * The behavior sources, by name, and the function that defines them on a host.
3
3
  *
4
- * Provides a resolver function that compiles behavior source strings on demand.
5
- * When the runtime encounters `install X` and X isn't registered, it calls
6
- * the resolver. If the resolver has source for X, it compiles it, registers
7
- * the behavior, and installation proceeds normally.
8
- *
9
- * @example
10
- * ```typescript
11
- * import { createBehaviorResolver } from '@hyperfixi/behaviors/resolver';
12
- *
13
- * const resolve = createBehaviorResolver(window.hyperfixi, window._hyperscript.behaviors);
14
- * window._hyperscript.behaviors.resolve = resolve;
15
- *
16
- * // Now `install Toggleable` will compile the behavior on first use
17
- * ```
4
+ * Every consumer — the npm `register*()` functions, the browser bundle
5
+ * (`resolver.browser.global.js`) and `@hyperfixi/patterns-reference` — reads the same
6
+ * `source` string from each schema in `src/schemas/`. A host defines a behavior from its
7
+ * source exactly as it would from a page's `<script type="text/hyperscript">`.
18
8
  */
19
9
 
20
10
  import { toggleableSchema } from './schemas/toggleable.schema';
@@ -28,6 +18,7 @@ import { tabsSchema } from './schemas/tabs.schema';
28
18
  import { focusTrapSchema } from './schemas/focustrap.schema';
29
19
  import { sortableSchema } from './schemas/sortable.schema';
30
20
  import { resizableSchema } from './schemas/resizable.schema';
21
+ import type { HyperscriptHost } from './schemas/types';
31
22
 
32
23
  export const BEHAVIOR_SOURCES: Record<string, string> = {
33
24
  Toggleable: toggleableSchema.source,
@@ -43,49 +34,26 @@ export const BEHAVIOR_SOURCES: Record<string, string> = {
43
34
  Resizable: resizableSchema.source,
44
35
  };
45
36
 
46
- interface CompileResult {
47
- ok: boolean;
48
- ast?: any;
49
- errors?: unknown[];
50
- }
51
-
52
- interface BehaviorAPI {
53
- set(name: string, definition: any): void;
54
- }
55
-
56
- interface HyperFixiAPI {
57
- compileSync(code: string, options?: { traditional?: boolean }): CompileResult;
58
- }
59
-
60
37
  /**
61
- * Create a behavior resolver that compiles hyperscript source on demand.
38
+ * Define the named behaviors (every one by default) on a host. A source that does not
39
+ * parse is reported and skipped, so one bad behavior does not take the others with it.
62
40
  *
63
- * The returned function is meant to be assigned to `_hyperscript.behaviors.resolve`.
64
- * When called with a behavior name, it looks up the source, compiles it,
65
- * and registers the resulting AST in the behavior registry.
66
- *
67
- * @param hyperfixi - The hyperfixi API (needs compileSync)
68
- * @param behaviorAPI - The behavior registry API (needs set)
69
- * @returns Resolver function: (name: string) => boolean
41
+ * @returns the names that were defined
70
42
  */
71
- export function createBehaviorResolver(
72
- hyperfixi: HyperFixiAPI,
73
- behaviorAPI: BehaviorAPI
74
- ): (name: string) => boolean {
75
- return (name: string): boolean => {
43
+ export function defineBehaviors(
44
+ host: HyperscriptHost,
45
+ names: readonly string[] = Object.keys(BEHAVIOR_SOURCES)
46
+ ): string[] {
47
+ const defined: string[] = [];
48
+ for (const name of names) {
76
49
  const source = BEHAVIOR_SOURCES[name];
77
- if (!source) return false;
78
-
79
- const result = hyperfixi.compileSync(source, { traditional: true });
80
- if (!result.ok || !result.ast) return false;
81
-
82
- const ast = result.ast as any;
83
- behaviorAPI.set(name, {
84
- name: ast.name,
85
- parameters: ast.parameters,
86
- eventHandlers: ast.eventHandlers,
87
- initBlock: ast.initBlock,
88
- });
89
- return true;
90
- };
50
+ if (!source) continue;
51
+ try {
52
+ host.evaluate(source);
53
+ defined.push(name);
54
+ } catch (e) {
55
+ console.error(`[behaviors] ${name} did not parse: ${(e as Error).message}`);
56
+ }
57
+ }
58
+ return defined;
91
59
  }
@@ -2,36 +2,28 @@ import { describe, it, expect, vi } from 'vitest';
2
2
 
3
3
  describe('AutoDismiss behavior', () => {
4
4
  describe('registerAutoDismiss', () => {
5
- it('should compile its hyperscript source and execute', async () => {
5
+ it('defines the behavior on the host from its hyperscript source', async () => {
6
6
  const { registerAutoDismiss, autoDismissSource } = await import('./autodismiss');
7
- const mock = {
8
- compileSync: vi.fn().mockReturnValue({ ok: true, ast: { type: 'behavior' } }),
9
- execute: vi.fn().mockResolvedValue(undefined),
10
- createContext: vi.fn().mockReturnValue({ locals: new Map(), globals: new Map() }),
11
- };
7
+ const host = { evaluate: vi.fn() };
12
8
 
13
- await registerAutoDismiss(mock);
9
+ await registerAutoDismiss(host);
14
10
 
15
- expect(mock.compileSync).toHaveBeenCalledWith(autoDismissSource, { traditional: true });
16
- expect(mock.execute).toHaveBeenCalledWith(
17
- { type: 'behavior' },
18
- expect.objectContaining({ locals: expect.any(Map), globals: expect.any(Map) })
19
- );
11
+ expect(host.evaluate).toHaveBeenCalledWith(autoDismissSource);
20
12
  });
21
13
 
22
- it('should throw on compile failure', async () => {
14
+ it('should throw when the host rejects the source', async () => {
23
15
  const { registerAutoDismiss } = await import('./autodismiss');
24
- const mock = {
25
- compileSync: vi.fn().mockReturnValue({ ok: false, errors: [{ message: 'boom' }] }),
26
- execute: vi.fn(),
27
- createContext: vi.fn().mockReturnValue({ locals: new Map(), globals: new Map() }),
16
+ const host = {
17
+ evaluate: vi.fn(() => {
18
+ throw new Error('boom');
19
+ }),
28
20
  };
29
- await expect(registerAutoDismiss(mock)).rejects.toThrowError(/Failed to compile AutoDismiss/);
21
+ await expect(registerAutoDismiss(host)).rejects.toThrowError(/Failed to define AutoDismiss/);
30
22
  });
31
23
 
32
- it('should throw when no runtime available', async () => {
24
+ it('should throw when no host is available', async () => {
33
25
  const { registerAutoDismiss } = await import('./autodismiss');
34
- await expect(registerAutoDismiss(undefined)).rejects.toThrowError(/LokaScript not found/);
26
+ await expect(registerAutoDismiss(undefined)).rejects.toThrowError(/No hyperscript host/);
35
27
  });
36
28
  });
37
29
 
@@ -5,39 +5,24 @@
5
5
  * Supports pause-on-hover and fade effect using standard setTimeout/clearTimeout
6
6
  * (in its hyperscript `source`'s js() body).
7
7
  *
8
- * Compiled from its hyperscript `source` (the single source of truth shared with
9
- * the CDN resolver bundle and patterns-reference).
8
+ * Defined from its hyperscript `source` (the single source of truth shared with
9
+ * the browser bundle and patterns-reference).
10
10
  */
11
11
 
12
12
  import { autoDismissSchema } from '../schemas/autodismiss.schema';
13
- import type { LokaScriptInstance } from '../schemas/types';
14
- import { resolveRuntime } from '../schemas/types';
13
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
14
+ import type { HyperscriptHost } from '../schemas/types';
15
15
 
16
16
  // Re-export schema-derived values
17
17
  export const autoDismissSource = autoDismissSchema.source;
18
18
  export const autoDismissMetadata = autoDismissSchema;
19
19
 
20
20
  /**
21
- * Register the AutoDismiss behavior with LokaScript by compiling its
22
- * hyperscript source and executing the resulting behavior definition.
21
+ * Define the AutoDismiss behavior on a hyperscript host (`window.hyperfixi` when none is given)
22
+ * from its source, as a `<script type="text/hyperscript">` would.
23
23
  */
24
- export async function registerAutoDismiss(hyperfixi?: LokaScriptInstance): Promise<void> {
25
- const hf = hyperfixi || resolveRuntime();
26
-
27
- if (!hf) {
28
- throw new Error(
29
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
30
- );
31
- }
32
-
33
- const result = hf.compileSync(autoDismissSchema.source, { traditional: true });
34
-
35
- if (!result.ok) {
36
- throw new Error(`Failed to compile AutoDismiss behavior: ${JSON.stringify(result.errors)}`);
37
- }
38
-
39
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
40
- await hf.execute(result.ast, ctx);
24
+ export async function registerAutoDismiss(host?: HyperscriptHost): Promise<void> {
25
+ defineBehavior(autoDismissSchema, host);
41
26
  }
42
27
 
43
28
  // Auto-register when loaded as a script tag
@@ -2,38 +2,30 @@ import { describe, it, expect, vi } from 'vitest';
2
2
 
3
3
  describe('ClickOutside behavior', () => {
4
4
  describe('registerClickOutside', () => {
5
- it('should compile its hyperscript source and execute', async () => {
5
+ it('defines the behavior on the host from its hyperscript source', async () => {
6
6
  const { registerClickOutside, clickOutsideSource } = await import('./clickoutside');
7
- const mock = {
8
- compileSync: vi.fn().mockReturnValue({ ok: true, ast: { type: 'behavior' } }),
9
- execute: vi.fn().mockResolvedValue(undefined),
10
- createContext: vi.fn().mockReturnValue({ locals: new Map(), globals: new Map() }),
11
- };
7
+ const host = { evaluate: vi.fn() };
12
8
 
13
- await registerClickOutside(mock);
9
+ await registerClickOutside(host);
14
10
 
15
- expect(mock.compileSync).toHaveBeenCalledWith(clickOutsideSource, { traditional: true });
16
- expect(mock.execute).toHaveBeenCalledWith(
17
- { type: 'behavior' },
18
- expect.objectContaining({ locals: expect.any(Map), globals: expect.any(Map) })
19
- );
11
+ expect(host.evaluate).toHaveBeenCalledWith(clickOutsideSource);
20
12
  });
21
13
 
22
- it('should throw on compile failure', async () => {
14
+ it('should throw when the host rejects the source', async () => {
23
15
  const { registerClickOutside } = await import('./clickoutside');
24
- const mock = {
25
- compileSync: vi.fn().mockReturnValue({ ok: false, errors: [{ message: 'boom' }] }),
26
- execute: vi.fn(),
27
- createContext: vi.fn().mockReturnValue({ locals: new Map(), globals: new Map() }),
16
+ const host = {
17
+ evaluate: vi.fn(() => {
18
+ throw new Error('boom');
19
+ }),
28
20
  };
29
- await expect(registerClickOutside(mock)).rejects.toThrowError(
30
- /Failed to compile ClickOutside/
21
+ await expect(registerClickOutside(host)).rejects.toThrowError(
22
+ /Failed to define ClickOutside/
31
23
  );
32
24
  });
33
25
 
34
- it('should throw when no runtime available', async () => {
26
+ it('should throw when no host is available', async () => {
35
27
  const { registerClickOutside } = await import('./clickoutside');
36
- await expect(registerClickOutside(undefined)).rejects.toThrowError(/LokaScript not found/);
28
+ await expect(registerClickOutside(undefined)).rejects.toThrowError(/No hyperscript host/);
37
29
  });
38
30
  });
39
31
 
@@ -4,39 +4,24 @@
4
4
  * Fires a clickoutside event when the user clicks outside the element.
5
5
  * Uses pointerdown instead of click to avoid timing issues.
6
6
  *
7
- * Compiled from its hyperscript `source` (the single source of truth shared with
8
- * the CDN resolver bundle and patterns-reference).
7
+ * Defined from its hyperscript `source` (the single source of truth shared with
8
+ * the browser bundle and patterns-reference).
9
9
  */
10
10
 
11
11
  import { clickOutsideSchema } from '../schemas/clickoutside.schema';
12
- import type { LokaScriptInstance } from '../schemas/types';
13
- import { resolveRuntime } from '../schemas/types';
12
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
13
+ import type { HyperscriptHost } from '../schemas/types';
14
14
 
15
15
  // Re-export schema-derived values
16
16
  export const clickOutsideSource = clickOutsideSchema.source;
17
17
  export const clickOutsideMetadata = clickOutsideSchema;
18
18
 
19
19
  /**
20
- * Register the ClickOutside behavior with LokaScript by compiling its
21
- * hyperscript source and executing the resulting behavior definition.
20
+ * Define the ClickOutside behavior on a hyperscript host (`window.hyperfixi` when none is given)
21
+ * from its source, as a `<script type="text/hyperscript">` would.
22
22
  */
23
- export async function registerClickOutside(hyperfixi?: LokaScriptInstance): Promise<void> {
24
- const hf = hyperfixi || resolveRuntime();
25
-
26
- if (!hf) {
27
- throw new Error(
28
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
29
- );
30
- }
31
-
32
- const result = hf.compileSync(clickOutsideSchema.source, { traditional: true });
33
-
34
- if (!result.ok) {
35
- throw new Error(`Failed to compile ClickOutside behavior: ${JSON.stringify(result.errors)}`);
36
- }
37
-
38
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
39
- await hf.execute(result.ast, ctx);
23
+ export async function registerClickOutside(host?: HyperscriptHost): Promise<void> {
24
+ defineBehavior(clickOutsideSchema, host);
40
25
  }
41
26
 
42
27
  // Auto-register when loaded as a script tag