Compare commits

..

89 Commits

Author SHA1 Message Date
renovate b00420d58d fix(deps): update dependency vue to v3.3.7
continuous-integration/drone/pr Build is failing Details
2023-10-25 00:09:16 +00:00
Frederick [Bot] 72f57a220d [skip ci] Updated translations via Crowdin 2023-10-24 00:04:26 +00:00
Frederick [Bot] b94acfcc84 [skip ci] Updated translations via Crowdin 2023-10-23 00:04:16 +00:00
kolaente 5f2787e18d
fix(task): use editor as preview first, then check for edit
continuous-integration/drone/push Build is passing Details
2023-10-22 22:47:52 +02:00
kolaente 2eac17ed57
fix(editor): commands list in dark mode
continuous-integration/drone/push Build is passing Details
2023-10-22 19:58:25 +02:00
renovate 8d566c9371 fix(deps): update dependency @types/is-touch-device to v1.0.1 (#3786)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3786
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 17:51:25 +00:00
renovate ab5118b51b chore(deps): update pnpm to v8.9.2
continuous-integration/drone/push Build is passing Details
2023-10-22 17:42:39 +00:00
kolaente 0b294de132 fix(task): make sure the modal close button is not overlapped with the title field (#3256)
continuous-integration/drone/push Build is passing Details
Resolves #3252
Reviewed-on: #3256
Co-authored-by: kolaente <k@knt.li>
Co-committed-by: kolaente <k@knt.li>
2023-10-22 17:40:50 +00:00
renovate c1149273f9 fix(deps): update dependency @types/sortablejs to v1.15.4 (#3788)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3788
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 17:31:22 +00:00
renovate 7496be5a44 fix(deps): update tiptap to v2.1.12 (#3790)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3790
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 17:22:46 +00:00
renovate a35b0f64a2 fix(deps): update dependency lowlight to v2.9.0 (#3789)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3789
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 17:01:34 +00:00
renovate 6c2b30f8ef fix(deps): update dependency @types/lodash.clonedeep to v4.5.8 (#3787)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3787
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 15:22:23 +00:00
renovate daa720669a fix(deps): update sentry-javascript monorepo to v7.74.1 (#3778)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3778
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-22 15:12:47 +00:00
konrad 26fc9b4e4f feat: move from easymde to tiptap editor (#2222)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #2222
2023-10-22 13:48:57 +00:00
kolaente 37af478811
chore(editor): remove marked usages
continuous-integration/drone/pr Build is passing Details
2023-10-22 15:18:39 +02:00
kolaente 22223a56bd
chore(editor): remove converting markdown
continuous-integration/drone/pr Build is failing Details
2023-10-22 15:12:36 +02:00
kolaente c367b70ccc
chore(editor): remove unused components 2023-10-22 15:12:23 +02:00
kolaente 9103ad8505
chore(deps): remove unused dependencies
continuous-integration/drone/pr Build is passing Details
2023-10-22 14:46:21 +02:00
kolaente e4eaca82e1
fix(editor): add missing dependency
continuous-integration/drone/pr Build is passing Details
2023-10-22 14:29:48 +02:00
kolaente 229beec1d1
fix(editor): lint
continuous-integration/drone/pr Build is failing Details
2023-10-22 14:24:10 +02:00
kolaente 803f9c81c2
fix(editor): make tests work with changed structure
continuous-integration/drone/pr Build is failing Details
2023-10-22 14:21:28 +02:00
kolaente c6b123734b
feat(editor): add tests to check rendering of task description 2023-10-22 14:21:13 +02:00
kolaente 0154b2a475
fix(editor): allow checking a checkbox even when the editor is set to read only
continuous-integration/drone/pr Build is failing Details
2023-10-22 13:26:01 +02:00
kolaente 32ca8853bc
fix(editor): don't use global shortcut when anything is focused 2023-10-22 12:48:31 +02:00
kolaente f6d5cbcf6f
feat(editor): only load attachment images when rendering is done
continuous-integration/drone/pr Build is failing Details
2023-10-22 12:38:34 +02:00
kolaente d7503dc4a2
feat(editor): edit mode
continuous-integration/drone/pr Build is failing Details
2023-10-22 12:08:27 +02:00
kolaente 632e3c5a0b
fix(editor): duplicate name for extension
continuous-integration/drone/pr Build is failing Details
2023-10-22 11:02:03 +02:00
kolaente c61f1a45fb
fix(editor): placeholder showing or not showing
continuous-integration/drone/pr Build is failing Details
2023-10-22 11:00:42 +02:00
kolaente 2f3196ef86
fix(editor): duplicate name
continuous-integration/drone/pr Build is failing Details
2023-10-22 10:50:16 +02:00
kolaente 2864854cd4
fix(editor): don't prevent typing editor focus shortcut when other instance of an editor is focused already
continuous-integration/drone/pr Build is failing Details
2023-10-22 10:47:13 +02:00
kolaente a453449fea
fix(editor): reset on empty
continuous-integration/drone/pr Build is failing Details
2023-10-22 10:41:34 +02:00
kolaente abb6630b4b
feat(editor): add comment when pressing ctrl enter 2023-10-22 10:40:12 +02:00
kolaente 63c40b29b0
feat(editor): save when pressing ctrl enter 2023-10-22 10:38:53 +02:00
Frederick [Bot] b7ff71ba76 [skip ci] Updated translations via Crowdin 2023-10-22 00:03:33 +00:00
kolaente 19a78f1f75
fix(editor): lint
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:52:56 +02:00
kolaente d6a41fa518
chore(editor): remove old editor component
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:48:17 +02:00
kolaente 859fc1e94e
feat(editor): edit shortcut to set focus into the editor
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:46:46 +02:00
kolaente aa715dd9e1
chore(editor): cleanup unused options 2023-10-21 19:46:25 +02:00
kolaente daa2ed3b1c
feat(editor): allow passing placeholder down
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:33:32 +02:00
kolaente 1443e23f18
chore(editor): cleanup 2023-10-21 19:29:27 +02:00
kolaente 34420b623c
fix(editor): use edit enable 2023-10-21 19:29:19 +02:00
kolaente 80dc35eabb
fix(editor): actions styling 2023-10-21 19:29:00 +02:00
kolaente cb1d2b3834
fix(editor): always show placeholder when empty
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:18:28 +02:00
kolaente 0ef775e9b9
feat(editor): improve overall styling
continuous-integration/drone/pr Build is failing Details
2023-10-21 19:08:11 +02:00
kolaente a7e4e3adf9
feat(editor): add placeholder 2023-10-21 19:02:55 +02:00
kolaente d005875bbf
chore(editor): make sure all tiptap dependencies are updated as one 2023-10-21 18:57:17 +02:00
kolaente dc3ee112bd
chore(editor): cleanup
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:47:04 +02:00
kolaente 9b20dc1899
feat(editor): properly bubble changes when they are made
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:40:30 +02:00
kolaente 22103626b8
fix(editor): make checklist indicator work again
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:18:17 +02:00
kolaente 4f2d7b3ce2
feat(editor): add uploading an image on save
continuous-integration/drone/pr Build is failing Details
2023-10-21 18:03:59 +02:00
kolaente 76d31c84ad
feat(editor): add tooltips for everything
continuous-integration/drone/pr Build is failing Details
2023-10-21 17:48:35 +02:00
kolaente 66c37f10e0
chore(editor): cleanup
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:10:26 +02:00
kolaente 0b2aa723a6
feat(editor): open links when clicking on them
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:07:39 +02:00
kolaente d75a963d08
feat(editor): add code highlighting
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:06:47 +02:00
kolaente beefc1d5ef
feat(editor): add bubble menu
continuous-integration/drone/pr Build is failing Details
2023-10-21 14:02:53 +02:00
kolaente 17c23d9463
feat(editor): make image upload work via slash command
continuous-integration/drone/pr Build is failing Details
2023-10-21 13:28:59 +02:00
kolaente 02ab1b8c0a
feat(editor): add all slash commands
continuous-integration/drone/pr Build is failing Details
2023-10-21 13:00:12 +02:00
kolaente e81c98fe5b
chore(editor): format 2023-10-21 11:52:20 +02:00
kolaente 3bf806f00c
fix(editor): add missing dependencies for commands
continuous-integration/drone/pr Build is failing Details
2023-10-21 11:46:02 +02:00
kolaente aea3f86a8f
feat(editor): add command list example
continuous-integration/drone/pr Build is failing Details
copied from 252acb32d2/demos/src/Experiments/Commands/Vue
2023-10-21 11:33:49 +02:00
kolaente 5297208d92
feat(editor): move all editor related components into one folder
continuous-integration/drone/pr Build is failing Details
2023-10-21 11:15:17 +02:00
kolaente c84bcfddba
feat(editor): add proper description for all buttons
continuous-integration/drone/pr Build is failing Details
2023-10-21 11:10:43 +02:00
kolaente 0772acbead
chore(editor): format 2023-10-21 10:53:41 +02:00
renovate 123c665d9d chore(deps): update dependency eslint to v8.52.0 (#3785)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3785
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-21 08:08:26 +00:00
kolaente 4f3efe4454
feat(editor): resolve and load attachment images from content
continuous-integration/drone/pr Build is failing Details
2023-10-20 23:03:38 +02:00
kolaente 671c658868
fix(editor): actually populate loaded data into the editor
continuous-integration/drone/pr Build is failing Details
2023-10-20 22:52:21 +02:00
kolaente 05bf7ccf0b
feat(editor): image upload
continuous-integration/drone/pr Build is failing Details
2023-10-20 22:43:10 +02:00
renovate b76acb15c7 chore(deps): update dev-dependencies (major) (#3741)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3741
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 19:34:11 +00:00
kolaente 953361c480
chore(deps): update lockfile
continuous-integration/drone/pr Build is failing Details
2023-10-20 17:10:55 +02:00
kolaente 8b60e5b2c8
fix(editor): add icons for clearing marks and nodes 2023-10-20 17:10:06 +02:00
kolaente faf93a6088
feat(editor): enable table 2023-10-20 17:10:06 +02:00
kolaente 8e07d9647a
chore(editor): move checklist to the other lists 2023-10-20 17:10:06 +02:00
kolaente 08959fdb77
fix(editor): focus state 2023-10-20 17:10:06 +02:00
kolaente e716fd1bf9
fix(editor): list styling 2023-10-20 17:10:06 +02:00
kolaente 63865028b8
feat(editor): make task list work 2023-10-20 17:10:06 +02:00
kolaente e760ce45e4
fix(editor): checklist button icon 2023-10-20 17:10:06 +02:00
kolaente af9eb358ee
fix(editor): permission check for table editing 2023-10-20 17:10:06 +02:00
kolaente ddcf6bf0a5
fix(editor): image button icon 2023-10-20 17:10:06 +02:00
kolaente 9c71e30efe
chore(editor): add break icon 2023-10-20 17:10:06 +02:00
kolaente c58ad47782
chore(editor): use typed props definition 2023-10-20 17:10:05 +02:00
kolaente ca0d9e6bd5
chore(editor): add horizontal line icon 2023-10-20 17:10:05 +02:00
kolaente ad3234b19f
chore(deps): update dependencies 2023-10-20 17:10:05 +02:00
Dominik Pschenitschni 24b8915983
wip: tiptap editor 2023-10-20 17:10:05 +02:00
kolaente 01c2acdf34
chore(deps): update flake
continuous-integration/drone/push Build is passing Details
2023-10-20 17:09:55 +02:00
kolaente ff2b4b8bf4
feat(notifications): add option to mark all as read
continuous-integration/drone/push Build is passing Details
2023-10-20 16:52:03 +02:00
kolaente d73c62a424
fix(task): correctly build task identifier 2023-10-20 16:52:03 +02:00
renovate cac41a1c86 fix(deps): update dependency vue to v3.3.6 (#3784)
continuous-integration/drone/push Build is failing Details
Reviewed-on: #3784
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 14:01:41 +00:00
kolaente aeed4b3a3b
fix(settings): allow removing the default project via settings
continuous-integration/drone/push Build is passing Details
2023-10-20 16:01:22 +02:00
renovate 8992caadf9 fix(deps): update dependency marked to v9.1.2 (#3774)
continuous-integration/drone/push Build is passing Details
Reviewed-on: #3774
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-10-20 13:17:59 +00:00
75 changed files with 4948 additions and 2046 deletions

View File

@ -1,40 +0,0 @@
import {createFakeUserAndLogin} from '../../support/authenticateUser'
import {TaskFactory} from '../../factories/task'
import {ProjectFactory} from '../../factories/project'
import {UserProjectFactory} from '../../factories/users_project'
import {BucketFactory} from '../../factories/bucket'
describe('Editor', () => {
createFakeUserAndLogin()
beforeEach(() => {
ProjectFactory.create(1)
BucketFactory.create(1)
TaskFactory.truncate()
UserProjectFactory.truncate()
})
it('Has a preview with checkable checkboxes', () => {
const tasks = TaskFactory.create(1, {
description: `# Test Heading
* Bullet 1
* Bullet 2
* [ ] Checklist
* [x] Checklist checked
`,
bucket_id: 1,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('input[type=checkbox][data-checkbox-num=0]')
.click()
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
.contains('Saved!')
.should('exist')
cy.get('.preview.content')
.should('contain', 'Test Heading')
})
})

View File

@ -24,7 +24,7 @@ function addLabelToTaskAndVerify(labelTitle: string) {
.first()
.click()
cy.get('.global-notification', { timeout: 4000 })
cy.get('.global-notification', {timeout: 4000})
.should('contain', 'Success')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
.should('exist')
@ -122,7 +122,7 @@ describe('Task', () => {
const tasks = TaskFactory.create(1, {
id: 1,
index: 1,
description: 'Lorem ipsum dolor sit amet.'
description: 'Lorem ipsum dolor sit amet.',
})
cy.visit(`/tasks/${tasks[0].id}`)
@ -143,7 +143,7 @@ describe('Task', () => {
id: 1,
index: 1,
done: true,
done_at: new Date().toISOString()
done_at: new Date().toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
@ -196,13 +196,13 @@ describe('Task', () => {
it('Can edit the description', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: 'Lorem ipsum dolor sit amet.'
description: 'Lorem ipsum dolor sit amet.',
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .details.content.description .editor button')
cy.get('.task-view .details.content.description .tiptap button.done-edit')
.click()
cy.get('.task-view .details.content.description .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
cy.get('.task-view .details.content.description .tiptap__editor .tiptap.ProseMirror')
.type('{selectall}New Description')
cy.get('[data-cy="saveEditor"]')
.contains('Save')
@ -219,7 +219,7 @@ describe('Task', () => {
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .comments .media.comment .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
cy.get('.task-view .comments .media.comment .tiptap__editor .tiptap.ProseMirror')
.should('be.visible')
.type('{selectall}New Comment')
cy.get('.task-view .comments .media.comment .button:not([disabled])')
@ -227,7 +227,7 @@ describe('Task', () => {
.should('be.visible')
.click()
cy.get('.task-view .comments .media.comment .editor')
cy.get('.task-view .comments .media.comment .tiptap__editor')
.should('contain', 'New Comment')
cy.get('.global-notification')
.should('contain', 'Success')
@ -236,7 +236,7 @@ describe('Task', () => {
it('Can move a task to another project', () => {
const projects = ProjectFactory.create(2)
BucketFactory.create(2, {
project_id: '{increment}'
project_id: '{increment}',
})
const tasks = TaskFactory.create(1, {
id: 1,
@ -380,7 +380,7 @@ describe('Task', () => {
addLabelToTaskAndVerify(labels[0].title)
})
it('Can add a label to a task and it shows up on the kanban board afterwards', () => {
const tasks = TaskFactory.create(1, {
id: 1,
@ -389,18 +389,18 @@ describe('Task', () => {
})
const labels = LabelFactory.create(1)
LabelTaskFactory.truncate()
cy.visit(`/projects/${projects[0].id}/kanban`)
cy.get('.bucket .task')
.contains(tasks[0].title)
.click()
addLabelToTaskAndVerify(labels[0].title)
cy.get('.modal-content .close')
.click()
cy.get('.bucket .task')
.should('contain.text', labels[0].title)
})
@ -461,7 +461,7 @@ describe('Task', () => {
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Can set a reminder', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
@ -543,7 +543,7 @@ describe('Task', () => {
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Allows to set a custom relative reminder when the task already has a due date', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
@ -579,7 +579,7 @@ describe('Task', () => {
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Allows to set a fixed reminder when the task already has a due date', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
@ -609,7 +609,7 @@ describe('Task', () => {
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Can set a priority for a task', () => {
const tasks = TaskFactory.create(1, {
id: 1,
@ -647,7 +647,7 @@ describe('Task', () => {
.select('50%')
cy.get('.global-notification')
.should('contain', 'Success')
cy.wait(200)
cy.get('.task-view .columns.details .column')
@ -656,7 +656,7 @@ describe('Task', () => {
.should('be.visible')
.should('have.value', '0.5')
})
it('Can add an attachment to a task', () => {
TaskAttachmentFactory.truncate()
const tasks = TaskFactory.create(1, {
@ -691,35 +691,119 @@ describe('Task', () => {
cy.get('.bucket .task .footer .icon svg.fa-paperclip')
.should('exist')
})
it('Can check items off a checklist', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: `
This is a checklist:
* [ ] one item
* [ ] another item
* [ ] third item
* [ ] fourth item
* [x] and this one is already done
`,
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>First Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Second Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Third Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Fourth Item</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Fifth Item</p></div>
</li>
</ul>`,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .checklist-summary')
.should('contain.text', '1 of 5 tasks')
cy.get('.editor .content ul > li input[type=checkbox]')
cy.get('.tiptap__editor ul > li input[type=checkbox]')
.eq(2)
.click()
cy.get('.editor .content ul > li input[type=checkbox]')
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
.contains('Saved!')
.should('exist')
cy.get('.tiptap__editor ul > li input[type=checkbox]')
.eq(2)
.should('be.checked')
cy.get('.editor .content input[type=checkbox]')
cy.get('.tiptap__editor input[type=checkbox]')
.should('have.length', 5)
cy.get('.task-view .checklist-summary')
.should('contain.text', '2 of 5 tasks')
})
it('Should use the editor to render description', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: `
<h1>Lorem Ipsum</h1>
<p>Dolor sit amet</p>
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>First Item</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Second Item</p></div>
</li>
</ul>`,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.tiptap__editor ul > li input[type=checkbox]')
.should('exist')
cy.get('.tiptap__editor h1')
.contains('Lorem Ipsum')
.should('exist')
cy.get('.tiptap__editor p')
.contains('Dolor sit amet')
.should('exist')
})
it.only('Should render an image from attachment', async () => {
TaskAttachmentFactory.truncate()
const tasks = TaskFactory.create(1, {
id: 1,
description: '',
})
cy.readFile('cypress/fixtures/image.jpg', null).then(file => {
const formData = new FormData()
formData.append('files', new Blob([file]), 'image.jpg')
cy.request({
method: 'PUT',
url: `${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments`,
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
'Content-Type': 'multipart/form-data',
},
body: formData,
})
.then(({body}) => {
const dec = new TextDecoder('utf-8')
const {success} = JSON.parse(dec.decode(body))
TaskFactory.create(1, {
id: 1,
description: `<img src="${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments/${success[0].id}" alt="test image">`,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.tiptap__editor img')
.should('be.visible')
.and(($img) => {
// "naturalWidth" and "naturalHeight" are set when the image loads
expect($img[0].naturalWidth).to.be.greaterThan(0)
})
})
})
})
})
})

View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1692494774,
"narHash": "sha256-noGVoOTyZ2Kr5OFglzKYOX48cx3hggdCPbXrYMG2FDw=",
"lastModified": 1697730408,
"narHash": "sha256-Ww//zzukdTrwTrCUkaJA/NsaLEfUfQpWZXBdXBYfhak=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3476a10478587dec90acb14ec6bde0966c545cc0",
"rev": "ff0a5a776b56e0ca32d47a4a47695452ec7f7d80",
"type": "github"
},
"original": {

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@8.9.0",
"packageManager": "pnpm@8.9.2",
"keywords": [
"todo",
"productivity",
@ -53,34 +53,68 @@
"@infectoone/vue-ganttastic": "2.2.0",
"@intlify/unplugin-vue-i18n": "1.4.0",
"@kyvg/vue3-notification": "3.0.2",
"@sentry/tracing": "7.74.0",
"@sentry/vue": "7.74.0",
"@sentry/tracing": "7.74.1",
"@sentry/vue": "7.74.1",
"@tiptap/core": "2.1.12",
"@tiptap/extension-blockquote": "2.1.12",
"@tiptap/extension-bold": "2.1.12",
"@tiptap/extension-bullet-list": "2.1.12",
"@tiptap/extension-code": "2.1.12",
"@tiptap/extension-code-block-lowlight": "2.1.12",
"@tiptap/extension-document": "2.1.12",
"@tiptap/extension-dropcursor": "2.1.12",
"@tiptap/extension-gapcursor": "2.1.12",
"@tiptap/extension-hard-break": "2.1.12",
"@tiptap/extension-heading": "2.1.12",
"@tiptap/extension-history": "2.1.12",
"@tiptap/extension-horizontal-rule": "2.1.12",
"@tiptap/extension-image": "2.1.12",
"@tiptap/extension-italic": "2.1.12",
"@tiptap/extension-link": "2.1.12",
"@tiptap/extension-list-item": "2.1.12",
"@tiptap/extension-ordered-list": "2.1.12",
"@tiptap/extension-paragraph": "2.1.12",
"@tiptap/extension-placeholder": "2.1.12",
"@tiptap/extension-strike": "2.1.12",
"@tiptap/extension-table": "2.1.12",
"@tiptap/extension-table-cell": "2.1.12",
"@tiptap/extension-table-header": "2.1.12",
"@tiptap/extension-table-row": "2.1.12",
"@tiptap/extension-task-item": "2.1.12",
"@tiptap/extension-task-list": "2.1.12",
"@tiptap/extension-text": "2.1.12",
"@tiptap/extension-typography": "2.1.12",
"@tiptap/extension-underline": "2.1.12",
"@tiptap/pm": "2.1.12",
"@tiptap/suggestion": "2.1.12",
"@tiptap/vue-3": "2.1.12",
"@types/is-touch-device": "1.0.1",
"@types/lodash.clonedeep": "4.5.8",
"@types/sortablejs": "1.15.4",
"@vueuse/core": "10.5.0",
"@vueuse/router": "10.5.0",
"axios": "1.5.1",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
"camel-case": "4.1.2",
"codemirror": "5.65.15",
"date-fns": "2.30.0",
"dayjs": "1.11.10",
"dompurify": "3.0.6",
"easymde": "2.18.0",
"fast-deep-equal": "3.1.3",
"flatpickr": "4.6.13",
"flexsearch": "0.7.31",
"floating-vue": "2.0.0-beta.24",
"highlight.js": "11.9.0",
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lodash.debounce": "4.0.8",
"marked": "9.1.1",
"lowlight": "2.9.0",
"pinia": "2.1.7",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
"sortablejs": "1.15.0",
"tippy.js": "6.3.7",
"ufo": "1.3.1",
"vue": "3.3.6",
"vue": "3.3.7",
"vue-advanced-cropper": "2.8.8",
"vue-flatpickr-component": "11.0.3",
"vue-i18n": "9.5.0",
@ -91,7 +125,7 @@
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.5",
"@cypress/vite-dev-server": "5.0.6",
"@cypress/vue": "5.0.5",
"@cypress/vue": "6.0.0",
"@faker-js/faker": "8.2.0",
"@histoire/plugin-screenshot": "0.17.0",
"@histoire/plugin-vue": "0.17.1",
@ -110,7 +144,7 @@
"@typescript-eslint/parser": "6.8.0",
"@vitejs/plugin-legacy": "4.1.1",
"@vitejs/plugin-vue": "4.4.0",
"@vue/eslint-config-typescript": "11.0.3",
"@vue/eslint-config-typescript": "12.0.0",
"@vue/test-utils": "2.4.1",
"@vue/tsconfig": "0.4.0",
"autoprefixer": "10.4.16",
@ -118,18 +152,18 @@
"caniuse-lite": "1.0.30001551",
"css-has-pseudo": "6.0.0",
"csstype": "3.1.2",
"cypress": "12.17.4",
"cypress": "13.3.2",
"esbuild": "0.19.5",
"eslint": "8.51.0",
"eslint": "8.52.0",
"eslint-plugin-vue": "9.17.0",
"happy-dom": "10.11.2",
"happy-dom": "12.9.1",
"histoire": "0.17.2",
"postcss": "8.4.31",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "4.0.0",
"postcss-focus-within": "8.0.0",
"postcss-preset-env": "9.2.0",
"rollup": "3.29.4",
"rollup": "4.1.4",
"rollup-plugin-visualizer": "5.9.2",
"sass": "1.69.4",
"start-server-and-test": "2.0.1",

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,13 @@
"histoire"
]
},
{
"groupName": "tiptap",
"matchPackagePrefixes": [
"@tiptap/",
"tiptap"
]
},
{
"matchDepTypes": ["devDependencies"],
"groupName": "dev-dependencies",

View File

@ -1,3 +1,5 @@
import {createAsyncComponent} from '@/helpers/createAsyncComponent'
export default createAsyncComponent(() => import('@/components/input/editor.vue'))
const TipTap = createAsyncComponent(() => import('@/components/input/editor/TipTap.vue'))
export default TipTap

View File

@ -1,444 +0,0 @@
<template>
<div class="editor">
<div class="clear"></div>
<vue-easymde
:configs="config"
@change="() => bubbleNow()"
@update:modelValue="handleInput"
class="content"
v-if="isEditActive"
v-model="text"/>
<div class="preview content" v-html="preview" v-if="isPreviewActive && text !== ''">
</div>
<p class="has-text-centered has-text-grey is-italic my-5" v-if="showPreviewText">
{{ emptyText }}
<template v-if="isEditEnabled">
<ButtonLink
@click="toggleEdit"
v-shortcut="editShortcut"
class="d-print-none">
{{ $t('input.editor.edit') }}
</ButtonLink>.
</template>
</p>
<ul class="actions d-print-none" v-if="bottomActions.length > 0">
<li v-if="isEditEnabled && !showPreviewText && showSave">
<BaseButton
v-if="showEditButton"
@click="toggleEdit"
v-shortcut="editShortcut">
{{ $t('input.editor.edit') }}
</BaseButton>
<BaseButton
v-else-if="isEditActive"
@click="bubbleSaveClick"
class="done-edit">
{{ $t('misc.save') }}
</BaseButton>
</li>
<li v-for="(action, k) in bottomActions" :key="k">
<BaseButton @click="action.action">{{ action.title }}</BaseButton>
</li>
</ul>
<template v-else-if="isEditEnabled && showSave">
<ul v-if="showEditButton" class="actions d-print-none">
<li>
<BaseButton
@click="toggleEdit"
v-shortcut="editShortcut">
{{ $t('input.editor.edit') }}
</BaseButton>
</li>
</ul>
<x-button
v-else-if="isEditActive"
@click="bubbleSaveClick"
variant="secondary"
:shadow="false"
v-cy="'saveEditor'">
{{ $t('misc.save') }}
</x-button>
</template>
</div>
</template>
<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 {createEasyMDEConfig} from './editorConfig'
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'
const props = defineProps({
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: {
type: Array,
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', 'save'])
const text = ref('')
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
}
bubbleNow()
},
)
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
bubbleNow()
}
function bubbleNow() {
emit('update:modelValue', text.value)
}
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, {
mangle: false,
headerIds: false,
}), {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
}
const projectPrefix = text.value.substring(index, index + 1)
console.debug({index, projectPrefix, checked, text: text.value})
text.value = replaceAt(text.value, index, `${projectPrefix} ${checked ? '[x]' : '[ ]'} `)
bubbleNow()
emit('save', text.value)
renderPreview()
}
function toggleEdit() {
isPreviewActive.value = false
isEditActive.value = true
}
function bubbleSaveClick() {
isPreviewActive.value = true
isEditActive.value = false
renderPreview()
bubbleNow()
emit('save', text.value)
}
</script>
<style lang="scss">
@import 'codemirror/lib/codemirror.css';
@import 'highlight.js/scss/base16/equilibrium-gray-light';
.editor {
.clear {
clear: both;
}
.preview.content {
margin-bottom: .5rem;
overflow-wrap: anywhere; // Safari does not understand "break-word" so we put that first to make sure it at least is able to show it somewhat properly there.
overflow-wrap: break-word;
ul li {
input[type="checkbox"] {
margin-right: .5rem;
}
&.has-checkbox {
margin-left: -1.25rem;
list-style: none;
}
}
}
}
.CodeMirror {
padding: .5rem;
border: 1px solid var(--grey-200) !important;
background: var(--white);
&-lines pre {
margin: 0 !important;
}
&-placeholder {
color: var(--grey-400) !important;
font-style: italic;
}
&-cursor {
border-color: var(--grey-700);
}
}
.editor-preview {
padding: 0;
&-side {
padding: .5rem;
}
}
.editor-toolbar {
background: var(--grey-50);
border: 1px solid var(--grey-200);
border-bottom: none;
button {
color: var(--grey-700);
&.active {
background: var(--grey-200);
}
svg {
vertical-align: middle;
&, rect {
width: 20px;
height: 20px;
}
}
&::after {
position: absolute;
top: 24px;
margin-left: -3px;
}
&:hover {
background: var(--grey-200);
border-color: var(--grey-300);
}
}
i.separator {
border-color: var(--grey-200) !important;
}
}
pre.CodeMirror-line {
margin-bottom: 0 !important;
color: var(--grey-700) !important;
}
.cm-header {
font-family: $vikunja-font;
font-weight: 400;
}
ul.actions {
font-size: .8rem;
margin: 0;
li {
display: inline-block;
&::after {
content: '·';
padding: 0 .25rem;
}
&:last-child:after {
content: '';
}
}
&, a {
color: var(--grey-500);
&.done-edit {
color: var(--primary);
}
}
a:hover {
text-decoration: underline;
}
}
.vue-easymde.content {
margin-bottom: 0 !important;
}
</style>

View File

@ -0,0 +1,145 @@
<template>
<div class="items">
<template v-if="items.length">
<button
class="item"
:class="{ 'is-selected': index === selectedIndex }"
v-for="(item, index) in items"
:key="index"
@click="selectItem(index)"
>
<icon :icon="item.icon"/>
<div class="description">
<p>{{ item.title }}</p>
<p>{{ item.description }}</p>
</div>
</button>
</template>
<div class="item" v-else>
No result
</div>
</div>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
command: {
type: Function,
required: true,
},
},
data() {
return {
selectedIndex: 0,
}
},
watch: {
items() {
this.selectedIndex = 0
},
},
methods: {
onKeyDown({event}) {
if (event.key === 'ArrowUp') {
this.upHandler()
return true
}
if (event.key === 'ArrowDown') {
this.downHandler()
return true
}
if (event.key === 'Enter') {
this.enterHandler()
return true
}
return false
},
upHandler() {
this.selectedIndex = ((this.selectedIndex + this.items.length) - 1) % this.items.length
},
downHandler() {
this.selectedIndex = (this.selectedIndex + 1) % this.items.length
},
enterHandler() {
this.selectItem(this.selectedIndex)
},
selectItem(index) {
const item = this.items[index]
if (item) {
this.command(item)
}
},
},
}
</script>
<style lang="scss" scoped>
.items {
padding: 0.2rem;
position: relative;
border-radius: 0.5rem;
background: var(--white);
color: var(--grey-900);
overflow: hidden;
font-size: 0.9rem;
box-shadow: var(--shadow-md);
}
.item {
display: flex;
align-items: center;
margin: 0;
width: 100%;
text-align: left;
background: transparent;
border-radius: $radius;
border: 0;
padding: 0.2rem 0.4rem;
transition: background-color $transition;
&.is-selected, &:hover {
background: var(--grey-100);
cursor: pointer;
}
> svg {
box-sizing: border-box;
width: 2rem;
height: 2rem;
border: 1px solid var(--grey-300);
padding: .5rem;
margin-right: .5rem;
border-radius: $radius;
color: var(--grey-700);
}
}
.description {
display: flex;
flex-direction: column;
font-size: .9rem;
color: var(--grey-800);
p:last-child {
font-size: .75rem;
color: var(--grey-500);
}
}
</style>

View File

@ -0,0 +1,439 @@
<template>
<div class="editor-toolbar">
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
v-tooltip="$t('input.editor.heading1')"
>
<span class="icon">
<icon :icon="['fa', 'fa-header']"/>
<span class="icon__lower-text">1</span>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
v-tooltip="$t('input.editor.heading2')"
>
<span class="icon">
<icon :icon="['fa', 'fa-header']"/>
<span class="icon__lower-text">2</span>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
v-tooltip="$t('input.editor.heading3')"
>
<span class="icon">
<icon :icon="['fa', 'fa-header']"/>
<span class="icon__lower-text">3</span>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleBold().run()"
:class="{ 'is-active': editor.isActive('bold') }"
v-tooltip="$t('input.editor.bold')"
>
<span class="icon">
<icon :icon="['fa', 'fa-bold']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleItalic().run()"
:class="{ 'is-active': editor.isActive('italic') }"
v-tooltip="$t('input.editor.italic')"
>
<span class="icon">
<icon :icon="['fa', 'fa-italic']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleUnderline().run()"
:class="{ 'is-active': editor.isActive('underline') }"
v-tooltip="$t('input.editor.underline')"
>
<span class="icon">
<icon :icon="['fa', 'fa-underline']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleStrike().run()"
:class="{ 'is-active': editor.isActive('strike') }"
v-tooltip="$t('input.editor.strikethrough')"
>
<span class="icon">
<icon :icon="['fa', 'fa-strikethrough']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleCodeBlock().run()"
:class="{ 'is-active': editor.isActive('codeBlock') }"
v-tooltip="$t('input.editor.code')"
>
<span class="icon">
<icon :icon="['fa', 'fa-code']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleBlockquote().run()"
:class="{ 'is-active': editor.isActive('blockquote') }"
v-tooltip="$t('input.editor.quote')"
>
<span class="icon">
<icon :icon="['fa', 'fa-quote-right']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleBulletList().run()"
:class="{ 'is-active': editor.isActive('bulletList') }"
v-tooltip="$t('input.editor.bulletList')"
>
<span class="icon">
<icon :icon="['fa', 'fa-list-ol']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleOrderedList().run()"
:class="{ 'is-active': editor.isActive('orderedList') }"
v-tooltip="$t('input.editor.orderedList')"
>
<span class="icon">
<icon :icon="['fa', 'fa-list-ul']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleTaskList().run()"
:class="{ 'is-active': editor.isActive('taskList') }"
v-tooltip="$t('input.editor.taskList')"
>
<span class="icon">
<icon icon="fa-list-check"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="openImagePicker"
v-tooltip="$t('input.editor.image')"
>
<span class="icon">
<icon icon="fa-image"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="setLink"
:class="{ 'is-active': editor.isActive('link') }"
title="set link"
v-tooltip="$t('input.editor.link')"
>
<span class="icon">
<icon :icon="['fa', 'fa-link']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().setParagraph().run()"
:class="{ 'is-active': editor.isActive('paragraph') }"
title="paragraph"
v-tooltip="$t('input.editor.text')"
>
<span class="icon">
<icon :icon="['fa', 'fa-paragraph']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().setHorizontalRule().run()"
v-tooltip="$t('input.editor.horizontalRule')"
>
<span class="icon">
<icon :icon="['fa', 'fa-ruler-horizontal']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().undo().run()"
v-tooltip="$t('input.editor.undo')"
>
<span class="icon">
<icon :icon="['fa', 'fa-undo']"/>
</span>
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().redo().run()"
v-tooltip="$t('input.editor.redo')"
>
<span class="icon">
<icon :icon="['fa', 'fa-redo']"/>
</span>
</BaseButton>
</div>
<div class="editor-toolbar__segment">
<!-- table -->
<BaseButton
class="editor-toolbar__button"
@click="toggleTableMode"
:class="{ 'is-active': editor.isActive('table') }"
v-tooltip="$t('input.editor.table.title')"
>
<span class="icon">
<icon :icon="['fa', 'fa-table']"/>
</span>
</BaseButton>
<div v-if="tableMode" class="editor-toolbar__table-buttons">
<BaseButton
class="editor-toolbar__button"
@click="
editor
.chain()
.focus()
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
.run()
"
>
{{ $t('input.editor.table.insert') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addColumnBefore().run()"
:disabled="!editor.can().addColumnBefore"
>
{{ $t('input.editor.table.addColumnBefore') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addColumnAfter().run()"
:disabled="!editor.can().addColumnAfter"
>
{{ $t('input.editor.table.addColumnAfter') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().deleteColumn().run()"
:disabled="!editor.can().deleteColumn"
>
{{ $t('input.editor.table.deleteColumn') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addRowBefore().run()"
:disabled="!editor.can().addRowBefore"
>
{{ $t('input.editor.table.addRowBefore') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().addRowAfter().run()"
:disabled="!editor.can().addRowAfter"
>
{{ $t('input.editor.table.addRowAfter') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().deleteRow().run()"
:disabled="!editor.can().deleteRow"
>
{{ $t('input.editor.table.deleteRow') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().deleteTable().run()"
:disabled="!editor.can().deleteTable"
>
{{ $t('input.editor.table.deleteTable') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().mergeCells().run()"
:disabled="!editor.can().mergeCells"
>
{{ $t('input.editor.table.mergeCells') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().splitCell().run()"
:disabled="!editor.can().splitCell"
>
{{ $t('input.editor.table.splitCell') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeaderColumn().run()"
:disabled="!editor.can().toggleHeaderColumn"
>
{{ $t('input.editor.table.toggleHeaderColumn') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeaderRow().run()"
:disabled="!editor.can().toggleHeaderRow"
>
{{ $t('input.editor.table.toggleHeaderRow') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().toggleHeaderCell().run()"
:disabled="!editor.can().toggleHeaderCell"
>
{{ $t('input.editor.table.toggleHeaderCell') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().mergeOrSplit().run()"
:disabled="!editor.can().mergeOrSplit"
>
{{ $t('input.editor.table.mergeOrSplit') }}
</BaseButton>
<BaseButton
class="editor-toolbar__button"
@click="editor.chain().focus().fixTables().run()"
:disabled="!editor.can().fixTables"
>
{{ $t('input.editor.table.fixTables') }}
</BaseButton>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import {Editor} from '@tiptap/vue-3'
import BaseButton from '@/components/base/BaseButton.vue'
const {
editor = null,
} = defineProps<{
editor: Editor,
}>()
const tableMode = ref(false)
function toggleTableMode() {
tableMode.value = !tableMode.value
}
function openImagePicker() {
document.getElementById('tiptap__image-upload').click()
}
function setLink() {
const previousUrl = editor.getAttributes('link').href
const url = window.prompt('URL', previousUrl)
// cancelled
if (url === null) {
return
}
// empty
if (url === '') {
editor.chain().focus().extendMarkRange('link').unsetLink().run()
return
}
// update link
editor
.chain()
.focus()
.extendMarkRange('link')
.setLink({href: url, target: '_blank'})
.run()
}
</script>
<style lang="scss" scoped>
.editor-toolbar {
background: var(--white);
border: 1px solid var(--grey-200);
user-select: none;
padding: .5rem;
border-radius: $radius;
display: flex;
flex-wrap: wrap;
> * + * {
border-left: 1px solid var(--grey-200);
margin-left: 6px;
padding-left: 6px;
}
}
.editor-toolbar__button {
min-width: 2rem;
height: 2rem;
border-radius: $radius;
border: 1px solid transparent;
color: var(--grey-700);
transition: all $transition;
background: transparent;
margin-right: .25rem;
&:hover {
background: var(--grey-100);
border-color: var(--grey-200);
}
.icon {
position: relative;
.icon__lower-text {
font-size: .75rem;
position: absolute;
bottom: -3px;
right: -2px;
font-weight: bold;
}
}
}
.editor-toolbar__table-buttons {
margin-top: .5rem;
> .editor-toolbar__button {
margin-right: .5rem;
margin-bottom: .5rem;
padding: 0 .25rem;
border: 1px solid var(--grey-400);
font-size: .75rem;
height: 1.5rem;
}
}
</style>

View File

@ -0,0 +1,854 @@
<template>
<div class="tiptap">
<EditorToolbar
v-if="editor && isEditing"
:editor="editor"
:upload-callback="uploadCallback"
/>
<BubbleMenu
v-if="editor && isEditing"
:editor="editor"
class="editor-bubble__wrapper"
>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleBold().run()"
:class="{ 'is-active': editor.isActive('bold') }"
v-tooltip="$t('input.editor.bold')"
>
<icon :icon="['fa', 'fa-bold']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleItalic().run()"
:class="{ 'is-active': editor.isActive('italic') }"
v-tooltip="$t('input.editor.italic')"
>
<icon :icon="['fa', 'fa-italic']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleUnderline().run()"
:class="{ 'is-active': editor.isActive('underline') }"
v-tooltip="$t('input.editor.underline')"
>
<icon :icon="['fa', 'fa-underline']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleStrike().run()"
:class="{ 'is-active': editor.isActive('strike') }"
v-tooltip="$t('input.editor.strikethrough')"
>
<icon :icon="['fa', 'fa-strikethrough']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="editor.chain().focus().toggleCode().run()"
:class="{ 'is-active': editor.isActive('code') }"
v-tooltip="$t('input.editor.code')"
>
<icon :icon="['fa', 'fa-code']"/>
</BaseButton>
<BaseButton
class="editor-bubble__button"
@click="setLink"
:class="{ 'is-active': editor.isActive('link') }"
v-tooltip="$t('input.editor.link')"
>
<icon :icon="['fa', 'fa-link']"/>
</BaseButton>
</BubbleMenu>
<editor-content
class="tiptap__editor"
:class="{'tiptap__editor-is-empty': isEmpty, 'tiptap__editor-is-edit-enabled': isEditing}"
:editor="editor"
/>
<input
v-if="isEditing"
type="file"
id="tiptap__image-upload"
class="is-hidden"
ref="uploadInputRef"
@change="addImage"
/>
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length === 0 && !isEditing">
<li>
<BaseButton
@click="setEdit"
class="done-edit">
{{ $t('input.editor.edit') }}
</BaseButton>
</li>
</ul>
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length > 0">
<li v-if="isEditing && showSave">
<BaseButton
@click="bubbleSave"
class="done-edit">
{{ $t('misc.save') }}
</BaseButton>
</li>
<li v-if="!isEditing">
<BaseButton
@click="setEdit"
class="done-edit">
{{ $t('input.editor.edit') }}
</BaseButton>
</li>
<li v-for="(action, k) in bottomActions" :key="k">
<BaseButton @click="action.action">{{ action.title }}</BaseButton>
</li>
</ul>
<x-button
v-else-if="isEditing && showSave"
class="mt-4"
@click="bubbleSave"
variant="secondary"
:shadow="false"
v-cy="'saveEditor'"
>
{{ $t('misc.save') }}
</x-button>
</div>
</template>
<script setup lang="ts">
import {ref, watch, onBeforeUnmount, nextTick, onMounted, computed} from 'vue'
import {refDebounced} from '@vueuse/core'
import EditorToolbar from './EditorToolbar.vue'
import Link from '@tiptap/extension-link'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import Typography from '@tiptap/extension-typography'
import Image from '@tiptap/extension-image'
import Underline from '@tiptap/extension-underline'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import {Blockquote} from '@tiptap/extension-blockquote'
import {Bold} from '@tiptap/extension-bold'
import {BulletList} from '@tiptap/extension-bullet-list'
import {Code} from '@tiptap/extension-code'
import {Document} from '@tiptap/extension-document'
import {Dropcursor} from '@tiptap/extension-dropcursor'
import {Gapcursor} from '@tiptap/extension-gapcursor'
import {HardBreak} from '@tiptap/extension-hard-break'
import {Heading} from '@tiptap/extension-heading'
import {History} from '@tiptap/extension-history'
import {HorizontalRule} from '@tiptap/extension-horizontal-rule'
import {Italic} from '@tiptap/extension-italic'
import {ListItem} from '@tiptap/extension-list-item'
import {OrderedList} from '@tiptap/extension-ordered-list'
import {Paragraph} from '@tiptap/extension-paragraph'
import {Strike} from '@tiptap/extension-strike'
import {Text} from '@tiptap/extension-text'
import {BubbleMenu, EditorContent, useEditor} from '@tiptap/vue-3'
import {Node} from '@tiptap/pm/model'
import Commands from './commands'
import suggestionSetup from './suggestion'
import {lowlight} from 'lowlight'
import type {BottomAction, UploadCallback} from './types'
import type {ITask} from '@/modelTypes/ITask'
import type {IAttachment} from '@/modelTypes/IAttachment'
import AttachmentModel from '@/models/attachment'
import AttachmentService from '@/services/attachment'
import {useI18n} from 'vue-i18n'
import BaseButton from '@/components/base/BaseButton.vue'
import XButton from '@/components/input/button.vue'
import {Placeholder} from '@tiptap/extension-placeholder'
import {eventToHotkeyString} from '@github/hotkey'
import {mergeAttributes} from '@tiptap/core'
import {createRandomID} from '@/helpers/randomId'
const {t} = useI18n()
const CustomTableCell = TableCell.extend({
addAttributes() {
return {
// extend the existing attributes
...this.parent?.(),
// and add a new one
backgroundColor: {
default: null,
parseHTML: (element: HTMLElement) => element.getAttribute('data-background-color'),
renderHTML: (attributes) => {
return {
'data-background-color': attributes.backgroundColor,
style: `background-color: ${attributes.backgroundColor}`,
}
},
},
}
},
})
type CacheKey = `${ITask['id']}-${IAttachment['id']}`
const loadedAttachments = ref<{ [key: CacheKey]: string }>({})
const CustomImage = Image.extend({
renderHTML({HTMLAttributes}) {
if (HTMLAttributes.src?.startsWith(window.API_URL)) {
const id = 'tiptap-image-' + createRandomID()
nextTick(async () => {
const img = document.getElementById(id)
if (!img) return
// 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') {
const attachment = new AttachmentModel({taskId: taskId, id: attachmentId})
const attachmentService = new AttachmentService()
const url = await attachmentService.getBlobUrl(attachment)
loadedAttachments.value[cacheKey] = url
}
img.src = loadedAttachments.value[cacheKey]
})
return ['img', mergeAttributes(this.options.HTMLAttributes, {
'data-src': HTMLAttributes.src,
src: '#',
alt: HTMLAttributes.alt,
title: HTMLAttributes.title,
id,
})]
}
return ['img', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]
},
})
type Mode = 'edit' | 'preview'
const {
modelValue,
uploadCallback,
isEditEnabled = true,
bottomActions = [],
showSave = false,
placeholder = '',
editShortcut = '',
initialMode = 'edit',
} = defineProps<{
modelValue: string,
uploadCallback?: UploadCallback,
isEditEnabled?: boolean,
bottomActions?: BottomAction[],
showSave?: boolean,
placeholder?: string,
editShortcut?: string,
initialMode?: Mode,
}>()
const emit = defineEmits(['update:modelValue', 'save'])
const inputHTML = ref('')
watch(
() => modelValue,
() => {
inputHTML.value = modelValue
},
{immediate: true},
)
const isEmpty = computed(() => inputHTML.value === '')
const internalMode = ref<Mode>(initialMode)
const isEditing = computed(() => internalMode.value === 'edit' && isEditEnabled)
function setEdit() {
internalMode.value = 'edit'
editor.value?.commands.focus()
}
const debouncedInputHTML = refDebounced(inputHTML, 1000)
watch(debouncedInputHTML, () => bubbleNow())
function bubbleNow() {
emit('update:modelValue', inputHTML.value)
}
function bubbleSave() {
bubbleNow()
emit('save', inputHTML.value)
if (initialMode === 'preview' && isEditing.value) {
internalMode.value = 'preview'
}
}
const editor = useEditor({
content: inputHTML.value,
editable: isEditing.value,
extensions: [
// Starterkit:
Blockquote,
Bold,
BulletList,
Code,
CodeBlockLowlight.configure({
lowlight,
}),
Document,
Dropcursor,
Gapcursor,
HardBreak.extend({
addKeyboardShortcuts() {
return {
'Mod-Enter': () => {
bubbleSave()
},
}
},
}),
Heading,
History,
HorizontalRule,
Italic,
ListItem,
OrderedList,
Paragraph,
Strike,
Text,
Placeholder.configure({
placeholder: ({editor}) => {
if (!isEditing.value) {
return ''
}
if (editor.getText() !== '' && !editor.isFocused) {
return ''
}
return placeholder !== ''
? placeholder
: t('input.editor.placeholder')
},
}),
Typography,
Underline,
Link.configure({
openOnClick: true,
validate: (href: string) => /^https?:\/\//.test(href),
}),
Table.configure({
resizable: true,
}),
TableRow,
TableHeader,
// Custom TableCell with backgroundColor attribute
CustomTableCell,
CustomImage,
TaskList,
TaskItem.configure({
nested: true,
onReadOnlyChecked: (node: Node, checked: boolean): boolean => {
if (isEditEnabled) {
node.attrs.checked = checked
inputHTML.value = editor.value?.getHTML()
bubbleSave()
return true
}
return false
},
}),
Commands.configure({
suggestion: suggestionSetup(t),
}),
BubbleMenu,
],
onUpdate: () => {
// HTML
inputHTML.value = editor.value!.getHTML()
// JSON
// this.$emit('update:modelValue', this.editor.getJSON())
},
})
watch(
() => isEditing.value,
() => {
editor.value?.setEditable(isEditing.value)
},
)
watch(inputHTML, (value) => {
if (!editor.value) return
// HTML
const isSame = editor.value.getHTML() === value
// JSON
// const isSame = JSON.stringify(editor.value.getJSON()) === JSON.stringify(value)
if (isSame) {
return
}
editor.value.commands.setContent(value, false)
})
onBeforeUnmount(() => editor.value?.destroy())
const uploadInputRef = ref<HTMLInputElement | null>(null)
function uploadAndInsertFiles(files: File[] | FileList) {
uploadCallback(files).then(urls => {
urls?.forEach(url => {
editor.value
?.chain()
.focus()
.setImage({src: url})
.run()
})
bubbleSave()
})
}
function addImage() {
if (typeof uploadCallback !== 'undefined') {
const files = uploadInputRef.value?.files
if (!files || files.length === 0) {
return
}
uploadAndInsertFiles(files)
return
}
const url = window.prompt('URL')
if (url) {
editor.value?.chain().focus().setImage({src: url}).run()
bubbleSave()
}
}
function setLink() {
const previousUrl = editor.value?.getAttributes('link').href
const url = window.prompt('URL', previousUrl)
// cancelled
if (url === null) {
return
}
// empty
if (url === '') {
editor.value
?.chain()
.focus()
.extendMarkRange('link')
.unsetLink()
.run()
return
}
// update link
editor.value
?.chain()
.focus()
.extendMarkRange('link')
.setLink({href: url, target: '_blank'})
.run()
}
onMounted(() => {
internalMode.value = initialMode
document.addEventListener('paste', handleImagePaste)
if (editShortcut !== '') {
document.addEventListener('keydown', setFocusToEditor)
}
})
onBeforeUnmount(() => {
document.removeEventListener('paste', handleImagePaste)
if (editShortcut !== '') {
document.removeEventListener('keydown', setFocusToEditor)
}
})
function handleImagePaste(event) {
event.preventDefault()
event?.clipboardData?.items?.forEach(i => {
if (i.kind === 'file' && i.type.startsWith('image/')) {
uploadAndInsertFiles([i.getAsFile()])
}
})
}
// See https://github.com/github/hotkey/discussions/85#discussioncomment-5214660
function setFocusToEditor(event) {
const hotkeyString = eventToHotkeyString(event)
if (!hotkeyString) return
if (hotkeyString !== editShortcut ||
event.target.tagName.toLowerCase() === 'input' ||
event.target.tagName.toLowerCase() === 'textarea' ||
event.target.contentEditable === 'true') {
return
}
event.preventDefault()
if (initialMode === 'preview' && isEditEnabled && !isEditing.value) {
internalMode.value = 'edit'
}
editor.value?.commands.focus()
}
</script>
<style lang="scss">
.tiptap__editor {
&.tiptap__editor-is-edit-enabled {
min-height: 10rem;
}
transition: box-shadow $transition;
border-radius: $radius;
&:focus-within, &:focus {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
}
.tiptap p.is-empty::before {
content: attr(data-placeholder);
color: var(--grey-400);
pointer-events: none;
height: 0;
float: left;
}
// Basic editor styles
.ProseMirror {
padding: .5rem;
&:focus-within, &:focus {
box-shadow: none;
}
> * + * {
margin-top: 0.75em;
}
ul,
ol {
padding: 0 1rem;
}
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
}
a {
color: #68cef8;
}
code {
background-color: rgba(#616161, 0.1);
color: #616161;
}
pre {
background: #0d0d0d;
color: #fff;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
}
pre {
background: #0d0d0d;
color: #fff;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
.hljs-comment,
.hljs-quote {
color: #616161;
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #f98181;
}
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #fbbc88;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #b9f18d;
}
.hljs-title,
.hljs-section {
color: #faf594;
}
.hljs-keyword,
.hljs-selector-tag {
color: #70cff8;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
}
img {
max-width: 100%;
height: auto;
&.ProseMirror-selectednode {
outline: 3px solid #68cef8;
}
}
blockquote {
padding-left: 1rem;
border-left: 2px solid rgba(#0d0d0d, 0.1);
}
hr {
border: none;
border-top: 2px solid rgba(#0d0d0d, 0.1);
margin: 2rem 0;
}
}
.ProseMirror {
/* Table-specific styling */
table {
border-collapse: collapse;
table-layout: fixed;
width: 100%;
margin: 0;
overflow: hidden;
td,
th {
min-width: 1em;
border: 2px solid #ced4da;
padding: 3px 5px;
vertical-align: top;
box-sizing: border-box;
position: relative;
> * {
margin-bottom: 0;
}
}
th {
font-weight: bold;
text-align: left;
background-color: #f1f3f5;
}
.selectedCell:after {
z-index: 2;
position: absolute;
content: '';
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(200, 200, 255, 0.4);
pointer-events: none;
}
.column-resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: -2px;
width: 4px;
background-color: #adf;
pointer-events: none;
}
p {
margin: 0;
}
}
// Lists
ul {
margin-left: .5rem;
margin-top: 0 !important;
li {
margin-top: 0;
}
p {
margin-bottom: 0 !important;
}
}
}
.tableWrapper {
overflow-x: auto;
}
.resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
// tasklist
ul[data-type='taskList'] {
list-style: none;
padding: 0;
margin-left: 0;
li {
display: flex;
> label {
flex: 0 0 auto;
margin-right: 0.5rem;
user-select: none;
}
> div {
flex: 1 1 auto;
}
}
input[type='checkbox'] {
cursor: pointer;
}
}
.editor-bubble__wrapper {
background: var(--white);
border-radius: $radius;
border: 1px solid var(--grey-200);
box-shadow: var(--shadow-md);
display: flex;
overflow: hidden;
}
.editor-bubble__button {
color: var(--grey-700);
transition: all $transition;
background: transparent;
svg {
box-sizing: border-box;
display: block;
width: 1rem;
height: 1rem;
padding: .5rem;
margin: 0;
}
&:hover {
background: var(--grey-200);
}
}
ul.tiptap__editor-actions {
font-size: .8rem;
margin: 0;
li {
display: inline-block;
&::after {
content: '·';
padding: 0 .25rem;
}
&:last-child:after {
content: '';
}
}
&, a {
color: var(--grey-500);
&.done-edit {
color: var(--primary);
}
}
a:hover {
text-decoration: underline;
}
}
</style>

View File

@ -0,0 +1,28 @@
import {Extension} from '@tiptap/core'
import Suggestion from '@tiptap/suggestion'
// Copied and adjusted from https://github.com/ueberdosis/tiptap/tree/252acb32d27a0f9af14813eeed83d8a50059a43a/demos/src/Experiments/Commands/Vue
export default Extension.create({
name: 'slash-menu-commands',
addOptions() {
return {
suggestion: {
char: '/',
command: ({editor, range, props}) => {
props.command({editor, range})
},
},
}
},
addProseMirrorPlugins() {
return [
Suggestion({
editor: this.editor,
...this.options.suggestion,
}),
]
},
})

View File

@ -0,0 +1,214 @@
import {VueRenderer} from '@tiptap/vue-3'
import tippy from 'tippy.js'
import CommandsList from './CommandsList.vue'
export default function suggestionSetup(t) {
return {
items: ({query}: { query: string }) => {
return [
{
title: t('input.editor.text'),
description: t('input.editor.textTooltip'),
icon: 'fa-font',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('paragraph', {level: 1})
.run()
},
},
{
title: t('input.editor.heading1'),
description: t('input.editor.heading1Tooltip'),
icon: 'fa-header',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('heading', {level: 1})
.run()
},
},
{
title: t('input.editor.heading2'),
description: t('input.editor.heading2Tooltip'),
icon: 'fa-header',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('heading', {level: 2})
.run()
},
},
{
title: t('input.editor.heading3'),
description: t('input.editor.heading3Tooltip'),
icon: 'fa-header',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setNode('heading', {level: 2})
.run()
},
},
{
title: t('input.editor.bulletList'),
description: t('input.editor.bulletListTooltip'),
icon: 'fa-list-ul',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleBulletList()
.run()
},
},
{
title: t('input.editor.orderedList'),
description: t('input.editor.orderedListTooltip'),
icon: 'fa-list-ol',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleOrderedList()
.run()
},
},
{
title: t('input.editor.taskList'),
description: t('input.editor.taskListTooltip'),
icon: 'fa-list-check',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleTaskList()
.run()
},
},
{
title: t('input.editor.quote'),
description: t('input.editor.quoteTooltip'),
icon: 'fa-quote-right',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleBlockquote()
.run()
},
},
{
title: t('input.editor.code'),
description: t('input.editor.codeTooltip'),
icon: 'fa-code',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.toggleCodeBlock()
.run()
},
},
{
title: t('input.editor.image'),
description: t('input.editor.imageTooltip'),
icon: 'fa-image',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.run()
document.getElementById('tiptap__image-upload').click()
},
},
{
title: t('input.editor.horizontalRule'),
description: t('input.editor.horizontalRuleTooltip'),
icon: 'fa-ruler-horizontal',
command: ({editor, range}) => {
editor
.chain()
.focus()
.deleteRange(range)
.setHorizontalRule()
.run()
},
},
].filter(item => item.title.toLowerCase().startsWith(query.toLowerCase()))
},
render: () => {
let component: VueRenderer
let popup
return {
onStart: props => {
component = new VueRenderer(CommandsList, {
// using vue 2:
// parent: this,
// propsData: props,
props,
editor: props.editor,
})
if (!props.clientRect) {
return
}
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
appendTo: () => document.body,
content: component.element,
showOnCreate: true,
interactive: true,
trigger: 'manual',
placement: 'bottom-start',
})
},
onUpdate(props) {
component.updateProps(props)
if (!props.clientRect) {
return
}
popup[0].setProps({
getReferenceClientRect: props.clientRect,
})
},
onKeyDown(props) {
if (props.event.key === 'Escape') {
popup[0].hide()
return true
}
return component.ref?.onKeyDown(props)
},
onExit() {
popup[0].destroy()
component.destroy()
},
}
},
}
}

View File

@ -0,0 +1,6 @@
export type UploadCallback = (files: File[] | FileList) => Promise<string[]>
export interface BottomAction {
title: string
action: () => void,
}

View File

@ -1,135 +0,0 @@
import EasyMDE from 'easymde'
import {i18n} from '@/i18n'
export function createEasyMDEConfig({ placeholder, uploadImage, imageUploadFunction }) {
return {
autoDownloadFontAwesome: false,
spellChecker: false,
placeholder,
uploadImage,
imageUploadFunction,
minHeight: '150px',
sideBySideFullscreen: false,
toolbar: [
{
name: 'heading-1',
action: EasyMDE.toggleHeading1,
title: i18n.global.t('input.editor.heading1'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-2',
action: EasyMDE.toggleHeading2,
title: i18n.global.t('input.editor.heading2'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-3',
action: EasyMDE.toggleHeading3,
title: i18n.global.t('input.editor.heading3'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-smaller',
action: EasyMDE.toggleHeadingSmaller,
title: i18n.global.t('input.editor.headingSmaller'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
{
name: 'heading-bigger',
action: EasyMDE.toggleHeadingBigger,
title: i18n.global.t('input.editor.headingBigger'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.2773 19.25L12.5773 4.34995C12.5773 4.34995 12.5773 4.24995 12.4773 4.24995C12.4773 4.24995 12.4773 4.14995 12.3773 4.14995C12.3773 4.14995 12.2773 4.14995 12.2773 4.04995L12.1773 3.94995H12.0773H11.9773C11.8773 3.94995 11.8773 3.94995 11.8773 3.94995H11.7773C11.6773 4.04995 11.6773 4.14995 11.5773 4.14995C11.5773 4.14995 11.5773 4.14995 11.4773 4.14995C11.4773 4.14995 11.4773 4.24995 11.3773 4.24995L11.2773 4.34995L5.67733 19.25C5.57733 19.55 5.67733 19.95 5.97733 20.05C6.07733 20.05 6.07733 20.05 6.17733 20.05C6.37733 20.05 6.67733 19.95 6.77733 19.65L7.87733 16.85H16.1773L17.2773 19.65C17.3773 19.85 17.5773 20.05 17.8773 20.05C17.9773 20.05 17.9773 20.05 18.0773 20.05C18.2773 19.85 18.4773 19.55 18.2773 19.25ZM8.27733 15.65L11.9773 6.24995L15.6773 15.65H8.27733Z"/></svg>',
},
'|',
{
name: 'bold',
action: EasyMDE.toggleBold,
title: i18n.global.t('input.editor.bold'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M3.5 3H6.5H15.25C18.15 3 20.5 5.36 20.5 8.25C20.5 9.8 19.81 11.19 18.73 12.15C20.37 13.04 21.5 14.76 21.5 16.75C21.5 19.64 19.15 22 16.25 22H6.5H3.5C2.95 22 2.5 21.55 2.5 21C2.5 20.45 2.95 20 3.5 20H5.5V5H3.5C2.95 5 2.5 4.55 2.5 4C2.5 3.45 2.95 3 3.5 3ZM7.5 20H16.25C18.04 20 19.5 18.54 19.5 16.75C19.5 14.96 18.04 13.5 16.25 13.5H7.5V20ZM7.5 11.5H15.25C17.04 11.5 18.5 10.04 18.5 8.25C18.5 6.46 17.04 5 15.25 5H7.5V11.5Z"/></svg>',
},
{
name: 'italic',
action: EasyMDE.toggleItalic,
title: i18n.global.t('input.editor.italic'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M14.0967 4.2H17.0001C17.3301 4.2 17.6001 3.93 17.6001 3.6C17.6001 3.27 17.3301 3 17.0001 3H10.2001C9.8701 3 9.6001 3.27 9.6001 3.6C9.6001 3.93 9.8701 4.2 10.2001 4.2H12.8748L9.90335 19.8H6.9999C6.6699 19.8 6.3999 20.07 6.3999 20.4C6.3999 20.73 6.6699 21 6.9999 21H13.7999C14.1299 21 14.3999 20.73 14.3999 20.4C14.3999 20.07 14.1299 19.8 13.7999 19.8H11.1253L14.0967 4.2Z"/></svg>',
},
{
name: 'strikethrough',
action: EasyMDE.toggleStrikethrough,
title: i18n.global.t('input.editor.strikethrough'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.25 7.17005C18.25 7.50005 17.98 7.77005 17.65 7.77005C17.32 7.77005 17.05 7.50005 17.05 7.17005V5.96005C15.97 5.12005 14.17 4.56005 12.79 4.31005C11.1 4.00005 9.51 4.30005 8.41 5.12005C7.2 6.03005 6.67 7.67005 7.19 8.88005C7.56 9.73005 8.37 10.31 8.98 10.64C9.57215 10.9644 10.1961 11.2013 10.8465 11.3999H20.4C20.73 11.3999 21 11.6699 21 11.9999C21 12.3299 20.73 12.5999 20.4 12.5999H15.3012C16.6583 13.0929 17.5255 13.7765 17.95 14.69C18.73 16.36 17.74 18.33 16.36 19.41C15.05 20.4401 13.35 21 11.54 21H11.16C9.78 20.9401 8.34 20.5301 6.95 19.85V20.3601C6.95 20.6901 6.68 20.96 6.35 20.96C6.02 20.96 5.75 20.6901 5.75 20.3601V17.36C5.75 17.03 6.02 16.76 6.35 16.76C6.68 16.76 6.95 17.03 6.95 17.36V18.5C8.35 19.2801 9.81 19.74 11.21 19.8C12.86 19.89 14.46 19.39 15.62 18.48C16.6 17.71 17.37 16.3 16.86 15.21C16.55 14.54 15.8 14.0201 14.58 13.63C13.9711 13.4331 13.3222 13.2762 12.6906 13.1235C12.6168 13.1056 12.5432 13.0878 12.47 13.07C12.4313 13.0607 12.3925 13.0514 12.3537 13.0421C11.7861 12.9055 11.2108 12.767 10.6413 12.5999H3.6C3.27 12.5999 3 12.3299 3 11.9999C3 11.6699 3.27 11.3999 3.6 11.3999H7.90288C7.04984 10.8343 6.42752 10.1363 6.09 9.36005C5.34 7.63005 6.03 5.40005 7.69 4.16005C9.05 3.15005 10.99 2.77005 13 3.13005C13.64 3.25005 15.53 3.66005 17.05 4.53005V4.17005C17.05 3.84005 17.32 3.57005 17.65 3.57005C17.98 3.57005 18.25 3.84005 18.25 4.17005V7.17005Z"/></svg>',
},
{
name: 'code',
action: EasyMDE.toggleCodeBlock,
title: i18n.global.t('input.editor.code'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M8.57 20.9601C8.64 20.9901 8.71 21.0001 8.78 21.0001C9.02 21.0001 9.24 20.8501 9.34 20.6101L15.79 3.81005C15.9 3.50005 15.75 3.15005 15.44 3.03005C15.14 2.92005 14.79 3.07005 14.67 3.38005L8.22 20.1801C8.11 20.4901 8.26 20.8401 8.57 20.9601ZM7.00007 18.0001C6.85007 18.0001 6.69007 17.9401 6.58007 17.8201L1.18007 12.4201C0.950068 12.1901 0.950068 11.8101 1.18007 11.5701L6.58007 6.17006C6.81007 5.94006 7.19007 5.94006 7.43007 6.17006C7.66007 6.40006 7.66007 6.78006 7.43007 7.02006L2.45007 12.0001L7.43007 16.9801C7.66007 17.2101 7.66007 17.5901 7.43007 17.8301C7.31007 17.9401 7.15007 18.0001 7.00007 18.0001ZM17 18.0001C16.85 18.0001 16.69 17.9401 16.58 17.8201C16.35 17.5901 16.35 17.2101 16.58 16.9701L21.55 12.0001L16.57 7.02006C16.34 6.79006 16.34 6.41006 16.57 6.17006C16.81 5.94006 17.19 5.94006 17.42 6.17006L22.82 11.5701C23.05 11.8001 23.05 12.1801 22.82 12.4201L17.42 17.8201C17.31 17.9401 17.15 18.0001 17 18.0001Z"/></svg>',
},
{
name: 'quote',
action: EasyMDE.toggleBlockquote,
title: i18n.global.t('input.editor.quote'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.373 5.16357H5.62695C4.79102 5.16357 4.11133 5.84326 4.11133 6.6792V16.2095C4.11133 17.0464 4.79102 17.7261 5.62695 17.7261H6.8877V21.1245C6.8877 21.3667 7.0332 21.5854 7.25684 21.6782C7.33203 21.7095 7.41016 21.7241 7.4873 21.7241C7.64258 21.7241 7.7959 21.6636 7.91113 21.5493L11.748 17.7261H19.373C20.209 17.7261 20.8887 17.0464 20.8887 16.2095V6.6792C20.8887 5.84326 20.209 5.16357 19.373 5.16357ZM19.6895 16.2095C19.6895 16.3843 19.5469 16.5269 19.373 16.5269H11.5C11.3408 16.5269 11.1895 16.5894 11.0762 16.7017L8.08691 19.6802V17.1265C8.08691 16.7954 7.81836 16.5269 7.4873 16.5269H5.62695C5.45312 16.5269 5.31055 16.3843 5.31055 16.2095V6.6792C5.31055 6.50537 5.45312 6.36279 5.62695 6.36279H19.373C19.5469 6.36279 19.6895 6.50537 19.6895 6.6792V16.2095ZM10.3431 8.45264C9.46326 8.45264 8.75 9.16589 8.75 10.0458C8.75 10.9257 9.46326 11.639 10.3431 11.639C10.4775 11.639 10.6058 11.6173 10.7305 11.5861V11.6195C10.7305 12.1322 10.3135 12.5492 9.75586 12.5492C9.4248 12.5492 9.17871 12.8177 9.17871 13.1488C9.17871 13.4799 9.46973 13.7484 9.80078 13.7484C10.9746 13.7484 11.9297 12.7933 11.9297 11.6195V10.1176L11.9294 10.1165L11.9292 10.1155C11.9297 10.1049 11.9312 10.0946 11.9326 10.0843L11.9326 10.0843C11.9345 10.0716 11.9363 10.059 11.9363 10.0458C11.9363 9.16589 11.223 8.45264 10.3431 8.45264ZM13.0637 10.0458C13.0637 9.16589 13.7771 8.45264 14.657 8.45264C15.5369 8.45264 16.2501 9.16589 16.2501 10.0458C16.2501 10.0584 16.2484 10.0706 16.2466 10.0828C16.2452 10.0929 16.2437 10.103 16.2433 10.1134C16.2433 10.1149 16.2441 10.1161 16.2441 10.1176V11.6195C16.2441 12.7933 15.2891 13.7484 14.1152 13.7484C13.7842 13.7484 13.4922 13.4799 13.4922 13.1488C13.4922 12.8177 13.7383 12.5492 14.0693 12.5492C14.6279 12.5492 15.0449 12.1322 15.0449 11.6195V11.5858C14.9202 11.6173 14.7915 11.639 14.657 11.639C13.7771 11.639 13.0637 10.9257 13.0637 10.0458Z"/></svg>',
},
{
name: 'unordered-list',
action: EasyMDE.toggleUnorderedList,
title: i18n.global.t('input.editor.unorderedList'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M6.5281 3.7002H3.5281C3.1981 3.7002 2.9281 3.9702 2.9281 4.3002V7.3002C2.9281 7.6302 3.1981 7.9002 3.5281 7.9002H6.5281C6.8581 7.9002 7.1281 7.6302 7.1281 7.3002V4.3002C7.1281 3.9702 6.8581 3.7002 6.5281 3.7002ZM5.9281 6.7002H4.1281V4.9002H5.9281V6.7002ZM3.5281 9.90015H6.5281C6.8581 9.90015 7.1281 10.1701 7.1281 10.5001V13.5001C7.1281 13.8301 6.8581 14.1001 6.5281 14.1001H3.5281C3.1981 14.1001 2.9281 13.8301 2.9281 13.5001V10.5001C2.9281 10.1701 3.1981 9.90015 3.5281 9.90015ZM4.1281 12.9001H5.9281V11.1001H4.1281V12.9001ZM3.5281 16.1001H6.5281C6.8581 16.1001 7.1281 16.3701 7.1281 16.7001V19.7001C7.1281 20.0301 6.8581 20.3001 6.5281 20.3001H3.5281C3.1981 20.3001 2.9281 20.0301 2.9281 19.7001V16.7001C2.9281 16.3701 3.1981 16.1001 3.5281 16.1001ZM4.1281 19.1001H5.9281V17.3001H4.1281V19.1001ZM9.72817 6.4002H20.7282C21.0582 6.4002 21.3282 6.1302 21.3282 5.8002C21.3282 5.4702 21.0582 5.2002 20.7282 5.2002H9.72817C9.39817 5.2002 9.12817 5.4702 9.12817 5.8002C9.12817 6.1302 9.39817 6.4002 9.72817 6.4002ZM9.72817 11.4001H20.7282C21.0582 11.4001 21.3282 11.6701 21.3282 12.0001C21.3282 12.3301 21.0582 12.6001 20.7282 12.6001H9.72817C9.39817 12.6001 9.12817 12.3301 9.12817 12.0001C9.12817 11.6701 9.39817 11.4001 9.72817 11.4001ZM9.72817 17.6001H20.7282C21.0582 17.6001 21.3282 17.8701 21.3282 18.2001C21.3282 18.5301 21.0582 18.8001 20.7282 18.8001H9.72817C9.39817 18.8001 9.12817 18.5301 9.12817 18.2001C9.12817 17.8701 9.39817 17.6001 9.72817 17.6001Z"/></svg>',
},
{
name: 'ordered-list',
action: EasyMDE.toggleOrderedList,
title: i18n.global.t('input.editor.orderedList'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M4.19494 8.29994H5.99494C6.26494 8.29994 6.49494 8.07995 6.49494 7.79994C6.49494 7.51995 6.27494 7.29994 5.99494 7.29994H5.59494V3.79994C5.59494 3.62994 5.50494 3.46994 5.36494 3.37994C5.22494 3.28994 5.04494 3.26994 4.89494 3.33994L3.89494 3.76994C3.64494 3.87994 3.52494 4.17994 3.63494 4.42994C3.74494 4.67994 4.03494 4.79994 4.29494 4.68994L4.59494 4.55994V7.29994H4.19494C3.91494 7.29994 3.69494 7.51995 3.69494 7.79994C3.69494 8.07995 3.91494 8.29994 4.19494 8.29994ZM20.195 6.39995H9.19497C8.86497 6.39995 8.59497 6.12995 8.59497 5.79995C8.59497 5.46995 8.86497 5.19995 9.19497 5.19995H20.195C20.525 5.19995 20.795 5.46995 20.795 5.79995C20.795 6.12995 20.525 6.39995 20.195 6.39995ZM3.78486 14.36H6.37486C6.65486 14.36 6.87486 14.14 6.87486 13.86C6.87486 13.58 6.65486 13.36 6.37486 13.36H4.88486C5.00486 13.23 5.12486 13.09 5.23486 12.95C5.26626 12.9151 5.29645 12.8802 5.32626 12.8458L5.32629 12.8457C5.38192 12.7814 5.43627 12.7186 5.49486 12.66C5.73486 12.4 5.98486 12.12 6.17486 11.79C6.47486 11.25 6.41486 10.63 6.01486 10.17C5.57486 9.66 4.86486 9.5 4.24486 9.74C3.74486 9.95 3.39486 10.35 3.22486 10.91C3.14486 11.18 3.29486 11.46 3.56486 11.54C3.82486 11.61 4.10486 11.46 4.18486 11.2C4.29486 10.85 4.48486 10.73 4.62486 10.67C4.88486 10.57 5.13486 10.68 5.26486 10.82C5.38486 10.96 5.40486 11.12 5.30486 11.29C5.17595 11.5202 4.99618 11.7165 4.80458 11.9257L4.75486 11.98C4.67298 12.0801 4.58283 12.1801 4.49946 12.2727L4.49945 12.2727L4.47486 12.3C4.12486 12.72 3.76486 13.13 3.40486 13.53C3.27486 13.68 3.23486 13.9 3.32486 14.07C3.41486 14.24 3.58486 14.36 3.78486 14.36ZM3.68486 20.3699C4.04486 20.5899 4.46486 20.6999 4.87486 20.6999C5.13486 20.6999 5.38486 20.6499 5.61486 20.5499C6.31486 20.2799 6.73486 19.5599 6.60486 18.8799C6.53486 18.5499 6.35486 18.2899 6.12486 18.0899C6.32486 17.8999 6.45486 17.6499 6.50486 17.3799C6.57486 17.0099 6.49486 16.6299 6.27486 16.3099C5.85486 15.6899 5.07486 15.5199 4.10486 15.8299C3.83486 15.9199 3.69486 16.1999 3.77486 16.4599C3.86486 16.7299 4.14486 16.8699 4.40486 16.7899C4.70486 16.6999 5.24486 16.5799 5.45486 16.8899C5.51486 16.9899 5.54486 17.0999 5.52486 17.1999C5.51486 17.2699 5.47486 17.3599 5.36486 17.4299C5.26486 17.4999 5.12486 17.5399 4.95486 17.5799L4.77486 17.6299C4.54486 17.6999 4.40486 17.9099 4.41486 18.1499C4.42486 18.3899 4.61486 18.5799 4.84486 18.6099C5.20486 18.6599 5.58486 18.8299 5.63486 19.0799C5.67486 19.2999 5.46486 19.5499 5.25486 19.6299C4.94486 19.7599 4.52486 19.7099 4.21486 19.5199C3.97486 19.3699 3.67486 19.4399 3.52486 19.6799C3.37486 19.9199 3.44486 20.2299 3.68486 20.3699ZM20.195 18.7999H9.19497C8.86497 18.7999 8.59497 18.5299 8.59497 18.1999C8.59497 17.8699 8.86497 17.5999 9.19497 17.5999H20.195C20.525 17.5999 20.795 17.8699 20.795 18.1999C20.795 18.5299 20.525 18.7999 20.195 18.7999ZM9.19497 12.5999H20.195C20.525 12.5999 20.795 12.3299 20.795 11.9999C20.795 11.6699 20.525 11.3999 20.195 11.3999H9.19497C8.86497 11.3999 8.59497 11.6699 8.59497 11.9999C8.59497 12.3299 8.86497 12.5999 9.19497 12.5999Z"/></svg>',
},
'|',
{
name: 'clean-block',
action: EasyMDE.cleanBlock,
title: i18n.global.t('input.editor.cleanBlock'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M9.25989 6.18384H20.4513C20.7823 6.18384 21.0509 6.45239 21.0509 6.78345V17.9749C21.0509 18.3059 20.7823 18.5745 20.4513 18.5745H9.25989C9.0929 18.5745 8.93469 18.5061 8.82043 18.384L3.6095 12.7883C3.39563 12.5579 3.39563 12.2004 3.6095 11.97L8.82043 6.37427C8.93469 6.2522 9.0929 6.18384 9.25989 6.18384ZM9.52063 17.3752H19.8517V7.38306H9.52063L4.86926 12.3792L9.52063 17.3752ZM12.7755 15.0686C12.6222 15.0686 12.4679 15.01 12.3517 14.8928C12.1173 14.6584 12.1173 14.2786 12.3517 14.0452L14.0503 12.3469L12.3517 10.6487C12.1173 10.4153 12.1173 10.0354 12.3517 9.80103C12.5841 9.56665 12.965 9.56665 13.1993 9.80103L14.8981 11.4994L16.5968 9.80103C16.8312 9.56665 17.212 9.56665 17.4445 9.80103C17.6788 10.0354 17.6788 10.4153 17.4445 10.6487L15.7458 12.3469L17.4445 14.0452C17.6788 14.2786 17.6788 14.6584 17.4445 14.8928C17.3282 15.01 17.174 15.0686 17.0206 15.0686C16.8673 15.0686 16.714 15.01 16.5968 14.8928L14.8981 13.1945L13.1993 14.8928C13.0822 15.01 12.9288 15.0686 12.7755 15.0686Z"/></svg>',
},
{
name: 'link',
action: EasyMDE.drawLink,
title: i18n.global.t('input.editor.link'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M11.4399 15.3452C11.4999 15.3652 11.5699 15.3752 11.6299 15.3752C11.8799 15.3752 12.1199 15.2152 12.1999 14.9652C12.2999 14.6452 12.1299 14.3052 11.8199 14.2052C11.3499 14.0452 10.9299 13.7852 10.5699 13.4152C10.1999 13.0552 9.9399 12.6452 9.7799 12.1552C9.6599 11.8252 9.5999 11.4652 9.5999 11.0952C9.5999 10.2152 9.9399 9.38518 10.5699 8.75518L15.1599 4.15518C16.4499 2.87518 18.5399 2.87518 19.8299 4.15518C20.4499 4.78518 20.7899 5.61518 20.7899 6.49518C20.7899 7.37518 20.4499 8.20518 19.8299 8.82518L16.7399 11.9052C16.5099 12.1452 16.5099 12.5252 16.7399 12.7552C16.9799 12.9852 17.3599 12.9852 17.5899 12.7552L20.6799 9.67518C21.5299 8.83518 21.9999 7.69518 21.9999 6.49518C21.9999 5.29518 21.5299 4.16518 20.6899 3.30518C18.9299 1.55518 16.0799 1.55518 14.3199 3.30518L9.7299 7.90518C8.8699 8.75518 8.3999 9.88518 8.3999 11.0952C8.3999 11.6152 8.4899 12.1152 8.6499 12.5552C8.8599 13.1952 9.2399 13.7952 9.7199 14.2652C10.1999 14.7552 10.7999 15.1352 11.4399 15.3452ZM3.32 20.6851C4.2 21.5551 5.35 21.9951 6.5 21.9951C7.65 21.9951 8.81 21.5551 9.69 20.7051L14.28 16.1051C15.14 15.2551 15.61 14.1251 15.61 12.9151C15.61 12.4551 15.54 11.9951 15.4 11.5551C15.17 10.8651 14.8 10.2551 14.28 9.73509C13.76 9.21509 13.15 8.84509 12.46 8.61509C12.14 8.51509 11.8 8.68509 11.7 8.99509C11.6 9.30509 11.77 9.64509 12.1 9.75509C12.61 9.91509 13.06 10.1951 13.44 10.5751C13.82 10.9551 14.09 11.4051 14.26 11.9151C14.36 12.2351 14.41 12.5651 14.41 12.9051C14.41 13.7951 14.06 14.6251 13.43 15.2451L8.84 19.8451C7.55 21.1251 5.46 21.1251 4.17 19.8451C3.55 19.2151 3.21 18.3951 3.21 17.5051C3.21 16.6151 3.55 15.7851 4.17 15.1651L7.35 11.9851C7.58 11.7451 7.59 11.3651 7.35 11.1351C7.11 10.9051 6.73 10.9051 6.5 11.1351L3.32 14.3151C2.47 15.1551 2 16.2851 2 17.4951C2 18.7051 2.47 19.8351 3.32 20.6851Z"/></svg>',
},
{
name: 'image',
action: EasyMDE.drawImage,
title: i18n.global.t('input.editor.image'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M4 4C2.89543 4 2 4.89543 2 6V16V17.5152V18C2 19.1046 2.89543 20 4 20H20C21.0528 20 21.9156 19.1866 21.9942 18.1539L22 18.1632V18V16V6C22 4.89543 21.1046 4 20 4H4ZM3.2 17.7V16.5642L6.78192 13.7254C6.8616 13.6622 6.97597 13.6689 7.04776 13.7409L10.3126 17.0146C10.7026 17.4056 11.3357 17.4065 11.7268 17.0165C11.7606 16.9827 11.792 16.9465 11.8207 16.9083L16.736 10.352C16.8023 10.2636 16.9277 10.2457 17.016 10.312C17.0355 10.3265 17.0521 10.3445 17.0651 10.365L20.8 16.2669V17.7C20.8 18.3075 20.3075 18.8 19.7 18.8H4.3C3.69249 18.8 3.2 18.3075 3.2 17.7ZM17.3865 8.61836L20.8 14.08V6.3C20.8 5.69249 20.3075 5.2 19.7 5.2H4.3C3.69249 5.2 3.2 5.69249 3.2 6.3V15.04L6.65054 12.2796C6.84949 12.1204 7.13629 12.1363 7.31645 12.3164L10.8369 15.8369C10.915 15.915 11.0417 15.915 11.1198 15.8369C11.1265 15.8302 16.5625 8.58336 16.5625 8.58336C16.7282 8.36245 17.0416 8.31768 17.2625 8.48336C17.3118 8.52034 17.3538 8.56611 17.3865 8.61836ZM8 8.5C8 9.32843 7.32843 10 6.5 10C5.67157 10 5 9.32843 5 8.5C5 7.67157 5.67157 7 6.5 7C7.32843 7 8 7.67157 8 8.5Z"/></svg>',
},
{
name: 'table',
action: EasyMDE.drawTable,
title: i18n.global.t('input.editor.table'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M6.18524 3.08496H19.4152C20.6752 3.08496 21.7152 4.11496 21.7152 5.38496V18.615C21.7152 19.885 20.6852 20.915 19.4152 20.915H6.18524C4.91524 20.915 3.88525 19.885 3.88525 18.615V5.38496C3.88525 4.11496 4.91524 3.08496 6.18524 3.08496ZM19.4052 19.705C20.0152 19.705 20.5052 19.215 20.5052 18.605H20.5153V5.38496C20.5153 4.77496 20.0252 4.28496 19.4152 4.28496H6.18524C5.57524 4.28496 5.08521 4.77496 5.08521 5.38496V18.605C5.08521 19.215 5.57524 19.705 6.18524 19.705H19.4052ZM17.4453 9.15503H8.15527C7.82527 9.15503 7.5553 9.42503 7.5553 9.75503C7.5553 10.085 7.82527 10.355 8.15527 10.355H17.4453C17.7753 10.355 18.0453 10.085 18.0453 9.75503C18.0453 9.42503 17.7753 9.15503 17.4453 9.15503ZM17.4453 13.635H8.15527C7.82527 13.635 7.5553 13.905 7.5553 14.235C7.5553 14.565 7.82527 14.835 8.15527 14.835H17.4453C17.7753 14.835 18.0453 14.565 18.0453 14.235C18.0453 13.905 17.7753 13.635 17.4453 13.635Z"/></svg>',
},
{
name: 'horizontal-rule',
action: EasyMDE.drawHorizontalRule,
title: i18n.global.t('input.editor.horizontalRule'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M21 13H3C2.45 13 2 12.55 2 12C2 11.45 2.45 11 3 11H21C21.55 11 22 11.45 22 12C22 12.55 21.55 13 21 13Z"/></svg>',
},
'|',
{
name: 'side-by-side',
action: EasyMDE.toggleSideBySide,
title: i18n.global.t('input.editor.sideBySide'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M18.4787 14.58C18.3587 14.69 18.2987 14.85 18.2987 15C18.2987 15.15 18.3587 15.31 18.4787 15.42C18.7187 15.65 19.0987 15.65 19.3287 15.42L22.3287 12.42C22.5587 12.18 22.5587 11.8 22.3287 11.57L19.3287 8.56996C19.0887 8.33996 18.7087 8.33996 18.4787 8.56996C18.2487 8.80996 18.2487 9.18996 18.4787 9.41996L20.451 11.3999L14.4487 11.3999L14.4487 4.6C14.4487 4.27 14.1787 4 13.8487 4C13.5187 4 13.2487 4.27 13.2487 4.6L13.2487 19.4C13.2487 19.73 13.5187 20 13.8487 20C14.1787 20 14.4487 19.73 14.4487 19.4L14.4487 12.5999L20.4511 12.5999L18.4787 14.58ZM9.54878 19.4L9.54878 12.5999L3.5486 12.5999L5.52867 14.58C5.75867 14.81 5.75867 15.19 5.52867 15.43C5.29867 15.66 4.91867 15.66 4.67867 15.43L1.67867 12.43C1.63274 12.384 1.5956 12.3323 1.56725 12.2774C1.53058 12.2077 1.50724 12.1299 1.50068 12.0477C1.49934 12.0317 1.49867 12.0158 1.49867 12C1.49867 11.9841 1.49933 11.9682 1.50067 11.9522C1.51454 11.778 1.60365 11.6242 1.73526 11.5234L4.67867 8.57997C4.90867 8.34997 5.28867 8.34997 5.52867 8.57997C5.75867 8.80997 5.75867 9.18997 5.52867 9.42997L3.55107 11.3999L9.54878 11.3999L9.54878 4.6C9.54878 4.27 9.81878 4 10.1488 4C10.4788 4 10.7488 4.27 10.7488 4.6L10.7488 11.9999L10.7488 19.4C10.7488 19.73 10.4788 20 10.1488 20C9.81878 20 9.54878 19.73 9.54878 19.4Z"/></svg>',
},
{
name: 'guide',
action() {
window.open('https://www.markdownguide.org/basic-syntax/', '_blank')
},
title: i18n.global.t('input.editor.guide'),
icon: '<svg viewBox="0 0 24 24"><rect fill="none" rx="0" ry="0"/><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M19.4999 2.3999H6.4999C5.0699 2.3999 3.8999 3.5699 3.8999 4.9999V18.9999C3.8999 20.4299 5.0699 21.5999 6.4999 21.5999H19.4999C19.8299 21.5999 20.0999 21.3299 20.0999 20.9999V16.9999V2.9999C20.0999 2.6699 19.8299 2.3999 19.4999 2.3999ZM5.0999 4.9999V16.8118C5.50468 16.5513 5.98546 16.3999 6.4999 16.3999H18.8999V3.5999H6.4999C5.7299 3.5999 5.0999 4.2299 5.0999 4.9999ZM6.4999 17.5999H18.8999V20.3999H6.4999C5.7299 20.3999 5.0999 19.7699 5.0999 18.9999C5.0999 18.2299 5.7299 17.5999 6.4999 17.5999ZM8.4999 8.5999H15.4999C15.8299 8.5999 16.0999 8.3299 16.0999 7.9999C16.0999 7.6699 15.8299 7.3999 15.4999 7.3999H8.4999C8.1699 7.3999 7.8999 7.6699 7.8999 7.9999C7.8999 8.3299 8.1699 8.5999 8.4999 8.5999ZM15.4999 11.3999H8.4999C8.1699 11.3999 7.8999 11.6699 7.8999 11.9999C7.8999 12.3299 8.1699 12.5999 8.4999 12.5999H15.4999C15.8299 12.5999 16.0999 12.3299 16.0999 11.9999C16.0999 11.6699 15.8299 11.3999 15.4999 11.3999Z"/></svg>',
},
],
}
}

View File

@ -9,9 +9,9 @@
<div class="control" :class="{'is-loading': loading || localLoading}">
<div
class="input-wrapper input"
:class="{'has-multiple': hasMultiple}"
:class="{'has-multiple': hasMultiple, 'has-removal-button': removalAvailable}"
>
<slot
<slot
v-if="Array.isArray(internalValue)"
name="items"
:items="internalValue"
@ -19,14 +19,14 @@
>
<template v-for="(item, key) in internalValue">
<slot name="tag" :item="item">
<span :key="`item${key}`" class="tag ml-2 mt-2">
{{ label !== '' ? item[label] : item }}
<BaseButton @click="() => remove(item)" class="delete is-small"></BaseButton>
</span>
<span :key="`item${key}`" class="tag ml-2 mt-2">
{{ label !== '' ? item[label] : item }}
<BaseButton @click="() => remove(item)" class="delete is-small"></BaseButton>
</span>
</slot>
</template>
</slot>
<input
type="text"
class="input"
@ -40,6 +40,13 @@
:autocomplete="autocompleteEnabled ? undefined : 'off'"
:spellcheck="autocompleteEnabled ? undefined : 'false'"
/>
<BaseButton
v-if="removalAvailable"
class="removal-button"
@click="resetSelectedValue"
>
<icon icon="times"/>
</BaseButton>
</div>
</div>
@ -100,6 +107,7 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import BaseButton from '@/components/base/BaseButton.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function elementInResults(elem: string | any, label: string, query: string): boolean {
// Don't make create available if we have an exact match in our search results.
if (label !== '') {
@ -128,7 +136,8 @@ const props = defineProps({
* The search results where the @search listener needs to put the results into
*/
searchResults: {
type: Array as PropType<{[id: string]: any}>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type: Array as PropType<{ [id: string]: any }>,
default: () => [],
},
/**
@ -143,7 +152,8 @@ const props = defineProps({
* The object with the value, updated every time an entry is selected.
*/
modelValue: {
type: [Object] as PropType<{[key: string]: any}>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type: [Object] as PropType<{ [key: string]: any }>,
default: null,
},
/**
@ -159,7 +169,7 @@ const props = defineProps({
createPlaceholder: {
type: String,
default() {
const {t} = useI18n({useScope: 'global'})
const {t} = useI18n({useScope: 'global'})
return t('input.multiselect.createPlaceholder')
},
},
@ -169,7 +179,7 @@ const props = defineProps({
selectPlaceholder: {
type: String,
default() {
const {t} = useI18n({useScope: 'global'})
const {t} = useI18n({useScope: 'global'})
return t('input.multiselect.selectPlaceholder')
},
},
@ -215,30 +225,33 @@ const props = defineProps({
})
const emit = defineEmits<{
(e: 'update:modelValue', value: null): void
(e: 'update:modelValue', value: null): void
/**
* Triggered every time the search query input changes
*/
(e: 'search', query: string): void
(e: 'search', query: string): void
/**
* Triggered every time an option from the search results is selected. Also triggers a change in v-model.
*/
(e: 'select', value: {[key: string]: any}): void
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(e: 'select', value: { [key: string]: any }): void
/**
* If nothing or no exact match was found and `creatable` is true, this event is triggered with the current value of the search query.
*/
(e: 'create', query: string): void
(e: 'create', query: string): void
/**
* If `multiple` is enabled, this will be fired every time an item is removed from the array of selected items.
*/
(e: 'remove', value: null): void
(e: 'remove', value: null): void
}>()
const query = ref<string | {[key: string]: any}>('')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const query = ref<string | { [key: string]: any }>('')
const searchTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
const localLoading = ref(false)
const showSearchResults = ref(false)
const internalValue = ref<string | {[key: string]: any} | any[] | null>(null)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const internalValue = ref<string | { [key: string]: any } | any[] | null>(null)
onMounted(() => document.addEventListener('click', hideSearchResultsHandler))
onBeforeUnmount(() => document.removeEventListener('click', hideSearchResultsHandler))
@ -266,17 +279,19 @@ const searchResultsVisible = computed(() => {
})
const creatableAvailable = computed(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const hasResult = filteredSearchResults.value.some((elem: any) => elementInResults(elem, props.label, query.value))
const hasQueryAlreadyAdded = Array.isArray(internalValue.value) && internalValue.value.some(elem => elementInResults(elem, props.label, query.value))
return props.creatable
&& query.value !== ''
return props.creatable
&& query.value !== ''
&& !(hasResult || hasQueryAlreadyAdded)
})
const filteredSearchResults = computed(() => {
const currentInternal = internalValue.value
if (props.multiple && currentInternal !== null && Array.isArray(currentInternal)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return searchResults.value.filter((item: any) => !currentInternal.some(e => e === item))
}
@ -287,7 +302,13 @@ const hasMultiple = computed(() => {
return props.multiple && Array.isArray(internalValue.value) && internalValue.value.length > 0
})
const removalAvailable = computed(() => !props.multiple && internalValue.value !== null && query.value !== '')
function resetSelectedValue() {
select(null)
}
const searchInput = ref<HTMLInputElement | null>(null)
// Searching will be triggered with a 200ms delay to avoid searching on every keyup event.
function search() {
@ -312,6 +333,7 @@ function search() {
}
const multiselectRoot = ref<HTMLElement | null>(null)
function hideSearchResultsHandler(e: MouseEvent) {
closeWhenClickedOutside(e, multiselectRoot.value, closeSearchResults)
}
@ -328,12 +350,14 @@ function handleFocus() {
}, 10)
}
function select(object: {[key: string]: any}) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function select(object: { [key: string]: any } | null) {
if (props.multiple) {
if (internalValue.value === null) {
internalValue.value = []
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(internalValue.value as any[]).push(object)
} else {
internalValue.value = object
@ -347,7 +371,8 @@ function select(object: {[key: string]: any}) {
}
}
function setSelectedObject(object: string | {[id: string]: any} | null, resetOnly = false) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setSelectedObject(object: string | { [id: string]: any } | null, resetOnly = false) {
internalValue.value = object
// We assume we're getting an array when multiple is enabled and can therefore leave the query
@ -370,6 +395,7 @@ function setSelectedObject(object: string | {[id: string]: any} | null, resetOnl
}
const results = ref<(Element | ComponentPublicInstance)[]>([])
function setResult(el: Element | ComponentPublicInstance | null, index: number) {
if (el === null) {
delete results.value[index]
@ -415,8 +441,9 @@ function createOrSelectOnEnter() {
if (!creatableAvailable.value) {
// Check if there's an exact match for our search term
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const exactMatch = filteredSearchResults.value.find((elem: any) => elementInResults(elem, props.label, query.value))
if(exactMatch) {
if (exactMatch) {
select(exactMatch)
}
@ -426,6 +453,7 @@ function createOrSelectOnEnter() {
create()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function remove(item: any) {
for (const ind in internalValue.value) {
if (internalValue.value[ind] === item) {
@ -572,4 +600,14 @@ function focus() {
transition: color $transition;
padding-left: .5rem;
}
.has-removal-button {
position: relative;
}
.removal-button {
position: absolute;
right: .5rem;
color: var(--danger);
}
</style>

View File

@ -1,522 +0,0 @@
<template>
<div class="vue-easymde" ref="easymdeRef">
<textarea
class="vue-simplemde-textarea"
:name="name"
:value="modelValue"
@input="handleInput(($event.target as HTMLTextAreaElement).value)"
/>
</div>
</template>
<script setup lang="ts">
import {ref, watch, onMounted, onDeactivated, onBeforeUnmount, nextTick, shallowReactive, type ShallowReactive, type PropType} from 'vue'
import EasyMDE, {toggleFullScreen} from 'easymde'
import {marked} from 'marked'
import type CodeMirror from 'codemirror'
const props = defineProps({
modelValue: {
type: String,
default: '',
},
name: {
type: String,
},
previewClass: {
type: String,
},
autoinit: {
type: Boolean,
default: true,
},
highlight: {
type: Boolean,
default: false,
},
sanitize: {
type: Boolean,
default: false,
},
configs: {
type: Object,
default: () => ({}),
},
previewRender: {
type: Function as PropType<EasyMDE.Options['previewRender']>,
},
})
const emit = defineEmits(['update:modelValue', 'blur', 'initialized'])
const isValueUpdateFromInner = ref(false)
let easymde: ShallowReactive<EasyMDE> | undefined
onMounted(() => {
if (props.autoinit) initialize()
})
onDeactivated(() => {
if (easymde === undefined) return
if (easymde.isFullscreenActive()) toggleFullScreen(easymde)
easymde.toTextArea
})
onBeforeUnmount(() => {
if (easymde) {
easymde.toTextArea()
easymde.cleanup()
easymde = undefined
}
})
const easymdeRef = ref<HTMLElement | null>(null)
function initialize() {
const configs: EasyMDE.Options = Object.assign({
element: easymdeRef.value?.firstElementChild as HTMLElement,
initialValue: props.modelValue,
previewRender: props.previewRender,
renderingConfig: {},
}, props.configs)
// Synchronize the values of value and initialValue
if (configs.initialValue) {
emit('update:modelValue', configs.initialValue)
}
// Determine whether to enable code highlighting
if (props.highlight) {
configs.renderingConfig!.codeSyntaxHighlighting = true
}
// Set whether to render the input html
marked.setOptions({ sanitize: props.sanitize })
// Instantiated editor
easymde = shallowReactive(new EasyMDE(configs))
// Add a custom previewClass
const className = props.previewClass || ''
addPreviewClass(easymde, className)
// Binding event
easymde.codemirror.on('change', handleCodemirrorInput)
easymde.codemirror.on('blur', handleCodemirrorBlur)
nextTick(() => emit('initialized', easymde))
}
function addPreviewClass(easymde: EasyMDE, className: string) {
const wrapper = easymde.codemirror.getWrapperElement()
const preview = document.createElement('div')
wrapper.nextSibling.className += ` ${className}`
preview.className = `editor-preview ${className}`
wrapper.appendChild(preview)
}
function handleInput(val: string) {
isValueUpdateFromInner.value = true
emit('update:modelValue', val)
}
function handleCodemirrorInput(instance: CodeMirror.Editor, changeObj: CodeMirror.EditorChange) {
if (changeObj.origin === 'setValue' || easymde === undefined) {
return
}
handleInput(easymde.value())
}
function handleCodemirrorBlur() {
if (easymde === undefined) {
return
}
isValueUpdateFromInner.value = true
emit('blur', easymde.value())
}
watch(
() => props.modelValue,
(val) => {
if (isValueUpdateFromInner.value) {
isValueUpdateFromInner.value = false
} else {
easymde?.value(val)
}
},
)
</script>
<style lang="scss">
.EasyMDEContainer {
display: block;
}
.EasyMDEContainer.sided--no-fullscreen {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.EasyMDEContainer .CodeMirror {
box-sizing: border-box;
height: auto;
border: 1px solid #ddd;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 10px;
font: inherit;
z-index: 0;
word-wrap: break-word;
}
.EasyMDEContainer .CodeMirror-scroll {
cursor: text;
}
.EasyMDEContainer .CodeMirror-fullscreen {
background: #fff;
position: fixed !important;
top: 50px;
left: 0;
right: 0;
bottom: 0;
height: auto;
z-index: 8;
border-right: none !important;
border-bottom-right-radius: 0 !important;
}
.EasyMDEContainer .CodeMirror-sided {
width: 50% !important;
}
.EasyMDEContainer.sided--no-fullscreen .CodeMirror-sided {
border-right: none!important;
border-bottom-right-radius: 0px;
position: relative;
flex: 1 1 auto;
}
.EasyMDEContainer .CodeMirror-placeholder {
opacity: .5;
}
.EasyMDEContainer .CodeMirror-focused .CodeMirror-selected {
background: #d9d9d9;
}
.editor-toolbar {
position: relative;
user-select: none;
padding: 9px 10px;
border-top: 1px solid #bbb;
border-left: 1px solid #bbb;
border-right: 1px solid #bbb;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.editor-toolbar.fullscreen {
width: 100%;
height: 50px;
padding-top: 10px;
padding-bottom: 10px;
box-sizing: border-box;
background: #fff;
border: 0;
position: fixed;
top: 0;
left: 0;
opacity: 1;
z-index: 9;
}
.editor-toolbar.fullscreen::before {
width: 20px;
height: 50px;
background: linear-gradient(to right, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
position: fixed;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
.editor-toolbar.fullscreen::after {
width: 20px;
height: 50px;
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
position: fixed;
top: 0;
right: 0;
margin: 0;
padding: 0;
}
.EasyMDEContainer.sided--no-fullscreen .editor-toolbar {
width: 100%;
}
.editor-toolbar button, .editor-toolbar .easymde-dropdown {
background: transparent;
display: inline-block;
text-align: center;
text-decoration: none !important;
height: 30px;
margin: 0;
padding: 0;
border: 1px solid transparent;
border-radius: 3px;
cursor: pointer;
}
.editor-toolbar button {
width: 30px;
}
.editor-toolbar button.active,
.editor-toolbar button:hover {
background: #fcfcfc;
border-color: #95a5a6;
}
.editor-toolbar i.separator {
display: inline-block;
width: 0;
border-left: 1px solid #d9d9d9;
border-right: 1px solid #fff;
color: transparent;
text-indent: -10px;
margin: 0 6px;
}
.editor-toolbar button:after {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
font-size: 65%;
vertical-align: text-bottom;
position: relative;
top: 2px;
}
.editor-toolbar button.heading-1:after {
content: "1";
}
.editor-toolbar button.heading-2:after {
content: "2";
}
.editor-toolbar button.heading-3:after {
content: "3";
}
.editor-toolbar button.heading-bigger:after {
content: "▲";
}
.editor-toolbar button.heading-smaller:after {
content: "▼";
}
.editor-toolbar.disabled-for-preview button:not(.no-disable) {
opacity: .6;
pointer-events: none;
}
@media only screen and (max-width: 700px) {
.editor-toolbar i.no-mobile {
display: none;
}
}
.editor-statusbar {
padding: 8px 10px;
font-size: 12px;
color: #959694;
text-align: right;
}
.EasyMDEContainer.sided--no-fullscreen .editor-statusbar {
width: 100%;
}
.editor-statusbar span {
display: inline-block;
min-width: 4em;
margin-left: 1em;
}
.editor-statusbar .lines:before {
content: 'lines: '
}
.editor-statusbar .words:before {
content: 'words: '
}
.editor-statusbar .characters:before {
content: 'characters: '
}
.editor-preview-full {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 7;
overflow: auto;
display: none;
box-sizing: border-box;
}
.editor-preview-side {
position: fixed;
bottom: 0;
width: 50%;
top: 50px;
right: 0;
z-index: 9;
overflow: auto;
display: none;
box-sizing: border-box;
border: 1px solid #ddd;
word-wrap: break-word;
}
.editor-preview-active-side {
display: block
}
.EasyMDEContainer.sided--no-fullscreen .editor-preview-active-side {
flex: 1 1 auto;
height: auto;
position: static;
}
.editor-preview-active {
display: block
}
.editor-preview {
padding: 10px;
background: #fafafa;
}
.editor-preview > p {
margin-top: 0
}
.editor-preview pre {
background: #eee;
margin-bottom: 10px;
}
.editor-preview table td,
.editor-preview table th {
border: 1px solid #ddd;
padding: 5px;
}
.cm-s-easymde .cm-tag {
color: #63a35c;
}
.cm-s-easymde .cm-attribute {
color: #795da3;
}
.cm-s-easymde .cm-string {
color: #183691;
}
.cm-s-easymde .cm-header-1 {
font-size: 200%;
line-height: 200%;
}
.cm-s-easymde .cm-header-2 {
font-size: 160%;
line-height: 160%;
}
.cm-s-easymde .cm-header-3 {
font-size: 125%;
line-height: 125%;
}
.cm-s-easymde .cm-header-4 {
font-size: 110%;
line-height: 110%;
}
.cm-s-easymde .cm-comment {
background: rgba(0, 0, 0, .05);
border-radius: 2px;
}
.cm-s-easymde .cm-link {
color: #7f8c8d;
}
.cm-s-easymde .cm-url {
color: #aab2b3;
}
.cm-s-easymde .cm-quote {
color: #7f8c8d;
font-style: italic;
}
.editor-toolbar .easymde-dropdown {
position: relative;
background: linear-gradient(to bottom right, #fff 0%, #fff 84%, #333 50%, #333 100%);
border-radius: 0;
border: 1px solid #fff;
}
.editor-toolbar .easymde-dropdown:hover {
background: linear-gradient(to bottom right, #fff 0%, #fff 84%, #333 50%, #333 100%);
}
.easymde-dropdown-content {
display: block;
visibility: hidden;
position: absolute;
background-color: #f9f9f9;
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
padding: 8px;
z-index: 2;
top: 30px;
}
.easymde-dropdown:active .easymde-dropdown-content,
.easymde-dropdown:focus .easymde-dropdown-content {
visibility: visible;
}
span[data-img-src]::after{
content: '';
background-image: var(--bg-image);
display: block;
max-height: 100%;
max-width: 100%;
background-size: contain;
height: 0;
padding-top: var(--height);
width: var(--width);
background-repeat: no-repeat;
}
</style>
<style lang="scss" scoped>
.vue-easymde .markdown-body {
padding: 0.5em
}
.vue-easymde .editor-preview-active,
.vue-easymde .editor-preview-active-side {
display: block;
}
</style>

View File

@ -6,6 +6,10 @@ import {
faArchive,
faArrowLeft,
faArrowUpFromBracket,
faBold,
faItalic,
faStrikethrough,
faCode,
faBars,
faBell,
faBolt,
@ -29,6 +33,7 @@ import {
faFilter,
faForward,
faGripLines,
faHeader,
faHistory,
faImage,
faKeyboard,
@ -59,14 +64,26 @@ import {
faTimes,
faTrashAlt,
faUser,
faUsers, faX,
faUsers,
faQuoteRight,
faListUl,
faLink,
faUndo,
faRedo,
faUnlink,
faParagraph,
faTable,
faX, faArrowTurnDown, faListCheck, faXmark, faXmarksLines, faFont, faRulerHorizontal, faUnderline,
} from '@fortawesome/free-solid-svg-icons'
import {
faBellSlash,
faCalendarAlt,
faCheckSquare,
faClock,
faComments,
faFileImage,
faSave,
faSquareCheck,
faStar,
faSun,
faTimesCircle,
@ -76,6 +93,21 @@ import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
import type {FontAwesomeIcon as FontAwesomeIconFixedTypes} from '@/types/vue-fontawesome'
library.add(faBold)
library.add(faUndo)
library.add(faRedo)
library.add(faItalic)
library.add(faLink)
library.add(faUnlink)
library.add(faParagraph)
library.add(faSquareCheck)
library.add(faTable)
library.add(faFileImage)
library.add(faCheckSquare)
library.add(faStrikethrough)
library.add(faCode)
library.add(faQuoteRight)
library.add(faListUl)
library.add(faAlignLeft)
library.add(faAngleRight)
library.add(faArchive)
@ -107,6 +139,7 @@ library.add(faFillDrip)
library.add(faFilter)
library.add(faForward)
library.add(faGripLines)
library.add(faHeader)
library.add(faHistory)
library.add(faImage)
library.add(faKeyboard)
@ -146,6 +179,13 @@ library.add(faArrowUpFromBracket)
library.add(faX)
library.add(faAnglesUp)
library.add(faBolt)
library.add(faArrowTurnDown)
library.add(faListCheck)
library.add(faXmark)
library.add(faXmarksLines)
library.add(faFont)
library.add(faRulerHorizontal)
library.add(faUnderline)
// overwriting the wrong types
export default FontAwesomeIcon as unknown as FontAwesomeIconFixedTypes

View File

@ -195,10 +195,9 @@ $modal-width: 1024px;
}
.close {
$close-button-min-space: 84px;
$close-button-padding: 26px;
position: fixed;
top: 5px;
top: .5rem;
right: $close-button-padding;
color: var(--grey-900);
font-size: 2rem;
@ -213,6 +212,10 @@ $modal-width: 1024px;
@media screen and (min-width: calc(#{$desktop } + #{$close-button-min-space})) {
color: var(--white);
}
@media screen and (min-width: $tablet) and (max-width: #{$desktop + $close-button-min-space}) {
top: .75rem;
}
}
</style>

View File

@ -36,6 +36,14 @@
</span>
</div>
</div>
<x-button
v-if="notifications.length > 0 && unreadNotifications > 0"
@click="markAllRead"
variant="tertiary"
class="mt-2 is-fullwidth"
>
{{ $t('notification.markAllRead') }}
</x-button>
<p class="nothing" v-if="notifications.length === 0">
{{ $t('notification.none') }}<br/>
<span class="explainer">
@ -60,11 +68,15 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
import {getDisplayName} from '@/models/user'
import {useAuthStore} from '@/stores/auth'
import XButton from '@/components/input/button.vue'
import {success} from '@/message'
import {useI18n} from 'vue-i18n'
const LOAD_NOTIFICATIONS_INTERVAL = 10000
const authStore = useAuthStore()
const router = useRouter()
const {t} = useI18n()
const allNotifications = ref<INotification[]>([])
const showNotifications = ref(false)
@ -138,6 +150,12 @@ function to(n, index) {
allNotifications.value[index] = await notificationService.update(n)
}
}
async function markAllRead() {
const notificationService = new NotificationService()
await notificationService.markAllRead()
success({message: t('notification.markAllReadSuccess')})
}
</script>
<style lang="scss" scoped>

View File

@ -93,6 +93,7 @@ import {success} from '@/message'
import type {ITeam} from '@/modelTypes/ITeam'
import type {ITask} from '@/modelTypes/ITask'
import type {IProject} from '@/modelTypes/IProject'
import type {IAbstract} from '@/modelTypes/IAbstract'
const {t} = useI18n({useScope: 'global'})
const router = useRouter()
@ -103,7 +104,7 @@ const labelStore = useLabelStore()
const taskStore = useTaskStore()
const authStore = useAuthStore()
type DoAction<Type = any> = { type: ACTION_TYPE } & Type
type DoAction<Type> = { type: ACTION_TYPE } & Type
enum ACTION_TYPE {
CMD = 'cmd',
@ -190,7 +191,7 @@ const foundCommands = computed(() => availableCmds.value.filter((a) =>
interface Result {
type: ACTION_TYPE
title: string
items: DoAction<any>
items: DoAction<IAbstract>
}
const results = computed<Result[]>(() => {

View File

@ -178,7 +178,7 @@ async function addTask() {
return rel
})
await Promise.all(relations)
} catch (e: any) {
} catch (e) {
newTaskTitle.value = taskTitleBackup
if (e?.message === 'NO_PROJECT') {
errorMessage.value = t('project.create.addProjectRequired')

View File

@ -4,8 +4,6 @@ import BaseButton from '@/components/base/BaseButton.vue'
import User from '@/components/misc/user.vue'
import {computed} from 'vue'
type removeFunction = (item: any) => void
const {
assignees,
remove,
@ -14,7 +12,7 @@ const {
inline = false,
} = defineProps<{
assignees: IUser[],
remove?: removeFunction,
remove?: (user: IUser) => void,
disabled?: boolean,
avatarSize?: number,
inline?: boolean,

View File

@ -66,7 +66,6 @@
</CustomTransition>
</div>
<editor
:hasPreview="true"
:is-edit-enabled="canWrite && c.author.id === currentUserId"
:upload-callback="attachmentUpload"
:upload-enabled="true"
@ -83,6 +82,7 @@
}"
:bottom-actions="actions[c.id]"
:show-save="true"
initial-mode="preview"
/>
</div>
</div>
@ -114,12 +114,12 @@
taskCommentService.loading &&
!isCommentEdit,
}"
:hasPreview="false"
:upload-callback="attachmentUpload"
:upload-enabled="true"
:placeholder="$t('task.comment.placeholder')"
v-if="editorActive"
v-model="newComment.comment"
@save="addComment()"
/>
</div>
<div class="field">

View File

@ -18,21 +18,20 @@
</h3>
<editor
:is-edit-enabled="canWrite"
:upload-callback="attachmentUpload"
:upload-enabled="true"
:upload-callback="uploadCallback"
:placeholder="$t('task.description.placeholder')"
:empty-text="$t('task.description.empty')"
:show-save="true"
edit-shortcut="e"
v-model="task.description"
@update:model-value="saveWithDelay"
@save="save"
:initial-mode="task.description !== '' ? 'preview' : 'edit'"
/>
</div>
</template>
<script setup lang="ts">
import {ref, computed, watch, type PropType} from 'vue'
import {ref, computed, watch} from 'vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import Editor from '@/components/input/AsyncEditor'
@ -41,19 +40,17 @@ import type {ITask} from '@/modelTypes/ITask'
import {useTaskStore} from '@/stores/tasks'
import TaskModel from '@/models/task'
const props = defineProps({
modelValue: {
type: Object as PropType<ITask>,
required: true,
},
attachmentUpload: {
required: true,
},
canWrite: {
type: Boolean,
required: true,
},
})
type AttachmentUploadFunction = (file: File, onSuccess: (attachmentUrl: string) => void) => Promise<string>
const {
modelValue,
attachmentUpload,
canWrite,
} = defineProps<{
modelValue: ITask,
attachmentUpload: AttachmentUploadFunction,
canWrite: boolean,
}>()
const emit = defineEmits(['update:modelValue'])
@ -67,7 +64,7 @@ const taskStore = useTaskStore()
const loading = computed(() => taskStore.isLoading)
watch(
props.modelValue,
() => modelValue,
(value) => {
task.value = value
},
@ -106,5 +103,20 @@ async function save() {
saving.value = false
}
}
async function uploadCallback(files: File[] | FileList): (Promise<string[]>) {
const uploadPromises: Promise<string>[] = []
files.forEach((file: File) => {
const promise = new Promise<string>((resolve) => {
attachmentUpload(file, (uploadedFileUrl: string) => resolve(uploadedFileUrl))
})
uploadPromises.push(promise)
})
return await Promise.all(uploadPromises)
}
</script>

View File

@ -113,7 +113,7 @@ async function save(title: string) {
<style lang="scss" scoped>
.heading {
display: flex;
justify-content: space-between;
justify-content: flex-start;
text-transform: none;
align-items: center;
@ -134,6 +134,10 @@ async function save(title: string) {
@media screen and (max-width: $tablet) {
margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest
}
@media screen and (min-width: $tablet) and (max-width: #{$desktop + $close-button-min-space}) {
width: calc(100% - 6.5rem);
}
}
.title.task-id {

View File

@ -70,11 +70,11 @@ function findProjects(query: string) {
foundProjects.value = projectStore.searchProject(query)
}
function select(l: IProject | null) {
if (l === null) {
return
function select(p: IProject | null) {
if (p === null) {
Object.assign(project, {id: 0})
}
Object.assign(project, l)
Object.assign(project, p)
emit('update:modelValue', project)
}
</script>

View File

@ -2,6 +2,7 @@ import {computed, ref, watch, type Ref} from 'vue'
import {useRouter, type RouteLocationNormalized, type RouteLocationRaw, type RouteRecordName} from 'vue-router'
import equal from 'fast-deep-equal/es6'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Filters = Record<string, any>
export function useRouteFilters<CurrentFilters extends Filters>(

View File

@ -4,6 +4,7 @@ import {snakeCase} from 'snake-case'
/**
* Transforms field names to camel case.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function objectToCamelCase(object: Record<string, any>) {
// When calling recursively, this can be called without being and object or array in which case we just return the value
@ -11,6 +12,7 @@ export function objectToCamelCase(object: Record<string, any>) {
return object
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const parsedObject: Record<string, any> = {}
for (const m in object) {
parsedObject[camelCase(m)] = object[m]
@ -23,6 +25,7 @@ export function objectToCamelCase(object: Record<string, any>) {
// Call it again for arrays
if (Array.isArray(object[m])) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedObject[camelCase(m)] = object[m].map((o: Record<string, any>) => objectToCamelCase(o))
// Because typeof [] === 'object' is true for arrays, we leave the loop here to prevent converting arrays to objects.
continue
@ -39,6 +42,7 @@ export function objectToCamelCase(object: Record<string, any>) {
/**
* Transforms field names to snake case - used before making an api request.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function objectToSnakeCase(object: Record<string, any>) {
// When calling recursively, this can be called without being and object or array in which case we just return the value
@ -46,6 +50,7 @@ export function objectToSnakeCase(object: Record<string, any>) {
return object
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const parsedObject: Record<string, any> = {}
for (const m in object) {
parsedObject[snakeCase(m)] = object[m]
@ -61,6 +66,7 @@ export function objectToSnakeCase(object: Record<string, any>) {
// Call it again for arrays
if (Array.isArray(object[m])) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parsedObject[snakeCase(m)] = object[m].map((o: Record<string, any>) => objectToSnakeCase(o))
// Because typeof [] === 'object' is true for arrays, we leave the loop here to prevent converting arrays to objects.
continue

View File

@ -10,48 +10,56 @@ describe('Find checklists in text', () => {
expect(checkboxes).toHaveLength(0)
})
it('should find multiple checkboxes', () => {
const text: string = `* [ ] Lorem Ipsum
* [ ] Dolor sit amet
Here's some text in between
* [x] Dolor sit amet
- [ ] Dolor sit amet`
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Another task</p>
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>subtask</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"
checked="checked"><span></span></label>
<div><p>done</p></div>
</li>
</ul>
</div>
</li>
</ul>`
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(4)
expect(checkboxes[0]).toBe(0)
expect(checkboxes[1]).toBe(18)
expect(checkboxes[2]).toBe(69)
expect(checkboxes[3]).toBe(90)
expect(checkboxes[0]).toBe(32)
expect(checkboxes[1]).toBe(163)
expect(checkboxes[2]).toBe(321)
expect(checkboxes[3]).toBe(464)
})
it('should find one checkbox with *', () => {
const text: string = '* [ ] Lorem Ipsum'
it('should find one unchecked checkbox', () => {
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
expect(checkboxes[0]).toBe(32)
})
it('should find one checkbox with -', () => {
const text: string = '- [ ] Lorem Ipsum'
it('should find one checked checkbox', () => {
const text: string = `
<ul data-type="taskList">
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
})
it('should find one checked checkbox with *', () => {
const text: string = '* [x] Lorem Ipsum'
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
})
it('should find one checked checkbox with -', () => {
const text: string = '- [x] Lorem Ipsum'
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
expect(checkboxes[0]).toBe(32)
})
})
@ -63,32 +71,60 @@ describe('Get Checklist Statistics in a Text', () => {
expect(stats.total).toBe(0)
})
it('should find one checkbox', () => {
const text: string = '* [ ] Lorem Ipsum'
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const stats = getChecklistStatistics(text)
expect(stats.total).toBe(1)
expect(stats.checked).toBe(0)
})
it('should find one checked checkbox', () => {
const text: string = '* [x] Lorem Ipsum'
const text: string = `
<ul data-type="taskList">
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const stats = getChecklistStatistics(text)
expect(stats.total).toBe(1)
expect(stats.checked).toBe(1)
})
it('should find multiple mixed and matched', () => {
const text: string = `* [ ] Lorem Ipsum
* [ ] Dolor sit amet
* [x] Dolor sit amet
- [x] Dolor sit amet
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Another task</p>
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>subtask</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>subtask 2</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"
checked="checked"><span></span></label>
<div><p>done</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"
checked="checked"><span></span></label>
<div><p>also done</p></div>
</li>
</ul>
</div>
</li>
</ul>`
Here's some text in between
* [x] Dolor sit amet
- [ ] Dolor sit amet`
const stats = getChecklistStatistics(text)
expect(stats.total).toBe(6)
expect(stats.checked).toBe(3)
expect(stats.checked).toBe(2)
})
})

View File

@ -1,5 +1,3 @@
const checked = '[x]'
interface CheckboxStatistics {
total: number
checked: number
@ -11,7 +9,7 @@ interface MatchedCheckboxes {
}
const getCheckboxesInText = (text: string): MatchedCheckboxes => {
const regex = /[*-] \[[ x]]/g
const regex = /data-checked="(true|false)"/g
let match
const checkboxes: MatchedCheckboxes = {
checked: [],
@ -19,7 +17,7 @@ const getCheckboxesInText = (text: string): MatchedCheckboxes => {
}
while ((match = regex.exec(text)) !== null) {
if (match[0].endsWith(checked)) {
if (match[1] === 'true') {
checkboxes.checked.push(match.index)
} else {
checkboxes.unchecked.push(match.index)

View File

@ -1,45 +0,0 @@
import {marked} from 'marked'
import hljs from 'highlight.js/lib/common'
export function setupMarkdownRenderer(checkboxId: string) {
const renderer = new marked.Renderer()
const linkRenderer = renderer.link
let checkboxNum = -1
marked.use({
renderer: {
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.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: boolean) {
let checkedString = ''
if (checked) {
checkedString = 'checked'
}
checkboxNum++
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checkedString} class="text-checkbox-${checkboxId}"/>`
},
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(code: string, language: string) {
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext'
return hljs.highlight(code, {language: validLanguage}).value
},
})
return renderer
}

View File

@ -6,7 +6,11 @@ export function findById<T extends {id: string | number}>(array : T[], id : stri
return array.find(({id: currentId}) => currentId === id)
}
export function includesById(array: any[], id: string | number) {
interface ObjectWithId {
id: number
}
export function includesById(array: ObjectWithId[], id: string | number) {
return array.some(({id: currentId}) => currentId === id)
}

View File

@ -34,6 +34,7 @@ export const i18n = createI18n({
legacy: false,
messages: {
[DEFAULT_LANGUAGE]: langEN,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as Record<SupportedLocale, any>,
})

View File

@ -258,7 +258,7 @@
"identifier": "مُعرّف المشروع",
"identifierPlaceholder": "معرف المشروع هنا…",
"description": "الوصف",
"descriptionPlaceholder": "وصف المشاريع هنا…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "اللون",
"success": "تم تحديث المشروع بنجاح."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "المُفضلات"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "العنوان",
"titlePlaceholder": "عنوان الفلتر المحفوظ هنا…",
"description": "الوصف",
"descriptionPlaceholder": "الوصف هنا…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "تضمين المهام التي لا تحتوي على مجموعة من القيم",
"requireAll": "يتطلب أن تكون جميع الفلاتر صحيحة لظهور المهمة",
"showDoneTasks": "إظهار المهام المكتملة",
@ -480,6 +495,7 @@
"custom": "تخصيص",
"id": "الرقم التعريفي",
"created": "تاريخ الإنشاء",
"createdBy": "Created by {0}",
"actions": "الإجراءات",
"cannotBeUndone": "لا يمكن التراجع عن هذا الإجراء!"
},
@ -498,24 +514,59 @@
"edit": "تعديل",
"done": "أكتمل",
"heading1": "العنوان 1",
"heading1Tooltip": "Big section heading.",
"heading2": "العنوان 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "العنوان 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "العنوان أصغر",
"headingBigger": "العنوان أكبر",
"bold": "عريض",
"italic": "مائل",
"strikethrough": "يتوسطه خط",
"underline": "Underline",
"code": "تعليمات برمجية",
"codeTooltip": "Capture a code snippet.",
"quote": "اقتباس",
"unorderedList": "قائمة غير مرتبة",
"orderedList": "قائمة مرتبة",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "محتوى منظّم",
"link": "رابط",
"image": "صورة",
"table": "جدول",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "مسطرة أفقية",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "جنباً إلى جنب",
"guide": "دليل الاستخدام"
"guide": "دليل الاستخدام",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "إنشاء جديد",
@ -689,7 +740,7 @@
"loading": "تحميل التعليقات…",
"edited": "تم التعديل في {date}",
"creating": "إنشاء تعليق…",
"placeholder": "أضف تعليقك…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "التعليق",
"delete": "حذف هذا التعليق",
"deleteText1": "هل أنت متأكد من رغبتك بحذف هذا التعليق؟",
@ -703,7 +754,7 @@
"1week": "أسبوع"
},
"description": {
"placeholder": "انقر هنا لإدخال الوصف…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "لا يوجد وصف متاح بعد."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "اسم الفريق هنا…",
"nameRequired": "الرجاء تحديد الاسم.",
"description": "الوصف",
"descriptionPlaceholder": "وصف الفرق هنا…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "مشرف",
"member": "عضو"
}
@ -933,7 +984,9 @@
"notification": {
"title": "الإشعارات",
"none": "ليس لديك أي إشعارات. نتمنى لك يوماً سعيداً!",
"explainer": "ستظهر الإشعارات هنا عند تنفيذ الإجراءات أو المهام التي اشتركت فيها."
"explainer": "ستظهر الإشعارات هنا عند تنفيذ الإجراءات أو المهام التي اشتركت فيها.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "اﻷوامر",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Název",
"titlePlaceholder": "Název uloženého filtru přijde sem…",
"description": "Popis",
"descriptionPlaceholder": "Popis přijde sem…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Zahrnout úkoly, které nemají nastavenou hodnotu",
"requireAll": "Vyžaduje aby všechny filtry odpovídaly, aby se úkol zobrazil",
"showDoneTasks": "Zobrazit dokončené úkoly",
@ -480,6 +495,7 @@
"custom": "Vlastní",
"id": "ID",
"created": "Vytvořeno",
"createdBy": "Created by {0}",
"actions": "Akce",
"cannotBeUndone": "Toto nelze vrátit!"
},
@ -498,24 +514,59 @@
"edit": "Upravit",
"done": "Hotovo",
"heading1": "Nadpis 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Nadpis 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Nadpis 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Menší nadpis",
"headingBigger": "Větší nadpis",
"bold": "Tučné",
"italic": "Skloněné",
"strikethrough": "Přeškrtnuté",
"underline": "Underline",
"code": "Kód",
"codeTooltip": "Capture a code snippet.",
"quote": "Citace",
"unorderedList": "Seznam s odrážkami",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Čistý blok",
"link": "Odkaz",
"image": "Obrázek",
"table": "Tabulka",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Vodorovná čára",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Vedle sebe",
"guide": "Průvodce"
"guide": "Průvodce",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Vytvořit nový",
@ -689,7 +740,7 @@
"loading": "Načítám komentáře…",
"edited": "upraveno {date}",
"creating": "Vytvářím komentář…",
"placeholder": "Přidejte svůj komentář…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Komentář",
"delete": "Smazat tento komentář",
"deleteText1": "Opravdu chcete smazat tento komentář?",
@ -703,7 +754,7 @@
"1week": "1 týden"
},
"description": {
"placeholder": "Kliknutím sem zadejte popis…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Ještě není k dispozici žádný popis."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Název týmu přijde sem…",
"nameRequired": "Zadejte název.",
"description": "Popis",
"descriptionPlaceholder": "Popis týmu přijde sem…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Administrátor",
"member": "Člen"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Oznámení",
"none": "Nemáte žádná oznámení. Mějte příjemný den!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Příkazy",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Titel",
"titlePlaceholder": "Det gemte filters titel skrives her…",
"description": "Beskrivelse",
"descriptionPlaceholder": "Beskrivelsen skrives her…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Inkluder Opgaver som ikke har en værdi indstillet",
"requireAll": "Kræv at alle filtre er sande for at en opgave kan vises",
"showDoneTasks": "Vis Udførte Opgaver",
@ -480,6 +495,7 @@
"custom": "Brugerdefineret",
"id": "ID",
"created": "Oprettet den",
"createdBy": "Created by {0}",
"actions": "Handlinger",
"cannotBeUndone": "Dette kan ikke fortrydes!"
},
@ -498,24 +514,59 @@
"edit": "Rediger",
"done": "Udfør",
"heading1": "Overskrift 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Overskrift 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Overskrift 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Overskrift Mindre",
"headingBigger": "Overskrift Større",
"bold": "Fed",
"italic": "Kursiv",
"strikethrough": "Gennemstreget",
"underline": "Underline",
"code": "Kode",
"codeTooltip": "Capture a code snippet.",
"quote": "Citat",
"unorderedList": "Usorteret liste",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Ryd Blok",
"link": "Link",
"image": "Billede",
"table": "Tabel",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horisontal Linje",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side Om Side",
"guide": "Vejledning"
"guide": "Vejledning",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Opret ny",
@ -689,7 +740,7 @@
"loading": "Indlæser kommentarer…",
"edited": "redigeret {date}",
"creating": "Opretter kommentar…",
"placeholder": "Tilføj din kommentar…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Kommentar",
"delete": "Slet denne kommentar",
"deleteText1": "Er du sikker på du vil slette denne kommentar?",
@ -703,7 +754,7 @@
"1week": "1 uge"
},
"description": {
"placeholder": "Klik her for at indtaste en beskrivelse…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Ingen beskrivelse tilgængelig endnu."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Holdnavnet skrives her…",
"nameRequired": "Angiv venligst et navn.",
"description": "Beskrivelse",
"descriptionPlaceholder": "Holdets beskrivelse skrives her…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Administrator",
"member": "Medlem"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifikationer",
"none": "Du har ingen notifikationer. Hav en dejlig dag!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Kommandoer",

View File

@ -258,7 +258,7 @@
"identifier": "Projektbezeichner",
"identifierPlaceholder": "Der Projektbezeichner kommt hierhin…",
"description": "Beschreibung",
"descriptionPlaceholder": "Projektbeschreibung eingeben…",
"descriptionPlaceholder": "Gib eine Beschreibung für dieses Projekt ein, drücke '/' für mehr Optionen…",
"color": "Farbe",
"success": "Das Projekt wurde erfolgreich aktualisiert."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoriten"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Ziel-URL",
"targetUrlInvalid": "Bitte gib eine gültige URL an.",
"events": "Ereignisse",
"eventsHint": "Wähle alle Ereignisse aus, für die dieser Webhook Updates erhalten soll (innerhalb des aktuellen Projekts).",
"mustSelectEvents": "Du musst mindestens ein Ereignis auswählen.",
"delete": "Diesen Webhook löschen",
"deleteText": "Bist du sicher, dass du diesen Webhook löschen möchtest? Externe Ziele werden nicht mehr über Ereignisse benachrichtigt.",
"deleteSuccess": "Der Webhook wurde erfolgreich gelöscht.",
"create": "Webhook erstellen",
"secret": "Schlüssel",
"secretHint": "Wenn angegeben, werden alle Anfragen an die Webhook Ziel-URL mit HMAC signiert.",
"secretDocs": "In der Dokumentation findest du weitere Informationen zum Umgang mit Schlüsseln."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Titel",
"titlePlaceholder": "Einen gespeicherten Filternamen eingeben …",
"description": "Beschreibung",
"descriptionPlaceholder": "Eine Beschreibung eingeben …",
"descriptionPlaceholder": "Gib eine Beschreibung für diesen Filter ein, drücke '/' für mehr Optionen…",
"includeNulls": "Aufgaben ohne Werte einbeziehen",
"requireAll": "Alle Filterkriterien müssen erfüllt sein, damit eine Aufgabe angezeigt wird",
"showDoneTasks": "Erledigte Aufgaben anzeigen",
@ -480,6 +495,7 @@
"custom": "Benutzerdefiniert",
"id": "ID",
"created": "Erstellt am",
"createdBy": "Erstellt von {0}",
"actions": "Aktionen",
"cannotBeUndone": "Dies kann nicht rückgängig gemacht werden!"
},
@ -498,24 +514,59 @@
"edit": "Bearbeiten",
"done": "Fertig",
"heading1": "Überschrift 1",
"heading1Tooltip": "Große Überschrift.",
"heading2": "Überschrift 2",
"heading2Tooltip": "Mittlere Überschrift.",
"heading3": "Überschrift 3",
"heading3Tooltip": "Kleine Überschrift.",
"headingSmaller": "Kleinere Überschrift",
"headingBigger": "Grössere Überschrift",
"bold": "Fett",
"italic": "Kursiv",
"strikethrough": "Durchgestrichen",
"underline": "Unterstrichen",
"code": "Code",
"codeTooltip": "Erfasse ein Code-Snippet.",
"quote": "Zitat",
"quoteTooltip": "Erfasse ein Zitat.",
"bulletList": "Stichpunktliste",
"bulletListTooltip": "Erstelle eine einfache Stichpunktliste.",
"unorderedList": "Ungeordnete Liste",
"orderedList": "Geordnete Liste",
"orderedListTooltip": "Erstelle eine Liste mit Nummerierung.",
"cleanBlock": "Formatierung löschen",
"link": "Link",
"image": "Bild",
"table": "Tabelle",
"imageTooltip": "Lade ein Bild von deinem PC hoch.",
"table": {
"title": "Tabelle",
"insert": "Tabelle einfügen",
"addColumnBefore": "Spalte davor hinzufügen",
"addColumnAfter": "Spalte danach hinzufügen",
"deleteColumn": "Spalte löschen",
"addRowBefore": "Zeile davor hinzufügen",
"addRowAfter": "Zeile danach hinzufügen",
"deleteRow": "Zeile löschen",
"deleteTable": "Tabelle löschen",
"mergeCells": "Zellen verbinden",
"splitCell": "Zelle teilen",
"toggleHeaderColumn": "Headerspalte ein/aus",
"toggleHeaderRow": "Headerzeile ein/aus",
"toggleHeaderCell": "Headerzelle ein/aus",
"mergeOrSplit": "Verbinden oder teilen",
"fixTables": "Tabellen reparieren"
},
"horizontalRule": "Horizontaler Strich",
"horizontalRuleTooltip": "Teile einen Bereich.",
"sideBySide": "Nebeneinander",
"guide": "Hilfslinie"
"guide": "Hilfslinie",
"text": "Text",
"textTooltip": "Einfach einen Text tippen.",
"taskList": "Aufgabenliste",
"taskListTooltip": "Aufgaben mit einer To-do-Liste verfolgen.",
"undo": "Rückgängig",
"redo": "Wiederholen",
"placeholder": "Gib Text ein, drücke '/' für mehr Optionen…"
},
"multiselect": {
"createPlaceholder": "Neu erstellen",
@ -689,7 +740,7 @@
"loading": "Kommentare werden geladen …",
"edited": "bearbeitet {date}",
"creating": "Kommentar wird erstellt …",
"placeholder": "Füge deinen Kommentar hinzu …",
"placeholder": "Füge deinen Kommentar hinzu, drücke '/' für weitere Optionen…",
"comment": "Kommentieren",
"delete": "Diesen Kommentar löschen",
"deleteText1": "Bist du sicher, dass du diesen Kommentar löschen willst?",
@ -703,7 +754,7 @@
"1week": "1 Woche"
},
"description": {
"placeholder": "Klicke hier, um eine Beschreibung einzugeben …",
"placeholder": "Gib eine Beschreibung ein, drücke '/' für mehr Optionen…",
"empty": "Noch keine Beschreibung vorhanden."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Teamname eingeben …",
"nameRequired": "Bitte gib einen Namen an.",
"description": "Beschreibung",
"descriptionPlaceholder": "Die Beschreibung des Teams steht hier …",
"descriptionPlaceholder": "Gib eine Beschreibung für dieses Team ein, drücke '/' für mehr Optionen…",
"admin": "Admin",
"member": "Mitglied"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Benachrichtigungen",
"none": "Du hast keine Benachrichtigungen. Einen schönen Tag noch!",
"explainer": "Benachrichtigungen werden hier angezeigt, wenn Aktionen für Projekte oder Aufgaben, die du abonniert hast, ausgeführt werden."
"explainer": "Benachrichtigungen werden hier angezeigt, wenn Aktionen für Projekte oder Aufgaben, die du abonniert hast, ausgeführt werden.",
"markAllRead": "Alle Benachrichtigungen als gelesen markieren",
"markAllReadSuccess": "Alle Benachrichtigungen erfolgreich als gelesen markiert."
},
"quickActions": {
"commands": "Befehle",

View File

@ -258,7 +258,7 @@
"identifier": "Projektbezeichner",
"identifierPlaceholder": "Der Projektbezeichner kommt hierhin…",
"description": "Beschreibung",
"descriptionPlaceholder": "Projektbeschreibung eingeben…",
"descriptionPlaceholder": "Gib eine Beschreibung für dieses Projekt ein, drücke '/' für mehr Optionen…",
"color": "Farbe",
"success": "Das Projekt wurde erfolgreich aktualisiert."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoriten"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Ziel-URL",
"targetUrlInvalid": "Bitte gib eine gültige URL an.",
"events": "Ereignisse",
"eventsHint": "Wähle alle Ereignisse aus, für die dieser Webhook Updates erhalten soll (innerhalb des aktuellen Projekts).",
"mustSelectEvents": "Du musst mindestens ein Ereignis auswählen.",
"delete": "Diesen Webhook löschen",
"deleteText": "Bist du sicher, dass du diesen Webhook löschen möchtest? Externe Ziele werden nicht mehr über Ereignisse benachrichtigt.",
"deleteSuccess": "Der Webhook wurde erfolgreich gelöscht.",
"create": "Webhook erstellen",
"secret": "Schlüssel",
"secretHint": "Wenn angegeben, werden alle Anfragen an die Webhook Ziel-URL mit HMAC signiert.",
"secretDocs": "In der Dokumentation findest du weitere Informationen zum Umgang mit Schlüsseln."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Titl",
"titlePlaceholder": "De Name für de g'speicheret Filter chunt da ahne…",
"description": "Beschriibig",
"descriptionPlaceholder": "D'Beschriibig chunt da hane…",
"descriptionPlaceholder": "Gib eine Beschreibung für diesen Filter ein, drücke '/' für mehr Optionen…",
"includeNulls": "Uufgabe ohni Wert iihbezieh",
"requireAll": "Alli Filter mend wahr sii, demits die Uufgab ahzeigt",
"showDoneTasks": "Zeig die fertige Uufgabe",
@ -480,6 +495,7 @@
"custom": "Benutzerdefiniert",
"id": "ID",
"created": "Erstellt am",
"createdBy": "Erstellt von {0}",
"actions": "Aktionen",
"cannotBeUndone": "Dies kann nicht rückgängig gemacht werden!"
},
@ -498,24 +514,59 @@
"edit": "Bearbeitä",
"done": "Fertig",
"heading1": "Überschrift 1",
"heading1Tooltip": "Große Überschrift.",
"heading2": "Überschrift 2",
"heading2Tooltip": "Mittlere Überschrift.",
"heading3": "Überschrift 3",
"heading3Tooltip": "Kleine Überschrift.",
"headingSmaller": "Chliini Überschrift",
"headingBigger": "Grösseri Überschrift",
"bold": "Fett",
"italic": "Kursiiv",
"strikethrough": "Duregstriche",
"underline": "Unterstrichen",
"code": "Code",
"codeTooltip": "Erfasse ein Code-Snippet.",
"quote": "Zitaat",
"unorderedList": "Ungordnedi Listä",
"quoteTooltip": "Erfasse ein Zitat.",
"bulletList": "Stichpunktliste",
"bulletListTooltip": "Erstelle eine einfache Stichpunktliste.",
"unorderedList": "Ungeordnete Liste",
"orderedList": "Geordnete Liste",
"orderedListTooltip": "Erstelle eine Liste mit Nummerierung.",
"cleanBlock": "Formatierig Lösche",
"link": "Link",
"image": "Bild",
"table": "Tabällä",
"imageTooltip": "Lade ein Bild von deinem PC hoch.",
"table": {
"title": "Tabelle",
"insert": "Tabelle einfügen",
"addColumnBefore": "Spalte davor hinzufügen",
"addColumnAfter": "Spalte danach hinzufügen",
"deleteColumn": "Spalte löschen",
"addRowBefore": "Zeile davor hinzufügen",
"addRowAfter": "Zeile danach hinzufügen",
"deleteRow": "Zeile löschen",
"deleteTable": "Tabelle löschen",
"mergeCells": "Zellen verbinden",
"splitCell": "Zelle teilen",
"toggleHeaderColumn": "Headerspalte ein/aus",
"toggleHeaderRow": "Headerzeile ein/aus",
"toggleHeaderCell": "Headerzelle ein/aus",
"mergeOrSplit": "Verbinden oder teilen",
"fixTables": "Tabellen reparieren"
},
"horizontalRule": "Horizontalä Strich",
"horizontalRuleTooltip": "Teile einen Bereich.",
"sideBySide": "Nebedenand",
"guide": "Hilfsliniä"
"guide": "Hilfsliniä",
"text": "Text",
"textTooltip": "Einfach einen Text tippen.",
"taskList": "Aufgabenliste",
"taskListTooltip": "Aufgaben mit einer To-do-Liste verfolgen.",
"undo": "Rückgängig",
"redo": "Wiederholen",
"placeholder": "Gib Text ein, drücke '/' für mehr Optionen…"
},
"multiselect": {
"createPlaceholder": "Neu erstelle",
@ -689,7 +740,7 @@
"loading": "Kommentär werded gladä…",
"edited": "beartbeitet am {date}",
"creating": "Kommentar wird erstellt…",
"placeholder": "Din Kommentar wird hinzuegfüegt…",
"placeholder": "Füge deinen Kommentar hinzu, drücke '/' für weitere Optionen…",
"comment": "Kommentar",
"delete": "De Kommentar chüble",
"deleteText1": "Bisch du dir sicher, dass du de Kommentar chüble wetsch?",
@ -703,7 +754,7 @@
"1week": "Ei Wuche"
},
"description": {
"placeholder": "Klicke do, um e Beschriibig iihzfüege…",
"placeholder": "Gib eine Beschreibung ein, drücke '/' für mehr Optionen…",
"empty": "Momentan hets kei Beschriibig."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Teamname da ahgeh…",
"nameRequired": "Bitte gib en Name an.",
"description": "Beschriibig",
"descriptionPlaceholder": "D'Team Beschriibig chunt da ahne…",
"descriptionPlaceholder": "Gib eine Beschreibung für dieses Team ein, drücke '/' für mehr Optionen…",
"admin": "Chef",
"member": "Mitglied"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Benachrichtigunge",
"none": "Du hesch kei neui Benachrichtunge. Heb e schös Tägli!",
"explainer": "Benachrichtigungen werden hier angezeigt, wenn Aktionen für Projekte oder Aufgaben, die du abonniert hast, ausgeführt werden."
"explainer": "Benachrichtigungen werden hier angezeigt, wenn Aktionen für Projekte oder Aufgaben, die du abonniert hast, ausgeführt werden.",
"markAllRead": "Alle Benachrichtigungen als gelesen markieren",
"markAllReadSuccess": "Alle Benachrichtigungen erfolgreich als gelesen markiert."
},
"quickActions": {
"commands": "Befehl",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -383,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -514,24 +514,60 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"imageTooltip": "Upload an image from your computer.",
"table": "Table",
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
}
},
"multiselect": {
"createPlaceholder": "Create new",
@ -708,7 +744,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -722,7 +758,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -873,7 +909,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -952,7 +988,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Identificador del proyecto",
"identifierPlaceholder": "El identificador del proyecto va aquí…",
"description": "Descripción",
"descriptionPlaceholder": "La descripción del proyecto va aquí…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "El proyecto se ha actualizado con éxito."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoritos"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Título",
"titlePlaceholder": "El título del filtro guardado va aquí…",
"description": "Descripción",
"descriptionPlaceholder": "La descripción va aquí…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Incluye tareas que no tienen un valor establecido",
"requireAll": "Requerir que todos los filtros sean verdaderos para que una tarea se muestre",
"showDoneTasks": "Mostrar tareas completadas",
@ -480,6 +495,7 @@
"custom": "Personalizado",
"id": "ID",
"created": "Creado en",
"createdBy": "Created by {0}",
"actions": "Acciones",
"cannotBeUndone": "¡No se puede deshacer!"
},
@ -498,24 +514,59 @@
"edit": "Editar",
"done": "Hecho",
"heading1": "Encabezado 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Encabezado 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Encabezado 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Reducir Encabezado",
"headingBigger": "Aumentar Encabezado",
"bold": "Negrita",
"italic": "Cursiva",
"strikethrough": "Tachado",
"underline": "Underline",
"code": "Código",
"codeTooltip": "Capture a code snippet.",
"quote": "Cita",
"unorderedList": "Lista no ordenada",
"orderedList": "Lista ordenada",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Borrar Bloque",
"link": "Enlace",
"image": "Imagen",
"table": "Tabla",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Regla Horizontal",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "De Lado a Lado",
"guide": "Guía"
"guide": "Guía",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Crear nuevo",
@ -689,7 +740,7 @@
"loading": "Cargando comentarios…",
"edited": "editado {date}",
"creating": "Creando comentario…",
"placeholder": "Añade tu comentario…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comentar",
"delete": "Eliminar este comentario",
"deleteText1": "¿Está seguro que desea eliminar este comentario?",
@ -703,7 +754,7 @@
"1week": "1 semana"
},
"description": {
"placeholder": "Presione aquí para añadir una descripción…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Aún no hay descripción disponible."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "El nombre del equipo va aquí…",
"nameRequired": "Por favor especifica un nombre.",
"description": "Descripción",
"descriptionPlaceholder": "La descripción del equipo va aquí…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Miembro"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notificaciones",
"none": "No tienes notificaciones. ¡Que tengas un buen día!",
"explainer": "Las notificaciones aparecerán aquí cuando se realicen acciones en proyectos o tareas a las que te hayas suscrito."
"explainer": "Las notificaciones aparecerán aquí cuando se realicen acciones en proyectos o tareas a las que te hayas suscrito.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Comandos",

View File

@ -258,7 +258,7 @@
"identifier": "Identifiant de projet",
"identifierPlaceholder": "Saisir ici lidentifiant du projet…",
"description": "Description",
"descriptionPlaceholder": "Saisir ici la description du projet…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Couleur",
"success": "Le projet a bien été mis à jour."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoris"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Nom",
"titlePlaceholder": "Entre un nom de filtre enregistré…",
"description": "Description",
"descriptionPlaceholder": "Écris une description…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Inclure les tâches sans valeurs",
"requireAll": "Exiger tous les filtres pour quune tâche saffiche",
"showDoneTasks": "Afficher les tâches terminées",
@ -480,6 +495,7 @@
"custom": "Personnaliser",
"id": "Identifiant",
"created": "Créé à",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "Cette action ne peut pas être annulée !"
},
@ -498,24 +514,59 @@
"edit": "Modifier",
"done": "Terminé",
"heading1": "En-tête 1",
"heading1Tooltip": "Big section heading.",
"heading2": "En-tête 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "En-tête 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "En-tête plus petit",
"headingBigger": "En-tête plus grand",
"bold": "Gras",
"italic": "Italique",
"strikethrough": "Barré",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Citation",
"unorderedList": "Liste non ordonnée",
"orderedList": "Liste ordonnée",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Nettoyer le code",
"link": "Lien",
"image": "Image",
"table": "Tableau",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Règle horizontale",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Côte à côte",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Créer un nouveau",
@ -689,7 +740,7 @@
"loading": "Chargement des commentaires…",
"edited": "modifié {date}",
"creating": "Création dun commentaire…",
"placeholder": "Ajouter votre commentaire…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Commentaire",
"delete": "Supprimer ce commentaire",
"deleteText1": "Supprimer ce commentaire ?",
@ -703,7 +754,7 @@
"1week": "1 semaine"
},
"description": {
"placeholder": "Cliquez ici pour entrer une description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Aucune description nest encore disponible."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Saisir le nom de léquipe…",
"nameRequired": "Indiquer un nom.",
"description": "Description",
"descriptionPlaceholder": "Saisir la description des équipes…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Membre"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "Vous navez pas de notification. Passez une bonne journée !",
"explainer": "Les notifications apparaîtront ici lorsque des actions auxquelles vous êtes abonné·e se produisent."
"explainer": "Les notifications apparaîtront ici lorsque des actions auxquelles vous êtes abonné·e se produisent.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commandes",

View File

@ -258,7 +258,7 @@
"identifier": "Projektazonosító",
"identifierPlaceholder": "Írja be a projekt projektazonosítót...",
"description": "Leírás",
"descriptionPlaceholder": "A leírás ide kerül…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Szín",
"success": "A projekt sikeresen frissítve."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Kedvencek"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Cím",
"titlePlaceholder": "A mentett szűrő címe ide kerül…",
"description": "Leírás",
"descriptionPlaceholder": "A leírás ide kerül…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Tartalmazza az olyan feladatokat, amelyeknek nincs beállított értéke",
"requireAll": "A feladat megjelenítéséhez minden szűrőnek igaznak kell lennie",
"showDoneTasks": "Elkészült feladatok megjelenítése",
@ -480,6 +495,7 @@
"custom": "Egyéni",
"id": "Azonosító",
"created": "Létrehozva ekkor:",
"createdBy": "Created by {0}",
"actions": "Műveletek",
"cannotBeUndone": "Ezt nem lehet visszavonni!"
},
@ -498,24 +514,59 @@
"edit": "Szerkesztés",
"done": "Befejezve",
"heading1": "Címsor 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Címsor 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Címsor 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Kisebb címsor",
"headingBigger": "Nagyobb címsor",
"bold": "Félkövér",
"italic": "Dőlt",
"strikethrough": "Áthúzott",
"underline": "Underline",
"code": "Kód",
"codeTooltip": "Capture a code snippet.",
"quote": "Idézet",
"unorderedList": "Rendezetlen lista",
"orderedList": "Rendezett lista",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Blokk kitisztítása",
"link": "Hivatkozás",
"image": "Kép",
"table": "Táblázat",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Vízszintes vonal",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Egymás mellett",
"guide": "Útmutató"
"guide": "Útmutató",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Új létrehozása",
@ -689,7 +740,7 @@
"loading": "Hozzászólások betöltése…",
"edited": "Szerkesztve: {date}",
"creating": "Hozzászólás létrehozása…",
"placeholder": "Szólj hozzá…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Hozzászólás",
"delete": "Hozzászólás törlése",
"deleteText1": "Biztos benne, hogy törölni akarja ezt a hozzászólást?",
@ -703,7 +754,7 @@
"1week": "1 hét"
},
"description": {
"placeholder": "Kattintson ide a leírás megadásához…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Nem érhető el leírás."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "A csapat nevét ide írja…",
"nameRequired": "Kérjük, adjon meg egy nevet.",
"description": "Leírás",
"descriptionPlaceholder": "A csapat leírása ide kerül…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Adminisztrátor",
"member": "Tag"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Értesítések",
"none": "Nincsenek értesítései. Legyen szép napja!",
"explainer": "Az értesítések itt jelennek meg, amikor olyan projektek vagy feladatok történnek, amelyekre feliratkozott."
"explainer": "Az értesítések itt jelennek meg, amikor olyan projektek vagy feladatok történnek, amelyekre feliratkozott.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Parancsok",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Descrizione",
"descriptionPlaceholder": "Descrizione del progetto…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Colore",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Titolo",
"titlePlaceholder": "Il titolo del filtro salvato va qui…",
"description": "Descrizione",
"descriptionPlaceholder": "La descrizione va qui…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Includi attività che non hanno un valore impostato",
"requireAll": "Tutti i filtri devono essere veri affinché l'attività venga mostrata",
"showDoneTasks": "Mostra Attività Fatte",
@ -480,6 +495,7 @@
"custom": "Personalizzato",
"id": "ID",
"created": "Creato il",
"createdBy": "Created by {0}",
"actions": "Azioni",
"cannotBeUndone": "Questa azione non può essere annullata!"
},
@ -498,24 +514,59 @@
"edit": "Modifica",
"done": "Fatto",
"heading1": "Intestazione 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Intestazione 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Intestazione 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Intestazione Più Piccola",
"headingBigger": "Intestazione Più Grande",
"bold": "Grassetto",
"italic": "Corsivo",
"strikethrough": "Barrato",
"underline": "Underline",
"code": "Codice",
"codeTooltip": "Capture a code snippet.",
"quote": "Citazione",
"unorderedList": "Elenco puntato",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Pulisci Blocco",
"link": "Link",
"image": "Immagine",
"table": "Tabella",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Divisore Orizzontale",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Affianca",
"guide": "Guida"
"guide": "Guida",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Crea nuovo",
@ -689,7 +740,7 @@
"loading": "Caricamento commenti…",
"edited": "modificato il {date}",
"creating": "Creazione del commento…",
"placeholder": "Aggiungi un commento…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Commenta",
"delete": "Elimina questo commento",
"deleteText1": "Sei sicuro di voler eliminare questo commento?",
@ -703,7 +754,7 @@
"1week": "1 settimana"
},
"description": {
"placeholder": "Clicca qui per inserire una descrizione…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Nessuna descrizione."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Il nome del gruppo va qui…",
"nameRequired": "Specifica un nome.",
"description": "Descrizione",
"descriptionPlaceholder": "La descrizione del gruppo va qui…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Amministratore",
"member": "Membro"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifiche",
"none": "Nessuna notifica. Buona giornata!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Comandi",

View File

@ -258,7 +258,7 @@
"identifier": "プロジェクトID",
"identifierPlaceholder": "プロジェクトIDを入力…",
"description": "説明",
"descriptionPlaceholder": "プロジェクトの説明を入力…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "色",
"success": "プロジェクトは正常に更新されました。"
},
@ -359,6 +359,21 @@
"favorites": {
"title": "お気に入り"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "絞り込み条件名",
"titlePlaceholder": "絞り込み条件名を入力…",
"description": "説明",
"descriptionPlaceholder": "絞り込み条件の説明を入力…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "値が設定されていないタスクを含める",
"requireAll": "すべての条件に一致するタスクのみ表示",
"showDoneTasks": "完了したタスクを表示",
@ -480,6 +495,7 @@
"custom": "カスタム",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "この操作は元に戻せません!"
},
@ -498,24 +514,59 @@
"edit": "編集",
"done": "完了",
"heading1": "見出し1",
"heading1Tooltip": "Big section heading.",
"heading2": "見出し2",
"heading2Tooltip": "Medium section heading.",
"heading3": "見出し3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "見出しを小さくする",
"headingBigger": "見出しを大きくする",
"bold": "太字",
"italic": "斜体",
"strikethrough": "打ち消し線",
"underline": "Underline",
"code": "コード",
"codeTooltip": "Capture a code snippet.",
"quote": "引用",
"unorderedList": "順序なしリスト",
"orderedList": "順序付きリスト",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "リンク",
"image": "画像",
"table": "テーブル",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "横罫",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "説明書"
"guide": "説明書",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "新規作成",
@ -689,7 +740,7 @@
"loading": "コメントを読み込み中…",
"edited": "edited {date}",
"creating": "コメントを作成中…",
"placeholder": "コメントを追加…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "コメント",
"delete": "コメントの削除",
"deleteText1": "このコメントを削除して本当によろしいですか?",
@ -703,7 +754,7 @@
"1week": "1週間後"
},
"description": {
"placeholder": "説明を入力…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "説明文はありません。"
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "説明",
"descriptionPlaceholder": "チームの説明を入力…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "管理者",
"member": "メンバー"
}
@ -933,7 +984,9 @@
"notification": {
"title": "通知",
"none": "通知はありません。それではよい一日を!",
"explainer": "購読しているプロジェクトやタスクが変更されると通知されます。"
"explainer": "購読しているプロジェクトやタスクが変更されると通知されます。",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "コマンド",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "설명",
"descriptionPlaceholder": "프로젝트 상세설명은 여기에…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "색상",
"success": "프로젝트가 성공적으로 업데이트되었습니다."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "즐겨찾기"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "제목",
"titlePlaceholder": "The saved filter title goes here…",
"description": "상세정보",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "완료한 목록 표시",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "굵게",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "코드",
"codeTooltip": "Capture a code snippet.",
"quote": "인용",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "블록 지우기",
"link": "링크",
"image": "이미지",
"table": "표",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "가이드"
"guide": "가이드",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "새로 생성하기",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "{date} 에 수정함",
"creating": "의견 작성 중...",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "의견",
"delete": "의견 삭제",
"deleteText1": "의견을 삭제하시겠습니까?",
@ -703,7 +754,7 @@
"1week": "1 주"
},
"description": {
"placeholder": "설명을 입력하려면 클릭하십시오...",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "설명이 없습니다."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Titel",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Beschrijving",
"descriptionPlaceholder": "De beschrijving komt hier…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Bewerken",
"done": "Voltooid",
"heading1": "Kop 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Kop 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Kop 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Dikgedrukt",
"italic": "Cursief",
"strikethrough": "Doorgestreept",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Citaat",
"unorderedList": "Ongesorteerde lijst",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Afbeelding",
"table": "Tabel",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontale lijn",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Naast elkaar",
"guide": "Handleiding"
"guide": "Handleiding",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Nieuwe aanmaken",
@ -689,7 +740,7 @@
"loading": "Bezig met laden van reacties…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Reactie",
"delete": "Verwijder deze reactie",
"deleteText1": "Weet je zeker dat je deze reactie wilt verwijderen?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Klik hier om een beschrijving in te voeren…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Nog geen beschrijving beschikbaar."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Beschrijving",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Beheerder",
"member": "Lid"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notificaties",
"none": "Je hebt geen meldingen. Fijne dag!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Prosjekt identifikator",
"identifierPlaceholder": "Prosjektidentifikatoren kommer her…",
"description": "Beskrivelse",
"descriptionPlaceholder": "Beskrivelsen gis her…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Farger",
"success": "Prosjektet ble opprettet."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoritter"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Tittel",
"titlePlaceholder": "Den lagrede filtertittelen kommer hit…",
"description": "Beskrivelse",
"descriptionPlaceholder": "Beskrivelsen gis her…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Inkluder oppgaver som ikke har en verdi satt",
"requireAll": "Krev at alle filtre er oppfylt for at en oppgave skal vises",
"showDoneTasks": "Vis utførte oppgaver",
@ -480,6 +495,7 @@
"custom": "Egendefinert",
"id": "Id",
"created": "Opprettet",
"createdBy": "Created by {0}",
"actions": "Handlinger",
"cannotBeUndone": "Dette kan ikke angres!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Ferdig",
"heading1": "Overskrift 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Overskrift 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Overskrift 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Overskrift mindre",
"headingBigger": "Overskrift større",
"bold": "Fet",
"italic": "Kursiv",
"strikethrough": "Gjennomstrekning",
"underline": "Underline",
"code": "Kode",
"codeTooltip": "Capture a code snippet.",
"quote": "Sitat",
"unorderedList": "Uordnet liste",
"orderedList": "Sortert liste",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Tøm blokk",
"link": "Link",
"image": "Bilde",
"table": "Tabell",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Vannrett linje",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side ved side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Opprett ny",
@ -689,7 +740,7 @@
"loading": "Laster inn kommenterer…",
"edited": "redigert {date}",
"creating": "Lager kommentar…",
"placeholder": "Legg inn din kommentar…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Kommentar",
"delete": "Slett denne kommentaren",
"deleteText1": "Er du sikker på at du vil slette denne kommentaren?",
@ -703,7 +754,7 @@
"1week": "1 uke"
},
"description": {
"placeholder": "Klikk for å legge inn en beskrivelse…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Ingen beskrivelse tilgjengelig."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Gruppens navn går her…",
"nameRequired": "Angi et navn.",
"description": "Beskrivelse",
"descriptionPlaceholder": "Beskrivelsen gis her…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Administrator",
"member": "Medlem"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Varsler",
"none": "Du har ingen varsler på dette tidspunktet!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Kommandoer",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Tytuł",
"titlePlaceholder": "Tu wpisz tytuł filtra stałego…",
"description": "Opis",
"descriptionPlaceholder": "Tu wpisz opis…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Uwzględnij zadania, które nie mają ustawionej danej wartości",
"requireAll": "Wymagaj, aby wszystkie filtry były spełnione, aby zadanie się pojawiło",
"showDoneTasks": "Pokaż ukończone zadania",
@ -480,6 +495,7 @@
"custom": "Własne",
"id": "ID",
"created": "Utworzono",
"createdBy": "Created by {0}",
"actions": "Akcje",
"cannotBeUndone": "Tego nie da się cofnąć!"
},
@ -498,24 +514,59 @@
"edit": "Edytuj",
"done": "Gotowe",
"heading1": "Nagłówek 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Nagłówek 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Nagłówek 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Nagłówek mniejszy",
"headingBigger": "Nagłówek większy",
"bold": "Pogrubiony",
"italic": "Kursywa",
"strikethrough": "Przekreślony",
"underline": "Underline",
"code": "Kod",
"codeTooltip": "Capture a code snippet.",
"quote": "Cytat",
"unorderedList": "Lista nieuporządkowana",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Wyczyść blok",
"link": "Link",
"image": "Obraz",
"table": "Tabela",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Linia pozioma",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Obok siebie",
"guide": "Przewodnik"
"guide": "Przewodnik",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Utwórz nowy",
@ -689,7 +740,7 @@
"loading": "Ładowanie komentarzy…",
"edited": "edytowane {date}",
"creating": "Tworzenie komentarza…",
"placeholder": "Dodaj swój komentarz…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Skomentuj",
"delete": "Usuń ten komentarz",
"deleteText1": "Czy na pewno chcesz usunąć ten komentarz?",
@ -703,7 +754,7 @@
"1week": "1 tydzień"
},
"description": {
"placeholder": "Kliknij tutaj, aby wprowadzić opis…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Nie ma jeszcze opisu."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Tu wpisz nazwę zespołu…",
"nameRequired": "Proszę, podaj nazwę.",
"description": "Opis",
"descriptionPlaceholder": "Tu wpisz opis drużyny…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Administrator",
"member": "Członek"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Powiadomienia",
"none": "Nie masz żadnych powiadomień. Miłego dnia!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Polecenia",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Título",
"titlePlaceholder": "O título do filtro salvo fica aqui…",
"description": "Descrição",
"descriptionPlaceholder": "A descrição fica aqui…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Incluir tarefas que não possuem um conjunto de valores",
"requireAll": "Exigir que todos os filtros sejam verdadeiros para uma tarefa ser exibida",
"showDoneTasks": "Mostrar tarefas concluídas",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Criado em",
"createdBy": "Created by {0}",
"actions": "Ações",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Editar",
"done": "Concluído",
"heading1": "Título 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Título 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Título 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Negrito",
"italic": "Itálico",
"strikethrough": "Riscado",
"underline": "Underline",
"code": "Código",
"codeTooltip": "Capture a code snippet.",
"quote": "Citação",
"unorderedList": "Lista não ordenada",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Imagem",
"table": "Tabela",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Linha horizontal",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Lado a Lado",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Criar novo",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Criando comentário…",
"placeholder": "Adicione seu comentário…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comentário",
"delete": "Apagar este comentário",
"deleteText1": "Tem certeza que deseja apagar este comentário?",
@ -703,7 +754,7 @@
"1week": "1 semana"
},
"description": {
"placeholder": "Clique aqui para inserir uma descrição…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Nenhuma descrição disponível ainda."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Por favor, especifique um nome.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Administrador",
"member": "Membro"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notificações",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Comandos",

View File

@ -258,7 +258,7 @@
"identifier": "Identificador do Projeto",
"identifierPlaceholder": "O identificador do porjeto será aqui…",
"description": "Descrição",
"descriptionPlaceholder": "A descrição do projeto será aqui…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Cor",
"success": "O projeto foi atualizado com sucesso."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoritos"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Título",
"titlePlaceholder": "O título do filtro memorizado será aqui…",
"description": "Descrição",
"descriptionPlaceholder": "A descrição será aqui…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Incluir Tarefas que não têm um valor definido",
"requireAll": "Requerer que todos os filtros sejam verdadeiros para que uma tarefa apareça",
"showDoneTasks": "Mostrar Tarefas Concluídas",
@ -480,6 +495,7 @@
"custom": "Personalizado",
"id": "ID",
"created": "Criado em",
"createdBy": "Created by {0}",
"actions": "Ações",
"cannotBeUndone": "Isto não pode ser revertido!"
},
@ -498,24 +514,59 @@
"edit": "Editar",
"done": "Concluído",
"heading1": "Cabeçalho 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Cabeçalho 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Cabeçalho 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Título Menor",
"headingBigger": "Título Maior",
"bold": "Negrito",
"italic": "Itálico",
"strikethrough": "Rasurado",
"underline": "Underline",
"code": "Código",
"codeTooltip": "Capture a code snippet.",
"quote": "Citação",
"unorderedList": "Lista Não Ordenada",
"orderedList": "Lista Ordenada",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Limpar Formatação",
"link": "Link",
"image": "Imagem",
"table": "Tabela",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Linha Horizontal",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Lado-a-lado",
"guide": "Guia"
"guide": "Guia",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Criar novo",
@ -689,7 +740,7 @@
"loading": "A carregar comentários…",
"edited": "editado em {date}",
"creating": "A criar comentário…",
"placeholder": "Adiciona o teu comentário…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comentário",
"delete": "Eliminar este comentário",
"deleteText1": "Tens a certeza que pretendes eliminar este comentário?",
@ -703,7 +754,7 @@
"1week": "1 semana"
},
"description": {
"placeholder": "Clica aqui para inserir uma descrição…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Nenhuma descrição ainda disponível."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "O nome da equipa será aqui…",
"nameRequired": "Por favor, específica um nome.",
"description": "Descrição",
"descriptionPlaceholder": "A descrição da equipa será aqui…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Administrador",
"member": "Membro"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notificações",
"none": "Não tens nenhuma notificação. Tem um bom dia!",
"explainer": "As notificações aparecerão aqui quando ocorrem ações em projetos ou tarefas às quais estejas subscrito."
"explainer": "As notificações aparecerão aqui quando ocorrem ações em projetos ou tarefas às quais estejas subscrito.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Comandos",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -12,9 +12,9 @@
}
},
"demo": {
"title": "This instance is in demo mode. Do not use this for real data!",
"everythingWillBeDeleted": "Everything will be deleted in regular intervals!",
"accountWillBeDeleted": "Your account will be deleted, including all projects, tasks and attachments you might create."
"title": "Этот сервер находится в демо-режиме. Не используйте его для реальных данных!",
"everythingWillBeDeleted": "Все данные регулярно удаляются!",
"accountWillBeDeleted": "Однажды ваш аккаунт будет удалён вместе со всеми созданными проектами, задачами и вложениями."
},
"404": {
"title": "Не найдено",
@ -82,8 +82,8 @@
"savedSuccess": "Настройки обновлены.",
"emailReminders": "Присылать мне напоминания о задачах на e-mail",
"overdueReminders": "Присылать сводку о просроченных задачах каждый день",
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
"discoverableByName": "Разрешить другим пользователям добавлять меня в состав команд или проектов при поиске моего имени",
"discoverableByEmail": "Разрешить другим пользователям добавлять меня в состав команд или проектов при поиске моего полного email",
"playSoundWhenDone": "Проигрывать звук, когда задача помечается завершённой",
"weekStart": "Первый день недели",
"weekStartSunday": "Воскресенье",
@ -92,7 +92,7 @@
"defaultProject": "Проект по умолчанию",
"timezone": "Часовой пояс",
"overdueTasksRemindersTime": "Время напоминания о невыполненных задачах",
"filterUsedOnOverview": "Saved filter used on the overview page"
"filterUsedOnOverview": "Сохранённый фильтр, используемый на странице обзора"
},
"totp": {
"title": "Двухфакторная аутентификация",
@ -146,29 +146,29 @@
}
},
"apiTokens": {
"title": "API Tokens",
"general": "API tokens allow you to use Vikunja's API without user credentials.",
"apiDocs": "Check out the api docs",
"createAToken": "Create a token",
"createToken": "Create token",
"30d": "30 Days",
"60d": "60 Days",
"90d": "90 Days",
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
"titleRequired": "The title is required",
"expired": "This token has expired {ago}.",
"tokenCreatedSuccess": "Here is your new api token: {token}",
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
"title": "Токены API",
"general": "Токены API позволяют использовать Vikunja API без использования данных для входа пользователя.",
"apiDocs": "Документация API",
"createAToken": "Создать токен",
"createToken": "Создать токен",
"30d": "30 дней",
"60d": "60 дней",
"90d": "90 дней",
"permissionExplanation": "Разрешения позволяют выбрать, какие действия можно выполнять с использованием этого токена.",
"titleRequired": "Название обязательно",
"expired": "Срок действия этого токена истёк {ago}.",
"tokenCreatedSuccess": "Ваш новый токен: {token}",
"tokenCreatedNotSeeAgain": "Сохраните его в безопасном месте, вы не увидите его снова!",
"delete": {
"header": "Delete this token",
"text1": "Are you sure you want to delete the token \"{token}\"?",
"text2": "This will revoke access to all applications or integrations using it. You cannot undo this."
"header": "Удалить этот токен",
"text1": "Удалить токен «{token}»?",
"text2": "Все приложения или интеграции, использующие его, потеряют доступ. Это действие отменить нельзя."
},
"attributes": {
"title": "Title",
"title": "Название",
"titlePlaceholder": "Enter a title you will recognize later",
"expiresAt": "Expires at",
"permissions": "Permissions"
"expiresAt": "Срок действия",
"permissions": "Разрешения"
}
}
},
@ -176,7 +176,7 @@
"title": "Удалить свой аккаунт VIkunja",
"text1": "Удаление аккаунта необратимо. Мы удалим все ваши проекты, задачи и всё связанное с ними.",
"text2": "Для продолжения введите свой пароль. На почту будет отправлено письмо с дальнейшими инструкциями.",
"text3": "To proceed, please press the button below. You will receive an email with further instructions.",
"text3": "Для продолжения нажмите кнопку внизу. На почту будет отправлено письмо с дальнейшими инструкциями.",
"confirm": "Удалить мой аккаунт",
"requestSuccess": "Запрос успешно выполнен. На почту будет отправлено письмо с дальнейшими инструкциями.",
"passwordRequired": "Введите свой пароль.",
@ -184,7 +184,7 @@
"scheduled": "Мы удалим ваш аккаунт Vikunja {date} ({dateSince}).",
"scheduledCancel": "Что отменить удаление аккаунта, нажмите сюда.",
"scheduledCancelText": "Чтобы отменить удаление аккаунта, введите свой пароль ниже:",
"scheduledCancelButton": "To cancel the deletion of your account, please press the button below:",
"scheduledCancelButton": "Чтобы отменить удаление аккаунта, нажмите кнопку внизу:",
"scheduledCancelConfirm": "Отменить удаление моего аккаунта",
"scheduledCancelSuccess": "Мы не будем удалять ваш аккаунт."
},
@ -258,8 +258,8 @@
"identifier": "Идентификатор проекта",
"identifierPlaceholder": "Введите идентификатор проекта…",
"description": "Описание",
"descriptionPlaceholder": "Введите описание проекта…",
"color": "Color",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Цвет",
"success": "Проект успешно обновлён."
},
"share": {
@ -275,7 +275,7 @@
"namePlaceholder": "напр. Lorem Ipsum",
"nameExplanation": "Все действия, выполненные через эту ссылку, будут подписаны этим именем.",
"password": "Пароль (необязательно)",
"passwordExplanation": "When signing in, the user will be required to enter this password.",
"passwordExplanation": "Пользователь будет должен ввести пароль для входа.",
"noName": "Без имени",
"remove": "Удалить ссылку для обмена",
"removeText": "Удалить эту ссылку для обмена? Больше не удастся получить доступ к проекту через эту ссылку. Это действие отменить нельзя!",
@ -338,9 +338,9 @@
"doneBucketHint": "Все задачи, помещённые в эту колонку, автоматически отмечаются как завершённые.",
"doneBucketHintExtended": "Все задачи, перенесённые в колонку завершённых, будут помечены как завершённые. Все задачи, помеченные как завершённые, также будут перемещены в эту колонку.",
"doneBucketSavedSuccess": "Колонка завершённых была успешно сохранена.",
"defaultBucket": "Default bucket",
"defaultBucketHint": "When creating tasks without specifying a bucket, they will be added to this bucket.",
"defaultBucketSavedSuccess": "The default bucket has been saved successfully.",
"defaultBucket": "Колонка по умолчанию",
"defaultBucketHint": "При создании задач без указания колонки они будут добавлены в эту колонку.",
"defaultBucketSavedSuccess": "Колонка по умолчанию была успешно сохранена.",
"deleteLast": "Нельзя удалить последнюю колонку.",
"addTaskPlaceholder": "Введите название задачи…",
"addTask": "Добавить задачу",
@ -359,6 +359,21 @@
"favorites": {
"title": "Избранное"
}
},
"webhooks": {
"title": "Вебхуки",
"targetUrl": "URL обработчика",
"targetUrlInvalid": "Укажите корректный URL.",
"events": "События",
"eventsHint": "Выберите все события, для которых этот вебхук должен получать обновления (в пределах текущего проекта).",
"mustSelectEvents": "Выберите хотя бы одно событие.",
"delete": "Удалить этот вебхук",
"deleteText": "Удалить этот вебхук? Внешние обработчики больше не будет получать события этого вебхука.",
"deleteSuccess": "Вебхук успешно удалён.",
"create": "Создать вебхук",
"secret": "Секрет",
"secretHint": "Если указан, все запросы к URL обработчика будут подписаны с помощью HMAC.",
"secretDocs": "Подробнее об использовании секретов в документации."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Название",
"titlePlaceholder": "Введите название сохранённого фильтра…",
"description": "Описание",
"descriptionPlaceholder": "Введите описание…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Включать задачи, у которых не установлено значение",
"requireAll": "Для отображения задачи требовать истинность всех фильтров",
"showDoneTasks": "Показывать завершённые задачи",
@ -382,7 +397,7 @@
},
"create": {
"title": "Создать сохранённый фильтр",
"description": "A saved filter is a virtual project which is computed from a set of filters each time it is accessed.",
"description": "Сохранённый фильтр — это виртуальный проект, содержимое которого выбирается с помощью фильтров в момент его просмотра.",
"action": "Создать новый сохранённый фильтр",
"titleRequired": "Укажите название фильтра."
},
@ -480,6 +495,7 @@
"custom": "Настраиваемый",
"id": "ID",
"created": "Дата создания",
"createdBy": "Created by {0}",
"actions": "Действия",
"cannotBeUndone": "Это действие отменить нельзя!"
},
@ -498,24 +514,59 @@
"edit": "Изменить",
"done": "Готово",
"heading1": "Заголовок 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Заголовок 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Заголовок 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Заголовок меньше",
"headingBigger": "Заголовок больше",
"bold": "Жирный",
"italic": "Курсив",
"strikethrough": "Зачёркнутый",
"underline": "Подчёркнутый",
"code": "Код",
"codeTooltip": "Capture a code snippet.",
"quote": "Цитата",
"unorderedList": "Маркированный список",
"orderedList": "Нумерованный список",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Очистить блок",
"link": "Ссылка",
"image": "Изображение",
"table": "Таблица",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Таблица",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Разделитель",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Руководство"
"guide": "Руководство",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Список задач",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Отменить",
"redo": "Вернуть",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Создать",
@ -551,8 +602,8 @@
"add1Day": "Добавить один день",
"minus1Day": "Вычесть один день",
"roundDay": "Округление вниз до начала дня",
"supportedUnits": "Supported time units",
"someExamples": "Examples of time expressions",
"supportedUnits": "Поддерживаемые единицы времени",
"someExamples": "Примеры выражений",
"units": {
"seconds": "Секунды",
"minutes": "Минуты",
@ -609,9 +660,9 @@
"belongsToProject": "Задача принадлежит проекту «{project}»",
"due": "Истекает {at}",
"closePopup": "Закрыть всплывающее окно",
"organization": "Organization",
"management": "Management",
"dateAndTime": "Date and time",
"organization": "Организация",
"management": "Управление",
"dateAndTime": "Дата и время",
"delete": {
"header": "Удалить задачу",
"text1": "Удалить эту задачу?",
@ -689,7 +740,7 @@
"loading": "Загрузка комментариев…",
"edited": "изменено {date}",
"creating": "Комментируем…",
"placeholder": "Добавить комментарий…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Комментировать",
"delete": "Удалить комментарий",
"deleteText1": "Удалить этот комментарий?",
@ -703,7 +754,7 @@
"1week": "1 неделя"
},
"description": {
"placeholder": "Нажмите сюда для ввода описания…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Описания ещё нет."
},
"assignee": {
@ -772,7 +823,7 @@
"repeat": {
"everyDay": "Каждый день",
"everyWeek": "Каждую неделю",
"every30d": "Every 30 Days",
"every30d": "Каждые 30 дней",
"mode": "Режим повтора",
"monthly": "Ежемесячно",
"fromCurrentDate": "От сегодняшей даты",
@ -854,7 +905,7 @@
"namePlaceholder": "Имя команды здесь…",
"nameRequired": "Пожалуйста, укажите имя.",
"description": "Описание",
"descriptionPlaceholder": "Описание команды здесь…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Администратор",
"member": "Участник"
}
@ -882,8 +933,8 @@
"description": "Включить изменение описания задачи",
"delete": "Удалить задачу",
"priority": "Изменить приоритет задачи",
"favorite": "Mark this task as favorite / unfavorite",
"save": "Save the current task"
"favorite": "Добавить задачу в избранное или удалить из избранного",
"save": "Сохранить текущую задачу"
},
"project": {
"title": "Просмотр проекта",
@ -933,7 +984,9 @@
"notification": {
"title": "Уведомления",
"none": "Уведомлений нет. Хорошего дня!",
"explainer": "Здесь появятся уведомления, когда что-нибудь произойдёт с проектами или задачами, на которые вы подписаны."
"explainer": "Здесь появятся уведомления, когда что-нибудь произойдёт с проектами или задачами, на которые вы подписаны.",
"markAllRead": "Отметить всё как прочитанное",
"markAllReadSuccess": "Все уведомления отмечены как прочитанные."
},
"quickActions": {
"commands": "Команды",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -173,7 +173,7 @@
}
},
"deletion": {
"title": "Delete your Vikunja Account",
"title": "Radera ditt Vikunja-konto",
"text1": "The deletion of your account is permanent and cannot be undone. We will delete all your projects, tasks and everything associated with it.",
"text2": "To proceed, please enter your password. You will receive an email with further instructions.",
"text3": "För att fortsätta, tryck på knappen nedan. Du kommer att få ett e-postmeddelande med ytterligare instruktioner.",
@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Beskrivning",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Färg",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favoriter"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Vänligen ange en giltig URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Titel",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Beskrivning",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Redigera",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Fetstil",
"italic": "Kursiv",
"strikethrough": "Genomstruken",
"underline": "Underline",
"code": "Kod",
"codeTooltip": "Capture a code snippet.",
"quote": "Citat",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Länk",
"image": "Bild",
"table": "Tabell",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Tabell",
"insert": "Insert table",
"addColumnBefore": "Lägg till kolumn före",
"addColumnAfter": "Lägg till kolumn efter",
"deleteColumn": "Radera kolumn",
"addRowBefore": "Lägg till rad före",
"addRowAfter": "Lägg till rad efter",
"deleteRow": "Radera rad",
"deleteTable": "Radera tabell",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Ångra",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -627,7 +678,7 @@
"reminders": "Set Reminders",
"repeatAfter": "Set Repeating Interval",
"percentDone": "Set Progress",
"attachments": "Add Attachments",
"attachments": "Lägg till bilagor",
"relatedTasks": "Add Relation",
"moveProject": "Flytta",
"color": "Set Color",
@ -672,10 +723,10 @@
"title": "Bilagor",
"createdBy": "skapad {0} av {1}",
"downloadTooltip": "Download this attachment",
"upload": "Upload attachment",
"upload": "Ladda upp bilaga",
"drop": "Drop files here to upload",
"delete": "Radera bilaga",
"deleteTooltip": "Delete this attachment",
"deleteTooltip": "Radera denna bilaga",
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
"copyUrl": "Kopiera URL",
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
@ -689,7 +740,7 @@
"loading": "Laddar kommentarer…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Skriv din kommentar…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Är du säker på att du vill radera denna kommentar?",
@ -703,7 +754,7 @@
"1week": "1 vecka"
},
"description": {
"placeholder": "Klicka här för att ange en beskrivning…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Beskrivning",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Medlem"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Kommandon",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Tiêu đề",
"titlePlaceholder": "Tiêu đề bộ lọc đã lưu ở đây…",
"description": "Mô tả",
"descriptionPlaceholder": "Mô tả ở đây…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Bao gồm các Công việc không có bộ giá trị",
"requireAll": "Yêu cầu tất cả các bộ lọc phải đúng để một công việc được hiển thị",
"showDoneTasks": "Hiển thị các công việc đã hoàn thành",
@ -480,6 +495,7 @@
"custom": "Tuỳ chỉnh",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Chỉnh sửa",
"done": "Hoàn thành",
"heading1": "Tiêu đề 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Tiêu đề 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Tiêu đề 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Tiêu đề nhỏ hơn",
"headingBigger": "Tiêu đề lớn hơn",
"bold": "In đậm",
"italic": "In nghiêng",
"strikethrough": "Gạch ngang",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Trích dẫn",
"unorderedList": "Gạch đầu dòng",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Làm sạch Khối",
"link": "Liên kết",
"image": "Ảnh",
"table": "Bảng",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Dòng kẻ ngang",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Chia đôi cửa sổ",
"guide": "Hướng dẫn"
"guide": "Hướng dẫn",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Tạo mới",
@ -689,7 +740,7 @@
"loading": "Đang tải bình luận…",
"edited": "đã sửa {date}",
"creating": "Đang tạo bình luận…",
"placeholder": "Thêm bình luận của bạn…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Bình luận",
"delete": "Xóa bình luận này",
"deleteText1": "Bạn có chắc muốn xóa bình luận này không?",
@ -703,7 +754,7 @@
"1week": "1 tuần"
},
"description": {
"placeholder": "Nhấp vào đây để nhập mô tả…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "Chưa có mô tả."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "Tên của Team ở đây…",
"nameRequired": "Hãy đặt một cái tên.",
"description": "Mô tả",
"descriptionPlaceholder": "Mô tả Team ở đây…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Quản trị viên",
"member": "Thành viên"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Thông báo",
"none": "Bạn không có thông báo nào. Chúc một ngày tốt lành!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Các lệnh",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "标题",
"titlePlaceholder": "填写筛选器标题",
"description": "描述信息",
"descriptionPlaceholder": "填写描述详情",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "包含没有设置值的任务",
"requireAll": "要求所有筛选器为真才能显示任务",
"showDoneTasks": "显示已完成的任务",
@ -480,6 +495,7 @@
"custom": "自定义",
"id": "ID",
"created": "创建于",
"createdBy": "Created by {0}",
"actions": "行为",
"cannotBeUndone": "此操作无法撤消!"
},
@ -498,24 +514,59 @@
"edit": "编辑",
"done": "完成",
"heading1": "一级标题",
"heading1Tooltip": "Big section heading.",
"heading2": "二级标题",
"heading2Tooltip": "Medium section heading.",
"heading3": "三级标题",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "下一级标题",
"headingBigger": "上一级标题",
"bold": "粗体",
"italic": "斜体",
"strikethrough": "删除线",
"underline": "Underline",
"code": "代码",
"codeTooltip": "Capture a code snippet.",
"quote": "引用",
"unorderedList": "无序列表",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "清除格式",
"link": "链接",
"image": "图片",
"table": "表格",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "水平线",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "并排",
"guide": "指南"
"guide": "指南",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "创建新的",
@ -689,7 +740,7 @@
"loading": "正在加载评论…",
"edited": "编辑于 {date}",
"creating": "正在创建评论…",
"placeholder": "添加你的评论......",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "评论",
"delete": "删除此评论",
"deleteText1": "确实要删除此评论吗?",
@ -703,7 +754,7 @@
"1week": "1周"
},
"description": {
"placeholder": "点击此处输入描述...",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "尚无描述。"
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "团队的名字在这里……",
"nameRequired": "请指定名称。",
"description": "描述信息",
"descriptionPlaceholder": "团队描述在这里…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "管理员",
"member": "成员"
}
@ -933,7 +984,9 @@
"notification": {
"title": "通知",
"none": "没有任何通知。 祝你今天过得愉快!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "命令",

View File

@ -258,7 +258,7 @@
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The projects description goes here…",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
},
@ -359,6 +359,21 @@
"favorites": {
"title": "Favorites"
}
},
"webhooks": {
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
}
},
"filters": {
@ -368,7 +383,7 @@
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
@ -480,6 +495,7 @@
"custom": "Custom",
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
@ -498,24 +514,59 @@
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
"heading2Tooltip": "Medium section heading.",
"heading3": "Heading 3",
"heading3Tooltip": "Smaller section header.",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"underline": "Underline",
"code": "Code",
"codeTooltip": "Capture a code snippet.",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
"link": "Link",
"image": "Image",
"table": "Table",
"imageTooltip": "Upload an image from your computer.",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Delete row",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
},
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Side By Side",
"guide": "Guide"
"guide": "Guide",
"text": "Text",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…"
},
"multiselect": {
"createPlaceholder": "Create new",
@ -689,7 +740,7 @@
"loading": "Loading comments…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
@ -703,7 +754,7 @@
"1week": "1 week"
},
"description": {
"placeholder": "Click here to enter a description…",
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
@ -854,7 +905,7 @@
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "The teams description goes here…",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member"
}
@ -933,7 +984,9 @@
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen.",
"markAllRead": "Mark all notifications as read",
"markAllReadSuccess": "Successfully marked all notifications as read."
},
"quickActions": {
"commands": "Commands",

View File

@ -18,7 +18,7 @@ export interface IUserSettings extends IAbstract {
discoverableByName: boolean
discoverableByEmail: boolean
overdueTasksRemindersEnabled: boolean
overdueTasksRemindersTime: any
overdueTasksRemindersTime: string | Date
defaultProjectId: undefined | IProject['id']
weekStart: 0 | 1 | 2 | 3 | 4 | 5 | 6
timezone: string

View File

@ -52,7 +52,7 @@ export function getTaskIdentifier(task: ITask | null | undefined): string {
}
if (task.identifier === '') {
return `#${task.identifier}`
return `#${task.index}`
}
return task.identifier

View File

@ -15,8 +15,16 @@ export default class NotificationService extends AbstractService<INotification>
}
beforeUpdate(model) {
if (!model) {
return model
}
model.created = new Date(model.created).toISOString()
model.readAt = new Date(model.readAt).toISOString()
return model
}
async markAllRead() {
return this.post('/notifications', false)
}
}

View File

@ -37,3 +37,5 @@ $navbar-padding: 2rem;
$vikunja-nav-color: var(--grey-700);
$vikunja-nav-selected-width: 0.4rem;
$close-button-min-space: 84px;

View File

@ -31,7 +31,6 @@
<editor
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
:preview-is-default="false"
id="description"
:placeholder="$t('filters.attributes.descriptionPlaceholder')"
v-model="filter.description"

View File

@ -32,7 +32,6 @@
v-model="filter.description"
:class="{ 'disabled': filterService.loading}"
:disabled="filterService.loading"
:preview-is-default="false"
id="description"
:placeholder="$t('filters.attributes.descriptionPlaceholder')"
/>

View File

@ -58,7 +58,6 @@
<label class="label">{{ $t('label.attributes.description') }}</label>
<div class="control">
<editor
:preview-is-default="false"
:placeholder="$t('label.attributes.description')"
v-if="editorActive"
v-model="labelEditLabel.description"

View File

@ -15,10 +15,7 @@
<script lang="ts" setup>
import {computed} from 'vue'
import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
import {marked} from 'marked'
import DOMPurify from 'dompurify'
import {createRandomID} from '@/helpers/randomId'
import {useProjectStore} from '@/stores/projects'
const props = defineProps({
@ -36,7 +33,6 @@ const htmlDescription = computed(() => {
return ''
}
setupMarkdownRenderer(createRandomID())
return DOMPurify.sanitize(marked(description), {ADD_ATTR: ['target']})
return DOMPurify.sanitize(description, {ADD_ATTR: ['target']})
})
</script>

View File

@ -82,7 +82,7 @@ export function useGanttTaskList<F extends Filters>(
// update the task with possible changes from server
tasks.value.set(updatedTask.id, updatedTask)
success('Saved')
} catch (e: any) {
} catch (e) {
error('Something went wrong saving the task')
// roll back changes
tasks.value.set(task.id, oldTask)

View File

@ -54,7 +54,6 @@
<Editor
:class="{ 'disabled': isLoading}"
:disabled="isLoading"
:previewIsDefault="false"
id="projectdescription"
:placeholder="$t('project.edit.descriptionPlaceholder')"
v-model="project.description"

View File

@ -105,18 +105,18 @@ function useAuth() {
params: {projectId},
hash,
})
} catch (e: any) {
if (e.response?.data?.code === 13001) {
} catch (e) {
if (e?.response?.data?.code === 13001) {
authenticateWithPassword.value = true
return
}
// TODO: Put this logic in a global errorMessage handler method which checks all auth codes
let err = t('sharing.error')
if (e.response?.data?.message) {
if (e?.response?.data?.message) {
err = e.response.data.message
}
if (e.response?.data?.code === 13002) {
if (e?.response?.data?.code === 13002) {
err = t('sharing.invalidPassword')
}
errorMessage.value = err

View File

@ -32,7 +32,6 @@
<editor
:class="{ disabled: teamService.loading }"
:disabled="teamService.loading"
:preview-is-default="false"
id="teamdescription"
:placeholder="$t('team.attributes.descriptionPlaceholder')"
v-model="team.description"

View File

@ -143,7 +143,7 @@ async function submit() {
try {
await authStore.register(toRaw(credentials))
} catch (e: any) {
} catch (e) {
errorMessage.value = e?.message
}
}