├── .github ├── FUNDING.yml └── copilot-instructions.md ├── nuxt ├── public │ ├── robots.txt │ └── favicon.ico ├── app │ ├── app.vue │ └── components │ │ └── CompleteEditor.vue ├── nuxt.config.ts ├── .gitignore ├── tsconfig.json ├── package.json └── README.md ├── src ├── vite-env.d.ts ├── index.ts └── components │ └── QuillyEditor.vue ├── demo ├── src │ ├── vite-env.d.ts │ ├── window.d.ts │ ├── main.ts │ ├── assets │ │ └── vue.svg │ ├── components │ │ ├── SemanticHTMLEditor.vue │ │ ├── CustomEditor.vue │ │ ├── DefaultEditorSnow.vue │ │ ├── DefaultEditorBubble.vue │ │ └── ImageResizeEditor.vue │ └── App.vue ├── .vscode │ └── extensions.json ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── README.md ├── package.json ├── tsconfig.json ├── index.html ├── public │ └── vite.svg └── pnpm-lock.yaml ├── docs ├── .vitepress │ ├── cache │ │ └── deps │ │ │ ├── package.json │ │ │ ├── vue.js.map │ │ │ ├── _metadata.json │ │ │ └── vue.js │ └── config.ts ├── markdown-examples.md ├── examples │ ├── bubble-theme.md │ ├── index.md │ ├── snow-theme.md │ ├── image-resize.md │ ├── semantic-html.md │ └── custom-editor.md ├── index.md ├── api │ ├── component.md │ ├── events.md │ └── types.md ├── guide │ ├── getting-started.md │ ├── installation.md │ └── basic-usage.md └── api-examples.md ├── tsconfig.node.json ├── index.html ├── vite.config.ts ├── tsconfig.json ├── LICENSE ├── package.json ├── .gitignore └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: oleksandrshevchuk 2 | -------------------------------------------------------------------------------- /nuxt/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /demo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /demo/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /demo/src/window.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | interface Window { 3 | Quill: any 4 | } 5 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import QuillyEditor from './components/QuillyEditor.vue' 2 | export { QuillyEditor } 3 | -------------------------------------------------------------------------------- /nuxt/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alekswebnet/vue-quilly/HEAD/nuxt/public/favicon.ico -------------------------------------------------------------------------------- /demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vue.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": [], 4 | "sourcesContent": [], 5 | "mappings": "", 6 | "names": [] 7 | } 8 | -------------------------------------------------------------------------------- /nuxt/app/app.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()] 7 | }) 8 | -------------------------------------------------------------------------------- /nuxt/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | compatibilityDate: '2025-07-15', 4 | devtools: { enabled: true }, 5 | ssr: true 6 | }) 7 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /demo/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /nuxt/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # vue-quilly demo 2 | 3 | This demo project is created to show how to build editor on top of `QullyEditor` component. 4 | 5 | ## Project Setup 6 | 7 | ```sh 8 | pnpm install 9 | ``` 10 | 11 | ### Compile and Hot-Reload for Development 12 | 13 | ```sh 14 | pnpm dev 15 | ``` 16 | 17 | ### Type-Check, Compile and Minify for Production 18 | 19 | ```sh 20 | pnpm build 21 | ``` -------------------------------------------------------------------------------- /nuxt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "files": [], 4 | "references": [ 5 | { 6 | "path": "./.nuxt/tsconfig.app.json" 7 | }, 8 | { 9 | "path": "./.nuxt/tsconfig.server.json" 10 | }, 11 | { 12 | "path": "./.nuxt/tsconfig.shared.json" 13 | }, 14 | { 15 | "path": "./.nuxt/tsconfig.node.json" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /nuxt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "katex": "^0.16.25", 14 | "nuxt": "^4.2.1", 15 | "vue": "^3.5.25", 16 | "vue-quilly": "^1.1.6", 17 | "vue-router": "^4.6.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@vueuse/core": "^14.1.0", 13 | "katex": "^0.16.25", 14 | "parchment": "^3.0.0", 15 | "quill": "^2.0.3", 16 | "quill-image-resize-module": "^3.0.0", 17 | "vue": "^3.5.25", 18 | "vue-markdown-render": "^2.3.0", 19 | "vue-quilly": "^1.1.6" 20 | }, 21 | "devDependencies": { 22 | "@vitejs/plugin-vue": "^6.0.2", 23 | "typescript": "^5.9.3", 24 | "vite": "^7.2.6", 25 | "vue-tsc": "^3.1.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import dts from 'vite-plugin-dts' 4 | import path from 'path' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | vue(), 10 | dts() 11 | ], 12 | build: { 13 | lib: { 14 | entry: path.resolve(__dirname, 'src/index.ts'), 15 | name: 'VueQuilly', 16 | fileName: 'vue-quilly' 17 | }, 18 | rollupOptions: { 19 | external: ['vue'], 20 | output: { 21 | globals: { 22 | vue: 'Vue' 23 | } 24 | } 25 | } 26 | }, 27 | resolve: { 28 | alias: { 29 | '@': path.resolve(__dirname, 'src') 30 | } 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "paths": { 16 | "@/*": [ 17 | "./src/*" 18 | ] 19 | }, 20 | "jsx": "preserve", 21 | 22 | /* Linting */ 23 | "strict": true, 24 | "noUnusedLocals": true, 25 | "noUnusedParameters": true, 26 | "noFallthroughCasesInSwitch": true 27 | }, 28 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], 29 | "references": [{ "path": "./tsconfig.node.json" }] 30 | } 31 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | QuillyEditor demo - vue-quilly 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Oleksandr Shevchuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "c335150a", 3 | "configHash": "2329454d", 4 | "lockfileHash": "e821ab6a", 5 | "browserHash": "a2244759", 6 | "optimized": { 7 | "vue": { 8 | "src": "../../../../node_modules/.pnpm/vue@3.5.25_typescript@5.9.3/node_modules/vue/dist/vue.runtime.esm-bundler.js", 9 | "file": "vue.js", 10 | "fileHash": "8d3c7f1f", 11 | "needsInterop": false 12 | }, 13 | "vitepress > @vue/devtools-api": { 14 | "src": "../../../../node_modules/.pnpm/@vue+devtools-api@8.0.5/node_modules/@vue/devtools-api/dist/index.js", 15 | "file": "vitepress___@vue_devtools-api.js", 16 | "fileHash": "128b0156", 17 | "needsInterop": false 18 | }, 19 | "vitepress > @vueuse/core": { 20 | "src": "../../../../node_modules/.pnpm/@vueuse+core@14.1.0_vue@3.5.25_typescript@5.9.3_/node_modules/@vueuse/core/dist/index.js", 21 | "file": "vitepress___@vueuse_core.js", 22 | "fileHash": "284b2707", 23 | "needsInterop": false 24 | } 25 | }, 26 | "chunks": { 27 | "chunk-GKNP4RGU": { 28 | "file": "chunk-GKNP4RGU.js" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /nuxt/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Minimal Starter 2 | 3 | Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /docs/markdown-examples.md: -------------------------------------------------------------------------------- 1 | # Markdown Extension Examples 2 | 3 | This page demonstrates some of the built-in markdown extensions provided by VitePress. 4 | 5 | ## Syntax Highlighting 6 | 7 | VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting: 8 | 9 | **Input** 10 | 11 | ````md 12 | ```js{4} 13 | export default { 14 | data () { 15 | return { 16 | msg: 'Highlighted!' 17 | } 18 | } 19 | } 20 | ``` 21 | ```` 22 | 23 | **Output** 24 | 25 | ```js{4} 26 | export default { 27 | data () { 28 | return { 29 | msg: 'Highlighted!' 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | ## Custom Containers 36 | 37 | **Input** 38 | 39 | ```md 40 | ::: info 41 | This is an info box. 42 | ::: 43 | 44 | ::: tip 45 | This is a tip. 46 | ::: 47 | 48 | ::: warning 49 | This is a warning. 50 | ::: 51 | 52 | ::: danger 53 | This is a dangerous warning. 54 | ::: 55 | 56 | ::: details 57 | This is a details block. 58 | ::: 59 | ``` 60 | 61 | **Output** 62 | 63 | ::: info 64 | This is an info box. 65 | ::: 66 | 67 | ::: tip 68 | This is a tip. 69 | ::: 70 | 71 | ::: warning 72 | This is a warning. 73 | ::: 74 | 75 | ::: danger 76 | This is a dangerous warning. 77 | ::: 78 | 79 | ::: details 80 | This is a details block. 81 | ::: 82 | 83 | ## More 84 | 85 | Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown). 86 | -------------------------------------------------------------------------------- /demo/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-quilly", 3 | "version": "1.1.6", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc && vite build", 8 | "preview": "vite preview", 9 | "docs:dev": "vitepress dev docs", 10 | "docs:build": "vitepress build docs", 11 | "docs:preview": "vitepress preview docs" 12 | }, 13 | "files": [ 14 | "dist", 15 | "src/components/" 16 | ], 17 | "author": { 18 | "name": "Oleksandr Shevchuk", 19 | "email": "alekswebnet@gmail.com" 20 | }, 21 | "license": "MIT", 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/alekswebnet/vue-quilly.git" 25 | }, 26 | "homepage": "https://github.com/alekswebnet/vue-quilly", 27 | "bugs": { 28 | "url": "https://github.com/alekswebnet/vue-quilly/issues" 29 | }, 30 | "keywords": [ 31 | "quill", 32 | "quill2", 33 | "wysiwyg", 34 | "rich-text", 35 | "html-editor", 36 | "vue3", 37 | "vue", 38 | "vuejs", 39 | "vue-component", 40 | "vue-quill", 41 | "nuxt", 42 | "nuxt4" 43 | ], 44 | "main": "./dist/vue-quilly.umd.cjs", 45 | "module": "./dist/vue-quilly.js", 46 | "types": "./dist/index.d.ts", 47 | "exports": { 48 | ".": { 49 | "import": { 50 | "types": "./dist/index.d.ts", 51 | "default": "./dist/vue-quilly.js" 52 | }, 53 | "require": { 54 | "types": "./dist/index.d.ts", 55 | "default": "./dist/vue-quilly.umd.cjs" 56 | } 57 | } 58 | }, 59 | "devDependencies": { 60 | "@types/node": "^24.10.1", 61 | "@vitejs/plugin-vue": "^6.0.2", 62 | "typescript": "^5.9.3", 63 | "vite": "^7.2.6", 64 | "vite-plugin-dts": "^4.5.4", 65 | "vitepress": "2.0.0-alpha.15", 66 | "vue-tsc": "^3.1.6" 67 | }, 68 | "dependencies": { 69 | "quill": "^2.0.3", 70 | "vue": "^3.5.25" 71 | }, 72 | "packageManager": "pnpm@10.25.0" 73 | } -------------------------------------------------------------------------------- /docs/examples/bubble-theme.md: -------------------------------------------------------------------------------- 1 | # Bubble Theme Editor 2 | 3 | Lightweight editor with the Bubble theme that shows toolbar on text selection. 4 | 5 | ## Features 6 | 7 | - Medium-style inline toolbar 8 | - Appears on text selection 9 | - Same formatting capabilities as Snow theme 10 | - Cleaner, more minimalist interface 11 | 12 | ## Code Example 13 | 14 | ```vue 15 | 48 | 49 | 56 | ``` 57 | 58 | ## Installation 59 | 60 | Install the required dependencies: 61 | 62 | ```bash 63 | pnpm add vue-quilly quill@2 katex 64 | ``` 65 | 66 | ## Usage Tips 67 | 68 | The bubble theme is perfect for: 69 | - Minimal, distraction-free writing interfaces 70 | - Mobile-friendly editors 71 | - Inline content editing 72 | - Medium-style blogging platforms 73 | 74 | ## Resources 75 | 76 | - [View Full Source Code](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/DefaultEditorBubble.vue) 77 | - [Try Live Demo](https://vue-quilly.vercel.app/) 78 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | // https://vitepress.dev/reference/site-config 4 | export default defineConfig({ 5 | title: "Vue Quilly Docs", 6 | description: "Documentation for vue-quilly", 7 | head: [ 8 | ['link', { rel: 'icon', href: 'data:image/svg+xml,🪶' }] 9 | ], 10 | themeConfig: { 11 | // https://vitepress.dev/reference/default-theme-config 12 | logo: 'data:image/svg+xml,🪶', 13 | nav: [ 14 | { text: 'Home', link: '/' }, 15 | { text: 'Guide', link: '/guide/getting-started' }, 16 | { text: 'API', link: '/api/component' }, 17 | { text: 'Examples', link: '/examples/' }, 18 | { text: 'Support', link: 'https://ko-fi.com/oleksandrshevchuk' } 19 | ], 20 | 21 | sidebar: [ 22 | { 23 | text: 'Guide', 24 | items: [ 25 | { text: 'Getting Started', link: '/guide/getting-started' }, 26 | { text: 'Installation', link: '/guide/installation' }, 27 | { text: 'Basic Usage', link: '/guide/basic-usage' } 28 | ] 29 | }, 30 | { 31 | text: 'API Reference', 32 | items: [ 33 | { text: 'Component API', link: '/api/component' }, 34 | { text: 'Events', link: '/api/events' }, 35 | { text: 'TypeScript Types', link: '/api/types' } 36 | ] 37 | }, 38 | { 39 | text: 'Examples', 40 | items: [ 41 | { text: 'Overview', link: '/examples/' }, 42 | { text: 'Snow Theme', link: '/examples/snow-theme' }, 43 | { text: 'Bubble Theme', link: '/examples/bubble-theme' }, 44 | { text: 'Semantic HTML', link: '/examples/semantic-html' }, 45 | { text: 'Image Resize', link: '/examples/image-resize' }, 46 | { text: 'Custom Editor', link: '/examples/custom-editor' } 47 | ] 48 | } 49 | ], 50 | 51 | socialLinks: [ 52 | { icon: 'github', link: 'https://github.com/alekswebnet/vue-quilly' } 53 | ] 54 | } 55 | }) 56 | -------------------------------------------------------------------------------- /docs/examples/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | 5 | # Examples 6 | 7 | This section showcases different editor configurations and use cases built with vue-quilly. Each example demonstrates specific features and integration patterns you can use in your projects. 8 | 9 | ## Available Examples 10 | 11 | ### [Snow Theme Editor](/examples/snow-theme) 12 | The classic Quill editor with the Snow theme, featuring a full toolbar with all formatting options. 13 | 14 | **Features:** 15 | - Complete toolbar with all formatting options 16 | - Formula support with KaTeX 17 | - Image and video embedding 18 | - Font, size, and color customization 19 | 20 | ### [Bubble Theme Editor](/examples/bubble-theme) 21 | Lightweight editor with the Bubble theme that shows toolbar on text selection. 22 | 23 | **Features:** 24 | - Medium-style inline toolbar 25 | - Appears on text selection 26 | - Same formatting capabilities as Snow theme 27 | - Cleaner, more minimalist interface 28 | 29 | ### [Semantic HTML Editor](/examples/semantic-html) 30 | Editor configured to output clean, semantic HTML without Quill-specific classes. 31 | 32 | **Features:** 33 | - Clean HTML output (e.g., `

` instead of `

`) 34 | - Better for SEO and accessibility 35 | - Easy content portability 36 | - Ideal for CMS and blog posts 37 | 38 | ### [Image Resize Editor](/examples/image-resize) 39 | Editor with image resize functionality using the `quill-image-resize-module`. 40 | 41 | **Features:** 42 | - Drag to resize images 43 | - Display size indicator 44 | - Resize handles on images 45 | - Image alignment toolbar 46 | 47 | ### [Custom Editor (Core Build)](/examples/custom-editor) 48 | Minimal custom editor using `quill/core` with only specific blots registered. 49 | 50 | **Features:** 51 | - Minimal bundle size (uses `quill/core`) 52 | - Custom blots (Bold, Italic, Headers) 53 | - External toolbar controls 54 | - Perfect for custom implementations 55 | 56 | ## Live Demo 57 | 58 | Try all these examples live at [vue-quilly.vercel.app](https://vue-quilly.vercel.app/) 59 | 60 | ## Source Code 61 | 62 | View the complete source code for all examples in our [demo directory](https://github.com/alekswebnet/vue-quilly/tree/main/demo). 63 | 64 | ## Integration Examples 65 | 66 | - [Nuxt 4 Integration](https://github.com/alekswebnet/vue-quilly/tree/main/nuxt) - SSR setup with Nuxt 67 | - [Browser CDN Setup](https://codepen.io/redrobot753/pen/VwJwPLP) - CodePen example 68 | -------------------------------------------------------------------------------- /docs/examples/snow-theme.md: -------------------------------------------------------------------------------- 1 | # Snow Theme Editor 2 | 3 | The classic Quill editor with the Snow theme, featuring a full toolbar with all formatting options. 4 | 5 | ## Features 6 | 7 | - Complete toolbar with all formatting options 8 | - Formula support with KaTeX 9 | - Image and video embedding 10 | - Font, size, and color customization 11 | - Headers, lists, and alignment 12 | 13 | ## Code Example 14 | 15 | ```vue 16 | 61 | 62 | 71 | ``` 72 | 73 | ## Installation 74 | 75 | Install the required dependencies: 76 | 77 | ```bash 78 | pnpm add vue-quilly quill@2 katex 79 | ``` 80 | 81 | ## Resources 82 | 83 | - [View Full Source Code](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/DefaultEditorSnow.vue) 84 | - [Try Live Demo](https://vue-quilly.vercel.app/) 85 | -------------------------------------------------------------------------------- /docs/examples/image-resize.md: -------------------------------------------------------------------------------- 1 | # Image Resize Editor 2 | 3 | Editor with image resize functionality using the `quill-image-resize-module`. 4 | 5 | ## Features 6 | 7 | - Drag to resize images 8 | - Display size indicator 9 | - Resize handles on images 10 | - Image alignment toolbar 11 | 12 | ## Code Example 13 | 14 | ```vue 15 | 53 | 54 | 61 | ``` 62 | 63 | ## Installation 64 | 65 | Install both vue-quilly and the image resize module: 66 | 67 | ```bash 68 | pnpm add vue-quilly quill@2 quill-image-resize-module 69 | ``` 70 | 71 | ## Configuration Options 72 | 73 | The `imageResize` module supports several configurations: 74 | 75 | - **Resize** - Enable drag-to-resize functionality 76 | - **DisplaySize** - Show image dimensions overlay 77 | - **Toolbar** - Show alignment toolbar on image selection 78 | 79 | ## Usage Tips 80 | 81 | 1. Click the image button in the toolbar to insert an image 82 | 2. Click on the inserted image to see resize handles 83 | 3. Drag the handles to resize the image 84 | 4. Use the alignment toolbar for positioning 85 | 86 | ## Resources 87 | 88 | - [View Full Source Code](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/ImageResizeEditor.vue) 89 | - [quill-image-resize-module](https://github.com/kensnyder/quill-image-resize-module) - Module documentation 90 | - [Try Live Demo](https://vue-quilly.vercel.app/) 91 | -------------------------------------------------------------------------------- /docs/examples/semantic-html.md: -------------------------------------------------------------------------------- 1 | # Semantic HTML Editor 2 | 3 | Editor configured to output clean, semantic HTML without Quill-specific classes. 4 | 5 | ## Features 6 | 7 | - Clean HTML output (e.g., `

` instead of `

`) 8 | - Better for SEO and accessibility 9 | - Easy content portability 10 | - Ideal for CMS and blog posts 11 | 12 | ## Code Example 13 | 14 | ```vue 15 | 40 | 41 | 53 | ``` 54 | 55 | ## Output Comparison 56 | 57 | The key difference is in the HTML output: 58 | 59 | ::: code-group 60 | 61 | ```html [Regular HTML] 62 |

Heading

63 |
    64 |
  • Item
  • 65 |
66 | ``` 67 | 68 | ```html [Semantic HTML] 69 |

Heading

70 | 73 | ``` 74 | 75 | ::: 76 | 77 | ## Benefits 78 | 79 | ### SEO Advantages 80 | - Search engines better understand semantic markup 81 | - Proper heading hierarchy improves content structure 82 | - Clean HTML signals content quality 83 | 84 | ### Accessibility 85 | - Screen readers work better with semantic HTML 86 | - Proper heading tags create better navigation 87 | - Better ARIA compatibility 88 | 89 | ### Maintainability 90 | - Cleaner code without framework-specific classes 91 | - Easier to style with custom CSS 92 | - Better compatibility with other systems 93 | 94 | ## Use Cases 95 | 96 | - Blog posts 97 | - CMS content 98 | - Email templates 99 | - Exported documents 100 | 101 | ## Installation 102 | 103 | ```bash 104 | pnpm add vue-quilly quill@2 105 | ``` 106 | 107 | ## Resources 108 | 109 | - [View Full Source Code](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/SemanticHTMLEditor.vue) 110 | - [Try Live Demo](https://vue-quilly.vercel.app/) 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Created by https://www.toptal.com/developers/gitignore/api/vue,vuejs,node 4 | # Edit at https://www.toptal.com/developers/gitignore?templates=vue,vuejs,node 5 | 6 | ### Node ### 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | # Diagnostic reports (https://nodejs.org/api/report.html) 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | *.lcov 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # TypeScript cache 54 | *.tsbuildinfo 55 | 56 | # Optional npm cache directory 57 | .npm 58 | 59 | # Optional eslint cache 60 | .eslintcache 61 | 62 | # Microbundle cache 63 | .rpt2_cache/ 64 | .rts2_cache_cjs/ 65 | .rts2_cache_es/ 66 | .rts2_cache_umd/ 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | .env.test 80 | .env*.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | 89 | # Nuxt.js build / generate output 90 | .nuxt 91 | dist 92 | 93 | # Gatsby files 94 | .cache/ 95 | # Comment in the public line in if your project uses Gatsby and not Next.js 96 | # https://nextjs.org/blog/next-9-1#public-directory-support 97 | # public 98 | 99 | # vuepress build output 100 | .vuepress/dist 101 | 102 | # Serverless directories 103 | .serverless/ 104 | 105 | # FuseBox cache 106 | .fusebox/ 107 | 108 | # DynamoDB Local files 109 | .dynamodb/ 110 | 111 | # TernJS port file 112 | .tern-port 113 | 114 | # Stores VSCode versions used for testing VSCode extensions 115 | .vscode-test 116 | 117 | ### Vue ### 118 | # gitignore template for Vue.js projects 119 | # 120 | # Recommended template: Node.gitignore 121 | 122 | # TODO: where does this rule come from? 123 | docs/_book 124 | 125 | # TODO: where does this rule come from? 126 | test/ 127 | 128 | ### Vuejs ### 129 | # Recommended template: Node.gitignore 130 | 131 | dist/ 132 | npm-debug.log 133 | yarn-error.log 134 | 135 | # End of https://www.toptal.com/developers/gitignore/api/vue,vuejs,node -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # AI Coding Assistant Instructions for vue-quilly 2 | 3 | ## Project Overview 4 | vue-quilly is a Vue 3 component wrapper for Quill v2, providing a WYSIWYG editor with the following key features: 5 | - Uses `quill/core` for minimal bundle size, allowing selective module imports 6 | - Supports both HTML and Delta formats for content manipulation 7 | - TypeScript support with full type definitions 8 | - Nuxt 4 compatibility 9 | 10 | ## Architecture 11 | 12 | ### Core Component 13 | The main `QuillyEditor.vue` component follows these patterns: 14 | - Uses Vue 3 ` 43 | 44 | 89 | -------------------------------------------------------------------------------- /docs/examples/custom-editor.md: -------------------------------------------------------------------------------- 1 | # Custom Editor (Core Build) 2 | 3 | Minimal custom editor using `quill/core` with only specific blots registered. 4 | 5 | ## Features 6 | 7 | - Minimal bundle size (uses `quill/core`) 8 | - Custom blots (Bold, Italic, Headers) 9 | - External toolbar controls 10 | - Perfect for custom implementations 11 | 12 | ## Code Example 13 | 14 | ```vue 15 | 62 | 63 | 77 | ``` 78 | 79 | ## Why Use Core Build? 80 | 81 | ### Bundle Size Benefits 82 | 83 | The core build significantly reduces bundle size by excluding unused modules: 84 | 85 | - **Full build**: ~250KB minified 86 | - **Core build**: ~100KB minified 87 | 88 | You only import what you need! 89 | 90 | ### Use Cases 91 | 92 | Perfect for: 93 | - Custom editor implementations 94 | - Mobile-first applications where size matters 95 | - Embedded widgets with limited features 96 | - Learning Quill internals 97 | 98 | ## Creating Custom Blots 99 | 100 | Custom blots give you complete control over formatting: 101 | 102 | ```typescript 103 | class CustomBlot extends Inline { 104 | static blotName = 'custom' 105 | static tagName = 'span' 106 | static className = 'my-custom-class' 107 | } 108 | 109 | Quill.register(CustomBlot) 110 | ``` 111 | 112 | ## Installation 113 | 114 | ```bash 115 | pnpm add vue-quilly quill@2 parchment 116 | ``` 117 | 118 | ## Resources 119 | 120 | - [View Full Source Code](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/CustomEditor.vue) 121 | - [Quill Core Build Documentation](https://quilljs.com/docs/installation#core-build) 122 | - [Parchment Documentation](https://github.com/quilljs/parchment) - Blot framework 123 | - [Try Live Demo](https://vue-quilly.vercel.app/) 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-quilly 2 | 3 | Tiny Vue component, that helps to create [Quill v2](https://quilljs.com/) based WYSIWYG editors in Vue-powered apps. 4 | 5 | [![npm version](https://img.shields.io/npm/v/vue-quilly?logo=npm&logoColor=fff)](https://www.npmjs.com/package/vue-quilly) 6 | [![npm bundle size](https://img.shields.io/bundlephobia/min/vue-quilly)](https://www.npmjs.com/package/vue-quilly?activeTab=code) 7 | [![NPM Type Definitions](https://img.shields.io/npm/types/vue-quilly)](https://www.npmjs.com/package/vue-quilly?activeTab=code) 8 | [![GitHub License](https://img.shields.io/github/license/alekswebnet/vue-quilly)](https://github.com/alekswebnet/vue-quilly?tab=readme-ov-file#license) 9 | 10 | ## Features 11 | 12 | - 🚀 Built on top of [Quill v2](https://github.com/quilljs/quill) and Vue 3 13 | - 📦 Uses `quill/core` to prevent importing all Quill modules (minimal bundle size) 14 | - 🔄 Works with both HTML and Quill Delta format 15 | - 🔷 TypeScript support 16 | - ⚙️ Highly customizable - build your own editor 17 | - ⚡ Framework ready - works with Vue 3 and Nuxt 4 18 | 19 | ## Documentation 20 | 21 | 📖 **[Full Documentation](https://vue-quilly-docs.vercel.app/)** 22 | 23 | - [Getting Started](https://vue-quilly-docs.vercel.app/guide/getting-started) 24 | - [Installation Guide](https://vue-quilly-docs.vercel.app/guide/installation) 25 | - [Basic Usage](https://vue-quilly-docs.vercel.app/guide/basic-usage) 26 | - [API Reference](https://vue-quilly-docs.vercel.app/api/component) 27 | - [Examples](https://vue-quilly-docs.vercel.app/examples/) 28 | 29 | ## Quick Start 30 | 31 | ### Installation 32 | 33 | ```bash 34 | npm install vue-quilly quill@2 35 | # or 36 | pnpm add vue-quilly quill@2 37 | # or 38 | yarn add vue-quilly quill@2 39 | ``` 40 | 41 | ### Basic Usage 42 | 43 | ```vue 44 | 69 | 70 | 73 | ``` 74 | 75 | ## Live Demo 76 | 77 | 🎯 **[Try it live](https://vue-quilly.vercel.app/)** - See various editors built with `vue-quilly` 78 | 79 | ## Examples 80 | 81 | - [Demo Source Code](https://github.com/alekswebnet/vue-quilly/tree/main/demo) - Complete examples with different configurations 82 | - [Nuxt 4 Integration](https://github.com/alekswebnet/vue-quilly/tree/main/nuxt) - SSR setup example 83 | - [Browser CDN Setup](https://codepen.io/redrobot753/pen/VwJwPLP) - CodePen example 84 | 85 | ## Star History 86 | 87 | [![Star History Chart](https://api.star-history.com/svg?repos=alekswebnet/vue-quilly&type=date&legend=top-left)](https://www.star-history.com/#alekswebnet/vue-quilly&type=date&legend=top-left) 88 | 89 | ## Support 90 | 91 | If you find `vue-quilly` useful and want to support its development: 92 | 93 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/oleksandrshevchuk) 94 | 95 | ❤️ Your support helps with maintenance, bug fixes, and long-term improvements. 96 | 97 | ## License 98 | 99 | [MIT](https://choosealicense.com/licenses/mit/) 100 | -------------------------------------------------------------------------------- /src/components/QuillyEditor.vue: -------------------------------------------------------------------------------- 1 | 117 | 118 | 121 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "vue-quilly" 7 | text: "" 8 | tagline: Tiny Vue component for creating Quill v2 based WYSIWYG editors 9 | actions: 10 | - theme: brand 11 | text: Get Started 12 | link: /guide/getting-started 13 | - theme: alt 14 | text: View on GitHub 15 | link: https://github.com/alekswebnet/vue-quilly 16 | 17 | features: 18 | - title: 🚀 Quill v2 Support 19 | details: Built on top of Quill v2 and Vue 3, providing modern rich text editing capabilities with the latest features. 20 | - title: 📦 Minimal Bundle Size 21 | details: Uses quill/core to prevent importing all Quill modules. Import only what you need to keep your bundle small. 22 | - title: 🔷 TypeScript Support 23 | details: Full TypeScript definitions included for better development experience, type safety, and IntelliSense support. 24 | - title: 🔄 Dual Format Support 25 | details: Works with both HTML and Quill Delta format. Use one or both formats to manipulate editor content. 26 | - title: ⚙️ Customizable 27 | details: Not an all-in-one solution. Build your own editor that matches your exact needs with flexible configuration. 28 | - title: ⚡ Framework Ready 29 | details: Works seamlessly with Vue 3 and Nuxt 4. SSR compatible with proper client-side initialization setup. 30 | --- 31 | 32 | ## Quick Start 33 | 34 | Install vue-quilly and Quill v2: 35 | 36 | ::: code-group 37 | 38 | ```bash [npm] 39 | npm install vue-quilly quill@2 40 | ``` 41 | 42 | ```bash [pnpm] 43 | pnpm add vue-quilly quill@2 44 | ``` 45 | 46 | ```bash [yarn] 47 | yarn add vue-quilly quill@2 48 | ``` 49 | 50 | ::: 51 | 52 | Use in your Vue component: 53 | 54 | ```vue 55 | 80 | 81 | 84 | ``` 85 | 86 | ## Why vue-quilly? 87 | 88 | vue-quilly is designed to be a flexible foundation for building custom Quill editors in Vue applications: 89 | 90 | - **Customizable** - Requires Quill configuration, giving you full control over features. 91 | - **Minimal footprint** - Import only the Quill modules you need to keep bundle size small 92 | - **Flexible formats** - Work with HTML or Delta format, or use both for maximum flexibility 93 | - **Full control** - Access the Quill instance directly for advanced customization and features 94 | 95 | ## Examples 96 | 97 | Check out live examples and learn from real implementations: 98 | 99 | - 🚀 [Live Demo](https://vue-quilly.vercel.app/) - See various editors built with vue-quilly 100 | - 📚 [Demo Source Code](https://github.com/alekswebnet/vue-quilly/tree/main/demo) - Complete examples with source code 101 | - ⚡ [Nuxt 4 Example](https://github.com/alekswebnet/vue-quilly/tree/main/nuxt) - SSR integration guide 102 | 103 | ## Star History 104 | 105 | [![Star History Chart](https://api.star-history.com/svg?repos=alekswebnet/vue-quilly&type=date&legend=top-left)](https://www.star-history.com/#alekswebnet/vue-quilly&type=date&legend=top-left) 106 | 107 | ## Support the Project 108 | 109 | If you find vue-quilly useful and want to support its development: 110 | 111 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/oleksandrshevchuk) 112 | 113 | ❤️ Your support helps with maintenance, bug fixes, and long-term improvements. 114 | 115 | -------------------------------------------------------------------------------- /demo/src/components/CustomEditor.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 105 | -------------------------------------------------------------------------------- /demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 82 | 83 | -------------------------------------------------------------------------------- /demo/src/components/DefaultEditorSnow.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 93 | -------------------------------------------------------------------------------- /demo/src/components/DefaultEditorBubble.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 93 | -------------------------------------------------------------------------------- /nuxt/app/components/CompleteEditor.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | -------------------------------------------------------------------------------- /docs/api/component.md: -------------------------------------------------------------------------------- 1 | # Component API 2 | 3 | The `QuillyEditor` component provides a Vue 3 wrapper for Quill v2 with full TypeScript support. 4 | 5 | ## Props 6 | 7 | ### modelValue 8 | 9 | - **Type:** `string | null` 10 | - **Default:** `null` 11 | - **Required:** No 12 | 13 | The HTML content of the editor. Used with `v-model` for two-way data binding. 14 | 15 | ```vue 16 | 17 | ``` 18 | 19 | The content is automatically synced bidirectionally: 20 | - Changes in the editor update the `modelValue` 21 | - External changes to `modelValue` update the editor content 22 | 23 | ### options 24 | 25 | - **Type:** `QuillOptions` 26 | - **Default:** `{}` 27 | - **Required:** No 28 | 29 | Quill editor initialization options. Supports all [Quill configuration options](https://quilljs.com/docs/configuration/). 30 | 31 | ```vue 32 | 47 | 48 | 51 | ``` 52 | 53 | **Common options:** 54 | 55 | | Option | Type | Description | 56 | |--------|------|-------------| 57 | | `theme` | `string` | Theme name (`'snow'`, `'bubble'`, or custom) | 58 | | `placeholder` | `string` | Placeholder text when editor is empty | 59 | | `readOnly` | `boolean` | Whether editor is read-only | 60 | | `modules` | `object` | Quill modules configuration (toolbar, clipboard, etc.) | 61 | | `formats` | `string[]` | Allowed formats whitelist | 62 | | `bounds` | `HTMLElement \| string` | DOM boundary for editor | 63 | | `scrollingContainer` | `HTMLElement \| string` | Scrolling container element | 64 | 65 | ### isSemanticHtmlModel 66 | 67 | - **Type:** `boolean` 68 | - **Default:** `false` 69 | - **Required:** No 70 | 71 | When `true`, the component outputs clean semantic HTML without Quill-specific classes. 72 | 73 | ```vue 74 | 78 | ``` 79 | 80 | **Comparison:** 81 | 82 | ::: code-group 83 | 84 | ```html [Regular HTML] 85 |

86 | Text 87 |

88 | ``` 89 | 90 | ```html [Semantic HTML] 91 |

92 | Text 93 |

94 | ``` 95 | 96 | ::: 97 | 98 | **Benefits:** 99 | - Better SEO 100 | - Improved accessibility 101 | - Cleaner HTML for external use 102 | - Easier to style with custom CSS 103 | 104 | ## Methods 105 | 106 | The component exposes methods via template refs. 107 | 108 | ### initialize(QuillClass) 109 | 110 | Initializes the Quill editor instance. Must be called after component mount. 111 | 112 | **Parameters:** 113 | - `QuillClass`: `typeof Quill` - The Quill constructor class 114 | 115 | **Returns:** `Quill` - The initialized Quill instance 116 | 117 | **Example:** 118 | 119 | ```vue 120 | 136 | 137 | 140 | ``` 141 | 142 | ## Usage Example 143 | 144 | Complete example with all features: 145 | 146 | ```vue 147 | 216 | 217 | 230 | ``` 231 | 232 | ## TypeScript Support 233 | 234 | The component is fully typed with TypeScript: 235 | 236 | ```typescript 237 | import type { QuillyEditor } from 'vue-quilly' 238 | import type { QuillOptions, Delta, Range, EmitterSource } from 'quill/core' 239 | 240 | // Component type 241 | const editor = ref>() 242 | 243 | // Quill instance type 244 | let quill: Quill | undefined 245 | ``` 246 | 247 | ## See Also 248 | 249 | - [Events](/api/events) - All available events 250 | - [TypeScript Types](/api/types) - Type definitions 251 | - [Examples](/examples/) - Practical examples 252 | -------------------------------------------------------------------------------- /demo/src/components/ImageResizeEditor.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 104 | -------------------------------------------------------------------------------- /docs/guide/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | Welcome to vue-quilly! This guide will help you get started with building rich text editors in your Vue 3 applications. 4 | 5 | ## What is vue-quilly? 6 | 7 | vue-quilly is a lightweight Vue 3 component that wraps [Quill v2](https://quilljs.com/), the powerful rich text editor. It provides: 8 | 9 | - 🎯 **Simple integration** - Easy to add to any Vue 3 project 10 | - 📦 **Minimal bundle** - Uses `quill/core` for tree-shaking 11 | - 🔷 **TypeScript support** - Full type definitions included 12 | - 🔄 **Dual format** - Works with HTML and Delta formats 13 | - ⚙️ **Highly customizable** - Build exactly what you need 14 | 15 | ## Philosophy 16 | 17 | vue-quilly is **not** an all-in-one editor solution. Instead, it's a flexible foundation that lets you: 18 | 19 | - Import only the Quill modules you need 20 | - Build custom editors tailored to your use case 21 | - Maintain full control over features and styling 22 | - Keep your bundle size minimal 23 | 24 | ## Quick Example 25 | 26 | Here's a minimal example to get you started: 27 | 28 | ```vue 29 | 53 | 54 | 61 | ``` 62 | 63 | That's it! You now have a working rich text editor. 64 | 65 | ## Core Concepts 66 | 67 | ### 1. Quill Initialization 68 | 69 | The editor requires explicit initialization: 70 | 71 | ```typescript 72 | const editor = ref>() 73 | let quill: Quill | undefined 74 | 75 | onMounted(() => { 76 | quill = editor.value?.initialize(Quill) 77 | }) 78 | ``` 79 | 80 | This gives you control over when and how Quill is initialized. 81 | 82 | **Accessing Quill instance:** 83 | 84 | Once initialized, you can access the full Quill API: 85 | 86 | ```typescript 87 | onMounted(() => { 88 | quill = editor.value?.initialize(Quill) 89 | 90 | // Use Quill methods 91 | quill?.focus() 92 | quill?.setSelection(0, 0) 93 | 94 | // Get content 95 | const text = quill?.getText() 96 | const delta = quill?.getContents() 97 | 98 | // Format text 99 | quill?.formatText(0, 5, 'bold', true) 100 | }) 101 | ``` 102 | 103 | **Using Quill plugins:** 104 | 105 | You can register and use Quill plugins by registering them before initialization: 106 | 107 | ```typescript 108 | import Quill from 'quill' 109 | import ImageResize from 'quill-resize-module' 110 | 111 | onMounted(() => { 112 | // Register image resize plugin 113 | Quill.register('modules/ImageResize', ImageResize) 114 | 115 | // Initialize editor with the plugin 116 | quill = editor.value?.initialize(Quill) 117 | }) 118 | ``` 119 | 120 | **Plugin configuration in options:** 121 | 122 | ```typescript 123 | const options = { 124 | theme: 'snow', 125 | modules: { 126 | toolbar: [['bold', 'italic'], ['image']], 127 | ImageResize: { 128 | modules: ['Resize', 'DisplaySize'] 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | **Example with KaTeX (formula support):** 135 | 136 | ```typescript 137 | import Quill from 'quill' 138 | import katex from 'katex' 139 | import 'katex/dist/katex.min.css' 140 | 141 | onMounted(() => { 142 | // Make KaTeX available globally for Quill 143 | (window.katex as typeof katex) = katex 144 | 145 | // Initialize editor - formula module is built-in 146 | quill = editor.value?.initialize(Quill) 147 | }) 148 | ``` 149 | 150 | **Example with custom formats/blots:** 151 | 152 | ```typescript 153 | import Quill from 'quill/core' 154 | import { type BlotConstructor } from 'parchment' 155 | 156 | const Inline = Quill.import('blots/inline') as BlotConstructor 157 | 158 | // Create custom blot 159 | class CustomBlot extends Inline { 160 | static blotName = 'custom' 161 | static tagName = 'span' 162 | static className = 'my-custom-class' 163 | } 164 | 165 | // Register before initialization 166 | Quill.register(CustomBlot) 167 | 168 | onMounted(() => { 169 | quill = editor.value?.initialize(Quill) 170 | }) 171 | ``` 172 | 173 | ### 2. Content Formats 174 | 175 | vue-quilly supports two content formats: 176 | 177 | **HTML Format (v-model):** 178 | ```vue 179 | 180 | ``` 181 | 182 | **Delta Format (Quill API):** 183 | ```typescript 184 | import { Delta } from 'quill/core' 185 | 186 | quill.setContents( 187 | new Delta() 188 | .insert('Hello ') 189 | .insert('World', { bold: true }) 190 | ) 191 | ``` 192 | 193 | ### 3. Configuration 194 | 195 | Configure the editor using Quill's options: 196 | 197 | ```typescript 198 | const options = { 199 | theme: 'snow', // or 'bubble' 200 | placeholder: 'Write...', 201 | readOnly: false, 202 | modules: { 203 | toolbar: [/* ... */] // Customize toolbar 204 | } 205 | } 206 | ``` 207 | 208 | ### 4. Event Handling 209 | 210 | Listen to editor events: 211 | 212 | ```vue 213 | 219 | ``` 220 | 221 | ## Next Steps 222 | 223 | Now that you understand the basics, continue with: 224 | 225 | 1. **[Installation](/guide/installation)** - Set up vue-quilly in your project 226 | 2. **[Basic Usage](/guide/basic-usage)** - Learn common patterns and features 227 | 3. **[Examples](/examples/)** - See practical implementations 228 | 4. **[API Reference](/api/component)** - Detailed API documentation 229 | 230 | ## Learning Resources 231 | 232 | - [Quill Documentation](https://quilljs.com/docs/) - Official Quill docs 233 | - [Live Demo](https://vue-quilly.vercel.app/) - Try vue-quilly online 234 | - [GitHub Repository](https://github.com/alekswebnet/vue-quilly) - Source code and issues 235 | - [Demo Source](https://github.com/alekswebnet/vue-quilly/tree/main/demo) - Example implementations 236 | 237 | ## Browser Support 238 | 239 | vue-quilly supports the same browsers as Vue 3 and Quill v2: 240 | 241 | - Chrome (latest) 242 | - Firefox (latest) 243 | - Safari (latest) 244 | - Edge (latest) 245 | 246 | ## Requirements 247 | 248 | - Vue 3.0+ 249 | - Quill 2.0+ 250 | 251 | ## Get Help 252 | 253 | If you need help: 254 | 255 | - 📖 Check the [API documentation](/api/component) 256 | - 💡 Browse [examples](/examples/) 257 | - 🐛 Report issues on [GitHub](https://github.com/alekswebnet/vue-quilly/issues) 258 | 259 | Ready to install? Head to the [Installation guide](/guide/installation)! 260 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/vue.js: -------------------------------------------------------------------------------- 1 | import { 2 | BaseTransition, 3 | BaseTransitionPropsValidators, 4 | Comment, 5 | DeprecationTypes, 6 | EffectScope, 7 | ErrorCodes, 8 | ErrorTypeStrings, 9 | Fragment, 10 | KeepAlive, 11 | ReactiveEffect, 12 | Static, 13 | Suspense, 14 | Teleport, 15 | Text, 16 | TrackOpTypes, 17 | Transition, 18 | TransitionGroup, 19 | TriggerOpTypes, 20 | VueElement, 21 | assertNumber, 22 | callWithAsyncErrorHandling, 23 | callWithErrorHandling, 24 | camelize, 25 | capitalize, 26 | cloneVNode, 27 | compatUtils, 28 | compile, 29 | computed, 30 | createApp, 31 | createBaseVNode, 32 | createBlock, 33 | createCommentVNode, 34 | createElementBlock, 35 | createHydrationRenderer, 36 | createPropsRestProxy, 37 | createRenderer, 38 | createSSRApp, 39 | createSlots, 40 | createStaticVNode, 41 | createTextVNode, 42 | createVNode, 43 | customRef, 44 | defineAsyncComponent, 45 | defineComponent, 46 | defineCustomElement, 47 | defineEmits, 48 | defineExpose, 49 | defineModel, 50 | defineOptions, 51 | defineProps, 52 | defineSSRCustomElement, 53 | defineSlots, 54 | devtools, 55 | effect, 56 | effectScope, 57 | getCurrentInstance, 58 | getCurrentScope, 59 | getCurrentWatcher, 60 | getTransitionRawChildren, 61 | guardReactiveProps, 62 | h, 63 | handleError, 64 | hasInjectionContext, 65 | hydrate, 66 | hydrateOnIdle, 67 | hydrateOnInteraction, 68 | hydrateOnMediaQuery, 69 | hydrateOnVisible, 70 | initCustomFormatter, 71 | initDirectivesForSSR, 72 | inject, 73 | isMemoSame, 74 | isProxy, 75 | isReactive, 76 | isReadonly, 77 | isRef, 78 | isRuntimeOnly, 79 | isShallow, 80 | isVNode, 81 | markRaw, 82 | mergeDefaults, 83 | mergeModels, 84 | mergeProps, 85 | nextTick, 86 | nodeOps, 87 | normalizeClass, 88 | normalizeProps, 89 | normalizeStyle, 90 | onActivated, 91 | onBeforeMount, 92 | onBeforeUnmount, 93 | onBeforeUpdate, 94 | onDeactivated, 95 | onErrorCaptured, 96 | onMounted, 97 | onRenderTracked, 98 | onRenderTriggered, 99 | onScopeDispose, 100 | onServerPrefetch, 101 | onUnmounted, 102 | onUpdated, 103 | onWatcherCleanup, 104 | openBlock, 105 | patchProp, 106 | popScopeId, 107 | provide, 108 | proxyRefs, 109 | pushScopeId, 110 | queuePostFlushCb, 111 | reactive, 112 | readonly, 113 | ref, 114 | registerRuntimeCompiler, 115 | render, 116 | renderList, 117 | renderSlot, 118 | resolveComponent, 119 | resolveDirective, 120 | resolveDynamicComponent, 121 | resolveFilter, 122 | resolveTransitionHooks, 123 | setBlockTracking, 124 | setDevtoolsHook, 125 | setTransitionHooks, 126 | shallowReactive, 127 | shallowReadonly, 128 | shallowRef, 129 | ssrContextKey, 130 | ssrUtils, 131 | stop, 132 | toDisplayString, 133 | toHandlerKey, 134 | toHandlers, 135 | toRaw, 136 | toRef, 137 | toRefs, 138 | toValue, 139 | transformVNodeArgs, 140 | triggerRef, 141 | unref, 142 | useAttrs, 143 | useCssModule, 144 | useCssVars, 145 | useHost, 146 | useId, 147 | useModel, 148 | useSSRContext, 149 | useShadowRoot, 150 | useSlots, 151 | useTemplateRef, 152 | useTransitionState, 153 | vModelCheckbox, 154 | vModelDynamic, 155 | vModelRadio, 156 | vModelSelect, 157 | vModelText, 158 | vShow, 159 | version, 160 | warn, 161 | watch, 162 | watchEffect, 163 | watchPostEffect, 164 | watchSyncEffect, 165 | withAsyncContext, 166 | withCtx, 167 | withDefaults, 168 | withDirectives, 169 | withKeys, 170 | withMemo, 171 | withModifiers, 172 | withScopeId 173 | } from "./chunk-GKNP4RGU.js"; 174 | export { 175 | BaseTransition, 176 | BaseTransitionPropsValidators, 177 | Comment, 178 | DeprecationTypes, 179 | EffectScope, 180 | ErrorCodes, 181 | ErrorTypeStrings, 182 | Fragment, 183 | KeepAlive, 184 | ReactiveEffect, 185 | Static, 186 | Suspense, 187 | Teleport, 188 | Text, 189 | TrackOpTypes, 190 | Transition, 191 | TransitionGroup, 192 | TriggerOpTypes, 193 | VueElement, 194 | assertNumber, 195 | callWithAsyncErrorHandling, 196 | callWithErrorHandling, 197 | camelize, 198 | capitalize, 199 | cloneVNode, 200 | compatUtils, 201 | compile, 202 | computed, 203 | createApp, 204 | createBlock, 205 | createCommentVNode, 206 | createElementBlock, 207 | createBaseVNode as createElementVNode, 208 | createHydrationRenderer, 209 | createPropsRestProxy, 210 | createRenderer, 211 | createSSRApp, 212 | createSlots, 213 | createStaticVNode, 214 | createTextVNode, 215 | createVNode, 216 | customRef, 217 | defineAsyncComponent, 218 | defineComponent, 219 | defineCustomElement, 220 | defineEmits, 221 | defineExpose, 222 | defineModel, 223 | defineOptions, 224 | defineProps, 225 | defineSSRCustomElement, 226 | defineSlots, 227 | devtools, 228 | effect, 229 | effectScope, 230 | getCurrentInstance, 231 | getCurrentScope, 232 | getCurrentWatcher, 233 | getTransitionRawChildren, 234 | guardReactiveProps, 235 | h, 236 | handleError, 237 | hasInjectionContext, 238 | hydrate, 239 | hydrateOnIdle, 240 | hydrateOnInteraction, 241 | hydrateOnMediaQuery, 242 | hydrateOnVisible, 243 | initCustomFormatter, 244 | initDirectivesForSSR, 245 | inject, 246 | isMemoSame, 247 | isProxy, 248 | isReactive, 249 | isReadonly, 250 | isRef, 251 | isRuntimeOnly, 252 | isShallow, 253 | isVNode, 254 | markRaw, 255 | mergeDefaults, 256 | mergeModels, 257 | mergeProps, 258 | nextTick, 259 | nodeOps, 260 | normalizeClass, 261 | normalizeProps, 262 | normalizeStyle, 263 | onActivated, 264 | onBeforeMount, 265 | onBeforeUnmount, 266 | onBeforeUpdate, 267 | onDeactivated, 268 | onErrorCaptured, 269 | onMounted, 270 | onRenderTracked, 271 | onRenderTriggered, 272 | onScopeDispose, 273 | onServerPrefetch, 274 | onUnmounted, 275 | onUpdated, 276 | onWatcherCleanup, 277 | openBlock, 278 | patchProp, 279 | popScopeId, 280 | provide, 281 | proxyRefs, 282 | pushScopeId, 283 | queuePostFlushCb, 284 | reactive, 285 | readonly, 286 | ref, 287 | registerRuntimeCompiler, 288 | render, 289 | renderList, 290 | renderSlot, 291 | resolveComponent, 292 | resolveDirective, 293 | resolveDynamicComponent, 294 | resolveFilter, 295 | resolveTransitionHooks, 296 | setBlockTracking, 297 | setDevtoolsHook, 298 | setTransitionHooks, 299 | shallowReactive, 300 | shallowReadonly, 301 | shallowRef, 302 | ssrContextKey, 303 | ssrUtils, 304 | stop, 305 | toDisplayString, 306 | toHandlerKey, 307 | toHandlers, 308 | toRaw, 309 | toRef, 310 | toRefs, 311 | toValue, 312 | transformVNodeArgs, 313 | triggerRef, 314 | unref, 315 | useAttrs, 316 | useCssModule, 317 | useCssVars, 318 | useHost, 319 | useId, 320 | useModel, 321 | useSSRContext, 322 | useShadowRoot, 323 | useSlots, 324 | useTemplateRef, 325 | useTransitionState, 326 | vModelCheckbox, 327 | vModelDynamic, 328 | vModelRadio, 329 | vModelSelect, 330 | vModelText, 331 | vShow, 332 | version, 333 | warn, 334 | watch, 335 | watchEffect, 336 | watchPostEffect, 337 | watchSyncEffect, 338 | withAsyncContext, 339 | withCtx, 340 | withDefaults, 341 | withDirectives, 342 | withKeys, 343 | withMemo, 344 | withModifiers, 345 | withScopeId 346 | }; 347 | -------------------------------------------------------------------------------- /docs/guide/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | This guide covers installing vue-quilly in different environments and setups. 4 | 5 | ## Package Managers 6 | 7 | Install vue-quilly and Quill using your preferred package manager: 8 | 9 | ::: code-group 10 | 11 | ```bash [npm] 12 | npm install vue-quilly quill@2 13 | ``` 14 | 15 | ```bash [pnpm] 16 | pnpm add vue-quilly quill@2 17 | ``` 18 | 19 | ```bash [yarn] 20 | yarn add vue-quilly quill@2 21 | ``` 22 | 23 | ::: 24 | 25 | ## Import Methods 26 | 27 | ### Full Build 28 | 29 | Import the full Quill build if you need all modules: 30 | 31 | ```typescript 32 | import Quill from 'quill' 33 | import { QuillyEditor } from 'vue-quilly' 34 | ``` 35 | 36 | **Use when:** 37 | - You need most Quill features 38 | - Bundle size is not a primary concern 39 | - Rapid prototyping 40 | 41 | ### Core Build (Recommended) 42 | 43 | Import only the core build and register modules as needed: 44 | 45 | ```typescript 46 | import Quill from 'quill/core' 47 | import { QuillyEditor } from 'vue-quilly' 48 | ``` 49 | 50 | **Use when:** 51 | - You want minimal bundle size 52 | - You need specific features only 53 | - Building for production 54 | 55 | **Example with selective imports:** 56 | 57 | ```typescript 58 | import Quill from 'quill/core' 59 | import Toolbar from 'quill/modules/toolbar' 60 | import Bold from 'quill/formats/bold' 61 | import Italic from 'quill/formats/italic' 62 | import Header from 'quill/formats/header' 63 | 64 | Quill.register({ 65 | 'modules/toolbar': Toolbar, 66 | 'formats/bold': Bold, 67 | 'formats/italic': Italic, 68 | 'formats/header': Header 69 | }) 70 | ``` 71 | 72 | ## Styles 73 | 74 | Import Quill CSS styles based on your needs: 75 | 76 | ### Core Styles (Required) 77 | 78 | Always import the core styles: 79 | 80 | ```typescript 81 | import 'quill/dist/quill.core.css' 82 | ``` 83 | 84 | ### Theme Styles (Optional) 85 | 86 | Import a theme if you're using one: 87 | 88 | ::: code-group 89 | 90 | ```typescript [Snow Theme] 91 | import 'quill/dist/quill.snow.css' 92 | ``` 93 | 94 | ```typescript [Bubble Theme] 95 | import 'quill/dist/quill.bubble.css' 96 | ``` 97 | 98 | ::: 99 | 100 | **Complete import example:** 101 | 102 | ```typescript 103 | import { QuillyEditor } from 'vue-quilly' 104 | import Quill from 'quill' 105 | import 'quill/dist/quill.snow.css' 106 | ``` 107 | 108 | ## Component Registration 109 | 110 | ### Global Registration 111 | 112 | Register globally in your main app file: 113 | 114 | ```typescript 115 | // main.ts 116 | import { createApp } from 'vue' 117 | import { QuillyEditor } from 'vue-quilly' 118 | import App from './App.vue' 119 | 120 | const app = createApp(App) 121 | app.component('QuillyEditor', QuillyEditor) 122 | app.mount('#app') 123 | ``` 124 | 125 | Then use it anywhere in your app: 126 | 127 | ```vue 128 | 131 | ``` 132 | 133 | ### Local Registration 134 | 135 | Import and register in individual components: 136 | 137 | ```vue 138 | 152 | 153 | 156 | ``` 157 | 158 | **Use local registration when:** 159 | - You only need the editor in specific components 160 | - You want to keep bundle size minimal with tree-shaking 161 | - You prefer explicit imports over global registration 162 | 163 | ### Type Declarations 164 | 165 | Types are automatically available when you import: 166 | 167 | ```typescript 168 | import type { QuillyEditor } from 'vue-quilly' 169 | import type { 170 | QuillOptions, 171 | Delta, 172 | Range 173 | } from 'quill/core' 174 | ``` 175 | 176 | ### Nuxt 3/4 177 | 178 | Use dynamic imports with `import.meta.client` check since Quill is browser-only: 179 | 180 | **Component (e.g., `components/Editor.vue`):** 181 | 182 | ```vue 183 | 213 | 214 | 217 | ``` 218 | 219 | **App/Page (e.g., `app.vue` or `pages/index.vue`):** 220 | 221 | ```vue 222 | 229 | ``` 230 | 231 | **Key points:** 232 | - Wrap the editor component in `` in your app/page, not in the component itself 233 | - Use `import.meta.client` check before dynamic import in the component 234 | - Import Quill dynamically in `onMounted` hook 235 | - Import types from `quill/core`: `import Quill, { Delta, Range } from 'quill/core'` 236 | 237 | See our [Nuxt example](https://github.com/alekswebnet/vue-quilly/tree/main/nuxt) for a complete implementation. 238 | 239 | ## Additional Modules 240 | 241 | ### KaTeX (for formulas) 242 | 243 | Install KaTeX for formula support: 244 | 245 | ```bash 246 | pnpm add katex 247 | ``` 248 | 249 | ```typescript 250 | import katex from 'katex' 251 | import 'katex/dist/katex.min.css' 252 | 253 | // Make KaTeX available globally 254 | (window.katex as typeof katex) = katex 255 | ``` 256 | 257 | ### Image Resize 258 | 259 | Install the image resize module: 260 | 261 | ```bash 262 | pnpm add quill-image-resize-module 263 | ``` 264 | 265 | ```typescript 266 | import 'quill-image-resize-module' 267 | 268 | const options = { 269 | modules: { 270 | imageResize: { 271 | modules: ['Resize', 'DisplaySize', 'Toolbar'] 272 | } 273 | } 274 | } 275 | ``` 276 | 277 | ## CDN Usage 278 | 279 | For quick prototyping or simple pages, use CDN: 280 | 281 | ```html 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 |
290 | 291 |
292 | 293 | 294 | 295 | 296 | 297 | 305 | 306 | 328 | 329 | 330 | ``` 331 | 332 | [Try on CodePen](https://codepen.io/redrobot753/pen/VwJwPLP) 333 | 334 | ## Troubleshooting 335 | 336 | ### Module Not Found 337 | 338 | If you get module not found errors: 339 | 340 | ```bash 341 | # Clear node_modules and reinstall 342 | rm -rf node_modules package-lock.json 343 | npm install 344 | ``` 345 | 346 | ## Next Steps 347 | 348 | - [Basic Usage](/guide/basic-usage) - Learn how to use the editor 349 | - [Examples](/examples/) - See practical implementations 350 | - [API Reference](/api/component) - Detailed documentation 351 | 352 | ## Version Compatibility 353 | 354 | | vue-quilly | Quill | Vue | 355 | |-----------|-------|-----| 356 | | 1.x | 2.x | 3.x | 357 | 358 | Always use Quill v2 with vue-quilly. 359 | -------------------------------------------------------------------------------- /docs/api/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | The `QuillyEditor` component emits various events that allow you to respond to user interactions and content changes. 4 | 5 | ## Event List 6 | 7 | All events are fully typed for TypeScript support. 8 | 9 | ### update:modelValue 10 | 11 | Emitted when the editor content changes (for `v-model` binding). 12 | 13 | **Type:** 14 | ```typescript 15 | (value: string) => void 16 | ``` 17 | 18 | **Parameters:** 19 | - `value`: `string` - The updated HTML content 20 | 21 | **Example:** 22 | ```vue 23 | 30 | 31 | 37 | ``` 38 | 39 | --- 40 | 41 | ### text-change 42 | 43 | Emitted when the editor content changes (text, formatting, or both). 44 | 45 | **Type:** 46 | ```typescript 47 | ({ 48 | delta: Delta 49 | oldContent: Delta 50 | source: EmitterSource 51 | }) => void 52 | ``` 53 | 54 | **Parameters:** 55 | - `delta`: `Delta` - Change delta representing the modifications 56 | - `oldContent`: `Delta` - Previous editor content as Delta 57 | - `source`: `EmitterSource` - Source of the change (`'user'`, `'api'`, or `'silent'`) 58 | 59 | **Example:** 60 | ```vue 61 | 78 | 79 | 82 | ``` 83 | 84 | **Use cases:** 85 | - Track user edits 86 | - Implement auto-save functionality 87 | - Sync content to server 88 | - Trigger validation 89 | 90 | --- 91 | 92 | ### selection-change 93 | 94 | Emitted when the user selection (cursor position or text selection) changes. 95 | 96 | **Type:** 97 | ```typescript 98 | ({ 99 | range: Range 100 | oldRange: Range 101 | source: EmitterSource 102 | }) => void 103 | ``` 104 | 105 | **Parameters:** 106 | - `range`: `Range` - New selection range (`null` when editor loses focus) 107 | - `oldRange`: `Range` - Previous selection range 108 | - `source`: `EmitterSource` - Source of the change 109 | 110 | **Range type:** 111 | ```typescript 112 | interface Range { 113 | index: number // Starting position 114 | length: number // Length of selection (0 for cursor) 115 | } 116 | ``` 117 | 118 | **Example:** 119 | ```vue 120 | 143 | 144 | 147 | ``` 148 | 149 | **Use cases:** 150 | - Show custom formatting toolbar 151 | - Display character/word count for selection 152 | - Implement custom context menus 153 | - Track cursor position 154 | 155 | --- 156 | 157 | ### editor-change 158 | 159 | Emitted for both text and selection changes. Useful when you need to respond to any editor change. 160 | 161 | **Type:** 162 | ```typescript 163 | (eventName: 'text-change' | 'selection-change') => void 164 | ``` 165 | 166 | **Parameters:** 167 | - `eventName`: `'text-change' | 'selection-change'` - Type of change 168 | 169 | **Example:** 170 | ```vue 171 | 177 | 178 | 181 | ``` 182 | 183 | --- 184 | 185 | ### ready 186 | 187 | Emitted when the Quill editor is fully initialized and ready to use. 188 | 189 | **Type:** 190 | ```typescript 191 | (quill: Quill) => void 192 | ``` 193 | 194 | **Parameters:** 195 | - `quill`: `Quill` - The initialized Quill instance 196 | 197 | **Example:** 198 | ```vue 199 | 210 | 211 | 214 | ``` 215 | 216 | **Use cases:** 217 | - Initial editor configuration 218 | - Set initial focus 219 | - Load saved selection 220 | - Register custom formats 221 | 222 | --- 223 | 224 | ### focus 225 | 226 | Emitted when the editor gains focus. 227 | 228 | **Type:** 229 | ```typescript 230 | (quill: Quill) => void 231 | ``` 232 | 233 | **Parameters:** 234 | - `quill`: `Quill` - The Quill instance 235 | 236 | **Example:** 237 | ```vue 238 | 246 | 247 | 250 | ``` 251 | 252 | --- 253 | 254 | ### blur 255 | 256 | Emitted when the editor loses focus. 257 | 258 | **Type:** 259 | ```typescript 260 | (quill: Quill) => void 261 | ``` 262 | 263 | **Parameters:** 264 | - `quill`: `Quill` - The Quill instance 265 | 266 | **Example:** 267 | ```vue 268 | 276 | 277 | 280 | ``` 281 | 282 | **Use cases:** 283 | - Auto-save on blur 284 | - Hide custom toolbars 285 | - Validate content 286 | - Update preview 287 | 288 | --- 289 | 290 | ## Complete Example 291 | 292 | Example using all events together: 293 | 294 | ```vue 295 | 351 | 352 | 371 | ``` 372 | 373 | ## Event Sources 374 | 375 | The `source` parameter in events indicates how the change was triggered: 376 | 377 | | Source | Description | 378 | |--------|-------------| 379 | | `'user'` | User interaction (typing, clicking, etc.) | 380 | | `'api'` | Programmatic change via Quill API | 381 | | `'silent'` | Change that shouldn't trigger handlers | 382 | 383 | **Example:** 384 | ```typescript 385 | const onTextChange = ({ source }: { source: EmitterSource }) => { 386 | if (source === 'user') { 387 | // Only react to user changes 388 | autoSave() 389 | } 390 | } 391 | ``` 392 | 393 | ## See Also 394 | 395 | - [Component API](/api/component) - Props and methods 396 | - [TypeScript Types](/api/types) - Type definitions 397 | - [Examples](/examples/) - Practical examples 398 | -------------------------------------------------------------------------------- /docs/api-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | 5 | # Examples 6 | 7 | This page showcases different editor configurations and use cases built with vue-quilly. Each example demonstrates specific features and integration patterns. 8 | 9 | ## Snow Theme Editor 10 | 11 | The classic Quill editor with the Snow theme, featuring a full toolbar with all formatting options. 12 | 13 | **Features:** 14 | - Complete toolbar with all formatting options 15 | - Formula support with KaTeX 16 | - Image and video embedding 17 | - Font, size, and color customization 18 | - Headers, lists, and alignment 19 | 20 | ```vue 21 | 66 | 67 | 76 | ``` 77 | 78 | [View Source](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/DefaultEditorSnow.vue) 79 | 80 | --- 81 | 82 | ## Bubble Theme Editor 83 | 84 | Lightweight editor with the Bubble theme that shows toolbar on text selection. 85 | 86 | **Features:** 87 | - Medium-style inline toolbar 88 | - Appears on text selection 89 | - Same formatting capabilities as Snow theme 90 | - Cleaner, more minimalist interface 91 | 92 | ```vue 93 | 125 | 126 | 133 | ``` 134 | 135 | [View Source](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/DefaultEditorBubble.vue) 136 | 137 | --- 138 | 139 | ## Semantic HTML Editor 140 | 141 | Editor configured to output clean, semantic HTML without Quill-specific classes. 142 | 143 | **Features:** 144 | - Clean HTML output (e.g., `

` instead of `

`) 145 | - Better for SEO and accessibility 146 | - Easy content portability 147 | - Ideal for CMS and blog posts 148 | 149 | ```vue 150 | 177 | 178 | 186 | ``` 187 | 188 | **Output comparison:** 189 | 190 | ::: code-group 191 | 192 | ```html [Regular HTML] 193 |

Heading

194 |
    195 |
  • Item
  • 196 |
197 | ``` 198 | 199 | ```html [Semantic HTML] 200 |

Heading

201 |
    202 |
  • Item
  • 203 |
204 | ``` 205 | 206 | ::: 207 | 208 | [View Source](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/SemanticHTMLEditor.vue) 209 | 210 | --- 211 | 212 | ## Image Resize Editor 213 | 214 | Editor with image resize functionality using the `quill-image-resize-module`. 215 | 216 | **Features:** 217 | - Drag to resize images 218 | - Display size indicator 219 | - Resize handles on images 220 | - Image alignment toolbar 221 | 222 | ```vue 223 | 261 | 262 | 269 | ``` 270 | 271 | **Installation:** 272 | 273 | ```bash 274 | pnpm add quill-image-resize-module 275 | ``` 276 | 277 | [View Source](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/ImageResizeEditor.vue) 278 | 279 | --- 280 | 281 | ## Custom Editor (Core Build) 282 | 283 | Minimal custom editor using `quill/core` with only specific blots registered. 284 | 285 | **Features:** 286 | - Minimal bundle size (uses `quill/core`) 287 | - Custom blots (Bold, Italic, Headers) 288 | - External toolbar controls 289 | - Perfect for custom implementations 290 | 291 | ```vue 292 | 339 | 340 | å 354 | ``` 355 | 356 | [View Source](https://github.com/alekswebnet/vue-quilly/blob/main/demo/src/components/CustomEditor.vue) 357 | 358 | --- 359 | 360 | ## Live Demo 361 | 362 | Try all these examples live: [vue-quilly.vercel.app](https://vue-quilly.vercel.app/) 363 | 364 | ## More Examples 365 | 366 | - [Nuxt 4 Integration](https://github.com/alekswebnet/vue-quilly/tree/main/nuxt) - SSR setup with Nuxt 367 | - [Complete Demo Source](https://github.com/alekswebnet/vue-quilly/tree/main/demo) - All examples with styling 368 | -------------------------------------------------------------------------------- /docs/api/types.md: -------------------------------------------------------------------------------- 1 | # TypeScript Types 2 | 3 | vue-quilly provides full TypeScript support with comprehensive type definitions. 4 | 5 | ## Component Types 6 | 7 | ### QuillyEditor 8 | 9 | The main component type reference. 10 | 11 | ```typescript 12 | import type { QuillyEditor } from 'vue-quilly' 13 | 14 | const editor = ref>() 15 | ``` 16 | 17 | **Usage in component:** 18 | ```vue 19 | 31 | ``` 32 | 33 | --- 34 | 35 | ## Quill Core Types 36 | 37 | Import types from `quill/core` for Quill-specific types. 38 | 39 | ### QuillOptions 40 | 41 | Editor configuration options. 42 | 43 | ```typescript 44 | import type { QuillOptions } from 'quill/core' 45 | 46 | interface QuillOptions { 47 | theme?: string 48 | placeholder?: string 49 | readOnly?: boolean 50 | formats?: string[] 51 | bounds?: HTMLElement | string 52 | scrollingContainer?: HTMLElement | string 53 | modules?: { 54 | toolbar?: boolean | string | string[][] | ToolbarConfig 55 | clipboard?: ClipboardOptions 56 | keyboard?: KeyboardOptions 57 | history?: HistoryOptions 58 | [key: string]: any 59 | } 60 | } 61 | ``` 62 | 63 | **Example:** 64 | ```typescript 65 | const options: QuillOptions = { 66 | theme: 'snow', 67 | placeholder: 'Start writing...', 68 | readOnly: false, 69 | modules: { 70 | toolbar: [ 71 | ['bold', 'italic'], 72 | [{ header: [1, 2, false] }] 73 | ] 74 | } 75 | } 76 | ``` 77 | 78 | --- 79 | 80 | ### Delta 81 | 82 | Quill's document format representing content and changes. 83 | 84 | ```typescript 85 | import type { Delta } from 'quill/core' 86 | 87 | interface Delta { 88 | ops?: Op[] 89 | } 90 | 91 | interface Op { 92 | insert?: any 93 | delete?: number 94 | retain?: number 95 | attributes?: Record 96 | } 97 | ``` 98 | 99 | **Example:** 100 | ```typescript 101 | import { Delta } from 'quill/core' 102 | 103 | // Create a new Delta 104 | const delta = new Delta() 105 | .insert('Hello ', { bold: true }) 106 | .insert('World') 107 | .insert('\n', { header: 1 }) 108 | 109 | // Use in event handler 110 | const onTextChange = ({ delta }: { delta: Delta }) => { 111 | console.log('Changes:', delta.ops) 112 | } 113 | ``` 114 | 115 | **Delta operations:** 116 | - `insert` - Add content 117 | - `delete` - Remove content 118 | - `retain` - Keep content unchanged 119 | - `attributes` - Apply formatting 120 | 121 | --- 122 | 123 | ### Range 124 | 125 | Selection or cursor position in the editor. 126 | 127 | ```typescript 128 | import type { Range } from 'quill/core' 129 | 130 | interface Range { 131 | index: number // Starting position (0-based) 132 | length: number // Length of selection 133 | } 134 | ``` 135 | 136 | **Example:** 137 | ```typescript 138 | const onSelectionChange = ({ range }: { range: Range }) => { 139 | if (range) { 140 | if (range.length === 0) { 141 | console.log(`Cursor at position ${range.index}`) 142 | } else { 143 | console.log(`Selected from ${range.index} to ${range.index + range.length}`) 144 | } 145 | } else { 146 | console.log('No selection (editor lost focus)') 147 | } 148 | } 149 | 150 | // Set selection programmatically 151 | quill.setSelection(0, 10) // Select first 10 characters 152 | quill.setSelection(5, 0) // Place cursor at position 5 153 | ``` 154 | 155 | --- 156 | 157 | ### EmitterSource 158 | 159 | Source of an event or change. 160 | 161 | ```typescript 162 | import type { Sources as EmitterSource } from 'quill/core' 163 | 164 | type EmitterSource = 'user' | 'api' | 'silent' 165 | ``` 166 | 167 | **Values:** 168 | - `'user'` - User interaction (typing, clicking, pasting) 169 | - `'api'` - Programmatic change via Quill API 170 | - `'silent'` - Silent change (no event emission) 171 | 172 | **Example:** 173 | ```typescript 174 | const onTextChange = ({ source }: { source: EmitterSource }) => { 175 | switch (source) { 176 | case 'user': 177 | console.log('User edited content') 178 | autoSave() 179 | break 180 | case 'api': 181 | console.log('Programmatic change') 182 | break 183 | case 'silent': 184 | console.log('Silent update') 185 | break 186 | } 187 | } 188 | ``` 189 | 190 | --- 191 | 192 | ### BoundsStatic 193 | 194 | Position and dimensions of a selection or element. 195 | 196 | ```typescript 197 | interface BoundsStatic { 198 | left: number 199 | right: number 200 | top: number 201 | bottom: number 202 | height: number 203 | width: number 204 | } 205 | ``` 206 | 207 | **Example:** 208 | ```typescript 209 | const range = quill.getSelection() 210 | if (range) { 211 | const bounds = quill.getBounds(range.index, range.length) 212 | console.log('Selection bounds:', bounds) 213 | 214 | // Position a tooltip 215 | tooltip.style.left = `${bounds.left}px` 216 | tooltip.style.top = `${bounds.bottom}px` 217 | } 218 | ``` 219 | 220 | --- 221 | 222 | ## Event Handler Types 223 | 224 | Type-safe event handlers for all component events. 225 | 226 | ### TextChangeHandler 227 | 228 | ```typescript 229 | type TextChangeHandler = (params: { 230 | delta: Delta 231 | oldContent: Delta 232 | source: EmitterSource 233 | }) => void 234 | 235 | const onTextChange: TextChangeHandler = ({ delta, oldContent, source }) => { 236 | // Fully typed parameters 237 | } 238 | ``` 239 | 240 | ### SelectionChangeHandler 241 | 242 | ```typescript 243 | type SelectionChangeHandler = (params: { 244 | range: Range 245 | oldRange: Range 246 | source: EmitterSource 247 | }) => void 248 | 249 | const onSelectionChange: SelectionChangeHandler = ({ range, oldRange, source }) => { 250 | // Fully typed parameters 251 | } 252 | ``` 253 | 254 | ### EditorChangeHandler 255 | 256 | ```typescript 257 | type EditorChangeHandler = (eventName: 'text-change' | 'selection-change') => void 258 | 259 | const onEditorChange: EditorChangeHandler = (eventName) => { 260 | // Fully typed parameter 261 | } 262 | ``` 263 | 264 | ### ReadyHandler 265 | 266 | ```typescript 267 | import type Quill from 'quill' 268 | 269 | type ReadyHandler = (quill: Quill) => void 270 | 271 | const onReady: ReadyHandler = (quill) => { 272 | // Fully typed Quill instance 273 | } 274 | ``` 275 | 276 | --- 277 | 278 | ## Parchment Types 279 | 280 | For custom blots and formats (when using `quill/core`). 281 | 282 | ```typescript 283 | import type { BlotConstructor } from 'parchment' 284 | 285 | const Inline = Quill.import('blots/inline') as BlotConstructor 286 | const Block = Quill.import('blots/block') as BlotConstructor 287 | 288 | class CustomBlot extends Inline { 289 | static blotName = 'custom' 290 | static tagName = 'span' 291 | } 292 | ``` 293 | 294 | --- 295 | 296 | ## Complete TypeScript Example 297 | 298 | ```vue 299 | 392 | 393 | 406 | ``` 407 | 408 | --- 409 | 410 | ## Type Imports Reference 411 | 412 | Quick reference for all type imports: 413 | 414 | ```typescript 415 | // Component types 416 | import type { QuillyEditor } from 'vue-quilly' 417 | 418 | // Quill core types 419 | import type { 420 | QuillOptions, 421 | Delta, 422 | Range, 423 | Sources as EmitterSource, 424 | BoundsStatic, 425 | TextChangeHandler, 426 | SelectionChangeHandler 427 | } from 'quill/core' 428 | 429 | // Quill class 430 | import Quill from 'quill' 431 | 432 | // Parchment (for custom formats) 433 | import type { BlotConstructor } from 'parchment' 434 | ``` 435 | 436 | --- 437 | 438 | ## Type Guards 439 | 440 | Helper type guards for runtime type checking: 441 | 442 | ```typescript 443 | // Check if range exists (editor has focus) 444 | function hasSelection(range: Range | null): range is Range { 445 | return range !== null 446 | } 447 | 448 | // Check if selection is a cursor (length 0) 449 | function isCursor(range: Range): boolean { 450 | return range.length === 0 451 | } 452 | 453 | // Usage 454 | const onSelectionChange = ({ range }: { range: Range }) => { 455 | if (hasSelection(range)) { 456 | if (isCursor(range)) { 457 | console.log('Cursor at', range.index) 458 | } else { 459 | console.log('Selection length', range.length) 460 | } 461 | } 462 | } 463 | ``` 464 | 465 | --- 466 | 467 | ## See Also 468 | 469 | - [Component API](/api/component) - Props and methods 470 | - [Events](/api/events) - Event reference 471 | - [Quill API Documentation](https://quilljs.com/docs/api/) - Official Quill docs 472 | -------------------------------------------------------------------------------- /docs/guide/basic-usage.md: -------------------------------------------------------------------------------- 1 | # Basic Usage 2 | 3 | Learn the essential patterns and features for working with vue-quilly. 4 | 5 | ## Basic Setup 6 | 7 | The minimal setup requires three steps: 8 | 9 | 1. Import the component and styles 10 | 2. Initialize the editor 11 | 3. Use v-model for content binding 12 | 13 | ```vue 14 | 28 | 29 | 32 | ``` 33 | 34 | ## Content Management 35 | 36 | ### HTML Format 37 | 38 | Use `v-model` for HTML content: 39 | 40 | ```vue 41 | 44 | 45 | 51 | ``` 52 | 53 | ### Delta Format 54 | 55 | Use Quill's Delta format for programmatic content manipulation: 56 | 57 | ```typescript 58 | import { Delta } from 'quill/core' 59 | 60 | const editor = ref>() 61 | let quill: Quill | undefined 62 | 63 | onMounted(() => { 64 | quill = editor.value?.initialize(Quill)! 65 | 66 | // Set content using Delta 67 | quill.setContents( 68 | new Delta() 69 | .insert('Hello ') 70 | .insert('World', { bold: true }) 71 | .insert('\n') 72 | ) 73 | 74 | // Get content as Delta 75 | const delta = quill.getContents() 76 | console.log(delta) 77 | }) 78 | ``` 79 | 80 | ### Semantic HTML 81 | 82 | For clean, SEO-friendly HTML output: 83 | 84 | ```vue 85 | 89 | ``` 90 | 91 | This outputs `

` instead of `

`. 92 | 93 | ## Toolbar Configuration 94 | 95 | ### Predefined Toolbar 96 | 97 | Simple toolbar with common formats: 98 | 99 | ```typescript 100 | const options = { 101 | theme: 'snow', 102 | modules: { 103 | toolbar: [ 104 | ['bold', 'italic', 'underline', 'strike'], 105 | ['blockquote', 'code-block'], 106 | [{ 'header': 1 }, { 'header': 2 }], 107 | [{ 'list': 'ordered'}, { 'list': 'bullet' }], 108 | [{ 'script': 'sub'}, { 'script': 'super' }], 109 | [{ 'indent': '-1'}, { 'indent': '+1' }], 110 | [{ 'direction': 'rtl' }], 111 | [{ 'size': ['small', false, 'large', 'huge'] }], 112 | [{ 'header': [1, 2, 3, 4, 5, 6, false] }], 113 | [{ 'color': [] }, { 'background': [] }], 114 | [{ 'font': [] }], 115 | [{ 'align': [] }], 116 | ['clean'], 117 | ['link', 'image', 'video'] 118 | ] 119 | } 120 | } 121 | ``` 122 | 123 | ### Custom Toolbar 124 | 125 | Create a custom toolbar with specific controls: 126 | 127 | ```typescript 128 | const options = { 129 | theme: 'snow', 130 | modules: { 131 | toolbar: [ 132 | ['bold', 'italic'], // Simple formatting 133 | [{ header: [1, 2, false] }], // Headers 134 | ['link', 'image'] // Media 135 | ] 136 | } 137 | } 138 | ``` 139 | 140 | ### Toolbar Handlers 141 | 142 | Add custom handlers for toolbar buttons: 143 | 144 | ```vue 145 | 175 | ``` 176 | 177 | ### External Toolbar 178 | 179 | Place toolbar outside the editor: 180 | 181 | ```vue 182 | 194 | ``` 195 | 196 | ## Event Handling 197 | 198 | ### Text Changes 199 | 200 | React to content changes: 201 | 202 | ```vue 203 | 220 | 221 | 224 | ``` 225 | 226 | ### Selection Changes 227 | 228 | Track cursor position and text selection: 229 | 230 | ```vue 231 | 244 | 245 | 251 | ``` 252 | 253 | ### Focus and Blur 254 | 255 | Handle editor focus states: 256 | 257 | ```vue 258 | 271 | 272 | 279 | ``` 280 | 281 | ## Quill Instance Methods 282 | 283 | Access Quill's full API through the instance: 284 | 285 | ### Content Methods 286 | 287 | ```typescript 288 | // Get/Set text 289 | const text = quill.getText() 290 | quill.setText('New text') 291 | 292 | // Get/Set contents (Delta) 293 | const delta = quill.getContents() 294 | quill.setContents(delta) 295 | 296 | // Get length 297 | const length = quill.getLength() 298 | 299 | // Delete content 300 | quill.deleteText(0, 10) 301 | 302 | // Insert text 303 | quill.insertText(0, 'Hello', { bold: true }) 304 | ``` 305 | 306 | ### Formatting Methods 307 | 308 | ```typescript 309 | // Format text 310 | quill.formatText(0, 5, 'bold', true) 311 | quill.format('color', 'red') 312 | 313 | // Remove formatting 314 | quill.removeFormat(0, 10) 315 | 316 | // Get format 317 | const format = quill.getFormat() 318 | ``` 319 | 320 | ### Selection Methods 321 | 322 | ```typescript 323 | // Get selection 324 | const range = quill.getSelection() 325 | 326 | // Set selection 327 | quill.setSelection(0, 10) // Select characters 0-10 328 | quill.setSelection(5, 0) // Place cursor at position 5 329 | 330 | // Get bounds 331 | const bounds = quill.getBounds(0, 10) 332 | ``` 333 | 334 | ### Editor State 335 | 336 | ```typescript 337 | // Enable/Disable 338 | quill.enable(false) // Read-only 339 | quill.enable(true) // Editable 340 | 341 | // Check state 342 | const isEnabled = quill.isEnabled() 343 | 344 | // Focus 345 | quill.focus() 346 | quill.blur() 347 | ``` 348 | 349 | ## Common Patterns 350 | 351 | ### Auto-save 352 | 353 | ```vue 354 | 371 | 372 | 375 | ``` 376 | 377 | ### Character Counter 378 | 379 | ```vue 380 | 387 | 388 | 396 | ``` 397 | 398 | ### Word Counter 399 | 400 | ```vue 401 | 411 | 412 | 416 | ``` 417 | 418 | ### Read-only Mode 419 | 420 | ```vue 421 | 436 | 437 | 443 | ``` 444 | 445 | ### Placeholder 446 | 447 | ```typescript 448 | const options = { 449 | theme: 'snow', 450 | placeholder: 'Start writing your story...', 451 | modules: { 452 | toolbar: [['bold', 'italic']] 453 | } 454 | } 455 | ``` 456 | 457 | ### Initial Content 458 | 459 | ```vue 460 | 466 | 467 | 470 | ``` 471 | 472 | ## Themes 473 | 474 | ### Snow Theme 475 | 476 | Default theme with toolbar: 477 | 478 | ```typescript 479 | import 'quill/dist/quill.snow.css' 480 | 481 | const options = { 482 | theme: 'snow', 483 | modules: { 484 | toolbar: [['bold', 'italic']] 485 | } 486 | } 487 | ``` 488 | 489 | ### Bubble Theme 490 | 491 | Tooltip-based theme: 492 | 493 | ```typescript 494 | import 'quill/dist/quill.bubble.css' 495 | 496 | const options = { 497 | theme: 'bubble' 498 | } 499 | ``` 500 | 501 | ### No Theme (Core) 502 | 503 | Minimal styling: 504 | 505 | ```typescript 506 | import 'quill/dist/quill.core.css' 507 | 508 | const options = { 509 | // No theme specified 510 | modules: { 511 | toolbar: false 512 | } 513 | } 514 | ``` 515 | 516 | ## Validation 517 | 518 | ### Required Field 519 | 520 | ```vue 521 | 541 | 542 | 549 | ``` 550 | 551 | ### Min/Max Length 552 | 553 | ```typescript 554 | const validateLength = (html: string) => { 555 | const text = html.replace(/<[^>]*>/g, '').trim() 556 | const length = text.length 557 | 558 | if (length < 10) { 559 | return 'Minimum 10 characters required' 560 | } 561 | if (length > 1000) { 562 | return 'Maximum 1000 characters allowed' 563 | } 564 | return null 565 | } 566 | ``` 567 | 568 | ## Next Steps 569 | 570 | - [Examples](/examples/) - See complete implementations 571 | - [API Reference](/api/component) - Detailed documentation 572 | - [Events](/api/events) - All available events 573 | - [TypeScript Types](/api/types) - Type definitions 574 | 575 | ## Tips & Best Practices 576 | 577 | ### Do's 578 | 579 | ✅ **Initialize the editor in `onMounted`** 580 | - Ensures DOM is ready before Quill initialization 581 | - Prevents hydration issues in SSR 582 | 583 | ✅ **Use TypeScript for better development experience** 584 | - Full type safety with Quill API 585 | - Better IntelliSense and autocomplete 586 | - Catch errors at compile time 587 | 588 | ✅ **Handle events for auto-save and validation** 589 | - Use `@text-change` for auto-save 590 | - Use `@blur` for validation 591 | - Track changes with `source` parameter 592 | 593 | ✅ **Use semantic HTML for better SEO** 594 | - Enable `isSemanticHtmlModel` prop 595 | - Outputs clean HTML tags (`

` instead of `

`) 596 | - Better for accessibility and search engines 597 | 598 | ### Don'ts 599 | 600 | ❌ **Don't initialize before component mount** 601 | ```vue 602 | // ❌ Bad - DOM not ready yet 603 | const editor = ref>() 604 | let quill: Quill | undefined 605 | quill = editor.value?.initialize(Quill) 606 | 607 | // ✅ Good - Initialize after mount 608 | onMounted(() => { 609 | quill = editor.value?.initialize(Quill) 610 | }) 611 | ``` 612 | 613 | ❌ **Don't modify Quill instance outside Vue's reactivity** 614 | 615 | Quill maintains its own internal state. Directly modifying the Quill instance won't trigger Vue's reactivity system: 616 | 617 | ```vue 618 | // ❌ Bad - Changes won't be reactive 619 | quill.root.innerHTML = '

New content

' 620 | 621 | // ✅ Good - Use Quill's API or v-model 622 | quill.setContents(new Delta().insert('New content\n')) 623 | // or 624 | content.value = '

New content

' 625 | ``` 626 | 627 | **Why this matters:** 628 | - Direct DOM manipulation bypasses Quill's change detection 629 | - Vue won't know about the changes 630 | - Can cause inconsistencies between model and editor state 631 | - May break undo/redo functionality 632 | 633 | **Correct approach:** 634 | - Use Quill's API methods (`setText()`, `setContents()`, `insertText()`) 635 | - Update the `v-model` binding 636 | - Let vue-quilly handle the synchronization 637 | 638 | ❌ **Don't forget to clean up event listeners** 639 | ```vue 640 | 655 | ``` 656 | 657 | ❌ **Don't use inline HTML without sanitization** 658 | ```vue 659 | // ❌ Bad - XSS vulnerability 660 |
661 | 662 | // ✅ Good - Sanitize first 663 | import DOMPurify from 'dompurify' 664 | 665 | const safeContent = computed(() => DOMPurify.sanitize(content.value)) 666 |
667 | ``` 668 | -------------------------------------------------------------------------------- /demo/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@vueuse/core': 12 | specifier: ^14.1.0 13 | version: 14.1.0(vue@3.5.25(typescript@5.9.3)) 14 | katex: 15 | specifier: ^0.16.25 16 | version: 0.16.25 17 | parchment: 18 | specifier: ^3.0.0 19 | version: 3.0.0 20 | quill: 21 | specifier: ^2.0.3 22 | version: 2.0.3 23 | quill-image-resize-module: 24 | specifier: ^3.0.0 25 | version: 3.0.0 26 | vue: 27 | specifier: ^3.5.25 28 | version: 3.5.25(typescript@5.9.3) 29 | vue-markdown-render: 30 | specifier: ^2.3.0 31 | version: 2.3.0(vue@3.5.25(typescript@5.9.3)) 32 | vue-quilly: 33 | specifier: ^1.1.5 34 | version: 1.1.5(typescript@5.9.3) 35 | devDependencies: 36 | '@vitejs/plugin-vue': 37 | specifier: ^6.0.2 38 | version: 6.0.2(vite@7.2.6)(vue@3.5.25(typescript@5.9.3)) 39 | typescript: 40 | specifier: ^5.9.3 41 | version: 5.9.3 42 | vite: 43 | specifier: ^7.2.6 44 | version: 7.2.6 45 | vue-tsc: 46 | specifier: ^3.1.6 47 | version: 3.1.6(typescript@5.9.3) 48 | 49 | packages: 50 | 51 | '@babel/helper-string-parser@7.27.1': 52 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 53 | engines: {node: '>=6.9.0'} 54 | 55 | '@babel/helper-validator-identifier@7.28.5': 56 | resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 57 | engines: {node: '>=6.9.0'} 58 | 59 | '@babel/parser@7.28.5': 60 | resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} 61 | engines: {node: '>=6.0.0'} 62 | hasBin: true 63 | 64 | '@babel/types@7.28.5': 65 | resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 66 | engines: {node: '>=6.9.0'} 67 | 68 | '@esbuild/aix-ppc64@0.25.12': 69 | resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} 70 | engines: {node: '>=18'} 71 | cpu: [ppc64] 72 | os: [aix] 73 | 74 | '@esbuild/android-arm64@0.25.12': 75 | resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} 76 | engines: {node: '>=18'} 77 | cpu: [arm64] 78 | os: [android] 79 | 80 | '@esbuild/android-arm@0.25.12': 81 | resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} 82 | engines: {node: '>=18'} 83 | cpu: [arm] 84 | os: [android] 85 | 86 | '@esbuild/android-x64@0.25.12': 87 | resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} 88 | engines: {node: '>=18'} 89 | cpu: [x64] 90 | os: [android] 91 | 92 | '@esbuild/darwin-arm64@0.25.12': 93 | resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} 94 | engines: {node: '>=18'} 95 | cpu: [arm64] 96 | os: [darwin] 97 | 98 | '@esbuild/darwin-x64@0.25.12': 99 | resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} 100 | engines: {node: '>=18'} 101 | cpu: [x64] 102 | os: [darwin] 103 | 104 | '@esbuild/freebsd-arm64@0.25.12': 105 | resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} 106 | engines: {node: '>=18'} 107 | cpu: [arm64] 108 | os: [freebsd] 109 | 110 | '@esbuild/freebsd-x64@0.25.12': 111 | resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} 112 | engines: {node: '>=18'} 113 | cpu: [x64] 114 | os: [freebsd] 115 | 116 | '@esbuild/linux-arm64@0.25.12': 117 | resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} 118 | engines: {node: '>=18'} 119 | cpu: [arm64] 120 | os: [linux] 121 | 122 | '@esbuild/linux-arm@0.25.12': 123 | resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} 124 | engines: {node: '>=18'} 125 | cpu: [arm] 126 | os: [linux] 127 | 128 | '@esbuild/linux-ia32@0.25.12': 129 | resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} 130 | engines: {node: '>=18'} 131 | cpu: [ia32] 132 | os: [linux] 133 | 134 | '@esbuild/linux-loong64@0.25.12': 135 | resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} 136 | engines: {node: '>=18'} 137 | cpu: [loong64] 138 | os: [linux] 139 | 140 | '@esbuild/linux-mips64el@0.25.12': 141 | resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} 142 | engines: {node: '>=18'} 143 | cpu: [mips64el] 144 | os: [linux] 145 | 146 | '@esbuild/linux-ppc64@0.25.12': 147 | resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} 148 | engines: {node: '>=18'} 149 | cpu: [ppc64] 150 | os: [linux] 151 | 152 | '@esbuild/linux-riscv64@0.25.12': 153 | resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} 154 | engines: {node: '>=18'} 155 | cpu: [riscv64] 156 | os: [linux] 157 | 158 | '@esbuild/linux-s390x@0.25.12': 159 | resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} 160 | engines: {node: '>=18'} 161 | cpu: [s390x] 162 | os: [linux] 163 | 164 | '@esbuild/linux-x64@0.25.12': 165 | resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} 166 | engines: {node: '>=18'} 167 | cpu: [x64] 168 | os: [linux] 169 | 170 | '@esbuild/netbsd-arm64@0.25.12': 171 | resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} 172 | engines: {node: '>=18'} 173 | cpu: [arm64] 174 | os: [netbsd] 175 | 176 | '@esbuild/netbsd-x64@0.25.12': 177 | resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} 178 | engines: {node: '>=18'} 179 | cpu: [x64] 180 | os: [netbsd] 181 | 182 | '@esbuild/openbsd-arm64@0.25.12': 183 | resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} 184 | engines: {node: '>=18'} 185 | cpu: [arm64] 186 | os: [openbsd] 187 | 188 | '@esbuild/openbsd-x64@0.25.12': 189 | resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} 190 | engines: {node: '>=18'} 191 | cpu: [x64] 192 | os: [openbsd] 193 | 194 | '@esbuild/openharmony-arm64@0.25.12': 195 | resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} 196 | engines: {node: '>=18'} 197 | cpu: [arm64] 198 | os: [openharmony] 199 | 200 | '@esbuild/sunos-x64@0.25.12': 201 | resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} 202 | engines: {node: '>=18'} 203 | cpu: [x64] 204 | os: [sunos] 205 | 206 | '@esbuild/win32-arm64@0.25.12': 207 | resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} 208 | engines: {node: '>=18'} 209 | cpu: [arm64] 210 | os: [win32] 211 | 212 | '@esbuild/win32-ia32@0.25.12': 213 | resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} 214 | engines: {node: '>=18'} 215 | cpu: [ia32] 216 | os: [win32] 217 | 218 | '@esbuild/win32-x64@0.25.12': 219 | resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} 220 | engines: {node: '>=18'} 221 | cpu: [x64] 222 | os: [win32] 223 | 224 | '@jridgewell/sourcemap-codec@1.5.5': 225 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 226 | 227 | '@rolldown/pluginutils@1.0.0-beta.50': 228 | resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} 229 | 230 | '@rollup/rollup-android-arm-eabi@4.53.3': 231 | resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} 232 | cpu: [arm] 233 | os: [android] 234 | 235 | '@rollup/rollup-android-arm64@4.53.3': 236 | resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} 237 | cpu: [arm64] 238 | os: [android] 239 | 240 | '@rollup/rollup-darwin-arm64@4.53.3': 241 | resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} 242 | cpu: [arm64] 243 | os: [darwin] 244 | 245 | '@rollup/rollup-darwin-x64@4.53.3': 246 | resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} 247 | cpu: [x64] 248 | os: [darwin] 249 | 250 | '@rollup/rollup-freebsd-arm64@4.53.3': 251 | resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} 252 | cpu: [arm64] 253 | os: [freebsd] 254 | 255 | '@rollup/rollup-freebsd-x64@4.53.3': 256 | resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} 257 | cpu: [x64] 258 | os: [freebsd] 259 | 260 | '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 261 | resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} 262 | cpu: [arm] 263 | os: [linux] 264 | 265 | '@rollup/rollup-linux-arm-musleabihf@4.53.3': 266 | resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} 267 | cpu: [arm] 268 | os: [linux] 269 | 270 | '@rollup/rollup-linux-arm64-gnu@4.53.3': 271 | resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} 272 | cpu: [arm64] 273 | os: [linux] 274 | 275 | '@rollup/rollup-linux-arm64-musl@4.53.3': 276 | resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} 277 | cpu: [arm64] 278 | os: [linux] 279 | 280 | '@rollup/rollup-linux-loong64-gnu@4.53.3': 281 | resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} 282 | cpu: [loong64] 283 | os: [linux] 284 | 285 | '@rollup/rollup-linux-ppc64-gnu@4.53.3': 286 | resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} 287 | cpu: [ppc64] 288 | os: [linux] 289 | 290 | '@rollup/rollup-linux-riscv64-gnu@4.53.3': 291 | resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} 292 | cpu: [riscv64] 293 | os: [linux] 294 | 295 | '@rollup/rollup-linux-riscv64-musl@4.53.3': 296 | resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} 297 | cpu: [riscv64] 298 | os: [linux] 299 | 300 | '@rollup/rollup-linux-s390x-gnu@4.53.3': 301 | resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} 302 | cpu: [s390x] 303 | os: [linux] 304 | 305 | '@rollup/rollup-linux-x64-gnu@4.53.3': 306 | resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} 307 | cpu: [x64] 308 | os: [linux] 309 | 310 | '@rollup/rollup-linux-x64-musl@4.53.3': 311 | resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} 312 | cpu: [x64] 313 | os: [linux] 314 | 315 | '@rollup/rollup-openharmony-arm64@4.53.3': 316 | resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} 317 | cpu: [arm64] 318 | os: [openharmony] 319 | 320 | '@rollup/rollup-win32-arm64-msvc@4.53.3': 321 | resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} 322 | cpu: [arm64] 323 | os: [win32] 324 | 325 | '@rollup/rollup-win32-ia32-msvc@4.53.3': 326 | resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} 327 | cpu: [ia32] 328 | os: [win32] 329 | 330 | '@rollup/rollup-win32-x64-gnu@4.53.3': 331 | resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} 332 | cpu: [x64] 333 | os: [win32] 334 | 335 | '@rollup/rollup-win32-x64-msvc@4.53.3': 336 | resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} 337 | cpu: [x64] 338 | os: [win32] 339 | 340 | '@types/estree@1.0.8': 341 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 342 | 343 | '@types/web-bluetooth@0.0.21': 344 | resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} 345 | 346 | '@vitejs/plugin-vue@6.0.2': 347 | resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==} 348 | engines: {node: ^20.19.0 || >=22.12.0} 349 | peerDependencies: 350 | vite: ^5.0.0 || ^6.0.0 || ^7.0.0 351 | vue: ^3.2.25 352 | 353 | '@volar/language-core@2.4.26': 354 | resolution: {integrity: sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==} 355 | 356 | '@volar/source-map@2.4.26': 357 | resolution: {integrity: sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==} 358 | 359 | '@volar/typescript@2.4.26': 360 | resolution: {integrity: sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==} 361 | 362 | '@vue/compiler-core@3.5.25': 363 | resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==} 364 | 365 | '@vue/compiler-dom@3.5.25': 366 | resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==} 367 | 368 | '@vue/compiler-sfc@3.5.25': 369 | resolution: {integrity: sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==} 370 | 371 | '@vue/compiler-ssr@3.5.25': 372 | resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==} 373 | 374 | '@vue/language-core@3.1.6': 375 | resolution: {integrity: sha512-F3BIvDVyyj+6Sgl9Ev9zsb/DJ48rrH2EiI5NnIEpJKo7Yk8v0n2QjfG7/RYyFhYSMOJcsf6aAt5hx4JaNbhKbg==} 376 | peerDependencies: 377 | typescript: '*' 378 | peerDependenciesMeta: 379 | typescript: 380 | optional: true 381 | 382 | '@vue/reactivity@3.5.25': 383 | resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==} 384 | 385 | '@vue/runtime-core@3.5.25': 386 | resolution: {integrity: sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==} 387 | 388 | '@vue/runtime-dom@3.5.25': 389 | resolution: {integrity: sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==} 390 | 391 | '@vue/server-renderer@3.5.25': 392 | resolution: {integrity: sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==} 393 | peerDependencies: 394 | vue: 3.5.25 395 | 396 | '@vue/shared@3.5.25': 397 | resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==} 398 | 399 | '@vueuse/core@14.1.0': 400 | resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==} 401 | peerDependencies: 402 | vue: ^3.5.0 403 | 404 | '@vueuse/metadata@14.1.0': 405 | resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==} 406 | 407 | '@vueuse/shared@14.1.0': 408 | resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==} 409 | peerDependencies: 410 | vue: ^3.5.0 411 | 412 | alien-signals@3.1.1: 413 | resolution: {integrity: sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==} 414 | 415 | argparse@2.0.1: 416 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 417 | 418 | call-bind-apply-helpers@1.0.2: 419 | resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 420 | engines: {node: '>= 0.4'} 421 | 422 | call-bind@1.0.8: 423 | resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} 424 | engines: {node: '>= 0.4'} 425 | 426 | call-bound@1.0.4: 427 | resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} 428 | engines: {node: '>= 0.4'} 429 | 430 | clone@2.1.2: 431 | resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} 432 | engines: {node: '>=0.8'} 433 | 434 | commander@8.3.0: 435 | resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} 436 | engines: {node: '>= 12'} 437 | 438 | csstype@3.2.3: 439 | resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} 440 | 441 | deep-equal@1.1.2: 442 | resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==} 443 | engines: {node: '>= 0.4'} 444 | 445 | define-data-property@1.1.4: 446 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 447 | engines: {node: '>= 0.4'} 448 | 449 | define-properties@1.2.1: 450 | resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} 451 | engines: {node: '>= 0.4'} 452 | 453 | dunder-proto@1.0.1: 454 | resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 455 | engines: {node: '>= 0.4'} 456 | 457 | entities@4.5.0: 458 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 459 | engines: {node: '>=0.12'} 460 | 461 | es-define-property@1.0.1: 462 | resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 463 | engines: {node: '>= 0.4'} 464 | 465 | es-errors@1.3.0: 466 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 467 | engines: {node: '>= 0.4'} 468 | 469 | es-object-atoms@1.1.1: 470 | resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 471 | engines: {node: '>= 0.4'} 472 | 473 | esbuild@0.25.12: 474 | resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} 475 | engines: {node: '>=18'} 476 | hasBin: true 477 | 478 | estree-walker@2.0.2: 479 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 480 | 481 | eventemitter3@2.0.3: 482 | resolution: {integrity: sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==} 483 | 484 | eventemitter3@5.0.1: 485 | resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} 486 | 487 | extend@3.0.2: 488 | resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} 489 | 490 | fast-diff@1.1.2: 491 | resolution: {integrity: sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==} 492 | 493 | fast-diff@1.3.0: 494 | resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} 495 | 496 | fdir@6.5.0: 497 | resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 498 | engines: {node: '>=12.0.0'} 499 | peerDependencies: 500 | picomatch: ^3 || ^4 501 | peerDependenciesMeta: 502 | picomatch: 503 | optional: true 504 | 505 | fsevents@2.3.3: 506 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 507 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 508 | os: [darwin] 509 | 510 | function-bind@1.1.2: 511 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 512 | 513 | functions-have-names@1.2.3: 514 | resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} 515 | 516 | get-intrinsic@1.3.0: 517 | resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 518 | engines: {node: '>= 0.4'} 519 | 520 | get-proto@1.0.1: 521 | resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 522 | engines: {node: '>= 0.4'} 523 | 524 | gopd@1.2.0: 525 | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 526 | engines: {node: '>= 0.4'} 527 | 528 | has-property-descriptors@1.0.2: 529 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 530 | 531 | has-symbols@1.1.0: 532 | resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 533 | engines: {node: '>= 0.4'} 534 | 535 | has-tostringtag@1.0.2: 536 | resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 537 | engines: {node: '>= 0.4'} 538 | 539 | hasown@2.0.2: 540 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 541 | engines: {node: '>= 0.4'} 542 | 543 | is-arguments@1.2.0: 544 | resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} 545 | engines: {node: '>= 0.4'} 546 | 547 | is-date-object@1.1.0: 548 | resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} 549 | engines: {node: '>= 0.4'} 550 | 551 | is-regex@1.2.1: 552 | resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} 553 | engines: {node: '>= 0.4'} 554 | 555 | katex@0.16.25: 556 | resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} 557 | hasBin: true 558 | 559 | linkify-it@5.0.0: 560 | resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} 561 | 562 | lodash-es@4.17.21: 563 | resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} 564 | 565 | lodash.clonedeep@4.5.0: 566 | resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} 567 | 568 | lodash.isequal@4.5.0: 569 | resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} 570 | deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. 571 | 572 | lodash@4.17.21: 573 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 574 | 575 | magic-string@0.30.21: 576 | resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} 577 | 578 | markdown-it@14.1.0: 579 | resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} 580 | hasBin: true 581 | 582 | math-intrinsics@1.1.0: 583 | resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 584 | engines: {node: '>= 0.4'} 585 | 586 | mdurl@2.0.0: 587 | resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} 588 | 589 | muggle-string@0.4.1: 590 | resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} 591 | 592 | nanoid@3.3.11: 593 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 594 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 595 | hasBin: true 596 | 597 | object-is@1.1.6: 598 | resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} 599 | engines: {node: '>= 0.4'} 600 | 601 | object-keys@1.1.1: 602 | resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 603 | engines: {node: '>= 0.4'} 604 | 605 | parchment@1.1.4: 606 | resolution: {integrity: sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==} 607 | 608 | parchment@3.0.0: 609 | resolution: {integrity: sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==} 610 | 611 | path-browserify@1.0.1: 612 | resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 613 | 614 | picocolors@1.1.1: 615 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 616 | 617 | picomatch@4.0.3: 618 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 619 | engines: {node: '>=12'} 620 | 621 | postcss@8.5.6: 622 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 623 | engines: {node: ^10 || ^12 || >=14} 624 | 625 | punycode.js@2.3.1: 626 | resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} 627 | engines: {node: '>=6'} 628 | 629 | quill-delta@3.6.3: 630 | resolution: {integrity: sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==} 631 | engines: {node: '>=0.10'} 632 | 633 | quill-delta@5.1.0: 634 | resolution: {integrity: sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==} 635 | engines: {node: '>= 12.0.0'} 636 | 637 | quill-image-resize-module@3.0.0: 638 | resolution: {integrity: sha512-1TZBnUxU/WIx5dPyVjQ9yN7C6mLZSp04HyWBEMqT320DIq4MW4JgzlOPDZX5ZpBM3bU6sacU4kTLUc8VgYQZYw==} 639 | 640 | quill@1.3.7: 641 | resolution: {integrity: sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==} 642 | 643 | quill@2.0.3: 644 | resolution: {integrity: sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==} 645 | engines: {npm: '>=8.2.3'} 646 | 647 | raw-loader@0.5.1: 648 | resolution: {integrity: sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==} 649 | 650 | regexp.prototype.flags@1.5.4: 651 | resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} 652 | engines: {node: '>= 0.4'} 653 | 654 | rollup@4.53.3: 655 | resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} 656 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 657 | hasBin: true 658 | 659 | set-function-length@1.2.2: 660 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 661 | engines: {node: '>= 0.4'} 662 | 663 | set-function-name@2.0.2: 664 | resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} 665 | engines: {node: '>= 0.4'} 666 | 667 | source-map-js@1.2.1: 668 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 669 | engines: {node: '>=0.10.0'} 670 | 671 | tinyglobby@0.2.15: 672 | resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 673 | engines: {node: '>=12.0.0'} 674 | 675 | typescript@5.9.3: 676 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 677 | engines: {node: '>=14.17'} 678 | hasBin: true 679 | 680 | uc.micro@2.1.0: 681 | resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} 682 | 683 | vite@7.2.6: 684 | resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} 685 | engines: {node: ^20.19.0 || >=22.12.0} 686 | hasBin: true 687 | peerDependencies: 688 | '@types/node': ^20.19.0 || >=22.12.0 689 | jiti: '>=1.21.0' 690 | less: ^4.0.0 691 | lightningcss: ^1.21.0 692 | sass: ^1.70.0 693 | sass-embedded: ^1.70.0 694 | stylus: '>=0.54.8' 695 | sugarss: ^5.0.0 696 | terser: ^5.16.0 697 | tsx: ^4.8.1 698 | yaml: ^2.4.2 699 | peerDependenciesMeta: 700 | '@types/node': 701 | optional: true 702 | jiti: 703 | optional: true 704 | less: 705 | optional: true 706 | lightningcss: 707 | optional: true 708 | sass: 709 | optional: true 710 | sass-embedded: 711 | optional: true 712 | stylus: 713 | optional: true 714 | sugarss: 715 | optional: true 716 | terser: 717 | optional: true 718 | tsx: 719 | optional: true 720 | yaml: 721 | optional: true 722 | 723 | vscode-uri@3.1.0: 724 | resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} 725 | 726 | vue-markdown-render@2.3.0: 727 | resolution: {integrity: sha512-ZWVVKba8t0tKBlaUGaWmNynIk38gE7Bt3psC/iN2NsqpdGY15VGfBeBvF0A8cEmwHnjNVJo2IzUUqkhhfldhtg==} 728 | peerDependencies: 729 | vue: ^3.3.4 730 | 731 | vue-quilly@1.1.5: 732 | resolution: {integrity: sha512-1y8ZOw9U3w/LZE+Gph4QcGH5Zi7mnZdnCr6g+iKop7xDQaT7re9bKhpJV5kmbnAr6O2B3SJlSS5AMZKoBH1QOA==} 733 | 734 | vue-tsc@3.1.6: 735 | resolution: {integrity: sha512-h5mMNGIDI+WMZxTeuYcpfSeDtBIiHXAg3qsrt65H4vcFTYmuM1THNHMzlnDvD8kX0fwLuf6auxWP340bH/zcpw==} 736 | hasBin: true 737 | peerDependencies: 738 | typescript: '>=5.0.0' 739 | 740 | vue@3.5.25: 741 | resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==} 742 | peerDependencies: 743 | typescript: '*' 744 | peerDependenciesMeta: 745 | typescript: 746 | optional: true 747 | 748 | snapshots: 749 | 750 | '@babel/helper-string-parser@7.27.1': {} 751 | 752 | '@babel/helper-validator-identifier@7.28.5': {} 753 | 754 | '@babel/parser@7.28.5': 755 | dependencies: 756 | '@babel/types': 7.28.5 757 | 758 | '@babel/types@7.28.5': 759 | dependencies: 760 | '@babel/helper-string-parser': 7.27.1 761 | '@babel/helper-validator-identifier': 7.28.5 762 | 763 | '@esbuild/aix-ppc64@0.25.12': 764 | optional: true 765 | 766 | '@esbuild/android-arm64@0.25.12': 767 | optional: true 768 | 769 | '@esbuild/android-arm@0.25.12': 770 | optional: true 771 | 772 | '@esbuild/android-x64@0.25.12': 773 | optional: true 774 | 775 | '@esbuild/darwin-arm64@0.25.12': 776 | optional: true 777 | 778 | '@esbuild/darwin-x64@0.25.12': 779 | optional: true 780 | 781 | '@esbuild/freebsd-arm64@0.25.12': 782 | optional: true 783 | 784 | '@esbuild/freebsd-x64@0.25.12': 785 | optional: true 786 | 787 | '@esbuild/linux-arm64@0.25.12': 788 | optional: true 789 | 790 | '@esbuild/linux-arm@0.25.12': 791 | optional: true 792 | 793 | '@esbuild/linux-ia32@0.25.12': 794 | optional: true 795 | 796 | '@esbuild/linux-loong64@0.25.12': 797 | optional: true 798 | 799 | '@esbuild/linux-mips64el@0.25.12': 800 | optional: true 801 | 802 | '@esbuild/linux-ppc64@0.25.12': 803 | optional: true 804 | 805 | '@esbuild/linux-riscv64@0.25.12': 806 | optional: true 807 | 808 | '@esbuild/linux-s390x@0.25.12': 809 | optional: true 810 | 811 | '@esbuild/linux-x64@0.25.12': 812 | optional: true 813 | 814 | '@esbuild/netbsd-arm64@0.25.12': 815 | optional: true 816 | 817 | '@esbuild/netbsd-x64@0.25.12': 818 | optional: true 819 | 820 | '@esbuild/openbsd-arm64@0.25.12': 821 | optional: true 822 | 823 | '@esbuild/openbsd-x64@0.25.12': 824 | optional: true 825 | 826 | '@esbuild/openharmony-arm64@0.25.12': 827 | optional: true 828 | 829 | '@esbuild/sunos-x64@0.25.12': 830 | optional: true 831 | 832 | '@esbuild/win32-arm64@0.25.12': 833 | optional: true 834 | 835 | '@esbuild/win32-ia32@0.25.12': 836 | optional: true 837 | 838 | '@esbuild/win32-x64@0.25.12': 839 | optional: true 840 | 841 | '@jridgewell/sourcemap-codec@1.5.5': {} 842 | 843 | '@rolldown/pluginutils@1.0.0-beta.50': {} 844 | 845 | '@rollup/rollup-android-arm-eabi@4.53.3': 846 | optional: true 847 | 848 | '@rollup/rollup-android-arm64@4.53.3': 849 | optional: true 850 | 851 | '@rollup/rollup-darwin-arm64@4.53.3': 852 | optional: true 853 | 854 | '@rollup/rollup-darwin-x64@4.53.3': 855 | optional: true 856 | 857 | '@rollup/rollup-freebsd-arm64@4.53.3': 858 | optional: true 859 | 860 | '@rollup/rollup-freebsd-x64@4.53.3': 861 | optional: true 862 | 863 | '@rollup/rollup-linux-arm-gnueabihf@4.53.3': 864 | optional: true 865 | 866 | '@rollup/rollup-linux-arm-musleabihf@4.53.3': 867 | optional: true 868 | 869 | '@rollup/rollup-linux-arm64-gnu@4.53.3': 870 | optional: true 871 | 872 | '@rollup/rollup-linux-arm64-musl@4.53.3': 873 | optional: true 874 | 875 | '@rollup/rollup-linux-loong64-gnu@4.53.3': 876 | optional: true 877 | 878 | '@rollup/rollup-linux-ppc64-gnu@4.53.3': 879 | optional: true 880 | 881 | '@rollup/rollup-linux-riscv64-gnu@4.53.3': 882 | optional: true 883 | 884 | '@rollup/rollup-linux-riscv64-musl@4.53.3': 885 | optional: true 886 | 887 | '@rollup/rollup-linux-s390x-gnu@4.53.3': 888 | optional: true 889 | 890 | '@rollup/rollup-linux-x64-gnu@4.53.3': 891 | optional: true 892 | 893 | '@rollup/rollup-linux-x64-musl@4.53.3': 894 | optional: true 895 | 896 | '@rollup/rollup-openharmony-arm64@4.53.3': 897 | optional: true 898 | 899 | '@rollup/rollup-win32-arm64-msvc@4.53.3': 900 | optional: true 901 | 902 | '@rollup/rollup-win32-ia32-msvc@4.53.3': 903 | optional: true 904 | 905 | '@rollup/rollup-win32-x64-gnu@4.53.3': 906 | optional: true 907 | 908 | '@rollup/rollup-win32-x64-msvc@4.53.3': 909 | optional: true 910 | 911 | '@types/estree@1.0.8': {} 912 | 913 | '@types/web-bluetooth@0.0.21': {} 914 | 915 | '@vitejs/plugin-vue@6.0.2(vite@7.2.6)(vue@3.5.25(typescript@5.9.3))': 916 | dependencies: 917 | '@rolldown/pluginutils': 1.0.0-beta.50 918 | vite: 7.2.6 919 | vue: 3.5.25(typescript@5.9.3) 920 | 921 | '@volar/language-core@2.4.26': 922 | dependencies: 923 | '@volar/source-map': 2.4.26 924 | 925 | '@volar/source-map@2.4.26': {} 926 | 927 | '@volar/typescript@2.4.26': 928 | dependencies: 929 | '@volar/language-core': 2.4.26 930 | path-browserify: 1.0.1 931 | vscode-uri: 3.1.0 932 | 933 | '@vue/compiler-core@3.5.25': 934 | dependencies: 935 | '@babel/parser': 7.28.5 936 | '@vue/shared': 3.5.25 937 | entities: 4.5.0 938 | estree-walker: 2.0.2 939 | source-map-js: 1.2.1 940 | 941 | '@vue/compiler-dom@3.5.25': 942 | dependencies: 943 | '@vue/compiler-core': 3.5.25 944 | '@vue/shared': 3.5.25 945 | 946 | '@vue/compiler-sfc@3.5.25': 947 | dependencies: 948 | '@babel/parser': 7.28.5 949 | '@vue/compiler-core': 3.5.25 950 | '@vue/compiler-dom': 3.5.25 951 | '@vue/compiler-ssr': 3.5.25 952 | '@vue/shared': 3.5.25 953 | estree-walker: 2.0.2 954 | magic-string: 0.30.21 955 | postcss: 8.5.6 956 | source-map-js: 1.2.1 957 | 958 | '@vue/compiler-ssr@3.5.25': 959 | dependencies: 960 | '@vue/compiler-dom': 3.5.25 961 | '@vue/shared': 3.5.25 962 | 963 | '@vue/language-core@3.1.6(typescript@5.9.3)': 964 | dependencies: 965 | '@volar/language-core': 2.4.26 966 | '@vue/compiler-dom': 3.5.25 967 | '@vue/shared': 3.5.25 968 | alien-signals: 3.1.1 969 | muggle-string: 0.4.1 970 | path-browserify: 1.0.1 971 | picomatch: 4.0.3 972 | optionalDependencies: 973 | typescript: 5.9.3 974 | 975 | '@vue/reactivity@3.5.25': 976 | dependencies: 977 | '@vue/shared': 3.5.25 978 | 979 | '@vue/runtime-core@3.5.25': 980 | dependencies: 981 | '@vue/reactivity': 3.5.25 982 | '@vue/shared': 3.5.25 983 | 984 | '@vue/runtime-dom@3.5.25': 985 | dependencies: 986 | '@vue/reactivity': 3.5.25 987 | '@vue/runtime-core': 3.5.25 988 | '@vue/shared': 3.5.25 989 | csstype: 3.2.3 990 | 991 | '@vue/server-renderer@3.5.25(vue@3.5.25(typescript@5.9.3))': 992 | dependencies: 993 | '@vue/compiler-ssr': 3.5.25 994 | '@vue/shared': 3.5.25 995 | vue: 3.5.25(typescript@5.9.3) 996 | 997 | '@vue/shared@3.5.25': {} 998 | 999 | '@vueuse/core@14.1.0(vue@3.5.25(typescript@5.9.3))': 1000 | dependencies: 1001 | '@types/web-bluetooth': 0.0.21 1002 | '@vueuse/metadata': 14.1.0 1003 | '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) 1004 | vue: 3.5.25(typescript@5.9.3) 1005 | 1006 | '@vueuse/metadata@14.1.0': {} 1007 | 1008 | '@vueuse/shared@14.1.0(vue@3.5.25(typescript@5.9.3))': 1009 | dependencies: 1010 | vue: 3.5.25(typescript@5.9.3) 1011 | 1012 | alien-signals@3.1.1: {} 1013 | 1014 | argparse@2.0.1: {} 1015 | 1016 | call-bind-apply-helpers@1.0.2: 1017 | dependencies: 1018 | es-errors: 1.3.0 1019 | function-bind: 1.1.2 1020 | 1021 | call-bind@1.0.8: 1022 | dependencies: 1023 | call-bind-apply-helpers: 1.0.2 1024 | es-define-property: 1.0.1 1025 | get-intrinsic: 1.3.0 1026 | set-function-length: 1.2.2 1027 | 1028 | call-bound@1.0.4: 1029 | dependencies: 1030 | call-bind-apply-helpers: 1.0.2 1031 | get-intrinsic: 1.3.0 1032 | 1033 | clone@2.1.2: {} 1034 | 1035 | commander@8.3.0: {} 1036 | 1037 | csstype@3.2.3: {} 1038 | 1039 | deep-equal@1.1.2: 1040 | dependencies: 1041 | is-arguments: 1.2.0 1042 | is-date-object: 1.1.0 1043 | is-regex: 1.2.1 1044 | object-is: 1.1.6 1045 | object-keys: 1.1.1 1046 | regexp.prototype.flags: 1.5.4 1047 | 1048 | define-data-property@1.1.4: 1049 | dependencies: 1050 | es-define-property: 1.0.1 1051 | es-errors: 1.3.0 1052 | gopd: 1.2.0 1053 | 1054 | define-properties@1.2.1: 1055 | dependencies: 1056 | define-data-property: 1.1.4 1057 | has-property-descriptors: 1.0.2 1058 | object-keys: 1.1.1 1059 | 1060 | dunder-proto@1.0.1: 1061 | dependencies: 1062 | call-bind-apply-helpers: 1.0.2 1063 | es-errors: 1.3.0 1064 | gopd: 1.2.0 1065 | 1066 | entities@4.5.0: {} 1067 | 1068 | es-define-property@1.0.1: {} 1069 | 1070 | es-errors@1.3.0: {} 1071 | 1072 | es-object-atoms@1.1.1: 1073 | dependencies: 1074 | es-errors: 1.3.0 1075 | 1076 | esbuild@0.25.12: 1077 | optionalDependencies: 1078 | '@esbuild/aix-ppc64': 0.25.12 1079 | '@esbuild/android-arm': 0.25.12 1080 | '@esbuild/android-arm64': 0.25.12 1081 | '@esbuild/android-x64': 0.25.12 1082 | '@esbuild/darwin-arm64': 0.25.12 1083 | '@esbuild/darwin-x64': 0.25.12 1084 | '@esbuild/freebsd-arm64': 0.25.12 1085 | '@esbuild/freebsd-x64': 0.25.12 1086 | '@esbuild/linux-arm': 0.25.12 1087 | '@esbuild/linux-arm64': 0.25.12 1088 | '@esbuild/linux-ia32': 0.25.12 1089 | '@esbuild/linux-loong64': 0.25.12 1090 | '@esbuild/linux-mips64el': 0.25.12 1091 | '@esbuild/linux-ppc64': 0.25.12 1092 | '@esbuild/linux-riscv64': 0.25.12 1093 | '@esbuild/linux-s390x': 0.25.12 1094 | '@esbuild/linux-x64': 0.25.12 1095 | '@esbuild/netbsd-arm64': 0.25.12 1096 | '@esbuild/netbsd-x64': 0.25.12 1097 | '@esbuild/openbsd-arm64': 0.25.12 1098 | '@esbuild/openbsd-x64': 0.25.12 1099 | '@esbuild/openharmony-arm64': 0.25.12 1100 | '@esbuild/sunos-x64': 0.25.12 1101 | '@esbuild/win32-arm64': 0.25.12 1102 | '@esbuild/win32-ia32': 0.25.12 1103 | '@esbuild/win32-x64': 0.25.12 1104 | 1105 | estree-walker@2.0.2: {} 1106 | 1107 | eventemitter3@2.0.3: {} 1108 | 1109 | eventemitter3@5.0.1: {} 1110 | 1111 | extend@3.0.2: {} 1112 | 1113 | fast-diff@1.1.2: {} 1114 | 1115 | fast-diff@1.3.0: {} 1116 | 1117 | fdir@6.5.0(picomatch@4.0.3): 1118 | optionalDependencies: 1119 | picomatch: 4.0.3 1120 | 1121 | fsevents@2.3.3: 1122 | optional: true 1123 | 1124 | function-bind@1.1.2: {} 1125 | 1126 | functions-have-names@1.2.3: {} 1127 | 1128 | get-intrinsic@1.3.0: 1129 | dependencies: 1130 | call-bind-apply-helpers: 1.0.2 1131 | es-define-property: 1.0.1 1132 | es-errors: 1.3.0 1133 | es-object-atoms: 1.1.1 1134 | function-bind: 1.1.2 1135 | get-proto: 1.0.1 1136 | gopd: 1.2.0 1137 | has-symbols: 1.1.0 1138 | hasown: 2.0.2 1139 | math-intrinsics: 1.1.0 1140 | 1141 | get-proto@1.0.1: 1142 | dependencies: 1143 | dunder-proto: 1.0.1 1144 | es-object-atoms: 1.1.1 1145 | 1146 | gopd@1.2.0: {} 1147 | 1148 | has-property-descriptors@1.0.2: 1149 | dependencies: 1150 | es-define-property: 1.0.1 1151 | 1152 | has-symbols@1.1.0: {} 1153 | 1154 | has-tostringtag@1.0.2: 1155 | dependencies: 1156 | has-symbols: 1.1.0 1157 | 1158 | hasown@2.0.2: 1159 | dependencies: 1160 | function-bind: 1.1.2 1161 | 1162 | is-arguments@1.2.0: 1163 | dependencies: 1164 | call-bound: 1.0.4 1165 | has-tostringtag: 1.0.2 1166 | 1167 | is-date-object@1.1.0: 1168 | dependencies: 1169 | call-bound: 1.0.4 1170 | has-tostringtag: 1.0.2 1171 | 1172 | is-regex@1.2.1: 1173 | dependencies: 1174 | call-bound: 1.0.4 1175 | gopd: 1.2.0 1176 | has-tostringtag: 1.0.2 1177 | hasown: 2.0.2 1178 | 1179 | katex@0.16.25: 1180 | dependencies: 1181 | commander: 8.3.0 1182 | 1183 | linkify-it@5.0.0: 1184 | dependencies: 1185 | uc.micro: 2.1.0 1186 | 1187 | lodash-es@4.17.21: {} 1188 | 1189 | lodash.clonedeep@4.5.0: {} 1190 | 1191 | lodash.isequal@4.5.0: {} 1192 | 1193 | lodash@4.17.21: {} 1194 | 1195 | magic-string@0.30.21: 1196 | dependencies: 1197 | '@jridgewell/sourcemap-codec': 1.5.5 1198 | 1199 | markdown-it@14.1.0: 1200 | dependencies: 1201 | argparse: 2.0.1 1202 | entities: 4.5.0 1203 | linkify-it: 5.0.0 1204 | mdurl: 2.0.0 1205 | punycode.js: 2.3.1 1206 | uc.micro: 2.1.0 1207 | 1208 | math-intrinsics@1.1.0: {} 1209 | 1210 | mdurl@2.0.0: {} 1211 | 1212 | muggle-string@0.4.1: {} 1213 | 1214 | nanoid@3.3.11: {} 1215 | 1216 | object-is@1.1.6: 1217 | dependencies: 1218 | call-bind: 1.0.8 1219 | define-properties: 1.2.1 1220 | 1221 | object-keys@1.1.1: {} 1222 | 1223 | parchment@1.1.4: {} 1224 | 1225 | parchment@3.0.0: {} 1226 | 1227 | path-browserify@1.0.1: {} 1228 | 1229 | picocolors@1.1.1: {} 1230 | 1231 | picomatch@4.0.3: {} 1232 | 1233 | postcss@8.5.6: 1234 | dependencies: 1235 | nanoid: 3.3.11 1236 | picocolors: 1.1.1 1237 | source-map-js: 1.2.1 1238 | 1239 | punycode.js@2.3.1: {} 1240 | 1241 | quill-delta@3.6.3: 1242 | dependencies: 1243 | deep-equal: 1.1.2 1244 | extend: 3.0.2 1245 | fast-diff: 1.1.2 1246 | 1247 | quill-delta@5.1.0: 1248 | dependencies: 1249 | fast-diff: 1.3.0 1250 | lodash.clonedeep: 4.5.0 1251 | lodash.isequal: 4.5.0 1252 | 1253 | quill-image-resize-module@3.0.0: 1254 | dependencies: 1255 | lodash: 4.17.21 1256 | quill: 1.3.7 1257 | raw-loader: 0.5.1 1258 | 1259 | quill@1.3.7: 1260 | dependencies: 1261 | clone: 2.1.2 1262 | deep-equal: 1.1.2 1263 | eventemitter3: 2.0.3 1264 | extend: 3.0.2 1265 | parchment: 1.1.4 1266 | quill-delta: 3.6.3 1267 | 1268 | quill@2.0.3: 1269 | dependencies: 1270 | eventemitter3: 5.0.1 1271 | lodash-es: 4.17.21 1272 | parchment: 3.0.0 1273 | quill-delta: 5.1.0 1274 | 1275 | raw-loader@0.5.1: {} 1276 | 1277 | regexp.prototype.flags@1.5.4: 1278 | dependencies: 1279 | call-bind: 1.0.8 1280 | define-properties: 1.2.1 1281 | es-errors: 1.3.0 1282 | get-proto: 1.0.1 1283 | gopd: 1.2.0 1284 | set-function-name: 2.0.2 1285 | 1286 | rollup@4.53.3: 1287 | dependencies: 1288 | '@types/estree': 1.0.8 1289 | optionalDependencies: 1290 | '@rollup/rollup-android-arm-eabi': 4.53.3 1291 | '@rollup/rollup-android-arm64': 4.53.3 1292 | '@rollup/rollup-darwin-arm64': 4.53.3 1293 | '@rollup/rollup-darwin-x64': 4.53.3 1294 | '@rollup/rollup-freebsd-arm64': 4.53.3 1295 | '@rollup/rollup-freebsd-x64': 4.53.3 1296 | '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 1297 | '@rollup/rollup-linux-arm-musleabihf': 4.53.3 1298 | '@rollup/rollup-linux-arm64-gnu': 4.53.3 1299 | '@rollup/rollup-linux-arm64-musl': 4.53.3 1300 | '@rollup/rollup-linux-loong64-gnu': 4.53.3 1301 | '@rollup/rollup-linux-ppc64-gnu': 4.53.3 1302 | '@rollup/rollup-linux-riscv64-gnu': 4.53.3 1303 | '@rollup/rollup-linux-riscv64-musl': 4.53.3 1304 | '@rollup/rollup-linux-s390x-gnu': 4.53.3 1305 | '@rollup/rollup-linux-x64-gnu': 4.53.3 1306 | '@rollup/rollup-linux-x64-musl': 4.53.3 1307 | '@rollup/rollup-openharmony-arm64': 4.53.3 1308 | '@rollup/rollup-win32-arm64-msvc': 4.53.3 1309 | '@rollup/rollup-win32-ia32-msvc': 4.53.3 1310 | '@rollup/rollup-win32-x64-gnu': 4.53.3 1311 | '@rollup/rollup-win32-x64-msvc': 4.53.3 1312 | fsevents: 2.3.3 1313 | 1314 | set-function-length@1.2.2: 1315 | dependencies: 1316 | define-data-property: 1.1.4 1317 | es-errors: 1.3.0 1318 | function-bind: 1.1.2 1319 | get-intrinsic: 1.3.0 1320 | gopd: 1.2.0 1321 | has-property-descriptors: 1.0.2 1322 | 1323 | set-function-name@2.0.2: 1324 | dependencies: 1325 | define-data-property: 1.1.4 1326 | es-errors: 1.3.0 1327 | functions-have-names: 1.2.3 1328 | has-property-descriptors: 1.0.2 1329 | 1330 | source-map-js@1.2.1: {} 1331 | 1332 | tinyglobby@0.2.15: 1333 | dependencies: 1334 | fdir: 6.5.0(picomatch@4.0.3) 1335 | picomatch: 4.0.3 1336 | 1337 | typescript@5.9.3: {} 1338 | 1339 | uc.micro@2.1.0: {} 1340 | 1341 | vite@7.2.6: 1342 | dependencies: 1343 | esbuild: 0.25.12 1344 | fdir: 6.5.0(picomatch@4.0.3) 1345 | picomatch: 4.0.3 1346 | postcss: 8.5.6 1347 | rollup: 4.53.3 1348 | tinyglobby: 0.2.15 1349 | optionalDependencies: 1350 | fsevents: 2.3.3 1351 | 1352 | vscode-uri@3.1.0: {} 1353 | 1354 | vue-markdown-render@2.3.0(vue@3.5.25(typescript@5.9.3)): 1355 | dependencies: 1356 | markdown-it: 14.1.0 1357 | vue: 3.5.25(typescript@5.9.3) 1358 | 1359 | vue-quilly@1.1.5(typescript@5.9.3): 1360 | dependencies: 1361 | quill: 2.0.3 1362 | vue: 3.5.25(typescript@5.9.3) 1363 | transitivePeerDependencies: 1364 | - typescript 1365 | 1366 | vue-tsc@3.1.6(typescript@5.9.3): 1367 | dependencies: 1368 | '@volar/typescript': 2.4.26 1369 | '@vue/language-core': 3.1.6(typescript@5.9.3) 1370 | typescript: 5.9.3 1371 | 1372 | vue@3.5.25(typescript@5.9.3): 1373 | dependencies: 1374 | '@vue/compiler-dom': 3.5.25 1375 | '@vue/compiler-sfc': 3.5.25 1376 | '@vue/runtime-dom': 3.5.25 1377 | '@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.9.3)) 1378 | '@vue/shared': 3.5.25 1379 | optionalDependencies: 1380 | typescript: 5.9.3 1381 | --------------------------------------------------------------------------------