@conduction/nextcloud-vue 2.60.0 → 2.61.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 (56) hide show
  1. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +41 -1
  2. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  4. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +9 -2
  5. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  6. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  7. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +10 -2
  8. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  9. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  10. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  11. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +6 -5
  12. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  13. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +3 -1
  14. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  15. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +10 -0
  17. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js.map +1 -0
  18. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +316 -0
  19. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -0
  20. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +105 -0
  21. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -0
  22. package/dist/esm/components/CnPageRenderer/pageTypes.js +1 -0
  23. package/dist/esm/components/CnPageRenderer/pageTypes.js.map +1 -1
  24. package/dist/esm/index.js +6 -0
  25. package/dist/esm/index.js.map +1 -1
  26. package/dist/esm/l10n/en.json.js +1 -0
  27. package/dist/esm/l10n/en.json.js.map +1 -1
  28. package/dist/esm/l10n/nl.json.js +1 -0
  29. package/dist/esm/l10n/nl.json.js.map +1 -1
  30. package/dist/esm/nextcloud-vue.css +92 -2
  31. package/dist/esm/utils/headers.js +28 -2
  32. package/dist/esm/utils/headers.js.map +1 -1
  33. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  34. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  35. package/dist/nextcloud-vue.cjs.js +8741 -8229
  36. package/dist/nextcloud-vue.cjs.js.map +2 -2
  37. package/dist/nextcloud-vue.css +92 -2
  38. package/l10n/en.json +1 -0
  39. package/l10n/nl.json +1 -0
  40. package/package.json +1 -1
  41. package/scripts/.jsdoc-baselines.json +1 -0
  42. package/src/components/CnAppNav/CnAppNav.vue +41 -1
  43. package/src/components/CnBannerWidget/CnBannerWidget.vue +9 -2
  44. package/src/components/CnDashboardPage/CnDashboardPage.vue +10 -2
  45. package/src/components/CnDetailPage/CnDetailPage.vue +4 -1
  46. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +3 -1
  47. package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +484 -0
  48. package/src/components/CnLinkCardsPage/index.js +4 -0
  49. package/src/components/CnPageRenderer/pageTypes.js +1 -0
  50. package/src/components/index.js +1 -0
  51. package/src/css/detail-page.css +10 -2
  52. package/src/index.js +7 -0
  53. package/src/schemas/app-manifest-v2.schema.json +44 -2
  54. package/src/types/manifest.d.ts +1 -1
  55. package/src/utils/headers.js +28 -2
  56. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -2660,9 +2660,17 @@
2660
2660
  font-weight: 700;
2661
2661
  line-height: 1.3;
2662
2662
  color: var(--color-main-text);
2663
- white-space: nowrap;
2663
+ /* Two lines, then an ellipsis. One line cut a long case title short on a
2664
+ page with room to spare, and the title is the one thing that says which
2665
+ record this is. The element carries the full text as its `title`, so a
2666
+ title cut after two lines is still reachable. `overflow-wrap` breaks a
2667
+ word longer than the line, which nowrap used to hide. */
2668
+ display: -webkit-box;
2669
+ -webkit-box-orient: vertical;
2670
+ -webkit-line-clamp: 2;
2671
+ line-clamp: 2;
2664
2672
  overflow: hidden;
2665
- text-overflow: ellipsis;
2673
+ overflow-wrap: anywhere;
2666
2674
  }
2667
2675
 
2668
2676
  .cn-detail-page__subtitle {
@@ -32005,6 +32013,88 @@ a.cn-integration-card__title[data-v-f7e9bbfa]:hover {
32005
32013
  }
32006
32014
 
32007
32015
 
32016
+ .cn-link-cards-page[data-v-2f9a546b] {
32017
+ display: flex;
32018
+ flex-direction: column;
32019
+ gap: calc(6 * var(--default-grid-baseline));
32020
+ padding: calc(5 * var(--default-grid-baseline));
32021
+ }
32022
+ .cn-link-cards-page__title[data-v-2f9a546b] {
32023
+ margin: 0 0 var(--default-grid-baseline);
32024
+ }
32025
+ .cn-link-cards-page__description[data-v-2f9a546b],
32026
+ .cn-link-cards-page__empty[data-v-2f9a546b] {
32027
+ color: var(--color-text-maxcontrast);
32028
+ margin: 0;
32029
+ max-width: 70ch;
32030
+ }
32031
+ .cn-link-cards-page__group[data-v-2f9a546b] {
32032
+ display: flex;
32033
+ flex-direction: column;
32034
+ gap: calc(3 * var(--default-grid-baseline));
32035
+ }
32036
+ .cn-link-cards-page__caption[data-v-2f9a546b] {
32037
+ color: var(--color-text-maxcontrast);
32038
+ font-size: 0.85em;
32039
+ font-weight: 600;
32040
+ letter-spacing: 0.06em;
32041
+ margin: 0;
32042
+ text-transform: uppercase;
32043
+ }
32044
+ .cn-link-cards-page__grid[data-v-2f9a546b] {
32045
+ display: grid;
32046
+ gap: calc(3 * var(--default-grid-baseline));
32047
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
32048
+ list-style: none;
32049
+ margin: 0;
32050
+ padding: 0;
32051
+ }
32052
+ .cn-link-cards-page__cell[data-v-2f9a546b] {
32053
+ display: flex;
32054
+ }
32055
+ .cn-link-cards-page__card[data-v-2f9a546b] {
32056
+ align-items: flex-start;
32057
+ background: var(--color-main-background);
32058
+ border: 1px solid var(--color-border-dark);
32059
+ border-radius: var(--border-radius-large);
32060
+ color: var(--color-main-text);
32061
+ display: flex;
32062
+ flex: 1;
32063
+ gap: calc(3 * var(--default-grid-baseline));
32064
+ min-width: 0;
32065
+ padding: calc(4 * var(--default-grid-baseline));
32066
+ text-decoration: none;
32067
+ }
32068
+ .cn-link-cards-page__card[data-v-2f9a546b]:hover {
32069
+ background: var(--color-background-hover);
32070
+ border-color: var(--color-primary-element);
32071
+ }
32072
+ .cn-link-cards-page__card[data-v-2f9a546b]:focus-visible {
32073
+ border-color: var(--color-primary-element);
32074
+ outline: 2px solid var(--color-primary-element);
32075
+ outline-offset: 2px;
32076
+ }
32077
+ .cn-link-cards-page__icon[data-v-2f9a546b] {
32078
+ color: var(--color-primary-element);
32079
+ flex: none;
32080
+ }
32081
+ .cn-link-cards-page__body[data-v-2f9a546b] {
32082
+ display: flex;
32083
+ flex-direction: column;
32084
+ gap: var(--default-grid-baseline);
32085
+ min-width: 0;
32086
+ }
32087
+ .cn-link-cards-page__label[data-v-2f9a546b] {
32088
+ font-weight: 600;
32089
+ overflow-wrap: anywhere;
32090
+ }
32091
+ .cn-link-cards-page__card-description[data-v-2f9a546b] {
32092
+ color: var(--color-text-maxcontrast);
32093
+ font-size: 0.9em;
32094
+ line-height: 1.4;
32095
+ }
32096
+
32097
+
32008
32098
  /* NcDialog supplies the padding and the heading (via `name`); this div keeps only
32009
32099
  the body's own column layout. */
32010
32100
  .cn-page-config[data-v-9e9a9cf8] {
@@ -130,10 +130,15 @@ function capitalize(str) {
130
130
  *
131
131
  * Array values are serialized with PHP bracket notation (`key[]=a&key[]=b`)
132
132
  * so the Nextcloud/OpenRegister backend parses them as an array — a plain
133
- * repeated `key=a&key=b` collapses to the last value in PHP. Also handles
134
- * _order serialization (JSON.stringify for objects) and skips
133
+ * repeated `key=a&key=b` collapses to the last value in PHP. A plain object
134
+ * under a filter key is a set of operators and goes out in bracket form
135
+ * (`{ deadline: { lt: 'x' } }` becomes `deadline[lt]=x`); a key already
136
+ * written that way (`'deadline[lt]'`) passes through unchanged. Reserved keys
137
+ * that start with `_` keep their JSON form (`_order`). Skips
135
138
  * null/undefined/empty values.
136
139
  *
140
+ * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-filter-operator-is-serialised-in-bracket-form
141
+ *
137
142
  * @param {object} params Key-value pairs for query parameters
138
143
  * @return {string} Query string including leading '?' or empty string
139
144
  */
@@ -159,6 +164,27 @@ function buildQueryString(params = {}) {
159
164
  queryParams.append(arrayKey, String(item));
160
165
  }
161
166
  }
167
+ } else if (typeof value === 'object' && !key.startsWith('_')) {
168
+ // A filter operator: `{ deadline: { lt: '2026-10-06' } }` goes out
169
+ // as `deadline[lt]=2026-10-06`, the form OpenRegister reads. As
170
+ // JSON it reached the database as the literal value and a date
171
+ // column answered 500. Reserved `_` keys (`_order`) keep JSON.
172
+ for (const [op, operand] of Object.entries(value)) {
173
+ if (operand === undefined || operand === null || operand === '') {
174
+ continue
175
+ }
176
+ if (Array.isArray(operand)) {
177
+ for (const item of operand) {
178
+ if (item !== undefined && item !== null && item !== '') {
179
+ queryParams.append(`${key}[${op}][]`, String(item));
180
+ }
181
+ }
182
+ } else if (typeof operand === 'object') {
183
+ queryParams.set(`${key}[${op}]`, JSON.stringify(operand));
184
+ } else {
185
+ queryParams.set(`${key}[${op}]`, String(operand));
186
+ }
187
+ }
162
188
  } else if (typeof value === 'object') {
163
189
  queryParams.set(key, JSON.stringify(value));
164
190
  } else {
@@ -1 +1 @@
1
- {"version":3,"file":"headers.js","sources":["../../../src/utils/headers.js"],"sourcesContent":["import { generateUrl } from '@nextcloud/router'\n\n/**\n * Prefix an app-absolute URL path the way `generateUrl()` would: the instance\n * webroot, plus `/index.php` unless Nextcloud reports working URL rewriting.\n *\n * On an instance without pretty URLs the bare `/apps/<app>/api/...` form is\n * not routed at all and answers the web server's own 404, which is why every\n * internal API path has to pass through here or through `generateUrl()`.\n *\n * Only the PREFIX comes from `generateUrl()`: the path itself is appended\n * untouched, because `generateUrl()` URL-encodes any `{placeholder}` it cannot\n * fill, and callers pass templates filled later and JSON query values.\n *\n * Idempotent, and inert on anything that is not an app-absolute path, since it\n * is wrapped around hundreds of call sites: an absolute or protocol-relative\n * URL, a scheme (`data:`, `blob:`, `mailto:`), a document-relative path, or a\n * path already carrying the webroot or `/index.php` comes back unchanged.\n * `/ocs/`, `/remote.php` and `/public.php` get the webroot only, as they are\n * never served behind `/index.php`.\n *\n * @param {string} path URL path (e.g. '/apps/openregister/api/objects')\n * @return {string} The path as the instance routes it\n */\nexport function prefixUrl(path) {\n\tif (typeof path !== 'string' || path === '') {\n\t\treturn path\n\t}\n\t// Absolute URL, protocol-relative URL, or any scheme (data:, blob:, mailto:).\n\tif (/^[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith('//')) {\n\t\treturn path\n\t}\n\t// Only app-absolute paths are ours to prefix.\n\tif (!path.startsWith('/')) {\n\t\treturn path\n\t}\n\t// `generateUrl('/')` is `<webroot>[/index.php]/`; the webroot is that minus `/index.php`.\n\tconst prefix = generateUrl('/', {}).replace(/\\/$/, '')\n\tconst root = prefix.replace(/\\/index\\.php$/, '')\n\tif (path.startsWith('/index.php') || (root && (path === root || path.startsWith(root + '/')))) {\n\t\treturn path\n\t}\n\tif (/^\\/(ocs|remote\\.php|public\\.php)(\\/|$)/.test(path)) {\n\t\treturn root + path\n\t}\n\treturn prefix + path\n}\n\n/**\n * Build standard Nextcloud request headers for API calls.\n *\n * Includes the CSRF request token and OCS API request header\n * required by Nextcloud's API layer.\n *\n * Two call shapes are supported:\n *\n * 1. **Positional (back-compat)** — `buildHeaders(contentType)`.\n * Returns the same object the v1 signature did.\n *\n * 2. **Options object** — `buildHeaders({ contentType?, organisationUuid?, targetLanguage? })`.\n * When `organisationUuid` is a non-empty string the returned object\n * includes `X-OpenRegister-Organisation: <uuid>`. The header is the\n * FE side of the `multi-tenancy-context` capability and is consumed\n * by OR's MultiTenancyTrait.\n * When `targetLanguage` is a non-empty BCP-47 string the returned\n * object includes `X-Translation-Target-Language: <bcp47>`. The\n * header is the FE side of the `i18n-source-of-truth` write\n * contract — OR stamps the value into `_translations[<bcp47>]`\n * instead of overwriting the source row. See the\n * `i18n-language-negotiation-getters` change.\n *\n * @param {string|object} [opts] Content-Type string (back-compat) or options object.\n * @param {string} [opts.contentType] Content-Type header value\n * @param {string} [opts.organisationUuid] Active organisation UUID for the request\n * @param {string} [opts.targetLanguage] BCP-47 target language for translation writes\n * @return {object} Headers object for use with fetch()\n */\nexport function buildHeaders(opts = 'application/json') {\n\tlet contentType = 'application/json'\n\tlet organisationUuid = null\n\tlet targetLanguage = null\n\n\tif (typeof opts === 'string') {\n\t\t// v1 positional signature: buildHeaders(contentType)\n\t\tcontentType = opts\n\t} else if (opts === null) {\n\t\t// v1 positional signature: buildHeaders(null) — explicit \"no Content-Type\"\n\t\tcontentType = null\n\t} else if (opts && typeof opts === 'object') {\n\t\t// Options-object signature\n\t\tif (Object.hasOwn(opts, 'contentType')) {\n\t\t\tcontentType = opts.contentType\n\t\t}\n\t\tif (typeof opts.organisationUuid === 'string' && opts.organisationUuid.length > 0) {\n\t\t\torganisationUuid = opts.organisationUuid\n\t\t}\n\t\tif (typeof opts.targetLanguage === 'string' && opts.targetLanguage.length > 0) {\n\t\t\ttargetLanguage = opts.targetLanguage\n\t\t}\n\t}\n\n\tconst headers = {\n\t\trequesttoken: typeof OC !== 'undefined' ? OC.requestToken : '',\n\t\t'OCS-APIREQUEST': 'true',\n\t}\n\tif (contentType) {\n\t\theaders['Content-Type'] = contentType\n\t}\n\tif (organisationUuid) {\n\t\theaders['X-OpenRegister-Organisation'] = organisationUuid\n\t}\n\tif (targetLanguage) {\n\t\theaders['X-Translation-Target-Language'] = targetLanguage\n\t}\n\treturn headers\n}\n\n/**\n * Capitalize the first letter of a string.\n *\n * @param {string} str Input string\n * @return {string} Capitalized string\n */\nexport function capitalize(str) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Build a query string from a params object.\n *\n * Array values are serialized with PHP bracket notation (`key[]=a&key[]=b`)\n * so the Nextcloud/OpenRegister backend parses them as an array — a plain\n * repeated `key=a&key=b` collapses to the last value in PHP. Also handles\n * _order serialization (JSON.stringify for objects) and skips\n * null/undefined/empty values.\n *\n * @param {object} params Key-value pairs for query parameters\n * @return {string} Query string including leading '?' or empty string\n */\nexport function buildQueryString(params = {}) {\n\tconst queryParams = new URLSearchParams()\n\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (value === undefined || value === null || value === '') {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(value) && value.length === 0) {\n\t\t\tcontinue\n\t\t}\n\t\tif (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\t// PHP needs `key[]` to receive repeated params as an array;\n\t\t\t// without the brackets only the last value survives server-side.\n\t\t\tconst arrayKey = key.endsWith('[]') ? key : `${key}[]`\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item !== undefined && item !== null && item !== '') {\n\t\t\t\t\tqueryParams.append(arrayKey, String(item))\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (typeof value === 'object') {\n\t\t\tqueryParams.set(key, JSON.stringify(value))\n\t\t} else {\n\t\t\tqueryParams.set(key, String(value))\n\t\t}\n\t}\n\n\tconst str = queryParams.toString()\n\treturn str ? '?' + str : ''\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"headers.js","sources":["../../../src/utils/headers.js"],"sourcesContent":["import { generateUrl } from '@nextcloud/router'\n\n/**\n * Prefix an app-absolute URL path the way `generateUrl()` would: the instance\n * webroot, plus `/index.php` unless Nextcloud reports working URL rewriting.\n *\n * On an instance without pretty URLs the bare `/apps/<app>/api/...` form is\n * not routed at all and answers the web server's own 404, which is why every\n * internal API path has to pass through here or through `generateUrl()`.\n *\n * Only the PREFIX comes from `generateUrl()`: the path itself is appended\n * untouched, because `generateUrl()` URL-encodes any `{placeholder}` it cannot\n * fill, and callers pass templates filled later and JSON query values.\n *\n * Idempotent, and inert on anything that is not an app-absolute path, since it\n * is wrapped around hundreds of call sites: an absolute or protocol-relative\n * URL, a scheme (`data:`, `blob:`, `mailto:`), a document-relative path, or a\n * path already carrying the webroot or `/index.php` comes back unchanged.\n * `/ocs/`, `/remote.php` and `/public.php` get the webroot only, as they are\n * never served behind `/index.php`.\n *\n * @param {string} path URL path (e.g. '/apps/openregister/api/objects')\n * @return {string} The path as the instance routes it\n */\nexport function prefixUrl(path) {\n\tif (typeof path !== 'string' || path === '') {\n\t\treturn path\n\t}\n\t// Absolute URL, protocol-relative URL, or any scheme (data:, blob:, mailto:).\n\tif (/^[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith('//')) {\n\t\treturn path\n\t}\n\t// Only app-absolute paths are ours to prefix.\n\tif (!path.startsWith('/')) {\n\t\treturn path\n\t}\n\t// `generateUrl('/')` is `<webroot>[/index.php]/`; the webroot is that minus `/index.php`.\n\tconst prefix = generateUrl('/', {}).replace(/\\/$/, '')\n\tconst root = prefix.replace(/\\/index\\.php$/, '')\n\tif (path.startsWith('/index.php') || (root && (path === root || path.startsWith(root + '/')))) {\n\t\treturn path\n\t}\n\tif (/^\\/(ocs|remote\\.php|public\\.php)(\\/|$)/.test(path)) {\n\t\treturn root + path\n\t}\n\treturn prefix + path\n}\n\n/**\n * Build standard Nextcloud request headers for API calls.\n *\n * Includes the CSRF request token and OCS API request header\n * required by Nextcloud's API layer.\n *\n * Two call shapes are supported:\n *\n * 1. **Positional (back-compat)** — `buildHeaders(contentType)`.\n * Returns the same object the v1 signature did.\n *\n * 2. **Options object** — `buildHeaders({ contentType?, organisationUuid?, targetLanguage? })`.\n * When `organisationUuid` is a non-empty string the returned object\n * includes `X-OpenRegister-Organisation: <uuid>`. The header is the\n * FE side of the `multi-tenancy-context` capability and is consumed\n * by OR's MultiTenancyTrait.\n * When `targetLanguage` is a non-empty BCP-47 string the returned\n * object includes `X-Translation-Target-Language: <bcp47>`. The\n * header is the FE side of the `i18n-source-of-truth` write\n * contract — OR stamps the value into `_translations[<bcp47>]`\n * instead of overwriting the source row. See the\n * `i18n-language-negotiation-getters` change.\n *\n * @param {string|object} [opts] Content-Type string (back-compat) or options object.\n * @param {string} [opts.contentType] Content-Type header value\n * @param {string} [opts.organisationUuid] Active organisation UUID for the request\n * @param {string} [opts.targetLanguage] BCP-47 target language for translation writes\n * @return {object} Headers object for use with fetch()\n */\nexport function buildHeaders(opts = 'application/json') {\n\tlet contentType = 'application/json'\n\tlet organisationUuid = null\n\tlet targetLanguage = null\n\n\tif (typeof opts === 'string') {\n\t\t// v1 positional signature: buildHeaders(contentType)\n\t\tcontentType = opts\n\t} else if (opts === null) {\n\t\t// v1 positional signature: buildHeaders(null) — explicit \"no Content-Type\"\n\t\tcontentType = null\n\t} else if (opts && typeof opts === 'object') {\n\t\t// Options-object signature\n\t\tif (Object.hasOwn(opts, 'contentType')) {\n\t\t\tcontentType = opts.contentType\n\t\t}\n\t\tif (typeof opts.organisationUuid === 'string' && opts.organisationUuid.length > 0) {\n\t\t\torganisationUuid = opts.organisationUuid\n\t\t}\n\t\tif (typeof opts.targetLanguage === 'string' && opts.targetLanguage.length > 0) {\n\t\t\ttargetLanguage = opts.targetLanguage\n\t\t}\n\t}\n\n\tconst headers = {\n\t\trequesttoken: typeof OC !== 'undefined' ? OC.requestToken : '',\n\t\t'OCS-APIREQUEST': 'true',\n\t}\n\tif (contentType) {\n\t\theaders['Content-Type'] = contentType\n\t}\n\tif (organisationUuid) {\n\t\theaders['X-OpenRegister-Organisation'] = organisationUuid\n\t}\n\tif (targetLanguage) {\n\t\theaders['X-Translation-Target-Language'] = targetLanguage\n\t}\n\treturn headers\n}\n\n/**\n * Capitalize the first letter of a string.\n *\n * @param {string} str Input string\n * @return {string} Capitalized string\n */\nexport function capitalize(str) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Build a query string from a params object.\n *\n * Array values are serialized with PHP bracket notation (`key[]=a&key[]=b`)\n * so the Nextcloud/OpenRegister backend parses them as an array — a plain\n * repeated `key=a&key=b` collapses to the last value in PHP. A plain object\n * under a filter key is a set of operators and goes out in bracket form\n * (`{ deadline: { lt: 'x' } }` becomes `deadline[lt]=x`); a key already\n * written that way (`'deadline[lt]'`) passes through unchanged. Reserved keys\n * that start with `_` keep their JSON form (`_order`). Skips\n * null/undefined/empty values.\n *\n * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-filter-operator-is-serialised-in-bracket-form\n *\n * @param {object} params Key-value pairs for query parameters\n * @return {string} Query string including leading '?' or empty string\n */\nexport function buildQueryString(params = {}) {\n\tconst queryParams = new URLSearchParams()\n\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (value === undefined || value === null || value === '') {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(value) && value.length === 0) {\n\t\t\tcontinue\n\t\t}\n\t\tif (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\t// PHP needs `key[]` to receive repeated params as an array;\n\t\t\t// without the brackets only the last value survives server-side.\n\t\t\tconst arrayKey = key.endsWith('[]') ? key : `${key}[]`\n\t\t\tfor (const item of value) {\n\t\t\t\tif (item !== undefined && item !== null && item !== '') {\n\t\t\t\t\tqueryParams.append(arrayKey, String(item))\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (typeof value === 'object' && !key.startsWith('_')) {\n\t\t\t// A filter operator: `{ deadline: { lt: '2026-10-06' } }` goes out\n\t\t\t// as `deadline[lt]=2026-10-06`, the form OpenRegister reads. As\n\t\t\t// JSON it reached the database as the literal value and a date\n\t\t\t// column answered 500. Reserved `_` keys (`_order`) keep JSON.\n\t\t\tfor (const [op, operand] of Object.entries(value)) {\n\t\t\t\tif (operand === undefined || operand === null || operand === '') {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(operand)) {\n\t\t\t\t\tfor (const item of operand) {\n\t\t\t\t\t\tif (item !== undefined && item !== null && item !== '') {\n\t\t\t\t\t\t\tqueryParams.append(`${key}[${op}][]`, String(item))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (typeof operand === 'object') {\n\t\t\t\t\tqueryParams.set(`${key}[${op}]`, JSON.stringify(operand))\n\t\t\t\t} else {\n\t\t\t\t\tqueryParams.set(`${key}[${op}]`, String(operand))\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (typeof value === 'object') {\n\t\t\tqueryParams.set(key, JSON.stringify(value))\n\t\t} else {\n\t\t\tqueryParams.set(key, String(value))\n\t\t}\n\t}\n\n\tconst str = queryParams.toString()\n\treturn str ? '?' + str : ''\n}\n"],"names":[],"mappings":"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"}