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 {