@conduction/nextcloud-vue 2.94.0 → 2.95.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 (72) hide show
  1. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +46 -28
  2. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +17 -10
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  5. package/dist/esm/components/CnBrandBar/CnBrandBar.vue2.js.map +1 -1
  6. package/dist/esm/components/CnBrandBar/CnBrandBar.vue3.js.map +1 -1
  7. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +56 -16
  8. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  9. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  10. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js +9 -0
  11. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js.map +1 -0
  12. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js +240 -0
  13. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js.map +1 -0
  14. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js +65 -0
  15. package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js.map +1 -0
  16. package/dist/esm/components/CnDeviceFramesWidget/index.js +30 -0
  17. package/dist/esm/components/CnDeviceFramesWidget/index.js.map +1 -0
  18. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  19. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +17 -1
  20. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  21. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +30 -31
  22. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  23. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js +6 -0
  24. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js.map +1 -0
  25. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js +90 -0
  26. package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js.map +1 -0
  27. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  28. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +48 -1
  29. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  30. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +20 -2
  31. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  32. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
  33. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +230 -25
  34. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
  35. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +447 -221
  36. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
  37. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  38. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  39. package/dist/esm/index.js +1 -0
  40. package/dist/esm/index.js.map +1 -1
  41. package/dist/esm/l10n/en.json.js +18 -1
  42. package/dist/esm/l10n/en.json.js.map +1 -1
  43. package/dist/esm/l10n/nl.json.js +18 -1
  44. package/dist/esm/l10n/nl.json.js.map +1 -1
  45. package/dist/esm/library-style.js +1 -1
  46. package/dist/esm/nextcloud-vue.css +356 -114
  47. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  48. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  49. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  50. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  51. package/dist/nextcloud-vue.cjs.js +6964 -5943
  52. package/dist/nextcloud-vue.cjs.js.map +2 -2
  53. package/dist/nextcloud-vue.css +356 -114
  54. package/l10n/en.json +18 -1
  55. package/l10n/nl.json +18 -1
  56. package/package.json +1 -1
  57. package/src/components/CnAppRoot/CnAppRoot.vue +20 -0
  58. package/src/components/CnBrandBar/CnBrandBar.vue +5 -4
  59. package/src/components/CnDetailPage/CnDetailPage.vue +55 -15
  60. package/src/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue +360 -0
  61. package/src/components/CnDeviceFramesWidget/index.js +29 -0
  62. package/src/components/CnFactsWidget/CnFactsWidget.vue +46 -2
  63. package/src/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue +107 -0
  64. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +81 -1
  65. package/src/components/CnRegisterMapping/CnRegisterMapping.md +10 -0
  66. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +511 -28
  67. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  68. package/src/components/index.js +1 -0
  69. package/src/index.js +1 -0
  70. package/src/schemas/app-manifest-v2.schema.json +6 -1
  71. package/src/utils/libraryWidgetKeys.js +1 -0
  72. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CnBrandBar.vue2.js","sources":["../../../../src/components/CnBrandBar/CnBrandBar.vue"],"sourcesContent":["<!--\n CnBrandBar — the brand block in Nextcloud's own header (screens-brand-block-top-bar).\n\n Draws the 237px brand block (emblem, organisation, app name, and a 1px\n divider at its end) at the START of `#header`, before the app menu, under the\n board look. CnAppRoot mounts it when a brand is declared and `#header` exists.\n\n `#header` is Nextcloud's and offers no extension point for apps, so this is\n the one place the library reaches into it, and it does so defensively:\n - it adds ONE node of its own (a host element) as the first child of the\n header, and teleports the block into it; it never moves, edits or removes\n a node Nextcloud drew;\n - it re-inserts its own host when Nextcloud re-renders the header (a\n MutationObserver on the header and its parent), never a second one;\n - with no `#header` it renders nothing and emits `unavailable`, so\n CnAppRoot lets the navigation draw the block instead.\n\n Sizes come from the board: `dossiq/DqKop`.\n-->\n<template>\n\t<Teleport v-if=\"host\" :to=\"host\">\n\t\t<div class=\"cn-brand-bar\" data-testid=\"cn-brand-bar\">\n\t\t\t<div class=\"cn-brand-bar__brand\" data-testid=\"cn-brand-bar-brand\">\n\t\t\t\t<!-- Decorative beside a name: the name already says whose app this is. -->\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"typeof brand.emblem === 'string' && brand.emblem !== ''\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\t:src=\"brand.emblem\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span\n\t\t\t\t\tv-else-if=\"brand.emblem === true\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem cn-brand-bar__emblem--theme\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<img\n\t\t\t\t\tv-else-if=\"brand.logo\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\t:src=\"brand.logo\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span v-if=\"brand.name || brand.caption\" class=\"cn-brand-bar__text\">\n\t\t\t\t\t<span v-if=\"brand.caption\" class=\"cn-brand-bar__caption\">{{ brand.caption }}</span>\n\t\t\t\t\t<strong v-if=\"brand.name\" class=\"cn-brand-bar__name\">{{ brand.name }}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<span class=\"cn-brand-bar__divider\" aria-hidden=\"true\" />\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { markRaw } from 'vue'\n\nconst HOST_CLASS = 'cn-brand-bar-host'\n\n/**\n * The brand block in Nextcloud's header.\n *\n * @event unavailable Emitted when there is no `#header` to put the block in (or it disappears), so the host lets the navigation draw it.\n *\n * @spec openspec/changes/screens-brand-block-top-bar/specs/layout-components/spec.md#requirement-the-board-look-draws-the-brand-block-in-the-nextcloud-header\n */\nexport default {\n\tname: 'CnBrandBar',\n\n\tprops: {\n\t\t/**\n\t\t * The resolved brand: `{ logo, emblem, name, caption, alt }`. `emblem`\n\t\t * is a URL, or `true` for the theme's `--nldesign-emblem-url`.\n\t\t *\n\t\t * @type {{logo?: string, emblem?: (string|boolean), name?: string, caption?: string, alt?: string}}\n\t\t */\n\t\tbrand: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\temits: ['unavailable'],\n\n\tdata() {\n\t\treturn { host: null, observer: null }\n\t},\n\n\tmounted() {\n\t\tthis.attach()\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.detach()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Insert the host as the first child of `#header` and watch the header.\n\t\t * Emits `unavailable` when there is no header.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tattach() {\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\t/**\n\t\t\t\t * @event unavailable Emitted when there is no `#header` to put the block in.\n\t\t\t\t */\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.host = markRaw(document.createElement('div'))\n\t\t\tthis.host.className = HOST_CLASS\n\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\tif (typeof MutationObserver === 'function') {\n\t\t\t\tthis.observer = new MutationObserver(() => this.reattach())\n\t\t\t\tthis.observer.observe(header, { childList: true })\n\t\t\t\tif (header.parentNode) {\n\t\t\t\t\tthis.observer.observe(header.parentNode, { childList: true })\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Put the same host back when Nextcloud re-rendered the header and took\n\t\t * it out. Never creates a second host; when the header is gone for good\n\t\t * it hands the block back to the navigation.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\treattach() {\n\t\t\tif (!this.host) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.host.parentNode !== header) {\n\t\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\t\tthis.observer?.observe(header, { childList: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stop watching and remove only the host this component added.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdetach() {\n\t\t\tthis.observer?.disconnect()\n\t\t\tthis.observer = null\n\t\t\tthis.host?.remove()\n\t\t\tthis.host = null\n\t\t},\n\t},\n}\n</script>\n\n<style>\n/* The host is one flex item at the start of Nextcloud's header. */\n.cn-brand-bar-host {\n\tflex: none;\n\tdisplay: flex;\n\talign-items: center;\n\theight: 100%;\n\tmargin-inline-end: 12px;\n}\n\n.cn-brand-bar {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\twidth: var(--cn-board-brand-width, 237px);\n}\n\n.cn-brand-bar__brand {\n\tflex: 1 1 0;\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tmin-height: 48px;\n\tmax-height: 100%;\n\tpadding: 0 6px;\n\tborder-radius: 8px;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n\n.cn-brand-bar__emblem {\n\tflex: none;\n\theight: var(--cn-nav-emblem-size, 34px);\n\twidth: auto;\n\tobject-fit: contain;\n}\n\n.cn-brand-bar__emblem--theme {\n\tdisplay: inline-block;\n\twidth: var(--cn-nav-emblem-size, 34px);\n\tbackground: var(--nldesign-emblem-url) center / contain no-repeat;\n}\n\n.cn-brand-bar__text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tline-height: 1.15;\n\tmin-width: 0;\n}\n\n.cn-brand-bar__caption {\n\tfont-size: 13px;\n\twhite-space: nowrap;\n\topacity: 0.8;\n}\n\n.cn-brand-bar__name {\n\tfont-size: 18px;\n\tfont-weight: 700;\n\twhite-space: nowrap;\n}\n\n.cn-brand-bar__divider {\n\tflex: none;\n\twidth: 1px;\n\theight: 28px;\n\tbackground: currentColor;\n\topacity: 0.25;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n</style>\n"],"names":[],"mappings":";;AAqDA,MAAM,aAAa,oBAAkB;;AAErC;AACC;AACA;;AAEA;AACA;;AAED,aAAe;AACd,CAAA,IAAI,EAAE,YAAY;;AAElB,CAAA,KAAK,EAAE;AACN;;;AAGC;AACA;;AAED,EAAA,KAAK,EAAE;GACN,IAAI,EAAE,MAAM;GACZ,QAAQ,EAAE,IAAI;GACd;EACD;;AAED,CAAA,KAAK,EAAE,CAAC,aAAa,CAAC;;AAEtB,CAAA,IAAI,GAAG;AACN,EAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAK,EAAA;EACpC;;AAED,CAAA,OAAO,GAAG;EACT,IAAI,CAAC,MAAM,GAAC;EACZ;;AAED,CAAA,aAAa,GAAG;EACf,IAAI,CAAC,MAAM,GAAC;EACZ;;AAED,CAAA,OAAO,EAAE;AACR;;AAEC;AACA;;;AAGD,EAAA,MAAM,GAAG;AACR,GAAA,MAAM,SAAS,QAAQ,CAAC,cAAc,CAAC,QAAQ,EAAA;GAC/C,IAAI,CAAC,MAAM,EAAE;AACZ;AACC;;IAED,IAAI,CAAC,KAAK,CAAC,aAAa,EAAA;IACxB,MAAK;AACN,IAAA;GACA,IAAI,CAAC,OAAO,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,EAAA;GACjD,IAAI,CAAC,IAAI,CAAC,SAAU,GAAE,WAAS;GAC/B,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAA;AAChD,GAAA,IAAI,OAAO,gBAAiB,KAAI,UAAU,EAAE;IAC3C,IAAI,CAAC,WAAW,IAAI,gBAAgB,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAA;AAC1D,IAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAA;AACjD,IAAA,IAAI,MAAM,CAAC,UAAU,EAAE;KACtB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAG,EAAG,EAAA;AAC7D,KAAA;AACD,IAAA;GACA;;AAED;;;AAGC;AACA;;;EAGD,QAAQ,GAAG;AACV,GAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;IACf,MAAK;AACN,IAAA;AACA,GAAA,MAAM,SAAS,QAAQ,CAAC,cAAc,CAAC,QAAQ,EAAA;GAC/C,IAAI,CAAC,MAAM,EAAE;IACZ,IAAI,CAAC,KAAK,CAAC,aAAa,EAAA;IACxB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,IAAI,CAAC,eAAe,MAAM,EAAE;IACpC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAA;IAChD,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAA;AACnD,IAAA;GACA;;AAED;;AAEC;;;AAGD,EAAA,MAAM,GAAG;GACR,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAC;AAC1B,GAAA,IAAI,CAAC,QAAO,GAAI,KAAG;AACnB,GAAA,IAAI,CAAC,IAAI,EAAE,MAAM,GAAC;GAClB,IAAI,CAAC,IAAK,GAAE,KAAG;GACf;EACD;AACF;;;;"}
1
+ {"version":3,"file":"CnBrandBar.vue2.js","sources":["../../../../src/components/CnBrandBar/CnBrandBar.vue"],"sourcesContent":["<!--\n CnBrandBar — the brand block in Nextcloud's own header (screens-brand-block-top-bar).\n\n Draws the brand block (a 237px brand link with the emblem, organisation and\n app name, then a 1px divider 12px on) at the START of `#header`, before the app menu, under the\n board look. CnAppRoot mounts it when a brand is declared and `#header` exists.\n\n `#header` is Nextcloud's and offers no extension point for apps, so this is\n the one place the library reaches into it, and it does so defensively:\n - it adds ONE node of its own (a host element) as the first child of the\n header, and teleports the block into it; it never moves, edits or removes\n a node Nextcloud drew;\n - it re-inserts its own host when Nextcloud re-renders the header (a\n MutationObserver on the header and its parent), never a second one;\n - with no `#header` it renders nothing and emits `unavailable`, so\n CnAppRoot lets the navigation draw the block instead.\n\n Sizes come from the board: `dossiq/DqKop`.\n-->\n<template>\n\t<Teleport v-if=\"host\" :to=\"host\">\n\t\t<div class=\"cn-brand-bar\" data-testid=\"cn-brand-bar\">\n\t\t\t<div class=\"cn-brand-bar__brand\" data-testid=\"cn-brand-bar-brand\">\n\t\t\t\t<!-- Decorative beside a name: the name already says whose app this is. -->\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"typeof brand.emblem === 'string' && brand.emblem !== ''\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\t:src=\"brand.emblem\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span\n\t\t\t\t\tv-else-if=\"brand.emblem === true\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem cn-brand-bar__emblem--theme\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<img\n\t\t\t\t\tv-else-if=\"brand.logo\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\t:src=\"brand.logo\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span v-if=\"brand.name || brand.caption\" class=\"cn-brand-bar__text\">\n\t\t\t\t\t<span v-if=\"brand.caption\" class=\"cn-brand-bar__caption\">{{ brand.caption }}</span>\n\t\t\t\t\t<strong v-if=\"brand.name\" class=\"cn-brand-bar__name\">{{ brand.name }}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<span class=\"cn-brand-bar__divider\" aria-hidden=\"true\" />\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { markRaw } from 'vue'\n\nconst HOST_CLASS = 'cn-brand-bar-host'\n\n/**\n * The brand block in Nextcloud's header.\n *\n * @event unavailable Emitted when there is no `#header` to put the block in (or it disappears), so the host lets the navigation draw it.\n *\n * @spec openspec/changes/screens-brand-block-top-bar/specs/layout-components/spec.md#requirement-the-board-look-draws-the-brand-block-in-the-nextcloud-header\n */\nexport default {\n\tname: 'CnBrandBar',\n\n\tprops: {\n\t\t/**\n\t\t * The resolved brand: `{ logo, emblem, name, caption, alt }`. `emblem`\n\t\t * is a URL, or `true` for the theme's `--nldesign-emblem-url`.\n\t\t *\n\t\t * @type {{logo?: string, emblem?: (string|boolean), name?: string, caption?: string, alt?: string}}\n\t\t */\n\t\tbrand: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\temits: ['unavailable'],\n\n\tdata() {\n\t\treturn { host: null, observer: null }\n\t},\n\n\tmounted() {\n\t\tthis.attach()\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.detach()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Insert the host as the first child of `#header` and watch the header.\n\t\t * Emits `unavailable` when there is no header.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tattach() {\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\t/**\n\t\t\t\t * @event unavailable Emitted when there is no `#header` to put the block in.\n\t\t\t\t */\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.host = markRaw(document.createElement('div'))\n\t\t\tthis.host.className = HOST_CLASS\n\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\tif (typeof MutationObserver === 'function') {\n\t\t\t\tthis.observer = new MutationObserver(() => this.reattach())\n\t\t\t\tthis.observer.observe(header, { childList: true })\n\t\t\t\tif (header.parentNode) {\n\t\t\t\t\tthis.observer.observe(header.parentNode, { childList: true })\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Put the same host back when Nextcloud re-rendered the header and took\n\t\t * it out. Never creates a second host; when the header is gone for good\n\t\t * it hands the block back to the navigation.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\treattach() {\n\t\t\tif (!this.host) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.host.parentNode !== header) {\n\t\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\t\tthis.observer?.observe(header, { childList: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stop watching and remove only the host this component added.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdetach() {\n\t\t\tthis.observer?.disconnect()\n\t\t\tthis.observer = null\n\t\t\tthis.host?.remove()\n\t\t\tthis.host = null\n\t\t},\n\t},\n}\n</script>\n\n<style>\n/* The host is one flex item at the start of Nextcloud's header. */\n.cn-brand-bar-host {\n\tflex: none;\n\tdisplay: flex;\n\talign-items: center;\n\theight: 100%;\n\tmargin-inline-end: 12px;\n}\n\n.cn-brand-bar {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n}\n\n/* The brand link is 237px; the divider follows it, 12px on. */\n.cn-brand-bar__brand {\n\tflex: none;\n\twidth: var(--cn-board-brand-width, 237px);\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tmin-height: 48px;\n\tmax-height: 100%;\n\tpadding: 0 6px;\n\tborder-radius: 8px;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n\n.cn-brand-bar__emblem {\n\tflex: none;\n\theight: var(--cn-nav-emblem-size, 34px);\n\twidth: auto;\n\tobject-fit: contain;\n}\n\n.cn-brand-bar__emblem--theme {\n\tdisplay: inline-block;\n\twidth: var(--cn-nav-emblem-size, 34px);\n\tbackground: var(--nldesign-emblem-url) center / contain no-repeat;\n}\n\n.cn-brand-bar__text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tline-height: 1.15;\n\tmin-width: 0;\n}\n\n.cn-brand-bar__caption {\n\tfont-size: 13px;\n\twhite-space: nowrap;\n\topacity: 0.8;\n}\n\n.cn-brand-bar__name {\n\tfont-size: 18px;\n\tfont-weight: 700;\n\twhite-space: nowrap;\n}\n\n.cn-brand-bar__divider {\n\tflex: none;\n\twidth: 1px;\n\theight: 28px;\n\tbackground: currentColor;\n\topacity: 0.25;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n</style>\n"],"names":[],"mappings":";;AAqDA,MAAM,aAAa,oBAAkB;;AAErC;AACC;AACA;;AAEA;AACA;;AAED,aAAe;AACd,CAAA,IAAI,EAAE,YAAY;;AAElB,CAAA,KAAK,EAAE;AACN;;;AAGC;AACA;;AAED,EAAA,KAAK,EAAE;GACN,IAAI,EAAE,MAAM;GACZ,QAAQ,EAAE,IAAI;GACd;EACD;;AAED,CAAA,KAAK,EAAE,CAAC,aAAa,CAAC;;AAEtB,CAAA,IAAI,GAAG;AACN,EAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAK,EAAA;EACpC;;AAED,CAAA,OAAO,GAAG;EACT,IAAI,CAAC,MAAM,GAAC;EACZ;;AAED,CAAA,aAAa,GAAG;EACf,IAAI,CAAC,MAAM,GAAC;EACZ;;AAED,CAAA,OAAO,EAAE;AACR;;AAEC;AACA;;;AAGD,EAAA,MAAM,GAAG;AACR,GAAA,MAAM,SAAS,QAAQ,CAAC,cAAc,CAAC,QAAQ,EAAA;GAC/C,IAAI,CAAC,MAAM,EAAE;AACZ;AACC;;IAED,IAAI,CAAC,KAAK,CAAC,aAAa,EAAA;IACxB,MAAK;AACN,IAAA;GACA,IAAI,CAAC,OAAO,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,EAAA;GACjD,IAAI,CAAC,IAAI,CAAC,SAAU,GAAE,WAAS;GAC/B,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAA;AAChD,GAAA,IAAI,OAAO,gBAAiB,KAAI,UAAU,EAAE;IAC3C,IAAI,CAAC,WAAW,IAAI,gBAAgB,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAA;AAC1D,IAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAA;AACjD,IAAA,IAAI,MAAM,CAAC,UAAU,EAAE;KACtB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAG,EAAG,EAAA;AAC7D,KAAA;AACD,IAAA;GACA;;AAED;;;AAGC;AACA;;;EAGD,QAAQ,GAAG;AACV,GAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;IACf,MAAK;AACN,IAAA;AACA,GAAA,MAAM,SAAS,QAAQ,CAAC,cAAc,CAAC,QAAQ,EAAA;GAC/C,IAAI,CAAC,MAAM,EAAE;IACZ,IAAI,CAAC,KAAK,CAAC,aAAa,EAAA;IACxB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,IAAI,CAAC,IAAI,CAAC,eAAe,MAAM,EAAE;IACpC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAA;IAChD,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAA;AACnD,IAAA;GACA;;AAED;;AAEC;;;AAGD,EAAA,MAAM,GAAG;GACR,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAC;AAC1B,GAAA,IAAI,CAAC,QAAO,GAAI,KAAG;AACnB,GAAA,IAAI,CAAC,IAAI,EAAE,MAAM,GAAC;GAClB,IAAI,CAAC,IAAK,GAAE,KAAG;GACf;EACD;AACF;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnBrandBar.vue3.js","sources":["../../../../src/components/CnBrandBar/CnBrandBar.vue?vue&type=template&id=bd288742&lang.js"],"sourcesContent":["<!--\n CnBrandBar — the brand block in Nextcloud's own header (screens-brand-block-top-bar).\n\n Draws the 237px brand block (emblem, organisation, app name, and a 1px\n divider at its end) at the START of `#header`, before the app menu, under the\n board look. CnAppRoot mounts it when a brand is declared and `#header` exists.\n\n `#header` is Nextcloud's and offers no extension point for apps, so this is\n the one place the library reaches into it, and it does so defensively:\n - it adds ONE node of its own (a host element) as the first child of the\n header, and teleports the block into it; it never moves, edits or removes\n a node Nextcloud drew;\n - it re-inserts its own host when Nextcloud re-renders the header (a\n MutationObserver on the header and its parent), never a second one;\n - with no `#header` it renders nothing and emits `unavailable`, so\n CnAppRoot lets the navigation draw the block instead.\n\n Sizes come from the board: `dossiq/DqKop`.\n-->\n<template>\n\t<Teleport v-if=\"host\" :to=\"host\">\n\t\t<div class=\"cn-brand-bar\" data-testid=\"cn-brand-bar\">\n\t\t\t<div class=\"cn-brand-bar__brand\" data-testid=\"cn-brand-bar-brand\">\n\t\t\t\t<!-- Decorative beside a name: the name already says whose app this is. -->\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"typeof brand.emblem === 'string' && brand.emblem !== ''\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\t:src=\"brand.emblem\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span\n\t\t\t\t\tv-else-if=\"brand.emblem === true\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem cn-brand-bar__emblem--theme\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<img\n\t\t\t\t\tv-else-if=\"brand.logo\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\t:src=\"brand.logo\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span v-if=\"brand.name || brand.caption\" class=\"cn-brand-bar__text\">\n\t\t\t\t\t<span v-if=\"brand.caption\" class=\"cn-brand-bar__caption\">{{ brand.caption }}</span>\n\t\t\t\t\t<strong v-if=\"brand.name\" class=\"cn-brand-bar__name\">{{ brand.name }}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<span class=\"cn-brand-bar__divider\" aria-hidden=\"true\" />\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { markRaw } from 'vue'\n\nconst HOST_CLASS = 'cn-brand-bar-host'\n\n/**\n * The brand block in Nextcloud's header.\n *\n * @event unavailable Emitted when there is no `#header` to put the block in (or it disappears), so the host lets the navigation draw it.\n *\n * @spec openspec/changes/screens-brand-block-top-bar/specs/layout-components/spec.md#requirement-the-board-look-draws-the-brand-block-in-the-nextcloud-header\n */\nexport default {\n\tname: 'CnBrandBar',\n\n\tprops: {\n\t\t/**\n\t\t * The resolved brand: `{ logo, emblem, name, caption, alt }`. `emblem`\n\t\t * is a URL, or `true` for the theme's `--nldesign-emblem-url`.\n\t\t *\n\t\t * @type {{logo?: string, emblem?: (string|boolean), name?: string, caption?: string, alt?: string}}\n\t\t */\n\t\tbrand: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\temits: ['unavailable'],\n\n\tdata() {\n\t\treturn { host: null, observer: null }\n\t},\n\n\tmounted() {\n\t\tthis.attach()\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.detach()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Insert the host as the first child of `#header` and watch the header.\n\t\t * Emits `unavailable` when there is no header.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tattach() {\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\t/**\n\t\t\t\t * @event unavailable Emitted when there is no `#header` to put the block in.\n\t\t\t\t */\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.host = markRaw(document.createElement('div'))\n\t\t\tthis.host.className = HOST_CLASS\n\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\tif (typeof MutationObserver === 'function') {\n\t\t\t\tthis.observer = new MutationObserver(() => this.reattach())\n\t\t\t\tthis.observer.observe(header, { childList: true })\n\t\t\t\tif (header.parentNode) {\n\t\t\t\t\tthis.observer.observe(header.parentNode, { childList: true })\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Put the same host back when Nextcloud re-rendered the header and took\n\t\t * it out. Never creates a second host; when the header is gone for good\n\t\t * it hands the block back to the navigation.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\treattach() {\n\t\t\tif (!this.host) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.host.parentNode !== header) {\n\t\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\t\tthis.observer?.observe(header, { childList: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stop watching and remove only the host this component added.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdetach() {\n\t\t\tthis.observer?.disconnect()\n\t\t\tthis.observer = null\n\t\t\tthis.host?.remove()\n\t\t\tthis.host = null\n\t\t},\n\t},\n}\n</script>\n\n<style>\n/* The host is one flex item at the start of Nextcloud's header. */\n.cn-brand-bar-host {\n\tflex: none;\n\tdisplay: flex;\n\talign-items: center;\n\theight: 100%;\n\tmargin-inline-end: 12px;\n}\n\n.cn-brand-bar {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\twidth: var(--cn-board-brand-width, 237px);\n}\n\n.cn-brand-bar__brand {\n\tflex: 1 1 0;\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tmin-height: 48px;\n\tmax-height: 100%;\n\tpadding: 0 6px;\n\tborder-radius: 8px;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n\n.cn-brand-bar__emblem {\n\tflex: none;\n\theight: var(--cn-nav-emblem-size, 34px);\n\twidth: auto;\n\tobject-fit: contain;\n}\n\n.cn-brand-bar__emblem--theme {\n\tdisplay: inline-block;\n\twidth: var(--cn-nav-emblem-size, 34px);\n\tbackground: var(--nldesign-emblem-url) center / contain no-repeat;\n}\n\n.cn-brand-bar__text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tline-height: 1.15;\n\tmin-width: 0;\n}\n\n.cn-brand-bar__caption {\n\tfont-size: 13px;\n\twhite-space: nowrap;\n\topacity: 0.8;\n}\n\n.cn-brand-bar__name {\n\tfont-size: 18px;\n\tfont-weight: 700;\n\twhite-space: nowrap;\n}\n\n.cn-brand-bar__divider {\n\tflex: none;\n\twidth: 1px;\n\theight: 28px;\n\tbackground: currentColor;\n\topacity: 0.25;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n</style>\n"],"names":["_createBlock","_createElementVNode","_createElementBlock"],"mappings":";;;AAwCO,EAAA,KAAK,EAAC,cAAc;AAAC,EAAA,aAAW,EAAC,cAAc;;;AAC9C,EAAA,KAAK,EAAC,qBAAqB;AAAC,EAAA,aAAW,EAAC,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;UAFnD,KAAK,CAAA,IAAA;oBAAjBA;;AAAmB,QAAA,EAAC,EAAI,KAAG,CAAA,IAAA;;AAC5B,QAAAC,kBAAA,QAAA;AACC,UAAAA,kBAAA,QAAA;AAGG,YAAA,CAAA,OAAA,MAAA,CAAA,KAAA,CAAA,MAAA,KAAA,QAAA,IAAA,MAAA,CAAA,KAAA,CAAA,MAAA,KAAA,EAAA;8BADJC;;AAEC,kBAAA,KAAK,EAAC,sBAAsB;AAC5B,kBAAA,aAAE,EAAA,qBAAA;kBACD,GAAG,EAAE,MAAA,CAAA,KAAA,CAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnBrandBar.vue3.js","sources":["../../../../src/components/CnBrandBar/CnBrandBar.vue?vue&type=template&id=639a1bad&lang.js"],"sourcesContent":["<!--\n CnBrandBar — the brand block in Nextcloud's own header (screens-brand-block-top-bar).\n\n Draws the brand block (a 237px brand link with the emblem, organisation and\n app name, then a 1px divider 12px on) at the START of `#header`, before the app menu, under the\n board look. CnAppRoot mounts it when a brand is declared and `#header` exists.\n\n `#header` is Nextcloud's and offers no extension point for apps, so this is\n the one place the library reaches into it, and it does so defensively:\n - it adds ONE node of its own (a host element) as the first child of the\n header, and teleports the block into it; it never moves, edits or removes\n a node Nextcloud drew;\n - it re-inserts its own host when Nextcloud re-renders the header (a\n MutationObserver on the header and its parent), never a second one;\n - with no `#header` it renders nothing and emits `unavailable`, so\n CnAppRoot lets the navigation draw the block instead.\n\n Sizes come from the board: `dossiq/DqKop`.\n-->\n<template>\n\t<Teleport v-if=\"host\" :to=\"host\">\n\t\t<div class=\"cn-brand-bar\" data-testid=\"cn-brand-bar\">\n\t\t\t<div class=\"cn-brand-bar__brand\" data-testid=\"cn-brand-bar-brand\">\n\t\t\t\t<!-- Decorative beside a name: the name already says whose app this is. -->\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"typeof brand.emblem === 'string' && brand.emblem !== ''\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\t:src=\"brand.emblem\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span\n\t\t\t\t\tv-else-if=\"brand.emblem === true\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem cn-brand-bar__emblem--theme\"\n\t\t\t\t\tdata-testid=\"cn-brand-bar-emblem\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<img\n\t\t\t\t\tv-else-if=\"brand.logo\"\n\t\t\t\t\tclass=\"cn-brand-bar__emblem\"\n\t\t\t\t\t:src=\"brand.logo\"\n\t\t\t\t\t:alt=\"brand.name ? '' : brand.alt\">\n\t\t\t\t<span v-if=\"brand.name || brand.caption\" class=\"cn-brand-bar__text\">\n\t\t\t\t\t<span v-if=\"brand.caption\" class=\"cn-brand-bar__caption\">{{ brand.caption }}</span>\n\t\t\t\t\t<strong v-if=\"brand.name\" class=\"cn-brand-bar__name\">{{ brand.name }}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<span class=\"cn-brand-bar__divider\" aria-hidden=\"true\" />\n\t\t</div>\n\t</Teleport>\n</template>\n\n<script>\nimport { markRaw } from 'vue'\n\nconst HOST_CLASS = 'cn-brand-bar-host'\n\n/**\n * The brand block in Nextcloud's header.\n *\n * @event unavailable Emitted when there is no `#header` to put the block in (or it disappears), so the host lets the navigation draw it.\n *\n * @spec openspec/changes/screens-brand-block-top-bar/specs/layout-components/spec.md#requirement-the-board-look-draws-the-brand-block-in-the-nextcloud-header\n */\nexport default {\n\tname: 'CnBrandBar',\n\n\tprops: {\n\t\t/**\n\t\t * The resolved brand: `{ logo, emblem, name, caption, alt }`. `emblem`\n\t\t * is a URL, or `true` for the theme's `--nldesign-emblem-url`.\n\t\t *\n\t\t * @type {{logo?: string, emblem?: (string|boolean), name?: string, caption?: string, alt?: string}}\n\t\t */\n\t\tbrand: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\temits: ['unavailable'],\n\n\tdata() {\n\t\treturn { host: null, observer: null }\n\t},\n\n\tmounted() {\n\t\tthis.attach()\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.detach()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Insert the host as the first child of `#header` and watch the header.\n\t\t * Emits `unavailable` when there is no header.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tattach() {\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\t/**\n\t\t\t\t * @event unavailable Emitted when there is no `#header` to put the block in.\n\t\t\t\t */\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.host = markRaw(document.createElement('div'))\n\t\t\tthis.host.className = HOST_CLASS\n\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\tif (typeof MutationObserver === 'function') {\n\t\t\t\tthis.observer = new MutationObserver(() => this.reattach())\n\t\t\t\tthis.observer.observe(header, { childList: true })\n\t\t\t\tif (header.parentNode) {\n\t\t\t\t\tthis.observer.observe(header.parentNode, { childList: true })\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Put the same host back when Nextcloud re-rendered the header and took\n\t\t * it out. Never creates a second host; when the header is gone for good\n\t\t * it hands the block back to the navigation.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\treattach() {\n\t\t\tif (!this.host) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst header = document.getElementById('header')\n\t\t\tif (!header) {\n\t\t\t\tthis.$emit('unavailable')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.host.parentNode !== header) {\n\t\t\t\theader.insertBefore(this.host, header.firstChild)\n\t\t\t\tthis.observer?.observe(header, { childList: true })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stop watching and remove only the host this component added.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tdetach() {\n\t\t\tthis.observer?.disconnect()\n\t\t\tthis.observer = null\n\t\t\tthis.host?.remove()\n\t\t\tthis.host = null\n\t\t},\n\t},\n}\n</script>\n\n<style>\n/* The host is one flex item at the start of Nextcloud's header. */\n.cn-brand-bar-host {\n\tflex: none;\n\tdisplay: flex;\n\talign-items: center;\n\theight: 100%;\n\tmargin-inline-end: 12px;\n}\n\n.cn-brand-bar {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n}\n\n/* The brand link is 237px; the divider follows it, 12px on. */\n.cn-brand-bar__brand {\n\tflex: none;\n\twidth: var(--cn-board-brand-width, 237px);\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tmin-height: 48px;\n\tmax-height: 100%;\n\tpadding: 0 6px;\n\tborder-radius: 8px;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n\n.cn-brand-bar__emblem {\n\tflex: none;\n\theight: var(--cn-nav-emblem-size, 34px);\n\twidth: auto;\n\tobject-fit: contain;\n}\n\n.cn-brand-bar__emblem--theme {\n\tdisplay: inline-block;\n\twidth: var(--cn-nav-emblem-size, 34px);\n\tbackground: var(--nldesign-emblem-url) center / contain no-repeat;\n}\n\n.cn-brand-bar__text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tline-height: 1.15;\n\tmin-width: 0;\n}\n\n.cn-brand-bar__caption {\n\tfont-size: 13px;\n\twhite-space: nowrap;\n\topacity: 0.8;\n}\n\n.cn-brand-bar__name {\n\tfont-size: 18px;\n\tfont-weight: 700;\n\twhite-space: nowrap;\n}\n\n.cn-brand-bar__divider {\n\tflex: none;\n\twidth: 1px;\n\theight: 28px;\n\tbackground: currentColor;\n\topacity: 0.25;\n\tcolor: var(--color-background-plain-text, var(--color-main-text));\n}\n</style>\n"],"names":["_createBlock","_createElementVNode","_createElementBlock"],"mappings":";;;AAwCO,EAAA,KAAK,EAAC,cAAc;AAAC,EAAA,aAAW,EAAC,cAAc;;;AAC9C,EAAA,KAAK,EAAC,qBAAqB;AAAC,EAAA,aAAW,EAAC,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;UAFnD,KAAK,CAAA,IAAA;oBAAjBA;;AAAmB,QAAA,EAAC,EAAI,KAAG,CAAA,IAAA;;AAC5B,QAAAC,kBAAA,QAAA;AACC,UAAAA,kBAAA,QAAA;AAGG,YAAA,CAAA,OAAA,MAAA,CAAA,KAAA,CAAA,MAAA,KAAA,QAAA,IAAA,MAAA,CAAA,KAAA,CAAA,MAAA,KAAA,EAAA;8BADJC;;AAEC,kBAAA,KAAK,EAAC,sBAAsB;AAC5B,kBAAA,aAAE,EAAA,qBAAA;kBACD,GAAG,EAAE,MAAA,CAAA,KAAA,CAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -885,7 +885,7 @@ var script = {
885
885
  default: false,
886
886
  },
887
887
 
888
- /** Label for the header Edit button. Defaults to a translated "Edit". */
888
+ /** Label for the header Edit button, run through the host translate. Defaults to a translated "Edit". */
889
889
  editLabel: {
890
890
  type: String,
891
891
  default: '',
@@ -2200,14 +2200,40 @@ var script = {
2200
2200
 
2201
2201
  /**
2202
2202
  * The side column's widget definitions. A string names a widget
2203
- * declared in `widgets`; an object is a definition of its own.
2203
+ * declared in `widgets`; an object is a definition of its own. A
2204
+ * definition whose `visibleWhen` does not hold for the record (or the
2205
+ * records `visibilityContext` names) is left out, exactly as in the
2206
+ * body grid; edit mode shows them all.
2204
2207
  *
2205
2208
  * @return {Array<object>} The definitions that resolve, each with an `id`.
2206
2209
  * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-side-column
2210
+ * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
2207
2211
  */
2208
2212
  sideColumnWidgets() {
2213
+ const resolved = this.declaredSideWidgets.filter((widget) => this.sideWidgetVisible(widget));
2214
+ if (!this.isBoard) {
2215
+ return resolved
2216
+ }
2217
+ // The board look: the notice first, the History card last.
2218
+ const rank = (widget) => (widget.type === 'banner' ? 0 : isActivityWidget(widget) ? 2 : 1);
2219
+ return resolved
2220
+ .map((widget, index) => ({ widget, index }))
2221
+ .sort((a, b) => (rank(a.widget) - rank(b.widget)) || (a.index - b.index))
2222
+ .map(({ widget }) => widget)
2223
+ },
2224
+
2225
+ /**
2226
+ * The side column's entries resolved to definitions, before any
2227
+ * `visibleWhen` is read: a string names a widget declared in
2228
+ * `widgets`, an object is a definition of its own (given an id when it
2229
+ * has none).
2230
+ *
2231
+ * @return {Array<object>}
2232
+ * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
2233
+ */
2234
+ declaredSideWidgets() {
2209
2235
  const declared = Array.isArray(this.widgets) ? this.widgets : [];
2210
- const resolved = (Array.isArray(this.sideColumn) ? this.sideColumn : [])
2236
+ return (Array.isArray(this.sideColumn) ? this.sideColumn : [])
2211
2237
  .map((entry, index) => {
2212
2238
  if (typeof entry === 'string') {
2213
2239
  return declared.find((widget) => widget && widget.id === entry) || null
@@ -2217,16 +2243,7 @@ var script = {
2217
2243
  }
2218
2244
  return entry.id ? entry : { ...entry, id: `cn-side-${index}` }
2219
2245
  })
2220
- .filter(Boolean);
2221
- if (!this.isBoard) {
2222
- return resolved
2223
- }
2224
- // The board look: the notice first, the History card last.
2225
- const rank = (widget) => (widget.type === 'banner' ? 0 : isActivityWidget(widget) ? 2 : 1);
2226
- return resolved
2227
- .map((widget, index) => ({ widget, index }))
2228
- .sort((a, b) => (rank(a.widget) - rank(b.widget)) || (a.index - b.index))
2229
- .map(({ widget }) => widget)
2246
+ .filter(Boolean)
2230
2247
  },
2231
2248
 
2232
2249
  /**
@@ -2547,12 +2564,14 @@ var script = {
2547
2564
  },
2548
2565
 
2549
2566
  /**
2550
- * Label for the header Edit button.
2567
+ * Label for the header Edit button: the manifest's `editLabel` through
2568
+ * the host translate, else the library's translated "Edit".
2551
2569
  *
2552
2570
  * @return {string}
2571
+ * @spec openspec/changes/detail-edit-label-translate/specs/detail-page/spec.md#requirement-the-edit-button-label-follows-the-reader-s-language
2553
2572
  */
2554
2573
  editActionLabel() {
2555
- return this.editLabel || translate('nextcloud-vue', 'Edit')
2574
+ return this.editLabel ? this.effectiveTranslate(this.editLabel) : translate('nextcloud-vue', 'Edit')
2556
2575
  },
2557
2576
 
2558
2577
  /**
@@ -2806,7 +2825,7 @@ var script = {
2806
2825
  if (!this.widgetVisibilityData || this.visibilityRowsPending) {
2807
2826
  return ''
2808
2827
  }
2809
- const defs = [...(this.bodyGridWidgets || []), ...((this.activeView && this.activeView.widgets) || [])];
2828
+ const defs = [...(this.bodyGridWidgets || []), ...((this.activeView && this.activeView.widgets) || []), ...this.declaredSideWidgets];
2810
2829
  const remote = defs.filter((def) => def && def.visibleWhen && typeof def.visibleWhen === 'object'
2811
2830
  && !isLocallyDecidableVisibleWhen(def.visibleWhen));
2812
2831
  if (remote.length === 0) {
@@ -4681,6 +4700,27 @@ var script = {
4681
4700
  return compactLayoutRows(drawable)
4682
4701
  },
4683
4702
 
4703
+ /**
4704
+ * Whether a side column widget shows: always in edit mode or without a
4705
+ * `visibleWhen`, else while it holds for the record and the records
4706
+ * `visibilityContext` names, read by the same `detailWidgetVisible` the
4707
+ * body grid uses (local field conditions, `@context.<name>.*`, and the
4708
+ * answers of endpoint and source conditions).
4709
+ *
4710
+ * @param {object} widget A side column widget definition.
4711
+ * @return {boolean}
4712
+ * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
4713
+ */
4714
+ sideWidgetVisible(widget) {
4715
+ if (this.editingBody || widget.visibleWhen === undefined || widget.visibleWhen === null) {
4716
+ return true
4717
+ }
4718
+ return detailWidgetVisible(widget, this.widgetVisibilityData, {
4719
+ pending: this.visibilityRowsPending,
4720
+ answers: this.widgetConditionAnswers,
4721
+ })
4722
+ },
4723
+
4684
4724
  /**
4685
4725
  * Whether a layout item has anything to draw, so a grid cell is worth
4686
4726
  * spending on it.