├── .npmrc ├── img └── screenshot.png ├── src ├── index.js ├── sass │ ├── variables.scss │ ├── icon.scss │ ├── menubar.scss │ ├── menububble.scss │ ├── main.scss │ └── editor.scss ├── assets │ └── icons │ │ ├── hr.svg │ │ ├── italic.svg │ │ ├── ul.svg │ │ ├── redo.svg │ │ ├── paragraph.svg │ │ ├── undo.svg │ │ ├── underline.svg │ │ ├── code.svg │ │ ├── bold.svg │ │ ├── quote.svg │ │ ├── remove.svg │ │ ├── mention.svg │ │ ├── image.svg │ │ ├── checklist.svg │ │ ├── strike.svg │ │ ├── ol.svg │ │ ├── link.svg │ │ ├── combine_cells.svg │ │ ├── delete_col.svg │ │ ├── delete_row.svg │ │ ├── table.svg │ │ ├── github.svg │ │ ├── add_row_after.svg │ │ ├── add_col_after.svg │ │ ├── add_row_before.svg │ │ ├── add_col_before.svg │ │ └── delete_table.svg └── components │ ├── InlineSvg.vue │ ├── Icon.vue │ └── Editor.vue ├── example ├── main.js └── App.vue ├── .github └── dependabot.yml ├── .npmignore ├── .gitignore ├── index.html ├── .prettierrc.json ├── LICENSE.md ├── vite.config.js ├── README.md ├── package.json └── eslint.config.js /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neverbot/vue-tiptap/HEAD/img/screenshot.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Editor from './components/Editor.vue'; 2 | 3 | export default Editor; 4 | -------------------------------------------------------------------------------- /src/sass/variables.scss: -------------------------------------------------------------------------------- 1 | $color-black: #000000; 2 | $color-white: #ffffff; 3 | $color-grey: #dddddd; 4 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | 4 | createApp(App).mount('#app'); 5 | -------------------------------------------------------------------------------- /src/sass/icon.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | 6 | &.has-align-fix { 7 | top: -.1rem; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic dependabot set up 2 | 3 | version: 2 4 | updates: 5 | 6 | # Maintain dependencies for npm 7 | - package-ecosystem: "npm" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | 4 | # log files 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | *.sw* 17 | -------------------------------------------------------------------------------- /src/assets/icons/hr.svg: -------------------------------------------------------------------------------- 1 | horizontal-rule 2 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | # dist/ 4 | !dist/.gitkeep 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw* 23 | -------------------------------------------------------------------------------- /src/assets/icons/italic.svg: -------------------------------------------------------------------------------- 1 | text-italic 2 | -------------------------------------------------------------------------------- /src/assets/icons/ul.svg: -------------------------------------------------------------------------------- 1 | list-bullets 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/redo.svg: -------------------------------------------------------------------------------- 1 | redo 2 | -------------------------------------------------------------------------------- /src/assets/icons/paragraph.svg: -------------------------------------------------------------------------------- 1 | paragraph 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "printWidth": 79, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "semi": true, 7 | "arrowParens": "always", 8 | "bracketSpacing": true, 9 | "overrides": [ 10 | { 11 | "files": "*.vue", 12 | "options": { 13 | "htmlWhitespaceSensitivity": "css" 14 | } 15 | }, 16 | { 17 | "files": "*.json", 18 | "options": { 19 | "parser": "json" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /src/assets/icons/undo.svg: -------------------------------------------------------------------------------- 1 | undo 2 | -------------------------------------------------------------------------------- /src/assets/icons/underline.svg: -------------------------------------------------------------------------------- 1 | text-underline 2 | -------------------------------------------------------------------------------- /src/assets/icons/code.svg: -------------------------------------------------------------------------------- 1 | angle-brackets 2 | -------------------------------------------------------------------------------- /src/assets/icons/bold.svg: -------------------------------------------------------------------------------- 1 | text-bold 2 | -------------------------------------------------------------------------------- /src/assets/icons/quote.svg: -------------------------------------------------------------------------------- 1 | close-quote 2 | -------------------------------------------------------------------------------- /src/assets/icons/remove.svg: -------------------------------------------------------------------------------- 1 | delete-2-alternate 2 | -------------------------------------------------------------------------------- /src/assets/icons/mention.svg: -------------------------------------------------------------------------------- 1 | read-email-at-alternate -------------------------------------------------------------------------------- /src/components/InlineSvg.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /src/assets/icons/image.svg: -------------------------------------------------------------------------------- 1 | paginate-filter-picture-alternate 2 | -------------------------------------------------------------------------------- /src/assets/icons/checklist.svg: -------------------------------------------------------------------------------- 1 | checklist-alternate 2 | -------------------------------------------------------------------------------- /src/components/Icon.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/assets/icons/strike.svg: -------------------------------------------------------------------------------- 1 | text-strike-through 2 | -------------------------------------------------------------------------------- /src/assets/icons/ol.svg: -------------------------------------------------------------------------------- 1 | list-numbers 2 | -------------------------------------------------------------------------------- /src/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | hyperlink-2 2 | -------------------------------------------------------------------------------- /src/assets/icons/combine_cells.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/sass/menubar.scss: -------------------------------------------------------------------------------- 1 | .menubar { 2 | 3 | margin-bottom: 1rem; 4 | transition: visibility 0.2s 0.4s, opacity 0.2s 0.4s; 5 | 6 | &.is-hidden { 7 | visibility: hidden; 8 | opacity: 0; 9 | } 10 | 11 | &.is-focused { 12 | visibility: visible; 13 | opacity: 1; 14 | transition: visibility 0.2s, opacity 0.2s; 15 | } 16 | 17 | &__button { 18 | vertical-align: middle; 19 | width: 1.8rem; 20 | height: 1.2rem; 21 | font-weight: bold; 22 | display: inline-flex; 23 | background: transparent; 24 | border: 0; 25 | color: $color-black; 26 | padding: 0.2rem 0.5rem; 27 | margin-right: 0.2rem; 28 | border-radius: 3px; 29 | cursor: pointer; 30 | 31 | &:hover { 32 | background-color: rgba($color-black, 0.05); 33 | } 34 | 35 | &.is-active { 36 | background-color: rgba($color-black, 0.1); 37 | } 38 | } 39 | 40 | span#{&}__button { 41 | font-size: 13.3333px; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/icons/delete_col.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/delete_row.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/table.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019, neverbot (limited liability) 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. 22 | -------------------------------------------------------------------------------- /src/sass/menububble.scss: -------------------------------------------------------------------------------- 1 | .menububble { 2 | position: absolute; 3 | display: flex; 4 | z-index: 20; 5 | background: $color-black; 6 | border-radius: 5px; 7 | padding: 0.3rem; 8 | margin-bottom: 0.5rem; 9 | transform: translateX(-50%); 10 | visibility: hidden; 11 | opacity: 0; 12 | transition: opacity 0.2s, visibility 0.2s; 13 | 14 | &.is-active { 15 | opacity: 1; 16 | visibility: visible; 17 | } 18 | 19 | &__button { 20 | display: inline-flex; 21 | background: transparent; 22 | border: 0; 23 | color: $color-white; 24 | padding: 0.2rem 0.5rem; 25 | margin-right: 0.2rem; 26 | border-radius: 3px; 27 | cursor: pointer; 28 | 29 | &:last-child { 30 | margin-right: 0; 31 | } 32 | 33 | &:hover { 34 | background-color: rgba($color-white, 0.1); 35 | } 36 | 37 | &.is-active { 38 | background-color: rgba($color-white, 0.2); 39 | } 40 | } 41 | 42 | &__form { 43 | display: flex; 44 | align-items: center; 45 | } 46 | 47 | &__input { 48 | font: inherit; 49 | border: none; 50 | background: transparent; 51 | color: $color-white; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | 4 | import path from 'path'; 5 | 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | css: { 9 | preprocessorOptions: { 10 | sass: { 11 | // To fix Deprecation [legacy-js-api]: The legacy JS API is deprecated 12 | // More info: https://sass-lang.com/d/legacy-js-api 13 | api: 'modern-compiler', // or 'modern' 14 | }, 15 | }, 16 | }, 17 | resolve: { 18 | alias: { 19 | '@': path.resolve(__dirname, './src'), 20 | }, 21 | }, 22 | build: { 23 | lib: { 24 | // src/index.js is where we have exported the component(s) 25 | entry: path.resolve(__dirname, 'src/index.js'), 26 | name: 'VueTipTap', 27 | // the name of the output files when the build is run 28 | fileName: 'vue-tiptap', 29 | }, 30 | rollupOptions: { 31 | // make sure to externalize deps that shouldn't be bundled 32 | // into your library 33 | external: ['vue'], 34 | output: { 35 | // Provide global variables to use in the UMD build 36 | // for externalized deps 37 | globals: { 38 | vue: 'Vue', 39 | }, 40 | }, 41 | }, 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /src/assets/icons/add_row_after.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/add_col_after.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/add_row_before.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/add_col_before.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/delete_table.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-tiptap 2 | 3 | [![npm](https://img.shields.io/npm/dt/vue-tiptap)](https://www.npmjs.com/package/vue-tiptap) 4 | [![npm](https://img.shields.io/npm/dw/vue-tiptap)](https://www.npmjs.com/package/vue-tiptap) 5 | [![GitHub license](https://img.shields.io/github/license/neverbot/vue-tiptap)](https://github.com/neverbot/vue-tiptap/blob/master/LICENSE.md) 6 | [![npm](https://img.shields.io/npm/v/vue-tiptap)](https://www.npmjs.com/package/vue-tiptap) 7 | 8 | Example of using [tiptap 2](https://github.com/scrumpy/tiptap/) as a Vue 3 component without being completely renderless, so you can see how to use it inside your own components or even use it right away. It can be imported as a Vue 3 component, and you can see in the `example` directory how it would be used inside your application. 9 | 10 | **Note**: now tiptap has their own examples and components for different frameworks (including vue 2 and vue 3), so maybe you should take a look to the official package [@tiptap/vue-3](https://tiptap.dev/installation/vue3). This package will not be updated anymore. 11 | 12 | ![screenshot](./img/screenshot.png) 13 | 14 | ## Install and Usage 15 | 16 | ```bash 17 | 18 | # install dependencies 19 | npm install 20 | 21 | # build component 22 | npm run build 23 | 24 | # serve demo 25 | npm run dev 26 | ``` 27 | 28 | ## License 29 | 30 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 31 | 32 | SVG Icons from the original [TipTap](https://github.com/scrumpy/tiptap/) package. 33 | -------------------------------------------------------------------------------- /src/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import "./variables"; 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | text-size-adjust: 100%; 8 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 9 | -webkit-touch-callout: none; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | text-rendering: optimizeLegibility; 13 | 14 | &:focus { 15 | outline: none; 16 | } 17 | } 18 | 19 | *::before, 20 | *::after { 21 | box-sizing: border-box; 22 | } 23 | 24 | html { 25 | font-family: 'Inter'; 26 | font-size: 18px; 27 | color: $color-black; 28 | line-height: 1.5; 29 | } 30 | 31 | body { 32 | margin: 0; 33 | } 34 | 35 | a { 36 | color: inherit; 37 | } 38 | 39 | h1, 40 | h2, 41 | h3, 42 | p, 43 | ul, 44 | ol, 45 | pre, 46 | blockquote { 47 | margin: 1rem 0; 48 | 49 | &:first-child { 50 | margin-top: 0; 51 | } 52 | 53 | &:last-child { 54 | margin-bottom: 0; 55 | } 56 | } 57 | 58 | h1, 59 | h2, 60 | h3 { 61 | line-height: 1.3; 62 | } 63 | 64 | .button { 65 | font-weight: bold; 66 | display: inline-flex; 67 | background: transparent; 68 | border: 0; 69 | color: $color-black; 70 | padding: 0.2rem 0.5rem; 71 | margin-right: 0.2rem; 72 | border-radius: 3px; 73 | cursor: pointer; 74 | background-color: rgba($color-black, 0.1); 75 | 76 | &:hover { 77 | background-color: rgba($color-black, 0.15); 78 | } 79 | } 80 | 81 | .message { 82 | background-color: rgba($color-black, 0.05); 83 | color: rgba($color-black, 0.7); 84 | padding: 1rem; 85 | border-radius: 6px; 86 | margin-bottom: 1.5rem; 87 | font-style: italic; 88 | } 89 | 90 | @import "./icon"; 91 | @import "./editor"; 92 | @import "./menubar"; 93 | @import "./menububble"; 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-tiptap", 3 | "version": "3.1.2", 4 | "description": "Vue component to use TipTap Editor without being renderless", 5 | "keywords": [ 6 | "vue", 7 | "tiptap", 8 | "vuejs" 9 | ], 10 | "bugs": { 11 | "url": "https://github.com/neverbot/vue-tiptap/issues" 12 | }, 13 | "type": "module", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/neverbot/vue-tiptap" 17 | }, 18 | "license": "MIT", 19 | "author": "neverbot (http://neverbot.com)", 20 | "exports": { 21 | ".": { 22 | "import": "./dist/vue-tiptap.js", 23 | "require": "./dist/vue-tiptap.umd.cjs" 24 | } 25 | }, 26 | "main": "./dist/vue-tiptap.umd.cjs", 27 | "module": "./dist/vue-tiptap.js", 28 | "scripts": { 29 | "build": "npx vite build", 30 | "dev": "npx vite", 31 | "lint": "npx eslint .", 32 | "lint:fix": "npx eslint --fix .", 33 | "serve": "npx vite preview", 34 | "test": "echo \"Error: no tests\" && exit 1" 35 | }, 36 | "dependencies": { 37 | "@tiptap/extension-underline": "^2.8.0", 38 | "@tiptap/starter-kit": "^2.8.0", 39 | "@tiptap/vue-3": "^2.8.0", 40 | "vue": "^3.5.10" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.25.2", 44 | "@babel/eslint-parser": "^7.25.1", 45 | "@babel/preset-env": "^7.25.4", 46 | "@eslint/js": "^9.11.1", 47 | "@vitejs/plugin-vue": "^5.1.4", 48 | "babel-loader": "^9.2.1", 49 | "eslint": "^9.11.1", 50 | "eslint-config-prettier": "^9.1.0", 51 | "eslint-plugin-jsonc": "^2.16.0", 52 | "eslint-plugin-prettier": "^5.2.1", 53 | "eslint-plugin-vue": "^9.28.0", 54 | "globals": "^15.10.0", 55 | "jsonc-eslint-parser": "^2.4.0", 56 | "prettier": "^3.3.3", 57 | "sass": "^1.79.4", 58 | "vite": "^5.4.8", 59 | "vue-eslint-parser": "^9.4.3" 60 | }, 61 | "engines": { 62 | "node": "^14 || ^16 || ^18 || ^20", 63 | "npm": "^7 || ^8 || ^9 || ^10" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /example/App.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 67 | 68 | 71 | 72 | 87 | -------------------------------------------------------------------------------- /src/sass/editor.scss: -------------------------------------------------------------------------------- 1 | .editor { 2 | position: relative; 3 | max-width: 30rem; 4 | margin: 0 auto 5rem auto; 5 | 6 | &__content { 7 | 8 | overflow-wrap: break-word; 9 | word-wrap: break-word; 10 | word-break: break-word; 11 | 12 | * { 13 | caret-color: currentColor; 14 | } 15 | 16 | pre { 17 | padding: 0.7rem 1rem; 18 | border-radius: 5px; 19 | background: $color-black; 20 | color: $color-white; 21 | font-size: 0.8rem; 22 | overflow-x: auto; 23 | 24 | code { 25 | display: block; 26 | } 27 | } 28 | 29 | p code { 30 | padding: 0.2rem 0.4rem; 31 | border-radius: 5px; 32 | font-size: 0.8rem; 33 | font-weight: bold; 34 | background: rgba($color-black, 0.1); 35 | color: rgba($color-black, 0.8); 36 | } 37 | 38 | ul, 39 | ol { 40 | padding-left: 1rem; 41 | } 42 | 43 | li > p, 44 | li > ol, 45 | li > ul { 46 | margin: 0; 47 | } 48 | 49 | a { 50 | color: inherit; 51 | } 52 | 53 | blockquote { 54 | border-left: 3px solid rgba($color-black, 0.1); 55 | color: rgba($color-black, 0.8); 56 | padding-left: 0.8rem; 57 | font-style: italic; 58 | 59 | p { 60 | margin: 0; 61 | } 62 | } 63 | 64 | img { 65 | max-width: 100%; 66 | border-radius: 3px; 67 | } 68 | 69 | table { 70 | border-collapse: collapse; 71 | table-layout: fixed; 72 | width: 100%; 73 | margin: 0; 74 | overflow: hidden; 75 | 76 | td, th { 77 | min-width: 1em; 78 | border: 2px solid $color-grey; 79 | padding: 3px 5px; 80 | vertical-align: top; 81 | box-sizing: border-box; 82 | position: relative; 83 | > * { 84 | margin-bottom: 0; 85 | } 86 | } 87 | 88 | th { 89 | font-weight: bold; 90 | text-align: left; 91 | } 92 | 93 | .selectedCell:after { 94 | z-index: 2; 95 | position: absolute; 96 | content: ""; 97 | left: 0; right: 0; top: 0; bottom: 0; 98 | background: rgba(200, 200, 255, 0.4); 99 | pointer-events: none; 100 | } 101 | 102 | .column-resize-handle { 103 | position: absolute; 104 | right: -2px; top: 0; bottom: 0; 105 | width: 4px; 106 | z-index: 20; 107 | background-color: #adf; 108 | pointer-events: none; 109 | } 110 | } 111 | 112 | .tableWrapper { 113 | margin: 1em 0; 114 | overflow-x: auto; 115 | } 116 | 117 | .resize-cursor { 118 | cursor: ew-resize; 119 | cursor: col-resize; 120 | } 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import babelParser from '@babel/eslint-parser'; 3 | import eslintJsonc from 'eslint-plugin-jsonc'; 4 | import eslintJsoncParser from 'jsonc-eslint-parser'; 5 | import vueParser from 'vue-eslint-parser'; 6 | import vue from 'eslint-plugin-vue'; 7 | import prettier from 'eslint-plugin-prettier'; 8 | import js from '@eslint/js'; 9 | 10 | export default [ 11 | { 12 | // global ignores 13 | // folders can only be ignored at the global level, per-cfg you must do: '**/dist/**/*' 14 | ignores: ['**/dist/'], 15 | }, 16 | // general defaults 17 | js.configs.recommended, 18 | { 19 | files: ['**/*.js'], 20 | rules: { 21 | 'prettier/prettier': [ 22 | 'error', 23 | {}, 24 | { 25 | usePrettierrc: true, 26 | }, 27 | ], 28 | 'no-console': 'warn', 29 | }, 30 | plugins: { 31 | prettier, 32 | }, 33 | languageOptions: { 34 | parser: babelParser, 35 | ecmaVersion: 2018, 36 | sourceType: 'module', 37 | globals: { 38 | ...globals.node, 39 | ...globals.browser, 40 | }, 41 | parserOptions: { 42 | requireConfigFile: false, 43 | allowImportExportEverywhere: true, 44 | 45 | ecmaFeatures: { 46 | experimentalObjectRestSpread: true, 47 | }, 48 | }, 49 | }, 50 | }, 51 | { 52 | files: ['**/*.json'], 53 | ignores: ['**/package.json', '**/package-lock.json'], 54 | plugins: { 55 | jsonc: eslintJsonc, 56 | prettier, 57 | }, 58 | languageOptions: { 59 | parser: eslintJsoncParser, 60 | parserOptions: { 61 | jsonSyntax: 'JSON', 62 | }, 63 | }, 64 | rules: { 65 | 'prettier/prettier': [ 66 | 'error', 67 | {}, 68 | { 69 | usePrettierrc: true, 70 | }, 71 | ], 72 | 'no-console': 'warn', 73 | }, 74 | }, 75 | ...vue.configs['flat/essential'], 76 | ...vue.configs['flat/recommended'], 77 | { 78 | files: ['**/*.vue'], 79 | plugins: { 80 | prettier, 81 | vue, 82 | }, 83 | languageOptions: { 84 | parser: vueParser, 85 | ecmaVersion: 'latest', 86 | sourceType: 'module', 87 | globals: { 88 | ...globals.node, 89 | ...globals.browser, 90 | }, 91 | }, 92 | processor: vue.processors['.vue'], 93 | rules: { 94 | 'prettier/prettier': [ 95 | 'error', 96 | {}, 97 | { 98 | usePrettierrc: true, 99 | }, 100 | ], 101 | ...vue.configs.base.rules, 102 | ...vue.configs.recommended.rules, 103 | ...vue.configs.essential.rules, 104 | ...vue.configs['strongly-recommended'].rules, 105 | 'no-console': 'warn', 106 | 'vue/multi-word-component-names': 'off', 107 | 'vue/no-v-html': 'off', 108 | 'vue/first-attribute-linebreak': [ 109 | 'warn', 110 | { 111 | singleline: 'ignore', 112 | multiline: 'below', 113 | }, 114 | ], 115 | 'vue/max-attributes-per-line': [ 116 | 'error', 117 | { 118 | singleline: { 119 | max: 3, 120 | }, 121 | multiline: { 122 | max: 1, 123 | }, 124 | }, 125 | ], 126 | 'vue/html-indent': [ 127 | 'error', 128 | 2, 129 | { 130 | attribute: 1, 131 | baseIndent: 1, 132 | closeBracket: 0, 133 | alignAttributesVertically: false, 134 | ignores: [], 135 | }, 136 | ], 137 | }, 138 | }, 139 | ]; 140 | -------------------------------------------------------------------------------- /src/components/Editor.vue: -------------------------------------------------------------------------------- 1 | 141 | 142 | 220 | 221 | 222 | --------------------------------------------------------------------------------