feat: editor script setup
This commit is contained in:
parent
cbec1f24aa
commit
db627ed28a
7 changed files with 289 additions and 279 deletions
|
@ -69,6 +69,7 @@
|
|||
"@faker-js/faker": "7.5.0",
|
||||
"@types/dompurify": "2.3.4",
|
||||
"@types/flexsearch": "0.7.3",
|
||||
"@types/marked": "4.0.7",
|
||||
"@types/node": "16.11.64",
|
||||
"@typescript-eslint/eslint-plugin": "5.39.0",
|
||||
"@typescript-eslint/parser": "5.39.0",
|
||||
|
|
104
pnpm-lock.yaml
104
pnpm-lock.yaml
|
@ -1,4 +1,4 @@
|
|||
lockfileVersion: 5.3
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@4tw/cypress-drag-drop': 2.2.1
|
||||
|
@ -17,6 +17,7 @@ specifiers:
|
|||
'@types/flexsearch': 0.7.3
|
||||
'@types/is-touch-device': 1.0.0
|
||||
'@types/lodash.clonedeep': 4.5.7
|
||||
'@types/marked': 4.0.7
|
||||
'@types/node': 16.11.64
|
||||
'@types/sortablejs': 1.15.0
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0
|
||||
|
@ -86,7 +87,7 @@ dependencies:
|
|||
'@fortawesome/fontawesome-svg-core': 6.2.0
|
||||
'@fortawesome/free-regular-svg-icons': 6.2.0
|
||||
'@fortawesome/free-solid-svg-icons': 6.2.0
|
||||
'@fortawesome/vue-fontawesome': 3.0.1_21e6b971ab1cbc4f6c07645df616013e
|
||||
'@fortawesome/vue-fontawesome': 3.0.1_ehtls4nlds6e63ahmro7mfqbhy
|
||||
'@github/hotkey': 2.0.1
|
||||
'@kyvg/vue3-notification': 2.4.1_vue@3.2.40
|
||||
'@sentry/tracing': 7.14.1
|
||||
|
@ -95,7 +96,7 @@ dependencies:
|
|||
'@types/lodash.clonedeep': 4.5.7
|
||||
'@types/sortablejs': 1.15.0
|
||||
'@vueuse/core': 9.3.0_vue@3.2.40
|
||||
'@vueuse/router': 9.3.0_vue-router@4.1.5+vue@3.2.40
|
||||
'@vueuse/router': 9.3.0_c7eza3xvlyb4mo6qeit5ggeo6u
|
||||
axios: 0.27.2
|
||||
blurhash: 2.0.3
|
||||
bulma-css-variables: 0.9.33
|
||||
|
@ -112,7 +113,7 @@ dependencies:
|
|||
lodash.debounce: 4.0.8
|
||||
marked: 4.1.1
|
||||
minimist: 1.2.6
|
||||
pinia: 2.0.22_typescript@4.8.4+vue@3.2.40
|
||||
pinia: 2.0.22_bfjwoga25wxjazzogo7o372nwq
|
||||
register-service-worker: 1.7.2
|
||||
snake-case: 3.0.4
|
||||
sortablejs: 1.15.0
|
||||
|
@ -134,12 +135,13 @@ devDependencies:
|
|||
'@faker-js/faker': 7.5.0
|
||||
'@types/dompurify': 2.3.4
|
||||
'@types/flexsearch': 0.7.3
|
||||
'@types/marked': 4.0.7
|
||||
'@types/node': 16.11.64
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0_be048b79d04b908735dc19ebba1dbd66
|
||||
'@typescript-eslint/parser': 5.39.0_eslint@8.24.0+typescript@4.8.4
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0_xyciw6oqjoiiono4dhv3uhn5my
|
||||
'@typescript-eslint/parser': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
|
||||
'@vitejs/plugin-legacy': 2.2.0_terser@5.10.0+vite@3.1.4
|
||||
'@vitejs/plugin-vue': 3.1.2_vite@3.1.4+vue@3.2.40
|
||||
'@vue/eslint-config-typescript': 11.0.2_ab5ac96ab635ce1fa6bba69a2b0ff7cb
|
||||
'@vue/eslint-config-typescript': 11.0.2_vnnms2vwgxhb7jv3u2ncwd7xzm
|
||||
'@vue/test-utils': 2.1.0_vue@3.2.40
|
||||
'@vue/tsconfig': 0.1.3_@types+node@16.11.64
|
||||
autoprefixer: 10.4.12_postcss@8.4.17
|
||||
|
@ -159,12 +161,12 @@ devDependencies:
|
|||
sass: 1.55.0
|
||||
typescript: 4.8.4
|
||||
vite: 3.1.4_sass@1.55.0+terser@5.10.0
|
||||
vite-plugin-pwa: 0.13.1_vite@3.1.4
|
||||
vite-plugin-pwa: 0.13.1_bhe5iaipiq3lmbaxwdxgnnn2gq
|
||||
vite-svg-loader: 3.6.0
|
||||
vitest: 0.23.4_ddc85b0b7a78b2cfae025c8e16b7f605
|
||||
vitest: 0.23.4_3xefwc32pczm7lqclshbnn7wau
|
||||
vue-tsc: 0.40.13_typescript@4.8.4
|
||||
wait-on: 6.0.1
|
||||
workbox-cli: 6.5.4
|
||||
workbox-cli: 6.5.4_acorn@8.8.0
|
||||
|
||||
packages:
|
||||
|
||||
|
@ -1392,7 +1394,7 @@ packages:
|
|||
peerDependencies:
|
||||
postcss: ^8.2
|
||||
dependencies:
|
||||
'@csstools/selector-specificity': 2.0.2_cd239324a5aeb6e3cee0fb61f6a33448
|
||||
'@csstools/selector-specificity': 2.0.2_zurzgjffv23ohtxa7nq7nizuja
|
||||
postcss: 8.4.17
|
||||
postcss-selector-parser: 6.0.10
|
||||
dev: true
|
||||
|
@ -1445,7 +1447,7 @@ packages:
|
|||
peerDependencies:
|
||||
postcss: ^8.2
|
||||
dependencies:
|
||||
'@csstools/selector-specificity': 2.0.2_cd239324a5aeb6e3cee0fb61f6a33448
|
||||
'@csstools/selector-specificity': 2.0.2_zurzgjffv23ohtxa7nq7nizuja
|
||||
postcss: 8.4.17
|
||||
postcss-selector-parser: 6.0.10
|
||||
dev: true
|
||||
|
@ -1530,7 +1532,7 @@ packages:
|
|||
postcss: 8.4.17
|
||||
dev: true
|
||||
|
||||
/@csstools/selector-specificity/2.0.2_cd239324a5aeb6e3cee0fb61f6a33448:
|
||||
/@csstools/selector-specificity/2.0.2_zurzgjffv23ohtxa7nq7nizuja:
|
||||
resolution: {integrity: sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==}
|
||||
engines: {node: ^12 || ^14 || >=16}
|
||||
peerDependencies:
|
||||
|
@ -1679,7 +1681,7 @@ packages:
|
|||
'@fortawesome/fontawesome-common-types': 6.2.0
|
||||
dev: false
|
||||
|
||||
/@fortawesome/vue-fontawesome/3.0.1_21e6b971ab1cbc4f6c07645df616013e:
|
||||
/@fortawesome/vue-fontawesome/3.0.1_ehtls4nlds6e63ahmro7mfqbhy:
|
||||
resolution: {integrity: sha512-CdXZJoCS+aEPec26ZP7hWWU3SaJlQPZSCGdgpQ2qGl2HUmtUUNrI3zC4XWdn1JUmh3t5OuDeRG1qB4eGRNSD4A==}
|
||||
peerDependencies:
|
||||
'@fortawesome/fontawesome-svg-core': ~1 || ~6
|
||||
|
@ -1917,7 +1919,7 @@ packages:
|
|||
strip-ansi: 7.0.1
|
||||
supports-color: 9.2.1
|
||||
tmp-promise: 3.0.3
|
||||
ts-node: 10.8.1_56922d2c3e8466316d745c2e9b343672
|
||||
ts-node: 10.8.1_k2jc2lb6qrtdc3lulqxjwnbwoi
|
||||
typescript: 4.8.4
|
||||
update-notifier: 5.1.0
|
||||
uuid: 8.3.2
|
||||
|
@ -2605,7 +2607,7 @@ packages:
|
|||
resolution: {integrity: sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==}
|
||||
dev: false
|
||||
|
||||
/@rollup/plugin-babel/5.3.0_@babel+core@7.17.2+rollup@2.79.1:
|
||||
/@rollup/plugin-babel/5.3.0_pf2mys4p2khuj2gysypj3zzjia:
|
||||
resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -2989,7 +2991,6 @@ packages:
|
|||
|
||||
/@types/marked/4.0.7:
|
||||
resolution: {integrity: sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==}
|
||||
dev: false
|
||||
|
||||
/@types/minimatch/3.0.5:
|
||||
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
|
||||
|
@ -3104,7 +3105,7 @@ packages:
|
|||
dev: true
|
||||
optional: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.39.0_be048b79d04b908735dc19ebba1dbd66:
|
||||
/@typescript-eslint/eslint-plugin/5.39.0_xyciw6oqjoiiono4dhv3uhn5my:
|
||||
resolution: {integrity: sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -3115,10 +3116,10 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.39.0_eslint@8.24.0+typescript@4.8.4
|
||||
'@typescript-eslint/parser': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
|
||||
'@typescript-eslint/scope-manager': 5.39.0
|
||||
'@typescript-eslint/type-utils': 5.39.0_eslint@8.24.0+typescript@4.8.4
|
||||
'@typescript-eslint/utils': 5.39.0_eslint@8.24.0+typescript@4.8.4
|
||||
'@typescript-eslint/type-utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
|
||||
'@typescript-eslint/utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
|
||||
debug: 4.3.4
|
||||
eslint: 8.24.0
|
||||
ignore: 5.2.0
|
||||
|
@ -3130,7 +3131,7 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/5.39.0_eslint@8.24.0+typescript@4.8.4:
|
||||
/@typescript-eslint/parser/5.39.0_ypn2ylkkyfa5i233caldtndbqa:
|
||||
resolution: {integrity: sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -3158,7 +3159,7 @@ packages:
|
|||
'@typescript-eslint/visitor-keys': 5.39.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils/5.39.0_eslint@8.24.0+typescript@4.8.4:
|
||||
/@typescript-eslint/type-utils/5.39.0_ypn2ylkkyfa5i233caldtndbqa:
|
||||
resolution: {integrity: sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -3169,7 +3170,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.39.0_typescript@4.8.4
|
||||
'@typescript-eslint/utils': 5.39.0_eslint@8.24.0+typescript@4.8.4
|
||||
'@typescript-eslint/utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
|
||||
debug: 4.3.4
|
||||
eslint: 8.24.0
|
||||
tsutils: 3.21.0_typescript@4.8.4
|
||||
|
@ -3183,7 +3184,7 @@ packages:
|
|||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/5.39.0_f1deb5be19df0fe0ff039530117daddf:
|
||||
/@typescript-eslint/typescript-estree/5.39.0_6hpllpqz34h6b7ydsuybc7nn34:
|
||||
resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -3225,7 +3226,7 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils/5.39.0_eslint@8.24.0+typescript@4.8.4:
|
||||
/@typescript-eslint/utils/5.39.0_ypn2ylkkyfa5i233caldtndbqa:
|
||||
resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -3303,7 +3304,7 @@ packages:
|
|||
magic-string: 0.26.3
|
||||
regenerator-runtime: 0.13.9
|
||||
systemjs: 6.12.6
|
||||
terser: 5.10.0
|
||||
terser: 5.10.0_acorn@8.8.0
|
||||
vite: 3.1.4_sass@1.55.0+terser@5.10.0
|
||||
dev: true
|
||||
|
||||
|
@ -3432,7 +3433,7 @@ packages:
|
|||
resolution: {integrity: sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ==}
|
||||
dev: false
|
||||
|
||||
/@vue/eslint-config-typescript/11.0.2_ab5ac96ab635ce1fa6bba69a2b0ff7cb:
|
||||
/@vue/eslint-config-typescript/11.0.2_vnnms2vwgxhb7jv3u2ncwd7xzm:
|
||||
resolution: {integrity: sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==}
|
||||
engines: {node: ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -3443,8 +3444,8 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0_be048b79d04b908735dc19ebba1dbd66
|
||||
'@typescript-eslint/parser': 5.39.0_eslint@8.24.0+typescript@4.8.4
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0_xyciw6oqjoiiono4dhv3uhn5my
|
||||
'@typescript-eslint/parser': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
|
||||
eslint: 8.24.0
|
||||
eslint-plugin-vue: 9.6.0_eslint@8.24.0
|
||||
typescript: 4.8.4
|
||||
|
@ -3551,7 +3552,7 @@ packages:
|
|||
resolution: {integrity: sha512-GnnfjbzIPJIh9ngL9s9oGU1+Hx/h5/KFqTfJykzh/1xjaHkedV9g0MASpdmPZIP+ynNhKAcEfA6g5i8KXwtoMA==}
|
||||
dev: false
|
||||
|
||||
/@vueuse/router/9.3.0_vue-router@4.1.5+vue@3.2.40:
|
||||
/@vueuse/router/9.3.0_c7eza3xvlyb4mo6qeit5ggeo6u:
|
||||
resolution: {integrity: sha512-UFN2MFciprH21oYsAgNHeDJ4Bd86HpRm9gximSN8j6h4fc2aa62fvfhprfHqdTxYAcgcGkMwcc9TO75jOvr8gg==}
|
||||
peerDependencies:
|
||||
vue-router: '>=4.0.0-rc.1'
|
||||
|
@ -5572,7 +5573,7 @@ packages:
|
|||
resolution: {integrity: sha512-lR78AugfUSBojwlSRZBeEqQ1l8LI7rbxOl1qTUnGLcjZQDjZmrZCb7R46rK8U8B5WzFvJrxa7fEBA8FoD/n5fA==}
|
||||
engines: {node: ^12.20.0 || ^14.14.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.39.0_f1deb5be19df0fe0ff039530117daddf
|
||||
'@typescript-eslint/typescript-estree': 5.39.0_6hpllpqz34h6b7ydsuybc7nn34
|
||||
ast-module-types: 3.0.0
|
||||
node-source-walk: 5.0.0
|
||||
typescript: 4.8.4
|
||||
|
@ -10088,7 +10089,7 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/pinia/2.0.22_typescript@4.8.4+vue@3.2.40:
|
||||
/pinia/2.0.22_bfjwoga25wxjazzogo7o372nwq:
|
||||
resolution: {integrity: sha512-u+b8/BC+tmvo3ACbYO2w5NfxHWFOjvvw9DQnyT0dW8aUMCPRQT5QnfZ5R5W2MzZBMTeZRMQI7V/QFbafmM9QHw==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.4.0
|
||||
|
@ -10331,7 +10332,7 @@ packages:
|
|||
peerDependencies:
|
||||
postcss: ^8.2
|
||||
dependencies:
|
||||
'@csstools/selector-specificity': 2.0.2_cd239324a5aeb6e3cee0fb61f6a33448
|
||||
'@csstools/selector-specificity': 2.0.2_zurzgjffv23ohtxa7nq7nizuja
|
||||
postcss: 8.4.17
|
||||
postcss-selector-parser: 6.0.10
|
||||
dev: true
|
||||
|
@ -11047,7 +11048,7 @@ packages:
|
|||
glob: 7.2.0
|
||||
dev: true
|
||||
|
||||
/rollup-plugin-terser/7.0.2_rollup@2.79.1:
|
||||
/rollup-plugin-terser/7.0.2_acorn@8.8.0+rollup@2.79.1:
|
||||
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
|
||||
peerDependencies:
|
||||
rollup: ^2.0.0
|
||||
|
@ -11056,7 +11057,9 @@ packages:
|
|||
jest-worker: 26.6.2
|
||||
rollup: 2.79.1
|
||||
serialize-javascript: 4.0.0
|
||||
terser: 5.10.0
|
||||
terser: 5.10.0_acorn@8.8.0
|
||||
transitivePeerDependencies:
|
||||
- acorn
|
||||
dev: true
|
||||
|
||||
/rollup-plugin-visualizer/5.8.2_rollup@2.79.1:
|
||||
|
@ -11938,10 +11941,12 @@ packages:
|
|||
supports-hyperlinks: 2.2.0
|
||||
dev: true
|
||||
|
||||
/terser/5.10.0:
|
||||
/terser/5.10.0_acorn@8.8.0:
|
||||
resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
acorn: ^8.5.0
|
||||
peerDependenciesMeta:
|
||||
acorn:
|
||||
optional: true
|
||||
|
@ -12147,7 +12152,7 @@ packages:
|
|||
resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==}
|
||||
dev: true
|
||||
|
||||
/ts-node/10.8.1_56922d2c3e8466316d745c2e9b343672:
|
||||
/ts-node/10.8.1_k2jc2lb6qrtdc3lulqxjwnbwoi:
|
||||
resolution: {integrity: sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -12545,20 +12550,21 @@ packages:
|
|||
extsprintf: 1.3.0
|
||||
dev: true
|
||||
|
||||
/vite-plugin-pwa/0.13.1_vite@3.1.4:
|
||||
/vite-plugin-pwa/0.13.1_bhe5iaipiq3lmbaxwdxgnnn2gq:
|
||||
resolution: {integrity: sha512-NR3dIa+o2hzlzo4lF4Gu0cYvoMjSw2DdRc6Epw1yjmCqWaGuN86WK9JqZie4arNlE1ZuWT3CLiMdiX5wcmmUmg==}
|
||||
peerDependencies:
|
||||
vite: ^3.1.0
|
||||
workbox-build: ^6.5.4
|
||||
workbox-window: ^6.5.4
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
fast-glob: 3.2.11
|
||||
pretty-bytes: 6.0.0
|
||||
rollup: 2.79.1
|
||||
vite: 3.1.4_sass@1.55.0+terser@5.10.0
|
||||
workbox-build: 6.5.4
|
||||
workbox-build: 6.5.4_acorn@8.8.0
|
||||
workbox-window: 6.5.4
|
||||
transitivePeerDependencies:
|
||||
- '@types/babel__core'
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
|
@ -12593,12 +12599,12 @@ packages:
|
|||
resolve: 1.22.1
|
||||
rollup: 2.78.0
|
||||
sass: 1.55.0
|
||||
terser: 5.10.0
|
||||
terser: 5.10.0_acorn@8.8.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
|
||||
/vitest/0.23.4_ddc85b0b7a78b2cfae025c8e16b7f605:
|
||||
/vitest/0.23.4_3xefwc32pczm7lqclshbnn7wau:
|
||||
resolution: {integrity: sha512-iukBNWqQAv8EKDBUNntspLp9SfpaVFbmzmM0sNcnTxASQZMzRw3PsM6DMlsHiI+I6GeO5/sYDg3ecpC+SNFLrQ==}
|
||||
engines: {node: '>=v14.16.0'}
|
||||
hasBin: true
|
||||
|
@ -12912,7 +12918,7 @@ packages:
|
|||
workbox-core: 6.5.4
|
||||
dev: true
|
||||
|
||||
/workbox-build/6.5.4:
|
||||
/workbox-build/6.5.4_acorn@8.8.0:
|
||||
resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dependencies:
|
||||
|
@ -12920,7 +12926,7 @@ packages:
|
|||
'@babel/core': 7.17.2
|
||||
'@babel/preset-env': 7.16.11_@babel+core@7.17.2
|
||||
'@babel/runtime': 7.17.2
|
||||
'@rollup/plugin-babel': 5.3.0_@babel+core@7.17.2+rollup@2.79.1
|
||||
'@rollup/plugin-babel': 5.3.0_pf2mys4p2khuj2gysypj3zzjia
|
||||
'@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.1
|
||||
'@rollup/plugin-replace': 2.4.2_rollup@2.79.1
|
||||
'@surma/rollup-plugin-off-main-thread': 2.2.3
|
||||
|
@ -12932,7 +12938,7 @@ packages:
|
|||
lodash: 4.17.21
|
||||
pretty-bytes: 5.6.0
|
||||
rollup: 2.79.1
|
||||
rollup-plugin-terser: 7.0.2_rollup@2.79.1
|
||||
rollup-plugin-terser: 7.0.2_acorn@8.8.0+rollup@2.79.1
|
||||
source-map: 0.8.0-beta.0
|
||||
stringify-object: 3.3.0
|
||||
strip-comments: 2.0.1
|
||||
|
@ -12955,6 +12961,7 @@ packages:
|
|||
workbox-window: 6.5.4
|
||||
transitivePeerDependencies:
|
||||
- '@types/babel__core'
|
||||
- acorn
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
|
@ -12964,7 +12971,7 @@ packages:
|
|||
workbox-core: 6.5.4
|
||||
dev: true
|
||||
|
||||
/workbox-cli/6.5.4:
|
||||
/workbox-cli/6.5.4_acorn@8.8.0:
|
||||
resolution: {integrity: sha512-+Cc0jYh25MofhCROZqfQkpYSAGvykyrUVekuuPaLFbJ8qxX/zzX8hRRpglfwxDwokAjz8S20oEph4s+MyQc+Yw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
hasBin: true
|
||||
|
@ -12981,9 +12988,10 @@ packages:
|
|||
stringify-object: 3.3.0
|
||||
upath: 1.2.0
|
||||
update-notifier: 4.1.3
|
||||
workbox-build: 6.5.4
|
||||
workbox-build: 6.5.4_acorn@8.8.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/babel__core'
|
||||
- acorn
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<vue-easymde
|
||||
:configs="config"
|
||||
@change="bubble"
|
||||
@change="() => bubble()"
|
||||
@update:modelValue="handleInput"
|
||||
class="content"
|
||||
v-if="isEditActive"
|
||||
|
@ -66,245 +66,245 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue'
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, onMounted, ref, toRefs, watch} from 'vue'
|
||||
|
||||
import VueEasymde from './vue-easymde.vue'
|
||||
import {marked} from 'marked'
|
||||
import DOMPurify from 'dompurify'
|
||||
import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
|
||||
|
||||
import {createEasyMDEConfig} from './editorConfig'
|
||||
|
||||
import AttachmentModel from '../../models/attachment'
|
||||
import AttachmentService from '../../services/attachment'
|
||||
import {findCheckboxesInText} from '../../helpers/checklistFromText'
|
||||
import AttachmentModel from '@/models/attachment'
|
||||
import AttachmentService from '@/services/attachment'
|
||||
|
||||
import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
|
||||
import {findCheckboxesInText} from '@/helpers/checklistFromText'
|
||||
import {createRandomID} from '@/helpers/randomId'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import ButtonLink from '@/components/misc/ButtonLink.vue'
|
||||
import type { IAttachment } from '@/modelTypes/IAttachment'
|
||||
import type { ITask } from '@/modelTypes/ITask'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editor',
|
||||
components: {
|
||||
VueEasymde,
|
||||
BaseButton,
|
||||
ButtonLink,
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
uploadEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
uploadCallback: {
|
||||
type: Function,
|
||||
},
|
||||
hasPreview: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
previewIsDefault: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isEditEnabled: {
|
||||
default: true,
|
||||
},
|
||||
bottomActions: {
|
||||
default: () => [],
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showSave: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// If a key is passed the editor will go in "edit" mode when the key is pressed.
|
||||
// Disabled if an empty string is passed.
|
||||
editShortcut: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
computed: {
|
||||
showPreviewText() {
|
||||
return this.isPreviewActive && this.text === '' && this.emptyText !== ''
|
||||
},
|
||||
showEditButton() {
|
||||
return !this.isEditActive && this.text !== ''
|
||||
},
|
||||
uploadEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: '',
|
||||
changeTimeout: null,
|
||||
isEditActive: false,
|
||||
isPreviewActive: true,
|
||||
|
||||
preview: '',
|
||||
attachmentService: null,
|
||||
loadedAttachments: {},
|
||||
config: createEasyMDEConfig({
|
||||
placeholder: this.placeholder,
|
||||
uploadImage: this.uploadEnabled,
|
||||
imageUploadFunction: this.uploadCallback,
|
||||
}),
|
||||
checkboxId: createRandomID(),
|
||||
}
|
||||
uploadCallback: {
|
||||
type: Function,
|
||||
},
|
||||
watch: {
|
||||
modelValue(modelValue) {
|
||||
this.text = modelValue
|
||||
this.$nextTick(this.renderPreview)
|
||||
},
|
||||
text(newVal, oldVal) {
|
||||
// Only bubble the new value if it actually changed, but not if the component just got mounted and the text changed from the outside.
|
||||
if (oldVal === '' && this.text === this.modelValue) {
|
||||
return
|
||||
}
|
||||
this.bubble()
|
||||
},
|
||||
hasPreview: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
mounted() {
|
||||
if (this.modelValue !== '') {
|
||||
this.text = this.modelValue
|
||||
}
|
||||
|
||||
if (this.previewIsDefault && this.hasPreview) {
|
||||
this.$nextTick(this.renderPreview)
|
||||
return
|
||||
}
|
||||
|
||||
this.isPreviewActive = false
|
||||
this.isEditActive = true
|
||||
previewIsDefault: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
methods: {
|
||||
// This gets triggered when only pasting content into the editor.
|
||||
// A change event would not get generated by that, an input event does.
|
||||
// Therefore, we're using this handler to catch paste events.
|
||||
// But because this also gets triggered when typing into the editor, we give
|
||||
// it a higher timeout to make the timouts cancel each other in that case so
|
||||
// that in the end, only one change event is triggered to the outside per change.
|
||||
handleInput(val) {
|
||||
// Don't bubble if the text is up to date
|
||||
if (val === this.text) {
|
||||
return
|
||||
}
|
||||
|
||||
this.text = val
|
||||
this.bubble(1000)
|
||||
},
|
||||
bubble(timeout = 500) {
|
||||
if (this.changeTimeout !== null) {
|
||||
clearTimeout(this.changeTimeout)
|
||||
}
|
||||
|
||||
this.changeTimeout = setTimeout(() => {
|
||||
this.$emit('update:modelValue', this.text)
|
||||
}, timeout)
|
||||
},
|
||||
replaceAt(str, index, replacement) {
|
||||
return str.substr(0, index) + replacement + str.substr(index + replacement.length)
|
||||
},
|
||||
findNthIndex(str, n) {
|
||||
const checkboxes = findCheckboxesInText(str)
|
||||
return checkboxes[n]
|
||||
},
|
||||
renderPreview() {
|
||||
setupMarkdownRenderer(this.checkboxId)
|
||||
|
||||
this.preview = DOMPurify.sanitize(marked(this.text), {ADD_ATTR: ['target']})
|
||||
|
||||
// Since the render function is synchronous, we can't do async http requests in it.
|
||||
// Therefore, we can't resolve the blob url at (markdown) compile time.
|
||||
// To work around this, we modify the url after rendering it in the vue component.
|
||||
// We're doing the whole thing in the next tick to ensure the image elements are available in the
|
||||
// dom tree. If we're calling this right after setting this.preview it could be the images were
|
||||
// not already made available.
|
||||
// Some docs at https://stackoverflow.com/q/62865160/10924593
|
||||
this.$nextTick(async () => {
|
||||
const attachmentImage = document.getElementsByClassName('attachment-image')
|
||||
if (attachmentImage) {
|
||||
for (const img of attachmentImage) {
|
||||
// The url is something like /tasks/<id>/attachments/<id>
|
||||
const parts = img.dataset.src.substr(window.API_URL.length + 1).split('/')
|
||||
const taskId = parseInt(parts[1])
|
||||
const attachmentId = parseInt(parts[3])
|
||||
const cacheKey = `${taskId}-${attachmentId}`
|
||||
|
||||
if (typeof this.loadedAttachments[cacheKey] !== 'undefined') {
|
||||
img.src = this.loadedAttachments[cacheKey]
|
||||
continue
|
||||
}
|
||||
|
||||
const attachment = new AttachmentModel({taskId: taskId, id: attachmentId})
|
||||
|
||||
if (this.attachmentService === null) {
|
||||
this.attachmentService = new AttachmentService()
|
||||
}
|
||||
|
||||
const url = await this.attachmentService.getBlobUrl(attachment)
|
||||
img.src = url
|
||||
this.loadedAttachments[cacheKey] = url
|
||||
}
|
||||
}
|
||||
|
||||
const textCheckbox = document.getElementsByClassName(`text-checkbox-${this.checkboxId}`)
|
||||
if (textCheckbox) {
|
||||
for (const check of textCheckbox) {
|
||||
check.removeEventListener('change', this.handleCheckboxClick)
|
||||
check.addEventListener('change', this.handleCheckboxClick)
|
||||
check.parentElement.classList.add('has-checkbox')
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCheckboxClick(e) {
|
||||
// Find the original markdown checkbox this is targeting
|
||||
const checked = e.target.checked
|
||||
const numMarkdownCheck = parseInt(e.target.dataset.checkboxNum)
|
||||
|
||||
const index = this.findNthIndex(this.text, numMarkdownCheck)
|
||||
if (index < 0 || typeof index === 'undefined') {
|
||||
console.debug('no index found')
|
||||
return
|
||||
}
|
||||
console.debug(index, this.text.substr(index, 9))
|
||||
|
||||
const listPrefix = this.text.substr(index, 1)
|
||||
|
||||
if (checked) {
|
||||
this.text = this.replaceAt(this.text, index, `${listPrefix} [x] `)
|
||||
} else {
|
||||
this.text = this.replaceAt(this.text, index, `${listPrefix} [ ] `)
|
||||
}
|
||||
this.bubble()
|
||||
this.renderPreview()
|
||||
},
|
||||
toggleEdit() {
|
||||
if (this.isEditActive) {
|
||||
this.isPreviewActive = true
|
||||
this.isEditActive = false
|
||||
this.renderPreview()
|
||||
this.bubble(0) // save instantly
|
||||
} else {
|
||||
this.isPreviewActive = false
|
||||
this.isEditActive = true
|
||||
}
|
||||
},
|
||||
isEditEnabled: {
|
||||
default: true,
|
||||
},
|
||||
bottomActions: {
|
||||
default: () => [],
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showSave: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// If a key is passed the editor will go in "edit" mode when the key is pressed.
|
||||
// Disabled if an empty string is passed.
|
||||
editShortcut: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const text = ref('')
|
||||
const changeTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
|
||||
const isEditActive = ref(false)
|
||||
const isPreviewActive = ref(true)
|
||||
|
||||
const showPreviewText = computed(() => isPreviewActive.value && text.value === '' && props.emptyText !== '')
|
||||
const showEditButton = computed(() => !isEditActive.value && text.value !== '')
|
||||
|
||||
const preview = ref('')
|
||||
const attachmentService = new AttachmentService()
|
||||
|
||||
type CacheKey = `${ITask['id']}-${IAttachment['id']}`
|
||||
const loadedAttachments = ref<{[key: CacheKey]: string}>({})
|
||||
const config = ref(createEasyMDEConfig({
|
||||
placeholder: props.placeholder,
|
||||
uploadImage: props.uploadEnabled,
|
||||
imageUploadFunction: props.uploadCallback,
|
||||
}))
|
||||
|
||||
const checkboxId = ref(createRandomID())
|
||||
|
||||
const {modelValue} = toRefs(props)
|
||||
|
||||
watch(
|
||||
modelValue,
|
||||
async (value) => {
|
||||
text.value = value
|
||||
await nextTick()
|
||||
renderPreview()
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
text,
|
||||
(newVal, oldVal) => {
|
||||
// Only bubble the new value if it actually changed, but not if the component just got mounted and the text changed from the outside.
|
||||
if (oldVal === '' && text.value === modelValue.value) {
|
||||
return
|
||||
}
|
||||
bubble()
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
if (modelValue.value !== '') {
|
||||
text.value = modelValue.value
|
||||
}
|
||||
|
||||
if (props.previewIsDefault && props.hasPreview) {
|
||||
nextTick(() => renderPreview())
|
||||
return
|
||||
}
|
||||
|
||||
isPreviewActive.value = false
|
||||
isEditActive.value = true
|
||||
})
|
||||
|
||||
|
||||
// This gets triggered when only pasting content into the editor.
|
||||
// A change event would not get generated by that, an input event does.
|
||||
// Therefore, we're using this handler to catch paste events.
|
||||
// But because this also gets triggered when typing into the editor, we give
|
||||
// it a higher timeout to make the timouts cancel each other in that case so
|
||||
// that in the end, only one change event is triggered to the outside per change.
|
||||
function handleInput(val: string) {
|
||||
// Don't bubble if the text is up to date
|
||||
if (val === text.value) {
|
||||
return
|
||||
}
|
||||
|
||||
text.value = val
|
||||
bubble(1000)
|
||||
}
|
||||
|
||||
function bubble(timeout = 500) {
|
||||
if (changeTimeout.value !== null) {
|
||||
clearTimeout(changeTimeout.value)
|
||||
}
|
||||
|
||||
changeTimeout.value = setTimeout(() => {
|
||||
emit('update:modelValue', text.value)
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
function replaceAt(str: string, index: number, replacement: string) {
|
||||
return str.slice(0, index) + replacement + str.slice(index + replacement.length)
|
||||
}
|
||||
|
||||
function findNthIndex(str: string, n: number) {
|
||||
const checkboxes = findCheckboxesInText(str)
|
||||
return checkboxes[n]
|
||||
}
|
||||
|
||||
function renderPreview() {
|
||||
setupMarkdownRenderer(checkboxId.value)
|
||||
|
||||
preview.value = DOMPurify.sanitize(marked(text.value), {ADD_ATTR: ['target']})
|
||||
|
||||
// Since the render function is synchronous, we can't do async http requests in it.
|
||||
// Therefore, we can't resolve the blob url at (markdown) compile time.
|
||||
// To work around this, we modify the url after rendering it in the vue component.
|
||||
// We're doing the whole thing in the next tick to ensure the image elements are available in the
|
||||
// dom tree. If we're calling this right after setting this.preview it could be the images were
|
||||
// not already made available.
|
||||
// Some docs at https://stackoverflow.com/q/62865160/10924593
|
||||
nextTick().then(async () => {
|
||||
const attachmentImage = document.querySelectorAll<HTMLImageElement>('.attachment-image')
|
||||
if (attachmentImage) {
|
||||
Array.from(attachmentImage).forEach(async (img) => {
|
||||
// The url is something like /tasks/<id>/attachments/<id>
|
||||
const parts = img.dataset.src?.slice(window.API_URL.length + 1).split('/')
|
||||
const taskId = Number(parts[1])
|
||||
const attachmentId = Number(parts[3])
|
||||
const cacheKey: CacheKey = `${taskId}-${attachmentId}`
|
||||
|
||||
if (typeof loadedAttachments.value[cacheKey] !== 'undefined') {
|
||||
img.src = loadedAttachments.value[cacheKey]
|
||||
return
|
||||
}
|
||||
|
||||
const attachment = new AttachmentModel({taskId: taskId, id: attachmentId})
|
||||
|
||||
const url = await attachmentService.getBlobUrl(attachment)
|
||||
img.src = url
|
||||
loadedAttachments.value[cacheKey] = url
|
||||
})
|
||||
}
|
||||
|
||||
const textCheckbox = document.querySelectorAll<HTMLInputElement>(`.text-checkbox-${checkboxId.value}`)
|
||||
if (textCheckbox) {
|
||||
Array.from(textCheckbox).forEach(check => {
|
||||
check.removeEventListener('change', handleCheckboxClick)
|
||||
check.addEventListener('change', handleCheckboxClick)
|
||||
check.parentElement?.classList.add('has-checkbox')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleCheckboxClick(e: Event) {
|
||||
// Find the original markdown checkbox this is targeting
|
||||
const checked = (e.target as HTMLInputElement).checked
|
||||
const numMarkdownCheck = Number((e.target as HTMLInputElement).dataset.checkboxNum)
|
||||
|
||||
const index = findNthIndex(text.value, numMarkdownCheck)
|
||||
if (index < 0 || typeof index === 'undefined') {
|
||||
console.debug('no index found')
|
||||
return
|
||||
}
|
||||
console.debug(index, text.value.slice(index, 9))
|
||||
|
||||
const listPrefix = text.value.slice(index, 1)
|
||||
|
||||
text.value = replaceAt(text.value, index, `${listPrefix} ${checked ? '[x]' : '[ ]'} `)
|
||||
bubble()
|
||||
renderPreview()
|
||||
}
|
||||
|
||||
function toggleEdit() {
|
||||
if (isEditActive.value) {
|
||||
isPreviewActive.value = true
|
||||
isEditActive.value = false
|
||||
renderPreview()
|
||||
bubble(0) // save instantly
|
||||
} else {
|
||||
isPreviewActive.value = false
|
||||
isEditActive.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -8,33 +8,34 @@ export function setupMarkdownRenderer(checkboxId: string) {
|
|||
let checkboxNum = -1
|
||||
marked.use({
|
||||
renderer: {
|
||||
image: (src, title, text) => {
|
||||
image(src: string, title: string, text: string) {
|
||||
|
||||
title = title ? ` title="${title}` : ''
|
||||
|
||||
// If the url starts with the api url, the image is likely an attachment and
|
||||
// we'll need to download and parse it properly.
|
||||
if (src.substr(0, window.API_URL.length + 7) === `${window.API_URL}/tasks/`) {
|
||||
if (src.slice(0, window.API_URL.length + 7) === `${window.API_URL}/tasks/`) {
|
||||
return `<img data-src="${src}" alt="${text}" ${title} class="attachment-image"/>`
|
||||
}
|
||||
|
||||
return `<img src="${src}" alt="${text}" ${title}/>`
|
||||
},
|
||||
checkbox: (checked) => {
|
||||
checkbox(checked: boolean) {
|
||||
let checkedString = ''
|
||||
if (checked) {
|
||||
checked = ' checked="checked"'
|
||||
checkedString = 'checked'
|
||||
}
|
||||
|
||||
checkboxNum++
|
||||
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checked} class="text-checkbox-${checkboxId}"/>`
|
||||
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checkedString} class="text-checkbox-${checkboxId}"/>`
|
||||
},
|
||||
link: (href, title, text) => {
|
||||
link(href: string, title: string, text: string) {
|
||||
const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`)
|
||||
const html = linkRenderer.call(renderer, href, title, text)
|
||||
return isLocal ? html : html.replace(/^<a /, '<a target="_blank" rel="noreferrer noopener nofollow" ')
|
||||
},
|
||||
},
|
||||
highlight: function (code, language) {
|
||||
highlight(code, language) {
|
||||
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext'
|
||||
return hljs.highlight(code, {language: validLanguage}).value
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const DEFAULT_ID_LENGTH = 9
|
||||
|
||||
export function createRandomID(idLength = DEFAULT_ID_LENGTH) {
|
||||
return Math.random().toString(36).substr(2, idLength)
|
||||
return Math.random().toString(36).slice(2, idLength)
|
||||
}
|
|
@ -285,7 +285,7 @@ const getDateFromWeekday = (text: string): dateFoundResult => {
|
|||
// matched string comes with a space at the end (last part of the regex).
|
||||
let foundText = results[0]
|
||||
if (foundText.endsWith(' ')) {
|
||||
foundText = foundText.substr(0, foundText.length - 1)
|
||||
foundText = foundText.slice(0, foundText.length - 1)
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -34,8 +34,8 @@ if (apiUrlFromStorage !== null) {
|
|||
}
|
||||
|
||||
// Make sure the api url does not contain a / at the end
|
||||
if (window.API_URL.substr(window.API_URL.length - 1, window.API_URL.length) === '/') {
|
||||
window.API_URL = window.API_URL.substr(0, window.API_URL.length - 1)
|
||||
if (window.API_URL.slice(window.API_URL.length - 1, window.API_URL.length) === '/') {
|
||||
window.API_URL = window.API_URL.slice(0, window.API_URL.length - 1)
|
||||
}
|
||||
|
||||
const app = createApp(App)
|
||||
|
|
Loading…
Reference in a new issue