From 8b148150946f504630e8d877efd999c5b6bdf6c5 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Tue, 5 May 2026 10:33:53 -0300 Subject: [PATCH] feat(components): pro_only flag + registry merge API + picker gate hook Three small additions so private overlays can add components gated behind a paid subscription without forking the picker: - types/component-metadata.ts: optional pro_only?: boolean field on ComponentMetadata. Self-hosters never set it; picker behaves identically. - services/componentRegistry.ts: new mergeComponents() public method. Pro overlay calls this after the default registry has loaded to splice in extra components (replacing any with the same id). - components/ComponentPickerModal.tsx: when a pro_only component is clicked, the picker first calls window.__velxio_pro_gate__(component) if defined. If the gate returns true, the click is consumed (overlay shows an upgrade modal). If absent or returns false, the click passes through to onSelectComponent as normal. Net upstream change: ~25 lines, all backwards-compatible. OSS image behaves exactly as before since no overlay sets pro_only or installs the gate. --- frontend/src/components/ComponentPickerModal.tsx | 13 ++++++++++++- frontend/src/types/component-metadata.ts | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ComponentPickerModal.tsx b/frontend/src/components/ComponentPickerModal.tsx index d3e29b3b..3eec2dd6 100644 --- a/frontend/src/components/ComponentPickerModal.tsx +++ b/frontend/src/components/ComponentPickerModal.tsx @@ -259,7 +259,18 @@ export const ComponentPickerModal: React.FC = ({ onSelectComponent(component)} + onSelect={() => { + // Pro overlays can intercept clicks on pro_only + // components by setting window.__velxio_pro_gate__. + // Returning true means "handled — do not pass through". + if (component.pro_only) { + const gate = (window as unknown as { + __velxio_pro_gate__?: (c: typeof component) => boolean; + }).__velxio_pro_gate__; + if (gate && gate(component)) return; + } + onSelectComponent(component); + }} /> )) )} diff --git a/frontend/src/types/component-metadata.ts b/frontend/src/types/component-metadata.ts index 1865ea68..0dc5428b 100644 --- a/frontend/src/types/component-metadata.ts +++ b/frontend/src/types/component-metadata.ts @@ -41,6 +41,12 @@ export interface ComponentMetadata { defaultValues: Record; pinCount: number; tags: string[]; // For search functionality + // Optional flag set by private overlays (e.g. velxio.dev) to mark a + // component as gated behind a paid subscription. The OSS image never + // sets this — self-hosters have everything unlocked. The picker can + // delegate the click on a pro_only component to a window-level gate + // (see ComponentPickerModal) which the overlay implements. + pro_only?: boolean; } export interface ComponentMetadataCollection {