@ansh-ui/core 0.2.0 → 0.3.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 (54) hide show
  1. package/dist/behaviours.cjs +20 -15
  2. package/dist/behaviours.d.cts +2 -2
  3. package/dist/behaviours.d.ts +2 -2
  4. package/dist/behaviours.js +3 -2
  5. package/dist/{chunk-56NNBIAO.js → chunk-2DOA43UB.js} +1047 -18
  6. package/dist/chunk-2DOA43UB.js.map +1 -0
  7. package/dist/{chunk-7MYTSAPR.js → chunk-37GPMMMT.js} +3 -3
  8. package/dist/{chunk-7MYTSAPR.js.map → chunk-37GPMMMT.js.map} +1 -1
  9. package/dist/{chunk-23GSSIMK.cjs → chunk-4ORYTFY3.cjs} +3 -3
  10. package/dist/{chunk-23GSSIMK.cjs.map → chunk-4ORYTFY3.cjs.map} +1 -1
  11. package/dist/{chunk-MMO7KO53.cjs → chunk-FNR4B3KY.cjs} +3 -3
  12. package/dist/{chunk-MMO7KO53.cjs.map → chunk-FNR4B3KY.cjs.map} +1 -1
  13. package/dist/{chunk-BOQQHICI.js → chunk-GQH5ZHPB.js} +3 -3
  14. package/dist/{chunk-BOQQHICI.js.map → chunk-GQH5ZHPB.js.map} +1 -1
  15. package/dist/{chunk-R7UJ7MWP.cjs → chunk-OSNUN5JJ.cjs} +1070 -34
  16. package/dist/chunk-OSNUN5JJ.cjs.map +1 -0
  17. package/dist/{chunk-GHBRGMSO.cjs → chunk-PZF3YXVE.cjs} +6 -6
  18. package/dist/{chunk-GHBRGMSO.cjs.map → chunk-PZF3YXVE.cjs.map} +1 -1
  19. package/dist/{chunk-UJBXY6QC.cjs → chunk-SSAWZIO2.cjs} +150 -28
  20. package/dist/chunk-SSAWZIO2.cjs.map +1 -0
  21. package/dist/{chunk-C6XPQO2O.js → chunk-WLOGUD3G.js} +126 -5
  22. package/dist/{chunk-C6XPQO2O.js.map → chunk-WLOGUD3G.js.map} +1 -1
  23. package/dist/{chunk-KTUBCQC3.js → chunk-YHSCAGCI.js} +3 -3
  24. package/dist/{chunk-KTUBCQC3.js.map → chunk-YHSCAGCI.js.map} +1 -1
  25. package/dist/color.cjs +39 -39
  26. package/dist/color.d.cts +5 -4
  27. package/dist/color.d.ts +5 -4
  28. package/dist/color.js +3 -3
  29. package/dist/effects.cjs +53 -25
  30. package/dist/effects.d.cts +4 -4
  31. package/dist/effects.d.ts +4 -4
  32. package/dist/effects.js +4 -4
  33. package/dist/{index-CQEEPu-D.d.ts → index-DN0bQII7.d.ts} +47 -2
  34. package/dist/{index-DHOfsEIn.d.cts → index-DmJ3i1Tr.d.cts} +330 -4
  35. package/dist/{index-Dl8GCsya.d.cts → index-Dv3gw7Mb.d.cts} +47 -2
  36. package/dist/{index-CCdpefMp.d.ts → index-SO8zHlVo.d.ts} +330 -4
  37. package/dist/index.cjs +186 -154
  38. package/dist/index.d.cts +10 -9
  39. package/dist/index.d.ts +10 -9
  40. package/dist/index.js +9 -9
  41. package/dist/math.d.cts +3 -2
  42. package/dist/math.d.ts +3 -2
  43. package/dist/noise.d.cts +2 -1
  44. package/dist/noise.d.ts +2 -1
  45. package/dist/{palette-6HkVltYU.d.ts → palette-Dcomsp9R.d.cts} +2 -1
  46. package/dist/{palette-6HkVltYU.d.cts → palette-Dcomsp9R.d.ts} +2 -1
  47. package/dist/{random--4mSc5Lp.d.cts → random-YnhdaiIx.d.cts} +2 -1
  48. package/dist/{random--4mSc5Lp.d.ts → random-YnhdaiIx.d.ts} +2 -1
  49. package/dist/{reduced-motion-D0LUgZle.d.cts → reduced-motion-DaeXwgzb.d.cts} +2 -1
  50. package/dist/{reduced-motion-D0LUgZle.d.ts → reduced-motion-DaeXwgzb.d.ts} +2 -1
  51. package/package.json +1 -1
  52. package/dist/chunk-56NNBIAO.js.map +0 -1
  53. package/dist/chunk-R7UJ7MWP.cjs.map +0 -1
  54. package/dist/chunk-UJBXY6QC.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk23GSSIMK_cjs = require('./chunk-23GSSIMK.cjs');
3
+ var chunk4ORYTFY3_cjs = require('./chunk-4ORYTFY3.cjs');
4
4
  var chunkLGA3N3YJ_cjs = require('./chunk-LGA3N3YJ.cjs');
5
5
  var chunkILKUL7GD_cjs = require('./chunk-ILKUL7GD.cjs');
6
6
 
@@ -8,7 +8,7 @@ var chunkILKUL7GD_cjs = require('./chunk-ILKUL7GD.cjs');
8
8
  function contrastRatio(foreground, background) {
9
9
  const bg = chunkLGA3N3YJ_cjs.parseColor(background);
10
10
  const fgRaw = chunkLGA3N3YJ_cjs.parseColor(foreground);
11
- const fg = fgRaw.a < 1 ? chunk23GSSIMK_cjs.composite(bg, fgRaw) : fgRaw;
11
+ const fg = fgRaw.a < 1 ? chunk4ORYTFY3_cjs.composite(bg, fgRaw) : fgRaw;
12
12
  const l1 = chunkLGA3N3YJ_cjs.relativeLuminance(fg);
13
13
  const l2 = chunkLGA3N3YJ_cjs.relativeLuminance(bg);
14
14
  const lighter = Math.max(l1, l2);
@@ -43,7 +43,7 @@ function ensureContrast(foreground, background, target = 4.5) {
43
43
  const limit = direction > 0 ? 1 - lch.l : lch.l;
44
44
  for (let i = 1; i <= STEPS; i++) {
45
45
  const l = chunkILKUL7GD_cjs.clamp(lch.l + direction * (limit * (i / STEPS)), 0, 1);
46
- const candidate = chunk23GSSIMK_cjs.oklchToRgbMapped({ l, c: lch.c, h: lch.h, alpha: fg.a });
46
+ const candidate = chunk4ORYTFY3_cjs.oklchToRgbMapped({ l, c: lch.c, h: lch.h, alpha: fg.a });
47
47
  const ratio = contrastRatio(candidate, bg);
48
48
  if (ratio > bestRatio) {
49
49
  bestRatio = ratio;
@@ -54,7 +54,7 @@ function ensureContrast(foreground, background, target = 4.5) {
54
54
  for (let i = 1; i <= 8; i++) {
55
55
  const c = lch.c * (1 - i / 8);
56
56
  const l = direction > 0 ? 1 : 0;
57
- const candidate = chunk23GSSIMK_cjs.oklchToRgbMapped({ l, c, h: lch.h, alpha: fg.a });
57
+ const candidate = chunk4ORYTFY3_cjs.oklchToRgbMapped({ l, c, h: lch.h, alpha: fg.a });
58
58
  const ratio = contrastRatio(candidate, bg);
59
59
  if (ratio > bestRatio) {
60
60
  bestRatio = ratio;
@@ -87,5 +87,5 @@ exports.perceivedLightness = perceivedLightness;
87
87
  exports.pickReadable = pickReadable;
88
88
  exports.requiredRatio = requiredRatio;
89
89
  exports.worstContrast = worstContrast;
90
- //# sourceMappingURL=chunk-GHBRGMSO.cjs.map
91
- //# sourceMappingURL=chunk-GHBRGMSO.cjs.map
90
+ //# sourceMappingURL=chunk-PZF3YXVE.cjs.map
91
+ //# sourceMappingURL=chunk-PZF3YXVE.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/color/contrast.ts"],"names":["parseColor","composite","relativeLuminance","rgbToOklch","clamp","oklchToRgbMapped"],"mappings":"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","file":"chunk-GHBRGMSO.cjs","sourcesContent":["import { clamp } from '../math/scalar'\nimport { relativeLuminance, rgbToOklch } from './convert'\nimport { oklchToRgbMapped } from './gamut'\nimport { parseColor } from './parse'\nimport { composite } from './interpolate'\nimport type { ColorInput, Rgb } from './types'\n\n/**\n * Contrast and legibility.\n *\n * Animated backgrounds are the single most common way a design ends up failing WCAG: the text\n * was legible against the mockup's midpoint and illegible against the animation's brightest\n * frame. These helpers let components check contrast against the *extremes* of their own\n * palette and adapt, rather than hoping.\n */\n\nexport type WcagLevel = 'AA' | 'AAA'\nexport type TextSize = 'normal' | 'large'\n\n/**\n * WCAG 2.x contrast ratio, from 1 (identical) to 21 (black on white).\n *\n * Semi-transparent colours are composited over `background` first, because a ratio computed\n * from a colour's own alpha is meaningless — what matters is what the eye actually receives.\n */\nexport function contrastRatio(foreground: ColorInput, background: ColorInput): number {\n const bg = parseColor(background)\n const fgRaw = parseColor(foreground)\n const fg = fgRaw.a < 1 ? composite(bg, fgRaw) : fgRaw\n\n const l1 = relativeLuminance(fg)\n const l2 = relativeLuminance(bg)\n const lighter = Math.max(l1, l2)\n const darker = Math.min(l1, l2)\n return (lighter + 0.05) / (darker + 0.05)\n}\n\n/** Minimum ratio required by WCAG 2.2 for the given level and text size. */\nexport function requiredRatio(level: WcagLevel, size: TextSize): number {\n if (level === 'AAA') return size === 'large' ? 4.5 : 7\n return size === 'large' ? 3 : 4.5\n}\n\nexport interface ContrastResult {\n ratio: number\n passes: boolean\n required: number\n level: WcagLevel\n size: TextSize\n}\n\n/** Check a foreground/background pair against a WCAG threshold. */\nexport function checkContrast(\n foreground: ColorInput,\n background: ColorInput,\n level: WcagLevel = 'AA',\n size: TextSize = 'normal',\n): ContrastResult {\n const ratio = contrastRatio(foreground, background)\n const required = requiredRatio(level, size)\n return { ratio, passes: ratio >= required, required, level, size }\n}\n\n/**\n * Pick whichever of `light`/`dark` reads better on `background`.\n *\n * This is the function to reach for when a component renders text over a user-supplied\n * colour, which is most of them.\n */\nexport function pickReadable(\n background: ColorInput,\n light: ColorInput = '#ffffff',\n dark: ColorInput = '#0b0b0f',\n): Rgb {\n const lightRatio = contrastRatio(light, background)\n const darkRatio = contrastRatio(dark, background)\n return parseColor(lightRatio >= darkRatio ? light : dark)\n}\n\n/**\n * Adjust a colour's lightness until it meets a contrast target against `background`.\n *\n * Works in OKLCH so hue and chroma survive the adjustment — the brand colour stays\n * recognisably itself, it just gets light or dark enough to read. Searches in whichever\n * direction has more headroom, and returns the best achievable colour if the target is\n * unreachable (a mid-grey background cannot support 7:1 with a saturated hue at any lightness).\n */\nexport function ensureContrast(\n foreground: ColorInput,\n background: ColorInput,\n target = 4.5,\n): { color: Rgb; ratio: number; achieved: boolean } {\n const fg = parseColor(foreground)\n const bg = parseColor(background)\n\n const initial = contrastRatio(fg, bg)\n if (initial >= target) return { color: fg, ratio: initial, achieved: true }\n\n const lch = rgbToOklch(fg)\n const bgLuminance = relativeLuminance(bg)\n // Move away from the background: darken against a light background, lighten against a dark one.\n const direction = bgLuminance > 0.35 ? -1 : 1\n\n let best = fg\n let bestRatio = initial\n\n // 24 steps across the available lightness range lands within ~4% of optimal, which is\n // finer than the eye can distinguish and costs 24 cheap conversions.\n const STEPS = 24\n const limit = direction > 0 ? 1 - lch.l : lch.l\n\n for (let i = 1; i <= STEPS; i++) {\n const l = clamp(lch.l + direction * (limit * (i / STEPS)), 0, 1)\n const candidate = oklchToRgbMapped({ l, c: lch.c, h: lch.h, alpha: fg.a })\n const ratio = contrastRatio(candidate, bg)\n if (ratio > bestRatio) {\n bestRatio = ratio\n best = candidate\n if (ratio >= target) return { color: candidate, ratio, achieved: true }\n }\n }\n\n // Last resort: drop chroma too. Desaturating buys real lightness range at the extremes.\n for (let i = 1; i <= 8; i++) {\n const c = lch.c * (1 - i / 8)\n const l = direction > 0 ? 1 : 0\n const candidate = oklchToRgbMapped({ l, c, h: lch.h, alpha: fg.a })\n const ratio = contrastRatio(candidate, bg)\n if (ratio > bestRatio) {\n bestRatio = ratio\n best = candidate\n if (ratio >= target) return { color: candidate, ratio, achieved: true }\n }\n }\n\n return { color: best, ratio: bestRatio, achieved: bestRatio >= target }\n}\n\n/**\n * Worst-case contrast of a foreground against every colour in a set.\n *\n * An animated gradient background cycles through many colours; text must be legible against\n * all of them. Components call this with their resolved palette and surface a dev warning when\n * it fails, which turns an invisible accessibility bug into a console message during development.\n */\nexport function worstContrast(\n foreground: ColorInput,\n backgrounds: readonly ColorInput[],\n): { ratio: number; against: Rgb | null } {\n let worst = Infinity\n let against: Rgb | null = null\n for (const bg of backgrounds) {\n const ratio = contrastRatio(foreground, bg)\n if (ratio < worst) {\n worst = ratio\n against = parseColor(bg)\n }\n }\n return { ratio: Number.isFinite(worst) ? worst : 21, against }\n}\n\n/**\n * APCA-style perceptual lightness, used for ordering colours by visual weight.\n *\n * Not a WCAG 3 implementation — that specification is still in flux and we won't ship a\n * moving target as if it were stable. This is OKLab's `L`, which is a better predictor of\n * perceived lightness than WCAG's relative luminance and is entirely sufficient for the\n * internal job of sorting a palette light-to-dark.\n */\nexport function perceivedLightness(color: ColorInput): number {\n return rgbToOklch(parseColor(color)).l\n}\n"]}
1
+ {"version":3,"sources":["../src/color/contrast.ts"],"names":["parseColor","composite","relativeLuminance","rgbToOklch","clamp","oklchToRgbMapped"],"mappings":"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","file":"chunk-PZF3YXVE.cjs","sourcesContent":["import { clamp } from '../math/scalar'\nimport { relativeLuminance, rgbToOklch } from './convert'\nimport { oklchToRgbMapped } from './gamut'\nimport { parseColor } from './parse'\nimport { composite } from './interpolate'\nimport type { ColorInput, Rgb } from './types'\n\n/**\n * Contrast and legibility.\n *\n * Animated backgrounds are the single most common way a design ends up failing WCAG: the text\n * was legible against the mockup's midpoint and illegible against the animation's brightest\n * frame. These helpers let components check contrast against the *extremes* of their own\n * palette and adapt, rather than hoping.\n */\n\nexport type WcagLevel = 'AA' | 'AAA'\nexport type TextSize = 'normal' | 'large'\n\n/**\n * WCAG 2.x contrast ratio, from 1 (identical) to 21 (black on white).\n *\n * Semi-transparent colours are composited over `background` first, because a ratio computed\n * from a colour's own alpha is meaningless — what matters is what the eye actually receives.\n */\nexport function contrastRatio(foreground: ColorInput, background: ColorInput): number {\n const bg = parseColor(background)\n const fgRaw = parseColor(foreground)\n const fg = fgRaw.a < 1 ? composite(bg, fgRaw) : fgRaw\n\n const l1 = relativeLuminance(fg)\n const l2 = relativeLuminance(bg)\n const lighter = Math.max(l1, l2)\n const darker = Math.min(l1, l2)\n return (lighter + 0.05) / (darker + 0.05)\n}\n\n/** Minimum ratio required by WCAG 2.2 for the given level and text size. */\nexport function requiredRatio(level: WcagLevel, size: TextSize): number {\n if (level === 'AAA') return size === 'large' ? 4.5 : 7\n return size === 'large' ? 3 : 4.5\n}\n\nexport interface ContrastResult {\n ratio: number\n passes: boolean\n required: number\n level: WcagLevel\n size: TextSize\n}\n\n/** Check a foreground/background pair against a WCAG threshold. */\nexport function checkContrast(\n foreground: ColorInput,\n background: ColorInput,\n level: WcagLevel = 'AA',\n size: TextSize = 'normal',\n): ContrastResult {\n const ratio = contrastRatio(foreground, background)\n const required = requiredRatio(level, size)\n return { ratio, passes: ratio >= required, required, level, size }\n}\n\n/**\n * Pick whichever of `light`/`dark` reads better on `background`.\n *\n * This is the function to reach for when a component renders text over a user-supplied\n * colour, which is most of them.\n */\nexport function pickReadable(\n background: ColorInput,\n light: ColorInput = '#ffffff',\n dark: ColorInput = '#0b0b0f',\n): Rgb {\n const lightRatio = contrastRatio(light, background)\n const darkRatio = contrastRatio(dark, background)\n return parseColor(lightRatio >= darkRatio ? light : dark)\n}\n\n/**\n * Adjust a colour's lightness until it meets a contrast target against `background`.\n *\n * Works in OKLCH so hue and chroma survive the adjustment — the brand colour stays\n * recognisably itself, it just gets light or dark enough to read. Searches in whichever\n * direction has more headroom, and returns the best achievable colour if the target is\n * unreachable (a mid-grey background cannot support 7:1 with a saturated hue at any lightness).\n */\nexport function ensureContrast(\n foreground: ColorInput,\n background: ColorInput,\n target = 4.5,\n): { color: Rgb; ratio: number; achieved: boolean } {\n const fg = parseColor(foreground)\n const bg = parseColor(background)\n\n const initial = contrastRatio(fg, bg)\n if (initial >= target) return { color: fg, ratio: initial, achieved: true }\n\n const lch = rgbToOklch(fg)\n const bgLuminance = relativeLuminance(bg)\n // Move away from the background: darken against a light background, lighten against a dark one.\n const direction = bgLuminance > 0.35 ? -1 : 1\n\n let best = fg\n let bestRatio = initial\n\n // 24 steps across the available lightness range lands within ~4% of optimal, which is\n // finer than the eye can distinguish and costs 24 cheap conversions.\n const STEPS = 24\n const limit = direction > 0 ? 1 - lch.l : lch.l\n\n for (let i = 1; i <= STEPS; i++) {\n const l = clamp(lch.l + direction * (limit * (i / STEPS)), 0, 1)\n const candidate = oklchToRgbMapped({ l, c: lch.c, h: lch.h, alpha: fg.a })\n const ratio = contrastRatio(candidate, bg)\n if (ratio > bestRatio) {\n bestRatio = ratio\n best = candidate\n if (ratio >= target) return { color: candidate, ratio, achieved: true }\n }\n }\n\n // Last resort: drop chroma too. Desaturating buys real lightness range at the extremes.\n for (let i = 1; i <= 8; i++) {\n const c = lch.c * (1 - i / 8)\n const l = direction > 0 ? 1 : 0\n const candidate = oklchToRgbMapped({ l, c, h: lch.h, alpha: fg.a })\n const ratio = contrastRatio(candidate, bg)\n if (ratio > bestRatio) {\n bestRatio = ratio\n best = candidate\n if (ratio >= target) return { color: candidate, ratio, achieved: true }\n }\n }\n\n return { color: best, ratio: bestRatio, achieved: bestRatio >= target }\n}\n\n/**\n * Worst-case contrast of a foreground against every colour in a set.\n *\n * An animated gradient background cycles through many colours; text must be legible against\n * all of them. Components call this with their resolved palette and surface a dev warning when\n * it fails, which turns an invisible accessibility bug into a console message during development.\n */\nexport function worstContrast(\n foreground: ColorInput,\n backgrounds: readonly ColorInput[],\n): { ratio: number; against: Rgb | null } {\n let worst = Infinity\n let against: Rgb | null = null\n for (const bg of backgrounds) {\n const ratio = contrastRatio(foreground, bg)\n if (ratio < worst) {\n worst = ratio\n against = parseColor(bg)\n }\n }\n return { ratio: Number.isFinite(worst) ? worst : 21, against }\n}\n\n/**\n * APCA-style perceptual lightness, used for ordering colours by visual weight.\n *\n * Not a WCAG 3 implementation — that specification is still in flux and we won't ship a\n * moving target as if it were stable. This is OKLab's `L`, which is a better predictor of\n * perceived lightness than WCAG's relative luminance and is entirely sufficient for the\n * internal job of sorting a palette light-to-dark.\n */\nexport function perceivedLightness(color: ColorInput): number {\n return rgbToOklch(parseColor(color)).l\n}\n"]}
@@ -1,13 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var chunkMMO7KO53_cjs = require('./chunk-MMO7KO53.cjs');
3
+ var chunkFNR4B3KY_cjs = require('./chunk-FNR4B3KY.cjs');
4
4
  var chunkLGA3N3YJ_cjs = require('./chunk-LGA3N3YJ.cjs');
5
5
  var chunkGJRIHN76_cjs = require('./chunk-GJRIHN76.cjs');
6
+ var chunkFNR35HTL_cjs = require('./chunk-FNR35HTL.cjs');
6
7
  var chunkILKUL7GD_cjs = require('./chunk-ILKUL7GD.cjs');
7
8
 
8
9
  // src/behaviours/types.ts
9
10
  function mountBehaviour(definition, element, userOptions = {}) {
10
- chunkMMO7KO53_cjs.getRegistry().requireCapability(definition.capability ?? "behaviour:core");
11
+ chunkFNR4B3KY_cjs.getRegistry().requireCapability(definition.capability ?? "behaviour:core");
11
12
  let options = { ...definition.defaults, ...stripUndefined(userOptions) };
12
13
  let instance = definition.setup(element, options);
13
14
  let destroyed = false;
@@ -33,7 +34,7 @@ function mountBehaviour(definition, element, userOptions = {}) {
33
34
  };
34
35
  }
35
36
  function defineBehaviour(definition) {
36
- const registry = chunkMMO7KO53_cjs.getRegistry();
37
+ const registry = chunkFNR4B3KY_cjs.getRegistry();
37
38
  if (!registry.has("behaviour", definition.name)) {
38
39
  registry.register("behaviour", definition.name, definition);
39
40
  }
@@ -85,7 +86,7 @@ var magnetic = defineBehaviour({
85
86
  let opts = options;
86
87
  const target = opts.target ? element.querySelector(opts.target) ?? element : element;
87
88
  const restoreStyles = captureStyles(target, ["transform", "willChange"]);
88
- const pointer = chunkMMO7KO53_cjs.trackPointer(element, { trackOutside: true, smoothing: 1e-3 });
89
+ const pointer = chunkFNR4B3KY_cjs.trackPointer(element, { trackOutside: true, smoothing: 1e-3 });
89
90
  let x = 0;
90
91
  let y = 0;
91
92
  let vx = 0;
@@ -94,9 +95,9 @@ var magnetic = defineBehaviour({
94
95
  let scaleVelocity = 0;
95
96
  let rotation = 0;
96
97
  let rotationVelocity = 0;
97
- let motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
98
+ let motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
98
99
  target.style.willChange = "transform";
99
- const ticker = chunkMMO7KO53_cjs.getTicker();
100
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
100
101
  const stop = ticker.add(
101
102
  ({ delta }) => {
102
103
  if (opts.disabled || !motion.animate) return;
@@ -153,7 +154,7 @@ var magnetic = defineBehaviour({
153
154
  return {
154
155
  update(next) {
155
156
  opts = { ...opts, ...next };
156
- motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
157
+ motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
157
158
  if (opts.disabled || !motion.animate) {
158
159
  x = y = vx = vy = 0;
159
160
  scaleValue = 1;
@@ -192,14 +193,14 @@ var tilt = defineBehaviour({
192
193
  setup(element, options) {
193
194
  let opts = options;
194
195
  const restoreStyles = captureStyles(element, ["transform", "transformStyle", "willChange"]);
195
- const pointer = chunkMMO7KO53_cjs.trackPointer(element, { smoothing: 2e-3 });
196
+ const pointer = chunkFNR4B3KY_cjs.trackPointer(element, { smoothing: 2e-3 });
196
197
  let rotateX = 0;
197
198
  let rotateY = 0;
198
199
  let velocityX = 0;
199
200
  let velocityY = 0;
200
201
  let presence = 0;
201
202
  let presenceVelocity = 0;
202
- let motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
203
+ let motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
203
204
  element.style.willChange = "transform";
204
205
  element.style.transformStyle = "preserve-3d";
205
206
  let glareElement = null;
@@ -233,7 +234,7 @@ var tilt = defineBehaviour({
233
234
  glareElement = null;
234
235
  }
235
236
  ensureGlare();
236
- const ticker = chunkMMO7KO53_cjs.getTicker();
237
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
237
238
  const stop = ticker.add(
238
239
  ({ delta }) => {
239
240
  if (opts.disabled || !motion.animate) return;
@@ -279,7 +280,7 @@ var tilt = defineBehaviour({
279
280
  update(next) {
280
281
  const previousGlare = opts.glare;
281
282
  opts = { ...opts, ...next };
282
- motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
283
+ motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
283
284
  if (opts.glare !== previousGlare) ensureGlare();
284
285
  if (opts.disabled || !motion.animate) {
285
286
  rotateX = rotateY = velocityX = velocityY = 0;
@@ -344,7 +345,7 @@ var marquee = defineBehaviour({
344
345
  let offset = 0;
345
346
  let currentSpeed = 0;
346
347
  let hovering = false;
347
- let motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
348
+ let motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
348
349
  function rebuild() {
349
350
  for (const clone of clones) clone.remove();
350
351
  clones = [];
@@ -373,8 +374,8 @@ var marquee = defineBehaviour({
373
374
  element.style.setProperty("mask-image", mask);
374
375
  element.style.setProperty("-webkit-mask-image", mask);
375
376
  }
376
- const stopResize = chunkMMO7KO53_cjs.observeResize(element, rebuild);
377
- const stopTrackResize = chunkMMO7KO53_cjs.observeResize(track, rebuild);
377
+ const stopResize = chunkFNR4B3KY_cjs.observeResize(element, rebuild);
378
+ const stopTrackResize = chunkFNR4B3KY_cjs.observeResize(track, rebuild);
378
379
  const onEnter = () => {
379
380
  hovering = true;
380
381
  };
@@ -385,7 +386,7 @@ var marquee = defineBehaviour({
385
386
  element.addEventListener("pointerleave", onLeave, { passive: true });
386
387
  applyEdgeFade();
387
388
  rebuild();
388
- const ticker = chunkMMO7KO53_cjs.getTicker();
389
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
389
390
  const stop = ticker.add(
390
391
  ({ delta }) => {
391
392
  if (opts.disabled || !motion.animate || cycleSize <= 0) return;
@@ -407,7 +408,7 @@ var marquee = defineBehaviour({
407
408
  const previousGap = opts.gap;
408
409
  const previousFade = opts.fadeEdges;
409
410
  opts = { ...opts, ...next };
410
- motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
411
+ motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
411
412
  if (opts.gap !== previousGap) {
412
413
  track.style.gap = opts.gap ? `${opts.gap}px` : "";
413
414
  rebuild();
@@ -795,7 +796,7 @@ var scrollReveal = defineBehaviour({
795
796
  target.time = 0;
796
797
  applyProgress(target, 0);
797
798
  }
798
- const ticker = chunkMMO7KO53_cjs.getTicker();
799
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
799
800
  function startTicker() {
800
801
  if (stopTicker) return;
801
802
  stopTicker = ticker.add(
@@ -830,12 +831,12 @@ var scrollReveal = defineBehaviour({
830
831
  );
831
832
  }
832
833
  collectTargets();
833
- const skipAnimation = opts.disabled || chunkMMO7KO53_cjs.prefersReducedMotion() && opts.reducedMotion !== "none";
834
+ const skipAnimation = opts.disabled || chunkFNR4B3KY_cjs.prefersReducedMotion() && opts.reducedMotion !== "none";
834
835
  if (!skipAnimation) {
835
836
  for (const target of targets) hide(target);
836
837
  }
837
838
  const stopObserver = skipAnimation ? () => {
838
- } : chunkMMO7KO53_cjs.observeIntersection(
839
+ } : chunkFNR4B3KY_cjs.observeIntersection(
839
840
  element,
840
841
  (entry) => {
841
842
  if (entry.isIntersecting) {
@@ -904,7 +905,7 @@ var parallax = defineBehaviour({
904
905
  let containerSize = 0;
905
906
  let needsMeasure = true;
906
907
  let currentTranslate = 0;
907
- let motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
908
+ let motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
908
909
  element.style.willChange = "transform";
909
910
  function measure() {
910
911
  const rect = element.getBoundingClientRect();
@@ -925,10 +926,10 @@ var parallax = defineBehaviour({
925
926
  needsMeasure = true;
926
927
  };
927
928
  window.addEventListener("resize", invalidate, { passive: true });
928
- const stopResize = chunkMMO7KO53_cjs.observeResize(element, invalidate);
929
+ const stopResize = chunkFNR4B3KY_cjs.observeResize(element, invalidate);
929
930
  let scroll = 0;
930
931
  let progress = 0;
931
- const ticker = chunkMMO7KO53_cjs.getTicker();
932
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
932
933
  const stopRead = ticker.add(
933
934
  () => {
934
935
  if (opts.disabled || !motion.animate) return;
@@ -981,7 +982,7 @@ var parallax = defineBehaviour({
981
982
  return {
982
983
  update(next) {
983
984
  opts = { ...opts, ...next };
984
- motion = chunkMMO7KO53_cjs.resolveMotion(opts.reducedMotion);
985
+ motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
985
986
  needsMeasure = true;
986
987
  if (opts.disabled || !motion.animate) {
987
988
  element.style.transform = "";
@@ -1039,7 +1040,7 @@ var ripple = defineBehaviour({
1039
1040
  element.appendChild(layer);
1040
1041
  const active = [];
1041
1042
  let stopTicker = null;
1042
- const ticker = chunkMMO7KO53_cjs.getTicker();
1043
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
1043
1044
  function resolveColor() {
1044
1045
  const raw = opts.color;
1045
1046
  if (raw === "currentColor") {
@@ -1097,7 +1098,7 @@ var ripple = defineBehaviour({
1097
1098
  if (stopTicker) return;
1098
1099
  stopTicker = ticker.add(
1099
1100
  ({ delta }) => {
1100
- const scale = chunkMMO7KO53_cjs.prefersReducedMotion() && opts.reducedMotion === "slow" ? 0.4 : 1;
1101
+ const scale = chunkFNR4B3KY_cjs.prefersReducedMotion() && opts.reducedMotion === "slow" ? 0.4 : 1;
1101
1102
  const duration = Math.max(0.01, opts.duration * scale);
1102
1103
  const fade = Math.max(0, opts.fadeDuration * scale);
1103
1104
  const total = duration + fade;
@@ -1156,6 +1157,125 @@ var ripple = defineBehaviour({
1156
1157
  }
1157
1158
  });
1158
1159
 
1160
+ // src/behaviours/text-scramble.ts
1161
+ var DEFAULTS8 = {
1162
+ intensity: 1,
1163
+ // `static` rather than `slow`: the churn is high-frequency flicker in the reader's focal area,
1164
+ // which is exactly the kind of motion the preference exists to suppress. Reduced-motion users
1165
+ // get the finished text immediately.
1166
+ reducedMotion: "static",
1167
+ disabled: false,
1168
+ chars: "!<>-_\\/[]{}\u2014=+*^?#________",
1169
+ duration: 0.55,
1170
+ stagger: 0.035,
1171
+ cycleSpeed: 26,
1172
+ trigger: "view",
1173
+ repeat: false,
1174
+ threshold: 0.3,
1175
+ seed: 1
1176
+ };
1177
+ var textScramble = defineBehaviour({
1178
+ name: "text-scramble",
1179
+ defaults: DEFAULTS8,
1180
+ setup(element, options) {
1181
+ let opts = options;
1182
+ const original = element.textContent ?? "";
1183
+ const length = original.length;
1184
+ const pool = opts.chars.length > 0 ? opts.chars : DEFAULTS8.chars;
1185
+ const rng = chunkFNR35HTL_cjs.createRng(opts.seed);
1186
+ const current = Array.from(original);
1187
+ const lastTick = new Int32Array(Math.max(1, length));
1188
+ let elapsed = 0;
1189
+ let running = false;
1190
+ let hasRun = false;
1191
+ let stopFrame = null;
1192
+ const isWhitespace = (character) => character.trim() === "";
1193
+ const totalDuration = () => Math.max(0, length - 1) * opts.stagger + opts.duration;
1194
+ function writeFinal() {
1195
+ element.textContent = original;
1196
+ }
1197
+ function stopFrames() {
1198
+ stopFrame?.();
1199
+ stopFrame = null;
1200
+ running = false;
1201
+ }
1202
+ function run() {
1203
+ if (opts.disabled) return;
1204
+ if (hasRun && !opts.repeat) return;
1205
+ const motion = chunkFNR4B3KY_cjs.resolveMotion(opts.reducedMotion);
1206
+ if (!motion.animate) {
1207
+ hasRun = true;
1208
+ writeFinal();
1209
+ return;
1210
+ }
1211
+ hasRun = true;
1212
+ elapsed = 0;
1213
+ lastTick.fill(-1);
1214
+ running = true;
1215
+ if (stopFrame) return;
1216
+ const ticker = chunkFNR4B3KY_cjs.getTicker();
1217
+ stopFrame = ticker.add(
1218
+ ({ delta }) => {
1219
+ if (!running || opts.disabled) return;
1220
+ elapsed += delta * motion.timeScale * opts.intensity;
1221
+ const total = totalDuration();
1222
+ if (elapsed >= total) {
1223
+ writeFinal();
1224
+ stopFrames();
1225
+ return;
1226
+ }
1227
+ const tick = Math.floor(elapsed * opts.cycleSpeed);
1228
+ for (let i = 0; i < length; i++) {
1229
+ const character = original[i];
1230
+ if (isWhitespace(character)) continue;
1231
+ if (elapsed >= i * opts.stagger + opts.duration) {
1232
+ current[i] = character;
1233
+ continue;
1234
+ }
1235
+ if (lastTick[i] !== tick) {
1236
+ lastTick[i] = tick;
1237
+ current[i] = pool[rng.int(0, pool.length - 1)];
1238
+ }
1239
+ }
1240
+ element.textContent = current.join("");
1241
+ },
1242
+ { phase: "write" }
1243
+ );
1244
+ }
1245
+ let stopObserver = null;
1246
+ let detachHover = null;
1247
+ if (chunkFNR4B3KY_cjs.prefersReducedMotion() && opts.reducedMotion === "static") ; else if (opts.trigger === "mount") {
1248
+ run();
1249
+ } else if (opts.trigger === "hover") {
1250
+ const onEnter = () => run();
1251
+ element.addEventListener("pointerenter", onEnter);
1252
+ detachHover = () => element.removeEventListener("pointerenter", onEnter);
1253
+ } else {
1254
+ stopObserver = chunkFNR4B3KY_cjs.observeIntersection(
1255
+ element,
1256
+ (entry) => {
1257
+ if (entry.isIntersecting) run();
1258
+ else if (opts.repeat) hasRun = false;
1259
+ },
1260
+ { threshold: opts.threshold }
1261
+ );
1262
+ }
1263
+ return {
1264
+ update(next) {
1265
+ opts = { ...opts, ...next };
1266
+ },
1267
+ destroy() {
1268
+ stopFrames();
1269
+ stopObserver?.();
1270
+ detachHover?.();
1271
+ stopObserver = null;
1272
+ detachHover = null;
1273
+ writeFinal();
1274
+ }
1275
+ };
1276
+ }
1277
+ });
1278
+
1159
1279
  // src/behaviours/index.ts
1160
1280
  var behaviours = {
1161
1281
  magnetic,
@@ -1164,7 +1284,8 @@ var behaviours = {
1164
1284
  "split-text": splitText,
1165
1285
  "scroll-reveal": scrollReveal,
1166
1286
  parallax,
1167
- ripple
1287
+ ripple,
1288
+ "text-scramble": textScramble
1168
1289
  };
1169
1290
 
1170
1291
  exports.behaviours = behaviours;
@@ -1181,6 +1302,7 @@ exports.split = split;
1181
1302
  exports.splitText = splitText;
1182
1303
  exports.stagger = stagger;
1183
1304
  exports.staggerDuration = staggerDuration;
1305
+ exports.textScramble = textScramble;
1184
1306
  exports.tilt = tilt;
1185
- //# sourceMappingURL=chunk-UJBXY6QC.cjs.map
1186
- //# sourceMappingURL=chunk-UJBXY6QC.cjs.map
1307
+ //# sourceMappingURL=chunk-SSAWZIO2.cjs.map
1308
+ //# sourceMappingURL=chunk-SSAWZIO2.cjs.map