{
  "version": 3,
  "sources": ["../src/modules/scroll.js", "../src/modules/events.js"],
  "sourcesContent": ["// zelvior-runtime/scroll -- lightweight scroll event helper, plus an\n// opt-in adaptive scroll monitor for feeling smoother/less sticky on\n// weak hardware. ESM source of truth; bundled by build.mjs.\n//\n// `onScroll` intentionally does NOT include a custom scrollbar or replace\n// native scrolling in any way. There is no benchmark evidence that native\n// scrolling needs replacing on any target hardware for this project, and a\n// custom scrollbar is real CSS/DOM/accessibility surface for a browser\n// feature that already performs well. What genuinely has a measurable cost\n// is *listening* to scroll carelessly (a non-passive listener blocks the\n// compositor from scrolling ahead of the main thread; an unthrottled\n// handler can run far more often than once per frame, and mixing DOM reads\n// with writes forces synchronous layout) -- so that's what both `onScroll`\n// and `createAdaptiveScroll` (below) address.\n\nimport { passiveOpts, throttleRaf } from './events.js';\n\n/**\n * Attach a passive, rAF-throttled scroll listener to `target` (defaults to\n * window). `fn` is called at most once per animation frame with\n * `{ x, y, target }`. Returns an unsubscribe function that also cancels any\n * pending throttled call.\n *\n * This does not change how the browser scrolls -- it only ensures your own\n * handler doesn't run more than once per frame and doesn't block the\n * compositor's own scroll handling.\n */\nexport function onScroll(target, fn, opts) {\n  if (typeof target === 'function') { opts = fn; fn = target; target = window; }\n  var capture = opts && opts.capture;\n  var throttled = throttleRaf(function () {\n    var x, y;\n    if (target === window) {\n      x = window.pageXOffset !== undefined ? window.pageXOffset : document.documentElement.scrollLeft;\n      y = window.pageYOffset !== undefined ? window.pageYOffset : document.documentElement.scrollTop;\n    } else {\n      x = target.scrollLeft;\n      y = target.scrollTop;\n    }\n    fn({ x: x, y: y, target: target });\n  });\n  target.addEventListener('scroll', throttled, passiveOpts(capture));\n  return function unsubscribe() {\n    target.removeEventListener('scroll', throttled, passiveOpts(capture));\n    throttled.cancel();\n  };\n}\n\n// --- Adaptive Native Scroll ------------------------------------------------\n//\n// Read this before using it: native compositor-driven scrolling is\n// already about as fast as it gets. This does not make the browser's own\n// scrolling faster, replace it, or add any smoothing/momentum of its own\n// -- doing that would mean re-implementing scroll physics on the main\n// thread, which is slower than the browser's native implementation, not\n// faster. What genuinely causes scrolling to *feel* sticky or janky on\n// weak hardware, and is actually addressable from JS:\n//\n//   1. A non-passive scroll/touch listener blocking the compositor.\n//   2. A scroll handler doing real work (DOM reads/writes) more than\n//      once per frame, or interleaving reads and writes so the browser\n//      is forced into synchronous layout mid-scroll.\n//   3. Scroll-driven work continuing to run its full workload even when\n//      the device is visibly struggling (dropped frames / long tasks\n//      already happening) or the user has asked for reduced motion.\n//\n// `createAdaptiveScroll` addresses exactly those three things, and\n// nothing else. It is entirely event-driven: there is no\n// `setInterval`/permanent polling loop anywhere in this function. A\n// `requestAnimationFrame` is only ever requested in direct response to a\n// real `scroll` event, and the chain stops the moment scrolling settles\n// -- an idle page with this active costs nothing beyond one passive\n// listener sitting there.\nexport function createAdaptiveScroll(fn, opts) {\n  opts = opts || {};\n  var target = opts.target || window;\n  var capture = !!opts.capture;\n  var settleMs = typeof opts.settleMs === 'number' ? opts.settleMs : 150;\n\n  // Device-capability signal, computed once, self-contained -- this\n  // module deliberately does not import zelvior-runtime/tier for this\n  // (an unrelated module pull-in for one cheap check isn't worth the\n  // coupling); it reads the same two real navigator signals tier.js\n  // does, directly. Read via `window.navigator`/`window.matchMedia`\n  // explicitly, not the bare `navigator`/`matchMedia` identifiers --\n  // Node.js itself provides a global `navigator` (since Node 21) that\n  // silently answers with Node's own values instead of throwing, which\n  // is a real, easy-to-hit footgun in any tooling that evaluates this\n  // code outside an actual page (bundler SSR passes, Node-based test\n  // harnesses). Explicitly scoping to `window.*` avoids the ambiguity\n  // entirely rather than relying on the accident of which global wins.\n  var lowEndDevice = false;\n  try {\n    var cores = window.navigator.hardwareConcurrency;\n    var mem = window.navigator.deviceMemory; // Chromium-only; undefined elsewhere, and that's fine below\n    lowEndDevice = (typeof cores === 'number' && cores <= 2) || (typeof mem === 'number' && mem <= 2);\n  } catch (e) {}\n\n  var reducedMotion = false;\n  try { reducedMotion = opts.reducedMotionAware !== false && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch (e) {}\n\n  // Long-task/frame-drop awareness, self-contained -- a local\n  // PerformanceObserver, not a pull from any other module. Feature-\n  // detected; simply stays at zero pressure on engines without the\n  // 'longtask' entry type (Safari, most non-Chromium browsers today),\n  // which only means the adaptive throttling below never escalates\n  // beyond its base cadence there -- it does not break anything.\n  var recentLongTasks = 0;\n  var longTaskObserver = null;\n  if (opts.longTaskAware !== false && typeof PerformanceObserver !== 'undefined') {\n    try {\n      if (PerformanceObserver.supportedEntryTypes && PerformanceObserver.supportedEntryTypes.indexOf('longtask') > -1) {\n        longTaskObserver = new PerformanceObserver(function (list) {\n          recentLongTasks += list.getEntries().length;\n        });\n        longTaskObserver.observe({ type: 'longtask', buffered: false });\n      }\n    } catch (e) { longTaskObserver = null; }\n  }\n  // Decays the long-task counter instead of ever fully resetting it on a\n  // fixed timer (which would itself be a small permanent loop) -- it's\n  // decremented lazily, only when a scroll frame actually runs.\n  function decayPressure() { if (recentLongTasks > 0) recentLongTasks--; }\n\n  // FastDOM-style read/write separation, local to this controller so\n  // scroll-time consumers get it without importing zelvior-runtime/paint\n  // (again: no unrelated module pulled in for this). Reads run before\n  // writes within the same already-scheduled frame -- never a separate\n  // frame each, which would just be a second layout-thrashing hazard.\n  var reads = [], writes = [];\n  function flushReadsWrites() {\n    var r = reads; reads = [];\n    var w = writes; writes = [];\n    for (var i = 0; i < r.length; i++) { try { r[i](); } catch (e) {} }\n    for (var j = 0; j < w.length; j++) { try { w[j](); } catch (e) {} }\n  }\n\n  var rafId = null;\n  var frameCounter = 0;\n  var settleTimer = null;\n  var stopped = false;\n\n  function currentPosition() {\n    if (target === window) {\n      return {\n        x: window.pageXOffset !== undefined ? window.pageXOffset : document.documentElement.scrollLeft,\n        y: window.pageYOffset !== undefined ? window.pageYOffset : document.documentElement.scrollTop,\n      };\n    }\n    return { x: target.scrollLeft, y: target.scrollTop };\n  }\n\n  function runFrame() {\n    rafId = null;\n    frameCounter++;\n    decayPressure();\n\n    // Escalating cadence, not a binary on/off: a genuinely struggling\n    // device (recent long tasks piling up) or a low-end/reduced-motion\n    // context runs the consumer callback every other frame instead of\n    // every frame -- still responsive, but roughly half the scroll-time\n    // work. This is \"reduce non-critical work while scrolling is\n    // expensive,\" not \"stop responding to scroll.\"\n    var underPressure = recentLongTasks >= 2 || lowEndDevice || reducedMotion;\n    var shouldRun = !underPressure || (frameCounter % 2 === 0);\n\n    if (shouldRun) {\n      var pos = currentPosition();\n      try {\n        fn({\n          x: pos.x, y: pos.y, target: target,\n          lowEndDevice: lowEndDevice, reducedMotion: reducedMotion, underPressure: underPressure,\n          read: function (r) { reads.push(r); },\n          write: function (w) { writes.push(w); },\n        });\n      } catch (e) {}\n      flushReadsWrites();\n    }\n  }\n\n  function onScrollEvent() {\n    if (stopped) return;\n    if (rafId === null) rafId = requestAnimationFrame(runFrame);\n    if (settleTimer) clearTimeout(settleTimer);\n    settleTimer = setTimeout(function () { settleTimer = null; }, settleMs);\n  }\n\n  target.addEventListener('scroll', onScrollEvent, passiveOpts(capture));\n  // Touch listeners are also registered passive -- this module never\n  // needs to call preventDefault(), so there is no reason not to, and\n  // doing so lets the browser start scrolling without waiting on this\n  // listener at all.\n  var touchTarget = target === window ? window : target;\n  touchTarget.addEventListener('touchmove', function () {}, passiveOpts(capture));\n\n  return {\n    stop: function () {\n      if (stopped) return;\n      stopped = true;\n      target.removeEventListener('scroll', onScrollEvent, passiveOpts(capture));\n      if (rafId !== null) { cancelAnimationFrame(rafId); rafId = null; }\n      if (settleTimer) { clearTimeout(settleTimer); settleTimer = null; }\n      if (longTaskObserver) { longTaskObserver.disconnect(); longTaskObserver = null; }\n      reads = []; writes = [];\n    },\n    isIdle: function () { return rafId === null && settleTimer === null; },\n    isLowEndDevice: function () { return lowEndDevice; },\n    isReducedMotion: function () { return reducedMotion; },\n  };\n}\n", "// zelvior-runtime/events -- standalone event helpers.\n// Zero dependency on core zelvior.js by design: importing this module never\n// pulls in the rest of the runtime. ESM source of truth; bundled by build.mjs.\n\nvar hasRaf = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function';\nvar hasRic = typeof window !== 'undefined' && typeof window.requestIdleCallback === 'function';\nvar IDLE_SHIM = { timeRemaining: function () { return 8; }, didTimeout: false };\n\n// Feature-detected {passive:true} support. Old Safari (<10) and IE throw a\n// TypeError if addEventListener's 3rd argument is an object at all, so this\n// must probe with a real (immediately-removed) listener rather than assume.\nvar _passiveSupported = null;\nfunction detectPassive() {\n  if (_passiveSupported !== null) return _passiveSupported;\n  _passiveSupported = false;\n  try {\n    var opts = Object.defineProperty({}, 'passive', {\n      get: function () { _passiveSupported = true; return true; }\n    });\n    window.addEventListener('__zelvior_passive_test__', null, opts);\n    window.removeEventListener('__zelvior_passive_test__', null, opts);\n  } catch (e) { _passiveSupported = false; }\n  return _passiveSupported;\n}\n\n/**\n * Feature-detected addEventListener options for a passive listener.\n * Falls back to `false` (bubble phase, non-passive) on browsers that don't\n * support the object form at all, so callers can always spread this in\n * safely: el.addEventListener('scroll', fn, passiveOpts())\n */\nexport function passiveOpts(capture) {\n  if (!detectPassive()) return !!capture;\n  return { passive: true, capture: !!capture };\n}\n\n/**\n * Coalesce rapid calls to at most once per animation frame. Useful for\n * scroll/resize/pointermove handlers where only the latest call in a frame\n * matters. Falls back to a 16ms setTimeout on browsers without rAF.\n * Returns the throttled function; call `.cancel()` on it to drop any\n * pending invocation (e.g. on cleanup).\n */\nexport function throttleRaf(fn) {\n  var scheduled = false;\n  var lastArgs = null;\n  var id = 0;\n  function flush() {\n    scheduled = false;\n    var args = lastArgs;\n    lastArgs = null;\n    fn.apply(null, args || []);\n  }\n  function throttled() {\n    lastArgs = arguments;\n    if (scheduled) return;\n    scheduled = true;\n    id = hasRaf ? requestAnimationFrame(flush) : setTimeout(flush, 16);\n  }\n  throttled.cancel = function () {\n    if (!scheduled) return;\n    if (hasRaf) cancelAnimationFrame(id); else clearTimeout(id);\n    scheduled = false;\n    lastArgs = null;\n  };\n  return throttled;\n}\n\n/**\n * Classic trailing-edge debounce: fn runs `wait` ms after the last call.\n * Distinct from throttleRaf -- this is for \"settled\" events (search-as-you-\n * type, resize-end), not per-frame coalescing.\n * Returns the debounced function; call `.cancel()` to drop a pending call.\n */\nexport function debounce(fn, wait) {\n  var t = 0;\n  function debounced() {\n    var args = arguments;\n    clearTimeout(t);\n    t = setTimeout(function () { fn.apply(null, args); }, wait);\n  }\n  debounced.cancel = function () { clearTimeout(t); };\n  return debounced;\n}\n\n/** requestAnimationFrame with a setTimeout(16) fallback. Returns a cancel function. */\nexport function onFrame(fn) {\n  var id = hasRaf ? requestAnimationFrame(fn) : setTimeout(fn, 16);\n  return function cancel() { hasRaf ? cancelAnimationFrame(id) : clearTimeout(id); };\n}\n\n/** requestIdleCallback with a setTimeout(1) fallback. Returns a cancel function. */\nexport function onIdle(fn, opts) {\n  var id = hasRic\n    ? requestIdleCallback(fn, opts || { timeout: 200 })\n    : setTimeout(function () { fn(IDLE_SHIM); }, 1);\n  return function cancel() { hasRic ? cancelIdleCallback(id) : clearTimeout(id); };\n}\n\n/**\n * Event delegation: one listener on `root` instead of one per matching\n * descendant. Real overhead reduction for lists/tables where attaching a\n * listener to every row/cell would mean hundreds of listeners.\n * `handler` is called as handler(event, matchedElement).\n * Returns an unsubscribe function.\n */\nexport function delegate(root, selector, type, handler, opts) {\n  function onEvent(e) {\n    var el = e.target;\n    while (el && el !== root) {\n      if (el.matches && el.matches(selector)) { handler(e, el); return; }\n      el = el.parentNode;\n    }\n  }\n  root.addEventListener(type, onEvent, opts);\n  return function unsubscribe() { root.removeEventListener(type, onEvent, opts); };\n}\n"],
  "mappings": "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",
  "names": ["scroll_exports", "__export", "createAdaptiveScroll", "onScroll", "__toCommonJS", "hasRaf", "hasRic", "_passiveSupported", "detectPassive", "opts", "passiveOpts", "capture", "throttleRaf", "fn", "scheduled", "lastArgs", "id", "flush", "args", "throttled", "hasRaf", "onScroll", "target", "fn", "opts", "capture", "throttled", "throttleRaf", "x", "y", "passiveOpts", "createAdaptiveScroll", "settleMs", "lowEndDevice", "cores", "mem", "e", "reducedMotion", "recentLongTasks", "longTaskObserver", "list", "decayPressure", "reads", "writes", "flushReadsWrites", "r", "w", "i", "j", "rafId", "frameCounter", "settleTimer", "stopped", "currentPosition", "runFrame", "underPressure", "shouldRun", "pos", "onScrollEvent", "touchTarget"]
}
