{"_id":"@alerix/m3-loading-indicator","_rev":"5-dd1333855dcc06b9cb2ab87d1ef4e393","name":"@alerix/m3-loading-indicator","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.0":{"name":"@alerix/m3-loading-indicator","version":"1.0.0","keywords":["material-design","material-3","loading-indicator","spinner","m3-expressive","react","svelte","vue","vanilla","shape-morph","android-port"],"author":{"name":"Alerix"},"license":"MIT","_id":"@alerix/m3-loading-indicator@1.0.0","maintainers":[{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"}],"homepage":"https://aler1x.github.io/m3-loading-indicator","dist":{"shasum":"4cc14b238ab58120bcf664726bc69e78e42f0a87","tarball":"https://registry.npmjs.org/@alerix/m3-loading-indicator/-/m3-loading-indicator-1.0.0.tgz","fileCount":21,"integrity":"sha512-5jI6r5++prV1f25Kqdo9uxs4ss/Cmu5igM4Tpp1gVPiAhGsnvO8T9GiITPugYc4AYhaXblrtopNTrjsnLd/waQ==","signatures":[{"sig":"MEUCIAcjfIJd7vLDSfhVQ4xiJVbi2dH26UzHigHKbFh5M1JqAiEAzbPCV+8ShRMlIXxWUDiyEDg+MvHhV8dwhRRb/Uc+YXM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":136294},"main":"./dist/core/index.cjs","type":"module","types":"./dist/core/index.d.ts","module":"./dist/core/index.js","svelte":"./dist/svelte/M3LoadingIndicator.svelte","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js","require":"./dist/vue.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./svelte":{"types":"./dist/svelte/M3LoadingIndicator.svelte.d.ts","svelte":"./dist/svelte/M3LoadingIndicator.svelte","default":"./dist/svelte/M3LoadingIndicator.svelte"}},"gitHead":"cb37473a9d221a230ecfe4381d2b51327d9103d1","scripts":{"dev":"tsup --watch","build":"tsup && shx cp -r src/svelte dist/svelte","pack:dry":"npm pack --dry-run","prepublishOnly":"npm run build","example:vanilla":"pnpm -C examples/vanilla dev"},"_npmUser":{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"},"repository":{"url":"","type":"git"},"_npmVersion":"11.2.0","description":"Material Design 3 Expressive Loading Indicator for React, Svelte 5, Vue 3, and vanilla JS. Faithfully ported from Android's material-components.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"shx":"^0.3.4","vue":"^3.5.0","tsup":"^8.0.0","react":"^19.0.0","svelte":"^5.0.0","react-dom":"^19.0.0","typescript":"^5.5.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"peerDependencies":{"vue":">=3.3.0","react":">=18.0.0","svelte":">=5.0.0","react-dom":">=18.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true},"svelte":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/m3-loading-indicator_1.0.0_1773539903956_0.21061264439091043","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@alerix/m3-loading-indicator","version":"1.0.1","keywords":["material-design","material-3","loading-indicator","spinner","m3-expressive","react","svelte","vue","vanilla","shape-morph","android-port"],"author":{"name":"Alerix"},"license":"MIT","_id":"@alerix/m3-loading-indicator@1.0.1","maintainers":[{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"}],"homepage":"https://aler1x.github.io/m3-loading-indicator","dist":{"shasum":"1c6356614c05867546469c913be1d39b39fbdd0e","tarball":"https://registry.npmjs.org/@alerix/m3-loading-indicator/-/m3-loading-indicator-1.0.1.tgz","fileCount":21,"integrity":"sha512-Cs9XjPh4nYniA/1Q1btcYyNtrFIKIKzYihV7W1l9fhhSbekjSKkhaE/XPPX2bKni86q4DD0QfNiWLp4w8NpCAw==","signatures":[{"sig":"MEUCIGbR8Qv6vhncf/pcXeJotRDdWr6zUTkzG1k73KoZ/auXAiEAvovZd+yDg1epA4cR+Fp2oyoQq/akypFMMtc3hDLjqu8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":146339},"main":"./dist/core/index.cjs","type":"module","types":"./dist/core/index.d.ts","module":"./dist/core/index.js","svelte":"./dist/svelte/M3LoadingIndicator.svelte","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js","require":"./dist/vue.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./svelte":{"types":"./dist/svelte/M3LoadingIndicator.svelte.d.ts","svelte":"./dist/svelte/M3LoadingIndicator.svelte","default":"./dist/svelte/M3LoadingIndicator.svelte"}},"gitHead":"28e72e0f1e6457d8139e9b30345bb5c8e092432d","scripts":{"dev":"tsup --watch","build":"tsup && shx cp -r src/svelte dist/svelte","pack:dry":"npm pack --dry-run","prepublishOnly":"npm run build","example:vanilla":"pnpm -C examples/vanilla dev"},"_npmUser":{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"},"repository":{"url":"","type":"git"},"_npmVersion":"11.2.0","description":"Material Design 3 Expressive Loading Indicator for React, Svelte 5, Vue 3, and vanilla JS. Faithfully ported from Android's material-components.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"shx":"^0.3.4","vue":"^3.5.0","tsup":"^8.0.0","react":"^19.0.0","svelte":"^5.0.0","react-dom":"^19.0.0","typescript":"^5.5.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"peerDependencies":{"vue":">=3.3.0","react":">=18.0.0","svelte":">=5.0.0","react-dom":">=18.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true},"svelte":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/m3-loading-indicator_1.0.1_1773540318793_0.4320091674637634","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@alerix/m3-loading-indicator","version":"1.0.2","keywords":["material-design","material-3","loading-indicator","spinner","m3-expressive","react","svelte","vue","vanilla","shape-morph","android-port"],"author":{"name":"Alerix"},"license":"MIT","_id":"@alerix/m3-loading-indicator@1.0.2","maintainers":[{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"}],"homepage":"https://aler1x.github.io/m3-loading-indicator","bugs":{"url":"https://github.com/aler1x/m3-loading-indicator/issues"},"dist":{"shasum":"0b5be36441daabadd9a7c6802c6aae736744ea53","tarball":"https://registry.npmjs.org/@alerix/m3-loading-indicator/-/m3-loading-indicator-1.0.2.tgz","fileCount":21,"integrity":"sha512-oEk+Lyzo05NzEgnk0tz8i7d30ZIvh6P+r3iH+TRgaRHE9DOKniNTvwwBaXMffJZ7KB7DjjQxeJz2gzC6iKqg3g==","signatures":[{"sig":"MEUCIQD4ZUV7CYJLQOO/CiAb20lkhFS+oSZstVj5it/MD9wM/gIgW10pH+PW0Frmc2Akr5yBD/D+M/L9A+LZESiEMuSDeTo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":146389},"main":"./dist/core/index.cjs","type":"module","types":"./dist/core/index.d.ts","module":"./dist/core/index.js","svelte":"./dist/svelte/M3LoadingIndicator.svelte","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js","require":"./dist/vue.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./svelte":{"types":"./dist/svelte/M3LoadingIndicator.svelte.d.ts","svelte":"./dist/svelte/M3LoadingIndicator.svelte","default":"./dist/svelte/M3LoadingIndicator.svelte"}},"gitHead":"bfe6bf637f041529a36650f58ccad12396a329a0","scripts":{"dev":"tsup --watch","build":"tsup && shx cp -r src/svelte dist/svelte","pack:dry":"npm pack --dry-run","prepublishOnly":"npm run build","example:vanilla":"pnpm -C examples/vanilla dev"},"_npmUser":{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"},"repository":{"url":"git+https://github.com/aler1x/m3-loading-indicator.git","type":"git"},"_npmVersion":"11.2.0","description":"Material Design 3 Expressive Loading Indicator for React, Svelte 5, Vue 3, and vanilla JS. Faithfully ported from Android's material-components.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"shx":"^0.3.4","vue":"^3.5.0","tsup":"^8.0.0","react":"^19.0.0","svelte":"^5.0.0","react-dom":"^19.0.0","typescript":"^5.5.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"peerDependencies":{"vue":">=3.3.0","react":">=18.0.0","svelte":">=5.0.0","react-dom":">=18.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true},"svelte":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/m3-loading-indicator_1.0.2_1773540509234_0.9465498607925711","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@alerix/m3-loading-indicator","version":"1.0.3","keywords":["material-design","material-3","loading-indicator","spinner","m3-expressive","react","svelte","vue","vanilla","shape-morph","android-port"],"author":{"name":"Alerix"},"license":"Apache-2.0","_id":"@alerix/m3-loading-indicator@1.0.3","maintainers":[{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"}],"homepage":"https://aler1x.github.io/m3-loading-indicator","bugs":{"url":"https://github.com/aler1x/m3-loading-indicator/issues"},"dist":{"shasum":"b36becc18daf3edabe172f89c70ae99a684d8103","tarball":"https://registry.npmjs.org/@alerix/m3-loading-indicator/-/m3-loading-indicator-1.0.3.tgz","fileCount":22,"integrity":"sha512-1jbBuxNL+7GuyVNG0+KUFiQMXZh0WgWVz33hP7B7KQEJSCaPMPHriA6FXFISxOIybkc0dyr2FmN4ksny8iZo5g==","signatures":[{"sig":"MEYCIQDOjsxd3lYqa7+hTK8SzDsRJjVsFkE7wJUmOfBZN6H1RQIhAPWUEQ5BQ8EnrwfTTWmsc/tcW2US+Yxfc+91BwDmmhUO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":147444},"main":"./dist/core/index.cjs","type":"module","types":"./dist/core/index.d.ts","module":"./dist/core/index.js","svelte":"./dist/svelte/M3LoadingIndicator.svelte","exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js","require":"./dist/vue.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./svelte":{"types":"./dist/svelte/M3LoadingIndicator.svelte.d.ts","svelte":"./dist/svelte/M3LoadingIndicator.svelte","default":"./dist/svelte/M3LoadingIndicator.svelte"}},"gitHead":"cd66e46ff0bb38f0e120e8e51198b3a5b6df4cf5","scripts":{"dev":"tsup --watch","build":"tsup && shx cp -r src/svelte dist/svelte","pack:dry":"npm pack --dry-run","prepublishOnly":"npm run build","example:vanilla":"pnpm -C examples/vanilla dev"},"_npmUser":{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"},"repository":{"url":"git+https://github.com/aler1x/m3-loading-indicator.git","type":"git"},"_npmVersion":"11.2.0","description":"Material Design 3 Expressive Loading Indicator for React, Svelte 5, Vue 3, and vanilla JS. Faithfully ported from Android's material-components.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"shx":"^0.3.4","vue":"^3.5.0","tsup":"^8.0.0","react":"^19.0.0","svelte":"^5.0.0","react-dom":"^19.0.0","typescript":"^5.5.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"peerDependencies":{"vue":">=3.3.0","react":">=18.0.0","svelte":">=5.0.0","react-dom":">=18.0.0"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true},"svelte":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/m3-loading-indicator_1.0.3_1773541259361_0.21886391607012734","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@alerix/m3-loading-indicator","version":"1.0.5","author":{"name":"Alerix"},"homepage":"https://aler1x.github.io/m3-loading-indicator","repository":{"type":"git","url":"git+https://github.com/aler1x/m3-loading-indicator.git"},"description":"Material Design 3 Expressive Loading Indicator for React, Svelte 5, Vue 3, and vanilla JS. Faithfully ported from Android's material-components.","type":"module","license":"Apache-2.0","sideEffects":false,"main":"./dist/core/index.cjs","module":"./dist/core/index.js","types":"./dist/core/index.d.ts","svelte":"./dist/svelte/M3LoadingIndicator.svelte","keywords":["material-design","material-3","loading-indicator","spinner","m3-expressive","react","svelte","vue","vanilla","shape-morph","android-port"],"exports":{".":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","require":"./dist/core/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js","require":"./dist/vue.cjs"},"./svelte":{"types":"./dist/svelte/M3LoadingIndicator.svelte.d.ts","svelte":"./dist/svelte/M3LoadingIndicator.svelte","default":"./dist/svelte/M3LoadingIndicator.svelte"}},"scripts":{"build":"tsup && shx cp -r src/svelte/. dist/svelte","dev":"tsup --watch","prepublishOnly":"npm run build","pack:dry":"npm pack --dry-run","example:vanilla":"pnpm -C examples/vanilla dev"},"devDependencies":{"@types/react":"^19.0.0","svelte":"^5.0.0","@types/react-dom":"^19.0.0","react":"^19.0.0","react-dom":"^19.0.0","shx":"^0.3.4","tsup":"^8.0.0","typescript":"^5.5.0","vue":"^3.5.0"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0","svelte":">=5.0.0","vue":">=3.3.0"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"svelte":{"optional":true},"vue":{"optional":true}},"_id":"@alerix/m3-loading-indicator@1.0.5","gitHead":"98e720b425fbf82990caeeb47601eded626953f8","bugs":{"url":"https://github.com/aler1x/m3-loading-indicator/issues"},"_nodeVersion":"22.14.0","_npmVersion":"11.2.0","dist":{"integrity":"sha512-BMoiNaeXZSsIG4HA+hP3igqnqYyzqXO4bCKeIuwLsGZflvvglES6pxqqhhOyh35Imhfl0K7CawjeYybnMXHoDw==","shasum":"7e1eecc3899b0d79541eb17a59a621d90d301277","tarball":"https://registry.npmjs.org/@alerix/m3-loading-indicator/-/m3-loading-indicator-1.0.5.tgz","fileCount":22,"unpackedSize":147450,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFu3V7dm7udH6oSqWFgq0KBiPPEM2PoJFzNwR++RTWEWAiEAxDIMGC8u9M8fNj87obGlVRZ6pnMpxtOVfGlkEalmQl8="}]},"_npmUser":{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"},"directories":{},"maintainers":[{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/m3-loading-indicator_1.0.5_1773678926605_0.4472384524348558"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-15T01:58:23.844Z","modified":"2026-03-16T16:35:26.871Z","1.0.0":"2026-03-15T01:58:24.103Z","1.0.1":"2026-03-15T02:05:18.958Z","1.0.2":"2026-03-15T02:08:29.386Z","1.0.3":"2026-03-15T02:20:59.520Z","1.0.5":"2026-03-16T16:35:26.758Z"},"bugs":{"url":"https://github.com/aler1x/m3-loading-indicator/issues"},"author":{"name":"Alerix"},"license":"Apache-2.0","homepage":"https://aler1x.github.io/m3-loading-indicator","keywords":["material-design","material-3","loading-indicator","spinner","m3-expressive","react","svelte","vue","vanilla","shape-morph","android-port"],"repository":{"type":"git","url":"git+https://github.com/aler1x/m3-loading-indicator.git"},"description":"Material Design 3 Expressive Loading Indicator for React, Svelte 5, Vue 3, and vanilla JS. Faithfully ported from Android's material-components.","maintainers":[{"name":"alerix","email":"alex.morozov.ak+npm@gmail.com"}],"readme":"# m3-loading-indicator\n\nMaterial Design 3 Expressive Loading Indicator for **React**, **Svelte 5**, **Vue 3**, and **vanilla JS**.\n\nFaithfully ported from Android's [material-components-android](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/loadingindicator) — uses the exact SVG path geometry from Material Design shape assets, spring physics animation (k=200, ζ=0.6), and the dual rotation system from `LoadingIndicatorAnimatorDelegate.java`.\n\n## Features\n\n- 7 morphing shapes: Soft Burst → Cookie 9 → Pentagon → Pill → Sunny → Cookie 4 → Oval\n- Spring-driven animation matching Android's `SpringForce`\n- Pixel-perfect shapes from official Material Design SVG assets\n- Canvas-based rendering with HiDPI support\n- Tree-shakeable — only import the framework you use\n- TypeScript-first with full type definitions\n\n## Install\n\n```bash\nnpm install @alerix/m3-loading-indicator\n```\n\n## Examples\n\nRun the vanilla JS example locally:\n\n```bash\npnpm install\npnpm build\npnpm example:vanilla\n```\n\n## React\n\n```tsx\nimport { M3LoadingIndicator } from \"@alerix/m3-loading-indicator/react\";\n\nfunction App() {\n  return (\n    <>\n      {/* Default — 48px, uses currentColor */}\n      <M3LoadingIndicator />\n\n      {/* Custom size and color */}\n      <M3LoadingIndicator size={64} color=\"#6750A4\" />\n\n      {/* Slow motion */}\n      <M3LoadingIndicator speed={0.5} />\n\n      {/* Paused */}\n      <M3LoadingIndicator paused />\n    </>\n  );\n}\n```\n\n## Svelte 5\n\n```svelte\n<script lang=\"ts\">\n  import M3LoadingIndicator from \"@alerix/m3-loading-indicator/svelte\";\n</script>\n\n<!-- Default -->\n<M3LoadingIndicator />\n\n<!-- Custom size and color -->\n<M3LoadingIndicator size={64} color=\"#6750A4\" />\n\n<!-- Slow motion -->\n<M3LoadingIndicator speed={0.5} />\n\n<!-- Paused -->\n<M3LoadingIndicator paused />\n```\n\n## Vue 3\n\n```vue\n<script setup>\nimport { M3LoadingIndicator } from \"@alerix/m3-loading-indicator/vue\";\n</script>\n\n<template>\n  <!-- Default -->\n  <M3LoadingIndicator />\n\n  <!-- Custom size and color -->\n  <M3LoadingIndicator :size=\"64\" color=\"#6750A4\" />\n\n  <!-- Slow motion -->\n  <M3LoadingIndicator :speed=\"0.5\" />\n\n  <!-- Paused -->\n  <M3LoadingIndicator paused />\n</template>\n```\n\n## Headless / Vanilla JS\n\nUse the core API directly for custom renderers:\n\n```ts\nimport {\n  M3Animator,\n  getMorphedShape,\n  drawIndicator,\n  setupCanvas,\n} from \"@alerix/m3-loading-indicator\";\n\nconst canvas = document.querySelector(\"canvas\")!;\nconst ctx = setupCanvas(canvas, 48);\nconst animator = new M3Animator();\n\nfunction loop(ts: number) {\n  animator.update(ts);\n  const shape = getMorphedShape(animator.morph);\n  drawIndicator(ctx, 48, shape, animator.rotation, {\n    color: \"#6750A4\",\n  });\n  requestAnimationFrame(loop);\n}\n\nrequestAnimationFrame(loop);\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `size` | `number` | `48` | CSS pixel size of the indicator |\n| `color` | `string` | `\"currentColor\"` | Fill color |\n| `sizeRatio` | `number` | `0.79` | Shape-to-container ratio (38dp in 48dp) |\n| `speed` | `number` | `1` | Animation speed multiplier |\n| `paused` | `boolean` | `false` | Pause the animation |\n| `contained` | `boolean` | `false` | Render with circular container background |\n| `containerColor` | `string` | `\"rgba(0,0,0,0.08)\"` | Container background color |\n\n### Contained variant\n\nThe contained variant draws a circular background behind the indicator, matching Android's `ContainedLoadingIndicator`:\n\n```tsx\n// React\n<M3LoadingIndicator contained />\n<M3LoadingIndicator contained containerColor=\"#E8DEF8\" color=\"#6750A4\" />\n```\n\n```svelte\n<!-- Svelte -->\n<M3LoadingIndicator contained />\n<M3LoadingIndicator contained containerColor=\"#E8DEF8\" color=\"#6750A4\" />\n```\n\n```vue\n<!-- Vue -->\n<M3LoadingIndicator contained />\n<M3LoadingIndicator contained container-color=\"#E8DEF8\" color=\"#6750A4\" />\n```\n\n## Architecture\n\n```\nm3-loading-indicator\n├── core/         # Framework-agnostic engine\n│   ├── spring    # Spring physics (matches Android SpringForce)\n│   ├── shapes    # SVG path parsing + 7 Material shapes\n│   ├── animator  # Animation controller (from AnimatorDelegate.java)\n│   └── renderer  # Canvas drawing + HiDPI setup\n├── react         # React component (useEffect + rAF)\n├── svelte        # Svelte 5 component ($effect + rAF)\n└── vue           # Vue 3 component (defineComponent + rAF)\n```\n\n## Animation System\n\nPorted from `LoadingIndicatorAnimatorDelegate.java`:\n\n- **Linear timer**: 650ms per shape cycle, infinite repeat\n- **Spring morph**: `SpringForce(stiffness=200, dampingRatio=0.6)` drives shape transitions — creates the organic overshoot/settle feel\n- **Rotation**: `(50° + 90°) × base + 50° × timeFraction + 90° × springFraction` — constant rotation + spring-driven extra rotation per cycle\n\n## Shapes\n\nThe 7 shapes use exact SVG path data from Material Design asset files, parsed at init time into 180-point arrays for smooth morphing:\n\n1. **Soft burst** — 8-pointed soft star\n2. **Cookie 9** — 9-sided scalloped shape\n3. **Pentagon** — heavily rounded 5-sided polygon\n4. **Pill** — rotated rounded diamond\n5. **Sunny** — 12-faceted rounded polygon\n6. **Cookie 4** — 4-sided scalloped shape\n7. **Oval** — horizontal ellipse\n\n## License\n\nApache 2.0 — see [LICENSE](./LICENSE) and [NOTICE](./NOTICE).\n\nPorted from [material-components-android](https://github.com/material-components/material-components-android) (Apache 2.0, Copyright The Android Open Source Project) with SVG shape data from [Material Design](https://m3.material.io/components/loading-indicator) (Apache 2.0, Copyright Google LLC).\n","readmeFilename":"README.md"}