Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Vrij en open source onder de EUPL-1.2-licentie.

**Ondersteuning:** Voor ondersteuning, neem contact op via support@conduction.nl.
]]></description>
<version>0.3.7</version>
<version>0.3.8</version>
<licence>agpl</licence>
<author mail="info@conduction.nl" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>OpenBuilt</namespace>
Expand Down
31 changes: 12 additions & 19 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<!-- SPDX-License-Identifier: EUPL-1.2 -->
<!--
OpenBuilt app shell. Mounts CnAppRoot with the bundled manifest and the
customComponents registry; CnAppRoot handles the OpenRegister dependency
OpenBuilt app shell. Mounts CnAppRoot with the bundled manifest and the v2
kind-tagged registry (ADR-036); CnAppRoot handles the OpenRegister dependency
check, renders CnAppNav from manifest.menu, and routes <router-view> pages
through CnPageRenderer. The #dependency-missing slot keeps OpenBuilt's
original "OpenRegister is required" empty state.

@adr ADR-024 (app manifest) β€” OpenBuilt is now Tier-1+ (its own shell is
manifest-driven, like the virtual apps it builds).
@adr ADR-036 (v2 registry) β€” all consumer components are registered via the
`registry` prop; the deprecated `customComponents` prop is no longer used.
-->
<template>
<CnAppRoot
app-id="openbuilt"
:manifest="manifest"
:custom-components="customComponents"
:page-types="pageTypes"
:registry="registry"
:page-types="pageTypes"
:translate="translateForApp"
:permissions="permissions">
<template #dependency-missing>
Expand Down Expand Up @@ -74,12 +75,16 @@ export default {
required: true,
},
/**
* Registry of consumer-injected components used by `type: "custom"`
* pages (`page.component`) and other manifest slot overrides.
* V2 kind-tagged registry (ADR-036) β€” map of registry key β†’
* `{ kind: "page", component }`. CnPageRenderer resolves every
* manifest-referenced component name (type:"custom" pages,
* cardComponent, headerComponent, actionsComponent,
* sidebarTabs[].component) against the `kind: "page"` entries here.
* Replaces the deprecated `customComponents` prop.
*
* @type {object}
*/
customComponents: {
registry: {
type: Object,
default: () => ({}),
},
Expand All @@ -93,18 +98,6 @@ export default {
type: Object,
default: null,
},
/**
* v2 component registry β€” map of registry key β†’ `{ kind, component, ...metadata }`.
* Passed to CnAppRoot which validates kinds at mounted() time and provides the
* map to descendants via `cnRegistry`. Resolves type:"custom" page components,
* modals, and future widget/form-field/cell-renderer entries.
*
* @type {object}
*/
registry: {
type: Object,
default: () => ({}),
},
},

computed: {
Expand Down
89 changes: 0 additions & 89 deletions src/customComponents.js

This file was deleted.

17 changes: 6 additions & 11 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import pinia from './pinia.js'
import App from './App.vue'
import bundledManifest from './manifest.json'
import customComponents from './customComponents.js'
import registry from './registry.js'

// Library CSS β€” must be an explicit import (webpack tree-shakes side-effect imports from aliased packages).
Expand Down Expand Up @@ -93,15 +92,12 @@ const router = new VueRouter({

tryLoadTranslations()

// Pass shallow copies of the registry maps β€” the lib exports
// `defaultPageTypes` (and consumers' `customComponents`) as frozen module
// objects in some bundle shapes, and Vue.extend() mutates component
// definitions to attach `_Ctor`. Cloning yields extensible objects without
// changing the values the lib resolves at render time.
// Pass shallow copies of the registry maps β€” the lib exports `defaultPageTypes`
// (and consumers' `registry`) as frozen module objects in some bundle shapes,
// and Vue.extend() mutates component definitions to attach `_Ctor`. Cloning
// yields extensible objects without changing the values the lib resolves at
// render time.
const pageTypesProp = { ...defaultPageTypes }
const customComponentsProp = { ...customComponents }
// Shallow-clone the v2 registry for the same reason β€” Vue.extend() attaches
// `_Ctor` to component definitions, so the entries need to be extensible.
const registryProp = { ...registry }

// Create the Vue instance β€” this installs Pinia and sets it active, so the
Expand All @@ -114,9 +110,8 @@ new Vue({
render: h => h(App, {
props: {
manifest: bundledManifest,
customComponents: customComponentsProp,
pageTypes: pageTypesProp,
registry: registryProp,
pageTypes: pageTypesProp,
},
}),
}).$mount('#content')
159 changes: 100 additions & 59 deletions src/registry.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,120 @@
// SPDX-License-Identifier: EUPL-1.2
//
// v2 component registry for OpenBuilt β€” passed as the `registry` prop on
// CnAppRoot. Each entry declares its `kind` so CnAppRoot can validate and
// dispatch components correctly (REQ-MVR-002).
//
// Recognised kinds (RegistryKindError.js):
// widget β€” renderable in page slots (body, sidebar, tab:*, section:*)
// modal β€” openable via cnOpenModal(key, props)
// page β€” full custom page component (for type:"custom" pages)
// form-field β€” field type injected into CnFormPage
// cell-renderer β€” column formatter in CnIndexPage tables
//
// All `type: "custom"` pages in manifest.json reference their component via
// `pages[].component`; those components are registered here as kind: "page".
//
// Custom tabs on VirtualAppDetail (ApplicationManifestTab, ApplicationVersionsTab,
// ApplicationDiffTab, ApplicationIconTab) are referenced via
// config.sidebarTabs[].component β€” those remain resolved through
// customComponents (carry-forward prop) until a dedicated "sidebar-tab" kind
// lands in the lib. They are NOT in this registry.
//
// Custom cards and header/actions components are resolved via customComponents
// too β€” they are pass-through component references in config.cardComponent,
// pages[].headerComponent, and pages[].actionsComponent.
//
// See ADR-024 (app manifest) and ADR-036 (manifest v2).
/**
* OpenBuilt v2 component registry (ADR-036).
*
* Kind-tagged map passed as the `registry` prop to CnAppRoot. CnPageRenderer
* resolves every manifest-referenced component name (type:"custom" pages,
* cardComponent, headerComponent, actionsComponent, sidebarTabs[].component)
* against this map. Only entries with `kind === "page"` are used for page and
* slot-override dispatch β€” the `kind` field is the discriminator CnPageRenderer
* keys on (see `resolveCustomComponent` in CnPageRenderer.vue). Future entry
* kinds (`"modal"`, `"widget"`, `"form-field"`, `"cell-renderer"`) will be
* added here as the library ships support for them.
*
* Replace the deprecated `customComponents` prop: all components previously
* passed through `customComponents` are now registered here with
* `kind: "page"` so CnPageRenderer resolves them through a single v2 path and
* CnAppRoot stops emitting the "customComponents is deprecated" console warning.
*
* Resolution order at runtime (CnPageRenderer):
* 1. Built-in page types (CnIndexPage, CnDetailPage, CnDashboardPage, …)
* 2. Built-in widget types (data, metadata, audit-trail, version-info, …)
* 3. registry (this file) ← all consumer-injected components (ADR-036)
*
* See ADR-024 (app manifest) and ADR-036 (manifest v2 kind-tagged registry).
*
* SPDX-License-Identifier: EUPL-1.2
* SPDX-FileCopyrightText: 2026 Conduction B.V.
*/

// ── Virtual apps β€” index card ─────────────────────────────────────────────────

// VirtualApps index card β€” name, status pill, version, "live" marker, caller's
// role; click navigates to VirtualAppDetail.
import ApplicationCard from './components/ApplicationCard.vue'

// ── Virtual apps β€” detail sidebar tabs ───────────────────────────────────────

// VirtualAppDetail sidebar tab: raw-JSON manifest editor (the visual designer
// lives at /builder/:slug/pages).
import ApplicationManifestTab from './components/tabs/ApplicationManifestTab.vue'

// VirtualAppDetail sidebar tab: version history + rollback.
import ApplicationVersionsTab from './components/tabs/ApplicationVersionsTab.vue'

// VirtualAppDetail sidebar tab: manifest diff between versions.
import ApplicationDiffTab from './components/tabs/ApplicationDiffTab.vue'

// VirtualAppDetail sidebar tab: icon upload and preview.
import ApplicationIconTab from './components/tabs/ApplicationIconTab.vue'

// ── Virtual apps β€” actions components ────────────────────────────────────────

// VirtualApps index actions bar β€” "Add application" button that opens the
// four-step CreateApplicationWizard (openbuilt-app-creation-wizard).
import VirtualAppsActions from './components/VirtualAppsActions.vue'

// VirtualAppDetail actions bar β€” Publish (OR lifecycle transition), Manage
// permissions (PermissionsModal, ADR-004 modal isolation), Design pages, Open
// virtual app.
import ApplicationDetailActions from './components/ApplicationDetailActions.vue'

// ── Virtual apps β€” detail header ──────────────────────────────────────────────

// VirtualAppDetail headerComponent (openbuilt-app-detail-overview
// REQ-OBADO-001 / REQ-OBADO-011) β€” purpose-built maintainer dashboard
// replacing the generic main-area data widget. Owns hero strip + version pill
// tabs + window toggle + KPI grid + activity chart + structural widgets.
import ApplicationDetailHeader from './components/applicationDetail/ApplicationDetailHeader.vue'

// ── Custom page components (kind: "page") ────────────────────────────────────

// Visual schema designer β€” three-pane canvas with drag-and-drop field
// placement. Handles both /schemas (shortcut) and /builder/:slug/schemas[/:id].
import SchemaDesignerView from './views/SchemaDesigner.vue'

// Template gallery β€” browse seeded ApplicationTemplate records and clone
// one into a new virtual app (openbuilt-templates-marketplace).

// Visual manifest page designer β€” three-pane editor that reads and writes
// a virtual app's manifest via PATCH (REQ-OBPD-003).
// Visual manifest page designer β€” three-pane editor that reads and writes a
// virtual app's manifest via PATCH (REQ-OBPD-003).
import PageDesignerView from './views/PageDesignerHost.vue'

// Export-jobs status list β€” Phase-2 "export to real Nextcloud app" runs.

// Virtual-app host β€” nested CnAppRoot rendering a virtual app's own manifest.
import BuilderHostView from './views/BuilderHost.vue'

// Features & Roadmap page β€” wrapper around CnFeaturesAndRoadmapView.
// ── Helper ───────────────────────────────────────────────────────────────────

/**
* Wrap a Vue component into the v2 registry shape required by CnAppRoot's
* `registry` prop (`kind: "page"` is the discriminator CnPageRenderer keys
* page and slot-override dispatch off).
*
* @param {object} component Vue component options.
*
* @return {object} A `{ kind: "page", component }` registry entry.
*/
function page(component) {
return { kind: 'page', component }
}

// ── Widget components (kind: "widget") ───────────────────────────────────────
// ── Registry export ──────────────────────────────────────────────────────────

// (No consumer-registered widget kinds in the initial v2 migration.
// The dashboard stats-block widgets use the lib's built-in "stats-block"
// widget key and do not need a registry entry here.)
export default {
// VirtualApps index card component.
ApplicationCard: page(ApplicationCard),

// ── Modal components (kind: "modal") ─────────────────────────────────────────
// VirtualAppDetail sidebar tabs.
ApplicationManifestTab: page(ApplicationManifestTab),
ApplicationVersionsTab: page(ApplicationVersionsTab),
ApplicationDiffTab: page(ApplicationDiffTab),
ApplicationIconTab: page(ApplicationIconTab),

// (No modal entries for the initial v2 migration.
// The CreateApplicationWizard and PermissionsModal are opened imperatively
// by VirtualAppsActions / ApplicationDetailActions components rather than
// via cnOpenModal(); they will be registered here when those components
// are refactored to use the modal registry pattern.)
// Actions bar components.
VirtualAppsActions: page(VirtualAppsActions),
ApplicationDetailActions: page(ApplicationDetailActions),

// ── Registry export ──────────────────────────────────────────────────────────
// Header component for the maintainer dashboard (detail page override).
ApplicationDetailHeader: page(ApplicationDetailHeader),

export default {
// Custom page components β€” resolved by CnPageRenderer for type:"custom" pages.
SchemaDesignerView: {
kind: 'page',
component: SchemaDesignerView,
},
PageDesignerView: {
kind: 'page',
component: PageDesignerView,
},
BuilderHostView: {
kind: 'page',
component: BuilderHostView,
},
SchemaDesignerView: page(SchemaDesignerView),
PageDesignerView: page(PageDesignerView),
BuilderHostView: page(BuilderHostView),
}