[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: test\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    env:\n      # Opt actions/checkout, actions/setup-node, etc. onto Node 24 now.\n      # Becomes the default on 2026-06-02; this silences the deprecation\n      # warning until then.\n      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n    strategy:\n      fail-fast: false\n      matrix:\n        node-version: [22.x, 24.x]\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: npm\n\n      - run: npm ci\n\n      - run: npm run typecheck\n\n      - run: npm test\n\n      - run: npm run build\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nnpm-debug.log\n# Demo debug screenshots — regenerated as needed.\ndemo/*.png\n# Bun build output for the demo (deployed to Vercel).\ndist-demo/\n# Keep any npm auth tokens out of the tree.\n.npmrc"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Viktor Sarström\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# tiny-slider 2.0 for Vue\n\nWrapper for Tiny slider for all purposes by [ganlanyuan](https://github.com/ganlanyuan/tiny-slider) in Vue. [Live demo →](https://vue-tiny-slider-blush.vercel.app/)\n\n[![version](https://img.shields.io/npm/v/vue-tiny-slider.svg)](https://www.npmjs.com/package/vue-tiny-slider) [![downloads](https://img.shields.io/npm/dt/vue-tiny-slider.svg)](https://www.npmjs.com/package/vue-tiny-slider) [![downloads per week](https://img.shields.io/npm/dw/vue-tiny-slider.svg)](https://www.npmjs.com/package/vue-tiny-slider)\n\n## Table of Contents\n  * [Compatibility](#compatibility)\n  * [Install](#install)\n  * [Use](#use)\n  * [Styling](#styling)\n  * [Options](#options)\n  * [Methods](#methods)\n      * [How to use the methods](#how-to-use-the-methods)\n  * [Nuxt 3 SSR](#nuxt-3-ssr)\n  * [Todos](#todo)\n  * [Collaborators](#collaborators)\n  * [License](#license)\n  * [Cheerios &lt;3](#cheerios-3)\n\n## Compatibility\n\n| `vue-tiny-slider` | Vue      |\n| ----------------- | -------- |\n| `^1.0.0`          | Vue 3.x  |\n| `^0.1.x`          | Vue 2.x  |\n\nIf you're on Vue 2, pin to `vue-tiny-slider@^0.1`.\n\n## Install\n\n`npm install vue-tiny-slider`\n\n## Use\n\n**Globally (Vue 3):**\n\n````javascript\nimport { createApp } from 'vue';\nimport VueTinySlider from 'vue-tiny-slider';\nimport App from './App.vue';\n\ncreateApp(App)\n  .component('tiny-slider', VueTinySlider)\n  .mount('#app');\n````\n\n**Or locally inside a single component:**\n\n````javascript\nimport VueTinySlider from 'vue-tiny-slider';\n\nexport default {\n  components: { 'tiny-slider': VueTinySlider }\n}\n````\n\n````html\n<tiny-slider :mouse-drag=\"true\" :loop=\"false\" items=\"2\" gutter=\"20\">\n  <div>Slider item #1</div>\n  <div>Slider item #2</div>\n  <div>Slider item #3</div>\n  <div>Slider item #4</div>\n  <div>Slider item #5</div>\n  <div>Slider item #6</div>\n</tiny-slider>\n````\n\n## Styling\n\nSCSS\n````scss\n@import 'tiny-slider/src/tiny-slider';\n````\n\nCDN\n````html\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.1/tiny-slider.css\">\n````\n\n## Options\n\n````\nauto-init\nitems\nmode\ngutter\nedge-padding\nfixed-width\nslide-by\ncontrols\ncontrols-text\ncontrols-container\nnav\nnav-container\narrow-keys\nspeed\nautoplay\nautoplay-timeout\nautoplay-direction\nautoplay-text\nautoplay-hover-pause\nautoplay-button\nautoplay-button-output\nautoplay-reset-on-visibility\nanimate-in\nanimate-out\nanimate-normal\nanimate-delay\nloop\nrewind\nauto-height\nresponsive\nlazyload\ntouch\nmouse-drag\nnested\nfreezable\ndisable\non-init\ncenter\nlazy-load-selector\nprevent-action-when-running\nprevent-scroll-on-touch\nnav-options\nauto-width\n````\n\nFor more detailed information about the options, see the [Tiny-slider documentation (Options)](https://github.com/ganlanyuan/tiny-slider#options).\n\n## Methods\n\n````getInfo````\n````goTo````\n````destroy````\n\n### How to use the methods\n\nTo be able to use the methods, you need to use ref on the component. Ref is used to register a reference to an element or a child component.\n\n```\n<vue-tiny-slider ref=\"tinySlider\"></vue-tiny-slider>\n```\n\n```\nimport VueTinySlider from 'vue-tiny-slider';\n\nexport default {\n  ...,\n    methods: {\n        getInfo: function(event) {\n             this.$refs.tinySlider.slider.getInfo();\n        }\n     }\n}\n```\n\nFor more detailed information about the methods, see the [Tiny-slider documentation (Methods)](https://github.com/ganlanyuan/tiny-slider#methods).\n\n## Nuxt 3 SSR\n\nTiny-slider touches the DOM, so the component must only render on the client.\n\n1. `npm install vue-tiny-slider`\n\n2. Create `plugins/vue-tiny-slider.client.js` (the `.client` suffix makes it client-only):\n\n```js\nimport VueTinySlider from 'vue-tiny-slider';\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.vueApp.component('VueTinySlider', VueTinySlider);\n});\n```\n\n3. Wrap the slider in `<ClientOnly>` where you use it:\n\n```html\n<ClientOnly>\n  <vue-tiny-slider v-bind=\"tinySliderOptions\">\n    <div>#1</div>\n    <div>#2</div>\n    <div>#3</div>\n  </vue-tiny-slider>\n</ClientOnly>\n```\n\n\n## Todo\n* ~~Add demo link from a fiddle or something similar~~\n* Better handling of the responsive-settings\n* Add Custom Events\n* ~~Add Methods~~\n\n## Collaborators\n* [Morgan Eklund](https://github.com/rymdkapten)\n* [Viktor Sarström](https://github.com/viktorlarsson)\n\n## License\n\nThis project is available under the MIT license.\n\n## Cheerios <3\n\n* Fixed broken demo link, @VitorLuizC\n* Moved tiny-slider from devDependencies to dependencies, @TitanFighter\n* Added nav position to props, @Irsanarisandy\n* Got it to work with NuxtJS SSR, @ilbonte\n"
  },
  {
    "path": "demo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"UTF-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<title>vue-tiny-slider — local demo</title>\n\t<link rel=\"stylesheet\" href=\"../node_modules/tiny-slider/dist/tiny-slider.css\" />\n\t<style>\n\t\tbody { margin: 0; background: #fafafa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #222; }\n\t\t.wrapper { max-width: 720px; margin: 2rem auto; padding: 0 1rem; }\n\t\th1 { font-size: 1.4rem; margin-bottom: .25rem; }\n\t\t.lede { color: #666; margin-top: 0; }\n\t\t.slide {\n\t\t\theight: 200px;\n\t\t\tdisplay: grid;\n\t\t\tplace-items: center;\n\t\t\tbackground: linear-gradient(135deg, #667eea, #764ba2);\n\t\t\tcolor: white;\n\t\t\tfont-size: 2rem;\n\t\t\tborder-radius: 8px;\n\t\t\tuser-select: none;\n\t\t}\n\t\t.slide:nth-child(2n) { background: linear-gradient(135deg, #f093fb, #f5576c); }\n\t\t.slide:nth-child(3n) { background: linear-gradient(135deg, #4facfe, #00f2fe); }\n\t\t/* Breathing room between slider and controls/nav placed below it. */\n\t\t.tns-controls, .tns-nav { margin-top: 1rem; }\n\t\t.tns-controls button {\n\t\t\tpadding: .35rem .8rem;\n\t\t\tmargin-right: .25rem;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tbackground: white;\n\t\t\tborder-radius: 4px;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.tns-controls button[disabled] { opacity: .4; cursor: default; }\n\t\t.tns-nav { text-align: center; }\n\t\t.tns-nav button {\n\t\t\twidth: 10px; height: 10px;\n\t\t\tborder-radius: 50%;\n\t\t\tborder: none;\n\t\t\tbackground: #ccc;\n\t\t\tmargin: 0 4px;\n\t\t\tpadding: 0;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t.tns-nav button.tns-nav-active { background: #667eea; }\n\t</style>\n</head>\n<body>\n\t<div id=\"app\"></div>\n\t<script type=\"module\" src=\"./main.ts\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "demo/main.ts",
    "content": "import { createApp, h } from 'vue';\n// Pull from local source so the demo always exercises the working tree.\nimport VueTinySlider from '../src/index.js';\n\ncreateApp({\n\tcomponents: { VueTinySlider },\n\trender() {\n\t\treturn h('div', { class: 'wrapper' }, [\n\t\t\th('h1', 'vue-tiny-slider — local demo'),\n\t\t\th('p', { class: 'lede' }, 'Drag, swipe, or click the controls.'),\n\t\t\th(\n\t\t\t\tVueTinySlider,\n\t\t\t\t{\n\t\t\t\t\tmouseDrag: true,\n\t\t\t\t\tloop: false,\n\t\t\t\t\titems: '2',\n\t\t\t\t\tgutter: 20,\n\t\t\t\t\tcontrolsPosition: 'bottom',\n\t\t\t\t\tnavPosition: 'bottom'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tdefault: () => [\n\t\t\t\t\t\th('div', { class: 'slide' }, 'Slide 1'),\n\t\t\t\t\t\th('div', { class: 'slide' }, 'Slide 2'),\n\t\t\t\t\t\th('div', { class: 'slide' }, 'Slide 3'),\n\t\t\t\t\t\th('div', { class: 'slide' }, 'Slide 4'),\n\t\t\t\t\t\th('div', { class: 'slide' }, 'Slide 5'),\n\t\t\t\t\t\th('div', { class: 'slide' }, 'Slide 6')\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t)\n\t\t]);\n\t}\n}).mount('#app');\n"
  },
  {
    "path": "dist/index.js",
    "content": "(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t(require(`vue`)):typeof define==`function`&&define.amd?define([`vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,e[`vue-tiny-slider`]=t(e.Vue))})(this,function(e){var t=typeof Node<`u`?Node:Object,n={eventsList:[`indexChanged`,`transitionStart`,`transitionEnd`,`newBreakpointStart`,`newBreakpointEnd`,`touchStart`,`touchMove`,`touchEnd`,`dragStart`,`dragMove`,`dragEnd`],props:{mode:{type:[String],default:`carousel`},autoInit:{type:[Boolean],default:!0},axis:{type:[String],validator:e=>e===`horizontal`||e===`vertical`},items:{type:[String,Number],default:1},gutter:{type:[String,Number],default:0},edgePadding:{type:[String,Number],default:0},fixedWidth:{type:[String,Boolean,Number],default:!1},viewportMax:{type:[String,Boolean,Number],default:!1},swipeAngle:{type:[Boolean,Number],default:15},slideBy:{type:[String,Number],default:1},controls:{type:[String,Boolean],default:!0},controlsPosition:{type:[String],validator:e=>e===`top`||e===`bottom`,default:`top`},controlsText:{type:[Array],default:()=>[`prev`,`next`]},controlsContainer:{type:[Boolean,t,String],default:!1},prevButton:{type:[t,String,Boolean],default:!1},nextButton:{type:[t,String,Boolean],default:!1},nav:{type:[Boolean],default:!0},navPosition:{type:[String],default:`top`},navContainer:{type:[Boolean,t,String],default:!1},navAsThumbnails:{type:[Boolean],default:!1},arrowKeys:{type:[Boolean],default:!1},speed:{type:[String,Number],default:300},autoplay:{type:[Boolean],default:!1},autoplayTimeout:{type:[Number],default:5e3},autoplayDirection:{type:[String],default:`forward`,validator:e=>e===`forward`||e===`backward`},autoplayText:{type:[Array],default:()=>[`start`,`stop`]},autoplayHoverPause:{type:[Boolean],default:!1},autoplayButton:{type:[Boolean,t,String],default:!1},autoplayButtonOutput:{type:[Boolean],default:!0},autoplayResetOnVisibility:{type:[Boolean],default:!0},animateIn:{type:[String],default:`tns-fadeIn`},animateOut:{type:[String],default:`tns-fadeOut`},animateNormal:{type:[String],default:`tns-normal`},animateDelay:{type:[String,Number,Boolean],default:!1},loop:{type:[Boolean],default:!0},rewind:{type:[Boolean],default:!1},autoHeight:{type:[Boolean],default:!1},responsive:{type:[Boolean,Object],default:!1},lazyload:{type:[Boolean],default:!1},touch:{type:[Boolean],default:!0},mouseDrag:{type:[Boolean],default:!1},nested:{type:[String,Boolean],default:!1,validator:e=>e===`inner`||e===`outer`||e===!1},freezable:{type:[Boolean],default:!0},disable:{type:[Boolean],default:!1},startIndex:{type:[Number],default:0},onInit:{type:[Function,Boolean],default:!1},center:{type:Boolean,default:!1},lazyLoadSelector:{type:String,default:`.tns-lazy-img`},preventActionWhenRunning:{type:Boolean,default:!1},autoWidth:{type:Boolean,default:!1},preventScrollOnTouch:{type:[String,Boolean],default:!1,validator:e=>e===`auto`||e===`force`||e===!1},useLocalStorage:{type:[Boolean],default:!0}},mounted:function(){this.autoInit&&this.init()},beforeUnmount:function(){this.slider&&this.slider.destroy()},methods:{$_vueTinySlider_subscribeTo(e){this.slider.events.on(e,t=>{this.$emit(e,t)})},$_vueTinySlider_subscribeToAll(){this.$options.eventsList.forEach(this.$_vueTinySlider_subscribeTo)},goTo:function(e){this.slider.goTo(e)},rebuild:function(){this.slider=this.slider.rebuild(),this.$emit(`rebuild`)},getInfo:function(){this.$emit(`getInfo`,this.slider.getInfo(),this.slider)},destroy:function(){this.slider.destroy()},init:async function(){var{tns:e}=await import(`tiny-slider/dist/tiny-slider.js`),t={container:this.$el,axis:this.axis,items:parseInt(this.items),mode:this.mode,gutter:this.gutter,edgePadding:this.edgePadding,fixedWidth:this.fixedWidth?parseInt(this.fixedWidth,10):this.fixedWidth,viewportMax:this.viewportMax,slideBy:this.slideBy,controls:this.controls,controlsPosition:this.controlsPosition,controlsText:this.controlsText,controlsContainer:this.controlsContainer,prevButton:this.prevButton,nextButton:this.nextButton,nav:this.nav,navPosition:this.navPosition,navContainer:this.navContainer,navAsThumbnails:this.navAsThumbnails,arrowKeys:this.arrowKeys,speed:this.speed,autoplay:this.autoplay,autoplayTimeout:this.autoplayTimeout,autoplayDirection:this.autoplayDirection,autoplayText:this.autoplayText,autoplayHoverPause:this.autoplayHoverPause,autoplayButton:this.autoplayButton,autoplayButtonOutput:this.autoplayButtonOutput,autoplayResetOnVisibility:this.autoplayResetOnVisibility,animateIn:this.animateIn,animateOut:this.animateOut,animateNormal:this.animateNormal,animateDelay:this.animateDelay,loop:this.loop,rewind:this.rewind,autoHeight:this.autoHeight,responsive:this.responsive,lazyload:this.lazyload,touch:this.touch,mouseDrag:this.mouseDrag,nested:this.nested,freezable:this.freezable,disable:this.disable,onInit:this.onInit,swipeAngle:this.swipeAngle,startIndex:this.startIndex,center:this.center,lazyLoadSelector:this.lazyLoadSelector,preventActionWhenRunning:this.preventActionWhenRunning,preventScrollOnTouch:this.preventScrollOnTouch,autoWidth:this.autoWidth,useLocalStorage:this.useLocalStorage};r(t),this.slider=e(t),this.$emit(`init`),this.$_vueTinySlider_subscribeToAll()}},render:function(){return(0,e.h)(`div`,this.$slots.default?this.$slots.default():[])}};function r(e){for(var t in e)e.hasOwnProperty(t)&&e[t]===void 0&&delete e[t]}return n});\n//# sourceMappingURL=index.js.map"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vue-tiny-slider\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Vanilla javascript slider for all purposes created by ganlanyuan in Vue.\",\n  \"main\": \"./dist/index.js\",\n  \"types\": \"./src/index.d.ts\",\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"watch\": \"vite build --watch\",\n    \"build\": \"vite build\",\n    \"test\": \"vitest run\",\n    \"typecheck\": \"tsc\",\n    \"demo\": \"bun --hot demo/index.html\",\n    \"demo:build\": \"bun build demo/index.html --outdir=dist-demo --minify\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/viktorlarsson/vue-tiny-slider.git\"\n  },\n  \"keywords\": [\n    \"javascript\",\n    \"vue\",\n    \"tiny-slider\",\n    \"slider\",\n    \"carousel\"\n  ],\n  \"author\": \"Viktor Sarström & Morgan Eklund\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/viktorlarsson/vue-tiny-slider/issues\"\n  },\n  \"homepage\": \"https://github.com/viktorlarsson/vue-tiny-slider#readme\",\n  \"dependencies\": {\n    \"tiny-slider\": \"^2.9.4\"\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-vue\": \"^6.0.6\",\n    \"@vue/test-utils\": \"^2.4.6\",\n    \"happy-dom\": \"^20.8.9\",\n    \"typescript\": \"^6.0.2\",\n    \"vite\": \"^8.0.8\",\n    \"vitest\": \"^4.1.4\",\n    \"vue\": \"^3.5.32\"\n  }\n}\n"
  },
  {
    "path": "src/index.d.ts",
    "content": "import type { DefineComponent } from 'vue';\nimport type {\n\tTinySliderSettings,\n\tTinySliderInstance,\n\tTinySliderInfo,\n\tSilderEvent\n} from 'tiny-slider';\n\nexport type {\n\tTinySliderSettings,\n\tTinySliderInstance,\n\tTinySliderInfo,\n\tSilderEvent\n};\n\n/**\n * Props accepted by the `<VueTinySlider>` component.\n *\n * Inherits every tiny-slider option (except `container`, which the wrapper\n * sets to the component's root element) and adds a handful of wrapper-\n * specific props.\n */\nexport interface VueTinySliderProps\n\textends Partial<Omit<TinySliderSettings, 'container'>> {\n\t/**\n\t * Automatically call `init()` in the mounted hook.\n\t * Set to `false` to call `init()` manually via `$refs`.\n\t * @default true\n\t */\n\tautoInit?: boolean;\n\t/**\n\t * Position of the prev/next controls relative to the slider.\n\t * @default \"top\"\n\t */\n\tcontrolsPosition?: 'top' | 'bottom';\n\t/**\n\t * Maximum viewport width (in px) considered for responsive breakpoints.\n\t * @default false\n\t */\n\tviewportMax?: number | string | boolean;\n}\n\n/**\n * The underlying tiny-slider instance and wrapper methods exposed on the\n * component instance (available through `$refs.yourRef`).\n */\nexport interface VueTinySliderInstance {\n\t/** The underlying tiny-slider instance once `init()` has resolved. */\n\tslider: TinySliderInstance | null;\n\t/** Initialize (or re-initialize) the slider. Returns a Promise because\n\t *  tiny-slider is lazy-loaded to keep the module SSR-safe. */\n\tinit(): Promise<void>;\n\t/** Go to a specific slide by index or keyword. */\n\tgoTo(target: number | 'next' | 'prev' | 'first' | 'last'): void;\n\t/** Rebuild the slider. Emits `rebuild`. */\n\trebuild(): void;\n\t/** Emits `getInfo` with the current slider state and instance. */\n\tgetInfo(): void;\n\t/** Destroy the underlying tiny-slider instance. */\n\tdestroy(): void;\n}\n\ndeclare const VueTinySlider: DefineComponent<VueTinySliderProps>;\n\nexport default VueTinySlider;\n"
  },
  {
    "path": "src/index.js",
    "content": "import { h } from 'vue';\n\n// `Node` is a browser DOM global; fall back to Object on the server so that\n// defining the component doesn't throw during SSR module evaluation.\nvar NodeType = typeof Node !== 'undefined' ? Node : Object;\n\nvar VueTinySlider = {\n\teventsList: [\n\t\t'indexChanged',\n\t\t'transitionStart',\n\t\t'transitionEnd',\n\t\t'newBreakpointStart',\n\t\t'newBreakpointEnd',\n\t\t'touchStart',\n\t\t'touchMove',\n\t\t'touchEnd',\n\t\t'dragStart',\n\t\t'dragMove',\n\t\t'dragEnd'\n\t],\n\tprops: {\n\t\tmode: {\n\t\t\ttype: [String],\n\t\t\tdefault: 'carousel'\n\t\t},\n\t\tautoInit: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\taxis: {\n\t\t\ttype: [String],\n\t\t\tvalidator: value => {\n\t\t\t\treturn value === 'horizontal' || value === 'vertical';\n\t\t\t}\n\t\t},\n\t\titems: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: 1\n\t\t},\n\t\tgutter: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: 0\n\t\t},\n\t\tedgePadding: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: 0\n\t\t},\n\t\tfixedWidth: {\n\t\t\ttype: [String, Boolean, Number],\n\t\t\tdefault: false\n\t\t},\n\t\tviewportMax: {\n\t\t\ttype: [String, Boolean, Number],\n\t\t\tdefault: false\n\t\t},\n\t\tswipeAngle: {\n\t\t\ttype: [Boolean, Number],\n\t\t\tdefault: 15\n\t\t},\n\t\tslideBy: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: 1\n\t\t},\n\t\tcontrols: {\n\t\t\ttype: [String, Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\tcontrolsPosition: {\n\t\t\ttype: [String],\n\t\t\tvalidator: value => {\n\t\t\t\treturn value === 'top' || value === 'bottom';\n\t\t\t},\n\t\t\tdefault: 'top'\n\t\t},\n\t\tcontrolsText: {\n\t\t\ttype: [Array],\n\t\t\tdefault: () => ['prev', 'next']\n\t\t},\n\t\tcontrolsContainer: {\n\t\t\ttype: [Boolean, NodeType, String],\n\t\t\tdefault: false\n\t\t},\n\t\tprevButton: {\n\t\t\ttype: [NodeType, String, Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tnextButton: {\n\t\t\ttype: [NodeType, String, Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tnav: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\tnavPosition: {\n\t\t\ttype: [String],\n\t\t\tdefault: \"top\"\n\t\t},\n\t\tnavContainer: {\n\t\t\ttype: [Boolean, NodeType, String],\n\t\t\tdefault: false\n\t\t},\n\t\tnavAsThumbnails: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tarrowKeys: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tspeed: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: 300\n\t\t},\n\t\tautoplay: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tautoplayTimeout: {\n\t\t\ttype: [Number],\n\t\t\tdefault: 5000\n\t\t},\n\t\tautoplayDirection: {\n\t\t\ttype: [String],\n\t\t\tdefault: 'forward',\n\t\t\tvalidator: value => {\n\t\t\t\treturn value === 'forward' || value === 'backward';\n\t\t\t}\n\t\t},\n\t\tautoplayText: {\n\t\t\ttype: [Array],\n\t\t\tdefault: () => ['start', 'stop']\n\t\t},\n\t\tautoplayHoverPause: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tautoplayButton: {\n\t\t\ttype: [Boolean, NodeType, String],\n\t\t\tdefault: false,\n\t\t},\n\t\tautoplayButtonOutput: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\tautoplayResetOnVisibility: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true,\n\t\t},\n\t\tanimateIn: {\n\t\t\ttype: [String],\n\t\t\tdefault: 'tns-fadeIn'\n\t\t},\n\t\tanimateOut: {\n\t\t\ttype: [String],\n\t\t\tdefault: 'tns-fadeOut'\n\t\t},\n\t\tanimateNormal: {\n\t\t\ttype: [String],\n\t\t\tdefault: 'tns-normal'\n\t\t},\n\t\tanimateDelay: {\n\t\t\ttype: [String, Number, Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tloop: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\trewind: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tautoHeight: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tresponsive: {\n\t\t\ttype: [Boolean, Object],\n\t\t\tdefault: false\n\t\t},\n\t\tlazyload: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\ttouch: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\tmouseDrag: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tnested: {\n\t\t\ttype: [String, Boolean],\n\t\t\tdefault: false,\n\t\t\tvalidator: value => {\n\t\t\t\treturn value === 'inner' || value === 'outer' || value === false;\n\t\t\t}\n\t\t},\n\t\tfreezable: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t},\n\t\tdisable: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tstartIndex: {\n\t\t\ttype: [Number],\n\t\t\tdefault: 0\n\t\t},\n\t\tonInit: {\n\t\t\ttype: [Function, Boolean],\n\t\t\tdefault: false\n\t\t},\n\t\tcenter: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\tlazyLoadSelector: {\n\t\t\ttype: String,\n\t\t\tdefault: '.tns-lazy-img'\n\t\t},\n\t\tpreventActionWhenRunning: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\tautoWidth: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false\n\t\t},\n\t\tpreventScrollOnTouch: {\n\t\t\ttype: [String, Boolean],\n\t\t\tdefault: false,\n\t\t\tvalidator: value => {\n\t\t\t\treturn value === 'auto' || value === 'force' || value === false;\n\t\t\t}\n\t\t},\n\t\tuseLocalStorage: {\n\t\t\ttype: [Boolean],\n\t\t\tdefault: true\n\t\t}\n\t},\n\tmounted: function () {\n\t\tif(this.autoInit) {\n\t\t\t// init() is async (lazy-loads tiny-slider); swallow the returned\n\t\t\t// promise — consumers who need ready-state should use @init event.\n\t\t\tthis.init();\n\t\t}\n\t},\n\tbeforeUnmount: function() {\n\t\tif(this.slider) {\n\t\t\tthis.slider.destroy();\n\t\t}\n\t},\n\tmethods: {\n\t\t$_vueTinySlider_subscribeTo (eventName) {\n\t\t\tthis.slider.events.on(eventName, (info) => {\n\t\t\t\tthis.$emit(eventName, info);\n\t\t\t});\n\t\t},\n\t\t$_vueTinySlider_subscribeToAll () {\n\t\t\tthis.$options.eventsList.forEach(this.$_vueTinySlider_subscribeTo)\n\t\t},\n\t\tgoTo: function(value) {\n\t\t\tthis.slider.goTo(value);\n\t\t},\n\t\trebuild: function() {\n\t\t\tthis.slider = this.slider.rebuild();\n\t\t\tthis.$emit('rebuild');\n\t\t},\n\t\tgetInfo: function() {\n\t\t\tthis.$emit('getInfo', this.slider.getInfo(), this.slider);\n\t\t},\n\t\tdestroy: function() {\n\t\t\tthis.slider.destroy();\n\t\t},\n\t\tinit: async function() {\n\t\t\t// Lazy-import tiny-slider so the module's top-level `document` /\n\t\t\t// `window` access never runs during SSR (mounted only fires on the\n\t\t\t// client, so this import only happens client-side).\n\t\t\t//\n\t\t\t// We import the pre-built `dist/tiny-slider.js` (single-file CJS\n\t\t\t// bundle) rather than the raw `src/tiny-slider`. The ESM source\n\t\t\t// exports an unbound `requestAnimationFrame` reference; modern\n\t\t\t// bundlers turn the import into namespace-object access\n\t\t\t// (`mod.raf(...)`) which sets `this = mod` and trips an\n\t\t\t// \"Illegal invocation\" inside the browser API on every pan/drag.\n\t\t\t// The CJS bundle keeps `raf(...)` as a bare call, so it works.\n\t\t\t// The dist path is explicit because some bundlers (Bun) prefer\n\t\t\t// the ESM `src/` even when `main` points at the dist bundle.\n\t\t\tvar { tns } = await import('tiny-slider/dist/tiny-slider.js');\n\n\t\t\tvar settings = {\n\t\t\t\tcontainer: this.$el,\n\t\t\t\taxis: this.axis,\n\t\t\t\titems: parseInt(this.items),\n\t\t\t\tmode: this.mode,\n\t\t\t\tgutter: this.gutter,\n\t\t\t\tedgePadding: this.edgePadding,\n\t\t\t\tfixedWidth: !this.fixedWidth ? this.fixedWidth : parseInt(this.fixedWidth, 10),\n\t\t\t\tviewportMax: this.viewportMax,\n\t\t\t\tslideBy: this.slideBy,\n\t\t\t\tcontrols: this.controls,\n\t\t\t\tcontrolsPosition: this.controlsPosition,\n\t\t\t\tcontrolsText: this.controlsText,\n\t\t\t\tcontrolsContainer: this.controlsContainer,\n\t\t\t\tprevButton: this.prevButton,\n\t\t\t\tnextButton: this.nextButton,\n\t\t\t\tnav: this.nav,\n\t\t\t\tnavPosition: this.navPosition,\n\t\t\t\tnavContainer: this.navContainer,\n\t\t\t\tnavAsThumbnails: this.navAsThumbnails,\n\t\t\t\tarrowKeys: this.arrowKeys,\n\t\t\t\tspeed: this.speed,\n\t\t\t\tautoplay: this.autoplay,\n\t\t\t\tautoplayTimeout: this.autoplayTimeout,\n\t\t\t\tautoplayDirection: this.autoplayDirection,\n\t\t\t\tautoplayText: this.autoplayText,\n\t\t\t\tautoplayHoverPause: this.autoplayHoverPause,\n\t\t\t\tautoplayButton: this.autoplayButton,\n\t\t\t\tautoplayButtonOutput: this.autoplayButtonOutput,\n\t\t\t\tautoplayResetOnVisibility: this.autoplayResetOnVisibility,\n\t\t\t\tanimateIn: this.animateIn,\n\t\t\t\tanimateOut: this.animateOut,\n\t\t\t\tanimateNormal: this.animateNormal,\n\t\t\t\tanimateDelay: this.animateDelay,\n\t\t\t\tloop: this.loop,\n\t\t\t\trewind: this.rewind,\n\t\t\t\tautoHeight: this.autoHeight,\n\t\t\t\tresponsive: this.responsive,\n\t\t\t\tlazyload: this.lazyload,\n\t\t\t\ttouch: this.touch,\n\t\t\t\tmouseDrag: this.mouseDrag,\n\t\t\t\tnested: this.nested,\n\t\t\t\tfreezable: this.freezable,\n\t\t\t\tdisable: this.disable,\n\t\t\t\tonInit: this.onInit,\n\t\t\t\tswipeAngle: this.swipeAngle,\n\t\t\t\tstartIndex: this.startIndex,\n\t\t\t\tcenter: this.center,\n\t\t\t\tlazyLoadSelector: this.lazyLoadSelector,\n\t\t\t\tpreventActionWhenRunning: this.preventActionWhenRunning,\n\t\t\t\tpreventScrollOnTouch: this.preventScrollOnTouch,\n\t\t\t\tautoWidth: this.autoWidth,\n\t\t\t\tuseLocalStorage: this.useLocalStorage\n\t\t\t};\n\t\t\tremoveUndefinedProps(settings);\n\n\t\t\tthis.slider = tns(settings);\n\n\t\t\t// Emit init event\n\t\t\tthis.$emit('init');\n\t\t\t// Subscribe to all kind of tiny-slider events\n\t\t\tthis.$_vueTinySlider_subscribeToAll();\n\t\t},\n\t},\n\trender: function(){\n\t\treturn h('div', this.$slots.default ? this.$slots.default() : []);\n\t}\n};\n\nfunction removeUndefinedProps(obj) {\n\tfor (var prop in obj) {\n\t\tif (obj.hasOwnProperty(prop) && obj[prop] === undefined) {\n\t\t\tdelete obj[prop];\n\t\t}\n\t}\n}\n\nexport default VueTinySlider;\n"
  },
  {
    "path": "tests/index.test.js",
    "content": "import { describe, it, expect, vi, beforeEach } from 'vitest';\nimport { mount, flushPromises } from '@vue/test-utils';\n\n// Capture the mock so each test can inspect/drive it.\nconst tnsMock = vi.fn();\n\nvi.mock('tiny-slider/dist/tiny-slider.js', () => ({\n\ttns: (...args) => tnsMock(...args)\n}));\n\nimport VueTinySlider from '../src/index.js';\n\nfunction makeFakeSlider(overrides = {}) {\n\treturn {\n\t\tevents: { on: vi.fn() },\n\t\tgoTo: vi.fn(),\n\t\trebuild: vi.fn(function () { return this; }),\n\t\tgetInfo: vi.fn(() => ({ index: 0 })),\n\t\tdestroy: vi.fn(),\n\t\t...overrides\n\t};\n}\n\n// init() is async (lazy-imports tiny-slider), so after mount we flush\n// microtasks before assertions that depend on the slider being ready.\nasync function mountSlider(props = {}, slots = undefined) {\n\tconst wrapper = mount(VueTinySlider, {\n\t\tprops,\n\t\tslots: slots ?? {\n\t\t\tdefault: '<div class=\"s\">A</div><div class=\"s\">B</div>'\n\t\t}\n\t});\n\tawait flushPromises();\n\treturn wrapper;\n}\n\nbeforeEach(() => {\n\ttnsMock.mockReset();\n\ttnsMock.mockImplementation(() => makeFakeSlider());\n});\n\ndescribe('VueTinySlider', () => {\n\tit('renders default slot children into a <div>', async () => {\n\t\tconst wrapper = await mountSlider();\n\t\texpect(wrapper.element.tagName).toBe('DIV');\n\t\texpect(wrapper.findAll('.s')).toHaveLength(2);\n\t});\n\n\tit('calls tns() on mount when autoInit is true (default)', async () => {\n\t\tawait mountSlider();\n\t\texpect(tnsMock).toHaveBeenCalledTimes(1);\n\t});\n\n\tit('does NOT call tns() on mount when autoInit is false', async () => {\n\t\tawait mountSlider({ autoInit: false });\n\t\texpect(tnsMock).not.toHaveBeenCalled();\n\t});\n\n\tit('passes the component root element as container', async () => {\n\t\tconst wrapper = await mountSlider();\n\t\tconst settings = tnsMock.mock.calls[0][0];\n\t\texpect(settings.container).toBe(wrapper.element);\n\t});\n\n\tit('forwards key props to tns(), coercing items to int', async () => {\n\t\tawait mountSlider({ items: '3', mode: 'gallery', loop: false, mouseDrag: true });\n\t\tconst settings = tnsMock.mock.calls[0][0];\n\t\texpect(settings.items).toBe(3);\n\t\texpect(settings.mode).toBe('gallery');\n\t\texpect(settings.loop).toBe(false);\n\t\texpect(settings.mouseDrag).toBe(true);\n\t});\n\n\tit('fixedWidth passes through as false when falsy, or parsed int when set', async () => {\n\t\tawait mountSlider();\n\t\texpect(tnsMock.mock.calls[0][0].fixedWidth).toBe(false);\n\n\t\ttnsMock.mockClear();\n\t\tawait mountSlider({ fixedWidth: '250' });\n\t\texpect(tnsMock.mock.calls[0][0].fixedWidth).toBe(250);\n\t});\n\n\tit('strips undefined props before calling tns()', async () => {\n\t\t// `axis` has no default, so it will be undefined unless set.\n\t\tawait mountSlider();\n\t\tconst settings = tnsMock.mock.calls[0][0];\n\t\texpect('axis' in settings).toBe(false);\n\t});\n\n\tit('emits init after tns() is created', async () => {\n\t\tconst wrapper = await mountSlider();\n\t\texpect(wrapper.emitted('init')).toBeTruthy();\n\t\texpect(wrapper.emitted('init')).toHaveLength(1);\n\t});\n\n\tit('subscribes to every tiny-slider event in eventsList and re-emits them', async () => {\n\t\tconst fake = makeFakeSlider();\n\t\ttnsMock.mockImplementationOnce(() => fake);\n\t\tconst wrapper = await mountSlider();\n\n\t\tconst expected = VueTinySlider.eventsList;\n\t\texpect(fake.events.on).toHaveBeenCalledTimes(expected.length);\n\t\tconst subscribedNames = fake.events.on.mock.calls.map(c => c[0]);\n\t\texpect(subscribedNames).toEqual(expected);\n\n\t\t// Simulate tiny-slider firing indexChanged; component should $emit it.\n\t\tconst indexChangedHandler = fake.events.on.mock.calls.find(\n\t\t\tc => c[0] === 'indexChanged'\n\t\t)[1];\n\t\tindexChangedHandler({ index: 4 });\n\t\texpect(wrapper.emitted('indexChanged')).toBeTruthy();\n\t\texpect(wrapper.emitted('indexChanged')[0]).toEqual([{ index: 4 }]);\n\t});\n\n\tit('goTo(value) delegates to the underlying slider', async () => {\n\t\tconst fake = makeFakeSlider();\n\t\ttnsMock.mockImplementationOnce(() => fake);\n\t\tconst wrapper = await mountSlider();\n\t\twrapper.vm.goTo(2);\n\t\texpect(fake.goTo).toHaveBeenCalledWith(2);\n\t});\n\n\tit('rebuild() replaces slider and emits rebuild', async () => {\n\t\tconst fake = makeFakeSlider();\n\t\ttnsMock.mockImplementationOnce(() => fake);\n\t\tconst wrapper = await mountSlider();\n\t\twrapper.vm.rebuild();\n\t\texpect(fake.rebuild).toHaveBeenCalled();\n\t\texpect(wrapper.emitted('rebuild')).toBeTruthy();\n\t});\n\n\tit('getInfo() emits getInfo with payload and slider', async () => {\n\t\tconst info = { index: 7 };\n\t\tconst fake = makeFakeSlider({ getInfo: vi.fn(() => info) });\n\t\ttnsMock.mockImplementationOnce(() => fake);\n\t\tconst wrapper = await mountSlider();\n\t\twrapper.vm.getInfo();\n\t\tconst emitted = wrapper.emitted('getInfo');\n\t\texpect(emitted).toBeTruthy();\n\t\texpect(emitted[0][0]).toBe(info);\n\t\texpect(emitted[0][1]).toBe(fake);\n\t});\n\n\tit('destroy() calls slider.destroy', async () => {\n\t\tconst fake = makeFakeSlider();\n\t\ttnsMock.mockImplementationOnce(() => fake);\n\t\tconst wrapper = await mountSlider();\n\t\twrapper.vm.destroy();\n\t\texpect(fake.destroy).toHaveBeenCalled();\n\t});\n\n\tit('destroys the slider when the component is unmounted', async () => {\n\t\tconst fake = makeFakeSlider();\n\t\ttnsMock.mockImplementationOnce(() => fake);\n\t\tconst wrapper = await mountSlider();\n\t\twrapper.unmount();\n\t\texpect(fake.destroy).toHaveBeenCalled();\n\t});\n\n\tit('controlsText default is [prev, next]', async () => {\n\t\tawait mountSlider();\n\t\texpect(tnsMock.mock.calls[0][0].controlsText).toEqual(['prev', 'next']);\n\t});\n});\n"
  },
  {
    "path": "tests/ssr.test.js",
    "content": "// @vitest-environment node\n// Regression guard: importing the component in a pure Node context\n// (no window, no document, no DOM globals) must NOT throw. This catches\n// top-level browser references leaking back into the module eval path.\nimport { describe, it, expect } from 'vitest';\n\ndescribe('SSR safety', () => {\n\tit('component module can be imported in Node without DOM globals', async () => {\n\t\tconst mod = await import('../src/index.js');\n\t\texpect(mod.default).toBeTypeOf('object');\n\t\texpect(mod.default.props).toBeTypeOf('object');\n\t});\n\n\tit('does not reference window/document at module-eval time', async () => {\n\t\t// Assert absence of the browser globals before import — proving\n\t\t// the happy-dom env isn't leaking into this test.\n\t\texpect(typeof window).toBe('undefined');\n\t\texpect(typeof document).toBe('undefined');\n\t\t// Re-import is a no-op (cached), but keeps the assertion meaningful.\n\t\tawait import('../src/index.js');\n\t});\n});\n"
  },
  {
    "path": "tests/types-check.ts",
    "content": "// Smoke-check the published types from a consumer perspective.\n// Run: npx tsc --noEmit tests/types-check.ts (or let CI do it)\nimport VueTinySlider, {\n\ttype VueTinySliderProps,\n\ttype VueTinySliderInstance,\n\ttype TinySliderInfo,\n\ttype SilderEvent\n} from '../src/index.js';\n\n// Default export is a Vue component.\nconst _component = VueTinySlider;\n\n// Prop interface accepts documented options.\nconst _props: VueTinySliderProps = {\n\titems: 3,\n\tgutter: 20,\n\tloop: false,\n\tmouseDrag: true,\n\tautoInit: false,\n\tcontrolsPosition: 'bottom',\n\tcontrolsText: ['prev', 'next'],\n\tmode: 'carousel',\n\tautoplay: true,\n\tpreventScrollOnTouch: 'auto'\n};\n\n// Instance shape available via $refs.\nconst _fakeRef: VueTinySliderInstance = {\n\tslider: null,\n\tinit: () => Promise.resolve(),\n\tgoTo: (_t: number | 'next' | 'prev' | 'first' | 'last') => {},\n\trebuild: () => {},\n\tgetInfo: () => {},\n\tdestroy: () => {}\n};\n\n// Event name type is re-exported from tiny-slider.\nconst _evt: SilderEvent = 'indexChanged';\nconst _info: TinySliderInfo = {} as TinySliderInfo;\n\nexport { _component, _props, _fakeRef, _evt, _info };\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"esnext\",\n\t\t\"module\": \"esnext\",\n\t\t\"moduleResolution\": \"bundler\",\n\t\t\"strict\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"noEmit\": true\n\t},\n\t\"include\": [\"src/**/*.d.ts\", \"tests/types-check.ts\"]\n}\n"
  },
  {
    "path": "vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport path from 'path';\n\nexport default defineConfig({\n\tbuild: {\n\t\tlib: {\n\t\t\tentry: path.resolve(__dirname, 'src/index.js'),\n\t\t\tname: 'vue-tiny-slider',\n\t\t\tfileName: () => 'index.js',\n\t\t\tformats: ['umd']\n\t\t},\n\t\trollupOptions: {\n\t\t\t// Keep vue and tiny-slider as runtime externals. tiny-slider must\n\t\t\t// not be inlined because it touches `document`/`window` at module\n\t\t\t// eval time — see SSR notes in README.\n\t\t\texternal: ['vue', /^tiny-slider(\\/|$)/],\n\t\t\toutput: {\n\t\t\t\tglobals: {\n\t\t\t\t\tvue: 'Vue',\n\t\t\t\t\t'tiny-slider': 'tns'\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tsourcemap: true\n\t}\n});\n"
  },
  {
    "path": "vitest.config.js",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n\ttest: {\n\t\tenvironment: 'happy-dom',\n\t\tglobals: false,\n\t\tinclude: ['tests/**/*.test.js']\n\t}\n});\n"
  }
]