You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(connectors): add dynamic selectors to knowledge base connector config
Replace manual ID text inputs with dynamic selector dropdowns that fetch
options from the existing selector registry. Users can toggle between
selector and manual input via canonical pairs (basic/advanced mode).
Adds selector support to 12 connectors: Airtable (cascading base→table),
Slack, Gmail, Google Calendar, Linear (cascading team→project), Jira,
Confluence, MS Teams (cascading team→channel), Notion, Asana, Webflow,
and Outlook. Dependency clearing propagates across canonical siblings to
prevent stale cross-mode data on submit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The add-connector modal renders these automatically — no custom UI needed.
119
119
120
+
Three field types are supported: `short-input`, `dropdown`, and `selector`.
121
+
120
122
```typescript
121
123
// Text input
122
124
{
@@ -141,6 +143,136 @@ The add-connector modal renders these automatically — no custom UI needed.
141
143
}
142
144
```
143
145
146
+
## Dynamic Selectors (Canonical Pairs)
147
+
148
+
Use `type: 'selector'` to fetch options dynamically from the existing selector registry (`hooks/selectors/registry.ts`). Selectors are always paired with a manual fallback input using the **canonical pair** pattern — a `selector` field (basic mode) and a `short-input` field (advanced mode) linked by `canonicalParamId`.
149
+
150
+
The user sees a toggle button (ArrowLeftRight) to switch between the selector dropdown and manual text input. On submit, the modal resolves each canonical pair to the active mode's value, keyed by `canonicalParamId`.
151
+
152
+
### Rules
153
+
154
+
1.**Every selector field MUST have a canonical pair** — a corresponding `short-input` (or `dropdown`) field with the same `canonicalParamId` and `mode: 'advanced'`.
155
+
2.**`required` must be set identically on both fields** in a pair. If the selector is required, the manual input must also be required.
156
+
3.**`canonicalParamId` must match the key the connector expects in `sourceConfig`** (e.g. `baseId`, `channel`, `teamId`). The advanced field's `id` should typically match `canonicalParamId`.
157
+
4.**`dependsOn` references the selector field's `id`**, not the `canonicalParamId`. The modal propagates dependency clearing across canonical siblings automatically — changing either field in a parent pair clears dependent children.
158
+
159
+
### Selector canonical pair example (Airtable base → table cascade)
160
+
161
+
```typescript
162
+
configFields: [
163
+
// Base: selector (basic) + manual (advanced)
164
+
{
165
+
id: 'baseSelector',
166
+
title: 'Base',
167
+
type: 'selector',
168
+
selectorKey: 'airtable.bases', // Must exist in hooks/selectors/registry.ts
169
+
canonicalParamId: 'baseId',
170
+
mode: 'basic',
171
+
placeholder: 'Select a base',
172
+
required: true,
173
+
},
174
+
{
175
+
id: 'baseId',
176
+
title: 'Base ID',
177
+
type: 'short-input',
178
+
canonicalParamId: 'baseId',
179
+
mode: 'advanced',
180
+
placeholder: 'e.g. appXXXXXXXXXXXXXX',
181
+
required: true,
182
+
},
183
+
// Table: selector depends on base (basic) + manual (advanced)
184
+
{
185
+
id: 'tableSelector',
186
+
title: 'Table',
187
+
type: 'selector',
188
+
selectorKey: 'airtable.tables',
189
+
canonicalParamId: 'tableIdOrName',
190
+
mode: 'basic',
191
+
dependsOn: ['baseSelector'], // References the selector field ID
### Selector with domain dependency (Jira/Confluence pattern)
210
+
211
+
When a selector depends on a plain `short-input` field (no canonical pair), `dependsOn` references that field's `id` directly. The `domain` field's value maps to `SelectorContext.domain` automatically via `SELECTOR_CONTEXT_FIELDS`.
212
+
213
+
```typescript
214
+
configFields: [
215
+
{
216
+
id: 'domain',
217
+
title: 'Jira Domain',
218
+
type: 'short-input',
219
+
placeholder: 'yoursite.atlassian.net',
220
+
required: true,
221
+
},
222
+
{
223
+
id: 'projectSelector',
224
+
title: 'Project',
225
+
type: 'selector',
226
+
selectorKey: 'jira.projects',
227
+
canonicalParamId: 'projectKey',
228
+
mode: 'basic',
229
+
dependsOn: ['domain'],
230
+
placeholder: 'Select a project',
231
+
required: true,
232
+
},
233
+
{
234
+
id: 'projectKey',
235
+
title: 'Project Key',
236
+
type: 'short-input',
237
+
canonicalParamId: 'projectKey',
238
+
mode: 'advanced',
239
+
placeholder: 'e.g. ENG, PROJ',
240
+
required: true,
241
+
},
242
+
]
243
+
```
244
+
245
+
### How `dependsOn` maps to `SelectorContext`
246
+
247
+
The connector selector field builds a `SelectorContext` from dependency values. For the mapping to work, each dependency's `canonicalParamId` (or field `id` for non-canonical fields) must exist in `SELECTOR_CONTEXT_FIELDS` (`lib/workflows/subblocks/context.ts`):
0 commit comments