api_binding_ui 2.0.0 → 2.0.2
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/SKILL.md +2 -1
- package/dist/framework/contract.d.ts +1 -1
- package/dist/framework/loadCatalog.d.ts +4 -2
- package/dist/framework/runner.d.ts +4 -1
- package/dist/{index-e9twc25y.js → index-3q1vx729.js} +7 -2
- package/dist/{index-e9twc25y.js.map → index-3q1vx729.js.map} +3 -3
- package/dist/index.js +6 -3
- package/dist/index.js.map +3 -3
- package/dist/studio/SettingsPanel.d.ts +3 -1
- package/dist/studio/index.css +38 -0
- package/dist/studio/index.js +177 -36
- package/dist/studio/index.js.map +6 -6
- package/dist/studio/useCatalog.d.ts +7 -2
- package/package.json +1 -1
package/dist/studio/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
createEngine2,
|
|
13
13
|
validateRegistry2,
|
|
14
14
|
useBindingRuntime
|
|
15
|
-
} from "../index-
|
|
15
|
+
} from "../index-3q1vx729.js";
|
|
16
16
|
|
|
17
17
|
// src/studio/BindingStudio.tsx
|
|
18
18
|
import * as React15 from "react";
|
|
@@ -2096,10 +2096,96 @@ function docsSummary(c) {
|
|
|
2096
2096
|
if (c.status === "error")
|
|
2097
2097
|
return c.error;
|
|
2098
2098
|
const products = new Set(c.catalog.operations.map((o) => o.product).filter(Boolean));
|
|
2099
|
-
|
|
2099
|
+
const where = c.source === "pasted" ? "Pasted file · " : "";
|
|
2100
|
+
return `${where}${products.size || 1} ${products.size === 1 ? "product" : "products"} · ${c.catalog.operations.length} ${c.catalog.operations.length === 1 ? "API" : "APIs"} · loaded ${TIME.format(c.loadedAt)}`;
|
|
2100
2101
|
}
|
|
2101
|
-
|
|
2102
|
+
var SIZE = new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 });
|
|
2103
|
+
var kb = (chars) => chars < 1e6 ? `${SIZE.format(chars / 1000)} KB` : `${SIZE.format(chars / 1e6)} MB`;
|
|
2104
|
+
function PasteDocs({ onPaste, onDone }) {
|
|
2105
|
+
const area = React12.useRef(null);
|
|
2106
|
+
const [size, setSize] = React12.useState(0);
|
|
2107
|
+
const [busy, setBusy] = React12.useState(false);
|
|
2108
|
+
const [error, setError] = React12.useState();
|
|
2109
|
+
const load = async () => {
|
|
2110
|
+
const text = area.current?.value ?? "";
|
|
2111
|
+
setBusy(true);
|
|
2112
|
+
setError(undefined);
|
|
2113
|
+
const problem = await onPaste(text);
|
|
2114
|
+
setBusy(false);
|
|
2115
|
+
if (problem) {
|
|
2116
|
+
setError(problem);
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
if (area.current)
|
|
2120
|
+
area.current.value = "";
|
|
2121
|
+
onDone();
|
|
2122
|
+
};
|
|
2123
|
+
return /* @__PURE__ */ jsxs15("div", {
|
|
2124
|
+
className: "abs-paste",
|
|
2125
|
+
children: [
|
|
2126
|
+
/* @__PURE__ */ jsx17("textarea", {
|
|
2127
|
+
ref: area,
|
|
2128
|
+
id: "abs-docs-paste",
|
|
2129
|
+
className: "abs-input abs-paste-area",
|
|
2130
|
+
rows: 7,
|
|
2131
|
+
wrap: "off",
|
|
2132
|
+
spellCheck: false,
|
|
2133
|
+
autoCorrect: "off",
|
|
2134
|
+
autoCapitalize: "off",
|
|
2135
|
+
autoComplete: "off",
|
|
2136
|
+
autoFocus: true,
|
|
2137
|
+
placeholder: "Paste the contents of index.yaml (or the JSON version)",
|
|
2138
|
+
"aria-label": "OpenAPI document",
|
|
2139
|
+
"aria-describedby": "abs-paste-note",
|
|
2140
|
+
"aria-invalid": !!error,
|
|
2141
|
+
onInput: (e) => setSize(e.currentTarget.value.length)
|
|
2142
|
+
}),
|
|
2143
|
+
/* @__PURE__ */ jsxs15("div", {
|
|
2144
|
+
className: "abs-set-row",
|
|
2145
|
+
children: [
|
|
2146
|
+
/* @__PURE__ */ jsx17("button", {
|
|
2147
|
+
type: "button",
|
|
2148
|
+
className: "abs-btn abs-btn-primary",
|
|
2149
|
+
onClick: () => void load(),
|
|
2150
|
+
disabled: busy || size === 0,
|
|
2151
|
+
children: busy ? /* @__PURE__ */ jsxs15(Fragment9, {
|
|
2152
|
+
children: [
|
|
2153
|
+
/* @__PURE__ */ jsx17(Icon, {
|
|
2154
|
+
name: "refresh",
|
|
2155
|
+
size: 14,
|
|
2156
|
+
className: "abs-spin"
|
|
2157
|
+
}),
|
|
2158
|
+
"Reading…"
|
|
2159
|
+
]
|
|
2160
|
+
}) : "Load"
|
|
2161
|
+
}),
|
|
2162
|
+
/* @__PURE__ */ jsx17("button", {
|
|
2163
|
+
type: "button",
|
|
2164
|
+
className: "abs-btn abs-btn-ghost",
|
|
2165
|
+
onClick: onDone,
|
|
2166
|
+
disabled: busy,
|
|
2167
|
+
children: "Cancel"
|
|
2168
|
+
}),
|
|
2169
|
+
/* @__PURE__ */ jsx17("span", {
|
|
2170
|
+
className: "abs-paste-size",
|
|
2171
|
+
"aria-live": "polite",
|
|
2172
|
+
children: size ? kb(size) : ""
|
|
2173
|
+
})
|
|
2174
|
+
]
|
|
2175
|
+
}),
|
|
2176
|
+
/* @__PURE__ */ jsx17("p", {
|
|
2177
|
+
id: "abs-paste-note",
|
|
2178
|
+
className: "abs-set-status" + (error ? " is-error" : ""),
|
|
2179
|
+
role: error ? "alert" : undefined,
|
|
2180
|
+
children: error ?? "Used for this session only. Nothing is saved; the URL is used again next time."
|
|
2181
|
+
})
|
|
2182
|
+
]
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
function SettingsPanel({ docsUrl, onDocsUrl, catalog, onReload, onPaste, manifests, onImport, onClose }) {
|
|
2102
2186
|
const [draft, setDraft] = React12.useState(docsUrl);
|
|
2187
|
+
const [pasting, setPasting] = React12.useState(false);
|
|
2188
|
+
const usingPaste = catalog.status === "ready" && catalog.source === "pasted";
|
|
2103
2189
|
const [importMsg, setImportMsg] = React12.useState(null);
|
|
2104
2190
|
const panel = React12.useRef(null);
|
|
2105
2191
|
const file = React12.useRef(null);
|
|
@@ -2213,15 +2299,46 @@ function SettingsPanel({ docsUrl, onDocsUrl, catalog, onReload, manifests, onImp
|
|
|
2213
2299
|
"aria-live": "polite",
|
|
2214
2300
|
children: valid ? docsSummary(catalog) : "Enter a full URL, for example " + DEFAULT_DOCS_URL
|
|
2215
2301
|
}),
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2302
|
+
pasting ? /* @__PURE__ */ jsx17(PasteDocs, {
|
|
2303
|
+
onPaste,
|
|
2304
|
+
onDone: () => setPasting(false)
|
|
2305
|
+
}) : /* @__PURE__ */ jsxs15("div", {
|
|
2306
|
+
className: "abs-set-links",
|
|
2307
|
+
children: [
|
|
2308
|
+
usingPaste ? /* @__PURE__ */ jsxs15("button", {
|
|
2309
|
+
type: "button",
|
|
2310
|
+
className: "abs-link",
|
|
2311
|
+
onClick: onReload,
|
|
2312
|
+
children: [
|
|
2313
|
+
/* @__PURE__ */ jsx17(Icon, {
|
|
2314
|
+
name: "refresh",
|
|
2315
|
+
size: 14
|
|
2316
|
+
}),
|
|
2317
|
+
"Use the URL again"
|
|
2318
|
+
]
|
|
2319
|
+
}) : /* @__PURE__ */ jsxs15("button", {
|
|
2320
|
+
type: "button",
|
|
2321
|
+
className: "abs-link",
|
|
2322
|
+
onClick: () => setPasting(true),
|
|
2323
|
+
children: [
|
|
2324
|
+
/* @__PURE__ */ jsx17(Icon, {
|
|
2325
|
+
name: "upload",
|
|
2326
|
+
size: 14
|
|
2327
|
+
}),
|
|
2328
|
+
"Paste the file instead"
|
|
2329
|
+
]
|
|
2330
|
+
}),
|
|
2331
|
+
docsUrl !== DEFAULT_DOCS_URL ? /* @__PURE__ */ jsx17("button", {
|
|
2332
|
+
type: "button",
|
|
2333
|
+
className: "abs-link",
|
|
2334
|
+
onClick: () => {
|
|
2335
|
+
setDraft(DEFAULT_DOCS_URL);
|
|
2336
|
+
onDocsUrl(DEFAULT_DOCS_URL);
|
|
2337
|
+
},
|
|
2338
|
+
children: "Use the default URL"
|
|
2339
|
+
}) : null
|
|
2340
|
+
]
|
|
2341
|
+
})
|
|
2225
2342
|
]
|
|
2226
2343
|
}),
|
|
2227
2344
|
/* @__PURE__ */ jsxs15("div", {
|
|
@@ -2364,13 +2481,29 @@ import * as React14 from "react";
|
|
|
2364
2481
|
// src/framework/loadCatalog.ts
|
|
2365
2482
|
import { parse } from "yaml";
|
|
2366
2483
|
class CatalogLoadError extends Error {
|
|
2367
|
-
|
|
2368
|
-
constructor(message,
|
|
2484
|
+
source;
|
|
2485
|
+
constructor(message, source) {
|
|
2369
2486
|
super(message);
|
|
2370
|
-
this.
|
|
2487
|
+
this.source = source;
|
|
2371
2488
|
this.name = "CatalogLoadError";
|
|
2372
2489
|
}
|
|
2373
2490
|
}
|
|
2491
|
+
function catalogFromText(text, source) {
|
|
2492
|
+
const body = text.trim();
|
|
2493
|
+
if (!body)
|
|
2494
|
+
throw new CatalogLoadError("Nothing to read: the text is empty.", source);
|
|
2495
|
+
let doc;
|
|
2496
|
+
try {
|
|
2497
|
+
doc = body.startsWith("{") ? JSON.parse(body) : parse(body);
|
|
2498
|
+
} catch {
|
|
2499
|
+
throw new CatalogLoadError("The text is not valid JSON or YAML.", source);
|
|
2500
|
+
}
|
|
2501
|
+
try {
|
|
2502
|
+
return catalogFromOpenApi2(doc, source.includes("://") ? { url: source } : {});
|
|
2503
|
+
} catch (e) {
|
|
2504
|
+
throw new CatalogLoadError(e instanceof Error ? e.message : "The text is not an OpenAPI document.", source);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2374
2507
|
async function loadCatalog(url, init) {
|
|
2375
2508
|
let res;
|
|
2376
2509
|
try {
|
|
@@ -2380,39 +2513,46 @@ async function loadCatalog(url, init) {
|
|
|
2380
2513
|
}
|
|
2381
2514
|
if (!res.ok)
|
|
2382
2515
|
throw new CatalogLoadError(`The docs server answered ${res.status} ${res.statusText}. Check the URL.`, url);
|
|
2383
|
-
|
|
2384
|
-
let doc;
|
|
2385
|
-
try {
|
|
2386
|
-
doc = text.trimStart().startsWith("{") ? JSON.parse(text) : parse(text);
|
|
2387
|
-
} catch {
|
|
2388
|
-
throw new CatalogLoadError("The file is not valid JSON or YAML.", url);
|
|
2389
|
-
}
|
|
2390
|
-
try {
|
|
2391
|
-
return catalogFromOpenApi2(doc, { url });
|
|
2392
|
-
} catch (e) {
|
|
2393
|
-
throw new CatalogLoadError(e instanceof Error ? e.message : "The file is not an OpenAPI document.", url);
|
|
2394
|
-
}
|
|
2516
|
+
return catalogFromText(await res.text(), url);
|
|
2395
2517
|
}
|
|
2396
2518
|
|
|
2397
2519
|
// src/studio/useCatalog.ts
|
|
2398
2520
|
function useCatalog(url, provided) {
|
|
2399
|
-
const [state, setState] = React14.useState(() => provided ? { status: "ready", catalog: provided, loadedAt: new Date } : { status: "loading" });
|
|
2521
|
+
const [state, setState] = React14.useState(() => provided ? { status: "ready", catalog: provided, loadedAt: new Date, source: "provided" } : { status: "loading" });
|
|
2400
2522
|
const [nonce, setNonce] = React14.useState(0);
|
|
2523
|
+
const [pasted, setPasted] = React14.useState(null);
|
|
2401
2524
|
React14.useEffect(() => {
|
|
2525
|
+
if (pasted) {
|
|
2526
|
+
setState({ status: "ready", catalog: pasted, loadedAt: new Date, source: "pasted" });
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2402
2529
|
if (provided) {
|
|
2403
|
-
setState({ status: "ready", catalog: provided, loadedAt: new Date });
|
|
2530
|
+
setState({ status: "ready", catalog: provided, loadedAt: new Date, source: "provided" });
|
|
2404
2531
|
return;
|
|
2405
2532
|
}
|
|
2406
2533
|
const ctrl = new AbortController;
|
|
2407
2534
|
setState((s) => ({ status: "loading", catalog: s.catalog }));
|
|
2408
|
-
loadCatalog(url, { signal: ctrl.signal }).then((catalog) => setState({ status: "ready", catalog, loadedAt: new Date }), (e) => {
|
|
2535
|
+
loadCatalog(url, { signal: ctrl.signal }).then((catalog) => setState({ status: "ready", catalog, loadedAt: new Date, source: "url" }), (e) => {
|
|
2409
2536
|
if (!ctrl.signal.aborted)
|
|
2410
2537
|
setState((s) => ({ status: "error", catalog: s.catalog, error: e instanceof Error ? e.message : String(e) }));
|
|
2411
2538
|
});
|
|
2412
2539
|
return () => ctrl.abort();
|
|
2413
|
-
}, [url, provided, nonce]);
|
|
2414
|
-
const reload = React14.useCallback(() =>
|
|
2415
|
-
|
|
2540
|
+
}, [url, provided, nonce, pasted]);
|
|
2541
|
+
const reload = React14.useCallback(() => {
|
|
2542
|
+
setPasted(null);
|
|
2543
|
+
setNonce((n) => n + 1);
|
|
2544
|
+
}, []);
|
|
2545
|
+
const loadPasted = React14.useCallback((text) => new Promise((resolve) => {
|
|
2546
|
+
setTimeout(() => {
|
|
2547
|
+
try {
|
|
2548
|
+
setPasted(catalogFromText(text, "pasted"));
|
|
2549
|
+
resolve(undefined);
|
|
2550
|
+
} catch (e) {
|
|
2551
|
+
resolve(e instanceof Error ? e.message : String(e));
|
|
2552
|
+
}
|
|
2553
|
+
}, 0);
|
|
2554
|
+
}), []);
|
|
2555
|
+
return [state, reload, loadPasted];
|
|
2416
2556
|
}
|
|
2417
2557
|
|
|
2418
2558
|
// src/studio/BindingStudio.tsx
|
|
@@ -2494,7 +2634,7 @@ var EMPTY_HINT = /* @__PURE__ */ jsxs16("aside", {
|
|
|
2494
2634
|
});
|
|
2495
2635
|
function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, initialScreen, title = "Bind API", onClose }) {
|
|
2496
2636
|
const [settings, updateSettings] = useSettings({ docsUrl });
|
|
2497
|
-
const [catalogState, reloadCatalog] = useCatalog(settings.docsUrl, framework.catalog);
|
|
2637
|
+
const [catalogState, reloadCatalog, loadPasted] = useCatalog(settings.docsUrl, framework.catalog);
|
|
2498
2638
|
const [settingsOpen, setSettingsOpen] = React15.useState(false);
|
|
2499
2639
|
const closeSettings = React15.useCallback(() => setSettingsOpen(false), []);
|
|
2500
2640
|
const adapter = React15.useMemo(() => persistence ?? localStorageAdapter2(), [persistence]);
|
|
@@ -2534,6 +2674,7 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2534
2674
|
onDocsUrl: (url) => updateSettings({ docsUrl: url }),
|
|
2535
2675
|
catalog: catalogState,
|
|
2536
2676
|
onReload: reloadCatalog,
|
|
2677
|
+
onPaste: loadPasted,
|
|
2537
2678
|
manifests: () => fw ? Object.entries(binds.index).map(([s, b]) => toManifest2(fw, s, b)) : [],
|
|
2538
2679
|
onImport: binds.importManifests,
|
|
2539
2680
|
onClose: closeSettings
|
|
@@ -2591,7 +2732,7 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2591
2732
|
type: "button",
|
|
2592
2733
|
className: "abs-btn",
|
|
2593
2734
|
onClick: () => setSettingsOpen(true),
|
|
2594
|
-
children: "Change the URL"
|
|
2735
|
+
children: "Change the URL or paste the file"
|
|
2595
2736
|
})
|
|
2596
2737
|
]
|
|
2597
2738
|
})
|
|
@@ -2802,5 +2943,5 @@ export {
|
|
|
2802
2943
|
BindingWidget
|
|
2803
2944
|
};
|
|
2804
2945
|
|
|
2805
|
-
//# debugId=
|
|
2946
|
+
//# debugId=C512BDFB06A7F43E64756E2164756E21
|
|
2806
2947
|
//# sourceMappingURL=index.js.map
|