@conduction/nextcloud-vue 2.69.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.
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +25 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +155 -152
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +77 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +8 -8
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +151 -13
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -0
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js +28 -0
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +4 -2
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js +25 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js +32 -17
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue2.js +14 -1
- package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue2.js.map +1 -1
- package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +43 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +3 -3
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +25 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +40 -43
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +35 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +54 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +6 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +28 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +3 -2
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js +12 -0
- package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +9 -0
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +29 -2
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +71 -34
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +54 -7
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +26 -8
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +165 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +69 -26
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +4 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +46 -32
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +17 -3
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +25 -24
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +27 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +7 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +24 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +11 -0
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +4 -2
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +30 -9
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +15 -2
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +31 -32
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useWalkthrough.js +11 -3
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +198 -0
- package/dist/esm/utils/capabilityComparison.js +29 -1
- package/dist/esm/utils/capabilityComparison.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +54 -4
- package/dist/esm/utils/columnFilters.js.map +1 -1
- package/dist/esm/utils/userAutocomplete.js +69 -3
- package/dist/esm/utils/userAutocomplete.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +2778 -1630
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +198 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +2 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +28 -2
- package/src/components/CnAppNav/CnAppNav.vue +85 -8
- package/src/components/CnAppRoot/CnAppRoot.vue +153 -13
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +45 -0
- package/src/components/CnCalendarWidget/CnCalendarWidget.vue +32 -4
- package/src/components/CnCapabilityTable/CnCapabilityTable.vue +14 -0
- package/src/components/CnDashboardGrid/CnDashboardGrid.md +2 -0
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +54 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +46 -19
- package/src/components/CnDataTable/CnDataTable.vue +35 -1
- package/src/components/CnDetailPage/CnDetailPage.md +2 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +57 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +30 -1
- package/src/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue +12 -0
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +9 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +62 -2
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +95 -8
- package/src/components/CnIndexPage/CnIndexPage.vue +194 -7
- package/src/components/CnNextStepCard/CnNextStepCard.vue +11 -0
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +4 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +1 -1
- package/src/components/CnSetupWizard/CnSetupWizard.vue +7 -0
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +34 -7
- package/src/components/CnStagesWidget/CnStagesWidget.vue +19 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +36 -0
- package/src/components/CnStatsBlock/CnStatsBlock.md +2 -0
- package/src/components/CnStatsBlock/CnStatsBlock.vue +25 -2
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +13 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +30 -9
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +24 -3
- package/src/composables/useWalkthrough.js +11 -3
- package/src/css/dashboard.css +44 -0
- package/src/css/detail-page.css +10 -0
- package/src/css/index-page.css +12 -0
- package/src/css/kpi-card.css +15 -0
- package/src/schemas/app-manifest-v2.schema.json +47 -5
- package/src/utils/capabilityComparison.js +28 -0
- package/src/utils/columnFilters.js +54 -4
- package/src/utils/userAutocomplete.js +68 -2
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFeaturesAndRoadmapPage.vue2.js","sources":["../../../../src/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue"],"sourcesContent":["<!--\n CnFeaturesAndRoadmapPage — Manifest-driven Features & Roadmap surface.\n\n v2 manifest page primitive for `type: \"roadmap\"`. Wraps\n `CnFeaturesAndRoadmapView` so consuming apps can declare\n `{ id: \"FeaturesRoadmap\", route: \"/features-roadmap\", type: \"roadmap\" }`\n in their manifest without writing a per-app wrapper that just\n unpacks initialState into the view's three props.\n\n Resolution order for each of the three values:\n\n 1. Explicit `pages[].config.<key>` from the manifest.\n 2. `loadState(appId, 'features_roadmap_<key>', <fallback>)`.\n 3. Hardcoded fallback (`repo` → `ConductionNL/<appId>` on GitHub;\n `forge` → the `cnFeatureRequestForge` inject, else DEFAULT_FORGE).\n\n `appId` comes from the `cnAiContext` inject that CnAppRoot already\n provides; consumers can override it explicitly via the prop for tests.\n\n Spec: ConductionNL/nextcloud-vue#264.\n-->\n<template>\n\t<CnFeaturesAndRoadmapView\n\t\t:repo=\"resolvedRepo\"\n\t\t:forge=\"resolvedForge\"\n\t\t:features=\"resolvedFeatures\"\n\t\t:disabled=\"resolvedDisabled\"\n\t\t:openbuiltUrl=\"resolvedOpenbuiltUrl\"\n\t\t:llmSkillsUrl=\"resolvedLlmSkillsUrl\"\n\t\t:suggestUrl=\"resolvedSuggestUrl\"\n\t\t:documentationUrl=\"resolvedDocumentationUrl\"\n\t\t:capabilityComparison=\"resolvedCapabilityComparison\" />\n</template>\n\n<script>\n// `@nextcloud/initial-state` is a DECLARED (non-optional) peer dependency, so\n// it is imported statically — a lazy CommonJS resolution can never succeed from\n// `dist/esm/**` (the package's `exports` map is import-only and the ESM build\n// has no such function), which meant this helper used to return its fallback\n// unconditionally.\nimport { loadState } from '@nextcloud/initial-state'\nimport CnFeaturesAndRoadmapView from '../CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue'\nimport { DEFAULT_FORGE } from '../../utils/forge.js'\n\n/**\n * Read a key from `@nextcloud/initial-state`, tolerating an unprovisioned\n * slot (mirrors resolveManifestSentinels.js).\n *\n * @param {string} appId Nextcloud app ID.\n * @param {string} key Initial-state key (full key, not prefixed).\n * @param {unknown} fallback Default value when no provisioned slot exists.\n * @return {unknown} Provisioned value or the fallback.\n */\nfunction readInitialState(appId, key, fallback) {\n\ttry {\n\t\t// `loadState` throws only when the `#initial-state-*` element is absent\n\t\t// AND no fallback is given; a fallback IS given here, so this catch\n\t\t// covers the parse-error case only. It is a runtime condition, never a\n\t\t// module-resolution failure.\n\t\treturn loadState(appId, key, fallback)\n\t} catch {\n\t\t// Slot present but unparseable — fall back.\n\t}\n\treturn fallback\n}\n\nexport default {\n\tname: 'CnFeaturesAndRoadmapPage',\n\n\tcomponents: {\n\t\tCnFeaturesAndRoadmapView,\n\t},\n\n\tinject: {\n\t\tcnAiContext: {\n\t\t\tdefault: () => ({ appId: 'unknown' }),\n\t\t},\n\n\t\t/**\n\t\t * Forge config provided by CnAppRoot from `manifest.nav.forge`.\n\t\t * Used as the fallback for `resolvedForge` so the whole app shares\n\t\t * one forge setting. Defaults to Codeberg when no ancestor.\n\t\t */\n\t\tcnFeatureRequestForge: {\n\t\t\tdefault: () => ({ ...DEFAULT_FORGE }),\n\t\t},\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * `<owner>/<repo>` of the app's repository on the forge. When\n\t\t * omitted, falls back to the loadState value, then to\n\t\t * `ConductionNL/<appId>` — the fleet convention on GitHub.\n\t\t */\n\t\trepo: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Target forge for the feature-request deep-link. Manifest config\n\t\t * > initialState > the `cnFeatureRequestForge` inject (CnAppRoot)\n\t\t * > Codeberg.\n\t\t *\n\t\t * @type {{type: 'codeberg'|'forgejo'|'gitea'|'github', baseUrl?: string}|null}\n\t\t */\n\t\tforge: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Build-time feature manifest. When omitted, falls back to\n\t\t * the loadState value, then to an empty array.\n\t\t *\n\t\t * @type {Array<{slug: string, title: string, summary: string, docsUrl: string}>}\n\t\t */\n\t\tfeatures: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Admin opt-out flag. When omitted, falls back to the\n\t\t * loadState value, then to false.\n\t\t *\n\t\t * @type {boolean|null}\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override for the OpenBuilt sidebar CTA target. Manifest config\n\t\t * > initialState > the view's own default (in-instance /apps/openbuilt).\n\t\t */\n\t\topenbuiltUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override for the LLM-skills sidebar CTA target. Manifest config\n\t\t * > initialState > docs.conduction.nl/ai-skills.\n\t\t */\n\t\tllmSkillsUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override for the Suggest sidebar CTA. When set, the sidebar\n\t\t * Suggest CTA renders as an anchor pointing here; when empty\n\t\t * (default) the view derives the forge's feature-request issue\n\t\t * form URL. Manifest config > initialState > '' (forge form).\n\t\t */\n\t\tsuggestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * URL of the app's public documentation site. When set, an info\n\t\t * banner above the card grid points users at the docs. Manifest\n\t\t * config > initialState > '' (no banner).\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional capability comparison document, forwarded to the view.\n\t\t * Manifest config > initialState (`features_roadmap_capabilities`) >\n\t\t * null. When null the view renders its two stops as it always has.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tcapabilityComparison: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override for the appId used to namespace loadState lookups.\n\t\t * Tests pass an explicit value; production reads it from the\n\t\t * `cnAiContext` inject populated by CnAppRoot.\n\t\t */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective app id used to namespace `loadState` calls.\n\t\t * Prop takes precedence over the cnAiContext inject so that\n\t\t * unit tests can pass a known id without mocking the inject.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\teffectiveAppId() {\n\t\t\treturn this.appId || this.cnAiContext?.appId || 'unknown'\n\t\t},\n\n\t\t/**\n\t\t * Effective repo. Manifest config > initialState > fallback.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedRepo() {\n\t\t\tif (this.repo) {\n\t\t\t\treturn this.repo\n\t\t\t}\n\t\t\treturn readInitialState(\n\t\t\t\tthis.effectiveAppId,\n\t\t\t\t'features_roadmap_repo',\n\t\t\t\t`ConductionNL/${this.effectiveAppId}`,\n\t\t\t)\n\t\t},\n\n\t\t/**\n\t\t * Effective forge. Manifest config > initialState >\n\t\t * `cnFeatureRequestForge` inject (CnAppRoot) > DEFAULT_FORGE.\n\t\t *\n\t\t * @return {{type: string, baseUrl?: string}}\n\t\t */\n\t\tresolvedForge() {\n\t\t\tif (this.forge) {\n\t\t\t\treturn this.forge\n\t\t\t}\n\t\t\treturn readInitialState(\n\t\t\t\tthis.effectiveAppId,\n\t\t\t\t'features_roadmap_forge',\n\t\t\t\tthis.cnFeatureRequestForge || { ...DEFAULT_FORGE },\n\t\t\t)\n\t\t},\n\n\t\t/**\n\t\t * Effective features list. Manifest config > initialState > [].\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\tresolvedFeatures() {\n\t\t\tif (this.features !== null) {\n\t\t\t\treturn this.features\n\t\t\t}\n\t\t\treturn readInitialState(this.effectiveAppId, 'features_roadmap_features', [])\n\t\t},\n\n\t\t/**\n\t\t * Effective disabled flag. Manifest config > initialState > false.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tresolvedDisabled() {\n\t\t\tif (this.disabled !== null) {\n\t\t\t\treturn this.disabled\n\t\t\t}\n\t\t\treturn readInitialState(this.effectiveAppId, 'features_roadmap_disabled', false)\n\t\t},\n\n\t\tresolvedOpenbuiltUrl() {\n\t\t\treturn this.openbuiltUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_openbuilt_url', '')\n\t\t},\n\n\t\tresolvedLlmSkillsUrl() {\n\t\t\treturn this.llmSkillsUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_llm_skills_url', '')\n\t\t},\n\n\t\tresolvedSuggestUrl() {\n\t\t\treturn this.suggestUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_suggest_url', '')\n\t\t},\n\n\t\tresolvedDocumentationUrl() {\n\t\t\treturn this.documentationUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_documentation_url', '')\n\t\t},\n\n\t\t/**\n\t\t * Effective capability comparison. Manifest config > initialState >\n\t\t * null.\n\t\t *\n\t\t * `null` and not `{}`: an empty object would make the view believe a\n\t\t * comparison exists and grow a third toggle stop over zero rows.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tresolvedCapabilityComparison() {\n\t\t\tif (this.capabilityComparison !== null) {\n\t\t\t\treturn this.capabilityComparison\n\t\t\t}\n\t\t\treturn readInitialState(this.effectiveAppId, 'features_roadmap_capabilities', null)\n\t\t},\n\t},\n}\n</script>\n"],"names":["CnFeaturesAndRoadmapView"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFeaturesAndRoadmapPage.vue2.js","sources":["../../../../src/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue"],"sourcesContent":["<!--\n CnFeaturesAndRoadmapPage — Manifest-driven Features & Roadmap surface.\n\n v2 manifest page primitive for `type: \"roadmap\"`. Wraps\n `CnFeaturesAndRoadmapView` so consuming apps can declare\n `{ id: \"FeaturesRoadmap\", route: \"/features-roadmap\", type: \"roadmap\" }`\n in their manifest without writing a per-app wrapper that just\n unpacks initialState into the view's three props.\n\n Resolution order for each of the three values:\n\n 1. Explicit `pages[].config.<key>` from the manifest.\n 2. `loadState(appId, 'features_roadmap_<key>', <fallback>)`.\n 3. Hardcoded fallback (`repo` → `ConductionNL/<appId>` on GitHub;\n `forge` → the `cnFeatureRequestForge` inject, else DEFAULT_FORGE).\n\n `appId` comes from the `cnAiContext` inject that CnAppRoot already\n provides; consumers can override it explicitly via the prop for tests.\n\n Spec: ConductionNL/nextcloud-vue#264.\n-->\n<template>\n\t<CnFeaturesAndRoadmapView\n\t\t:repo=\"resolvedRepo\"\n\t\t:forge=\"resolvedForge\"\n\t\t:features=\"resolvedFeatures\"\n\t\t:disabled=\"resolvedDisabled\"\n\t\t:openbuiltUrl=\"resolvedOpenbuiltUrl\"\n\t\t:llmSkillsUrl=\"resolvedLlmSkillsUrl\"\n\t\t:suggestUrl=\"resolvedSuggestUrl\"\n\t\t:documentationUrl=\"resolvedDocumentationUrl\"\n\t\t:capabilityComparison=\"resolvedCapabilityComparison\" />\n</template>\n\n<script>\n// `@nextcloud/initial-state` is a DECLARED (non-optional) peer dependency, so\n// it is imported statically — a lazy CommonJS resolution can never succeed from\n// `dist/esm/**` (the package's `exports` map is import-only and the ESM build\n// has no such function), which meant this helper used to return its fallback\n// unconditionally.\nimport { loadState } from '@nextcloud/initial-state'\nimport CnFeaturesAndRoadmapView from '../CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue'\nimport { warnCapabilityComparisonDeprecated } from '../../utils/capabilityComparison.js'\nimport { DEFAULT_FORGE } from '../../utils/forge.js'\n\n/**\n * Read a key from `@nextcloud/initial-state`, tolerating an unprovisioned\n * slot (mirrors resolveManifestSentinels.js).\n *\n * @param {string} appId Nextcloud app ID.\n * @param {string} key Initial-state key (full key, not prefixed).\n * @param {unknown} fallback Default value when no provisioned slot exists.\n * @return {unknown} Provisioned value or the fallback.\n */\nfunction readInitialState(appId, key, fallback) {\n\ttry {\n\t\t// `loadState` throws only when the `#initial-state-*` element is absent\n\t\t// AND no fallback is given; a fallback IS given here, so this catch\n\t\t// covers the parse-error case only. It is a runtime condition, never a\n\t\t// module-resolution failure.\n\t\treturn loadState(appId, key, fallback)\n\t} catch {\n\t\t// Slot present but unparseable — fall back.\n\t}\n\treturn fallback\n}\n\nexport default {\n\tname: 'CnFeaturesAndRoadmapPage',\n\n\tcomponents: {\n\t\tCnFeaturesAndRoadmapView,\n\t},\n\n\tinject: {\n\t\tcnAiContext: {\n\t\t\tdefault: () => ({ appId: 'unknown' }),\n\t\t},\n\n\t\t/**\n\t\t * Forge config provided by CnAppRoot from `manifest.nav.forge`.\n\t\t * Used as the fallback for `resolvedForge` so the whole app shares\n\t\t * one forge setting. Defaults to Codeberg when no ancestor.\n\t\t */\n\t\tcnFeatureRequestForge: {\n\t\t\tdefault: () => ({ ...DEFAULT_FORGE }),\n\t\t},\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * `<owner>/<repo>` of the app's repository on the forge. When\n\t\t * omitted, falls back to the loadState value, then to\n\t\t * `ConductionNL/<appId>` — the fleet convention on GitHub.\n\t\t */\n\t\trepo: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Target forge for the feature-request deep-link. Manifest config\n\t\t * > initialState > the `cnFeatureRequestForge` inject (CnAppRoot)\n\t\t * > Codeberg.\n\t\t *\n\t\t * @type {{type: 'codeberg'|'forgejo'|'gitea'|'github', baseUrl?: string}|null}\n\t\t */\n\t\tforge: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Build-time feature manifest. When omitted, falls back to\n\t\t * the loadState value, then to an empty array.\n\t\t *\n\t\t * @type {Array<{slug: string, title: string, summary: string, docsUrl: string}>}\n\t\t */\n\t\tfeatures: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Admin opt-out flag. When omitted, falls back to the\n\t\t * loadState value, then to false.\n\t\t *\n\t\t * @type {boolean|null}\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override for the OpenBuilt sidebar CTA target. Manifest config\n\t\t * > initialState > the view's own default (in-instance /apps/openbuilt).\n\t\t */\n\t\topenbuiltUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override for the LLM-skills sidebar CTA target. Manifest config\n\t\t * > initialState > docs.conduction.nl/ai-skills.\n\t\t */\n\t\tllmSkillsUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override for the Suggest sidebar CTA. When set, the sidebar\n\t\t * Suggest CTA renders as an anchor pointing here; when empty\n\t\t * (default) the view derives the forge's feature-request issue\n\t\t * form URL. Manifest config > initialState > '' (forge form).\n\t\t */\n\t\tsuggestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * URL of the app's public documentation site. When set, an info\n\t\t * banner above the card grid points users at the docs. Manifest\n\t\t * config > initialState > '' (no banner).\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional capability comparison document, forwarded to the view.\n\t\t * Manifest config > initialState (`features_roadmap_capabilities`) >\n\t\t * null. When null the view renders its two stops as it always has.\n\t\t *\n\t\t * Deprecated: publish the comparison on the app's docs site and link to it (as dossiq #3312 and pipelinq #2195 did). Still rendered; removal waits for a major version. The same goes for the `features_roadmap_capabilities` initial\n\t\t * state key. A development build warns once when either is set.\n\t\t *\n\t\t * @deprecated since 2.66.0, publish the comparison on the app's docs site and link to it.\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-the-in-app-capability-comparison-is-deprecated\n\t\t * @type {object|null}\n\t\t */\n\t\tcapabilityComparison: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override for the appId used to namespace loadState lookups.\n\t\t * Tests pass an explicit value; production reads it from the\n\t\t * `cnAiContext` inject populated by CnAppRoot.\n\t\t */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective app id used to namespace `loadState` calls.\n\t\t * Prop takes precedence over the cnAiContext inject so that\n\t\t * unit tests can pass a known id without mocking the inject.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\teffectiveAppId() {\n\t\t\treturn this.appId || this.cnAiContext?.appId || 'unknown'\n\t\t},\n\n\t\t/**\n\t\t * Effective repo. Manifest config > initialState > fallback.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedRepo() {\n\t\t\tif (this.repo) {\n\t\t\t\treturn this.repo\n\t\t\t}\n\t\t\treturn readInitialState(\n\t\t\t\tthis.effectiveAppId,\n\t\t\t\t'features_roadmap_repo',\n\t\t\t\t`ConductionNL/${this.effectiveAppId}`,\n\t\t\t)\n\t\t},\n\n\t\t/**\n\t\t * Effective forge. Manifest config > initialState >\n\t\t * `cnFeatureRequestForge` inject (CnAppRoot) > DEFAULT_FORGE.\n\t\t *\n\t\t * @return {{type: string, baseUrl?: string}}\n\t\t */\n\t\tresolvedForge() {\n\t\t\tif (this.forge) {\n\t\t\t\treturn this.forge\n\t\t\t}\n\t\t\treturn readInitialState(\n\t\t\t\tthis.effectiveAppId,\n\t\t\t\t'features_roadmap_forge',\n\t\t\t\tthis.cnFeatureRequestForge || { ...DEFAULT_FORGE },\n\t\t\t)\n\t\t},\n\n\t\t/**\n\t\t * Effective features list. Manifest config > initialState > [].\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\tresolvedFeatures() {\n\t\t\tif (this.features !== null) {\n\t\t\t\treturn this.features\n\t\t\t}\n\t\t\treturn readInitialState(this.effectiveAppId, 'features_roadmap_features', [])\n\t\t},\n\n\t\t/**\n\t\t * Effective disabled flag. Manifest config > initialState > false.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tresolvedDisabled() {\n\t\t\tif (this.disabled !== null) {\n\t\t\t\treturn this.disabled\n\t\t\t}\n\t\t\treturn readInitialState(this.effectiveAppId, 'features_roadmap_disabled', false)\n\t\t},\n\n\t\tresolvedOpenbuiltUrl() {\n\t\t\treturn this.openbuiltUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_openbuilt_url', '')\n\t\t},\n\n\t\tresolvedLlmSkillsUrl() {\n\t\t\treturn this.llmSkillsUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_llm_skills_url', '')\n\t\t},\n\n\t\tresolvedSuggestUrl() {\n\t\t\treturn this.suggestUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_suggest_url', '')\n\t\t},\n\n\t\tresolvedDocumentationUrl() {\n\t\t\treturn this.documentationUrl\n\t\t\t\t|| readInitialState(this.effectiveAppId, 'features_roadmap_documentation_url', '')\n\t\t},\n\n\t\t/**\n\t\t * Effective capability comparison. Manifest config > initialState >\n\t\t * null.\n\t\t *\n\t\t * `null` and not `{}`: an empty object would make the view believe a\n\t\t * comparison exists and grow a third toggle stop over zero rows.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tresolvedCapabilityComparison() {\n\t\t\tif (this.capabilityComparison !== null) {\n\t\t\t\treturn this.capabilityComparison\n\t\t\t}\n\t\t\treturn readInitialState(this.effectiveAppId, 'features_roadmap_capabilities', null)\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (this.resolvedCapabilityComparison) {\n\t\t\twarnCapabilityComparisonDeprecated('CnFeaturesAndRoadmapPage `capabilityComparison` / the `features_roadmap_capabilities` initial state')\n\t\t}\n\t},\n}\n</script>\n"],"names":["CnFeaturesAndRoadmapView"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import __sfc_main$4 from '../CnFeaturesAndRoadmapSidebar/CnFeaturesAndRoadmapSid
|
|
|
12
12
|
import __sfc_main$1 from '../CnFeaturesTab/CnFeaturesTab.vue.js';
|
|
13
13
|
import __sfc_main$2 from '../CnRoadmapTab/CnRoadmapTab.vue.js';
|
|
14
14
|
import __sfc_main$3 from '../CnSupportDialog/CnSupportDialog.vue.js';
|
|
15
|
+
import { warnCapabilityComparisonDeprecated } from '../../utils/capabilityComparison.js';
|
|
15
16
|
import { DEFAULT_FORGE, buildFeatureRequestUrl } from '../../utils/forge.js';
|
|
16
17
|
|
|
17
18
|
const DEFAULT_OPENBUILT_PATH = '/apps/openbuilt';
|
|
@@ -112,6 +113,11 @@ var script = {
|
|
|
112
113
|
* When null (default) the toggle keeps its two stops and the view
|
|
113
114
|
* renders exactly as it does for every app that passes nothing.
|
|
114
115
|
*
|
|
116
|
+
* Deprecated: publish the comparison on the app's docs site and link to it (as dossiq #3312 and pipelinq #2195 did). Still rendered; removal waits for a major version.
|
|
117
|
+
* A development build warns once when it is set.
|
|
118
|
+
*
|
|
119
|
+
* @deprecated since 2.66.0, publish the comparison on the app's docs site and link to it.
|
|
120
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-the-in-app-capability-comparison-is-deprecated
|
|
115
121
|
* @type {object|null}
|
|
116
122
|
*/
|
|
117
123
|
capabilityComparison: {
|
|
@@ -425,6 +431,9 @@ var script = {
|
|
|
425
431
|
|
|
426
432
|
mounted() {
|
|
427
433
|
this.publishHoistedSidebar();
|
|
434
|
+
if (this.capabilityComparison) {
|
|
435
|
+
warnCapabilityComparisonDeprecated('CnFeaturesAndRoadmapView `capabilityComparison`');
|
|
436
|
+
}
|
|
428
437
|
},
|
|
429
438
|
|
|
430
439
|
beforeUnmount() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFeaturesAndRoadmapView.vue2.js","sources":["../../../../src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-features-and-roadmap-view\">\n\t\t<div v-if=\"disabled\" class=\"cn-features-and-roadmap-view__disabled\">\n\t\t\t<NcEmptyContent :name=\"disabledTitle\" :description=\"disabledDescription\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LockOutline :size=\"48\" />\n\t\t\t\t</template>\n\t\t\t</NcEmptyContent>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<header class=\"cn-features-and-roadmap-view__header\">\n\t\t\t\t<h2 class=\"cn-features-and-roadmap-view__title\">\n\t\t\t\t\t{{ headerTitle }}\n\t\t\t\t</h2>\n\t\t\t\t<div class=\"cn-features-and-roadmap-view__actions\">\n\t\t\t\t\t<NcButton @click=\"toggleView\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TableSearch v-if=\"nextView === 'capabilities'\" :size=\"20\" />\n\t\t\t\t\t\t\t<RoadVariant v-else-if=\"nextView === 'roadmap'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FormatListBulleted v-else :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ toggleLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<!-- Direct link to the forge's feature-request issue form\n\t\t\t\t\t (team decision 2026-09-04: the in-product suggestion\n\t\t\t\t\t modal is gone — the forge is where the conversation\n\t\t\t\t\t happens, in English). -->\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"resolvedSuggestUrl\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"resolvedSuggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ suggestLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</header>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"resolvedDocumentationUrl\"\n\t\t\t\ttype=\"info\"\n\t\t\t\tclass=\"cn-features-and-roadmap-view__docs-note\">\n\t\t\t\t{{ docsNoteLeading }}\n\t\t\t\t<a\n\t\t\t\t\t:href=\"resolvedDocumentationUrl\"\n\t\t\t\t\t:target=\"documentationUrlIsExternal ? '_blank' : null\"\n\t\t\t\t\t:rel=\"documentationUrlIsExternal ? 'noopener noreferrer' : null\">{{ docsNoteLinkLabel }}</a>\n\t\t\t\t{{ docsNoteTrailing }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<main class=\"cn-features-and-roadmap-view__panel\">\n\t\t\t\t<CnFeaturesTab v-if=\"activeView === 'features'\" :features=\"features\" />\n\t\t\t\t<CnCapabilityTable\n\t\t\t\t\tv-else-if=\"activeView === 'capabilities'\"\n\t\t\t\t\t:comparison=\"capabilityComparison\" />\n\t\t\t\t<CnRoadmapTab v-else :repo=\"repo\" />\n\t\t\t</main>\n\n\t\t\t<CnSupportDialog\n\t\t\t\tv-if=\"showSupportDialog\"\n\t\t\t\t:appName=\"resolvedAppName\"\n\t\t\t\t:appSlug=\"resolvedAppSlug\"\n\t\t\t\t:appStoreUrl=\"resolvedAppStoreUrl\"\n\t\t\t\t:featureRequestUrl=\"resolvedFeatureRequestUrl\"\n\t\t\t\t:donateUrl=\"donateUrl\"\n\t\t\t\t:supportUrl=\"supportUrl\"\n\t\t\t\t:founderName=\"founderName\"\n\t\t\t\t:founderTitle=\"founderTitle\"\n\t\t\t\t@close=\"showSupportDialog = false\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\n/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * CnFeaturesAndRoadmapView — route-level container for the Features &\n * Roadmap surface. Header carries the view-toggle and the primary\n * Suggest-feature CTA. Body is a card grid (Features OR Roadmap).\n *\n * The right-edge sidebar is hoisted to NcContent level via the same\n * `cnIndexSidebarConfig` provide mechanism CnIndexPage uses for its\n * CnIndexSidebar — that's the only place where Nextcloud's right-edge\n * sidebar slot renders correctly (next to NcAppContent, not inside it).\n * When mounted under CnAppRoot the view publishes\n * `CnFeaturesAndRoadmapSidebar` + props into the holder on mounted()\n * and clears it on beforeUnmount(). Sidebar carries four pitch\n * sections: Suggest, OpenBuilt, LLM, Support. Suggest renders as an\n * anchor to the forge's feature-request issue form (`resolvedSuggestUrl`\n * is always passed); Support emits `@support` (forwarded to a\n * freshly-mounted `CnSupportDialog`).\n *\n * Sidebar link targets are overridable via the `openbuiltUrl` and\n * `llmSkillsUrl` props. Defaults: in-instance `/apps/openbuilt` (via\n * `generateUrl`) and `https://docs.conduction.nl/ai-skills`.\n *\n * Spec: features-roadmap-component — Requirement \"CnFeaturesAndRoadmapView\".\n */\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcNoteCard } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport TableSearch from 'vue-material-design-icons/TableSearch.vue'\nimport CnCapabilityTable from '../CnCapabilityTable/CnCapabilityTable.vue'\nimport CnFeaturesAndRoadmapSidebar from '../CnFeaturesAndRoadmapSidebar/CnFeaturesAndRoadmapSidebar.vue'\nimport CnFeaturesTab from '../CnFeaturesTab/CnFeaturesTab.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnSupportDialog from '../CnSupportDialog/CnSupportDialog.vue'\nimport { buildFeatureRequestUrl, DEFAULT_FORGE } from '../../utils/forge.js'\n\nconst DEFAULT_OPENBUILT_PATH = '/apps/openbuilt'\nconst DEFAULT_LLM_SKILLS_URL = 'https://docs.conduction.nl/ai-skills'\nconst DEFAULT_DONATE_URL = 'https://github.com/sponsors/ConductionNL'\nconst DEFAULT_SUPPORT_URL = 'https://www.conduction.nl/support'\nconst DEFAULT_FOUNDER_NAME = 'Ruben van der Linde'\nconst DEFAULT_FOUNDER_TITLE = 'a founder of Conduction'\n\nexport default {\n\tname: 'CnFeaturesAndRoadmapView',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcNoteCard,\n\t\tFormatListBulleted,\n\t\tLockOutline,\n\t\tPlus,\n\t\tRoadVariant,\n\t\tTableSearch,\n\t\tCnCapabilityTable,\n\t\tCnFeaturesTab,\n\t\tCnRoadmapTab,\n\t\tCnSupportDialog,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Set to `true` by CnAppRoot so descendants know the host will\n\t\t * mount a hoisted sidebar at NcContent level (see CnAppRoot.vue\n\t\t * `cnHostsIndexSidebar: true`). When `false` (no CnAppRoot\n\t\t * ancestor) the view skips publishing — there's no inline\n\t\t * fallback for this surface.\n\t\t */\n\t\tcnHostsIndexSidebar: { default: false },\n\t\t/**\n\t\t * Reactive holder published by CnAppRoot that backs the\n\t\t * hoisted-sidebar render in NcContent. Set value to\n\t\t * `{component, props, listeners}` to mount; set to `null` to\n\t\t * unmount. Same holder CnIndexPage uses.\n\t\t */\n\t\tcnIndexSidebarConfig: { default: () => ({ value: null }) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * `<owner>/<repo>` of the app's repository on the forge.\n\t\t */\n\t\trepo: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * Target forge the feature-request deep-links are built against\n\t\t * (the Suggest CTA and the support dialog's feature-request URL).\n\t\t * Set `{type: 'github'}` (optionally with `baseUrl`) to switch\n\t\t * forge.\n\t\t *\n\t\t * @type {{type: 'codeberg'|'forgejo'|'gitea'|'github', baseUrl?: string}}\n\t\t */\n\t\tforge: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_FORGE }),\n\t\t},\n\n\t\t/**\n\t\t * Build-time feature manifest (alphabetical list rendered by CnFeaturesTab).\n\t\t *\n\t\t * @type {Array<{slug: string, title: string, summary: string, docsUrl: string}>}\n\t\t */\n\t\tfeatures: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Admin opt-out flag — when true the entire view collapses to a\n\t\t * single \"disabled by admin\" empty state.\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Optional capability comparison. When supplied, the header toggle\n\t\t * gains a third stop that renders `CnCapabilityTable`: a searchable\n\t\t * table of the rows, grouped by feature or by area, with a\n\t\t * provided-by column naming which app or platform delivers each one.\n\t\t *\n\t\t * Shape: `{systems, areas, features?, providers?, capabilities}`. The\n\t\t * rows carry the competitor ratings they already carry, plus the\n\t\t * optional `provider`, `providerHow`, `feature` and\n\t\t * `featureConfidence` fields.\n\t\t *\n\t\t * When null (default) the toggle keeps its two stops and the view\n\t\t * renders exactly as it does for every app that passes nothing.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tcapabilityComparison: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override the OpenBuilt sidebar CTA target. Pass an absolute URL or\n\t\t * a Nextcloud-relative path. When unset, defaults to the in-instance\n\t\t * OpenBuilt route at `/apps/openbuilt` (resolved via `generateUrl`).\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\topenbuiltUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override the LLM-skills sidebar CTA target. Defaults to\n\t\t * `https://docs.conduction.nl/ai-skills`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tllmSkillsUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional override for the Suggest CTA inside the sidebar. When\n\t\t * set the CTA renders as an anchor pointing at this URL —\n\t\t * appropriate when the app routes feature suggestions through a\n\t\t * public form, a Discord channel, or any non-forge target. When\n\t\t * empty (default) the CTA links the forge's feature-request\n\t\t * issue form derived from `repo` + `forge`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsuggestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional URL of the app's public documentation site. When set,\n\t\t * an info banner is rendered above the card grid pointing users at\n\t\t * `<docs>` for full technical + user docs. Per-app — pipelinq\n\t\t * passes `https://pipelinq.conduction.nl`, decidesk passes its\n\t\t * own, etc. When empty (default) no banner renders.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Display name of the host app, used as the title of the bundled\n\t\t * `CnSupportDialog` and interpolated into its body copy. When\n\t\t * empty (default) the View derives a humanised label from `repo`\n\t\t * — `ConductionNL/openregister` becomes `Openregister` — which is\n\t\t * fine for stock apps and overridable for camelCased names.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappName: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Kebab-case host-app id passed to `CnSupportDialog` (its\n\t\t * localStorage namespace). When empty (default) the View derives\n\t\t * it from `repo` — second segment lower-cased.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappSlug: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Nextcloud App Store listing URL for the host app, threaded\n\t\t * through to `CnSupportDialog`. When empty (default) the View\n\t\t * derives `https://apps.nextcloud.com/apps/{appSlug}`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappStoreUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * URL the \"Suggest a feature\" CTA inside `CnSupportDialog` opens.\n\t\t * When empty (default) the View derives\n\t\t * `<forge-base>/{repo}/issues/new` from the `forge` prop. This is\n\t\t * intentionally separate from the prop-of-the-same-name on the\n\t\t * sidebar's Suggest CTA — that one opens the modal-driven flow;\n\t\t * this one is the \"open on the forge\" fallback used by the support\n\t\t * dialog, which is meant for the casual visitor rather than the\n\t\t * existing suggest-feature loop.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfeatureRequestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Donate-CTA URL passed to `CnSupportDialog`. Defaults to\n\t\t * ConductionNL's GitHub Sponsors page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdonateUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_DONATE_URL,\n\t\t},\n\n\t\t/**\n\t\t * Business-support CTA URL passed to `CnSupportDialog`. Defaults\n\t\t * to the Conduction contact page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsupportUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_SUPPORT_URL,\n\t\t},\n\n\t\t/**\n\t\t * Name rendered in the handwritten signature of `CnSupportDialog`.\n\t\t * Defaults to Ruben van der Linde.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderName: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_NAME,\n\t\t},\n\n\t\t/**\n\t\t * Title shown after the handwritten signature in `CnSupportDialog`\n\t\t * (e.g. \"Founder\", \"Oprichter\").\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_TITLE,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tactiveView: 'features',\n\t\t\tshowSupportDialog: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The stops the header toggle cycles through. Without a capability\n\t\t * comparison this is the two-stop list every consumer has today, and\n\t\t * the toggle behaves exactly as it did.\n\t\t *\n\t\t * @return {Array<string>} The views, in cycle order.\n\t\t */\n\t\tavailableViews() {\n\t\t\treturn this.capabilityComparison\n\t\t\t\t? ['features', 'capabilities', 'roadmap']\n\t\t\t\t: ['features', 'roadmap']\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The view the toggle moves to next.\n\t\t */\n\t\tnextView() {\n\t\t\tconst views = this.availableViews\n\t\t\tconst index = views.indexOf(this.activeView)\n\t\t\treturn views[(index + 1) % views.length]\n\t\t},\n\n\t\theaderTitle() {\n\t\t\treturn this.viewTitle(this.activeView)\n\t\t},\n\n\t\ttoggleLabel() {\n\t\t\tif (this.nextView === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Show capabilities')\n\t\t\t}\n\t\t\treturn this.nextView === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Show roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Show features')\n\t\t},\n\n\t\tsuggestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Suggest feature')\n\t\t},\n\n\t\tdisabledTitle() {\n\t\t\treturn t('nextcloud-vue', 'This feature has been disabled by your administrator')\n\t\t},\n\n\t\tdisabledDescription() {\n\t\t\treturn t('nextcloud-vue', 'Contact your Nextcloud administrator to enable Features & Roadmap on this instance.')\n\t\t},\n\n\t\tresolvedOpenbuiltUrl() {\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\treturn this.openbuiltUrl\n\t\t\t}\n\t\t\treturn generateUrl(DEFAULT_OPENBUILT_PATH)\n\t\t},\n\n\t\tresolvedLlmSkillsUrl() {\n\t\t\treturn this.llmSkillsUrl || DEFAULT_LLM_SKILLS_URL\n\t\t},\n\n\t\tresolvedDocumentationUrl() {\n\t\t\treturn this.documentationUrl || ''\n\t\t},\n\n\t\trepoSlug() {\n\t\t\tif (!this.repo || !this.repo.includes('/')) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.repo.split('/')[1].toLowerCase()\n\t\t},\n\n\t\tresolvedAppSlug() {\n\t\t\treturn this.appSlug || this.repoSlug\n\t\t},\n\n\t\tresolvedAppName() {\n\t\t\tif (this.appName) {\n\t\t\t\treturn this.appName\n\t\t\t}\n\t\t\tconst slug = this.repoSlug\n\t\t\tif (!slug) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn slug.charAt(0).toUpperCase() + slug.slice(1)\n\t\t},\n\n\t\tresolvedAppStoreUrl() {\n\t\t\treturn this.appStoreUrl || (this.resolvedAppSlug\n\t\t\t\t? `https://apps.nextcloud.com/apps/${this.resolvedAppSlug}`\n\t\t\t\t: '')\n\t\t},\n\n\t\tresolvedFeatureRequestUrl() {\n\t\t\tif (this.featureRequestUrl) {\n\t\t\t\treturn this.featureRequestUrl\n\t\t\t}\n\t\t\tif (!this.repo) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// The feature-request issue FORM, not the blank new-issue page —\n\t\t\t// the form's structured fields replaced the removed in-product\n\t\t\t// suggestion modal.\n\t\t\treturn buildFeatureRequestUrl(this.forge, this.repo)\n\t\t},\n\n\t\t/**\n\t\t * The Suggest CTA's target: the host's `suggestUrl` override when\n\t\t * given (a public form, a Discord channel), else the forge's\n\t\t * feature-request issue form.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSuggestUrl() {\n\t\t\treturn this.suggestUrl || this.resolvedFeatureRequestUrl\n\t\t},\n\n\t\tdocumentationUrlIsExternal() {\n\t\t\treturn /^https?:\\/\\//i.test(this.resolvedDocumentationUrl)\n\t\t},\n\n\t\tdocsNoteLeading() {\n\t\t\treturn t('nextcloud-vue', 'Looking for documentation? Visit')\n\t\t},\n\n\t\tdocsNoteLinkLabel() {\n\t\t\t// Strip protocol for a cleaner inline link label.\n\t\t\treturn this.resolvedDocumentationUrl.replace(/^https?:\\/\\//i, '')\n\t\t},\n\n\t\tdocsNoteTrailing() {\n\t\t\treturn t('nextcloud-vue', 'for all technical and user documentation.')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A comparison that goes away leaves the reader on a stop that no\n\t\t// longer exists: the panel's v-else-if still matches, so it renders an\n\t\t// empty CnCapabilityTable under a \"Capabilities\" heading until the\n\t\t// reader thinks to press the toggle.\n\t\tavailableViews(views) {\n\t\t\tif (!views.includes(this.activeView)) {\n\t\t\t\tthis.activeView = views[0]\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.publishHoistedSidebar()\n\t},\n\n\tbeforeUnmount() {\n\t\t// Clear the holder so the hoisted sidebar disappears when the\n\t\t// user navigates away from this route. Mirrors CnIndexPage.\n\t\tif (this.cnHostsIndexSidebar && this.cnIndexSidebarConfig) {\n\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * @param {string} view One of the `availableViews`.\n\t\t * @return {string} The heading for that view.\n\t\t */\n\t\tviewTitle(view) {\n\t\t\tif (view === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Capabilities')\n\t\t\t}\n\t\t\treturn view === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Features')\n\t\t},\n\n\t\ttoggleView() {\n\t\t\tthis.activeView = this.nextView\n\t\t},\n\n\t\topenSupportDialog() {\n\t\t\tthis.showSupportDialog = true\n\t\t},\n\n\t\t/**\n\t\t * Publish the hoisted-sidebar config to `cnIndexSidebarConfig`\n\t\t * (same holder CnIndexPage uses) so CnAppRoot mounts the\n\t\t * CnFeaturesAndRoadmapSidebar at NcContent level. The sidebar\n\t\t * emits `suggest` when the user clicks the Suggest CTA inside\n\t\t * it; the view forwards that to `openSuggestModal()`. No-op\n\t\t * when there's no CnAppRoot ancestor.\n\t\t */\n\t\tpublishHoistedSidebar() {\n\t\t\tif (!this.cnHostsIndexSidebar || !this.cnIndexSidebarConfig) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.disabled) {\n\t\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.cnIndexSidebarConfig.value = {\n\t\t\t\t// markRaw: see CnIndexPage's publishHoistedSidebar — the holder\n\t\t\t\t// is deeply reactive and would proxy the component definition.\n\t\t\t\tcomponent: markRaw(CnFeaturesAndRoadmapSidebar),\n\t\t\t\tprops: {\n\t\t\t\t\topenbuiltUrl: this.resolvedOpenbuiltUrl,\n\t\t\t\t\tllmSkillsUrl: this.resolvedLlmSkillsUrl,\n\t\t\t\t\t// Always a URL now (the removed in-product modal was the\n\t\t\t\t\t// only non-link path), so the sidebar CTA always renders\n\t\t\t\t\t// as an anchor and its `suggest` emit path stays idle.\n\t\t\t\t\tsuggestUrl: this.resolvedSuggestUrl,\n\t\t\t\t},\n\n\t\t\t\tlisteners: {\n\t\t\t\t\tsupport: () => this.openSupportDialog(),\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-and-roadmap-view {\n\tmax-width: 1200px;\n\tmargin: 0 auto;\n\tpadding: 24px 16px;\n}\n\n.cn-features-and-roadmap-view__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-bottom: 24px;\n\tflex-wrap: wrap;\n\t/* Clear the Nextcloud navigation toggle button (44px wide, absolutely\n\t positioned at the left edge of .app-content) plus 12px breathing\n\t room. Only the HEADER shifts right — the panel below keeps full width. */\n\tpadding-inline-start: 56px;\n}\n\n.cn-features-and-roadmap-view__title {\n\tfont-size: 1.4em;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-and-roadmap-view__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: center;\n\tflex-wrap: wrap;\n}\n\n.cn-features-and-roadmap-view__panel {\n\tmin-height: 320px;\n}\n\n/* Underline the inline docs URL so it reads as a link inside the note\n body — NcNoteCard's default link styling drops the underline by\n inheriting from the host theme. */\n.cn-features-and-roadmap-view__docs-note :deep(a) {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-features-and-roadmap-view__docs-note :deep(a:hover) {\n\ttext-decoration: underline;\n\topacity: 0.85;\n}\n</style>\n"],"names":["CnCapabilityTable","CnFeaturesTab","CnRoadmapTab","CnSupportDialog","t","CnFeaturesAndRoadmapSidebar"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFeaturesAndRoadmapView.vue2.js","sources":["../../../../src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-features-and-roadmap-view\">\n\t\t<div v-if=\"disabled\" class=\"cn-features-and-roadmap-view__disabled\">\n\t\t\t<NcEmptyContent :name=\"disabledTitle\" :description=\"disabledDescription\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LockOutline :size=\"48\" />\n\t\t\t\t</template>\n\t\t\t</NcEmptyContent>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<header class=\"cn-features-and-roadmap-view__header\">\n\t\t\t\t<h2 class=\"cn-features-and-roadmap-view__title\">\n\t\t\t\t\t{{ headerTitle }}\n\t\t\t\t</h2>\n\t\t\t\t<div class=\"cn-features-and-roadmap-view__actions\">\n\t\t\t\t\t<NcButton @click=\"toggleView\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TableSearch v-if=\"nextView === 'capabilities'\" :size=\"20\" />\n\t\t\t\t\t\t\t<RoadVariant v-else-if=\"nextView === 'roadmap'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FormatListBulleted v-else :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ toggleLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<!-- Direct link to the forge's feature-request issue form\n\t\t\t\t\t (team decision 2026-09-04: the in-product suggestion\n\t\t\t\t\t modal is gone — the forge is where the conversation\n\t\t\t\t\t happens, in English). -->\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"resolvedSuggestUrl\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"resolvedSuggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ suggestLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</header>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"resolvedDocumentationUrl\"\n\t\t\t\ttype=\"info\"\n\t\t\t\tclass=\"cn-features-and-roadmap-view__docs-note\">\n\t\t\t\t{{ docsNoteLeading }}\n\t\t\t\t<a\n\t\t\t\t\t:href=\"resolvedDocumentationUrl\"\n\t\t\t\t\t:target=\"documentationUrlIsExternal ? '_blank' : null\"\n\t\t\t\t\t:rel=\"documentationUrlIsExternal ? 'noopener noreferrer' : null\">{{ docsNoteLinkLabel }}</a>\n\t\t\t\t{{ docsNoteTrailing }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<main class=\"cn-features-and-roadmap-view__panel\">\n\t\t\t\t<CnFeaturesTab v-if=\"activeView === 'features'\" :features=\"features\" />\n\t\t\t\t<CnCapabilityTable\n\t\t\t\t\tv-else-if=\"activeView === 'capabilities'\"\n\t\t\t\t\t:comparison=\"capabilityComparison\" />\n\t\t\t\t<CnRoadmapTab v-else :repo=\"repo\" />\n\t\t\t</main>\n\n\t\t\t<CnSupportDialog\n\t\t\t\tv-if=\"showSupportDialog\"\n\t\t\t\t:appName=\"resolvedAppName\"\n\t\t\t\t:appSlug=\"resolvedAppSlug\"\n\t\t\t\t:appStoreUrl=\"resolvedAppStoreUrl\"\n\t\t\t\t:featureRequestUrl=\"resolvedFeatureRequestUrl\"\n\t\t\t\t:donateUrl=\"donateUrl\"\n\t\t\t\t:supportUrl=\"supportUrl\"\n\t\t\t\t:founderName=\"founderName\"\n\t\t\t\t:founderTitle=\"founderTitle\"\n\t\t\t\t@close=\"showSupportDialog = false\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\n/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * CnFeaturesAndRoadmapView — route-level container for the Features &\n * Roadmap surface. Header carries the view-toggle and the primary\n * Suggest-feature CTA. Body is a card grid (Features OR Roadmap).\n *\n * The right-edge sidebar is hoisted to NcContent level via the same\n * `cnIndexSidebarConfig` provide mechanism CnIndexPage uses for its\n * CnIndexSidebar — that's the only place where Nextcloud's right-edge\n * sidebar slot renders correctly (next to NcAppContent, not inside it).\n * When mounted under CnAppRoot the view publishes\n * `CnFeaturesAndRoadmapSidebar` + props into the holder on mounted()\n * and clears it on beforeUnmount(). Sidebar carries four pitch\n * sections: Suggest, OpenBuilt, LLM, Support. Suggest renders as an\n * anchor to the forge's feature-request issue form (`resolvedSuggestUrl`\n * is always passed); Support emits `@support` (forwarded to a\n * freshly-mounted `CnSupportDialog`).\n *\n * Sidebar link targets are overridable via the `openbuiltUrl` and\n * `llmSkillsUrl` props. Defaults: in-instance `/apps/openbuilt` (via\n * `generateUrl`) and `https://docs.conduction.nl/ai-skills`.\n *\n * Spec: features-roadmap-component — Requirement \"CnFeaturesAndRoadmapView\".\n */\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcNoteCard } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport TableSearch from 'vue-material-design-icons/TableSearch.vue'\nimport CnCapabilityTable from '../CnCapabilityTable/CnCapabilityTable.vue'\nimport CnFeaturesAndRoadmapSidebar from '../CnFeaturesAndRoadmapSidebar/CnFeaturesAndRoadmapSidebar.vue'\nimport CnFeaturesTab from '../CnFeaturesTab/CnFeaturesTab.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnSupportDialog from '../CnSupportDialog/CnSupportDialog.vue'\nimport { warnCapabilityComparisonDeprecated } from '../../utils/capabilityComparison.js'\nimport { buildFeatureRequestUrl, DEFAULT_FORGE } from '../../utils/forge.js'\n\nconst DEFAULT_OPENBUILT_PATH = '/apps/openbuilt'\nconst DEFAULT_LLM_SKILLS_URL = 'https://docs.conduction.nl/ai-skills'\nconst DEFAULT_DONATE_URL = 'https://github.com/sponsors/ConductionNL'\nconst DEFAULT_SUPPORT_URL = 'https://www.conduction.nl/support'\nconst DEFAULT_FOUNDER_NAME = 'Ruben van der Linde'\nconst DEFAULT_FOUNDER_TITLE = 'a founder of Conduction'\n\nexport default {\n\tname: 'CnFeaturesAndRoadmapView',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcNoteCard,\n\t\tFormatListBulleted,\n\t\tLockOutline,\n\t\tPlus,\n\t\tRoadVariant,\n\t\tTableSearch,\n\t\tCnCapabilityTable,\n\t\tCnFeaturesTab,\n\t\tCnRoadmapTab,\n\t\tCnSupportDialog,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Set to `true` by CnAppRoot so descendants know the host will\n\t\t * mount a hoisted sidebar at NcContent level (see CnAppRoot.vue\n\t\t * `cnHostsIndexSidebar: true`). When `false` (no CnAppRoot\n\t\t * ancestor) the view skips publishing — there's no inline\n\t\t * fallback for this surface.\n\t\t */\n\t\tcnHostsIndexSidebar: { default: false },\n\t\t/**\n\t\t * Reactive holder published by CnAppRoot that backs the\n\t\t * hoisted-sidebar render in NcContent. Set value to\n\t\t * `{component, props, listeners}` to mount; set to `null` to\n\t\t * unmount. Same holder CnIndexPage uses.\n\t\t */\n\t\tcnIndexSidebarConfig: { default: () => ({ value: null }) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * `<owner>/<repo>` of the app's repository on the forge.\n\t\t */\n\t\trepo: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * Target forge the feature-request deep-links are built against\n\t\t * (the Suggest CTA and the support dialog's feature-request URL).\n\t\t * Set `{type: 'github'}` (optionally with `baseUrl`) to switch\n\t\t * forge.\n\t\t *\n\t\t * @type {{type: 'codeberg'|'forgejo'|'gitea'|'github', baseUrl?: string}}\n\t\t */\n\t\tforge: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_FORGE }),\n\t\t},\n\n\t\t/**\n\t\t * Build-time feature manifest (alphabetical list rendered by CnFeaturesTab).\n\t\t *\n\t\t * @type {Array<{slug: string, title: string, summary: string, docsUrl: string}>}\n\t\t */\n\t\tfeatures: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Admin opt-out flag — when true the entire view collapses to a\n\t\t * single \"disabled by admin\" empty state.\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Optional capability comparison. When supplied, the header toggle\n\t\t * gains a third stop that renders `CnCapabilityTable`: a searchable\n\t\t * table of the rows, grouped by feature or by area, with a\n\t\t * provided-by column naming which app or platform delivers each one.\n\t\t *\n\t\t * Shape: `{systems, areas, features?, providers?, capabilities}`. The\n\t\t * rows carry the competitor ratings they already carry, plus the\n\t\t * optional `provider`, `providerHow`, `feature` and\n\t\t * `featureConfidence` fields.\n\t\t *\n\t\t * When null (default) the toggle keeps its two stops and the view\n\t\t * renders exactly as it does for every app that passes nothing.\n\t\t *\n\t\t * Deprecated: publish the comparison on the app's docs site and link to it (as dossiq #3312 and pipelinq #2195 did). Still rendered; removal waits for a major version.\n\t\t * A development build warns once when it is set.\n\t\t *\n\t\t * @deprecated since 2.66.0, publish the comparison on the app's docs site and link to it.\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-the-in-app-capability-comparison-is-deprecated\n\t\t * @type {object|null}\n\t\t */\n\t\tcapabilityComparison: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override the OpenBuilt sidebar CTA target. Pass an absolute URL or\n\t\t * a Nextcloud-relative path. When unset, defaults to the in-instance\n\t\t * OpenBuilt route at `/apps/openbuilt` (resolved via `generateUrl`).\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\topenbuiltUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override the LLM-skills sidebar CTA target. Defaults to\n\t\t * `https://docs.conduction.nl/ai-skills`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tllmSkillsUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional override for the Suggest CTA inside the sidebar. When\n\t\t * set the CTA renders as an anchor pointing at this URL —\n\t\t * appropriate when the app routes feature suggestions through a\n\t\t * public form, a Discord channel, or any non-forge target. When\n\t\t * empty (default) the CTA links the forge's feature-request\n\t\t * issue form derived from `repo` + `forge`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsuggestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional URL of the app's public documentation site. When set,\n\t\t * an info banner is rendered above the card grid pointing users at\n\t\t * `<docs>` for full technical + user docs. Per-app — pipelinq\n\t\t * passes `https://pipelinq.conduction.nl`, decidesk passes its\n\t\t * own, etc. When empty (default) no banner renders.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Display name of the host app, used as the title of the bundled\n\t\t * `CnSupportDialog` and interpolated into its body copy. When\n\t\t * empty (default) the View derives a humanised label from `repo`\n\t\t * — `ConductionNL/openregister` becomes `Openregister` — which is\n\t\t * fine for stock apps and overridable for camelCased names.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappName: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Kebab-case host-app id passed to `CnSupportDialog` (its\n\t\t * localStorage namespace). When empty (default) the View derives\n\t\t * it from `repo` — second segment lower-cased.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappSlug: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Nextcloud App Store listing URL for the host app, threaded\n\t\t * through to `CnSupportDialog`. When empty (default) the View\n\t\t * derives `https://apps.nextcloud.com/apps/{appSlug}`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappStoreUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * URL the \"Suggest a feature\" CTA inside `CnSupportDialog` opens.\n\t\t * When empty (default) the View derives\n\t\t * `<forge-base>/{repo}/issues/new` from the `forge` prop. This is\n\t\t * intentionally separate from the prop-of-the-same-name on the\n\t\t * sidebar's Suggest CTA — that one opens the modal-driven flow;\n\t\t * this one is the \"open on the forge\" fallback used by the support\n\t\t * dialog, which is meant for the casual visitor rather than the\n\t\t * existing suggest-feature loop.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfeatureRequestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Donate-CTA URL passed to `CnSupportDialog`. Defaults to\n\t\t * ConductionNL's GitHub Sponsors page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdonateUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_DONATE_URL,\n\t\t},\n\n\t\t/**\n\t\t * Business-support CTA URL passed to `CnSupportDialog`. Defaults\n\t\t * to the Conduction contact page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsupportUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_SUPPORT_URL,\n\t\t},\n\n\t\t/**\n\t\t * Name rendered in the handwritten signature of `CnSupportDialog`.\n\t\t * Defaults to Ruben van der Linde.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderName: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_NAME,\n\t\t},\n\n\t\t/**\n\t\t * Title shown after the handwritten signature in `CnSupportDialog`\n\t\t * (e.g. \"Founder\", \"Oprichter\").\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_TITLE,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tactiveView: 'features',\n\t\t\tshowSupportDialog: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The stops the header toggle cycles through. Without a capability\n\t\t * comparison this is the two-stop list every consumer has today, and\n\t\t * the toggle behaves exactly as it did.\n\t\t *\n\t\t * @return {Array<string>} The views, in cycle order.\n\t\t */\n\t\tavailableViews() {\n\t\t\treturn this.capabilityComparison\n\t\t\t\t? ['features', 'capabilities', 'roadmap']\n\t\t\t\t: ['features', 'roadmap']\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The view the toggle moves to next.\n\t\t */\n\t\tnextView() {\n\t\t\tconst views = this.availableViews\n\t\t\tconst index = views.indexOf(this.activeView)\n\t\t\treturn views[(index + 1) % views.length]\n\t\t},\n\n\t\theaderTitle() {\n\t\t\treturn this.viewTitle(this.activeView)\n\t\t},\n\n\t\ttoggleLabel() {\n\t\t\tif (this.nextView === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Show capabilities')\n\t\t\t}\n\t\t\treturn this.nextView === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Show roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Show features')\n\t\t},\n\n\t\tsuggestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Suggest feature')\n\t\t},\n\n\t\tdisabledTitle() {\n\t\t\treturn t('nextcloud-vue', 'This feature has been disabled by your administrator')\n\t\t},\n\n\t\tdisabledDescription() {\n\t\t\treturn t('nextcloud-vue', 'Contact your Nextcloud administrator to enable Features & Roadmap on this instance.')\n\t\t},\n\n\t\tresolvedOpenbuiltUrl() {\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\treturn this.openbuiltUrl\n\t\t\t}\n\t\t\treturn generateUrl(DEFAULT_OPENBUILT_PATH)\n\t\t},\n\n\t\tresolvedLlmSkillsUrl() {\n\t\t\treturn this.llmSkillsUrl || DEFAULT_LLM_SKILLS_URL\n\t\t},\n\n\t\tresolvedDocumentationUrl() {\n\t\t\treturn this.documentationUrl || ''\n\t\t},\n\n\t\trepoSlug() {\n\t\t\tif (!this.repo || !this.repo.includes('/')) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.repo.split('/')[1].toLowerCase()\n\t\t},\n\n\t\tresolvedAppSlug() {\n\t\t\treturn this.appSlug || this.repoSlug\n\t\t},\n\n\t\tresolvedAppName() {\n\t\t\tif (this.appName) {\n\t\t\t\treturn this.appName\n\t\t\t}\n\t\t\tconst slug = this.repoSlug\n\t\t\tif (!slug) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn slug.charAt(0).toUpperCase() + slug.slice(1)\n\t\t},\n\n\t\tresolvedAppStoreUrl() {\n\t\t\treturn this.appStoreUrl || (this.resolvedAppSlug\n\t\t\t\t? `https://apps.nextcloud.com/apps/${this.resolvedAppSlug}`\n\t\t\t\t: '')\n\t\t},\n\n\t\tresolvedFeatureRequestUrl() {\n\t\t\tif (this.featureRequestUrl) {\n\t\t\t\treturn this.featureRequestUrl\n\t\t\t}\n\t\t\tif (!this.repo) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// The feature-request issue FORM, not the blank new-issue page —\n\t\t\t// the form's structured fields replaced the removed in-product\n\t\t\t// suggestion modal.\n\t\t\treturn buildFeatureRequestUrl(this.forge, this.repo)\n\t\t},\n\n\t\t/**\n\t\t * The Suggest CTA's target: the host's `suggestUrl` override when\n\t\t * given (a public form, a Discord channel), else the forge's\n\t\t * feature-request issue form.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSuggestUrl() {\n\t\t\treturn this.suggestUrl || this.resolvedFeatureRequestUrl\n\t\t},\n\n\t\tdocumentationUrlIsExternal() {\n\t\t\treturn /^https?:\\/\\//i.test(this.resolvedDocumentationUrl)\n\t\t},\n\n\t\tdocsNoteLeading() {\n\t\t\treturn t('nextcloud-vue', 'Looking for documentation? Visit')\n\t\t},\n\n\t\tdocsNoteLinkLabel() {\n\t\t\t// Strip protocol for a cleaner inline link label.\n\t\t\treturn this.resolvedDocumentationUrl.replace(/^https?:\\/\\//i, '')\n\t\t},\n\n\t\tdocsNoteTrailing() {\n\t\t\treturn t('nextcloud-vue', 'for all technical and user documentation.')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A comparison that goes away leaves the reader on a stop that no\n\t\t// longer exists: the panel's v-else-if still matches, so it renders an\n\t\t// empty CnCapabilityTable under a \"Capabilities\" heading until the\n\t\t// reader thinks to press the toggle.\n\t\tavailableViews(views) {\n\t\t\tif (!views.includes(this.activeView)) {\n\t\t\t\tthis.activeView = views[0]\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.publishHoistedSidebar()\n\t\tif (this.capabilityComparison) {\n\t\t\twarnCapabilityComparisonDeprecated('CnFeaturesAndRoadmapView `capabilityComparison`')\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\t// Clear the holder so the hoisted sidebar disappears when the\n\t\t// user navigates away from this route. Mirrors CnIndexPage.\n\t\tif (this.cnHostsIndexSidebar && this.cnIndexSidebarConfig) {\n\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * @param {string} view One of the `availableViews`.\n\t\t * @return {string} The heading for that view.\n\t\t */\n\t\tviewTitle(view) {\n\t\t\tif (view === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Capabilities')\n\t\t\t}\n\t\t\treturn view === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Features')\n\t\t},\n\n\t\ttoggleView() {\n\t\t\tthis.activeView = this.nextView\n\t\t},\n\n\t\topenSupportDialog() {\n\t\t\tthis.showSupportDialog = true\n\t\t},\n\n\t\t/**\n\t\t * Publish the hoisted-sidebar config to `cnIndexSidebarConfig`\n\t\t * (same holder CnIndexPage uses) so CnAppRoot mounts the\n\t\t * CnFeaturesAndRoadmapSidebar at NcContent level. The sidebar\n\t\t * emits `suggest` when the user clicks the Suggest CTA inside\n\t\t * it; the view forwards that to `openSuggestModal()`. No-op\n\t\t * when there's no CnAppRoot ancestor.\n\t\t */\n\t\tpublishHoistedSidebar() {\n\t\t\tif (!this.cnHostsIndexSidebar || !this.cnIndexSidebarConfig) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.disabled) {\n\t\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.cnIndexSidebarConfig.value = {\n\t\t\t\t// markRaw: see CnIndexPage's publishHoistedSidebar — the holder\n\t\t\t\t// is deeply reactive and would proxy the component definition.\n\t\t\t\tcomponent: markRaw(CnFeaturesAndRoadmapSidebar),\n\t\t\t\tprops: {\n\t\t\t\t\topenbuiltUrl: this.resolvedOpenbuiltUrl,\n\t\t\t\t\tllmSkillsUrl: this.resolvedLlmSkillsUrl,\n\t\t\t\t\t// Always a URL now (the removed in-product modal was the\n\t\t\t\t\t// only non-link path), so the sidebar CTA always renders\n\t\t\t\t\t// as an anchor and its `suggest` emit path stays idle.\n\t\t\t\t\tsuggestUrl: this.resolvedSuggestUrl,\n\t\t\t\t},\n\n\t\t\t\tlisteners: {\n\t\t\t\t\tsupport: () => this.openSupportDialog(),\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-and-roadmap-view {\n\tmax-width: 1200px;\n\tmargin: 0 auto;\n\tpadding: 24px 16px;\n}\n\n.cn-features-and-roadmap-view__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-bottom: 24px;\n\tflex-wrap: wrap;\n\t/* Clear the Nextcloud navigation toggle button (44px wide, absolutely\n\t positioned at the left edge of .app-content) plus 12px breathing\n\t room. Only the HEADER shifts right — the panel below keeps full width. */\n\tpadding-inline-start: 56px;\n}\n\n.cn-features-and-roadmap-view__title {\n\tfont-size: 1.4em;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-and-roadmap-view__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: center;\n\tflex-wrap: wrap;\n}\n\n.cn-features-and-roadmap-view__panel {\n\tmin-height: 320px;\n}\n\n/* Underline the inline docs URL so it reads as a link inside the note\n body — NcNoteCard's default link styling drops the underline by\n inheriting from the host theme. */\n.cn-features-and-roadmap-view__docs-note :deep(a) {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-features-and-roadmap-view__docs-note :deep(a:hover) {\n\ttext-decoration: underline;\n\topacity: 0.85;\n}\n</style>\n"],"names":["CnCapabilityTable","CnFeaturesTab","CnRoadmapTab","CnSupportDialog","t","CnFeaturesAndRoadmapSidebar"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnFeaturesAndRoadmapView.vue3.js","sources":["../../../../src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue?vue&type=template&id=7c44400f&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-features-and-roadmap-view\">\n\t\t<div v-if=\"disabled\" class=\"cn-features-and-roadmap-view__disabled\">\n\t\t\t<NcEmptyContent :name=\"disabledTitle\" :description=\"disabledDescription\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LockOutline :size=\"48\" />\n\t\t\t\t</template>\n\t\t\t</NcEmptyContent>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<header class=\"cn-features-and-roadmap-view__header\">\n\t\t\t\t<h2 class=\"cn-features-and-roadmap-view__title\">\n\t\t\t\t\t{{ headerTitle }}\n\t\t\t\t</h2>\n\t\t\t\t<div class=\"cn-features-and-roadmap-view__actions\">\n\t\t\t\t\t<NcButton @click=\"toggleView\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TableSearch v-if=\"nextView === 'capabilities'\" :size=\"20\" />\n\t\t\t\t\t\t\t<RoadVariant v-else-if=\"nextView === 'roadmap'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FormatListBulleted v-else :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ toggleLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<!-- Direct link to the forge's feature-request issue form\n\t\t\t\t\t (team decision 2026-09-04: the in-product suggestion\n\t\t\t\t\t modal is gone — the forge is where the conversation\n\t\t\t\t\t happens, in English). -->\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"resolvedSuggestUrl\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"resolvedSuggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ suggestLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</header>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"resolvedDocumentationUrl\"\n\t\t\t\ttype=\"info\"\n\t\t\t\tclass=\"cn-features-and-roadmap-view__docs-note\">\n\t\t\t\t{{ docsNoteLeading }}\n\t\t\t\t<a\n\t\t\t\t\t:href=\"resolvedDocumentationUrl\"\n\t\t\t\t\t:target=\"documentationUrlIsExternal ? '_blank' : null\"\n\t\t\t\t\t:rel=\"documentationUrlIsExternal ? 'noopener noreferrer' : null\">{{ docsNoteLinkLabel }}</a>\n\t\t\t\t{{ docsNoteTrailing }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<main class=\"cn-features-and-roadmap-view__panel\">\n\t\t\t\t<CnFeaturesTab v-if=\"activeView === 'features'\" :features=\"features\" />\n\t\t\t\t<CnCapabilityTable\n\t\t\t\t\tv-else-if=\"activeView === 'capabilities'\"\n\t\t\t\t\t:comparison=\"capabilityComparison\" />\n\t\t\t\t<CnRoadmapTab v-else :repo=\"repo\" />\n\t\t\t</main>\n\n\t\t\t<CnSupportDialog\n\t\t\t\tv-if=\"showSupportDialog\"\n\t\t\t\t:appName=\"resolvedAppName\"\n\t\t\t\t:appSlug=\"resolvedAppSlug\"\n\t\t\t\t:appStoreUrl=\"resolvedAppStoreUrl\"\n\t\t\t\t:featureRequestUrl=\"resolvedFeatureRequestUrl\"\n\t\t\t\t:donateUrl=\"donateUrl\"\n\t\t\t\t:supportUrl=\"supportUrl\"\n\t\t\t\t:founderName=\"founderName\"\n\t\t\t\t:founderTitle=\"founderTitle\"\n\t\t\t\t@close=\"showSupportDialog = false\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\n/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * CnFeaturesAndRoadmapView — route-level container for the Features &\n * Roadmap surface. Header carries the view-toggle and the primary\n * Suggest-feature CTA. Body is a card grid (Features OR Roadmap).\n *\n * The right-edge sidebar is hoisted to NcContent level via the same\n * `cnIndexSidebarConfig` provide mechanism CnIndexPage uses for its\n * CnIndexSidebar — that's the only place where Nextcloud's right-edge\n * sidebar slot renders correctly (next to NcAppContent, not inside it).\n * When mounted under CnAppRoot the view publishes\n * `CnFeaturesAndRoadmapSidebar` + props into the holder on mounted()\n * and clears it on beforeUnmount(). Sidebar carries four pitch\n * sections: Suggest, OpenBuilt, LLM, Support. Suggest renders as an\n * anchor to the forge's feature-request issue form (`resolvedSuggestUrl`\n * is always passed); Support emits `@support` (forwarded to a\n * freshly-mounted `CnSupportDialog`).\n *\n * Sidebar link targets are overridable via the `openbuiltUrl` and\n * `llmSkillsUrl` props. Defaults: in-instance `/apps/openbuilt` (via\n * `generateUrl`) and `https://docs.conduction.nl/ai-skills`.\n *\n * Spec: features-roadmap-component — Requirement \"CnFeaturesAndRoadmapView\".\n */\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcNoteCard } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport TableSearch from 'vue-material-design-icons/TableSearch.vue'\nimport CnCapabilityTable from '../CnCapabilityTable/CnCapabilityTable.vue'\nimport CnFeaturesAndRoadmapSidebar from '../CnFeaturesAndRoadmapSidebar/CnFeaturesAndRoadmapSidebar.vue'\nimport CnFeaturesTab from '../CnFeaturesTab/CnFeaturesTab.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnSupportDialog from '../CnSupportDialog/CnSupportDialog.vue'\nimport { buildFeatureRequestUrl, DEFAULT_FORGE } from '../../utils/forge.js'\n\nconst DEFAULT_OPENBUILT_PATH = '/apps/openbuilt'\nconst DEFAULT_LLM_SKILLS_URL = 'https://docs.conduction.nl/ai-skills'\nconst DEFAULT_DONATE_URL = 'https://github.com/sponsors/ConductionNL'\nconst DEFAULT_SUPPORT_URL = 'https://www.conduction.nl/support'\nconst DEFAULT_FOUNDER_NAME = 'Ruben van der Linde'\nconst DEFAULT_FOUNDER_TITLE = 'a founder of Conduction'\n\nexport default {\n\tname: 'CnFeaturesAndRoadmapView',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcNoteCard,\n\t\tFormatListBulleted,\n\t\tLockOutline,\n\t\tPlus,\n\t\tRoadVariant,\n\t\tTableSearch,\n\t\tCnCapabilityTable,\n\t\tCnFeaturesTab,\n\t\tCnRoadmapTab,\n\t\tCnSupportDialog,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Set to `true` by CnAppRoot so descendants know the host will\n\t\t * mount a hoisted sidebar at NcContent level (see CnAppRoot.vue\n\t\t * `cnHostsIndexSidebar: true`). When `false` (no CnAppRoot\n\t\t * ancestor) the view skips publishing — there's no inline\n\t\t * fallback for this surface.\n\t\t */\n\t\tcnHostsIndexSidebar: { default: false },\n\t\t/**\n\t\t * Reactive holder published by CnAppRoot that backs the\n\t\t * hoisted-sidebar render in NcContent. Set value to\n\t\t * `{component, props, listeners}` to mount; set to `null` to\n\t\t * unmount. Same holder CnIndexPage uses.\n\t\t */\n\t\tcnIndexSidebarConfig: { default: () => ({ value: null }) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * `<owner>/<repo>` of the app's repository on the forge.\n\t\t */\n\t\trepo: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * Target forge the feature-request deep-links are built against\n\t\t * (the Suggest CTA and the support dialog's feature-request URL).\n\t\t * Set `{type: 'github'}` (optionally with `baseUrl`) to switch\n\t\t * forge.\n\t\t *\n\t\t * @type {{type: 'codeberg'|'forgejo'|'gitea'|'github', baseUrl?: string}}\n\t\t */\n\t\tforge: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_FORGE }),\n\t\t},\n\n\t\t/**\n\t\t * Build-time feature manifest (alphabetical list rendered by CnFeaturesTab).\n\t\t *\n\t\t * @type {Array<{slug: string, title: string, summary: string, docsUrl: string}>}\n\t\t */\n\t\tfeatures: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Admin opt-out flag — when true the entire view collapses to a\n\t\t * single \"disabled by admin\" empty state.\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Optional capability comparison. When supplied, the header toggle\n\t\t * gains a third stop that renders `CnCapabilityTable`: a searchable\n\t\t * table of the rows, grouped by feature or by area, with a\n\t\t * provided-by column naming which app or platform delivers each one.\n\t\t *\n\t\t * Shape: `{systems, areas, features?, providers?, capabilities}`. The\n\t\t * rows carry the competitor ratings they already carry, plus the\n\t\t * optional `provider`, `providerHow`, `feature` and\n\t\t * `featureConfidence` fields.\n\t\t *\n\t\t * When null (default) the toggle keeps its two stops and the view\n\t\t * renders exactly as it does for every app that passes nothing.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tcapabilityComparison: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override the OpenBuilt sidebar CTA target. Pass an absolute URL or\n\t\t * a Nextcloud-relative path. When unset, defaults to the in-instance\n\t\t * OpenBuilt route at `/apps/openbuilt` (resolved via `generateUrl`).\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\topenbuiltUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override the LLM-skills sidebar CTA target. Defaults to\n\t\t * `https://docs.conduction.nl/ai-skills`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tllmSkillsUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional override for the Suggest CTA inside the sidebar. When\n\t\t * set the CTA renders as an anchor pointing at this URL —\n\t\t * appropriate when the app routes feature suggestions through a\n\t\t * public form, a Discord channel, or any non-forge target. When\n\t\t * empty (default) the CTA links the forge's feature-request\n\t\t * issue form derived from `repo` + `forge`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsuggestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional URL of the app's public documentation site. When set,\n\t\t * an info banner is rendered above the card grid pointing users at\n\t\t * `<docs>` for full technical + user docs. Per-app — pipelinq\n\t\t * passes `https://pipelinq.conduction.nl`, decidesk passes its\n\t\t * own, etc. When empty (default) no banner renders.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Display name of the host app, used as the title of the bundled\n\t\t * `CnSupportDialog` and interpolated into its body copy. When\n\t\t * empty (default) the View derives a humanised label from `repo`\n\t\t * — `ConductionNL/openregister` becomes `Openregister` — which is\n\t\t * fine for stock apps and overridable for camelCased names.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappName: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Kebab-case host-app id passed to `CnSupportDialog` (its\n\t\t * localStorage namespace). When empty (default) the View derives\n\t\t * it from `repo` — second segment lower-cased.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappSlug: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Nextcloud App Store listing URL for the host app, threaded\n\t\t * through to `CnSupportDialog`. When empty (default) the View\n\t\t * derives `https://apps.nextcloud.com/apps/{appSlug}`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappStoreUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * URL the \"Suggest a feature\" CTA inside `CnSupportDialog` opens.\n\t\t * When empty (default) the View derives\n\t\t * `<forge-base>/{repo}/issues/new` from the `forge` prop. This is\n\t\t * intentionally separate from the prop-of-the-same-name on the\n\t\t * sidebar's Suggest CTA — that one opens the modal-driven flow;\n\t\t * this one is the \"open on the forge\" fallback used by the support\n\t\t * dialog, which is meant for the casual visitor rather than the\n\t\t * existing suggest-feature loop.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfeatureRequestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Donate-CTA URL passed to `CnSupportDialog`. Defaults to\n\t\t * ConductionNL's GitHub Sponsors page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdonateUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_DONATE_URL,\n\t\t},\n\n\t\t/**\n\t\t * Business-support CTA URL passed to `CnSupportDialog`. Defaults\n\t\t * to the Conduction contact page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsupportUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_SUPPORT_URL,\n\t\t},\n\n\t\t/**\n\t\t * Name rendered in the handwritten signature of `CnSupportDialog`.\n\t\t * Defaults to Ruben van der Linde.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderName: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_NAME,\n\t\t},\n\n\t\t/**\n\t\t * Title shown after the handwritten signature in `CnSupportDialog`\n\t\t * (e.g. \"Founder\", \"Oprichter\").\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_TITLE,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tactiveView: 'features',\n\t\t\tshowSupportDialog: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The stops the header toggle cycles through. Without a capability\n\t\t * comparison this is the two-stop list every consumer has today, and\n\t\t * the toggle behaves exactly as it did.\n\t\t *\n\t\t * @return {Array<string>} The views, in cycle order.\n\t\t */\n\t\tavailableViews() {\n\t\t\treturn this.capabilityComparison\n\t\t\t\t? ['features', 'capabilities', 'roadmap']\n\t\t\t\t: ['features', 'roadmap']\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The view the toggle moves to next.\n\t\t */\n\t\tnextView() {\n\t\t\tconst views = this.availableViews\n\t\t\tconst index = views.indexOf(this.activeView)\n\t\t\treturn views[(index + 1) % views.length]\n\t\t},\n\n\t\theaderTitle() {\n\t\t\treturn this.viewTitle(this.activeView)\n\t\t},\n\n\t\ttoggleLabel() {\n\t\t\tif (this.nextView === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Show capabilities')\n\t\t\t}\n\t\t\treturn this.nextView === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Show roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Show features')\n\t\t},\n\n\t\tsuggestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Suggest feature')\n\t\t},\n\n\t\tdisabledTitle() {\n\t\t\treturn t('nextcloud-vue', 'This feature has been disabled by your administrator')\n\t\t},\n\n\t\tdisabledDescription() {\n\t\t\treturn t('nextcloud-vue', 'Contact your Nextcloud administrator to enable Features & Roadmap on this instance.')\n\t\t},\n\n\t\tresolvedOpenbuiltUrl() {\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\treturn this.openbuiltUrl\n\t\t\t}\n\t\t\treturn generateUrl(DEFAULT_OPENBUILT_PATH)\n\t\t},\n\n\t\tresolvedLlmSkillsUrl() {\n\t\t\treturn this.llmSkillsUrl || DEFAULT_LLM_SKILLS_URL\n\t\t},\n\n\t\tresolvedDocumentationUrl() {\n\t\t\treturn this.documentationUrl || ''\n\t\t},\n\n\t\trepoSlug() {\n\t\t\tif (!this.repo || !this.repo.includes('/')) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.repo.split('/')[1].toLowerCase()\n\t\t},\n\n\t\tresolvedAppSlug() {\n\t\t\treturn this.appSlug || this.repoSlug\n\t\t},\n\n\t\tresolvedAppName() {\n\t\t\tif (this.appName) {\n\t\t\t\treturn this.appName\n\t\t\t}\n\t\t\tconst slug = this.repoSlug\n\t\t\tif (!slug) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn slug.charAt(0).toUpperCase() + slug.slice(1)\n\t\t},\n\n\t\tresolvedAppStoreUrl() {\n\t\t\treturn this.appStoreUrl || (this.resolvedAppSlug\n\t\t\t\t? `https://apps.nextcloud.com/apps/${this.resolvedAppSlug}`\n\t\t\t\t: '')\n\t\t},\n\n\t\tresolvedFeatureRequestUrl() {\n\t\t\tif (this.featureRequestUrl) {\n\t\t\t\treturn this.featureRequestUrl\n\t\t\t}\n\t\t\tif (!this.repo) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// The feature-request issue FORM, not the blank new-issue page —\n\t\t\t// the form's structured fields replaced the removed in-product\n\t\t\t// suggestion modal.\n\t\t\treturn buildFeatureRequestUrl(this.forge, this.repo)\n\t\t},\n\n\t\t/**\n\t\t * The Suggest CTA's target: the host's `suggestUrl` override when\n\t\t * given (a public form, a Discord channel), else the forge's\n\t\t * feature-request issue form.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSuggestUrl() {\n\t\t\treturn this.suggestUrl || this.resolvedFeatureRequestUrl\n\t\t},\n\n\t\tdocumentationUrlIsExternal() {\n\t\t\treturn /^https?:\\/\\//i.test(this.resolvedDocumentationUrl)\n\t\t},\n\n\t\tdocsNoteLeading() {\n\t\t\treturn t('nextcloud-vue', 'Looking for documentation? Visit')\n\t\t},\n\n\t\tdocsNoteLinkLabel() {\n\t\t\t// Strip protocol for a cleaner inline link label.\n\t\t\treturn this.resolvedDocumentationUrl.replace(/^https?:\\/\\//i, '')\n\t\t},\n\n\t\tdocsNoteTrailing() {\n\t\t\treturn t('nextcloud-vue', 'for all technical and user documentation.')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A comparison that goes away leaves the reader on a stop that no\n\t\t// longer exists: the panel's v-else-if still matches, so it renders an\n\t\t// empty CnCapabilityTable under a \"Capabilities\" heading until the\n\t\t// reader thinks to press the toggle.\n\t\tavailableViews(views) {\n\t\t\tif (!views.includes(this.activeView)) {\n\t\t\t\tthis.activeView = views[0]\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.publishHoistedSidebar()\n\t},\n\n\tbeforeUnmount() {\n\t\t// Clear the holder so the hoisted sidebar disappears when the\n\t\t// user navigates away from this route. Mirrors CnIndexPage.\n\t\tif (this.cnHostsIndexSidebar && this.cnIndexSidebarConfig) {\n\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * @param {string} view One of the `availableViews`.\n\t\t * @return {string} The heading for that view.\n\t\t */\n\t\tviewTitle(view) {\n\t\t\tif (view === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Capabilities')\n\t\t\t}\n\t\t\treturn view === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Features')\n\t\t},\n\n\t\ttoggleView() {\n\t\t\tthis.activeView = this.nextView\n\t\t},\n\n\t\topenSupportDialog() {\n\t\t\tthis.showSupportDialog = true\n\t\t},\n\n\t\t/**\n\t\t * Publish the hoisted-sidebar config to `cnIndexSidebarConfig`\n\t\t * (same holder CnIndexPage uses) so CnAppRoot mounts the\n\t\t * CnFeaturesAndRoadmapSidebar at NcContent level. The sidebar\n\t\t * emits `suggest` when the user clicks the Suggest CTA inside\n\t\t * it; the view forwards that to `openSuggestModal()`. No-op\n\t\t * when there's no CnAppRoot ancestor.\n\t\t */\n\t\tpublishHoistedSidebar() {\n\t\t\tif (!this.cnHostsIndexSidebar || !this.cnIndexSidebarConfig) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.disabled) {\n\t\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.cnIndexSidebarConfig.value = {\n\t\t\t\t// markRaw: see CnIndexPage's publishHoistedSidebar — the holder\n\t\t\t\t// is deeply reactive and would proxy the component definition.\n\t\t\t\tcomponent: markRaw(CnFeaturesAndRoadmapSidebar),\n\t\t\t\tprops: {\n\t\t\t\t\topenbuiltUrl: this.resolvedOpenbuiltUrl,\n\t\t\t\t\tllmSkillsUrl: this.resolvedLlmSkillsUrl,\n\t\t\t\t\t// Always a URL now (the removed in-product modal was the\n\t\t\t\t\t// only non-link path), so the sidebar CTA always renders\n\t\t\t\t\t// as an anchor and its `suggest` emit path stays idle.\n\t\t\t\t\tsuggestUrl: this.resolvedSuggestUrl,\n\t\t\t\t},\n\n\t\t\t\tlisteners: {\n\t\t\t\t\tsupport: () => this.openSupportDialog(),\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-and-roadmap-view {\n\tmax-width: 1200px;\n\tmargin: 0 auto;\n\tpadding: 24px 16px;\n}\n\n.cn-features-and-roadmap-view__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-bottom: 24px;\n\tflex-wrap: wrap;\n\t/* Clear the Nextcloud navigation toggle button (44px wide, absolutely\n\t positioned at the left edge of .app-content) plus 12px breathing\n\t room. Only the HEADER shifts right — the panel below keeps full width. */\n\tpadding-inline-start: 56px;\n}\n\n.cn-features-and-roadmap-view__title {\n\tfont-size: 1.4em;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-and-roadmap-view__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: center;\n\tflex-wrap: wrap;\n}\n\n.cn-features-and-roadmap-view__panel {\n\tmin-height: 320px;\n}\n\n/* Underline the inline docs URL so it reads as a link inside the note\n body — NcNoteCard's default link styling drops the underline by\n inheriting from the host theme. */\n.cn-features-and-roadmap-view__docs-note :deep(a) {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-features-and-roadmap-view__docs-note :deep(a:hover) {\n\ttext-decoration: underline;\n\topacity: 0.85;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_Fragment","_createElementVNode","_toDisplayString","_createBlock","_createTextVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnFeaturesAndRoadmapView.vue3.js","sources":["../../../../src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue?vue&type=template&id=7c44400f&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-features-and-roadmap-view\">\n\t\t<div v-if=\"disabled\" class=\"cn-features-and-roadmap-view__disabled\">\n\t\t\t<NcEmptyContent :name=\"disabledTitle\" :description=\"disabledDescription\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LockOutline :size=\"48\" />\n\t\t\t\t</template>\n\t\t\t</NcEmptyContent>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<header class=\"cn-features-and-roadmap-view__header\">\n\t\t\t\t<h2 class=\"cn-features-and-roadmap-view__title\">\n\t\t\t\t\t{{ headerTitle }}\n\t\t\t\t</h2>\n\t\t\t\t<div class=\"cn-features-and-roadmap-view__actions\">\n\t\t\t\t\t<NcButton @click=\"toggleView\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<TableSearch v-if=\"nextView === 'capabilities'\" :size=\"20\" />\n\t\t\t\t\t\t\t<RoadVariant v-else-if=\"nextView === 'roadmap'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FormatListBulleted v-else :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ toggleLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<!-- Direct link to the forge's feature-request issue form\n\t\t\t\t\t (team decision 2026-09-04: the in-product suggestion\n\t\t\t\t\t modal is gone — the forge is where the conversation\n\t\t\t\t\t happens, in English). -->\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"resolvedSuggestUrl\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:href=\"resolvedSuggestUrl\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ suggestLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</header>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"resolvedDocumentationUrl\"\n\t\t\t\ttype=\"info\"\n\t\t\t\tclass=\"cn-features-and-roadmap-view__docs-note\">\n\t\t\t\t{{ docsNoteLeading }}\n\t\t\t\t<a\n\t\t\t\t\t:href=\"resolvedDocumentationUrl\"\n\t\t\t\t\t:target=\"documentationUrlIsExternal ? '_blank' : null\"\n\t\t\t\t\t:rel=\"documentationUrlIsExternal ? 'noopener noreferrer' : null\">{{ docsNoteLinkLabel }}</a>\n\t\t\t\t{{ docsNoteTrailing }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<main class=\"cn-features-and-roadmap-view__panel\">\n\t\t\t\t<CnFeaturesTab v-if=\"activeView === 'features'\" :features=\"features\" />\n\t\t\t\t<CnCapabilityTable\n\t\t\t\t\tv-else-if=\"activeView === 'capabilities'\"\n\t\t\t\t\t:comparison=\"capabilityComparison\" />\n\t\t\t\t<CnRoadmapTab v-else :repo=\"repo\" />\n\t\t\t</main>\n\n\t\t\t<CnSupportDialog\n\t\t\t\tv-if=\"showSupportDialog\"\n\t\t\t\t:appName=\"resolvedAppName\"\n\t\t\t\t:appSlug=\"resolvedAppSlug\"\n\t\t\t\t:appStoreUrl=\"resolvedAppStoreUrl\"\n\t\t\t\t:featureRequestUrl=\"resolvedFeatureRequestUrl\"\n\t\t\t\t:donateUrl=\"donateUrl\"\n\t\t\t\t:supportUrl=\"supportUrl\"\n\t\t\t\t:founderName=\"founderName\"\n\t\t\t\t:founderTitle=\"founderTitle\"\n\t\t\t\t@close=\"showSupportDialog = false\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\n/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * CnFeaturesAndRoadmapView — route-level container for the Features &\n * Roadmap surface. Header carries the view-toggle and the primary\n * Suggest-feature CTA. Body is a card grid (Features OR Roadmap).\n *\n * The right-edge sidebar is hoisted to NcContent level via the same\n * `cnIndexSidebarConfig` provide mechanism CnIndexPage uses for its\n * CnIndexSidebar — that's the only place where Nextcloud's right-edge\n * sidebar slot renders correctly (next to NcAppContent, not inside it).\n * When mounted under CnAppRoot the view publishes\n * `CnFeaturesAndRoadmapSidebar` + props into the holder on mounted()\n * and clears it on beforeUnmount(). Sidebar carries four pitch\n * sections: Suggest, OpenBuilt, LLM, Support. Suggest renders as an\n * anchor to the forge's feature-request issue form (`resolvedSuggestUrl`\n * is always passed); Support emits `@support` (forwarded to a\n * freshly-mounted `CnSupportDialog`).\n *\n * Sidebar link targets are overridable via the `openbuiltUrl` and\n * `llmSkillsUrl` props. Defaults: in-instance `/apps/openbuilt` (via\n * `generateUrl`) and `https://docs.conduction.nl/ai-skills`.\n *\n * Spec: features-roadmap-component — Requirement \"CnFeaturesAndRoadmapView\".\n */\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcNoteCard } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RoadVariant from 'vue-material-design-icons/RoadVariant.vue'\nimport TableSearch from 'vue-material-design-icons/TableSearch.vue'\nimport CnCapabilityTable from '../CnCapabilityTable/CnCapabilityTable.vue'\nimport CnFeaturesAndRoadmapSidebar from '../CnFeaturesAndRoadmapSidebar/CnFeaturesAndRoadmapSidebar.vue'\nimport CnFeaturesTab from '../CnFeaturesTab/CnFeaturesTab.vue'\nimport CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'\nimport CnSupportDialog from '../CnSupportDialog/CnSupportDialog.vue'\nimport { warnCapabilityComparisonDeprecated } from '../../utils/capabilityComparison.js'\nimport { buildFeatureRequestUrl, DEFAULT_FORGE } from '../../utils/forge.js'\n\nconst DEFAULT_OPENBUILT_PATH = '/apps/openbuilt'\nconst DEFAULT_LLM_SKILLS_URL = 'https://docs.conduction.nl/ai-skills'\nconst DEFAULT_DONATE_URL = 'https://github.com/sponsors/ConductionNL'\nconst DEFAULT_SUPPORT_URL = 'https://www.conduction.nl/support'\nconst DEFAULT_FOUNDER_NAME = 'Ruben van der Linde'\nconst DEFAULT_FOUNDER_TITLE = 'a founder of Conduction'\n\nexport default {\n\tname: 'CnFeaturesAndRoadmapView',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcNoteCard,\n\t\tFormatListBulleted,\n\t\tLockOutline,\n\t\tPlus,\n\t\tRoadVariant,\n\t\tTableSearch,\n\t\tCnCapabilityTable,\n\t\tCnFeaturesTab,\n\t\tCnRoadmapTab,\n\t\tCnSupportDialog,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Set to `true` by CnAppRoot so descendants know the host will\n\t\t * mount a hoisted sidebar at NcContent level (see CnAppRoot.vue\n\t\t * `cnHostsIndexSidebar: true`). When `false` (no CnAppRoot\n\t\t * ancestor) the view skips publishing — there's no inline\n\t\t * fallback for this surface.\n\t\t */\n\t\tcnHostsIndexSidebar: { default: false },\n\t\t/**\n\t\t * Reactive holder published by CnAppRoot that backs the\n\t\t * hoisted-sidebar render in NcContent. Set value to\n\t\t * `{component, props, listeners}` to mount; set to `null` to\n\t\t * unmount. Same holder CnIndexPage uses.\n\t\t */\n\t\tcnIndexSidebarConfig: { default: () => ({ value: null }) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * `<owner>/<repo>` of the app's repository on the forge.\n\t\t */\n\t\trepo: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * Target forge the feature-request deep-links are built against\n\t\t * (the Suggest CTA and the support dialog's feature-request URL).\n\t\t * Set `{type: 'github'}` (optionally with `baseUrl`) to switch\n\t\t * forge.\n\t\t *\n\t\t * @type {{type: 'codeberg'|'forgejo'|'gitea'|'github', baseUrl?: string}}\n\t\t */\n\t\tforge: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_FORGE }),\n\t\t},\n\n\t\t/**\n\t\t * Build-time feature manifest (alphabetical list rendered by CnFeaturesTab).\n\t\t *\n\t\t * @type {Array<{slug: string, title: string, summary: string, docsUrl: string}>}\n\t\t */\n\t\tfeatures: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Admin opt-out flag — when true the entire view collapses to a\n\t\t * single \"disabled by admin\" empty state.\n\t\t */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Optional capability comparison. When supplied, the header toggle\n\t\t * gains a third stop that renders `CnCapabilityTable`: a searchable\n\t\t * table of the rows, grouped by feature or by area, with a\n\t\t * provided-by column naming which app or platform delivers each one.\n\t\t *\n\t\t * Shape: `{systems, areas, features?, providers?, capabilities}`. The\n\t\t * rows carry the competitor ratings they already carry, plus the\n\t\t * optional `provider`, `providerHow`, `feature` and\n\t\t * `featureConfidence` fields.\n\t\t *\n\t\t * When null (default) the toggle keeps its two stops and the view\n\t\t * renders exactly as it does for every app that passes nothing.\n\t\t *\n\t\t * Deprecated: publish the comparison on the app's docs site and link to it (as dossiq #3312 and pipelinq #2195 did). Still rendered; removal waits for a major version.\n\t\t * A development build warns once when it is set.\n\t\t *\n\t\t * @deprecated since 2.66.0, publish the comparison on the app's docs site and link to it.\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-the-in-app-capability-comparison-is-deprecated\n\t\t * @type {object|null}\n\t\t */\n\t\tcapabilityComparison: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Override the OpenBuilt sidebar CTA target. Pass an absolute URL or\n\t\t * a Nextcloud-relative path. When unset, defaults to the in-instance\n\t\t * OpenBuilt route at `/apps/openbuilt` (resolved via `generateUrl`).\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\topenbuiltUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Override the LLM-skills sidebar CTA target. Defaults to\n\t\t * `https://docs.conduction.nl/ai-skills`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tllmSkillsUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional override for the Suggest CTA inside the sidebar. When\n\t\t * set the CTA renders as an anchor pointing at this URL —\n\t\t * appropriate when the app routes feature suggestions through a\n\t\t * public form, a Discord channel, or any non-forge target. When\n\t\t * empty (default) the CTA links the forge's feature-request\n\t\t * issue form derived from `repo` + `forge`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsuggestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Optional URL of the app's public documentation site. When set,\n\t\t * an info banner is rendered above the card grid pointing users at\n\t\t * `<docs>` for full technical + user docs. Per-app — pipelinq\n\t\t * passes `https://pipelinq.conduction.nl`, decidesk passes its\n\t\t * own, etc. When empty (default) no banner renders.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Display name of the host app, used as the title of the bundled\n\t\t * `CnSupportDialog` and interpolated into its body copy. When\n\t\t * empty (default) the View derives a humanised label from `repo`\n\t\t * — `ConductionNL/openregister` becomes `Openregister` — which is\n\t\t * fine for stock apps and overridable for camelCased names.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappName: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Kebab-case host-app id passed to `CnSupportDialog` (its\n\t\t * localStorage namespace). When empty (default) the View derives\n\t\t * it from `repo` — second segment lower-cased.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappSlug: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Nextcloud App Store listing URL for the host app, threaded\n\t\t * through to `CnSupportDialog`. When empty (default) the View\n\t\t * derives `https://apps.nextcloud.com/apps/{appSlug}`.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tappStoreUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * URL the \"Suggest a feature\" CTA inside `CnSupportDialog` opens.\n\t\t * When empty (default) the View derives\n\t\t * `<forge-base>/{repo}/issues/new` from the `forge` prop. This is\n\t\t * intentionally separate from the prop-of-the-same-name on the\n\t\t * sidebar's Suggest CTA — that one opens the modal-driven flow;\n\t\t * this one is the \"open on the forge\" fallback used by the support\n\t\t * dialog, which is meant for the casual visitor rather than the\n\t\t * existing suggest-feature loop.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfeatureRequestUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Donate-CTA URL passed to `CnSupportDialog`. Defaults to\n\t\t * ConductionNL's GitHub Sponsors page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tdonateUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_DONATE_URL,\n\t\t},\n\n\t\t/**\n\t\t * Business-support CTA URL passed to `CnSupportDialog`. Defaults\n\t\t * to the Conduction contact page.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tsupportUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_SUPPORT_URL,\n\t\t},\n\n\t\t/**\n\t\t * Name rendered in the handwritten signature of `CnSupportDialog`.\n\t\t * Defaults to Ruben van der Linde.\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderName: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_NAME,\n\t\t},\n\n\t\t/**\n\t\t * Title shown after the handwritten signature in `CnSupportDialog`\n\t\t * (e.g. \"Founder\", \"Oprichter\").\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\tfounderTitle: {\n\t\t\ttype: String,\n\t\t\tdefault: DEFAULT_FOUNDER_TITLE,\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tactiveView: 'features',\n\t\t\tshowSupportDialog: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The stops the header toggle cycles through. Without a capability\n\t\t * comparison this is the two-stop list every consumer has today, and\n\t\t * the toggle behaves exactly as it did.\n\t\t *\n\t\t * @return {Array<string>} The views, in cycle order.\n\t\t */\n\t\tavailableViews() {\n\t\t\treturn this.capabilityComparison\n\t\t\t\t? ['features', 'capabilities', 'roadmap']\n\t\t\t\t: ['features', 'roadmap']\n\t\t},\n\n\t\t/**\n\t\t * @return {string} The view the toggle moves to next.\n\t\t */\n\t\tnextView() {\n\t\t\tconst views = this.availableViews\n\t\t\tconst index = views.indexOf(this.activeView)\n\t\t\treturn views[(index + 1) % views.length]\n\t\t},\n\n\t\theaderTitle() {\n\t\t\treturn this.viewTitle(this.activeView)\n\t\t},\n\n\t\ttoggleLabel() {\n\t\t\tif (this.nextView === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Show capabilities')\n\t\t\t}\n\t\t\treturn this.nextView === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Show roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Show features')\n\t\t},\n\n\t\tsuggestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Suggest feature')\n\t\t},\n\n\t\tdisabledTitle() {\n\t\t\treturn t('nextcloud-vue', 'This feature has been disabled by your administrator')\n\t\t},\n\n\t\tdisabledDescription() {\n\t\t\treturn t('nextcloud-vue', 'Contact your Nextcloud administrator to enable Features & Roadmap on this instance.')\n\t\t},\n\n\t\tresolvedOpenbuiltUrl() {\n\t\t\tif (this.openbuiltUrl) {\n\t\t\t\treturn this.openbuiltUrl\n\t\t\t}\n\t\t\treturn generateUrl(DEFAULT_OPENBUILT_PATH)\n\t\t},\n\n\t\tresolvedLlmSkillsUrl() {\n\t\t\treturn this.llmSkillsUrl || DEFAULT_LLM_SKILLS_URL\n\t\t},\n\n\t\tresolvedDocumentationUrl() {\n\t\t\treturn this.documentationUrl || ''\n\t\t},\n\n\t\trepoSlug() {\n\t\t\tif (!this.repo || !this.repo.includes('/')) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.repo.split('/')[1].toLowerCase()\n\t\t},\n\n\t\tresolvedAppSlug() {\n\t\t\treturn this.appSlug || this.repoSlug\n\t\t},\n\n\t\tresolvedAppName() {\n\t\t\tif (this.appName) {\n\t\t\t\treturn this.appName\n\t\t\t}\n\t\t\tconst slug = this.repoSlug\n\t\t\tif (!slug) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn slug.charAt(0).toUpperCase() + slug.slice(1)\n\t\t},\n\n\t\tresolvedAppStoreUrl() {\n\t\t\treturn this.appStoreUrl || (this.resolvedAppSlug\n\t\t\t\t? `https://apps.nextcloud.com/apps/${this.resolvedAppSlug}`\n\t\t\t\t: '')\n\t\t},\n\n\t\tresolvedFeatureRequestUrl() {\n\t\t\tif (this.featureRequestUrl) {\n\t\t\t\treturn this.featureRequestUrl\n\t\t\t}\n\t\t\tif (!this.repo) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// The feature-request issue FORM, not the blank new-issue page —\n\t\t\t// the form's structured fields replaced the removed in-product\n\t\t\t// suggestion modal.\n\t\t\treturn buildFeatureRequestUrl(this.forge, this.repo)\n\t\t},\n\n\t\t/**\n\t\t * The Suggest CTA's target: the host's `suggestUrl` override when\n\t\t * given (a public form, a Discord channel), else the forge's\n\t\t * feature-request issue form.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSuggestUrl() {\n\t\t\treturn this.suggestUrl || this.resolvedFeatureRequestUrl\n\t\t},\n\n\t\tdocumentationUrlIsExternal() {\n\t\t\treturn /^https?:\\/\\//i.test(this.resolvedDocumentationUrl)\n\t\t},\n\n\t\tdocsNoteLeading() {\n\t\t\treturn t('nextcloud-vue', 'Looking for documentation? Visit')\n\t\t},\n\n\t\tdocsNoteLinkLabel() {\n\t\t\t// Strip protocol for a cleaner inline link label.\n\t\t\treturn this.resolvedDocumentationUrl.replace(/^https?:\\/\\//i, '')\n\t\t},\n\n\t\tdocsNoteTrailing() {\n\t\t\treturn t('nextcloud-vue', 'for all technical and user documentation.')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A comparison that goes away leaves the reader on a stop that no\n\t\t// longer exists: the panel's v-else-if still matches, so it renders an\n\t\t// empty CnCapabilityTable under a \"Capabilities\" heading until the\n\t\t// reader thinks to press the toggle.\n\t\tavailableViews(views) {\n\t\t\tif (!views.includes(this.activeView)) {\n\t\t\t\tthis.activeView = views[0]\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.publishHoistedSidebar()\n\t\tif (this.capabilityComparison) {\n\t\t\twarnCapabilityComparisonDeprecated('CnFeaturesAndRoadmapView `capabilityComparison`')\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\t// Clear the holder so the hoisted sidebar disappears when the\n\t\t// user navigates away from this route. Mirrors CnIndexPage.\n\t\tif (this.cnHostsIndexSidebar && this.cnIndexSidebarConfig) {\n\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * @param {string} view One of the `availableViews`.\n\t\t * @return {string} The heading for that view.\n\t\t */\n\t\tviewTitle(view) {\n\t\t\tif (view === 'capabilities') {\n\t\t\t\treturn t('nextcloud-vue', 'Capabilities')\n\t\t\t}\n\t\t\treturn view === 'roadmap'\n\t\t\t\t? t('nextcloud-vue', 'Roadmap')\n\t\t\t\t: t('nextcloud-vue', 'Features')\n\t\t},\n\n\t\ttoggleView() {\n\t\t\tthis.activeView = this.nextView\n\t\t},\n\n\t\topenSupportDialog() {\n\t\t\tthis.showSupportDialog = true\n\t\t},\n\n\t\t/**\n\t\t * Publish the hoisted-sidebar config to `cnIndexSidebarConfig`\n\t\t * (same holder CnIndexPage uses) so CnAppRoot mounts the\n\t\t * CnFeaturesAndRoadmapSidebar at NcContent level. The sidebar\n\t\t * emits `suggest` when the user clicks the Suggest CTA inside\n\t\t * it; the view forwards that to `openSuggestModal()`. No-op\n\t\t * when there's no CnAppRoot ancestor.\n\t\t */\n\t\tpublishHoistedSidebar() {\n\t\t\tif (!this.cnHostsIndexSidebar || !this.cnIndexSidebarConfig) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.disabled) {\n\t\t\t\tthis.cnIndexSidebarConfig.value = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.cnIndexSidebarConfig.value = {\n\t\t\t\t// markRaw: see CnIndexPage's publishHoistedSidebar — the holder\n\t\t\t\t// is deeply reactive and would proxy the component definition.\n\t\t\t\tcomponent: markRaw(CnFeaturesAndRoadmapSidebar),\n\t\t\t\tprops: {\n\t\t\t\t\topenbuiltUrl: this.resolvedOpenbuiltUrl,\n\t\t\t\t\tllmSkillsUrl: this.resolvedLlmSkillsUrl,\n\t\t\t\t\t// Always a URL now (the removed in-product modal was the\n\t\t\t\t\t// only non-link path), so the sidebar CTA always renders\n\t\t\t\t\t// as an anchor and its `suggest` emit path stays idle.\n\t\t\t\t\tsuggestUrl: this.resolvedSuggestUrl,\n\t\t\t\t},\n\n\t\t\t\tlisteners: {\n\t\t\t\t\tsupport: () => this.openSupportDialog(),\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-features-and-roadmap-view {\n\tmax-width: 1200px;\n\tmargin: 0 auto;\n\tpadding: 24px 16px;\n}\n\n.cn-features-and-roadmap-view__header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-bottom: 24px;\n\tflex-wrap: wrap;\n\t/* Clear the Nextcloud navigation toggle button (44px wide, absolutely\n\t positioned at the left edge of .app-content) plus 12px breathing\n\t room. Only the HEADER shifts right — the panel below keeps full width. */\n\tpadding-inline-start: 56px;\n}\n\n.cn-features-and-roadmap-view__title {\n\tfont-size: 1.4em;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-features-and-roadmap-view__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: center;\n\tflex-wrap: wrap;\n}\n\n.cn-features-and-roadmap-view__panel {\n\tmin-height: 320px;\n}\n\n/* Underline the inline docs URL so it reads as a link inside the note\n body — NcNoteCard's default link styling drops the underline by\n inheriting from the host theme. */\n.cn-features-and-roadmap-view__docs-note :deep(a) {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-features-and-roadmap-view__docs-note :deep(a:hover) {\n\ttext-decoration: underline;\n\topacity: 0.85;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_Fragment","_createElementVNode","_toDisplayString","_createBlock","_createTextVNode"],"mappings":"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"}
|
|
@@ -1936,6 +1936,26 @@ var script = {
|
|
|
1936
1936
|
* @param {object} field The field definition
|
|
1937
1937
|
* @return {boolean}
|
|
1938
1938
|
*/
|
|
1939
|
+
/**
|
|
1940
|
+
* The text a select option shows: its label, else a display name, a
|
|
1941
|
+
* name or the id. A plain string option (a tag) is its own text.
|
|
1942
|
+
*
|
|
1943
|
+
* @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md
|
|
1944
|
+
* @param {object|string} option The option (slot props).
|
|
1945
|
+
* @return {string} The text.
|
|
1946
|
+
*/
|
|
1947
|
+
optionText(option) {
|
|
1948
|
+
if (option === null || option === undefined) {
|
|
1949
|
+
return ''
|
|
1950
|
+
}
|
|
1951
|
+
if (typeof option !== 'object') {
|
|
1952
|
+
return String(option)
|
|
1953
|
+
}
|
|
1954
|
+
const text = [option.label, option.displayName, option.name, option.id]
|
|
1955
|
+
.find((v) => v !== undefined && v !== null && v !== '');
|
|
1956
|
+
return text === undefined ? '' : String(text)
|
|
1957
|
+
},
|
|
1958
|
+
|
|
1939
1959
|
isUserField(field) {
|
|
1940
1960
|
return !!(field && field.userPicker && !field.userPicker.multiple)
|
|
1941
1961
|
},
|
|
@@ -3003,8 +3023,15 @@ var script = {
|
|
|
3003
3023
|
if (this.isIdPickerField(field)) {
|
|
3004
3024
|
// Reference / user fields store the chosen id (UUID / UID),
|
|
3005
3025
|
// not the full option. Cache its label so the selection displays.
|
|
3026
|
+
// A user option can carry only `displayName` (NcSelectUsers' own
|
|
3027
|
+
// shape), and without that fallback the chip showed the uid.
|
|
3006
3028
|
if (option && option.id) {
|
|
3007
|
-
|
|
3029
|
+
const name = option.label || option.displayName || this.referenceLabels[option.id];
|
|
3030
|
+
if (name) {
|
|
3031
|
+
this.referenceLabels = { ...this.referenceLabels, [option.id]: name };
|
|
3032
|
+
} else if (this.isUserField(field)) {
|
|
3033
|
+
this.resolveUserLabel(option.id);
|
|
3034
|
+
}
|
|
3008
3035
|
}
|
|
3009
3036
|
this.updateField(field.key, option ? option.id : null);
|
|
3010
3037
|
// Reference options are label-only ({id,label}) — pass the id so
|
|
@@ -3071,7 +3098,7 @@ var script = {
|
|
|
3071
3098
|
const labels = {};
|
|
3072
3099
|
for (const o of list) {
|
|
3073
3100
|
if (o && o.id) {
|
|
3074
|
-
labels[o.id] = o.label || String(o.id);
|
|
3101
|
+
labels[o.id] = o.label || o.displayName || this.referenceLabels[o.id] || String(o.id);
|
|
3075
3102
|
}
|
|
3076
3103
|
}
|
|
3077
3104
|
if (Object.keys(labels).length > 0) {
|