From cb9495e65b9654cd4e1d4d0caeb7968283167fa6 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Sun, 15 Nov 2026 17:31:49 +0000 Subject: [PATCH 1/3] fix(deps): resolve npm security advisories and audit moderate findings Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- package-lock.json | 201 +++++++++------------------------------------- package.json | 6 +- 2 files changed, 43 insertions(+), 164 deletions(-) diff --git a/package-lock.json b/package-lock.json index 107318c..3a843a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1987,42 +1987,6 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@eslint/config-helpers": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", @@ -2509,6 +2473,13 @@ } } }, + "node_modules/@isaacs/nonrecursive-glob-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.1.0.tgz", + "integrity": "sha512-GPm/Dbvk5RLmhRnxXIa7nTpK0xtl4E0wj0MslsuNiJ1FIROU+2+9lgUC/dgF7Ey4QCWhfEsgxilMRMCdHPfDfw==", + "devOptional": true, + "license": "BlueOak-1.0.0" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3619,48 +3590,12 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@stryker-mutator/core/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@stryker-mutator/core/node_modules/brace-expansion": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.13.tgz", - "integrity": "sha512-Vh3dmFbc4wZeox7M9I/0L9GfFd83Su4+yCck/OF9Gi/P/dAHn0RT7X4K9R7gMNMhDck3DvhR6QXsnQdjjgwNvQ==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@stryker-mutator/core/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/@stryker-mutator/core/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@stryker-mutator/instrumenter": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-10.1.1.tgz", @@ -4012,42 +3947,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.72.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.2.tgz", @@ -4839,10 +4738,14 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "optional": true + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base-64": { "version": "1.0.0", @@ -4882,12 +4785,16 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", - "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", - "optional": true, + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.13.tgz", + "integrity": "sha512-Vh3dmFbc4wZeox7M9I/0L9GfFd83Su4+yCck/OF9Gi/P/dAHn0RT7X4K9R7gMNMhDck3DvhR6QXsnQdjjgwNvQ==", + "devOptional": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/braces": { @@ -5824,27 +5731,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5857,21 +5743,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/espree": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", @@ -8107,15 +7978,17 @@ "dev": true }, "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "optional": true, + "version": "11.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.8.tgz", + "integrity": "sha512-nFQuYSTVrGvysVBps5Ih+0BdnYFN/Cj3C816r6+p7yK+FHrVLGE4O4N9AFwykrlNlvM2HYt+Rad1Uiro6h88Ig==", + "devOptional": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "@isaacs/nonrecursive-glob-match": "^1.1.0", + "brace-expansion": "^5.0.12" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -8912,12 +8785,14 @@ "dev": true }, "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.17.1.tgz", + "integrity": "sha512-jd3COkaKVgGUW+ZHyEHCG9bH7RoaaiWuMgzsIBjUMVjLAaWGWpoUAVwljVvBHxRu/2qEHnadLGZrkutYUBPQJA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" diff --git a/package.json b/package.json index fc84381..677f6c1 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "test:e2e": "playwright test", "test:e2e:libresign": "playwright test tests/e2e/libresign-event.spec.mjs", "lint": "eslint src tests scripts eslint.config.mjs vite*.ts vitest.config.ts playwright.config.mjs && npm run format:vue && npm run format:ts && npm run format:mjs", - "audit": "npm audit --audit-level=high", + "audit": "npm audit --audit-level=moderate", "test": "npm run test:unit", "test:coverage": "vitest run --coverage", "ts:check": "vue-tsc --noEmit", @@ -70,6 +70,10 @@ "vitest": "4.1.12", "vue-tsc": "3.3.13" }, + "overrides": { + "minimatch": "^11.0.1", + "qs": "^6.16.1" + }, "engines": { "node": "^24.0.0", "npm": "^11.0.0" From 07687f313b77662cf13c37e0d9df356de4b7ec91 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Sun, 15 Nov 2026 17:32:03 +0000 Subject: [PATCH 2/3] docs: position Communications as a finished free-software product and add LibreSign funding Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .github/FUNDING.yml | 4 ++++ README.md | 20 +++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e7a6803 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2026 LibreCode Coop and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +github: libresign diff --git a/README.md b/README.md index a86f394..7539883 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,19 @@ # Communications -**The right message, in the right place, at the right time — inside Nextcloud.** +**Reach the right people at the right time, directly in Nextcloud.** Communications is a **free and open-source application for Nextcloud** that helps organizations reach their people without scattering important announcements across unrelated tools. Compose a publication once, choose who should see it and when, then deliver it through the channels that fit the message. ## Communicate where people already work -- **Contextual banners and dialogs** — show an announcement within Nextcloud, including relevant pages and applications, rather than waiting for people to check another inbox. -- **A dedicated Inbox** — keep institutional publications accessible after the first notification, with reading and commenting features where enabled. -- **Nextcloud-native channels** — use Notifications, Activity, and email independently or together, according to your configuration. -- **Optional messaging channels** — connect supported providers through the separate Two-Factor Gateway app when installed, configured, and allowed by recipient preferences. -- **Scheduled or event-driven publications** — publish immediately, within a defined period, or in response to registered Nextcloud application events. -- **Audience and visibility controls** — target users and groups, exclude selected audiences, and define which applications or routes can display a contextual message. -- **A single publication workspace** — write rich text, preview presentation, manage drafts and active notices, reorder publications, and follow estimated engagement metrics. +- **Contextual banners and dialogs:** show an announcement within Nextcloud, including relevant pages and applications, rather than waiting for people to check another inbox. +- **A dedicated Inbox:** keep institutional publications accessible after the first notification, with reading and commenting features where enabled. +- **Nextcloud-native channels:** use Notifications, Activity, and email independently or together, according to your configuration. +- **Optional messaging channels:** connect supported providers through the separate Two-Factor Gateway app when installed, configured, and allowed by recipient preferences. +- **Scheduled or event-driven publications:** publish immediately, within a defined period, or in response to registered Nextcloud application events. +- **Audience and visibility controls:** target users and groups, exclude selected audiences, and define which applications or routes can display a contextual message. +- **A single publication workspace:** write rich text, preview presentation, manage drafts and active notices, reorder publications, and follow estimated engagement metrics. ## From everyday notices to automated communication @@ -37,6 +37,4 @@ If you need help with hosting, deployment, integrations, accessibility, maintena - [API and integration reference](docs/API.md) - [Contribute to the project](CONTRIBUTING.md) -The project grows through code, translations, testing, design, documentation, and financial support. You can help sustain development through [GitHub Sponsors](https://github.com/sponsors/vitormattos) or contact [LibreCode Coop](mailto:contact@librecode.coop) for professional services. - -> **Development preview:** Communications is not yet released or certified for production. Current validation uses Nextcloud 36 development builds and disposable test environments. Features and integration contracts may change before the first release. +The project grows through code, translations, testing, design, documentation, and financial support. You can help sustain development through [GitHub Sponsors](https://github.com/sponsors/libresign) or contact [LibreCode Coop](mailto:contact@librecode.coop) for professional services. From a2092842b472b7a3007b938ffe27de43c2a23645 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Sun, 15 Nov 2026 17:32:15 +0000 Subject: [PATCH 3/3] refactor(copy): replace long dashes in user-facing labels and translations Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- l10n/pt_BR.js | 5 +++-- l10n/pt_BR.json | 5 +++-- src/components/CampaignAppearanceFields.vue | 12 +++++++----- src/components/CampaignEditorReviewStep.vue | 2 +- src/components/CampaignOverview.vue | 15 +++++++++++---- src/components/FlowCampaignPicker.vue | 2 +- src/components/MarkdownEditor.vue | 2 +- src/composables/useCampaignAdminController.ts | 2 +- 8 files changed, 28 insertions(+), 17 deletions(-) diff --git a/l10n/pt_BR.js b/l10n/pt_BR.js index a7dfd8d..34eccd2 100644 --- a/l10n/pt_BR.js +++ b/l10n/pt_BR.js @@ -454,7 +454,7 @@ OC.L10N.register('communications', { "Preview message": "Pré-visualizar mensagem", "Preview presentation": "Visualizar apresentação", "Previous": "Anterior", - "Priority (0–100)": "Prioridade (0–100)", + "Priority (0 to 100)": "Prioridade (0 a 100)", "Priority must be between 0 and 100.": "A prioridade deve estar entre 0 e 100.", "Private bot chat identifier.": "Identificador de chat privado com o bot.", "Provide a version in the recipient's language. Empty fields use the original text.": "Escreva uma versão para o idioma do usuário. Campos vazios usam o texto original.", @@ -687,8 +687,9 @@ OC.L10N.register('communications', { "numeric": "numérico", "{ready} of {total} providers configured": "{ready} de {total} provedores configurados", "of": "de", + "to": "a", "options: search by name to find more.": "opções: pesquise pelo nome para encontrar outras.", "starts with": "começa com", "{count} in 24 hours": "{count} em 24 horas", - "— Choose color": "— Escolher cor" + "Choose color": "Escolher cor" }, "nplurals=2; plural=(n != 1);"); diff --git a/l10n/pt_BR.json b/l10n/pt_BR.json index 7321c89..0bd3ba6 100644 --- a/l10n/pt_BR.json +++ b/l10n/pt_BR.json @@ -453,7 +453,7 @@ "Preview message": "Pré-visualizar mensagem", "Preview presentation": "Visualizar apresentação", "Previous": "Anterior", - "Priority (0–100)": "Prioridade (0–100)", + "Priority (0 to 100)": "Prioridade (0 a 100)", "Priority must be between 0 and 100.": "A prioridade deve estar entre 0 e 100.", "Private bot chat identifier.": "Identificador de chat privado com o bot.", "Provide a version in the recipient's language. Empty fields use the original text.": "Escreva uma versão para o idioma do usuário. Campos vazios usam o texto original.", @@ -686,10 +686,11 @@ "numeric": "numérico", "{ready} of {total} providers configured": "{ready} de {total} provedores configurados", "of": "de", + "to": "a", "options: search by name to find more.": "opções: pesquise pelo nome para encontrar outras.", "starts with": "começa com", "{count} in 24 hours": "{count} em 24 horas", - "— Choose color": "— Escolher cor" + "Choose color": "Escolher cor" }, "pluralForm": "nplurals=2; plural=(n != 1);" } diff --git a/src/components/CampaignAppearanceFields.vue b/src/components/CampaignAppearanceFields.vue index 3127101..697c744 100644 --- a/src/components/CampaignAppearanceFields.vue +++ b/src/components/CampaignAppearanceFields.vue @@ -93,11 +93,13 @@ const { backgroundColor: form.banner_color, }" aria-hidden="true"> - {{ form.banner_color }} - - {{ t('communications', '— Choose color') }} + + + {{ t('communications', 'Choose color') }} + + diff --git a/src/components/CampaignEditorReviewStep.vue b/src/components/CampaignEditorReviewStep.vue index b21ae96..4fd3361 100644 --- a/src/components/CampaignEditorReviewStep.vue +++ b/src/components/CampaignEditorReviewStep.vue @@ -111,7 +111,7 @@ const { diff --git a/src/components/CampaignOverview.vue b/src/components/CampaignOverview.vue index 38f5d06..9442fc5 100644 --- a/src/components/CampaignOverview.vue +++ b/src/components/CampaignOverview.vue @@ -161,7 +161,14 @@ function period(c: CampaignRecord): string { // TRANSLATORS Publication with neither a start nor an end date restriction. if (!c.starts_at && !c.ends_at) return t('communications', 'No deadline') if (c.starts_at && c.ends_at) - return dateDisplay(c.starts_at) + ' – ' + dateDisplay(c.ends_at) + return ( + // TRANSLATORS Prefix of a date range; preserve the trailing space. + t('communications', 'From ') + + dateDisplay(c.starts_at) + // TRANSLATORS Separator within a date range; preserve the surrounding spaces. + + t('communications', ' until ') + + dateDisplay(c.ends_at) + ) return c.starts_at ? // TRANSLATORS Prefix followed by a dynamic value in the publications management screen; preserve its trailing space. t('communications', 'Since ') + dateDisplay(c.starts_at) @@ -525,9 +532,9 @@ const adminActivateActionLabel = t('communications', 'Activate') {{ t('communications', 'Showing') }} - {{ (currentPage - 1) * PAGE_SIZE + 1 }}–{{ - Math.min(currentPage * PAGE_SIZE, visible.length) - }} + {{ (currentPage - 1) * PAGE_SIZE + 1 }} + {{ t('communications', 'to') }} + {{ Math.min(currentPage * PAGE_SIZE, visible.length) }} {{ t('communications', 'of') }} {{ visible.length }} campaigns.value.map((c) => ({ value: String(c.id), - label: c.name + (c.status === 'active' ? '' : ' — ' + c.status), + label: c.name + (c.status === 'active' ? '' : ' (' + c.status + ')'), })), ) const reduce = (option: FlowCampaignOption) => option.value diff --git a/src/components/MarkdownEditor.vue b/src/components/MarkdownEditor.vue index be096f7..74e130b 100644 --- a/src/components/MarkdownEditor.vue +++ b/src/components/MarkdownEditor.vue @@ -291,7 +291,7 @@ const toggleLinePrefix = (view: EditorView, prefix: string) => { const toLine = state.doc.lineAt(to) const changes: { from: number; to: number; insert: string }[] = [] - // Detect if ALL selected lines already have the prefix – if so, remove it + // Remove the prefix when every selected line already begins with it. let allHavePrefix = true for (let lineNo = fromLine.number; lineNo <= toLine.number; lineNo++) { const line = state.doc.line(lineNo) diff --git a/src/composables/useCampaignAdminController.ts b/src/composables/useCampaignAdminController.ts index b2b6ee3..bf683e4 100644 --- a/src/composables/useCampaignAdminController.ts +++ b/src/composables/useCampaignAdminController.ts @@ -424,7 +424,7 @@ export function useCampaignAdminController( // TRANSLATORS Name of a contextual visual delivery format: modal dialog. const adminModalDialogLabel = t('communications', 'Modal dialog') // TRANSLATORS Short label for an option or field in the publications management screen. - const adminPriority0100Label = t('communications', 'Priority (0–100)') + const adminPriority0100Label = t('communications', 'Priority (0 to 100)') // TRANSLATORS Short label for an option or field in the publications management screen. const adminWhereToDisplayLabel = t('communications', 'Where to display') // TRANSLATORS Short label for an option or field in the publications management screen.