@conduction/nextcloud-vue 2.70.0 → 2.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +151 -13
  2. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  5. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  6. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +38 -42
  7. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  8. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +29 -2
  9. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  10. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +71 -34
  11. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  12. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +22 -0
  13. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  14. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +1 -1
  15. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  16. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +1 -1
  17. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  18. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  19. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  20. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +46 -32
  21. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  22. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +30 -9
  23. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  24. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  25. package/dist/esm/composables/useWalkthrough.js +11 -3
  26. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  27. package/dist/esm/nextcloud-vue.css +53 -0
  28. package/dist/esm/utils/columnFilters.js +54 -4
  29. package/dist/esm/utils/columnFilters.js.map +1 -1
  30. package/dist/esm/utils/userAutocomplete.js +69 -3
  31. package/dist/esm/utils/userAutocomplete.js.map +1 -1
  32. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  33. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  34. package/dist/nextcloud-vue.cjs.js +615 -235
  35. package/dist/nextcloud-vue.cjs.js.map +2 -2
  36. package/dist/nextcloud-vue.css +53 -0
  37. package/package.json +1 -1
  38. package/src/components/CnAppRoot/CnAppRoot.vue +153 -13
  39. package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -18
  40. package/src/components/CnFormDialog/CnFormDialog.vue +62 -2
  41. package/src/components/CnIndexPage/CnIndexPage.vue +23 -1
  42. package/src/components/CnObjectSidebar/CnNotesTab.vue +1 -1
  43. package/src/components/CnSetupWizard/CnSetupWizard.vue +7 -0
  44. package/src/components/CnWalkthrough/CnWalkthrough.vue +30 -9
  45. package/src/composables/useWalkthrough.js +11 -3
  46. package/src/css/dashboard.css +44 -0
  47. package/src/schemas/app-manifest-v2.schema.json +2 -1
  48. package/src/utils/columnFilters.js +54 -4
  49. package/src/utils/userAutocomplete.js +68 -2
  50. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -149,7 +149,7 @@ var script = {
149
149
  * @param {(suggestions: Array<object>) => void} callback Receives the suggestion array.
150
150
  */
151
151
  async fetchMentionSuggestions(search, callback) {
152
- const users = await searchNextcloudUsers(search);
152
+ const users = await searchNextcloudUsers(search, { includeCurrentUser: false });
153
153
  callback(users.map((user) => ({
154
154
  id: user.id,
155
155
  label: user.label,
@@ -1 +1 @@
1
- {"version":3,"file":"CnNotesTab.vue2.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<NcRichContenteditable\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:autoComplete=\"fetchMentionSuggestions\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\tmultiline\n\t\t\t\t@update:modelValue=\"newNoteText = $event\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"note in notes\"\n\t\t\t\t:key=\"note.id\"\n\t\t\t\t:name=\"note.actorDisplayName || note.author || 'Unknown'\"\n\t\t\t\t:bold=\"false\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t<template v-for=\"(segment, index) in noteSegments(note)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"segment.type === 'mention'\"\n\t\t\t\t\t\t\t\t:key=\"`m-${note.id}-${index}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__mention\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__mention--unknown': isUnknownMention(segment.id) }\">{{ mentionDisplayName(segment.id) }}</span>\n\t\t\t\t\t\t\t<template v-else>{{ segment.value }}</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #details>\n\t\t\t\t\t{{ formatDate(note.creationDateTime || note.created) }}\n\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(note) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"canDelete(note) || wasEdited(note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"startEdit(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t@click=\"openHistory(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"deleteNote(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t:key=\"`${note.id}-${action.id}`\"\n\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note })\">\n\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon, NcRichContenteditable } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedIds, parseMentions } from '../../utils/mentions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, NcRichContenteditable, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Supply `@mention` suggestions to NcRichContenteditable's Tribute\n\t\t * integration. Results come from the core autocomplete OCS endpoint\n\t\t * via `searchNextcloudUsers` (fail-soft: errors resolve to []).\n\t\t *\n\t\t * @param {string} search The partial id/name typed after `@`.\n\t\t * @param {(suggestions: Array<object>) => void} callback Receives the suggestion array.\n\t\t */\n\t\tasync fetchMentionSuggestions(search, callback) {\n\t\t\tconst users = await searchNextcloudUsers(search)\n\t\t\tcallback(users.map((user) => ({\n\t\t\t\tid: user.id,\n\t\t\t\tlabel: user.label,\n\t\t\t\tsubline: user.subline,\n\t\t\t\ticon: 'icon-user',\n\t\t\t\tsource: 'users',\n\t\t\t})))\n\t\t},\n\n\t\t/**\n\t\t * Parse a note's message into text/mention segments for rendering.\n\t\t *\n\t\t * @param {object} note The note object from the backend.\n\t\t * @return {Array<object>} Segments from `parseMentions`.\n\t\t */\n\t\tnoteSegments(note) {\n\t\t\tconst message = note.message || note.content || ''\n\t\t\treturn parseMentions(message)\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Display name for a mentioned id — the resolved label, or the raw\n\t\t * id when the user is unknown/deleted.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {string} Chip text.\n\t\t */\n\t\tmentionDisplayName(id) {\n\t\t\treturn this.mentionNames[id] || id\n\t\t},\n\n\t\t/**\n\t\t * Whether a mentioned id failed to resolve to a real user.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {boolean} True when unresolved (renders the muted chip).\n\t\t */\n\t\tisUnknownMention(id) {\n\t\t\treturn !this.mentionNames[id]\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t})\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__mention {\n\tdisplay: inline-block;\n\tpadding: 0 6px;\n\tborder-radius: var(--border-radius-pill, 100px);\n\tbackground-color: var(--color-primary-element-light);\n\tcolor: var(--color-main-text);\n\tfont-weight: bold;\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n.cn-notes-tab__mention--unknown {\n\topacity: 0.6;\n\tfont-weight: normal;\n}\n</style>\n"],"names":["CnNoteHistoryDialog","HistoryIcon","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnNotesTab.vue2.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<NcRichContenteditable\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:autoComplete=\"fetchMentionSuggestions\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\tmultiline\n\t\t\t\t@update:modelValue=\"newNoteText = $event\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"note in notes\"\n\t\t\t\t:key=\"note.id\"\n\t\t\t\t:name=\"note.actorDisplayName || note.author || 'Unknown'\"\n\t\t\t\t:bold=\"false\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t<template v-for=\"(segment, index) in noteSegments(note)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"segment.type === 'mention'\"\n\t\t\t\t\t\t\t\t:key=\"`m-${note.id}-${index}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__mention\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__mention--unknown': isUnknownMention(segment.id) }\">{{ mentionDisplayName(segment.id) }}</span>\n\t\t\t\t\t\t\t<template v-else>{{ segment.value }}</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #details>\n\t\t\t\t\t{{ formatDate(note.creationDateTime || note.created) }}\n\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(note) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"canDelete(note) || wasEdited(note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"startEdit(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t@click=\"openHistory(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"deleteNote(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t:key=\"`${note.id}-${action.id}`\"\n\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note })\">\n\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon, NcRichContenteditable } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedIds, parseMentions } from '../../utils/mentions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, NcRichContenteditable, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Supply `@mention` suggestions to NcRichContenteditable's Tribute\n\t\t * integration. Results come from the core autocomplete OCS endpoint\n\t\t * via `searchNextcloudUsers` (fail-soft: errors resolve to []).\n\t\t *\n\t\t * @param {string} search The partial id/name typed after `@`.\n\t\t * @param {(suggestions: Array<object>) => void} callback Receives the suggestion array.\n\t\t */\n\t\tasync fetchMentionSuggestions(search, callback) {\n\t\t\tconst users = await searchNextcloudUsers(search, { includeCurrentUser: false })\n\t\t\tcallback(users.map((user) => ({\n\t\t\t\tid: user.id,\n\t\t\t\tlabel: user.label,\n\t\t\t\tsubline: user.subline,\n\t\t\t\ticon: 'icon-user',\n\t\t\t\tsource: 'users',\n\t\t\t})))\n\t\t},\n\n\t\t/**\n\t\t * Parse a note's message into text/mention segments for rendering.\n\t\t *\n\t\t * @param {object} note The note object from the backend.\n\t\t * @return {Array<object>} Segments from `parseMentions`.\n\t\t */\n\t\tnoteSegments(note) {\n\t\t\tconst message = note.message || note.content || ''\n\t\t\treturn parseMentions(message)\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Display name for a mentioned id — the resolved label, or the raw\n\t\t * id when the user is unknown/deleted.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {string} Chip text.\n\t\t */\n\t\tmentionDisplayName(id) {\n\t\t\treturn this.mentionNames[id] || id\n\t\t},\n\n\t\t/**\n\t\t * Whether a mentioned id failed to resolve to a real user.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {boolean} True when unresolved (renders the muted chip).\n\t\t */\n\t\tisUnknownMention(id) {\n\t\t\treturn !this.mentionNames[id]\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t})\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__mention {\n\tdisplay: inline-block;\n\tpadding: 0 6px;\n\tborder-radius: var(--border-radius-pill, 100px);\n\tbackground-color: var(--color-primary-element-light);\n\tcolor: var(--color-main-text);\n\tfont-weight: bold;\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n.cn-notes-tab__mention--unknown {\n\topacity: 0.6;\n\tfont-weight: normal;\n}\n</style>\n"],"names":["CnNoteHistoryDialog","HistoryIcon","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnNotesTab.vue3.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue?vue&type=template&id=6e7356a8&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<NcRichContenteditable\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:autoComplete=\"fetchMentionSuggestions\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\tmultiline\n\t\t\t\t@update:modelValue=\"newNoteText = $event\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"note in notes\"\n\t\t\t\t:key=\"note.id\"\n\t\t\t\t:name=\"note.actorDisplayName || note.author || 'Unknown'\"\n\t\t\t\t:bold=\"false\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t<template v-for=\"(segment, index) in noteSegments(note)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"segment.type === 'mention'\"\n\t\t\t\t\t\t\t\t:key=\"`m-${note.id}-${index}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__mention\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__mention--unknown': isUnknownMention(segment.id) }\">{{ mentionDisplayName(segment.id) }}</span>\n\t\t\t\t\t\t\t<template v-else>{{ segment.value }}</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #details>\n\t\t\t\t\t{{ formatDate(note.creationDateTime || note.created) }}\n\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(note) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"canDelete(note) || wasEdited(note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"startEdit(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t@click=\"openHistory(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"deleteNote(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t:key=\"`${note.id}-${action.id}`\"\n\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note })\">\n\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon, NcRichContenteditable } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedIds, parseMentions } from '../../utils/mentions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, NcRichContenteditable, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Supply `@mention` suggestions to NcRichContenteditable's Tribute\n\t\t * integration. Results come from the core autocomplete OCS endpoint\n\t\t * via `searchNextcloudUsers` (fail-soft: errors resolve to []).\n\t\t *\n\t\t * @param {string} search The partial id/name typed after `@`.\n\t\t * @param {(suggestions: Array<object>) => void} callback Receives the suggestion array.\n\t\t */\n\t\tasync fetchMentionSuggestions(search, callback) {\n\t\t\tconst users = await searchNextcloudUsers(search)\n\t\t\tcallback(users.map((user) => ({\n\t\t\t\tid: user.id,\n\t\t\t\tlabel: user.label,\n\t\t\t\tsubline: user.subline,\n\t\t\t\ticon: 'icon-user',\n\t\t\t\tsource: 'users',\n\t\t\t})))\n\t\t},\n\n\t\t/**\n\t\t * Parse a note's message into text/mention segments for rendering.\n\t\t *\n\t\t * @param {object} note The note object from the backend.\n\t\t * @return {Array<object>} Segments from `parseMentions`.\n\t\t */\n\t\tnoteSegments(note) {\n\t\t\tconst message = note.message || note.content || ''\n\t\t\treturn parseMentions(message)\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Display name for a mentioned id — the resolved label, or the raw\n\t\t * id when the user is unknown/deleted.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {string} Chip text.\n\t\t */\n\t\tmentionDisplayName(id) {\n\t\t\treturn this.mentionNames[id] || id\n\t\t},\n\n\t\t/**\n\t\t * Whether a mentioned id failed to resolve to a real user.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {boolean} True when unresolved (renders the muted chip).\n\t\t */\n\t\tisUnknownMention(id) {\n\t\t\treturn !this.mentionNames[id]\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t})\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__mention {\n\tdisplay: inline-block;\n\tpadding: 0 6px;\n\tborder-radius: var(--border-radius-pill, 100px);\n\tbackground-color: var(--color-primary-element-light);\n\tcolor: var(--color-main-text);\n\tfont-weight: bold;\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n.cn-notes-tab__mention--unknown {\n\topacity: 0.6;\n\tfont-weight: normal;\n}\n</style>\n"],"names":["_createElementVNode","_createVNode","_createBlock","_toDisplayString","_createElementBlock","_Fragment","_renderList","_createTextVNode","_openBlock","_resolveDynamicComponent"],"mappings":"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"}
1
+ {"version":3,"file":"CnNotesTab.vue3.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue?vue&type=template&id=6e7356a8&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<NcRichContenteditable\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:autoComplete=\"fetchMentionSuggestions\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\tmultiline\n\t\t\t\t@update:modelValue=\"newNoteText = $event\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"note in notes\"\n\t\t\t\t:key=\"note.id\"\n\t\t\t\t:name=\"note.actorDisplayName || note.author || 'Unknown'\"\n\t\t\t\t:bold=\"false\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t<template v-for=\"(segment, index) in noteSegments(note)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"segment.type === 'mention'\"\n\t\t\t\t\t\t\t\t:key=\"`m-${note.id}-${index}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__mention\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__mention--unknown': isUnknownMention(segment.id) }\">{{ mentionDisplayName(segment.id) }}</span>\n\t\t\t\t\t\t\t<template v-else>{{ segment.value }}</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #details>\n\t\t\t\t\t{{ formatDate(note.creationDateTime || note.created) }}\n\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(note) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"canDelete(note) || wasEdited(note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"startEdit(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-if=\"wasEdited(note)\"\n\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t@click=\"openHistory(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<NcActionButton v-if=\"canDelete(note)\" @click=\"deleteNote(note)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t:key=\"`${note.id}-${action.id}`\"\n\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note })\">\n\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon, NcRichContenteditable } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedIds, parseMentions } from '../../utils/mentions.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, NcRichContenteditable, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Supply `@mention` suggestions to NcRichContenteditable's Tribute\n\t\t * integration. Results come from the core autocomplete OCS endpoint\n\t\t * via `searchNextcloudUsers` (fail-soft: errors resolve to []).\n\t\t *\n\t\t * @param {string} search The partial id/name typed after `@`.\n\t\t * @param {(suggestions: Array<object>) => void} callback Receives the suggestion array.\n\t\t */\n\t\tasync fetchMentionSuggestions(search, callback) {\n\t\t\tconst users = await searchNextcloudUsers(search, { includeCurrentUser: false })\n\t\t\tcallback(users.map((user) => ({\n\t\t\t\tid: user.id,\n\t\t\t\tlabel: user.label,\n\t\t\t\tsubline: user.subline,\n\t\t\t\ticon: 'icon-user',\n\t\t\t\tsource: 'users',\n\t\t\t})))\n\t\t},\n\n\t\t/**\n\t\t * Parse a note's message into text/mention segments for rendering.\n\t\t *\n\t\t * @param {object} note The note object from the backend.\n\t\t * @return {Array<object>} Segments from `parseMentions`.\n\t\t */\n\t\tnoteSegments(note) {\n\t\t\tconst message = note.message || note.content || ''\n\t\t\treturn parseMentions(message)\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Display name for a mentioned id — the resolved label, or the raw\n\t\t * id when the user is unknown/deleted.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {string} Chip text.\n\t\t */\n\t\tmentionDisplayName(id) {\n\t\t\treturn this.mentionNames[id] || id\n\t\t},\n\n\t\t/**\n\t\t * Whether a mentioned id failed to resolve to a real user.\n\t\t *\n\t\t * @param {string} id The mentioned user id.\n\t\t * @return {boolean} True when unresolved (renders the muted chip).\n\t\t */\n\t\tisUnknownMention(id) {\n\t\t\treturn !this.mentionNames[id]\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t})\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__mention {\n\tdisplay: inline-block;\n\tpadding: 0 6px;\n\tborder-radius: var(--border-radius-pill, 100px);\n\tbackground-color: var(--color-primary-element-light);\n\tcolor: var(--color-main-text);\n\tfont-weight: bold;\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n.cn-notes-tab__mention--unknown {\n\topacity: 0.6;\n\tfont-weight: normal;\n}\n</style>\n"],"names":["_createElementVNode","_createVNode","_createBlock","_toDisplayString","_createElementBlock","_Fragment","_renderList","_createTextVNode","_openBlock","_resolveDynamicComponent"],"mappings":";;AAWM,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,gBAAgB,GAAA;AAErB,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,wBAAuB,GAAA;AAQ5B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,6BAAO,GAAA;;;AAwBiB,EAAA,KAAG,EAAA,uBAAA;;;;AAK5B,EAAA,KAAG,EAAA,sBAAA;;AAWL,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAmB,GAAA;;;AAmB9B,EAAA,KAAK,EAAC,sBAAS;AACd,EAAA,aAAU,EAAC,gBAAS;;;;;;;;;;;;;;;;;AApEvB,IAAAA,kBAAA,CAyBI,OAzBJ,UAyBI,EAAA;MAxBJC,WAMoB,CAAA,gCAAA,EAAA;AALpB,QAAA,KAAK,EAAC,0BAA0B;AAC/B,QAAA,UAAU,EAAE,KAAW,CAAA,WAAA;AACvB,QAAA,YAAO,EAAA,QAAA,CAAA,uBAAA;AACP,QAAA,WAAW,EAAE,MAAM,CAAA,kBAAA;AACnB,QAAA,SAAQ,EAAR,EAAQ;QACR,qBAAiB,EAAA,MAAA,CAAA,CAAA,CAAA,KAAA,MAAA,CAAA,CAAA,CAAA,GAAA,MAAA,KAAE,KAAA,CAAA,WAAA,GAAA,MAAA,CAAA,CAAA;;AACnB,MAAAD,kBAAA,CAgBI,OAhBJ,UAgBI,EAAA;SAdE,KAAC,CAAA,aAAA;0BADRE,WAKW,CAAA,mBAAA,EAAA;;AAHV,cAAA,OAAO,EAAC,UAAQ;AACf,cAAA,OAAK,EAAE,QAAU,CAAA,UAAA;;+BAClB,MAAiB;gDAAd,MAAW,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;;;;;QAEbD,WAQS,CAAA,mBAAA,EAAA;AAPV,UAAA,OAAO,EAAC,SAAE;AACT,UAAA,QAAQ,EAAG,CAAA,KAAA,CAAA,WAAW,CAAC,IAAK,MAAK,KAAM,CAAA,MAAA;AACvC,UAAA,OAAK,EAAE,MAAA,CAAA,CAAA,CAAA,KAAA,MAAA,CAAA,CAAA,CAAA,GAAA,MAAA,KAAA,KAAA,CAAA,aAAC,GAAA,QAAA,CAAA,QAAA,KAAA,QAAA,CAAA,OAAA,EAAA,CAAA,CAAA;;AACN,UAAA,IAAA,UACF,MAAC;YAADA,WAAC,CAAA,eAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;iCAEF;+BAAA,GAAGE,eAAA,CAAA,KAAA,CAAA,aAAY,GAAA,MAAA,CAAA,SAAA,GAAA,MAAA,CAAA,YAAA,CAAA,EAAA,CAAA,YAAA;;;;;;KASG,KAAO,CAAA,OAAA;iCAAyB,CAAA,wBAAA,EAAA;;AAAtB,UAAA,IAAI,EAAE,MAAa,CAAA,YAAA;;AAClC,QAAA,CAAA,KAAA,CAAA,KAAK,CAAC,MAAM,KAAA,CAAA;wBAA5BC,kBAEM,CAAA,KAAA,EAFN,UAEM,EAAAD,eAAA,CADF,MAAO,CAAA,YAAA,CAAA,EAAA,CAAA,YAAA;2CA0EN,gBAAA,EAAA;gDADO,CAAAE,QAAA,EAAA,IAAA,EAAAC,UAAA,CAnEK,KAAA,CAAA,KAAA,EAAA,CAAR,IAAI,KAAA;8CAmED,CAAA,qBAAA,EAAA;gBAlET,GAAG,EAAE,IAAI,CAAC,EAAE;AACZ,gBAAA,IAAI,EAAE,IAAG,CAAA,gBAAA,IAAA,IAAA,CAAA,MAAA,IAAA,SAAA;AACT,gBAAA,IAAI,EAAE,KAAK;AACX,gBAAA,mBAAmB,EAAE,IAAI;;AACd,gBAAA,IAAG,gBACR;6BAAA,CAAA,6BAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;AAEI,gBAAA,OAAO;;yDASLD,2BAP0B,QAAY,CAAA,YAAA,CAAC,IAAI,CAApC,EAAA,CAAA,OAAO,EAAE,KAAK,KAAA;;AAExB,wBAAA,CAAA,OAAE,CAAA,IAAA,KAAA,SAAA;0CADTD,kBAIe,CAAA,MAAA,EAAA;AAFb,8BAAA,GAAE,EAAA,CAAA,EAAA,EAAA,IAAA,CAAA,EAAA,IAAA,KAAA,CAAA,CAAA;AACH,8BAAA,KAAK,kBAAC,uBAAA,EAAA,EAAA,gCAAA,EACQ,yBAAA,CAAA,OAAA,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA;+CAAA,QAAA,CAAA,kBAAA,CAAA,OAAA,CAAA,EAAA,CAAA,CAAA,EAAA,CAAA,mBAAA;0CAChBA,kBAAgD,CAAAC,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;AAA3B,8BAAAE,eAAA,CAAAJ,eAAA,CAAA,OAAO,CAAC,KAAI,CAAA,EAAA,CAAA,YAAA;;;;;;AAIxB,gBAAA,OAAO,UACjB,MAAI;kDAAD,QAAC,CAAA,UAAA,CAAA,IAAA,CAAA,gBAAA,IAAA,IAAA,CAAA,OAAA,CAAA,CAAA,GAAA,GAKJ,EAAA,CAAA,YAAA;AACO,kBAAA,CAAA,QAAA,CAAA,SAAS,CAAC,IAAI,CAAA;AADrB,uBAAAK,SAAA,EAAA,EAAAJ,kBAAA,CAGsB,MAHtB,EAAA,UAGsB,EAAAD,eAAA,CAAA,QAAA,CAAA,UAAA,CAAA,IAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;;iBAEP,QAAA,CAAA,SAAA,CAAA,IAAA,CAAA,IAAA,QAAA,CAAA,SAAA,CAAA,IAAA,CAAA,IAAA,MAAA,CAAA,WAAA,CAAA,MAAA,GAAA,CAAA;;4BAAA,SAAA;kCACf,MAKgB;AALA,wBAAA,CAAA,QAAA,CAAA,SAAA,CAAA,IAAA,CAAA;0CAAhBD,WAKgB,CAAA,yBAAA,EAAA;;8BALA,OAAA,EAAA,MAAA,KAAA,QAAA,CAAA,SAAA,CAAA,IAAA,CAAA,CAAA;;AACJ,8BAAA,IAAE,UACZ,MAAoB;gCAApBD,WAAoB,CAAA,iBAAA,EAAA,EAAX,IAAI,EAAE,EAAE,EAAA,CAAA;;+CACP,MACX;AADW,gCAAAM,eAAA,CAAA,GACX,mBAAG,MAAS,CAAA,SAAA,CAAA,EAAA,CAAA,YAAA;;;;;AAGN,wBAAA,CAAA,QAAA,CAAA,SAAI,CAAA,IAAA,CAAA;qDAOM,CAAA,yBAAA,EAAA;;AANhB,8BAAA,aAAW,EAAC,wBAAK;8BAChB,OAAK,EAAA,MAAA,KAAE,QAAO,CAAA,WAAA,CAAA,IAAA,CAAA,CAAA;;AACJ,8BAAA,IAAI,UACd,MAAc;gCAAdN,WAAc,CAAA,sBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;+CACJ,MACX;AADW,gCAAAM,eAAA,CAAA,GACX,mBAAG,MAAO,CAAA,YAAA,CAAA,EAAA,CAAA,YAAA;;;;;AAEK,wBAAA,CAAA,QAAA,CAAA,SAAA,CAAA,IAAA,CAAA;0CAAhBL,WAKkB,CAAA,yBAAA,EAAA;;8BALF,OAAA,EAAA,MAAA,KAAA,QAAA,CAAA,UAAA,CAAA,IAAA,CAAA,CAAA;;AACJ,8BAAA,IAAI,UACX,MAAkB;gCAAlBD,WAAkB,CAAA,iBAAA,EAAA,EAAZ,IAAG,EAAG,EAAE,EAAA,CAAA;;+CACP,MACX;AADW,gCAAAM,eAAA,CAAA,GACX,mBAAG,MAAU,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;;;;;0CAKbH,kBASe,CAAAC,QAAA,EAAA,IAAA,EAAAC,UAAA,CARE,MAAW,CAAA,WAAA,EAAA,CAArB,MAAM,KAAA;+CADbJ,WASe,CAAA,yBAAA,EAAA;AAPd,4BAAA,GAAG,KAAK,IAAC,CAAA,EAAA,CAAA,CAAA,EAAA,MAAA,CAAA,EAAA,CAAA,CAAA;4BACT,aAAW,EAAA,CAAA,eAAA,EAAK,MAAA,CAAA,EAAA,CAAA,CAAA;AAChB,4BAAA,OAAK,aAAE,IAAK,CAAA,KAAA,CAAA,aAAA,EAAA,EAAA,MAAA,EAAE,MAAA,CAAA,EAAA,EAAA,IAAA,EAAA,CAAA,CAAA;;6CAGf;8CAAA;;;;AAFQ,4BAAA,CAAA,MAAA,CAAA,IAAA;;wCAAA,MAAA;8CACP,MAAQ;AAAR,qCAAAM,SAAA,EAAA,EAAAN,WAAA,CAAQO,wBAAA,MAAA,CAAA,IAAA,CAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;;;;;;;;;;;;;KASN,KAAS,CAAA,aAAA;sBADfP;;AAEC,UAAA,IAAI,EAAE,IAAI;AACV,UAAA,MAAM,EAAE,KAAa,CAAA,aAAA;AACrB,UAAA,QAAE,EAAA,MAAA,CAAA,QAAA;;;;;;;;;;;;"}