Appearance
Connector custom controls ​
Connectors can supply embedded HTML controls for their settings forms. The app renders a generic iframe, supplies public deployment configuration, and saves the control's string value through the normal form submission flow. The connector owns its markup, scripts, external SDKs, and validation.
Register a control ​
Embed the HTML file in the connector assembly and register a ConnectorViewAsset with AddViewAsset. Set its content type and provide an Open delegate that returns a new stream for each request. Add the control to a form with WithEmbeddedField, using the registered asset path and the persisted setting's property name. Use configuration for connector-owned options and defaultValue for the current field value.
Asset retrieval requires an authenticated user with connector read permission in the selected administration. Form submissions retain their existing update permission and server-side validation.
Supply public configuration ​
The app forwards the complete runtimeConfig.public.connectorControls object to every control. The control selects its own namespace. Connector field options and public deployment values are separate objects; neither overrides the other.
Google Sheets reads publicConfiguration.googlePicker.clientId and publicConfiguration.googlePicker.apiKey. Configure these app environment variables:
| Environment variable | Aspire parameter | Pipeline variable |
|---|---|---|
NUXT_PUBLIC_CONNECTOR_CONTROLS_GOOGLE_PICKER_CLIENT_ID | google-sheet-client-id | googlesheet-ClientId |
NUXT_PUBLIC_CONNECTOR_CONTROLS_GOOGLE_PICKER_API_KEY | google-picker-api-key | googlesheet-PickerApiKey |
Nuxt declares empty defaults and reads overrides at process startup. Restart the app after changing environment variables. Adding a new public configuration namespace requires configuration and deployment declarations, but no connector-specific renderer changes.
These values are browser-visible, even when their deployment parameters are marked secret. Never place OAuth client secrets, application bearer tokens, or private connector settings in this namespace. Keep Google browser key restrictions and authorized JavaScript origins aligned with the app's local and deployed origins.
Message lifecycle ​
Messages use { source: 'ocp-connector-embedded-control', type, payload }. Both sides verify the sender window, exact origin, message type, and payload shape. Send to the exact app origin.
| Direction | Type | Payload |
|---|---|---|
| Control → app | ready | None |
| App → control | initialize | configuration, publicConfiguration, string value, boolean disabled |
| Control → app | initialized | None |
| App → control | setValue | String value |
| App → control | setDisabled | Boolean disabled |
| Control → app | valueChanged | String value |
| Control → app | error | String message |
| Control → app | setPresentation | Boolean expanded |
| App → control | presentationChanged | Boolean expanded |
Install the control's listener before sending ready. Initialization can arrive more than once, including after configuration changes, and must be idempotent. Acknowledge configuration receipt immediately; SDK loading is a separate control state. The app displays a retry action if acknowledgement does not arrive within ten seconds.
The app ignores value changes while a field is disabled. Controls must also disable their interactions. Keep manual entry usable if an optional external picker is unavailable.
Use expanded presentation for dialogs that need more room than the inline frame. Wait for presentationChanged before measuring the frame or opening the dialog. The same iframe remains mounted. Request inline presentation on success, cancellation, or failure, and dispose external dialogs and pending work when the control unloads.
Trust and browser policies ​
Controls are trusted executable code shipped with connector assemblies. Same-origin iframes provide layout separation, not a security boundary against the app. Do not use this mechanism for untrusted uploaded HTML.
Assets are served without caching and permit same-origin framing. A connector can supply a CSP for the external scripts and frames its control needs. Google allowlists belong to the Google connector asset, not the app document. Normal app pages continue to deny embedding.
The app uses a popup-compatible opener policy across entry pages because client-side navigation to connector settings does not reload document headers. Keep authorization calls tied to a user click; preload external SDKs beforehand. Failed loads should support retry without reloading the whole settings page.
Verify configuration and behavior ​
Check that both Aspire parameters are non-empty, that the running app receives the matching environment variable names, and that the browser's public control configuration and initialization payload contain the values. Inspect presence without logging credentials. For deployment testing, build with empty defaults and supply values only when starting the production server.
Verify manual entry, selection, cancellation, retry after script failure, authorization failure, disabled fields, and saving/reloading the selected value. Test direct navigation and navigation from another app page. A successful mocked SDK test does not replace selecting a real spreadsheet with the configured Google project.