{"_id":"@arthur5005/warper-ember","_rev":"3-f98f1083d5b08516f5238b2e6251dc9e","name":"@arthur5005/warper-ember","dist-tags":{"alpha":"0.1.0-alpha.1","latest":"0.2.0"},"versions":{"0.1.0-alpha.1":{"name":"@arthur5005/warper-ember","version":"0.1.0-alpha.1","keywords":["warper-ember"],"author":"","license":"MIT","_id":"@arthur5005/warper-ember@0.1.0-alpha.1","maintainers":[{"name":"arthur.goldsmith","email":"arthur5005@gmail.com"}],"homepage":"https://github.com/arthur5005/warper-ember#readme","bugs":{"url":"https://github.com/arthur5005/warper-ember/issues"},"dist":{"shasum":"408756febb6f71cdd44424f1e9689ab2828d7c43","tarball":"https://registry.npmjs.org/@arthur5005/warper-ember/-/warper-ember-0.1.0-alpha.1.tgz","fileCount":43,"integrity":"sha512-17GNsIkmG3/WNoituIlUKhv6tqjDJ9abnsbvBTC0e09sic8C1Iq8pX8EKveMAjmaPWV8JNt1E26pLLdAxAF/Nw==","signatures":[{"sig":"MEQCIFIDiP3O1aPNnEyHUe4eTuw9PgHDIwQL4BNzUsvk05ixAiA1uKF4SRyq1oWdOEHNxbE48zc5SLpxXlt+tqcz35DSXw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":369677},"ember":{"edition":"octane"},"exports":{".":{"types":"./declarations/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./declarations/*.d.ts","default":"./dist/*.js"},"./*.css":"./dist/*.css","./addon-main.js":"./addon-main.cjs"},"gitHead":"f3be7603c017d07b109efeb2349a5631421778f4","imports":{"#src/*":"./src/*"},"scripts":{"dev":"concurrently \"rollup --config --watch\" \"ember-tsc --declaration --project tsconfig.publish.json --watch --preserveWatchOutput\" \"vite dev\" --names \"build,types,serve\" --prefixColors auto","lint":"concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto","test":"vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0","build":"rollup --config","start":"vite dev","format":"prettier . --cache --write","lint:js":"eslint . --cache","prepack":"rollup --config","release":"release-it","lint:fix":"concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format","lint:hbs":"ember-template-lint . --no-error-on-unmatched-pattern","lint:types":"ember-tsc --noEmit","lint:format":"prettier . --cache --check","lint:js:fix":"eslint . --fix","lint:hbs:fix":"ember-template-lint . --fix --no-error-on-unmatched-pattern"},"_npmUser":{"name":"arthur.goldsmith","email":"arthur5005@gmail.com"},"repository":{"url":"git+https://github.com/arthur5005/warper-ember.git","type":"git"},"_npmVersion":"11.4.2","description":"A high-performance virtual list component for Ember using Warper from https://github.com/warper-org/warper","directories":{},"ember-addon":{"main":"addon-main.cjs","type":"addon","app-js":{"./components/virtualizer.js":"./dist/_app_/components/virtualizer.js","./components/warper-component.js":"./dist/_app_/components/warper-component.js"},"version":2},"_nodeVersion":"22.17.0","dependencies":{"reactiveweb":"^1.9.1","ember-modifier":"^4.2.2","decorator-transforms":"^2.2.2","@embroider/addon-shim":"^1.8.9"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.1.9","qunit":"^2.24.1","eslint":"^9.17.0","rollup":"^4.22.5","testem":"^3.15.1","csstype":"^3.2.3","globals":"^16.1.0","prettier":"^3.4.2","qunit-dom":"^3.4.0","@eslint/js":"^9.17.0","release-it":"^19.2.4","typescript":"~5.8.3","@babel/core":"^7.25.2","ember-qunit":"^9.0.2","@types/qunit":"^2.19.12","concurrently":"^9.0.1","ember-source":"^6.7.0","@babel/runtime":"^7.25.6","@embroider/core":"^4.1.0","@embroider/vite":"^1.1.5","@glint/template":"^1.6.1","eslint-plugin-n":"^17.15.1","@glint/ember-tsc":"^1.0.3","ember-page-title":"^9.0.3","@embroider/compat":"^4.1.0","@embroider/macros":"^1.18.0","typescript-eslint":"^8.19.1","@glimmer/component":"^2.0.0","rollup-plugin-copy":"^3.5.0","@ember/app-tsconfig":"^1.0.0","@ember/test-helpers":"^5.2.1","@ember/test-waiters":"^4.1.1","ember-template-lint":"^7.9.0","eslint-plugin-ember":"^12.3.3","@babel/eslint-parser":"^7.25.1","@embroider/addon-dev":"^8.1.0","@rollup/plugin-babel":"^6.0.4","eslint-plugin-import":"^2.31.0","@glint/tsserver-plugin":"^2.0.0","eslint-config-prettier":"^10.1.5","@ember/library-tsconfig":"^1.0.0","ember-strict-application-resolver":"^0.1.0","@babel/plugin-transform-typescript":"^7.25.2","@release-it/conventional-changelog":"^10.0.4","prettier-plugin-ember-template-tag":"^2.0.4","babel-plugin-ember-template-compilation":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/warper-ember_0.1.0-alpha.1_1769804274696_0.8384651184880199","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@arthur5005/warper-ember","version":"0.1.0","keywords":["warper-ember"],"author":"","license":"MIT","_id":"@arthur5005/warper-ember@0.1.0","maintainers":[{"name":"arthur.goldsmith","email":"arthur5005@gmail.com"}],"homepage":"https://github.com/arthur5005/warper-ember#readme","bugs":{"url":"https://github.com/arthur5005/warper-ember/issues"},"dist":{"shasum":"a2da85de1687731f3e45009721da5657460e4662","tarball":"https://registry.npmjs.org/@arthur5005/warper-ember/-/warper-ember-0.1.0.tgz","fileCount":36,"integrity":"sha512-3r9BO+LYpWq5kGlgW2OH7Ex7d2FPC/fk9l9k0LTJb8w4wKYiPp3HGdu7XzAMfILqJOn0M2IuAKt0KifnQTS91Q==","signatures":[{"sig":"MEYCIQC/RL1PhdM1FmkNGNW3xkWG1CR2mRAknV9NKNZgZ6FpDwIhAOGS6nqH21+wJWnmVSZ6M56HlONrUPCjuSGLigXHY2FS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@arthur5005%2fwarper-ember@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":367428},"ember":{"edition":"octane"},"exports":{".":{"types":"./declarations/index.d.ts","default":"./dist/index.js"},"./*":{"types":"./declarations/*.d.ts","default":"./dist/*.js"},"./*.css":"./dist/*.css","./addon-main.js":"./addon-main.cjs"},"gitHead":"761ede47e8032d0aab6ff1f9cfbca361b3b1b105","imports":{"#src/*":"./src/*"},"scripts":{"dev":"concurrently \"rollup --config --watch\" \"ember-tsc --declaration --project tsconfig.publish.json --watch --preserveWatchOutput\" \"vite dev\" --names \"build,types,serve\" --prefixColors auto","lint":"concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto","test":"vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0","build":"rollup --config","start":"vite dev","format":"prettier . --cache --write","lint:js":"eslint . --cache","prepack":"rollup --config","release":"release-it","lint:fix":"concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format","lint:hbs":"ember-template-lint . --no-error-on-unmatched-pattern","lint:types":"ember-tsc --noEmit","lint:format":"prettier . --cache --check","lint:js:fix":"eslint . --fix","lint:hbs:fix":"ember-template-lint . --fix --no-error-on-unmatched-pattern"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5c2bed30-f2ad-4a80-939e-70dfa562ccec"}},"repository":{"url":"git+https://github.com/arthur5005/warper-ember.git","type":"git"},"_npmVersion":"11.8.0","description":"A high-performance virtual list component for Ember using Warper from https://github.com/warper-org/warper","directories":{},"ember-addon":{"main":"addon-main.cjs","type":"addon","app-js":{"./components/virtualizer.js":"./dist/_app_/components/virtualizer.js","./components/warper-component.js":"./dist/_app_/components/warper-component.js"},"version":2},"_nodeVersion":"22.22.0","dependencies":{"reactiveweb":"^1.9.1","ember-modifier":"^4.2.2","decorator-transforms":"^2.2.2","@embroider/addon-shim":"^1.8.9"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.11.1","devDependencies":{"vite":"^7.1.9","qunit":"^2.24.1","eslint":"^9.17.0","rollup":"^4.22.5","testem":"^3.15.1","csstype":"^3.2.3","globals":"^16.1.0","prettier":"^3.4.2","qunit-dom":"^3.4.0","@eslint/js":"^9.17.0","release-it":"^19.2.4","typescript":"~5.8.3","@babel/core":"^7.25.2","ember-qunit":"^9.0.2","@types/qunit":"^2.19.12","concurrently":"^9.0.1","ember-source":"^6.7.0","@babel/runtime":"^7.25.6","@embroider/core":"^4.1.0","@embroider/vite":"^1.1.5","@glint/template":"^1.6.1","eslint-plugin-n":"^17.15.1","@glint/ember-tsc":"^1.0.3","ember-page-title":"^9.0.3","@embroider/compat":"^4.1.0","@embroider/macros":"^1.18.0","typescript-eslint":"^8.19.1","@glimmer/component":"^2.0.0","rollup-plugin-copy":"^3.5.0","@ember/app-tsconfig":"^1.0.0","@ember/test-helpers":"^5.2.1","@ember/test-waiters":"^4.1.1","ember-template-lint":"^7.9.0","eslint-plugin-ember":"^12.3.3","@babel/eslint-parser":"^7.25.1","@embroider/addon-dev":"^8.1.0","@rollup/plugin-babel":"^6.0.4","eslint-plugin-import":"^2.31.0","@glint/tsserver-plugin":"^2.0.0","eslint-config-prettier":"^10.1.5","@ember/library-tsconfig":"^1.0.0","ember-strict-application-resolver":"^0.1.0","@babel/plugin-transform-typescript":"^7.25.2","@release-it/conventional-changelog":"^10.0.4","prettier-plugin-ember-template-tag":"^2.0.4","babel-plugin-ember-template-compilation":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/warper-ember_0.1.0_1769805462882_0.40028256272449414","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@arthur5005/warper-ember","version":"0.2.0","packageManager":"pnpm@10.11.1","description":"A high-performance virtual list component for Ember using Warper from https://github.com/warper-org/warper","keywords":["warper-ember"],"repository":{"type":"git","url":"git+https://github.com/arthur5005/warper-ember.git"},"license":"MIT","author":"","imports":{"#src/*":"./src/*"},"exports":{".":{"types":"./declarations/index.d.ts","default":"./dist/index.js"},"./addon-main.js":"./addon-main.cjs","./*.css":"./dist/*.css","./*":{"types":"./declarations/*.d.ts","default":"./dist/*.js"}},"scripts":{"build":"rollup --config","dev":"concurrently \"rollup --config --watch\" \"ember-tsc --declaration --project tsconfig.publish.json --watch --preserveWatchOutput\" \"vite dev\" --names \"build,types,serve\" --prefixColors auto","format":"prettier . --cache --write","lint":"concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto","lint:fix":"concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format","lint:format":"prettier . --cache --check","lint:hbs":"ember-template-lint . --no-error-on-unmatched-pattern","lint:hbs:fix":"ember-template-lint . --fix --no-error-on-unmatched-pattern","lint:js":"eslint . --cache","lint:js:fix":"eslint . --fix","lint:types":"ember-tsc --noEmit","prepack":"rollup --config","start":"vite dev","test":"vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0","release":"release-it"},"dependencies":{"@embroider/addon-shim":"^1.8.9","decorator-transforms":"^2.2.2","ember-modifier":"^4.2.2","reactiveweb":"^1.9.1"},"devDependencies":{"@babel/core":"^7.25.2","@babel/eslint-parser":"^7.25.1","@babel/plugin-transform-typescript":"^7.25.2","@babel/runtime":"^7.25.6","@ember/app-tsconfig":"^1.0.0","@ember/library-tsconfig":"^1.0.0","@ember/test-helpers":"^5.2.1","@ember/test-waiters":"^4.1.1","@embroider/addon-dev":"^8.1.0","@embroider/compat":"^4.1.0","@embroider/core":"^4.1.0","@embroider/macros":"^1.18.0","@embroider/vite":"^1.1.5","@eslint/js":"^9.17.0","@glimmer/component":"^2.0.0","@glint/ember-tsc":"^1.0.3","@glint/template":"^1.6.1","@glint/tsserver-plugin":"^2.0.0","@release-it/conventional-changelog":"^10.0.4","@rollup/plugin-babel":"^6.0.4","@types/qunit":"^2.19.12","babel-plugin-ember-template-compilation":"^3.0.0","concurrently":"^9.0.1","csstype":"^3.2.3","ember-page-title":"^9.0.3","ember-qunit":"^9.0.2","ember-source":"^6.7.0","ember-strict-application-resolver":"^0.1.0","ember-template-lint":"^7.9.0","eslint":"^9.17.0","eslint-config-prettier":"^10.1.5","eslint-plugin-ember":"^12.3.3","eslint-plugin-import":"^2.31.0","eslint-plugin-n":"^17.15.1","globals":"^16.1.0","prettier":"^3.4.2","prettier-plugin-ember-template-tag":"^2.0.4","qunit":"^2.24.1","qunit-dom":"^3.4.0","release-it":"^19.2.4","rollup":"^4.22.5","rollup-plugin-copy":"^3.5.0","testem":"^3.15.1","typescript":"~5.8.3","typescript-eslint":"^8.19.1","vite":"^7.1.9"},"ember":{"edition":"octane"},"ember-addon":{"version":2,"type":"addon","main":"addon-main.cjs","app-js":{"./components/virtualizer.js":"./dist/_app_/components/virtualizer.js","./components/warper-component.js":"./dist/_app_/components/warper-component.js"}},"gitHead":"7eff65aa27c0b8b80c16dd2b24efb87d4b269596","_id":"@arthur5005/warper-ember@0.2.0","bugs":{"url":"https://github.com/arthur5005/warper-ember/issues"},"homepage":"https://github.com/arthur5005/warper-ember#readme","_nodeVersion":"22.22.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-C3UDQ4aPki9FuWTDppwwqf9+4OotaXgmq4TebU+LuAkR7KN96Hs9lyTTvzKuSIHVA5EepCU0ueTl/pHttDneCA==","shasum":"68b5e9cda259aa3e1f12a61e6cecc29d041b96d2","tarball":"https://registry.npmjs.org/@arthur5005/warper-ember/-/warper-ember-0.2.0.tgz","fileCount":28,"unpackedSize":366046,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@arthur5005%2fwarper-ember@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBPSxaHz2IsPyX02kbfMHzoMgSjCUNqWUaSvq9fW2KokAiBK+VfzXf4kk55nYNRTL7LINU7tOkSqt8KOIy304GhezA=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5c2bed30-f2ad-4a80-939e-70dfa562ccec"}},"directories":{},"maintainers":[{"name":"arthur.goldsmith","email":"arthur5005@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/warper-ember_0.2.0_1769808461614_0.3289097849747793"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-30T20:17:54.597Z","modified":"2026-01-30T21:27:42.061Z","0.1.0-alpha.1":"2026-01-30T20:17:54.837Z","0.1.0":"2026-01-30T20:37:43.065Z","0.2.0":"2026-01-30T21:27:41.762Z"},"bugs":{"url":"https://github.com/arthur5005/warper-ember/issues"},"license":"MIT","homepage":"https://github.com/arthur5005/warper-ember#readme","keywords":["warper-ember"],"repository":{"type":"git","url":"git+https://github.com/arthur5005/warper-ember.git"},"description":"A high-performance virtual list component for Ember using Warper from https://github.com/warper-org/warper","maintainers":[{"name":"arthur.goldsmith","email":"arthur5005@gmail.com"}],"readme":"# warper-ember\n\nAn Ember addon providing high-performance list virtualization powered by [Warper](https://github.com/warper-org/warper)'s Rust/WASM core.\n\n## Demo\n\n**[Live Demo](https://arthur5005.github.io/warper-ember/)** - Stress test with 1M+ rows at 120+ FPS\n\n> **Performance Tip:** For maximum performance, disable Ember Inspector or open the demo in an incognito/private browser window. Browser extensions can significantly impact rendering performance.\n\n> **Early Development Warning**: This is an experimental release with no tests. It is intended as a starting point for development work on replacing or updating the core of Ember's [vertical-collection](https://github.com/html-next/vertical-collection). The API reflects the upstream Warper API and is subject to change as development continues. Use at your own risk.\n\n## What is Warper?\n\nWarper is a virtualization library that efficiently renders massive datasets (10+ million items) while maintaining smooth 120+ FPS performance. Instead of rendering all items in the DOM, it intelligently displays only visible items.\n\nKey performance characteristics:\n- **O(1)** lookups for uniform-height items\n- **O(log n)** calculations for variable-height items via Fenwick tree\n- Zero-copy transfers between WASM and JavaScript via typed arrays\n- CSS containment and transforms for GPU acceleration\n\n## Installation\n\n```bash\npnpm add @arthur5005/warper-ember\n```\n\n## Requirements\n\n- **Ember 5.0+** (may work with 4.x but untested)\n- **GJS/GTS only** - This addon only supports strict mode templates (`.gjs`/`.gts` files). Classic `.hbs` templates are not supported.\n- **Embroider** - This is a v2 addon and requires an Embroider-compatible build pipeline.\n\n## Limitations\n\n**No automatic element measurement**: This library does not measure rendered elements to determine their actual heights. The `estimateSize` function you provide is used directly as the item size. If your items have variable heights that can only be known after rendering, you'll need to handle measurement yourself.\n\n**Initialization overhead**: The `estimateSize` callback is invoked for every item during initialization to build the internal data structures. For very large lists with complex size calculations, this can cause a noticeable delay on first render. Keep your `estimateSize` function fast—ideally returning a constant or doing simple arithmetic.\n\n## Usage\n\n### Basic Example\n\n```gts\nimport { WarperComponent } from '@arthur5005/warper-ember';\n\nfunction estimateSize(_item: number) {\n  return 50;\n}\n\nexport default <template>\n  <WarperComponent\n    @itemCount={{1000}}\n    @estimateSize={{estimateSize}}\n    @height={{600}}\n    @overscan={{3}}\n    as |index|\n  >\n    <div>Item {{index}}</div>\n  </WarperComponent>\n</template>\n```\n\n### With Data Array\n\n```gts\nimport Component from '@glimmer/component';\nimport { WarperComponent } from '@arthur5005/warper-ember';\n\ninterface Item {\n  id: number;\n  name: string;\n}\n\nexport default class MyList extends Component {\n  items: Item[] = Array.from({ length: 10000 }, (_, i) => ({\n    id: i,\n    name: `Item ${i}`,\n  }));\n\n  estimateSize = () => 40;\n\n  <template>\n    <WarperComponent\n      @items={{this.items}}\n      @estimateSize={{this.estimateSize}}\n      @height=\"100vh\"\n      @overscan={{5}}\n      as |item index|\n    >\n      <div class=\"row\">\n        {{item.name}} (index: {{index}})\n      </div>\n    </WarperComponent>\n  </template>\n}\n```\n\n### Custom Loading and Error States\n\n```gts\nimport Component from '@glimmer/component';\nimport { tracked } from '@glimmer/tracking';\nimport { WarperComponent } from '@arthur5005/warper-ember';\n\nexport default class MyList extends Component {\n  @tracked count = 1000;\n\n  estimateSize = () => 50;\n\n  <template>\n    <WarperComponent\n      @itemCount={{this.count}}\n      @estimateSize={{this.estimateSize}}\n      @height={{400}}\n    >\n      <:loading>\n        <div class=\"custom-loader\">Initializing WASM...</div>\n      </:loading>\n\n      <:error as |err|>\n        <div class=\"error-state\">Failed to load: {{err.message}}</div>\n      </:error>\n\n      <:default as |index|>\n        <div>Item {{index}}</div>\n      </:default>\n    </WarperComponent>\n  </template>\n}\n```\n\n## API\n\n### WarperComponent\n\nThe main component for rendering virtualized lists.\n\n| Argument | Type | Required | Description |\n|----------|------|----------|-------------|\n| `@itemCount` | `number` | * | Total number of items (use this OR `@items`) |\n| `@items` | `T[]` | * | Data array (use this OR `@itemCount`) |\n| `@estimateSize` | `(index: number) => number` | Yes | Function returning item height in pixels |\n| `@height` | `number \\| string` | No | Container height (default: `\"100%\"`) |\n| `@overscan` | `number` | No | Extra items to render above/below viewport |\n| `@horizontal` | `boolean` | No | Enable horizontal scrolling |\n\n**Yielded values:**\n- When using `@items`: yields `[item, index]`\n- When using `@itemCount`: yields `[index, index]` (index as both values)\n\n**Named blocks:**\n- `default` - Content for each visible item\n- `loading` - Custom loading state while WASM initializes\n- `error` - Custom error handling (receives error object)\n\n### Virtualizer (Low-level)\n\nFor advanced use cases, the `Virtualizer` component provides direct access to scroll methods and range data:\n\n```gts\nimport Component from '@glimmer/component';\nimport { htmlSafe } from '@ember/template';\nimport { on } from '@ember/modifier';\nimport { fn } from '@ember/helper';\nimport Virtualizer from '@arthur5005/warper-ember/components/virtualizer';\nimport type { VirtualRange } from '@arthur5005/warper-ember/components/virtualizer';\n\nexport default class CustomVirtualList extends Component {\n  estimateSize = () => 50;\n\n  containerStyle = (range: VirtualRange) =>\n    htmlSafe(`height:${range.totalHeight}px;position:relative;`);\n\n  rowStyle = (range: VirtualRange, i: number) => {\n    const offset = range.offsets[i] ?? 0;\n    const size = range.sizes[i] ?? 0;\n    return htmlSafe(\n      `position:absolute;top:0;left:0;width:100%;height:${size}px;transform:translateY(${offset}px);`\n    );\n  };\n\n  <template>\n    <Virtualizer\n      @itemCount={{1000}}\n      @estimateSize={{this.estimateSize}}\n      as |v|\n    >\n      <div\n        {{v.scrollElementRef}}\n        style=\"height:400px;overflow:auto;\"\n      >\n        <div style={{this.containerStyle v.range}}>\n          {{#each v.range.items as |index i|}}\n            <div style={{this.rowStyle v.range i}}>\n              Item {{index}}\n            </div>\n          {{/each}}\n        </div>\n      </div>\n      <button type=\"button\" {{on \"click\" (fn v.scrollToIndex 500)}}>\n        Jump to middle\n      </button>\n    </Virtualizer>\n  </template>\n}\n```\n\n## Implementation Notes\n\nThis port aims to match the API of the original Warper's `WarperComponent` and `useVirtualizer` hook. The hook is exposed as a yielded component pattern instead.\n\nKey implementation details:\n- Uses double RAF (requestAnimationFrame) for rendering to ensure paint happens before calculations\n- WASM binary and bindings are copied directly from the upstream project (no React-free dependency exists yet)\n- Automatically detects uniform vs variable item sizes and uses the optimal algorithm\n\n## License\n\nMIT\n","readmeFilename":"README.md"}