api_binding_ui 0.0.0-stage → 1.1.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 +394 -3
- package/SKILL.md +350 -0
- package/dist/api-binding-ui.css +248 -0
- package/dist/components/ApiBindingWorkspace.d.ts +10 -0
- package/dist/components/ApiMappings.d.ts +21 -0
- package/dist/components/BindApiButton.d.ts +41 -0
- package/dist/components/RequestPreview.d.ts +7 -0
- package/dist/components/SimulationDialog.d.ts +8 -0
- package/dist/components/ValidationDialog.d.ts +8 -0
- package/dist/components/WorkspaceContext.d.ts +7 -0
- package/dist/components/ui/AppGrid.d.ts +27 -0
- package/dist/components/ui/Badge.d.ts +9 -0
- package/dist/components/ui/Banner.d.ts +8 -0
- package/dist/components/ui/Button.d.ts +31 -0
- package/dist/components/ui/Card.d.ts +14 -0
- package/dist/components/ui/Dialog.d.ts +61 -0
- package/dist/components/ui/Icons.d.ts +21 -0
- package/dist/components/ui/Input.d.ts +31 -0
- package/dist/core/bindingContext.d.ts +67 -0
- package/dist/core/catalog.d.ts +18 -0
- package/dist/core/componentId.d.ts +12 -0
- package/dist/core/model.d.ts +63 -0
- package/dist/core/openapi.d.ts +2 -0
- package/dist/core/transport.d.ts +33 -0
- package/dist/core/validation.d.ts +2 -0
- package/dist/core/valueCoercion.d.ts +17 -0
- package/dist/hooks/useAppliedBinding.d.ts +6 -0
- package/dist/index.css +248 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +11932 -0
- package/dist/style.css +244 -0
- package/dist/types.d.ts +245 -0
- package/package.json +47 -6
package/SKILL.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-binding-ui
|
|
3
|
+
description: "Use when integrating api_binding_ui into React screens, dialogs, drawers or forms, importing OpenAPI contracts, proposing component mappings, or validating requests offline."
|
|
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
|
+
Install the published release using the host's package manager and update its existing lockfile:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install api_binding_ui
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use the published package from the host project's package manager. Do not use a local filesystem dependency in a deployed project.
|
|
31
|
+
|
|
32
|
+
### Catalog And Proposed Mappings Are Host Responsibilities
|
|
33
|
+
|
|
34
|
+
The package performs visual mapping and offline request validation, not live API execution or runtime AI inference. Lovable's AI should inspect the host components and attached OpenAPI descriptions/tags, then generate editable per-window `MappingFile` seeds. `proposeConfig` imports those seeds; it does not guess endpoints.
|
|
35
|
+
|
|
36
|
+
For an OpenAPI file attached to an AI coding task, have the AI generate a static typed `ApiDefinition[]` module from that file; do not copy the YAML into the app or add a runtime YAML parser just for binding. `buildCatalogFromOpenApi` is available when a host already has a parsed OpenAPI 3.0/3.1 object, but is not required for a generated static catalog. The catalog should preserve operationId, summary, description, tags, parameter descriptions and request schemas. `definitions` must be passed to every `BindApiButton` through a shared host wrapper; the default is an empty array, not automatic API discovery. `hook` is a catalog identifier, not a callable client.
|
|
37
|
+
|
|
38
|
+
Use the exported `windowIds` and `componentId` helpers instead of deriving keys or action names twice:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { componentId, windowIds, type MappingFile } from "api_binding_ui";
|
|
42
|
+
|
|
43
|
+
const ids = windowIds({ appId: "collections", gridId: "worklistUIGrid", windowId: "worklist-add-notes" });
|
|
44
|
+
export const mappingFile: MappingFile = {
|
|
45
|
+
key: ids.key,
|
|
46
|
+
app: ids.appId,
|
|
47
|
+
grid: ids.gridId,
|
|
48
|
+
window: ids.windowId,
|
|
49
|
+
name: "Add Notes",
|
|
50
|
+
savedAt: new Date().toISOString(),
|
|
51
|
+
read: [],
|
|
52
|
+
write: [{
|
|
53
|
+
component: componentId({ kind: "button", name: "Submit" }),
|
|
54
|
+
api: "/cls/v1/notes",
|
|
55
|
+
method: "POST",
|
|
56
|
+
params: [
|
|
57
|
+
{ param: "noteSubject", in: "body", source: "subject", property: "subject" },
|
|
58
|
+
{ param: "noteDescription", in: "body", source: "notes", property: "notes" },
|
|
59
|
+
{ param: "noteTypeId", in: "body", source: "notesType", property: "notesType" },
|
|
60
|
+
{ param: "customerId", in: "body", source: "worklistUIGrid", property: "worklistUIGrid.customerId" },
|
|
61
|
+
],
|
|
62
|
+
}],
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This example assumes the real button is Submit, option values are numeric note-type IDs and exactly one customer is selected. Use actual host IDs and labels. `noteType` is a separate string property from `noteTypeId`. Never rename API fields to fit a simulator, substitute a customer ID for a note ID, or fabricate lookup APIs. Multiple selected rows feed comma-joined array values; scalar parameters require one row. The package does not dispatch one live request per row.
|
|
67
|
+
|
|
68
|
+
Match APIs using component labels/names, window and parent entity, operationId, summary, description, tags and schema compatibility. POST search operations are reads. Automatically populate only high-confidence mappings; leave missing or ambiguous sources unresolved with a report. Do not invent IDs, credentials or defaults. Fields use their actual names as sources, parents use `<windowId>.<fieldName>`, and literals use the exported `FREE_TEXT_SOURCE` with the literal in `property`. Preserve `in` to distinguish same-name path/body/query/header parameters.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 3. Router Setup Requirements
|
|
73
|
+
|
|
74
|
+
Every application using `api_binding_ui` must declare a route matching `/api-binding/$key`.
|
|
75
|
+
|
|
76
|
+
### TanStack Router (`src/routes/api-binding.$key.tsx`)
|
|
77
|
+
```tsx
|
|
78
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
79
|
+
import { ApiBindingWorkspace } from "api_binding_ui";
|
|
80
|
+
import "api_binding_ui/style.css";
|
|
81
|
+
|
|
82
|
+
export const Route = createFileRoute("/api-binding/$key")({
|
|
83
|
+
head: () => ({
|
|
84
|
+
meta: [{ title: "API Binding Studio" }],
|
|
85
|
+
}),
|
|
86
|
+
component: ApiBindingRouteView,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
function ApiBindingRouteView() {
|
|
90
|
+
const { key } = Route.useParams();
|
|
91
|
+
return <ApiBindingWorkspace key={key} pageKey={key} />;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### React Router (`src/routes/ApiBindingRoute.tsx`)
|
|
96
|
+
```tsx
|
|
97
|
+
import { useParams } from "react-router-dom";
|
|
98
|
+
import { ApiBindingWorkspace } from "api_binding_ui";
|
|
99
|
+
import "api_binding_ui/style.css";
|
|
100
|
+
|
|
101
|
+
export function ApiBindingRoute() {
|
|
102
|
+
const { key = "default" } = useParams<{ key: string }>();
|
|
103
|
+
return <ApiBindingWorkspace key={key} pageKey={key} />;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Register this component explicitly in the existing React Router tree with `<Route path="/api-binding/:key" element={<ApiBindingRoute />} />`; the component alone does not register a route. Do not add a second BrowserRouter. For TanStack, follow the host's existing file/code route conventions. Import package CSS once. The studio opens a named same-origin tab inside the host app; verify popup permissions, direct-route refresh and deployment fallback routing.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 4. Host Form Integration Rules
|
|
112
|
+
|
|
113
|
+
When rendering forms, modals, or drawers that will be bound to APIs, AI agents MUST follow these structural rules:
|
|
114
|
+
|
|
115
|
+
### Rule 1: Form Inputs Must Have Identifiers
|
|
116
|
+
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`).
|
|
117
|
+
- Correct: `<input name="subject" required />`
|
|
118
|
+
- Incorrect: `<input placeholder="Subject" />` (lacks identifier, will be ignored by DOM discovery).
|
|
119
|
+
|
|
120
|
+
### Rule 2: Explicit Labels
|
|
121
|
+
Controls should be paired with standard `<label htmlFor="...">` tags or include `aria-label`. The studio extracts human-readable field labels from these associations.
|
|
122
|
+
|
|
123
|
+
### Rule 3: Pass Dropdown Options via `options` Prop
|
|
124
|
+
Dropdown choices captured in state or constants must be passed to `<BindApiButton />` using the `options` prop:
|
|
125
|
+
```tsx
|
|
126
|
+
const noteTypeOptions = [
|
|
127
|
+
{ value: "1", label: "General Follow-up" },
|
|
128
|
+
{ value: "2", label: "Promise to Pay" },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
const templateOptions = [
|
|
132
|
+
{
|
|
133
|
+
value: "tmpl-1",
|
|
134
|
+
label: "Payment Reminder",
|
|
135
|
+
subject: "Payment Reminder",
|
|
136
|
+
description: "Followed up with customer regarding pending invoices.",
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
<BindApiButton
|
|
141
|
+
// ...
|
|
142
|
+
options={{
|
|
143
|
+
notesType: noteTypeOptions,
|
|
144
|
+
template: templateOptions,
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Rule 4: Pass Grid Selection when Targeting Grid Rows
|
|
150
|
+
If the action is triggered from or operates on selected table/grid rows:
|
|
151
|
+
```tsx
|
|
152
|
+
<BindApiButton
|
|
153
|
+
// ...
|
|
154
|
+
grid={{
|
|
155
|
+
gridId: "worklistUIGrid",
|
|
156
|
+
rows: selectedRecords,
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
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.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. Complete Implementation Patterns
|
|
165
|
+
|
|
166
|
+
### Pattern A: Modal Dialog Triggered from a Data Grid
|
|
167
|
+
```tsx
|
|
168
|
+
import * as React from "react";
|
|
169
|
+
import { BindApiButton, type ApiDefinition, type MappingFile } from "api_binding_ui";
|
|
170
|
+
|
|
171
|
+
interface CustomerRecord {
|
|
172
|
+
customerId: number;
|
|
173
|
+
customerName: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function AddNotesModal({
|
|
177
|
+
open,
|
|
178
|
+
onClose,
|
|
179
|
+
selectedCustomers,
|
|
180
|
+
definitions,
|
|
181
|
+
mappingFile,
|
|
182
|
+
}: {
|
|
183
|
+
open: boolean;
|
|
184
|
+
onClose: () => void;
|
|
185
|
+
selectedCustomers: CustomerRecord[];
|
|
186
|
+
definitions: ApiDefinition[];
|
|
187
|
+
mappingFile: MappingFile;
|
|
188
|
+
}) {
|
|
189
|
+
if (!open) return null;
|
|
190
|
+
|
|
191
|
+
const noteTypeOptions = [
|
|
192
|
+
{ value: "1", label: "General Follow-up" },
|
|
193
|
+
{ value: "2", label: "Promise to Pay" },
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
const templateOptions = [
|
|
197
|
+
{
|
|
198
|
+
value: "tmpl-payment",
|
|
199
|
+
label: "Payment Reminder",
|
|
200
|
+
subject: "Reminder: Invoice due",
|
|
201
|
+
description: "Contacted customer regarding pending balance.",
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div role="dialog" aria-labelledby="dialog-title" className="ds-dialog">
|
|
207
|
+
<h2 id="dialog-title">Add Notes</h2>
|
|
208
|
+
|
|
209
|
+
<div className="form-group">
|
|
210
|
+
<label htmlFor="notesType">Notes Type</label>
|
|
211
|
+
<select id="notesType" name="notesType" required>
|
|
212
|
+
{noteTypeOptions.map((opt) => (
|
|
213
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
214
|
+
))}
|
|
215
|
+
</select>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div className="form-group">
|
|
219
|
+
<label htmlFor="subject">Subject</label>
|
|
220
|
+
<input id="subject" name="subject" required />
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div className="form-group">
|
|
224
|
+
<label htmlFor="notes">Description</label>
|
|
225
|
+
<textarea id="notes" name="notes" required />
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<div className="button-bar">
|
|
229
|
+
<button type="button" onClick={onClose}>Cancel</button>
|
|
230
|
+
<button type="submit">Submit</button>
|
|
231
|
+
|
|
232
|
+
<BindApiButton
|
|
233
|
+
definitions={definitions}
|
|
234
|
+
mappingFile={mappingFile}
|
|
235
|
+
appId="collections"
|
|
236
|
+
windowId="worklist-add-notes"
|
|
237
|
+
source="Add Notes"
|
|
238
|
+
parent="Worklist Grid"
|
|
239
|
+
grid={{
|
|
240
|
+
gridId: "worklistUIGrid",
|
|
241
|
+
rows: selectedCustomers,
|
|
242
|
+
}}
|
|
243
|
+
options={{
|
|
244
|
+
notesType: noteTypeOptions,
|
|
245
|
+
template: templateOptions,
|
|
246
|
+
}}
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Pattern B: Standalone Drawer or Form without Grid
|
|
255
|
+
```tsx
|
|
256
|
+
import * as React from "react";
|
|
257
|
+
import { BindApiButton, type ApiDefinition, type MappingFile } from "api_binding_ui";
|
|
258
|
+
|
|
259
|
+
export function DisputeDrawer({ open, onClose, definitions, mappingFile }: { open: boolean; onClose: () => void; definitions: ApiDefinition[]; mappingFile: MappingFile }) {
|
|
260
|
+
if (!open) return null;
|
|
261
|
+
|
|
262
|
+
const disputeReasons = [
|
|
263
|
+
{ value: "DAMAGED", label: "Damaged Goods" },
|
|
264
|
+
{ value: "PRICING", label: "Incorrect Pricing" },
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<aside className="drawer-container" aria-label="Create Dispute">
|
|
269
|
+
<h2>Create Dispute</h2>
|
|
270
|
+
|
|
271
|
+
<form id="dispute-form">
|
|
272
|
+
<label htmlFor="reasonCode">Dispute Reason</label>
|
|
273
|
+
<select id="reasonCode" name="reasonCode" required>
|
|
274
|
+
{disputeReasons.map((r) => (
|
|
275
|
+
<option key={r.value} value={r.value}>{r.label}</option>
|
|
276
|
+
))}
|
|
277
|
+
</select>
|
|
278
|
+
|
|
279
|
+
<label htmlFor="amount">Disputed Amount</label>
|
|
280
|
+
<input id="amount" name="amount" type="number" required />
|
|
281
|
+
|
|
282
|
+
<label htmlFor="comments">Comments</label>
|
|
283
|
+
<textarea id="comments" name="comments" />
|
|
284
|
+
|
|
285
|
+
<div className="drawer-actions">
|
|
286
|
+
<button type="button" onClick={onClose}>Cancel</button>
|
|
287
|
+
<button type="submit">Submit Dispute</button>
|
|
288
|
+
|
|
289
|
+
<BindApiButton
|
|
290
|
+
definitions={definitions}
|
|
291
|
+
mappingFile={mappingFile}
|
|
292
|
+
appId="receivables"
|
|
293
|
+
windowId="disputes-create"
|
|
294
|
+
source="Create Dispute"
|
|
295
|
+
parent="Disputes Management"
|
|
296
|
+
options={{
|
|
297
|
+
reasonCode: disputeReasons,
|
|
298
|
+
}}
|
|
299
|
+
/>
|
|
300
|
+
</div>
|
|
301
|
+
</form>
|
|
302
|
+
</aside>
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 6. Listening to Applied Configurations
|
|
310
|
+
|
|
311
|
+
When the user validates requests and applies a binding, the configuration is stored in browser storage. `useAppliedBinding` only observes it: it does not submit requests, populate fields from responses, or write repository files. Preserve existing host API clients/runtime behavior. Live execution is a separate integration task.
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import * as React from "react";
|
|
315
|
+
import { useAppliedBinding } from "api_binding_ui";
|
|
316
|
+
|
|
317
|
+
export function ScreenComponent() {
|
|
318
|
+
const { isApplied, appliedConfig } = useAppliedBinding(
|
|
319
|
+
"collections.worklistUIGrid.worklist-add-notes"
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
React.useEffect(() => {
|
|
323
|
+
if (isApplied && appliedConfig) {
|
|
324
|
+
// Configuration applied: access appliedConfig.calls or appliedConfig.fields
|
|
325
|
+
}
|
|
326
|
+
}, [isApplied, appliedConfig]);
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 7. Agent Verification Checklist
|
|
333
|
+
|
|
334
|
+
Before completing an integration task, the AI agent must verify:
|
|
335
|
+
1. `name` attributes are assigned to all form controls on the host screen.
|
|
336
|
+
2. The `<BindApiButton />` has accurate `appId`, `windowId`, `source`, and `parent` strings.
|
|
337
|
+
3. Dropdown choices are forwarded in the `options` record.
|
|
338
|
+
4. The application router has an active route at `/api-binding/$key` mounting `<ApiBindingWorkspace />`.
|
|
339
|
+
5. No emojis or informal characters are introduced into the code or documentation.
|
|
340
|
+
6. Every trigger supplies the contract `definitions` and its own proposed/saved `mappingFile`. Existing browser drafts take precedence over seeds; never erase all browser storage to refresh defaults.
|
|
341
|
+
7. Portal/custom controls are scoped to their actual owner using `containerRef`, `containerSelector`, `rootElement` or `formId`. Custom widgets expose their real values through `name`/`data-value` or a complete `components` override; `components` and `buttons` replace, not append to, discovery.
|
|
342
|
+
8. Pass actual dropdown options and live selected rows. Use `notifySelectionUpdate(pageKey, { id: gridId, rows })` when selection changes while the studio is open. Forward `parents`, `kind` and `trigger` through host wrappers.
|
|
343
|
+
9. Validate all configured read/write requests through the generic Validate dialog, including read-only mappings and non-Submit buttons. Offline validation covers request constraints, not network access, authorization, responses or business success. Legacy `simulateSave`/`SimulationDialog` remain note-specific exports and are not the workspace's validation flow.
|
|
344
|
+
10. Config, contract and captured source changes require revalidation; existing draft mappings survive the upgrade but old verification tokens do not authorize Apply. Never manually forge applied storage or signatures.
|
|
345
|
+
11. Do not capture passwords, tokens or secrets in snapshots/options/mappings. Pass an explicit safe component list for sensitive forms and keep authentication in the existing secure client. Browser snapshots, selections and drafts contain captured data and are not shared server-side persistence.
|
|
346
|
+
12. Run host tests/typecheck/build and browser-check every binding trigger, selected-row updates, standalone forms, similar window names, popup restrictions, existing drafts, metadata and desktop/mobile layout. Confirm that opening, previewing and validating send no API request.
|
|
347
|
+
|
|
348
|
+
## Migration From api-binding-studio
|
|
349
|
+
|
|
350
|
+
The older wrapper's `apiInfoList`, `onSave` and `grid.id` are not new-button props. Replace them with `definitions`, `mappingFile` and `grid.gridId`. The package renders its own button: do not nest another Button inside it. A browser Apply does not replace a server-side source-file save handler. Preserve or explicitly report legacy file persistence/runtime behavior rather than claiming it was migrated automatically. Prepare the host changes inside the Lovable app only; do not copy this package's studio code.
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/* src/styles/highradius-bridge.css */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/* src/styles/index.css */
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/* src/styles/workspace.css */
|
|
8
|
+
.binding-workspace, .binding-workspace * {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.binding-workspace, .api-binding, .binding-v2 {
|
|
13
|
+
display: flex;
|
|
14
|
+
background: var(--ds-color-background, #fff);
|
|
15
|
+
color: var(--ds-color-foreground, #0f172a);
|
|
16
|
+
font-family: var(--ds-font-family-sans, sans-serif);
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.binding-workspace h1, .api-binding h1, .binding-v2 h1 {
|
|
22
|
+
font-size: var(--ds-font-size-2xl, 1.5rem);
|
|
23
|
+
font-weight: var(--ds-font-weight-bold, 700);
|
|
24
|
+
margin: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.binding-workspace h2, .api-binding h2, .binding-v2 h2 {
|
|
28
|
+
font-size: var(--ds-font-size-lg, 1.125rem);
|
|
29
|
+
font-weight: var(--ds-font-weight-semibold, 600);
|
|
30
|
+
margin: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.binding-workspace h3, .api-binding h3, .binding-v2 h3 {
|
|
34
|
+
font-size: var(--ds-font-size-base, .875rem);
|
|
35
|
+
font-weight: var(--ds-font-weight-semibold, 600);
|
|
36
|
+
margin: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.binding-workspace p, .api-binding p, .binding-v2 p {
|
|
40
|
+
line-height: var(--ds-line-height-normal, 1.5);
|
|
41
|
+
margin: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.binding-header, .binding-footer, .binding-v2-header, .binding-v2-footer {
|
|
45
|
+
padding: var(--ds-page-padding, 1rem 1.5rem);
|
|
46
|
+
display: flex;
|
|
47
|
+
gap: var(--ds-space-4, 1rem);
|
|
48
|
+
flex-wrap: wrap;
|
|
49
|
+
justify-content: space-between;
|
|
50
|
+
align-items: center;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.binding-header, .binding-v2-header {
|
|
54
|
+
border-bottom-width: var(--ds-screen-surface-border-width, 1px);
|
|
55
|
+
border-bottom-style: solid;
|
|
56
|
+
border-bottom-color: var(--ds-color-border, #e2e8f0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.binding-body, .binding-v2-body {
|
|
60
|
+
overflow: auto;
|
|
61
|
+
position: relative;
|
|
62
|
+
padding: var(--ds-page-padding, 1rem 1.5rem);
|
|
63
|
+
flex: 1;
|
|
64
|
+
min-height: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.binding-columns, .binding-v2-columns {
|
|
68
|
+
isolation: isolate;
|
|
69
|
+
display: flex;
|
|
70
|
+
gap: var(--ds-space-8, 2rem);
|
|
71
|
+
flex-wrap: wrap;
|
|
72
|
+
align-items: start;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.binding-main, .binding-v2-main {
|
|
76
|
+
flex-grow: 1;
|
|
77
|
+
flex-basis: var(--ds-dialog-form-min-width, 32rem);
|
|
78
|
+
padding-bottom: var(--ds-space-12, 3rem);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.binding-main, .binding-stack, .binding-v2-main, .binding-v2-stack {
|
|
82
|
+
display: flex;
|
|
83
|
+
gap: var(--ds-space-4, 1rem);
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.binding-section, .binding-v2-section {
|
|
89
|
+
padding-bottom: var(--ds-space-6, 1.5rem);
|
|
90
|
+
display: flex;
|
|
91
|
+
gap: var(--ds-space-4, 1rem);
|
|
92
|
+
border-bottom-width: var(--ds-screen-surface-border-width, 1px);
|
|
93
|
+
border-bottom-style: solid;
|
|
94
|
+
border-bottom-color: var(--ds-color-border, #e2e8f0);
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.binding-section-title, .binding-v2-section-title {
|
|
99
|
+
display: flex;
|
|
100
|
+
justify-content: space-between;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: var(--ds-space-4, 1rem);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.binding-muted, .binding-v2-muted {
|
|
106
|
+
color: var(--ds-color-muted-foreground, #64748b);
|
|
107
|
+
font-size: var(--ds-font-size-sm, .8125rem);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.binding-preview, .binding-v2-preview {
|
|
111
|
+
flex-basis: var(--ds-dialog-min-width, 24rem);
|
|
112
|
+
position: sticky;
|
|
113
|
+
background: var(--ds-color-surface, #fff);
|
|
114
|
+
border-width: var(--ds-screen-surface-border-width, 1px);
|
|
115
|
+
border-style: solid;
|
|
116
|
+
border-color: var(--ds-color-border, #e2e8f0);
|
|
117
|
+
border-radius: var(--ds-radius-md, 6px);
|
|
118
|
+
padding: var(--ds-space-6, 1.5rem);
|
|
119
|
+
display: flex;
|
|
120
|
+
gap: var(--ds-space-4, 1rem);
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
flex-grow: 0;
|
|
123
|
+
max-width: 100%;
|
|
124
|
+
top: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.binding-panel, .binding-v2-panel {
|
|
128
|
+
position: absolute;
|
|
129
|
+
width: var(--ds-dialog-form-min-width, 32rem);
|
|
130
|
+
background: var(--ds-color-background, #fff);
|
|
131
|
+
border-left-width: var(--ds-screen-surface-border-width, 1px);
|
|
132
|
+
border-left-style: solid;
|
|
133
|
+
border-left-color: var(--ds-color-border, #e2e8f0);
|
|
134
|
+
box-shadow: var(--ds-shadow-md, 0 4px 6px -1px #0000001a);
|
|
135
|
+
padding: var(--ds-space-6, 1.5rem);
|
|
136
|
+
display: flex;
|
|
137
|
+
gap: var(--ds-space-6, 1.5rem);
|
|
138
|
+
z-index: var(--ds-focus-z-index, 50);
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
max-width: 100%;
|
|
141
|
+
min-height: 100%;
|
|
142
|
+
top: 0;
|
|
143
|
+
right: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.binding-details, .binding-v2-details {
|
|
147
|
+
display: flex;
|
|
148
|
+
gap: var(--ds-space-2, .5rem);
|
|
149
|
+
font-size: var(--ds-font-size-sm, .8125rem);
|
|
150
|
+
overflow-wrap: anywhere;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.binding-details summary, .binding-v2-details summary {
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
color: var(--ds-color-muted-foreground, #64748b);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.binding-footer, .binding-v2-footer {
|
|
160
|
+
border-top-width: var(--ds-screen-surface-border-width, 1px);
|
|
161
|
+
border-top-style: solid;
|
|
162
|
+
border-top-color: var(--ds-color-border, #e2e8f0);
|
|
163
|
+
background: var(--ds-color-background, #fff);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.binding-simulation, .binding-v2-simulation {
|
|
167
|
+
display: flex;
|
|
168
|
+
gap: var(--ds-space-4, 1rem);
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.binding-empty, .binding-v2-empty {
|
|
173
|
+
padding: var(--ds-space-12, 3rem);
|
|
174
|
+
margin: auto;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.binding-error, .binding-v2-error {
|
|
178
|
+
color: var(--ds-color-destructive, #ef4444);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.binding-stack-tight, .binding-v2-stack-tight {
|
|
182
|
+
display: flex;
|
|
183
|
+
align-items: flex-start;
|
|
184
|
+
gap: var(--ds-space-1, .25rem);
|
|
185
|
+
flex-direction: column;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.binding-list, .binding-v2-list {
|
|
189
|
+
padding-left: var(--ds-space-4, 1rem);
|
|
190
|
+
margin: 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.binding-panel-wide, .binding-v2-panel-wide {
|
|
194
|
+
width: var(--ds-dialog-width-large, 48rem);
|
|
195
|
+
overflow: auto;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.binding-call, .binding-v2-call {
|
|
199
|
+
display: flex;
|
|
200
|
+
gap: var(--ds-space-3, .75rem);
|
|
201
|
+
padding: var(--ds-space-4, 1rem);
|
|
202
|
+
border-width: var(--ds-screen-surface-border-width, 1px);
|
|
203
|
+
border-style: solid;
|
|
204
|
+
border-color: var(--ds-color-border, #e2e8f0);
|
|
205
|
+
border-radius: var(--ds-radius-md, 6px);
|
|
206
|
+
flex-direction: column;
|
|
207
|
+
min-width: 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.binding-call-grid, .binding-v2-call-grid {
|
|
211
|
+
display: grid;
|
|
212
|
+
grid-template-columns: repeat(auto-fill, minmax(var(--ds-menu-min-width, 12rem), 1fr));
|
|
213
|
+
gap: var(--ds-space-3, .75rem);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.binding-params, .binding-v2-params {
|
|
217
|
+
display: flex;
|
|
218
|
+
gap: var(--ds-space-2, .5rem);
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.binding-param, .binding-v2-param {
|
|
223
|
+
display: grid;
|
|
224
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
225
|
+
gap: var(--ds-space-3, .75rem);
|
|
226
|
+
overflow-wrap: anywhere;
|
|
227
|
+
align-items: start;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.binding-endpoint, .binding-v2-endpoint {
|
|
231
|
+
font-family: var(--ds-font-family-mono, monospace);
|
|
232
|
+
font-size: var(--ds-font-size-sm, .8125rem);
|
|
233
|
+
overflow-wrap: anywhere;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.binding-preview-parts, .binding-v2-preview-parts {
|
|
237
|
+
display: grid;
|
|
238
|
+
grid-template-columns: repeat(auto-fill, minmax(var(--ds-menu-min-width, 12rem), 1fr));
|
|
239
|
+
gap: var(--ds-space-3, .75rem);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.binding-pre, .binding-v2-pre {
|
|
243
|
+
font-family: var(--ds-font-family-mono, monospace);
|
|
244
|
+
font-size: var(--ds-font-size-sm, .8125rem);
|
|
245
|
+
white-space: pre-wrap;
|
|
246
|
+
overflow-wrap: anywhere;
|
|
247
|
+
margin: 0;
|
|
248
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ApiBindingContext, WorkspaceComponents } from "../types";
|
|
3
|
+
import "../styles/workspace.css";
|
|
4
|
+
export interface ApiBindingWorkspaceProps {
|
|
5
|
+
pageKey: string;
|
|
6
|
+
initialContext?: ApiBindingContext;
|
|
7
|
+
components?: Partial<WorkspaceComponents>;
|
|
8
|
+
}
|
|
9
|
+
export declare function ApiBindingWorkspace({ pageKey, initialContext, components, }: ApiBindingWorkspaceProps): React.JSX.Element;
|
|
10
|
+
export declare const ApiBindingStudio: typeof ApiBindingWorkspace;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ApiBindingConfig, ApiBindingContext } from "../types";
|
|
3
|
+
export declare const TIMING_OPTIONS: {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}[];
|
|
7
|
+
export declare const FRESHNESS_OPTIONS: {
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}[];
|
|
11
|
+
/**
|
|
12
|
+
* The single canonical API-mapping editor. The global entry shows every call;
|
|
13
|
+
* a field or action entry passes `focus` and sees only calls targeting it or sending its value.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ApiMappings({ config, context, focus, defaultTarget, onChange, }: {
|
|
16
|
+
config: ApiBindingConfig;
|
|
17
|
+
context: ApiBindingContext;
|
|
18
|
+
focus?: string;
|
|
19
|
+
defaultTarget?: string;
|
|
20
|
+
onChange: (updater: (old: ApiBindingConfig) => ApiBindingConfig) => void;
|
|
21
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ApiDefinition, BindingGrid, BindingOption, MappingFile, SnapshotComponent, SnapshotContext, WindowKind, WindowTrigger } from "../types";
|
|
3
|
+
export interface BindApiButtonProps {
|
|
4
|
+
appId?: string;
|
|
5
|
+
windowId: string;
|
|
6
|
+
/** Source window, screen, or action name, e.g. "Add Notes", "Create P2P", "Invoice Details" */
|
|
7
|
+
source: string;
|
|
8
|
+
/** Parent grid, panel, or screen name, e.g. "Worklist Grid", "Dashboard", "Main Screen" */
|
|
9
|
+
parent: string;
|
|
10
|
+
/** Optional Grid context (selected rows and columns) if triggered from or acting upon a grid */
|
|
11
|
+
grid?: BindingGrid;
|
|
12
|
+
/** Parent windows / screens context hierarchy */
|
|
13
|
+
parents?: SnapshotContext[];
|
|
14
|
+
/** UI presentation kind (modal, drawer, screen, form, etc.) */
|
|
15
|
+
kind?: WindowKind;
|
|
16
|
+
/** Action trigger info */
|
|
17
|
+
trigger?: WindowTrigger;
|
|
18
|
+
/** Captured dropdown/combobox options from the host form */
|
|
19
|
+
options?: Record<string, BindingOption[]>;
|
|
20
|
+
/** Registered API definitions catalog */
|
|
21
|
+
definitions?: ApiDefinition[];
|
|
22
|
+
/** Existing saved mapping file */
|
|
23
|
+
mappingFile?: MappingFile | null;
|
|
24
|
+
/** Base route for the Studio in the router (defaults to `/api-binding`) */
|
|
25
|
+
routeBase?: string;
|
|
26
|
+
/** Optional container ref to override automatic DOM discovery */
|
|
27
|
+
containerRef?: React.RefObject<HTMLElement | null>;
|
|
28
|
+
/** Optional container CSS selector to locate screen root */
|
|
29
|
+
containerSelector?: string;
|
|
30
|
+
/** Explicit root DOM element */
|
|
31
|
+
rootElement?: HTMLElement | null;
|
|
32
|
+
/** Optional HTML form id */
|
|
33
|
+
formId?: string;
|
|
34
|
+
/** Explicit components / fields override if not discovered via DOM */
|
|
35
|
+
components?: SnapshotComponent[];
|
|
36
|
+
/** Explicit action buttons override */
|
|
37
|
+
buttons?: string[];
|
|
38
|
+
className?: string;
|
|
39
|
+
children?: React.ReactNode;
|
|
40
|
+
}
|
|
41
|
+
export declare function BindApiButton({ appId, windowId, source, parent, grid, parents, kind, trigger, options, definitions, mappingFile, routeBase, containerRef, containerSelector, rootElement, formId, components: explicitComponents, buttons: explicitButtons, className, children, }: BindApiButtonProps): React.JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { RequestPreviewItem } from "../types";
|
|
3
|
+
/** Simulated requests in call order. Nothing here is sent over network. */
|
|
4
|
+
export declare function RequestPreview({ title, requests }: {
|
|
5
|
+
title: string;
|
|
6
|
+
requests: RequestPreviewItem[];
|
|
7
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ApiBindingConfig, ApiBindingContext } from "../types";
|
|
3
|
+
export declare function SimulationDialog({ config, context, onClose, onVerified, }: {
|
|
4
|
+
config: ApiBindingConfig;
|
|
5
|
+
context: ApiBindingContext;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onVerified: (signature: string) => void;
|
|
8
|
+
}): React.JSX.Element;
|