@hyperfixi/behaviors 3.3.0 → 4.0.1

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 +199 -1
  2. package/README.md +29 -9
  3. package/dist/all.browser.global.js +45 -100
  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 +19 -19
  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-5S7WN3ZQ.js → chunk-IFAH652L.js} +4 -7
  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-ZR66E3T6.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 +96 -271
  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 +75 -237
  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 +4 -7
  138. package/dist/resizable.browser.global.js.map +1 -1
  139. package/dist/resolver.browser.global.js +45 -100
  140. package/dist/resolver.browser.global.js.map +1 -1
  141. package/dist/schemas/index.cjs +38 -93
  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 +3 -6
  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-5S7WN3ZQ.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-7TPDLB7W.js +0 -40
  207. package/dist/chunk-7TPDLB7W.js.map +0 -1
  208. package/dist/chunk-CR3J7VV6.js +0 -40
  209. package/dist/chunk-CR3J7VV6.js.map +0 -1
  210. package/dist/chunk-EDBG2F57.js +0 -40
  211. package/dist/chunk-EDBG2F57.js.map +0 -1
  212. package/dist/chunk-FBQ3X3UY.js.map +0 -1
  213. package/dist/chunk-HQDYH45I.js.map +0 -1
  214. package/dist/chunk-HX663BJ4.js +0 -40
  215. package/dist/chunk-HX663BJ4.js.map +0 -1
  216. package/dist/chunk-KROW43CG.js +0 -40
  217. package/dist/chunk-KROW43CG.js.map +0 -1
  218. package/dist/chunk-L52AGCMP.js +0 -40
  219. package/dist/chunk-L52AGCMP.js.map +0 -1
  220. package/dist/chunk-MTI5PSRH.js +0 -40
  221. package/dist/chunk-MTI5PSRH.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-ZR66E3T6.js.map → chunk-JNLVXPHR.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Resizable Behavior
3
3
  *
4
- * Makes elements resizable by dragging. Compiled from its hyperscript `source`
5
- * (single source of truth) — no imperative installer. The source measures the
6
- * start size, runs a `repeat until event pointerup` loop, clamps to
4
+ * Makes elements resizable by dragging. Defined from its hyperscript `source`
5
+ * (single source of truth) — no imperative installer. The source reads the start
6
+ * size from `my offsetWidth` / `my offsetHeight`, runs a `repeat until event pointerup` loop, clamps to
7
7
  * min/max dimensions, and writes `*width` / `*height` inline styles.
8
8
  *
9
9
  * @example
@@ -15,34 +15,19 @@
15
15
  */
16
16
 
17
17
  import { resizableSchema } from '../schemas/resizable.schema';
18
- import type { LokaScriptInstance } from '../schemas/types';
19
- import { resolveRuntime } from '../schemas/types';
18
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
19
+ import type { HyperscriptHost } from '../schemas/types';
20
20
 
21
21
  // Re-export schema-derived values for backwards compatibility
22
22
  export const resizableSource = resizableSchema.source;
23
23
  export const resizableMetadata = resizableSchema;
24
24
 
25
25
  /**
26
- * Register the Resizable behavior with LokaScript by compiling its hyperscript
27
- * source and executing the resulting behavior definition.
26
+ * Define the Resizable behavior on a hyperscript host (`window.hyperfixi` when none is given)
27
+ * from its source, as a `<script type="text/hyperscript">` would.
28
28
  */
29
- export async function registerResizable(hyperfixi?: LokaScriptInstance): Promise<void> {
30
- const hf = hyperfixi || resolveRuntime();
31
-
32
- if (!hf) {
33
- throw new Error(
34
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
35
- );
36
- }
37
-
38
- const result = hf.compileSync(resizableSchema.source, { traditional: true });
39
-
40
- if (!result.ok) {
41
- throw new Error(`Failed to compile Resizable behavior: ${JSON.stringify(result.errors)}`);
42
- }
43
-
44
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
45
- await hf.execute(result.ast, ctx);
29
+ export async function registerResizable(host?: HyperscriptHost): Promise<void> {
30
+ defineBehavior(resizableSchema, host);
46
31
  }
47
32
 
48
33
  // Auto-register when loaded as a script tag
@@ -2,38 +2,30 @@ import { describe, it, expect, vi } from 'vitest';
2
2
 
3
3
  describe('ScrollReveal behavior', () => {
4
4
  describe('registerScrollReveal', () => {
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 { registerScrollReveal, scrollRevealSource } = await import('./scrollreveal');
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 registerScrollReveal(mock);
9
+ await registerScrollReveal(host);
14
10
 
15
- expect(mock.compileSync).toHaveBeenCalledWith(scrollRevealSource, { 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(scrollRevealSource);
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 { registerScrollReveal } = await import('./scrollreveal');
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(registerScrollReveal(mock)).rejects.toThrowError(
30
- /Failed to compile ScrollReveal/
21
+ await expect(registerScrollReveal(host)).rejects.toThrowError(
22
+ /Failed to define ScrollReveal/
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 { registerScrollReveal } = await import('./scrollreveal');
36
- await expect(registerScrollReveal(undefined)).rejects.toThrowError(/LokaScript not found/);
28
+ await expect(registerScrollReveal(undefined)).rejects.toThrowError(/No hyperscript host/);
37
29
  });
38
30
  });
39
31
 
@@ -5,41 +5,26 @@
5
5
  * Uses the IntersectionObserver web standard API (in its hyperscript `source`'s
6
6
  * `init`-block `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. This collapses ScrollReveal onto
8
+ * Defined from its hyperscript `source` — the single source of truth shared with
9
+ * the browser bundle and patterns-reference. This collapses ScrollReveal onto
10
10
  * the one runtime path (no more imperative-installer fork between CDN and npm);
11
11
  * see docs-internal/BEHAVIORS_CONSOLIDATION_PLAN.md §3d.
12
12
  */
13
13
 
14
14
  import { scrollRevealSchema } from '../schemas/scrollreveal.schema';
15
- import type { LokaScriptInstance } from '../schemas/types';
16
- import { resolveRuntime } from '../schemas/types';
15
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
16
+ import type { HyperscriptHost } from '../schemas/types';
17
17
 
18
18
  // Re-export schema-derived values
19
19
  export const scrollRevealSource = scrollRevealSchema.source;
20
20
  export const scrollRevealMetadata = scrollRevealSchema;
21
21
 
22
22
  /**
23
- * Register the ScrollReveal behavior with LokaScript by compiling its
24
- * hyperscript source and executing the resulting behavior definition.
23
+ * Define the ScrollReveal behavior on a hyperscript host (`window.hyperfixi` when none is given)
24
+ * from its source, as a `<script type="text/hyperscript">` would.
25
25
  */
26
- export async function registerScrollReveal(hyperfixi?: LokaScriptInstance): Promise<void> {
27
- const hf = hyperfixi || resolveRuntime();
28
-
29
- if (!hf) {
30
- throw new Error(
31
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
32
- );
33
- }
34
-
35
- const result = hf.compileSync(scrollRevealSchema.source, { traditional: true });
36
-
37
- if (!result.ok) {
38
- throw new Error(`Failed to compile ScrollReveal behavior: ${JSON.stringify(result.errors)}`);
39
- }
40
-
41
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
42
- await hf.execute(result.ast, ctx);
26
+ export async function registerScrollReveal(host?: HyperscriptHost): Promise<void> {
27
+ defineBehavior(scrollRevealSchema, host);
43
28
  }
44
29
 
45
30
  // Auto-register when loaded as a script tag
@@ -2,7 +2,7 @@
2
2
  * Sortable Behavior
3
3
  *
4
4
  * Drag-and-drop reordering of list items. Apply to a container element; its
5
- * `<li>` children become sortable. Compiled from its hyperscript `source` (single
5
+ * `<li>` children become sortable. Defined from its hyperscript `source` (single
6
6
  * source of truth) — no imperative installer.
7
7
  *
8
8
  * Note: This behavior fires lifecycle events but does NOT automatically reorder
@@ -18,34 +18,19 @@
18
18
  */
19
19
 
20
20
  import { sortableSchema } from '../schemas/sortable.schema';
21
- import type { LokaScriptInstance } from '../schemas/types';
22
- import { resolveRuntime } from '../schemas/types';
21
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
22
+ import type { HyperscriptHost } from '../schemas/types';
23
23
 
24
24
  // Re-export schema-derived values for backwards compatibility
25
25
  export const sortableSource = sortableSchema.source;
26
26
  export const sortableMetadata = sortableSchema;
27
27
 
28
28
  /**
29
- * Register the Sortable behavior with LokaScript by compiling its hyperscript
30
- * source and executing the resulting behavior definition.
29
+ * Define the Sortable behavior on a hyperscript host (`window.hyperfixi` when none is given)
30
+ * from its source, as a `<script type="text/hyperscript">` would.
31
31
  */
32
- export async function registerSortable(hyperfixi?: LokaScriptInstance): Promise<void> {
33
- const hf = hyperfixi || resolveRuntime();
34
-
35
- if (!hf) {
36
- throw new Error(
37
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
38
- );
39
- }
40
-
41
- const result = hf.compileSync(sortableSchema.source, { traditional: true });
42
-
43
- if (!result.ok) {
44
- throw new Error(`Failed to compile Sortable behavior: ${JSON.stringify(result.errors)}`);
45
- }
46
-
47
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
48
- await hf.execute(result.ast, ctx);
32
+ export async function registerSortable(host?: HyperscriptHost): Promise<void> {
33
+ defineBehavior(sortableSchema, host);
49
34
  }
50
35
 
51
36
  // Auto-register when loaded as a script tag
@@ -2,50 +2,28 @@ import { describe, it, expect, vi } from 'vitest';
2
2
 
3
3
  describe('Tabs behavior', () => {
4
4
  describe('registerTabs', () => {
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 { registerTabs, tabsSource } = await import('./tabs');
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 registerTabs(mock);
9
+ await registerTabs(host);
14
10
 
15
- expect(mock.compileSync).toHaveBeenCalledWith(tabsSource, { 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(tabsSource);
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 { registerTabs } = await import('./tabs');
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(registerTabs(mock)).rejects.toThrowError(/Failed to compile Tabs/);
30
- });
31
-
32
- it('should throw when no runtime available', async () => {
33
- const { registerTabs } = await import('./tabs');
34
- await expect(registerTabs(undefined)).rejects.toThrowError(/LokaScript not found/);
21
+ await expect(registerTabs(host)).rejects.toThrowError(/Failed to define Tabs/);
35
22
  });
36
23
 
37
- it('should fall back to manual context when createContext is missing', async () => {
24
+ it('should throw when no host is available', async () => {
38
25
  const { registerTabs } = await import('./tabs');
39
- const mock = {
40
- compileSync: vi.fn().mockReturnValue({ ok: true, ast: { type: 'behavior' } }),
41
- execute: vi.fn().mockResolvedValue(undefined),
42
- };
43
-
44
- await registerTabs(mock as any);
45
- expect(mock.execute).toHaveBeenCalledWith(
46
- expect.anything(),
47
- expect.objectContaining({ locals: expect.any(Map), globals: expect.any(Map) })
48
- );
26
+ await expect(registerTabs(undefined)).rejects.toThrowError(/No hyperscript host/);
49
27
  });
50
28
  });
51
29
 
@@ -6,40 +6,25 @@
6
6
  * wiring / keyboard-navigation logic lives in the schema's hyperscript `source`
7
7
  * (an `init`-block `js()` body).
8
8
  *
9
- * Compiled from its hyperscript `source` — the single source of truth shared with
10
- * the CDN resolver bundle and patterns-reference. This collapses Tabs onto the one
9
+ * Defined from its hyperscript `source` — the single source of truth shared with
10
+ * the browser bundle and patterns-reference. This collapses Tabs onto the one
11
11
  * runtime path (no more imperative-installer fork between CDN and npm);
12
12
  * see docs-internal/BEHAVIORS_CONSOLIDATION_PLAN.md §3d.
13
13
  */
14
14
 
15
15
  import { tabsSchema } from '../schemas/tabs.schema';
16
- import type { LokaScriptInstance } from '../schemas/types';
17
- import { resolveRuntime } from '../schemas/types';
16
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
17
+ import type { HyperscriptHost } from '../schemas/types';
18
18
 
19
19
  export const tabsSource = tabsSchema.source;
20
20
  export const tabsMetadata = tabsSchema;
21
21
 
22
22
  /**
23
- * Register the Tabs behavior with LokaScript by compiling its
24
- * hyperscript source and executing the resulting behavior definition.
23
+ * Define the Tabs behavior on a hyperscript host (`window.hyperfixi` when none is given)
24
+ * from its source, as a `<script type="text/hyperscript">` would.
25
25
  */
26
- export async function registerTabs(hyperfixi?: LokaScriptInstance): Promise<void> {
27
- const hf = hyperfixi || resolveRuntime();
28
-
29
- if (!hf) {
30
- throw new Error(
31
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
32
- );
33
- }
34
-
35
- const result = hf.compileSync(tabsSchema.source, { traditional: true });
36
-
37
- if (!result.ok) {
38
- throw new Error(`Failed to compile Tabs behavior: ${JSON.stringify(result.errors)}`);
39
- }
40
-
41
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
42
- await hf.execute(result.ast, ctx);
26
+ export async function registerTabs(host?: HyperscriptHost): Promise<void> {
27
+ defineBehavior(tabsSchema, host);
43
28
  }
44
29
 
45
30
  // Auto-register when loaded as a script tag
@@ -4,48 +4,32 @@
4
4
  * A behavior that toggles a CSS class on click.
5
5
  * Useful for accordions, dropdowns, and toggle buttons.
6
6
  *
7
- * Compiled from its hyperscript `source` (the single source of truth shared with
8
- * the CDN resolver bundle and patterns-reference). The `.{cls}` dynamic class
9
- * selector resolves the behavior's `cls` parameter at runtime — see
10
- * `packages/core/src/commands/behaviors/__tests__/template-interpolation.test.ts`.
7
+ * Defined from its hyperscript `source` (the single source of truth shared with
8
+ * the browser bundle and patterns-reference). The `.{cls}` dynamic class
9
+ * selector resolves the behavior's `cls` parameter at runtime.
11
10
  *
12
11
  * @example
13
12
  * ```html
14
13
  * <button _="install Toggleable">Toggle</button>
15
14
  *
16
- * <button _="install Toggleable(cls: expanded, target: #menu)">Menu</button>
15
+ * <button _="install Toggleable(cls: 'expanded', targetEl: #menu)">Menu</button>
17
16
  * ```
18
17
  */
19
18
 
20
19
  import { toggleableSchema } from '../schemas/toggleable.schema';
21
- import type { LokaScriptInstance } from '../schemas/types';
22
- import { resolveRuntime } from '../schemas/types';
20
+ import { defineBehavior, resolveRuntime } from '../schemas/types';
21
+ import type { HyperscriptHost } from '../schemas/types';
23
22
 
24
23
  // Re-export schema-derived values for backwards compatibility
25
24
  export const toggleableSource = toggleableSchema.source;
26
25
  export const toggleableMetadata = toggleableSchema;
27
26
 
28
27
  /**
29
- * Register the Toggleable behavior with LokaScript by compiling its
30
- * hyperscript source and executing the resulting behavior definition.
28
+ * Define the Toggleable behavior on a hyperscript host (`window.hyperfixi` when none is given)
29
+ * from its source, as a `<script type="text/hyperscript">` would.
31
30
  */
32
- export async function registerToggleable(hyperfixi?: LokaScriptInstance): Promise<void> {
33
- const hf = hyperfixi || resolveRuntime();
34
-
35
- if (!hf) {
36
- throw new Error(
37
- 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'
38
- );
39
- }
40
-
41
- const result = hf.compileSync(toggleableSchema.source, { traditional: true });
42
-
43
- if (!result.ok) {
44
- throw new Error(`Failed to compile Toggleable behavior: ${JSON.stringify(result.errors)}`);
45
- }
46
-
47
- const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };
48
- await hf.execute(result.ast, ctx);
31
+ export async function registerToggleable(host?: HyperscriptHost): Promise<void> {
32
+ defineBehavior(toggleableSchema, host);
49
33
  }
50
34
 
51
35
  // Auto-register when loaded as a script tag
@@ -1,40 +1,30 @@
1
1
  /**
2
- * Browser Behavior Resolver Bundle
2
+ * The behaviors browser bundle: `resolver.browser.global.js`.
3
3
  *
4
- * Pre-registers all standard behavior definitions with the hyperfixi runtime.
5
- * Include after hyperfixi.js — all standard behaviors become available.
4
+ * Defines every standard behavior on the hyperscript host, so `install Toggleable`,
5
+ * `install Draggable`, … just work. Include it after the host's script:
6
6
  *
7
- * Usage:
8
- * <script src="hyperfixi.js"></script>
9
- * <script src="hyperfixi-behaviors.js"></script>
10
- * <!-- install Toggleable, Draggable, etc. just work -->
7
+ * <script src="hyperfixi-hs.js"></script>
8
+ * <script src="resolver.browser.global.js"></script>
9
+ *
10
+ * The host is `@hyperfixi/engine` (`window.hyperfixi`, also `window._hyperscript`) or
11
+ * upstream `_hyperscript` itself: each source is defined with `evaluate`, as a page's
12
+ * `<script type="text/hyperscript">` would be. Loaded before the document is ready, this runs
13
+ * before the host reads the page, so every `install` finds its behavior; loaded later, the
14
+ * document is processed again so the installs can succeed now.
11
15
  */
12
16
 
13
- import { BEHAVIOR_SOURCES } from './behavior-resolver';
14
-
15
- function init() {
16
- const hf = (window as any).hyperfixi;
17
- if (!hf?.compileSync || !hf?.execute) return;
18
-
19
- // Compile and register each behavior definition with the runtime.
20
- // This is equivalent to having them in a <script type="text/hyperscript"> block.
21
- for (const source of Object.values(BEHAVIOR_SOURCES)) {
22
- const result = hf.compileSync(source, { traditional: true });
23
- if (result?.ok && result.ast) {
24
- // execute() registers the behavior in the runtime's behaviorRegistry
25
- hf.execute(result.ast).catch(() => {
26
- // Async — errors handled silently
27
- });
28
- }
29
- }
17
+ import { defineBehaviors } from './behavior-resolver';
18
+ import { resolveRuntime } from './schemas/types';
30
19
 
31
- // Re-scan: the attribute processor may have already processed elements
32
- // and failed (behaviors weren't registered). Reset and re-init.
33
- const ap = hf.attributeProcessor;
34
- if (ap) {
35
- ap.reset();
36
- ap.init();
20
+ function init(): void {
21
+ const host = resolveRuntime();
22
+ if (!host) {
23
+ console.error('[behaviors] no hyperscript host: load hyperfixi-hs.js before this bundle');
24
+ return;
37
25
  }
26
+ defineBehaviors(host);
27
+ if (document.readyState !== 'loading') host.processNode?.(document.body);
38
28
  }
39
29
 
40
30
  if (typeof window !== 'undefined') {
@@ -11,25 +11,25 @@ export { registerToggleable, toggleableSource, toggleableMetadata } from './beha
11
11
 
12
12
  import { registerDraggable } from './behaviors/draggable';
13
13
  import { registerToggleable } from './behaviors/toggleable';
14
- import type { LokaScriptInstance, LokaScriptWindow } from './schemas/types';
14
+ import type { HyperscriptHost } from './schemas/types';
15
+ import { resolveRuntime } from './schemas/types';
15
16
 
16
17
  /**
17
- * Register all core tier behaviors.
18
+ * Define the core tier behaviors on a hyperscript host.
18
19
  */
19
- export async function registerCore(hyperfixi?: LokaScriptInstance): Promise<void> {
20
- await Promise.all([registerDraggable(hyperfixi), registerToggleable(hyperfixi)]);
20
+ export async function registerCore(host?: HyperscriptHost): Promise<void> {
21
+ await Promise.all([registerDraggable(host), registerToggleable(host)]);
21
22
  }
22
23
 
23
24
  /**
24
- * Promise that resolves when core behaviors are registered.
25
- * Set when the package auto-registers in browser environments.
25
+ * Resolves when the core behaviors are defined, when the package was loaded as a script tag
26
+ * after the host; `null` otherwise.
26
27
  */
27
28
  export let ready: Promise<void> | null = null;
28
29
 
29
- // Auto-register when loaded in browser
30
- if (typeof window !== 'undefined' && (window as unknown as LokaScriptWindow).lokascript) {
31
- ready = registerCore();
32
- ready.catch(err => {
33
- console.error('[behaviors] Core auto-registration failed:', err);
30
+ const _host = resolveRuntime();
31
+ if (_host) {
32
+ ready = registerCore(_host).catch(err => {
33
+ console.error('[behaviors] Core auto-definition failed:', err);
34
34
  });
35
35
  }
package/src/index.test.ts CHANGED
@@ -195,7 +195,7 @@ describe('@hyperfixi/behaviors', () => {
195
195
 
196
196
  it('should use triggerEl parameter (not trigger)', () => {
197
197
  expect(removableSource).toContain('behavior Removable(triggerEl');
198
- expect(removableSource).toContain('on click from triggerEl');
198
+ expect(removableSource).toContain('on click from (triggerEl or me)');
199
199
  });
200
200
  });
201
201
 
@@ -211,7 +211,7 @@ describe('@hyperfixi/behaviors', () => {
211
211
  });
212
212
 
213
213
  it('should default to "active" class', () => {
214
- expect(toggleableSource).toContain('set cls to "active"');
214
+ expect(toggleableSource).toContain(`set element's cls to "active"`);
215
215
  });
216
216
  });
217
217
 
package/src/index.ts CHANGED
@@ -26,7 +26,7 @@
26
26
  *
27
27
  * @example CDN usage
28
28
  * ```html
29
- * <script src="lokascript-browser.js"></script>
29
+ * <script src="hyperfixi-hs.js"></script>
30
30
  * <script src="@hyperfixi/behaviors/draggable.browser.js"></script>
31
31
  * <!-- Draggable is auto-registered -->
32
32
  * ```
@@ -119,7 +119,8 @@ export type {
119
119
  ParameterSchema,
120
120
  EventSchema,
121
121
  BehaviorModule,
122
- LokaScriptInstance,
122
+ HyperscriptHost,
123
+ HyperscriptWindow,
123
124
  } from './schemas/types';
124
125
 
125
126
  // =============================================================================
@@ -214,15 +215,14 @@ import { registerClickOutside } from './behaviors/clickoutside';
214
215
  import { registerFocusTrap } from './behaviors/focustrap';
215
216
  import { registerScrollReveal } from './behaviors/scrollreveal';
216
217
  import { registerTabs } from './behaviors/tabs';
217
- import type { LokaScriptInstance } from './schemas/types';
218
+ import type { HyperscriptHost } from './schemas/types';
218
219
  import { resolveRuntime } from './schemas/types';
219
- import type { LokaScriptWindow } from './schemas/types';
220
220
 
221
221
  /**
222
- * Register all behaviors with HyperFixi.
222
+ * Define every behavior on a hyperscript host.
223
223
  *
224
- * @param hyperfixi - The hyperfixi instance (defaults to window.hyperfixi)
225
- * @returns Promise that resolves when all behaviors are registered
224
+ * @param host - The host (`@hyperfixi/engine`'s `window.hyperfixi`, or upstream's
225
+ * `window._hyperscript`); defaults to the one on `window`
226
226
  *
227
227
  * @example
228
228
  * ```javascript
@@ -230,59 +230,42 @@ import type { LokaScriptWindow } from './schemas/types';
230
230
  * await registerAll();
231
231
  * ```
232
232
  */
233
- export async function registerAll(hyperfixi?: LokaScriptInstance): Promise<void> {
233
+ export async function registerAll(host?: HyperscriptHost): Promise<void> {
234
234
  await Promise.all([
235
- registerDraggable(hyperfixi),
236
- registerRemovable(hyperfixi),
237
- registerToggleable(hyperfixi),
238
- registerSortable(hyperfixi),
239
- registerResizable(hyperfixi),
240
- registerClipboard(hyperfixi),
241
- registerAutoDismiss(hyperfixi),
242
- registerClickOutside(hyperfixi),
243
- registerFocusTrap(hyperfixi),
244
- registerScrollReveal(hyperfixi),
245
- registerTabs(hyperfixi),
235
+ registerDraggable(host),
236
+ registerRemovable(host),
237
+ registerToggleable(host),
238
+ registerSortable(host),
239
+ registerResizable(host),
240
+ registerClipboard(host),
241
+ registerAutoDismiss(host),
242
+ registerClickOutside(host),
243
+ registerFocusTrap(host),
244
+ registerScrollReveal(host),
245
+ registerTabs(host),
246
246
  ]);
247
247
  }
248
248
 
249
249
  // =============================================================================
250
- // Auto-registration for browser
250
+ // Auto-definition for the browser bundles
251
251
  // =============================================================================
252
252
 
253
253
  /**
254
- * Promise that resolves when all behaviors are registered.
255
- * This is set when the package auto-registers in browser environments.
254
+ * Resolves when every behavior is defined, when the package was loaded as a script tag after
255
+ * the host; `null` otherwise.
256
256
  */
257
257
  export let ready: Promise<void> | null = null;
258
258
 
259
- // Auto-register all behaviors when loaded in browser with hyperfixi available
260
- const _runtime = resolveRuntime();
261
- if (_runtime) {
262
- const lsWin = window as unknown as LokaScriptWindow;
263
- ready = registerAll(_runtime);
264
-
265
- lsWin.__hyperfixi_behaviors_ready = ready;
266
-
267
- ready
259
+ // Loaded as a script tag after the host, before the document is ready, this runs before the
260
+ // host reads the page, so every `install` finds its behavior. Loaded later, the elements the
261
+ // host has already seen are processed again, so their installs can succeed now.
262
+ const _host = resolveRuntime();
263
+ if (_host) {
264
+ ready = registerAll(_host)
268
265
  .then(() => {
269
- const hf = _runtime as LokaScriptInstance & {
270
- attributeProcessor?: { scanAndProcessAll: () => Promise<void> };
271
- processNode?: (node: Document) => void;
272
- };
273
- const savedPromise = lsWin.__hyperfixi_behaviors_ready;
274
- delete lsWin.__hyperfixi_behaviors_ready;
275
-
276
- if (hf.attributeProcessor?.scanAndProcessAll) {
277
- hf.attributeProcessor.scanAndProcessAll().finally(() => {
278
- lsWin.__hyperfixi_behaviors_ready = savedPromise;
279
- });
280
- } else if (hf.processNode) {
281
- hf.processNode(document);
282
- lsWin.__hyperfixi_behaviors_ready = savedPromise;
283
- }
266
+ if (document.readyState !== 'loading') _host.processNode?.(document.body);
284
267
  })
285
268
  .catch(err => {
286
- console.error('[behaviors] Auto-registration failed:', err);
269
+ console.error('[behaviors] Auto-definition failed:', err);
287
270
  });
288
271
  }