api_binding_ui 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,384 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # API Binding Studio (api_binding_ui)
2
+
3
+ API Binding Studio (`api_binding_ui`) is an isolated, browser-side visual API mapping, simulation, and verification engine designed for enterprise web applications. It enables engineers, business analysts, and QA specialists to visually bind user interface components and action buttons to backend REST/OpenAPI endpoints, inspect live request payloads, and execute interactive end-to-end simulations without modifying production code or sending destructive mutations to live servers.
4
+
5
+ ---
6
+
7
+ ## Architecture Overview
8
+
9
+ The system is engineered as an independent package that mounts into any React application while operating its management workspace in a dedicated browser window.
10
+
11
+ ### 1. Canonical Single-Configuration Model (`src/core/model.ts`)
12
+ The studio is driven by a single canonical configuration (`ApiBindingConfig`) containing:
13
+ - Ordered API calls (`calls: BindingCall[]`) covering both data retrieval (`read`) and action execution (`write`).
14
+ - Component declarations (`fields: BindingField[]`) reflecting all interactive form controls on the screen.
15
+ - Execution parameters, customer iteration rules (`customerRule: "each" | "single"`), post-execution behaviors (`success: "close" | "keep"`), and template population policies.
16
+
17
+ All workspace views, data grids, request payload previews, and simulation runners derive from this single state structure. When API contracts change or are reselected, the reconciliation engine automatically preserves matching parameter assignments while reporting removed or newly required parameters.
18
+
19
+ ### 2. Universal Screen Context and DOM Discovery (`src/core/bindingContext.ts`)
20
+ The `<BindApiButton />` component is screen-agnostic and can be mounted within:
21
+ - Modal dialogs (`[role="dialog"]`, `<dialog>`, `.ds-dialog`)
22
+ - Slide-out drawers and flyout panels (`[role="region"]`, `[data-drawer]`, `<aside>`)
23
+ - Dedicated forms (`<form>`)
24
+ - Full-page views and standalone screens
25
+
26
+ The context extractor progressively inspects the DOM hierarchy using explicit references (`containerRef`, `containerSelector`, `rootElement`, `formId`) or heuristic boundary detection. It automatically discovers form inputs, selects, textareas, custom ARIA widgets (`role="combobox"`, `role="textbox"`, `role="checkbox"`, `role="switch"`), and action buttons.
27
+
28
+ ### 3. Multi-Channel Cross-Window Transport (`src/core/transport.ts`)
29
+ The studio operates in a dedicated browser window identified by a stable key (`api-binding:<key>`). Re-clicking the trigger button re-focuses the existing tab, preserving in-progress unsaved configurations. Communication between the host application and the studio window utilizes three redundant layers:
30
+ 1. Bidirectional `window.postMessage` handshake (`api-binding-ready` and `api-binding-context`).
31
+ 2. `BroadcastChannel("api-binding-studio")` for real-time grid selection updates and cross-tab synchronization.
32
+ 3. Structured `localStorage` persistence keys for context, draft configurations, and applied bindings across page refreshes.
33
+
34
+ ### 4. Design System CSS Custom Property Inheritance (`src/styles/highradius-bridge.css`)
35
+ The studio UI relies entirely on design token custom properties:
36
+ - Background and surfaces: `--ds-color-background`, `--ds-color-surface`, `--ds-color-surface-subtle`
37
+ - Text and typography: `--ds-color-foreground`, `--ds-color-foreground-muted`, `--ds-font-family-sans`, `--ds-font-family-mono`
38
+ - Borders and focus: `--ds-color-border`, `--ds-color-focus`
39
+ - Accents and feedback: `--ds-color-primary`, `--ds-color-destructive`, `--ds-color-warning`, `--ds-color-success`
40
+ - Layout geometry: `--ds-space-*`, `--ds-radius-*`
41
+
42
+ When rendered within an application that declares these variables, the studio immediately inherits the host styling. Standalone fallback values are bundled to ensure rendering fidelity when loaded outside the parent shell.
43
+
44
+ ### 5. Zero-Network Simulation and Verification Engine (`src/components/SimulationDialog.tsx`)
45
+ Before any configuration can be marked as verified or applied:
46
+ - The engine constructs actual HTTP request payloads (headers, query parameters, path variables, request bodies).
47
+ - Dynamic value coercion handles arrays (`number[]`, `string[]`), booleans, nested object dot-paths (`customer.billing.id`), and static text overrides.
48
+ - An interactive simulation environment executes the mapped submission against sample datasets without initiating actual network fetch operations.
49
+ - Verifications validate all required fields, parameter constraints, and record iterators before recording a cryptographic-style fingerprint of the verified configuration.
50
+
51
+ ---
52
+
53
+ ## Installation
54
+
55
+ Add `api_binding_ui` to your application dependencies:
56
+
57
+ ```json
58
+ {
59
+ "dependencies": {
60
+ "api_binding_ui": "file:../api_binding_ui"
61
+ }
62
+ }
63
+ ```
64
+
65
+ Or via your package manager when using an internal package registry:
66
+
67
+ ```bash
68
+ npm install api_binding_ui
69
+ # or
70
+ yarn add api_binding_ui
71
+ # or
72
+ bun add api_binding_ui
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Host Application Setup
78
+
79
+ ### Step 1: Register the Studio Route
80
+
81
+ The studio window opens at the path `/api-binding/$key`. Configure your application router to mount the `ApiBindingWorkspace` component at this path.
82
+
83
+ #### TanStack Router (`src/routes/api-binding.$key.tsx`)
84
+ ```tsx
85
+ import { createFileRoute } from "@tanstack/react-router";
86
+ import { ApiBindingWorkspace } from "api_binding_ui";
87
+ import "api_binding_ui/style.css";
88
+
89
+ export const Route = createFileRoute("/api-binding/$key")({
90
+ head: () => ({
91
+ meta: [
92
+ { title: "API Binding Studio" },
93
+ { name: "description", content: "Visual API mapping and simulation studio." },
94
+ ],
95
+ }),
96
+ component: ApiBindingRouteView,
97
+ });
98
+
99
+ function ApiBindingRouteView() {
100
+ const { key } = Route.useParams();
101
+ return <ApiBindingWorkspace key={key} pageKey={key} />;
102
+ }
103
+ ```
104
+
105
+ #### React Router (`src/App.tsx`)
106
+ ```tsx
107
+ import { BrowserRouter, Routes, Route, useParams } from "react-router-dom";
108
+ import { ApiBindingWorkspace } from "api_binding_ui";
109
+ import "api_binding_ui/style.css";
110
+
111
+ function ApiBindingPage() {
112
+ const { key = "default" } = useParams();
113
+ return <ApiBindingWorkspace key={key} pageKey={key} />;
114
+ }
115
+
116
+ export function App() {
117
+ return (
118
+ <BrowserRouter>
119
+ <Routes>
120
+ <Route path="/api-binding/:key" element={<ApiBindingPage />} />
121
+ {/* Application routes */}
122
+ </Routes>
123
+ </BrowserRouter>
124
+ );
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ### Step 2: Mount `<BindApiButton />` on Host Screens
131
+
132
+ Place `<BindApiButton />` inside your modal dialogs, slide-out drawers, form panels, or screen toolbars.
133
+
134
+ #### Example A: Dialog with Data Grid Context
135
+ ```tsx
136
+ import * as React from "react";
137
+ import { BindApiButton } from "api_binding_ui";
138
+
139
+ interface CustomerRecord {
140
+ customerId: number;
141
+ customerName: string;
142
+ accountNumber: string;
143
+ }
144
+
145
+ export function AddNotesModal({
146
+ open,
147
+ onClose,
148
+ selectedCustomers,
149
+ }: {
150
+ open: boolean;
151
+ onClose: () => void;
152
+ selectedCustomers: CustomerRecord[];
153
+ }) {
154
+ if (!open) return null;
155
+
156
+ const noteTypeOptions = [
157
+ { value: "1", label: "General Follow-up" },
158
+ { value: "2", label: "Promise to Pay" },
159
+ ];
160
+
161
+ const templateOptions = [
162
+ {
163
+ value: "tmpl-payment",
164
+ label: "Payment Reminder",
165
+ subject: "Pending statement follow-up",
166
+ description: "Contacted accounts payable regarding past-due invoices.",
167
+ },
168
+ ];
169
+
170
+ return (
171
+ <div role="dialog" aria-labelledby="modal-heading" className="modal-container">
172
+ <h2 id="modal-heading">Add Customer Note</h2>
173
+
174
+ <div className="form-group">
175
+ <label htmlFor="notesType">Note Type</label>
176
+ <select id="notesType" name="notesType" required>
177
+ {noteTypeOptions.map((opt) => (
178
+ <option key={opt.value} value={opt.value}>
179
+ {opt.label}
180
+ </option>
181
+ ))}
182
+ </select>
183
+ </div>
184
+
185
+ <div className="form-group">
186
+ <label htmlFor="subject">Subject</label>
187
+ <input id="subject" name="subject" required />
188
+ </div>
189
+
190
+ <div className="form-group">
191
+ <label htmlFor="notes">Description</label>
192
+ <textarea id="notes" name="notes" required />
193
+ </div>
194
+
195
+ <div className="modal-actions">
196
+ <button type="button" onClick={onClose}>
197
+ Cancel
198
+ </button>
199
+ <button type="submit">
200
+ Save Note
201
+ </button>
202
+
203
+ <BindApiButton
204
+ appId="collections"
205
+ windowId="worklist-add-notes"
206
+ source="Add Notes Dialog"
207
+ parent="Worklist Grid"
208
+ grid={{
209
+ gridId: "worklistUIGrid",
210
+ rows: selectedCustomers,
211
+ }}
212
+ options={{
213
+ notesType: noteTypeOptions,
214
+ template: templateOptions,
215
+ }}
216
+ />
217
+ </div>
218
+ </div>
219
+ );
220
+ }
221
+ ```
222
+
223
+ #### Example B: Standalone Screen or Drawer without a Grid
224
+ ```tsx
225
+ import * as React from "react";
226
+ import { BindApiButton } from "api_binding_ui";
227
+
228
+ export function CreateDisputeDrawer({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
229
+ if (!isOpen) return null;
230
+
231
+ const reasonCodes = [
232
+ { value: "PRICING_ERROR", label: "Pricing Discrepancy" },
233
+ { value: "DAMAGED_GOODS", label: "Damaged Goods" },
234
+ ];
235
+
236
+ return (
237
+ <aside className="drawer-panel" aria-label="Create Dispute">
238
+ <h2>Create Dispute</h2>
239
+
240
+ <form id="dispute-form">
241
+ <label htmlFor="reasonCode">Reason Code</label>
242
+ <select id="reasonCode" name="reasonCode" required>
243
+ {reasonCodes.map((r) => (
244
+ <option key={r.value} value={r.value}>{r.label}</option>
245
+ ))}
246
+ </select>
247
+
248
+ <label htmlFor="amount">Disputed Amount</label>
249
+ <input id="amount" name="amount" type="number" required />
250
+
251
+ <label htmlFor="comments">Comments</label>
252
+ <textarea id="comments" name="comments" />
253
+
254
+ <div className="drawer-footer">
255
+ <button type="button" onClick={onClose}>Cancel</button>
256
+ <button type="submit">Submit Dispute</button>
257
+
258
+ <BindApiButton
259
+ appId="receivables"
260
+ windowId="dispute-create"
261
+ source="Create Dispute"
262
+ parent="Disputes Management"
263
+ options={{
264
+ reasonCode: reasonCodes,
265
+ }}
266
+ />
267
+ </div>
268
+ </form>
269
+ </aside>
270
+ );
271
+ }
272
+ ```
273
+
274
+ ---
275
+
276
+ ## Component API Specification
277
+
278
+ ### `<BindApiButton />` Props
279
+
280
+ | Prop | Type | Default | Description |
281
+ | :--- | :--- | :--- | :--- |
282
+ | `appId` | `string` | `"default"` | Identifier of the parent application module. |
283
+ | `windowId` | `string` | *(Required)* | Stable identifier of the screen, dialog, or form. |
284
+ | `source` | `string` | *(Required)* | Human-readable title of the current screen or action. |
285
+ | `parent` | `string` | *(Required)* | Human-readable title of the parent view or grid. |
286
+ | `grid` | `BindingGrid` | `undefined` | Grid context including `gridId`, `columns`, and `rows` when actions target selected records. |
287
+ | `parents` | `SnapshotContext[]` | `undefined` | Array of parent context hierarchies for multi-level navigation. |
288
+ | `kind` | `WindowKind` | Automatic | `"dialog" \| "drawer" \| "screen" \| "action-form"`. Detected automatically if omitted. |
289
+ | `trigger` | `WindowTrigger` | `undefined` | Metadata regarding the invocation trigger (button text, hotkey, icon). |
290
+ | `options` | `Record<string, BindingOption[]>` | `{}` | Lookup options for select and combobox fields. Supports `{ value, label, subject, description }`. |
291
+ | `definitions` | `ApiDefinition[]` | `[]` | Registered API endpoints catalog available for mapping. |
292
+ | `mappingFile` | `MappingFile \| null` | `null` | Pre-existing mapping file to preload. |
293
+ | `routeBase` | `string` | `"/api-binding"` | Studio route base path configured in the application router. |
294
+ | `containerRef` | `React.RefObject<HTMLElement>` | `undefined` | Explicit DOM reference to the container element. |
295
+ | `containerSelector` | `string` | `undefined` | CSS selector used to locate the enclosing container element. |
296
+ | `rootElement` | `HTMLElement \| null` | `undefined` | Direct DOM element override. |
297
+ | `formId` | `string` | `undefined` | Target HTML `<form id="...">` identifier. |
298
+ | `components` | `SnapshotComponent[]` | `undefined` | Explicit components list overriding automatic DOM discovery. |
299
+ | `buttons` | `string[]` | `undefined` | Explicit action buttons list overriding automatic DOM discovery. |
300
+ | `className` | `string` | `""` | Additional CSS class for the button. |
301
+ | `children` | `React.ReactNode` | `"Bind API"` | Button label or children nodes. |
302
+
303
+ ---
304
+
305
+ ## DOM Discovery Protocol
306
+
307
+ When `<BindApiButton />` is clicked, the context extraction pipeline executes the following sequence:
308
+
309
+ 1. **Container Resolution**:
310
+ - Checks `rootElement`, `containerRef`, `containerSelector`, and `formId`.
311
+ - Searches upward for `role="dialog"`, `<dialog>`, or `.ds-dialog`.
312
+ - Searches upward for `<form>`.
313
+ - Searches upward for `role="region"`, `[data-drawer]`, `<aside>`, `<main>`, `<section>`, `.drawer`, or `.panel`.
314
+ - Traverses parent elements to locate any ancestor containing at least two input elements.
315
+ - Falls back to `document.body`.
316
+
317
+ 2. **Form Control Discovery**:
318
+ - Evaluates all standard controls (`input`, `select`, `textarea`) and custom ARIA controls (`role="combobox"`, `role="textbox"`, `role="checkbox"`, `role="switch"`).
319
+ - Resolves control names from `name`, `id`, `data-testid`, `data-field`, or `aria-label`.
320
+ - Extracts labels from `<label for="...">`, parent `<label>`, `aria-labelledby`, `aria-label`, or field placeholders.
321
+ - Captures current input values and state (`checked`, `value`, `data-value`).
322
+
323
+ 3. **Action Button Discovery**:
324
+ - Evaluates buttons and submit inputs within the container.
325
+ - Excludes modal close controls, minimize/maximize adornments, dismiss triggers, and API binding triggers.
326
+ - Captures active primary and secondary action labels (e.g., `"Save"`, `"Submit"`, `"Cancel"`, `"Reset"`).
327
+
328
+ ---
329
+
330
+ ## Consuming Applied Bindings
331
+
332
+ When an engineer or analyst clicks **Apply Binding** inside the Studio, the verified configuration is stored in `localStorage` under `api-binding:applied:<key>` and broadcast across active windows.
333
+
334
+ Host components can listen to applied bindings using the `useAppliedBinding` hook:
335
+
336
+ ```tsx
337
+ import { useAppliedBinding } from "api_binding_ui";
338
+
339
+ export function AddNotesModal() {
340
+ const { isApplied, appliedConfig, appliedAt } = useAppliedBinding(
341
+ "collections.worklistUIGrid.worklist-add-notes"
342
+ );
343
+
344
+ React.useEffect(() => {
345
+ if (isApplied && appliedConfig) {
346
+ console.log("Applied API Binding Configuration:", appliedConfig);
347
+ // Access appliedConfig.calls, appliedConfig.fields, appliedConfig.customerRule
348
+ }
349
+ }, [isApplied, appliedConfig]);
350
+
351
+ // Render modal
352
+ }
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Testing and Verification
358
+
359
+ Run test suites using the bundled test runner:
360
+
361
+ ```bash
362
+ bun test
363
+ # or
364
+ yarn test
365
+ ```
366
+
367
+ Build the distribution package:
368
+
369
+ ```bash
370
+ bun run build
371
+ # or
372
+ yarn build
373
+ ```
374
+
375
+ The build produces:
376
+ - `dist/index.js`: Browser ES module bundle.
377
+ - `dist/api-binding-ui.css`: Compiled style bundle with `--ds-*` design token bridges.
378
+ - `dist/**/*.d.ts`: TypeScript declaration definitions.
379
+
380
+ ---
381
+
382
+ ## License
383
+
384
+ Internal enterprise package. All rights reserved.
package/SKILL.md ADDED
@@ -0,0 +1,294 @@
1
+ ---
2
+ name: api-binding-ui
3
+ description: Instructions for AI agents to integrate API Binding Studio (api_binding_ui) into React application screens, modals, drawers, and forms.
4
+ ---
5
+
6
+ # API Binding Studio Integration Skill
7
+
8
+ This skill defines the operational standards and technical requirements for AI agents integrating `api_binding_ui` into frontend codebases. Follow these instructions whenever adding, updating, or maintaining action dialogs, flyout drawers, forms, or data grid action bars.
9
+
10
+ ---
11
+
12
+ ## 1. When to Apply This Skill
13
+
14
+ Apply this skill whenever any of the following conditions are met:
15
+ 1. The user asks to bind, map, or connect a screen, dialog, modal, drawer, or action form to backend APIs.
16
+ 2. The user requests an API binding trigger button on an action modal (such as "Add Notes", "Create P2P", "Dispute Invoice", "Edit Customer", "Assign Task").
17
+ 3. The application requires visual mapping, request payload previewing, or zero-network simulated validation across selected table/grid records.
18
+ 4. Setting up the `/api-binding/$key` studio route in an application router.
19
+
20
+ ---
21
+
22
+ ## 2. Dependency Verification
23
+
24
+ Verify that `package.json` contains `api_binding_ui`:
25
+
26
+ ```json
27
+ {
28
+ "dependencies": {
29
+ "api_binding_ui": "file:../api_binding_ui"
30
+ }
31
+ }
32
+ ```
33
+
34
+ If missing, add it to dependencies or prompt the user to link the local package.
35
+
36
+ ---
37
+
38
+ ## 3. Router Setup Requirements
39
+
40
+ Every application using `api_binding_ui` must declare a route matching `/api-binding/$key`.
41
+
42
+ ### TanStack Router (`src/routes/api-binding.$key.tsx`)
43
+ ```tsx
44
+ import { createFileRoute } from "@tanstack/react-router";
45
+ import { ApiBindingWorkspace } from "api_binding_ui";
46
+ import "api_binding_ui/style.css";
47
+
48
+ export const Route = createFileRoute("/api-binding/$key")({
49
+ head: () => ({
50
+ meta: [{ title: "API Binding Studio" }],
51
+ }),
52
+ component: ApiBindingRouteView,
53
+ });
54
+
55
+ function ApiBindingRouteView() {
56
+ const { key } = Route.useParams();
57
+ return <ApiBindingWorkspace key={key} pageKey={key} />;
58
+ }
59
+ ```
60
+
61
+ ### React Router (`src/routes/ApiBindingRoute.tsx`)
62
+ ```tsx
63
+ import { useParams } from "react-router-dom";
64
+ import { ApiBindingWorkspace } from "api_binding_ui";
65
+ import "api_binding_ui/style.css";
66
+
67
+ export function ApiBindingRoute() {
68
+ const { key = "default" } = useParams<{ key: string }>();
69
+ return <ApiBindingWorkspace key={key} pageKey={key} />;
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 4. Host Form Integration Rules
76
+
77
+ When rendering forms, modals, or drawers that will be bound to APIs, AI agents MUST follow these structural rules:
78
+
79
+ ### Rule 1: Form Inputs Must Have Identifiers
80
+ Every form control (`<input>`, `<select>`, `<textarea>`, or custom combobox) MUST have a `name` attribute matching the semantic field name (or an `id`, `data-testid`, or `aria-label`).
81
+ - Correct: `<input name="subject" required />`
82
+ - Incorrect: `<input placeholder="Subject" />` (lacks identifier, will be ignored by DOM discovery).
83
+
84
+ ### Rule 2: Explicit Labels
85
+ Controls should be paired with standard `<label htmlFor="...">` tags or include `aria-label`. The studio extracts human-readable field labels from these associations.
86
+
87
+ ### Rule 3: Pass Dropdown Options via `options` Prop
88
+ Dropdown choices captured in state or constants must be passed to `<BindApiButton />` using the `options` prop:
89
+ ```tsx
90
+ const noteTypeOptions = [
91
+ { value: "1", label: "General Follow-up" },
92
+ { value: "2", label: "Promise to Pay" },
93
+ ];
94
+
95
+ const templateOptions = [
96
+ {
97
+ value: "tmpl-1",
98
+ label: "Payment Reminder",
99
+ subject: "Payment Reminder",
100
+ description: "Followed up with customer regarding pending invoices.",
101
+ },
102
+ ];
103
+
104
+ <BindApiButton
105
+ // ...
106
+ options={{
107
+ notesType: noteTypeOptions,
108
+ template: templateOptions,
109
+ }}
110
+ />
111
+ ```
112
+
113
+ ### Rule 4: Pass Grid Selection when Targeting Grid Rows
114
+ If the action is triggered from or operates on selected table/grid rows:
115
+ ```tsx
116
+ <BindApiButton
117
+ // ...
118
+ grid={{
119
+ gridId: "worklistUIGrid",
120
+ rows: selectedRecords,
121
+ }}
122
+ />
123
+ ```
124
+ If the screen does not operate on a grid (such as a standalone dispute creation screen or customer profile editor), omit the `grid` prop. The studio automatically adapts its target context view and simulation engine to direct screen input.
125
+
126
+ ---
127
+
128
+ ## 5. Complete Implementation Patterns
129
+
130
+ ### Pattern A: Modal Dialog Triggered from a Data Grid
131
+ ```tsx
132
+ import * as React from "react";
133
+ import { BindApiButton } from "api_binding_ui";
134
+
135
+ interface CustomerRecord {
136
+ customerId: number;
137
+ customerName: string;
138
+ }
139
+
140
+ export function AddNotesModal({
141
+ open,
142
+ onClose,
143
+ selectedCustomers,
144
+ }: {
145
+ open: boolean;
146
+ onClose: () => void;
147
+ selectedCustomers: CustomerRecord[];
148
+ }) {
149
+ if (!open) return null;
150
+
151
+ const noteTypeOptions = [
152
+ { value: "1", label: "General Follow-up" },
153
+ { value: "2", label: "Promise to Pay" },
154
+ ];
155
+
156
+ const templateOptions = [
157
+ {
158
+ value: "tmpl-payment",
159
+ label: "Payment Reminder",
160
+ subject: "Reminder: Invoice due",
161
+ description: "Contacted customer regarding pending balance.",
162
+ },
163
+ ];
164
+
165
+ return (
166
+ <div role="dialog" aria-labelledby="dialog-title" className="ds-dialog">
167
+ <h2 id="dialog-title">Add Notes</h2>
168
+
169
+ <div className="form-group">
170
+ <label htmlFor="notesType">Notes Type</label>
171
+ <select id="notesType" name="notesType" required>
172
+ {noteTypeOptions.map((opt) => (
173
+ <option key={opt.value} value={opt.value}>{opt.label}</option>
174
+ ))}
175
+ </select>
176
+ </div>
177
+
178
+ <div className="form-group">
179
+ <label htmlFor="subject">Subject</label>
180
+ <input id="subject" name="subject" required />
181
+ </div>
182
+
183
+ <div className="form-group">
184
+ <label htmlFor="notes">Description</label>
185
+ <textarea id="notes" name="notes" required />
186
+ </div>
187
+
188
+ <div className="button-bar">
189
+ <button type="button" onClick={onClose}>Cancel</button>
190
+ <button type="submit">Submit</button>
191
+
192
+ <BindApiButton
193
+ appId="collections"
194
+ windowId="worklist-add-notes"
195
+ source="Add Notes"
196
+ parent="Worklist Grid"
197
+ grid={{
198
+ gridId: "worklistUIGrid",
199
+ rows: selectedCustomers,
200
+ }}
201
+ options={{
202
+ notesType: noteTypeOptions,
203
+ template: templateOptions,
204
+ }}
205
+ />
206
+ </div>
207
+ </div>
208
+ );
209
+ }
210
+ ```
211
+
212
+ ### Pattern B: Standalone Drawer or Form without Grid
213
+ ```tsx
214
+ import * as React from "react";
215
+ import { BindApiButton } from "api_binding_ui";
216
+
217
+ export function DisputeDrawer({ open, onClose }: { open: boolean; onClose: () => void }) {
218
+ if (!open) return null;
219
+
220
+ const disputeReasons = [
221
+ { value: "DAMAGED", label: "Damaged Goods" },
222
+ { value: "PRICING", label: "Incorrect Pricing" },
223
+ ];
224
+
225
+ return (
226
+ <aside className="drawer-container" aria-label="Create Dispute">
227
+ <h2>Create Dispute</h2>
228
+
229
+ <form id="dispute-form">
230
+ <label htmlFor="reasonCode">Dispute Reason</label>
231
+ <select id="reasonCode" name="reasonCode" required>
232
+ {disputeReasons.map((r) => (
233
+ <option key={r.value} value={r.value}>{r.label}</option>
234
+ ))}
235
+ </select>
236
+
237
+ <label htmlFor="amount">Disputed Amount</label>
238
+ <input id="amount" name="amount" type="number" required />
239
+
240
+ <label htmlFor="comments">Comments</label>
241
+ <textarea id="comments" name="comments" />
242
+
243
+ <div className="drawer-actions">
244
+ <button type="button" onClick={onClose}>Cancel</button>
245
+ <button type="submit">Submit Dispute</button>
246
+
247
+ <BindApiButton
248
+ appId="receivables"
249
+ windowId="disputes-create"
250
+ source="Create Dispute"
251
+ parent="Disputes Management"
252
+ options={{
253
+ reasonCode: disputeReasons,
254
+ }}
255
+ />
256
+ </div>
257
+ </form>
258
+ </aside>
259
+ );
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ## 6. Listening to Applied Configurations
266
+
267
+ When the user verifies and applies an API binding inside the Studio, the configuration is stored in browser storage. The parent screen can react to this using `useAppliedBinding`:
268
+
269
+ ```tsx
270
+ import { useAppliedBinding } from "api_binding_ui";
271
+
272
+ export function ScreenComponent() {
273
+ const { isApplied, appliedConfig } = useAppliedBinding(
274
+ "collections.worklistUIGrid.worklist-add-notes"
275
+ );
276
+
277
+ React.useEffect(() => {
278
+ if (isApplied && appliedConfig) {
279
+ // Configuration applied: access appliedConfig.calls or appliedConfig.fields
280
+ }
281
+ }, [isApplied, appliedConfig]);
282
+ }
283
+ ```
284
+
285
+ ---
286
+
287
+ ## 7. Agent Verification Checklist
288
+
289
+ Before completing an integration task, the AI agent must verify:
290
+ 1. `name` attributes are assigned to all form controls on the host screen.
291
+ 2. The `<BindApiButton />` has accurate `appId`, `windowId`, `source`, and `parent` strings.
292
+ 3. Dropdown choices are forwarded in the `options` record.
293
+ 4. The application router has an active route at `/api-binding/$key` mounting `<ApiBindingWorkspace />`.
294
+ 5. No emojis or informal characters are introduced into the code or documentation.