mirror of
https://github.com/go-vikunja/vikunja.git
synced 2025-12-05 19:16:51 -06:00
Merge branch 'main' into feat-custom-keyboard-shortcuts
This commit is contained in:
2
.github/workflows/pr-docker.yml
vendored
2
.github/workflows/pr-docker.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
version: latest
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
|
||||
with:
|
||||
images: ghcr.io/go-vikunja/vikunja
|
||||
tags: |
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Docker meta version
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
id: meta
|
||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
|
||||
with:
|
||||
images: |
|
||||
vikunja/vikunja
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -362,10 +362,10 @@ jobs:
|
||||
ports:
|
||||
- 5556:5556
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.57.0-jammy
|
||||
image: mcr.microsoft.com/playwright:v1.57.0-jammy@sha256:6aca677c27a967caf7673d108ac67ffaf8fed134f27e17b27a05464ca0ace831
|
||||
options: --user 1001
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
||||
- name: Download Vikunja Binary
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
with:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"main": "main.js",
|
||||
"repository": "https://code.vikunja.io/desktop",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"packageManager": "pnpm@10.22.0",
|
||||
"packageManager": "pnpm@10.23.0",
|
||||
"author": {
|
||||
"email": "maintainers@vikunja.io",
|
||||
"name": "Vikunja Team"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"homepage": "https://vikunja.io/",
|
||||
"funding": "https://opencollective.com/vikunja",
|
||||
"packageManager": "pnpm@10.22.0",
|
||||
"packageManager": "pnpm@10.23.0",
|
||||
"keywords": [
|
||||
"todo",
|
||||
"productivity",
|
||||
@@ -93,7 +93,7 @@
|
||||
"is-touch-device": "1.0.1",
|
||||
"klona": "2.0.6",
|
||||
"lowlight": "3.3.0",
|
||||
"marked": "16.4.2",
|
||||
"marked": "17.0.1",
|
||||
"pinia": "3.0.4",
|
||||
"register-service-worker": "1.7.2",
|
||||
"sortablejs": "1.15.6",
|
||||
@@ -102,10 +102,10 @@
|
||||
"vue": "3.5.24",
|
||||
"vue-advanced-cropper": "2.8.9",
|
||||
"vue-flatpickr-component": "11.0.5",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-i18n": "11.2.1",
|
||||
"vue-router": "4.6.3",
|
||||
"vuemoji-picker": "0.3.2",
|
||||
"workbox-precaching": "7.3.0",
|
||||
"workbox-precaching": "7.4.0",
|
||||
"zhyswan-vuedraggable": "4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
99
frontend/pnpm-lock.yaml
generated
99
frontend/pnpm-lock.yaml
generated
@@ -36,7 +36,7 @@ importers:
|
||||
version: 3.1.1(patch_hash=145ab3233cbcd3bc934b4961cd8710e2b15e4ae5dd20862a8d1d6621d7f9d4a8)
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: 11.0.1
|
||||
version: 11.0.1(@vue/compiler-dom@3.5.24)(eslint@9.39.1(jiti@2.4.2))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))
|
||||
version: 11.0.1(@vue/compiler-dom@3.5.24)(eslint@9.39.1(jiti@2.4.2))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))
|
||||
'@kyvg/vue3-notification':
|
||||
specifier: 3.4.2
|
||||
version: 3.4.2(vue@3.5.24(typescript@5.9.3))
|
||||
@@ -134,8 +134,8 @@ importers:
|
||||
specifier: 3.3.0
|
||||
version: 3.3.0
|
||||
marked:
|
||||
specifier: 16.4.2
|
||||
version: 16.4.2
|
||||
specifier: 17.0.1
|
||||
version: 17.0.1
|
||||
pinia:
|
||||
specifier: 3.0.4
|
||||
version: 3.0.4(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
||||
@@ -161,8 +161,8 @@ importers:
|
||||
specifier: 11.0.5
|
||||
version: 11.0.5(vue@3.5.24(typescript@5.9.3))
|
||||
vue-i18n:
|
||||
specifier: 11.1.12
|
||||
version: 11.1.12(vue@3.5.24(typescript@5.9.3))
|
||||
specifier: 11.2.1
|
||||
version: 11.2.1(vue@3.5.24(typescript@5.9.3))
|
||||
vue-router:
|
||||
specifier: 4.6.3
|
||||
version: 4.6.3(vue@3.5.24(typescript@5.9.3))
|
||||
@@ -170,8 +170,8 @@ importers:
|
||||
specifier: 0.3.2
|
||||
version: 0.3.2(vue@3.5.24(typescript@5.9.3))
|
||||
workbox-precaching:
|
||||
specifier: 7.3.0
|
||||
version: 7.3.0
|
||||
specifier: 7.4.0
|
||||
version: 7.4.0
|
||||
zhyswan-vuedraggable:
|
||||
specifier: 4.1.3
|
||||
version: 4.1.3(vue@3.5.24(typescript@5.9.3))
|
||||
@@ -1722,18 +1722,26 @@ packages:
|
||||
vue-i18n:
|
||||
optional: true
|
||||
|
||||
'@intlify/core-base@11.1.12':
|
||||
resolution: {integrity: sha512-whh0trqRsSqVLNEUCwU59pyJZYpU8AmSWl8M3Jz2Mv5ESPP6kFh4juas2NpZ1iCvy7GlNRffUD1xr84gceimjg==}
|
||||
'@intlify/core-base@11.2.1':
|
||||
resolution: {integrity: sha512-2V1A4yaN9ElAnQ6ih3HHEc+jZ+sHV6BlQHjCsnIVlOotL5NCUgJElIxgUFiJs6zV4puoAq3hHuQIfWNp+J+8yQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@11.1.12':
|
||||
resolution: {integrity: sha512-Fv9iQSJoJaXl4ZGkOCN1LDM3trzze0AS2zRz2EHLiwenwL6t0Ki9KySYlyr27yVOj5aVz0e55JePO+kELIvfdQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@11.2.1':
|
||||
resolution: {integrity: sha512-J2454D3Agg3Kvgaj14gxTleJU8/H06Sisz7C2BwiHF0/i5Soyfb5ySpwn8GCL6yscDbOGj6xM+lUe6gO6BFQyg==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@11.1.12':
|
||||
resolution: {integrity: sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@11.2.1':
|
||||
resolution: {integrity: sha512-O67LZM4dbfr70WCsZLW+g+pIXdgQ66laLVd/FicW7iYgP/RuH0X1FDGSh+Hr9Gou/8TeldUE6KmTGdLwX2ufIA==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.1':
|
||||
resolution: {integrity: sha512-nH5NJdNjy/lO6Ne8LDtZzv4SbpVsMhPE+LbvBDmMeIeJDiino8sOJN2QB3MXzTliYTnqe3aB9Fw5+LJ/XVaXCg==}
|
||||
engines: {node: '>= 20'}
|
||||
@@ -4836,8 +4844,8 @@ packages:
|
||||
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
|
||||
hasBin: true
|
||||
|
||||
marked@16.4.2:
|
||||
resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
|
||||
marked@17.0.1:
|
||||
resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==}
|
||||
engines: {node: '>= 20'}
|
||||
hasBin: true
|
||||
|
||||
@@ -6838,8 +6846,8 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
vue-i18n@11.1.12:
|
||||
resolution: {integrity: sha512-BnstPj3KLHLrsqbVU2UOrPmr0+Mv11bsUZG0PyCOzsawCivk8W00GMXHeVUWIDOgNaScCuZah47CZFE+Wnl8mw==}
|
||||
vue-i18n@11.2.1:
|
||||
resolution: {integrity: sha512-cc3Wx4eJZac9WMS8mxhfYiCipm9PBQ2Dz15piWYm7DwNcCehaKRgpolEdiqrjjT27T3Wijz3xJ7NeIc8ofIWAA==}
|
||||
engines: {node: '>= 16'}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
@@ -6986,9 +6994,6 @@ packages:
|
||||
engines: {node: '>=20.0.0'}
|
||||
hasBin: true
|
||||
|
||||
workbox-core@7.3.0:
|
||||
resolution: {integrity: sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw==}
|
||||
|
||||
workbox-core@7.4.0:
|
||||
resolution: {integrity: sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==}
|
||||
|
||||
@@ -7001,9 +7006,6 @@ packages:
|
||||
workbox-navigation-preload@7.4.0:
|
||||
resolution: {integrity: sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==}
|
||||
|
||||
workbox-precaching@7.3.0:
|
||||
resolution: {integrity: sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw==}
|
||||
|
||||
workbox-precaching@7.4.0:
|
||||
resolution: {integrity: sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==}
|
||||
|
||||
@@ -7013,15 +7015,9 @@ packages:
|
||||
workbox-recipes@7.4.0:
|
||||
resolution: {integrity: sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==}
|
||||
|
||||
workbox-routing@7.3.0:
|
||||
resolution: {integrity: sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A==}
|
||||
|
||||
workbox-routing@7.4.0:
|
||||
resolution: {integrity: sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==}
|
||||
|
||||
workbox-strategies@7.3.0:
|
||||
resolution: {integrity: sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg==}
|
||||
|
||||
workbox-strategies@7.4.0:
|
||||
resolution: {integrity: sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==}
|
||||
|
||||
@@ -8627,7 +8623,7 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.2': {}
|
||||
|
||||
'@intlify/bundle-utils@11.0.1(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))':
|
||||
'@intlify/bundle-utils@11.0.1(vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 11.1.12
|
||||
'@intlify/shared': 11.1.12
|
||||
@@ -8639,26 +8635,33 @@ snapshots:
|
||||
source-map-js: 1.2.1
|
||||
yaml-eslint-parser: 1.2.3
|
||||
optionalDependencies:
|
||||
vue-i18n: 11.1.12(vue@3.5.24(typescript@5.9.3))
|
||||
vue-i18n: 11.2.1(vue@3.5.24(typescript@5.9.3))
|
||||
|
||||
'@intlify/core-base@11.1.12':
|
||||
'@intlify/core-base@11.2.1':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 11.1.12
|
||||
'@intlify/shared': 11.1.12
|
||||
'@intlify/message-compiler': 11.2.1
|
||||
'@intlify/shared': 11.2.1
|
||||
|
||||
'@intlify/message-compiler@11.1.12':
|
||||
dependencies:
|
||||
'@intlify/shared': 11.1.12
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@intlify/message-compiler@11.2.1':
|
||||
dependencies:
|
||||
'@intlify/shared': 11.2.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@intlify/shared@11.1.12': {}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.1(@vue/compiler-dom@3.5.24)(eslint@9.39.1(jiti@2.4.2))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))':
|
||||
'@intlify/shared@11.2.1': {}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@11.0.1(@vue/compiler-dom@3.5.24)(eslint@9.39.1(jiti@2.4.2))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.39.1(jiti@2.4.2))
|
||||
'@intlify/bundle-utils': 11.0.1(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))
|
||||
'@intlify/bundle-utils': 11.0.1(vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)))
|
||||
'@intlify/shared': 11.1.12
|
||||
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.24)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))
|
||||
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.24)(vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))
|
||||
'@rollup/pluginutils': 5.1.3(rollup@4.53.3)
|
||||
'@typescript-eslint/scope-manager': 8.40.0
|
||||
'@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.3)
|
||||
@@ -8669,7 +8672,7 @@ snapshots:
|
||||
unplugin: 2.3.10
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
vue-i18n: 11.1.12(vue@3.5.24(typescript@5.9.3))
|
||||
vue-i18n: 11.2.1(vue@3.5.24(typescript@5.9.3))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/compiler-dom'
|
||||
- eslint
|
||||
@@ -8677,14 +8680,14 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.24)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))':
|
||||
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.24)(vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.3
|
||||
optionalDependencies:
|
||||
'@intlify/shared': 11.1.12
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
vue-i18n: 11.1.12(vue@3.5.24(typescript@5.9.3))
|
||||
vue-i18n: 11.2.1(vue@3.5.24(typescript@5.9.3))
|
||||
|
||||
'@isaacs/balanced-match@4.0.1': {}
|
||||
|
||||
@@ -12053,7 +12056,7 @@ snapshots:
|
||||
punycode.js: 2.3.1
|
||||
uc.micro: 2.1.0
|
||||
|
||||
marked@16.4.2: {}
|
||||
marked@17.0.1: {}
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
@@ -14291,10 +14294,10 @@ snapshots:
|
||||
flatpickr: 4.6.13
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
|
||||
vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)):
|
||||
vue-i18n@11.2.1(vue@3.5.24(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 11.1.12
|
||||
'@intlify/shared': 11.1.12
|
||||
'@intlify/core-base': 11.2.1
|
||||
'@intlify/shared': 11.2.1
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
|
||||
@@ -14505,8 +14508,6 @@ snapshots:
|
||||
- '@types/babel__core'
|
||||
- supports-color
|
||||
|
||||
workbox-core@7.3.0: {}
|
||||
|
||||
workbox-core@7.4.0: {}
|
||||
|
||||
workbox-expiration@7.4.0:
|
||||
@@ -14525,12 +14526,6 @@ snapshots:
|
||||
dependencies:
|
||||
workbox-core: 7.4.0
|
||||
|
||||
workbox-precaching@7.3.0:
|
||||
dependencies:
|
||||
workbox-core: 7.3.0
|
||||
workbox-routing: 7.3.0
|
||||
workbox-strategies: 7.3.0
|
||||
|
||||
workbox-precaching@7.4.0:
|
||||
dependencies:
|
||||
workbox-core: 7.4.0
|
||||
@@ -14550,18 +14545,10 @@ snapshots:
|
||||
workbox-routing: 7.4.0
|
||||
workbox-strategies: 7.4.0
|
||||
|
||||
workbox-routing@7.3.0:
|
||||
dependencies:
|
||||
workbox-core: 7.3.0
|
||||
|
||||
workbox-routing@7.4.0:
|
||||
dependencies:
|
||||
workbox-core: 7.4.0
|
||||
|
||||
workbox-strategies@7.3.0:
|
||||
dependencies:
|
||||
workbox-core: 7.3.0
|
||||
|
||||
workbox-strategies@7.4.0:
|
||||
dependencies:
|
||||
workbox-core: 7.4.0
|
||||
|
||||
@@ -145,7 +145,7 @@ import {eventToHotkeyString} from '@github/hotkey'
|
||||
import EditorToolbar from './EditorToolbar.vue'
|
||||
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import {Extension, mergeAttributes} from '@tiptap/core'
|
||||
import {Extension, mergeAttributes, type SetContentOptions} from '@tiptap/core'
|
||||
import {EditorContent, type Extensions, useEditor, VueNodeViewRenderer} from '@tiptap/vue-3'
|
||||
import {Plugin, PluginKey} from '@tiptap/pm/state'
|
||||
import {marked} from 'marked'
|
||||
@@ -216,6 +216,12 @@ const tiptapInstanceRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const defaultSetContentOptions: SetContentOptions = {
|
||||
parseOptions: {
|
||||
preserveWhitespace: true,
|
||||
},
|
||||
}
|
||||
|
||||
const CustomTableCell = TableCell.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
@@ -549,6 +555,9 @@ const editor = useEditor({
|
||||
onUpdate: () => {
|
||||
bubbleNow()
|
||||
},
|
||||
parseOptions: {
|
||||
preserveWhitespace: true,
|
||||
},
|
||||
})
|
||||
|
||||
watch(
|
||||
@@ -606,7 +615,10 @@ function bubbleSave() {
|
||||
}
|
||||
|
||||
function exitEditMode() {
|
||||
editor.value?.commands.setContent(lastSavedState, {emitUpdate: false})
|
||||
editor.value?.commands.setContent(lastSavedState, {
|
||||
...defaultSetContentOptions,
|
||||
emitUpdate: false,
|
||||
})
|
||||
|
||||
// Clear draft from localStorage when discarding changes
|
||||
if (props.storageKey) {
|
||||
@@ -659,7 +671,10 @@ function uploadAndInsertFiles(files: File[] | FileList) {
|
||||
|
||||
const html = editor.value?.getHTML().replace(UPLOAD_PLACEHOLDER_ELEMENT, '') ?? ''
|
||||
|
||||
editor.value?.commands.setContent(html, {emitUpdate: false})
|
||||
editor.value?.commands.setContent(html, {
|
||||
...defaultSetContentOptions,
|
||||
emitUpdate: false,
|
||||
})
|
||||
|
||||
bubbleNow()
|
||||
})
|
||||
@@ -733,7 +748,10 @@ onBeforeUnmount(() => {
|
||||
|
||||
function setModeAndValue(value: string) {
|
||||
internalMode.value = isEditorContentEmpty(value) ? 'edit' : 'preview'
|
||||
editor.value?.commands.setContent(value, {emitUpdate: false})
|
||||
editor.value?.commands.setContent(value, {
|
||||
...defaultSetContentOptions,
|
||||
emitUpdate: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="editEnabled"
|
||||
:class="{hidden: !isOverDropZone}"
|
||||
:class="{hidden: !showDropzone}"
|
||||
class="dropzone"
|
||||
>
|
||||
<div class="drop-hint">
|
||||
@@ -172,8 +172,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowReactive, computed} from 'vue'
|
||||
import {useDropZone} from '@/composables/useDropzone'
|
||||
import {ref, shallowReactive, computed, watch} from 'vue'
|
||||
import {useDropZone} from '@vueuse/core'
|
||||
|
||||
import User from '@/components/misc/User.vue'
|
||||
import ProgressBar from '@/components/misc/ProgressBar.vue'
|
||||
@@ -205,6 +205,28 @@ const props = withDefaults(defineProps<{
|
||||
const emit = defineEmits<{
|
||||
'taskChanged': [ITask],
|
||||
}>()
|
||||
|
||||
const EDITOR_SELECTOR = '.tiptap, .tiptap__editor, [contenteditable]'
|
||||
|
||||
function eventTargetsEditor(event: Event | null | undefined): boolean {
|
||||
if (!event) {
|
||||
return false
|
||||
}
|
||||
|
||||
const target = event.target
|
||||
if (target instanceof HTMLElement && target.closest(EDITOR_SELECTOR)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (typeof event.composedPath === 'function') {
|
||||
return event.composedPath().some(element =>
|
||||
element instanceof HTMLElement && element.matches(EDITOR_SELECTOR),
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -215,22 +237,67 @@ const attachments = computed(() => attachmentStore.attachments)
|
||||
|
||||
const loading = computed(() => attachmentService.loading || taskStore.isLoading)
|
||||
|
||||
function onDrop(files: File[] | null) {
|
||||
if (files && files.length !== 0) {
|
||||
uploadFilesToTask(files)
|
||||
}
|
||||
const isDraggingFiles = ref(false)
|
||||
const isDragOverEditor = ref(false)
|
||||
|
||||
function resetDragState() {
|
||||
isDraggingFiles.value = false
|
||||
isDragOverEditor.value = false
|
||||
}
|
||||
|
||||
const {isOverDropZone} = useDropZone(document, {
|
||||
onDrop,
|
||||
checkValidity: items => {
|
||||
for (const item of items) {
|
||||
if (item.kind === 'file') {
|
||||
return true
|
||||
onEnter(files, event) {
|
||||
if (!props.editEnabled) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
isDraggingFiles.value = true
|
||||
isDragOverEditor.value = eventTargetsEditor(event)
|
||||
},
|
||||
onOver(files, event) {
|
||||
if (!props.editEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
isDragOverEditor.value = eventTargetsEditor(event)
|
||||
},
|
||||
onLeave(files, event) {
|
||||
if (!props.editEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isOverDropZone.value) {
|
||||
resetDragState()
|
||||
return
|
||||
}
|
||||
|
||||
isDragOverEditor.value = eventTargetsEditor(event)
|
||||
},
|
||||
onDrop(files, event) {
|
||||
if (!props.editEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
const dropOverEditor = eventTargetsEditor(event)
|
||||
resetDragState()
|
||||
|
||||
// Ignore drops over editor - let TipTap handle them
|
||||
if (dropOverEditor || !files || files.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
uploadFilesToTask(files)
|
||||
},
|
||||
})
|
||||
|
||||
const showDropzone = computed(() =>
|
||||
props.editEnabled && isDraggingFiles.value && !isDragOverEditor.value,
|
||||
)
|
||||
|
||||
watch(() => props.editEnabled, enabled => {
|
||||
if (!enabled) {
|
||||
resetDragState()
|
||||
}
|
||||
})
|
||||
|
||||
function downloadAttachment(attachment: IAttachment) {
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
import type { MaybeRef, MaybeRefOrGetter, ShallowRef } from 'vue'
|
||||
import { isClient } from '@vueuse/shared'
|
||||
import { shallowRef, unref } from 'vue'
|
||||
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
|
||||
|
||||
/////////
|
||||
// Temporary copy until https://github.com/vueuse/vueuse/pull/5169 is merged and released
|
||||
////////
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
|
||||
export interface UseDropZoneReturn {
|
||||
files: ShallowRef<File[] | null>
|
||||
isOverDropZone: ShallowRef<boolean>
|
||||
}
|
||||
|
||||
export interface UseDropZoneOptions {
|
||||
/**
|
||||
* Allowed data types, if not set, all data types are allowed.
|
||||
* Also can be a function to check the data types.
|
||||
*/
|
||||
dataTypes?: MaybeRef<readonly string[]> | ((types: readonly string[]) => boolean)
|
||||
/**
|
||||
* Similar to dataTypes, but exposes the DataTransferItemList for custom validation.
|
||||
* If provided, this function takes precedence over dataTypes.
|
||||
*/
|
||||
checkValidity?: (items: DataTransferItemList) => boolean
|
||||
onDrop?: (files: File[] | null, event: DragEvent) => void
|
||||
onEnter?: (files: File[] | null, event: DragEvent) => void
|
||||
onLeave?: (files: File[] | null, event: DragEvent) => void
|
||||
onOver?: (files: File[] | null, event: DragEvent) => void
|
||||
/**
|
||||
* Allow multiple files to be dropped. Defaults to true.
|
||||
*/
|
||||
multiple?: boolean
|
||||
/**
|
||||
* Prevent default behavior for unhandled events. Defaults to false.
|
||||
*/
|
||||
preventDefaultForUnhandled?: boolean
|
||||
}
|
||||
|
||||
export function useDropZone(
|
||||
target: MaybeRefOrGetter<HTMLElement | Document | null | undefined>,
|
||||
options: UseDropZoneOptions | UseDropZoneOptions['onDrop'] = {},
|
||||
): UseDropZoneReturn {
|
||||
const isOverDropZone = shallowRef(false)
|
||||
const files = shallowRef<File[] | null>(null)
|
||||
let counter = 0
|
||||
let isValid = true
|
||||
|
||||
if (isClient) {
|
||||
const _options = typeof options === 'function' ? { onDrop: options } : options
|
||||
const multiple = _options.multiple ?? true
|
||||
const preventDefaultForUnhandled = _options.preventDefaultForUnhandled ?? false
|
||||
|
||||
const getFiles = (event: DragEvent) => {
|
||||
const list = Array.from(event.dataTransfer?.files ?? [])
|
||||
return list.length === 0 ? null : (multiple ? list : [list[0]])
|
||||
}
|
||||
|
||||
const checkDataTypes = (types: string[]) => {
|
||||
const dataTypes = unref(_options.dataTypes)
|
||||
|
||||
if (typeof dataTypes === 'function')
|
||||
return dataTypes(types)
|
||||
|
||||
if (!dataTypes?.length)
|
||||
return true
|
||||
|
||||
if (types.length === 0)
|
||||
return false
|
||||
|
||||
return types.every(type =>
|
||||
dataTypes.some(allowedType => type.includes(allowedType)),
|
||||
)
|
||||
}
|
||||
|
||||
const checkValidity = (items: DataTransferItemList) => {
|
||||
if (_options.checkValidity) {
|
||||
return _options.checkValidity(items)
|
||||
}
|
||||
|
||||
const types = Array.from(items ?? []).map(item => item.type)
|
||||
|
||||
const dataTypesValid = checkDataTypes(types)
|
||||
const multipleFilesValid = multiple || items.length <= 1
|
||||
|
||||
return dataTypesValid && multipleFilesValid
|
||||
}
|
||||
|
||||
const isSafari = () => (
|
||||
/^(?:(?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
&& !('chrome' in window)
|
||||
)
|
||||
|
||||
const handleDragEvent = (event: DragEvent, eventType: 'enter' | 'over' | 'leave' | 'drop') => {
|
||||
const dataTransferItemList = event.dataTransfer?.items
|
||||
isValid = (dataTransferItemList && checkValidity(dataTransferItemList)) ?? false
|
||||
|
||||
if (preventDefaultForUnhandled) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
if (!isSafari() && !isValid) {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'none'
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'copy'
|
||||
}
|
||||
|
||||
const currentFiles = getFiles(event)
|
||||
|
||||
switch (eventType) {
|
||||
case 'enter':
|
||||
counter += 1
|
||||
isOverDropZone.value = true
|
||||
_options.onEnter?.(null, event)
|
||||
break
|
||||
case 'over':
|
||||
_options.onOver?.(null, event)
|
||||
break
|
||||
case 'leave':
|
||||
counter -= 1
|
||||
if (counter === 0)
|
||||
isOverDropZone.value = false
|
||||
_options.onLeave?.(null, event)
|
||||
break
|
||||
case 'drop':
|
||||
counter = 0
|
||||
isOverDropZone.value = false
|
||||
if (isValid) {
|
||||
files.value = currentFiles
|
||||
_options.onDrop?.(currentFiles, event)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
useEventListener<DragEvent>(target, 'dragenter', event => handleDragEvent(event, 'enter'))
|
||||
useEventListener<DragEvent>(target, 'dragover', event => handleDragEvent(event, 'over'))
|
||||
useEventListener<DragEvent>(target, 'dragleave', event => handleDragEvent(event, 'leave'))
|
||||
useEventListener<DragEvent>(target, 'drop', event => handleDragEvent(event, 'drop'))
|
||||
}
|
||||
|
||||
return {
|
||||
files,
|
||||
isOverDropZone,
|
||||
}
|
||||
}
|
||||
@@ -37,15 +37,14 @@ async function login(page: Page): Promise<void> {
|
||||
}
|
||||
|
||||
test.describe('Login', () => {
|
||||
test.beforeEach(async ({apiContext}) => {
|
||||
test.beforeEach(async ({page, apiContext}) => {
|
||||
await UserFactory.create(1, {username: credentials.username})
|
||||
await page.clock.setFixedTime(new Date(1625656161057)) // 13:00
|
||||
})
|
||||
|
||||
test('Should log in with the right credentials', async ({page}) => {
|
||||
await page.goto('/login')
|
||||
await login(page)
|
||||
await page.clock.install({time: new Date(1625656161057)}) // 13:00
|
||||
// Use more specific selector to avoid strict mode violation
|
||||
await expect(page.locator('main h2')).toContainText(`Hi ${credentials.username}!`)
|
||||
})
|
||||
|
||||
|
||||
4
go.mod
4
go.mod
@@ -33,7 +33,7 @@ require (
|
||||
github.com/fclairamb/afero-s3 v0.3.1
|
||||
github.com/gabriel-vasile/mimetype v1.4.11
|
||||
github.com/ganigeorgiev/fexpr v0.5.0
|
||||
github.com/getsentry/sentry-go v0.38.0
|
||||
github.com/getsentry/sentry-go v0.40.0
|
||||
github.com/getsentry/sentry-go/echo v0.38.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.12
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
@@ -58,7 +58,7 @@ require (
|
||||
github.com/olekukonko/tablewriter v1.1.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/spf13/afero v1.15.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -109,6 +109,8 @@ github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv
|
||||
github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||
github.com/getsentry/sentry-go v0.38.0 h1:S8Xui7gLeAvXINVLMOaX94HnsDf1GexnfXGSNC4+KQs=
|
||||
github.com/getsentry/sentry-go v0.38.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
|
||||
github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo=
|
||||
github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
|
||||
github.com/getsentry/sentry-go/echo v0.38.0 h1:ZKvDf3O7jXS+UoeGCBiVVB6J14XWqz+9Dtldstl7FS4=
|
||||
github.com/getsentry/sentry-go/echo v0.38.0/go.mod h1:iEsS3MBdYoeCMXeG94dhpCca0nOdhB6dMMvbk/XMvvo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
@@ -398,6 +400,8 @@ github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7D
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/redis/go-redis/v9 v9.17.0 h1:K6E+ZlYN95KSMmZeEQPbU/c++wfmEvfFB17yEAq/VhM=
|
||||
github.com/redis/go-redis/v9 v9.17.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/redis/go-redis/v9 v9.17.1 h1:7tl732FjYPRT9H9aNfyTwKg9iTETjWjGKEJ2t/5iWTs=
|
||||
github.com/redis/go-redis/v9 v9.17.1/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
|
||||
Reference in New Issue
Block a user