api_binding_ui 2.0.2 → 3.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 +68 -48
- package/SKILL.md +29 -34
- package/dist/app/index-4mgcb4wd.js +367 -0
- package/dist/app/index-aaxy714b.css +1 -0
- package/dist/app/index.html +7 -0
- package/dist/cli.js +1006 -0
- package/dist/codegen/generate.d.ts +22 -0
- package/dist/codegen/helpers.d.ts +2 -0
- package/dist/framework/contract.d.ts +38 -23
- package/dist/framework/expr.d.ts +26 -27
- package/dist/framework/file.d.ts +28 -0
- package/dist/framework/store.d.ts +3 -15
- package/dist/{index-3q1vx729.js → index-jj8twdw9.js} +158 -173
- package/dist/index-jj8twdw9.js.map +16 -0
- package/dist/index.d.ts +6 -8
- package/dist/index.js +461 -298
- package/dist/index.js.map +6 -8
- package/dist/overlay.js +24 -0
- package/dist/server/handler.d.ts +18 -0
- package/dist/studio/BindingStudio.d.ts +8 -11
- package/dist/studio/SettingsPanel.d.ts +3 -7
- package/dist/studio/index.css +0 -149
- package/dist/studio/index.d.ts +1 -2
- package/dist/studio/index.js +231 -438
- package/dist/studio/index.js.map +8 -10
- package/dist/studio/useBindings.d.ts +7 -5
- package/dist/studio/useCatalog.d.ts +2 -0
- package/dist/vite/plugin.d.ts +36 -0
- package/dist/vite.js +201 -0
- package/package.json +68 -52
- package/spec/FRAMEWORK.md +107 -201
- package/dist/framework/runner.d.ts +0 -36
- package/dist/index-3q1vx729.js.map +0 -16
- package/dist/runtime/BindingProvider.d.ts +0 -42
- package/dist/runtime/BindingScreen.d.ts +0 -23
- package/dist/runtime/hooks.d.ts +0 -22
- package/dist/runtime/screenStore.d.ts +0 -7
- package/dist/studio/BindingWidget.d.ts +0 -9
- package/dist/studio/useSettings.d.ts +0 -8
package/dist/studio/index.js
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
toManifest2,
|
|
5
|
-
markDirty,
|
|
6
|
-
savePending,
|
|
7
|
-
loadAll,
|
|
8
|
-
putBinding,
|
|
9
|
-
dropBinding,
|
|
10
|
-
parseManifests,
|
|
2
|
+
catalogFromText2,
|
|
3
|
+
loadCatalog2,
|
|
11
4
|
humanize,
|
|
5
|
+
screenOf2,
|
|
6
|
+
fromFile2,
|
|
7
|
+
toFile2,
|
|
12
8
|
createEngine2,
|
|
13
|
-
validateRegistry2
|
|
14
|
-
|
|
15
|
-
} from "../index-3q1vx729.js";
|
|
9
|
+
validateRegistry2
|
|
10
|
+
} from "../index-jj8twdw9.js";
|
|
16
11
|
|
|
17
12
|
// src/studio/BindingStudio.tsx
|
|
18
|
-
import * as
|
|
13
|
+
import * as React14 from "react";
|
|
19
14
|
|
|
20
15
|
// src/studio/ComponentList.tsx
|
|
21
16
|
import * as React2 from "react";
|
|
@@ -2052,44 +2047,53 @@ function Navigator({ engine, current, onPick }) {
|
|
|
2052
2047
|
// src/studio/SettingsPanel.tsx
|
|
2053
2048
|
import * as React12 from "react";
|
|
2054
2049
|
|
|
2055
|
-
// src/studio/
|
|
2050
|
+
// src/studio/useCatalog.ts
|
|
2056
2051
|
import * as React11 from "react";
|
|
2057
2052
|
var DEFAULT_DOCS_URL = "https://apidocs.highradius.com/documentation-ui/index.yaml";
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2053
|
+
function useCatalog(url, provided) {
|
|
2054
|
+
const [state, setState] = React11.useState(() => provided ? { status: "ready", catalog: provided, loadedAt: new Date, source: "provided" } : { status: "loading" });
|
|
2055
|
+
const [nonce, setNonce] = React11.useState(0);
|
|
2056
|
+
const [pasted, setPasted] = React11.useState(null);
|
|
2057
|
+
React11.useEffect(() => {
|
|
2058
|
+
if (pasted) {
|
|
2059
|
+
setState({ status: "ready", catalog: pasted, loadedAt: new Date, source: "pasted" });
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
if (provided) {
|
|
2063
|
+
setState({ status: "ready", catalog: provided, loadedAt: new Date, source: "provided" });
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
if (!url)
|
|
2067
|
+
return;
|
|
2068
|
+
const ctrl = new AbortController;
|
|
2069
|
+
setState((s) => ({ status: "loading", catalog: s.catalog }));
|
|
2070
|
+
loadCatalog2(url, { signal: ctrl.signal }).then((catalog) => setState({ status: "ready", catalog, loadedAt: new Date, source: "url" }), (e) => {
|
|
2071
|
+
if (!ctrl.signal.aborted)
|
|
2072
|
+
setState((s) => ({ status: "error", catalog: s.catalog, error: e instanceof Error ? e.message : String(e) }));
|
|
2077
2073
|
});
|
|
2074
|
+
return () => ctrl.abort();
|
|
2075
|
+
}, [url, provided, nonce, pasted]);
|
|
2076
|
+
const reload = React11.useCallback(() => {
|
|
2077
|
+
setPasted(null);
|
|
2078
|
+
setNonce((n) => n + 1);
|
|
2078
2079
|
}, []);
|
|
2079
|
-
|
|
2080
|
+
const loadPasted = React11.useCallback((text) => new Promise((resolve) => {
|
|
2081
|
+
setTimeout(() => {
|
|
2082
|
+
try {
|
|
2083
|
+
setPasted(catalogFromText2(text, "pasted"));
|
|
2084
|
+
resolve(undefined);
|
|
2085
|
+
} catch (e) {
|
|
2086
|
+
resolve(e instanceof Error ? e.message : String(e));
|
|
2087
|
+
}
|
|
2088
|
+
}, 0);
|
|
2089
|
+
}), []);
|
|
2090
|
+
return [state, reload, loadPasted];
|
|
2080
2091
|
}
|
|
2081
2092
|
|
|
2082
2093
|
// src/studio/SettingsPanel.tsx
|
|
2083
2094
|
import { jsx as jsx17, jsxs as jsxs15, Fragment as Fragment9 } from "react/jsx-runtime";
|
|
2084
2095
|
var TIME = new Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "2-digit" });
|
|
2085
|
-
|
|
2086
|
-
try {
|
|
2087
|
-
const u = new URL(v, location.href);
|
|
2088
|
-
return u.protocol === "https:" || u.protocol === "http:" || u.protocol === "blob:";
|
|
2089
|
-
} catch {
|
|
2090
|
-
return false;
|
|
2091
|
-
}
|
|
2092
|
-
}
|
|
2096
|
+
var isSource = (v) => v.trim().length > 0 && !/^\s*(javascript|data):/i.test(v);
|
|
2093
2097
|
function docsSummary(c) {
|
|
2094
2098
|
if (c.status === "loading")
|
|
2095
2099
|
return "Loading…";
|
|
@@ -2182,14 +2186,12 @@ function PasteDocs({ onPaste, onDone }) {
|
|
|
2182
2186
|
]
|
|
2183
2187
|
});
|
|
2184
2188
|
}
|
|
2185
|
-
function SettingsPanel({ docsUrl, onDocsUrl, catalog, onReload, onPaste,
|
|
2189
|
+
function SettingsPanel({ docsUrl, onDocsUrl, catalog, onReload, onPaste, onClose }) {
|
|
2186
2190
|
const [draft, setDraft] = React12.useState(docsUrl);
|
|
2187
2191
|
const [pasting, setPasting] = React12.useState(false);
|
|
2188
2192
|
const usingPaste = catalog.status === "ready" && catalog.source === "pasted";
|
|
2189
|
-
const [importMsg, setImportMsg] = React12.useState(null);
|
|
2190
2193
|
const panel = React12.useRef(null);
|
|
2191
|
-
const
|
|
2192
|
-
const valid = isUrl(draft);
|
|
2194
|
+
const valid = isSource(draft);
|
|
2193
2195
|
const dirty = draft.trim() !== docsUrl;
|
|
2194
2196
|
React12.useEffect(() => {
|
|
2195
2197
|
const key = (e) => {
|
|
@@ -2218,223 +2220,144 @@ function SettingsPanel({ docsUrl, onDocsUrl, catalog, onReload, onPaste, manifes
|
|
|
2218
2220
|
else
|
|
2219
2221
|
onReload();
|
|
2220
2222
|
};
|
|
2221
|
-
|
|
2222
|
-
const blob = new Blob([JSON.stringify(manifests(), null, 2) + `
|
|
2223
|
-
`], { type: "application/json" });
|
|
2224
|
-
const a = document.createElement("a");
|
|
2225
|
-
a.href = URL.createObjectURL(blob);
|
|
2226
|
-
a.download = "bindings.json";
|
|
2227
|
-
a.click();
|
|
2228
|
-
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
|
|
2229
|
-
};
|
|
2230
|
-
const readFile = async (f) => {
|
|
2231
|
-
if (!f)
|
|
2232
|
-
return;
|
|
2233
|
-
try {
|
|
2234
|
-
const list = parseManifests(await f.text());
|
|
2235
|
-
onImport(list);
|
|
2236
|
-
setImportMsg({ ok: true, text: `Imported ${list.length} ${list.length === 1 ? "screen" : "screens"}.` });
|
|
2237
|
-
} catch (err) {
|
|
2238
|
-
setImportMsg({ ok: false, text: err instanceof Error ? err.message : "The file could not be read." });
|
|
2239
|
-
} finally {
|
|
2240
|
-
if (file.current)
|
|
2241
|
-
file.current.value = "";
|
|
2242
|
-
}
|
|
2243
|
-
};
|
|
2244
|
-
return /* @__PURE__ */ jsxs15("div", {
|
|
2223
|
+
return /* @__PURE__ */ jsx17("div", {
|
|
2245
2224
|
className: "abs-settings abs-pop",
|
|
2246
2225
|
ref: panel,
|
|
2247
2226
|
role: "dialog",
|
|
2248
2227
|
"aria-label": "Settings",
|
|
2249
|
-
children:
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
children: dirty ? "Use" : /* @__PURE__ */ jsxs15(Fragment9, {
|
|
2284
|
-
children: [
|
|
2285
|
-
/* @__PURE__ */ jsx17(Icon, {
|
|
2286
|
-
name: "refresh",
|
|
2287
|
-
size: 14,
|
|
2288
|
-
className: catalog.status === "loading" ? "abs-spin" : ""
|
|
2289
|
-
}),
|
|
2290
|
-
"Reload"
|
|
2291
|
-
]
|
|
2292
|
-
})
|
|
2293
|
-
})
|
|
2294
|
-
]
|
|
2295
|
-
}),
|
|
2296
|
-
/* @__PURE__ */ jsx17("p", {
|
|
2297
|
-
id: "abs-docs-status",
|
|
2298
|
-
className: "abs-set-status" + (catalog.status === "error" ? " is-error" : ""),
|
|
2299
|
-
"aria-live": "polite",
|
|
2300
|
-
children: valid ? docsSummary(catalog) : "Enter a full URL, for example " + DEFAULT_DOCS_URL
|
|
2301
|
-
}),
|
|
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,
|
|
2228
|
+
children: /* @__PURE__ */ jsxs15("form", {
|
|
2229
|
+
className: "abs-set-sec",
|
|
2230
|
+
onSubmit: apply,
|
|
2231
|
+
children: [
|
|
2232
|
+
/* @__PURE__ */ jsx17("label", {
|
|
2233
|
+
className: "abs-set-h",
|
|
2234
|
+
htmlFor: "abs-docs-url",
|
|
2235
|
+
children: "API docs"
|
|
2236
|
+
}),
|
|
2237
|
+
/* @__PURE__ */ jsx17("p", {
|
|
2238
|
+
className: "abs-set-d",
|
|
2239
|
+
children: "The OpenAPI file every API is read from: a URL, or a path next to the bindings file."
|
|
2240
|
+
}),
|
|
2241
|
+
/* @__PURE__ */ jsxs15("div", {
|
|
2242
|
+
className: "abs-set-row",
|
|
2243
|
+
children: [
|
|
2244
|
+
/* @__PURE__ */ jsx17("input", {
|
|
2245
|
+
id: "abs-docs-url",
|
|
2246
|
+
name: "docsUrl",
|
|
2247
|
+
type: "text",
|
|
2248
|
+
inputMode: "url",
|
|
2249
|
+
autoComplete: "off",
|
|
2250
|
+
spellCheck: false,
|
|
2251
|
+
className: "abs-input" + (valid ? "" : " is-invalid"),
|
|
2252
|
+
value: draft,
|
|
2253
|
+
onChange: (e) => setDraft(e.target.value),
|
|
2254
|
+
"aria-invalid": !valid,
|
|
2255
|
+
"aria-describedby": "abs-docs-status"
|
|
2256
|
+
}),
|
|
2257
|
+
/* @__PURE__ */ jsx17("button", {
|
|
2258
|
+
type: "submit",
|
|
2259
|
+
className: "abs-btn abs-btn-primary",
|
|
2260
|
+
disabled: !valid || catalog.status === "loading",
|
|
2261
|
+
children: dirty ? "Use" : /* @__PURE__ */ jsxs15(Fragment9, {
|
|
2312
2262
|
children: [
|
|
2313
2263
|
/* @__PURE__ */ jsx17(Icon, {
|
|
2314
2264
|
name: "refresh",
|
|
2315
|
-
size: 14
|
|
2265
|
+
size: 14,
|
|
2266
|
+
className: catalog.status === "loading" ? "abs-spin" : ""
|
|
2316
2267
|
}),
|
|
2317
|
-
"
|
|
2268
|
+
"Reload"
|
|
2318
2269
|
]
|
|
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
|
-
})
|
|
2342
|
-
]
|
|
2343
|
-
}),
|
|
2344
|
-
/* @__PURE__ */ jsxs15("div", {
|
|
2345
|
-
className: "abs-set-sec",
|
|
2346
|
-
children: [
|
|
2347
|
-
/* @__PURE__ */ jsx17("span", {
|
|
2348
|
-
className: "abs-set-h",
|
|
2349
|
-
children: "Bindings"
|
|
2350
|
-
}),
|
|
2351
|
-
/* @__PURE__ */ jsx17("p", {
|
|
2352
|
-
className: "abs-set-d",
|
|
2353
|
-
children: "Download to commit them with the app. Import replaces the screens the file contains."
|
|
2354
|
-
}),
|
|
2355
|
-
/* @__PURE__ */ jsxs15("div", {
|
|
2356
|
-
className: "abs-set-row",
|
|
2357
|
-
children: [
|
|
2358
|
-
/* @__PURE__ */ jsxs15("button", {
|
|
2359
|
-
type: "button",
|
|
2360
|
-
className: "abs-btn",
|
|
2361
|
-
onClick: download,
|
|
2362
|
-
children: [
|
|
2363
|
-
/* @__PURE__ */ jsx17(Icon, {
|
|
2364
|
-
name: "download",
|
|
2365
|
-
size: 14
|
|
2366
|
-
}),
|
|
2367
|
-
"Download"
|
|
2368
|
-
]
|
|
2369
|
-
}),
|
|
2370
|
-
/* @__PURE__ */ jsxs15("button", {
|
|
2371
|
-
type: "button",
|
|
2372
|
-
className: "abs-btn",
|
|
2373
|
-
onClick: () => file.current?.click(),
|
|
2374
|
-
children: [
|
|
2375
|
-
/* @__PURE__ */ jsx17(Icon, {
|
|
2376
|
-
name: "upload",
|
|
2377
|
-
size: 14
|
|
2378
|
-
}),
|
|
2379
|
-
"Import…"
|
|
2380
|
-
]
|
|
2381
|
-
}),
|
|
2382
|
-
/* @__PURE__ */ jsx17("input", {
|
|
2383
|
-
ref: file,
|
|
2384
|
-
type: "file",
|
|
2385
|
-
accept: "application/json,.json",
|
|
2386
|
-
hidden: true,
|
|
2387
|
-
onChange: (e) => void readFile(e.target.files?.[0])
|
|
2388
2270
|
})
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2271
|
+
})
|
|
2272
|
+
]
|
|
2273
|
+
}),
|
|
2274
|
+
/* @__PURE__ */ jsx17("p", {
|
|
2275
|
+
id: "abs-docs-status",
|
|
2276
|
+
className: "abs-set-status" + (catalog.status === "error" ? " is-error" : ""),
|
|
2277
|
+
"aria-live": "polite",
|
|
2278
|
+
children: valid ? docsSummary(catalog) : "Enter a URL or a path, for example " + DEFAULT_DOCS_URL
|
|
2279
|
+
}),
|
|
2280
|
+
pasting ? /* @__PURE__ */ jsx17(PasteDocs, {
|
|
2281
|
+
onPaste,
|
|
2282
|
+
onDone: () => setPasting(false)
|
|
2283
|
+
}) : /* @__PURE__ */ jsxs15("div", {
|
|
2284
|
+
className: "abs-set-links",
|
|
2285
|
+
children: [
|
|
2286
|
+
usingPaste ? /* @__PURE__ */ jsxs15("button", {
|
|
2287
|
+
type: "button",
|
|
2288
|
+
className: "abs-link",
|
|
2289
|
+
onClick: onReload,
|
|
2290
|
+
children: [
|
|
2291
|
+
/* @__PURE__ */ jsx17(Icon, {
|
|
2292
|
+
name: "refresh",
|
|
2293
|
+
size: 14
|
|
2294
|
+
}),
|
|
2295
|
+
"Use the URL again"
|
|
2296
|
+
]
|
|
2297
|
+
}) : /* @__PURE__ */ jsxs15("button", {
|
|
2298
|
+
type: "button",
|
|
2299
|
+
className: "abs-link",
|
|
2300
|
+
onClick: () => setPasting(true),
|
|
2301
|
+
children: [
|
|
2302
|
+
/* @__PURE__ */ jsx17(Icon, {
|
|
2303
|
+
name: "upload",
|
|
2304
|
+
size: 14
|
|
2305
|
+
}),
|
|
2306
|
+
"Paste the file instead"
|
|
2307
|
+
]
|
|
2308
|
+
}),
|
|
2309
|
+
docsUrl !== DEFAULT_DOCS_URL ? /* @__PURE__ */ jsx17("button", {
|
|
2310
|
+
type: "button",
|
|
2311
|
+
className: "abs-link",
|
|
2312
|
+
onClick: () => {
|
|
2313
|
+
setDraft(DEFAULT_DOCS_URL);
|
|
2314
|
+
onDocsUrl(DEFAULT_DOCS_URL);
|
|
2315
|
+
},
|
|
2316
|
+
children: "Use the default URL"
|
|
2317
|
+
}) : null
|
|
2318
|
+
]
|
|
2319
|
+
})
|
|
2320
|
+
]
|
|
2321
|
+
})
|
|
2399
2322
|
});
|
|
2400
2323
|
}
|
|
2401
2324
|
|
|
2402
2325
|
// src/studio/useBindings.ts
|
|
2403
2326
|
import * as React13 from "react";
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2327
|
+
|
|
2328
|
+
// src/framework/store.ts
|
|
2329
|
+
function putBinding(idx, screen, b) {
|
|
2330
|
+
const list = (idx[screen] ?? []).filter((x) => !(x.component === b.component && x.slot === b.slot));
|
|
2331
|
+
return { ...idx, [screen]: [...list, b] };
|
|
2332
|
+
}
|
|
2333
|
+
function dropBinding(idx, screen, component, slot) {
|
|
2334
|
+
return { ...idx, [screen]: (idx[screen] ?? []).filter((x) => !(x.component === component && x.slot === slot)) };
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// src/studio/useBindings.ts
|
|
2338
|
+
function useBindings(initial, save) {
|
|
2339
|
+
const [index, setIndex] = React13.useState(initial);
|
|
2407
2340
|
const [status, setStatus] = React13.useState("idle");
|
|
2408
2341
|
const [removed, setRemoved] = React13.useState(null);
|
|
2409
|
-
const
|
|
2342
|
+
const edits = React13.useRef(0);
|
|
2410
2343
|
const latest = React13.useRef(index);
|
|
2411
2344
|
latest.current = index;
|
|
2412
|
-
const screenIds = fw?.registry.screens.map((s) => s.id).join(`
|
|
2413
|
-
`) ?? "";
|
|
2414
|
-
React13.useEffect(() => {
|
|
2415
|
-
if (!screenIds)
|
|
2416
|
-
return;
|
|
2417
|
-
let live = true;
|
|
2418
|
-
loadAll(adapter, screenIds.split(`
|
|
2419
|
-
`)).then((i) => {
|
|
2420
|
-
if (live) {
|
|
2421
|
-
setIndex(i);
|
|
2422
|
-
setLoaded(true);
|
|
2423
|
-
}
|
|
2424
|
-
});
|
|
2425
|
-
return () => {
|
|
2426
|
-
live = false;
|
|
2427
|
-
};
|
|
2428
|
-
}, [adapter, screenIds]);
|
|
2429
2345
|
const flush = React13.useCallback(async () => {
|
|
2430
|
-
|
|
2346
|
+
const at = edits.current;
|
|
2347
|
+
if (!at)
|
|
2431
2348
|
return;
|
|
2432
2349
|
setStatus("saving");
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2350
|
+
try {
|
|
2351
|
+
await save(latest.current);
|
|
2352
|
+
if (edits.current === at)
|
|
2353
|
+
edits.current = 0;
|
|
2354
|
+
setStatus(edits.current ? "saving" : "saved");
|
|
2355
|
+
} catch {
|
|
2356
|
+
setStatus("failed");
|
|
2357
|
+
}
|
|
2358
|
+
}, [save]);
|
|
2436
2359
|
React13.useEffect(() => {
|
|
2437
|
-
if (!
|
|
2360
|
+
if (!edits.current)
|
|
2438
2361
|
return;
|
|
2439
2362
|
setStatus("saving");
|
|
2440
2363
|
const t = setTimeout(flush, 400);
|
|
@@ -2445,21 +2368,21 @@ function useBindings(fw, adapter) {
|
|
|
2445
2368
|
}, [flush]);
|
|
2446
2369
|
React13.useEffect(() => {
|
|
2447
2370
|
const warn = (e) => {
|
|
2448
|
-
if (
|
|
2371
|
+
if (edits.current)
|
|
2449
2372
|
e.preventDefault();
|
|
2450
2373
|
};
|
|
2451
2374
|
window.addEventListener("beforeunload", warn);
|
|
2452
2375
|
return () => window.removeEventListener("beforeunload", warn);
|
|
2453
2376
|
}, []);
|
|
2454
2377
|
const put = React13.useCallback((screen, b) => {
|
|
2455
|
-
|
|
2378
|
+
edits.current++;
|
|
2456
2379
|
setIndex((i) => putBinding(i, screen, b));
|
|
2457
2380
|
}, []);
|
|
2458
2381
|
const drop = React13.useCallback((screen, component, slot) => {
|
|
2459
2382
|
const binding = latest.current[screen]?.find((x) => x.component === component && x.slot === slot);
|
|
2460
2383
|
if (binding)
|
|
2461
2384
|
setRemoved({ screen, binding });
|
|
2462
|
-
|
|
2385
|
+
edits.current++;
|
|
2463
2386
|
setIndex((i) => dropBinding(i, screen, component, slot));
|
|
2464
2387
|
}, []);
|
|
2465
2388
|
const undo = React13.useCallback(() => {
|
|
@@ -2468,95 +2391,12 @@ function useBindings(fw, adapter) {
|
|
|
2468
2391
|
setRemoved(null);
|
|
2469
2392
|
}, [put, removed]);
|
|
2470
2393
|
const dismiss = React13.useCallback(() => setRemoved(null), []);
|
|
2471
|
-
|
|
2472
|
-
manifests.forEach((m) => markDirty(pending.current, m.screen));
|
|
2473
|
-
setIndex((i) => ({ ...i, ...Object.fromEntries(manifests.map((m) => [m.screen, m.bindings])) }));
|
|
2474
|
-
}, []);
|
|
2475
|
-
return { index, loaded, status, removed, put, drop, undo, dismiss, importManifests, retry: flush };
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
// src/studio/useCatalog.ts
|
|
2479
|
-
import * as React14 from "react";
|
|
2480
|
-
|
|
2481
|
-
// src/framework/loadCatalog.ts
|
|
2482
|
-
import { parse } from "yaml";
|
|
2483
|
-
class CatalogLoadError extends Error {
|
|
2484
|
-
source;
|
|
2485
|
-
constructor(message, source) {
|
|
2486
|
-
super(message);
|
|
2487
|
-
this.source = source;
|
|
2488
|
-
this.name = "CatalogLoadError";
|
|
2489
|
-
}
|
|
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
|
-
}
|
|
2507
|
-
async function loadCatalog(url, init) {
|
|
2508
|
-
let res;
|
|
2509
|
-
try {
|
|
2510
|
-
res = await fetch(url, { ...init, headers: { Accept: "application/yaml, application/json, text/yaml, */*", ...init?.headers } });
|
|
2511
|
-
} catch {
|
|
2512
|
-
throw new CatalogLoadError("The docs could not be reached. Check the URL, your network, and that the docs server allows this site (CORS).", url);
|
|
2513
|
-
}
|
|
2514
|
-
if (!res.ok)
|
|
2515
|
-
throw new CatalogLoadError(`The docs server answered ${res.status} ${res.statusText}. Check the URL.`, url);
|
|
2516
|
-
return catalogFromText(await res.text(), url);
|
|
2517
|
-
}
|
|
2518
|
-
|
|
2519
|
-
// src/studio/useCatalog.ts
|
|
2520
|
-
function useCatalog(url, provided) {
|
|
2521
|
-
const [state, setState] = React14.useState(() => provided ? { status: "ready", catalog: provided, loadedAt: new Date, source: "provided" } : { status: "loading" });
|
|
2522
|
-
const [nonce, setNonce] = React14.useState(0);
|
|
2523
|
-
const [pasted, setPasted] = React14.useState(null);
|
|
2524
|
-
React14.useEffect(() => {
|
|
2525
|
-
if (pasted) {
|
|
2526
|
-
setState({ status: "ready", catalog: pasted, loadedAt: new Date, source: "pasted" });
|
|
2527
|
-
return;
|
|
2528
|
-
}
|
|
2529
|
-
if (provided) {
|
|
2530
|
-
setState({ status: "ready", catalog: provided, loadedAt: new Date, source: "provided" });
|
|
2531
|
-
return;
|
|
2532
|
-
}
|
|
2533
|
-
const ctrl = new AbortController;
|
|
2534
|
-
setState((s) => ({ status: "loading", catalog: s.catalog }));
|
|
2535
|
-
loadCatalog(url, { signal: ctrl.signal }).then((catalog) => setState({ status: "ready", catalog, loadedAt: new Date, source: "url" }), (e) => {
|
|
2536
|
-
if (!ctrl.signal.aborted)
|
|
2537
|
-
setState((s) => ({ status: "error", catalog: s.catalog, error: e instanceof Error ? e.message : String(e) }));
|
|
2538
|
-
});
|
|
2539
|
-
return () => ctrl.abort();
|
|
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];
|
|
2394
|
+
return { index, loaded: true, status, removed, put, drop, undo, dismiss, retry: flush };
|
|
2556
2395
|
}
|
|
2557
2396
|
|
|
2558
2397
|
// src/studio/BindingStudio.tsx
|
|
2559
2398
|
import { jsx as jsx18, jsxs as jsxs16, Fragment as Fragment10 } from "react/jsx-runtime";
|
|
2399
|
+
var same = (s) => s;
|
|
2560
2400
|
function SaveState({ status, onRetry }) {
|
|
2561
2401
|
return /* @__PURE__ */ jsx18("span", {
|
|
2562
2402
|
className: "abs-saved is-" + status,
|
|
@@ -2592,7 +2432,7 @@ function SaveState({ status, onRetry }) {
|
|
|
2592
2432
|
});
|
|
2593
2433
|
}
|
|
2594
2434
|
function UndoToast({ removed, label, onUndo, onDismiss }) {
|
|
2595
|
-
|
|
2435
|
+
React14.useEffect(() => {
|
|
2596
2436
|
const t = setTimeout(onDismiss, 6000);
|
|
2597
2437
|
return () => clearTimeout(t);
|
|
2598
2438
|
}, [removed, onDismiss]);
|
|
@@ -2632,15 +2472,45 @@ var EMPTY_HINT = /* @__PURE__ */ jsxs16("aside", {
|
|
|
2632
2472
|
})
|
|
2633
2473
|
]
|
|
2634
2474
|
});
|
|
2635
|
-
function BindingStudio({
|
|
2636
|
-
const [
|
|
2637
|
-
const [
|
|
2638
|
-
const
|
|
2639
|
-
|
|
2640
|
-
|
|
2475
|
+
function BindingStudio({ store, docsUrl = same, initialScreen, initialComponent, title = "Bind API", onClose }) {
|
|
2476
|
+
const [file, setFile] = React14.useState(null);
|
|
2477
|
+
const [fileError, setFileError] = React14.useState();
|
|
2478
|
+
const latest = React14.useRef({ file: null, index: {} });
|
|
2479
|
+
React14.useEffect(() => {
|
|
2480
|
+
let live = true;
|
|
2481
|
+
store.load().then((f) => {
|
|
2482
|
+
if (live) {
|
|
2483
|
+
latest.current.file = f;
|
|
2484
|
+
setFile(f);
|
|
2485
|
+
}
|
|
2486
|
+
}, (e) => live && setFileError(e instanceof Error ? e.message : String(e)));
|
|
2487
|
+
return () => {
|
|
2488
|
+
live = false;
|
|
2489
|
+
};
|
|
2490
|
+
}, [store]);
|
|
2491
|
+
const [catalogState, reloadCatalog, loadPasted] = useCatalog(file ? docsUrl(file.openapi) : "");
|
|
2492
|
+
const [settingsOpen, setSettingsOpen] = React14.useState(false);
|
|
2493
|
+
const closeSettings = React14.useCallback(() => setSettingsOpen(false), []);
|
|
2641
2494
|
const catalog = catalogState.catalog;
|
|
2642
|
-
const
|
|
2643
|
-
|
|
2495
|
+
const model = React14.useMemo(() => {
|
|
2496
|
+
if (!file || !catalog)
|
|
2497
|
+
return null;
|
|
2498
|
+
const { fw, index } = fromFile2(file, catalog);
|
|
2499
|
+
return { fw: { ...fw, registry: validateRegistry2(fw).registry }, index };
|
|
2500
|
+
}, [file?.kinds, file?.screens, file?.components, catalog]);
|
|
2501
|
+
const save = React14.useCallback(async (index) => {
|
|
2502
|
+
latest.current.index = index;
|
|
2503
|
+
if (latest.current.file)
|
|
2504
|
+
await store.save(toFile2(latest.current.file, index));
|
|
2505
|
+
}, [store]);
|
|
2506
|
+
const setOpenapi = (openapi) => {
|
|
2507
|
+
const f = latest.current.file;
|
|
2508
|
+
if (!f)
|
|
2509
|
+
return;
|
|
2510
|
+
latest.current.file = { ...f, openapi };
|
|
2511
|
+
setFile(latest.current.file);
|
|
2512
|
+
store.save(toFile2(latest.current.file, Object.keys(latest.current.index).length ? latest.current.index : model?.index ?? {}));
|
|
2513
|
+
};
|
|
2644
2514
|
const settingsButton = /* @__PURE__ */ jsxs16("div", {
|
|
2645
2515
|
className: "abs-settings-wrap",
|
|
2646
2516
|
children: [
|
|
@@ -2669,19 +2539,18 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2669
2539
|
name: "close"
|
|
2670
2540
|
})
|
|
2671
2541
|
}) : null,
|
|
2672
|
-
settingsOpen ? /* @__PURE__ */ jsx18(SettingsPanel, {
|
|
2673
|
-
docsUrl:
|
|
2674
|
-
onDocsUrl:
|
|
2542
|
+
settingsOpen && file ? /* @__PURE__ */ jsx18(SettingsPanel, {
|
|
2543
|
+
docsUrl: file.openapi,
|
|
2544
|
+
onDocsUrl: setOpenapi,
|
|
2675
2545
|
catalog: catalogState,
|
|
2676
2546
|
onReload: reloadCatalog,
|
|
2677
2547
|
onPaste: loadPasted,
|
|
2678
|
-
manifests: () => fw ? Object.entries(binds.index).map(([s, b]) => toManifest2(fw, s, b)) : [],
|
|
2679
|
-
onImport: binds.importManifests,
|
|
2680
2548
|
onClose: closeSettings
|
|
2681
2549
|
}) : null
|
|
2682
2550
|
]
|
|
2683
2551
|
});
|
|
2684
|
-
if (!
|
|
2552
|
+
if (!model) {
|
|
2553
|
+
const error = fileError ?? (catalogState.status === "error" ? catalogState.error : undefined);
|
|
2685
2554
|
return /* @__PURE__ */ jsxs16("div", {
|
|
2686
2555
|
className: "abs-root",
|
|
2687
2556
|
children: [
|
|
@@ -2698,7 +2567,7 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2698
2567
|
}),
|
|
2699
2568
|
/* @__PURE__ */ jsx18("div", {
|
|
2700
2569
|
className: "abs-state-page",
|
|
2701
|
-
children:
|
|
2570
|
+
children: error ? /* @__PURE__ */ jsxs16(Fragment10, {
|
|
2702
2571
|
children: [
|
|
2703
2572
|
/* @__PURE__ */ jsx18("span", {
|
|
2704
2573
|
className: "abs-empty-ico is-bad",
|
|
@@ -2708,12 +2577,12 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2708
2577
|
})
|
|
2709
2578
|
}),
|
|
2710
2579
|
/* @__PURE__ */ jsx18("b", {
|
|
2711
|
-
children: "The API docs did not load"
|
|
2580
|
+
children: fileError ? "The bindings file did not load" : "The API docs did not load"
|
|
2712
2581
|
}),
|
|
2713
2582
|
/* @__PURE__ */ jsx18("span", {
|
|
2714
|
-
children:
|
|
2583
|
+
children: error
|
|
2715
2584
|
}),
|
|
2716
|
-
/* @__PURE__ */ jsxs16("div", {
|
|
2585
|
+
fileError ? null : /* @__PURE__ */ jsxs16("div", {
|
|
2717
2586
|
className: "abs-set-row",
|
|
2718
2587
|
children: [
|
|
2719
2588
|
/* @__PURE__ */ jsxs16("button", {
|
|
@@ -2745,7 +2614,7 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2745
2614
|
}),
|
|
2746
2615
|
/* @__PURE__ */ jsx18("span", {
|
|
2747
2616
|
"aria-live": "polite",
|
|
2748
|
-
children: "Loading API docs…"
|
|
2617
|
+
children: file ? "Loading API docs…" : "Loading bindings…"
|
|
2749
2618
|
})
|
|
2750
2619
|
]
|
|
2751
2620
|
})
|
|
@@ -2754,26 +2623,28 @@ function BindingStudio({ framework, docsUrl = DEFAULT_DOCS_URL, persistence, ini
|
|
|
2754
2623
|
});
|
|
2755
2624
|
}
|
|
2756
2625
|
return /* @__PURE__ */ jsx18(Workbench, {
|
|
2757
|
-
fw,
|
|
2758
|
-
|
|
2626
|
+
fw: model.fw,
|
|
2627
|
+
initial: model.index,
|
|
2628
|
+
save,
|
|
2759
2629
|
title,
|
|
2760
|
-
initialScreen,
|
|
2630
|
+
initialScreen: initialComponent ? screenOf2(initialComponent, model.fw.registry.screens.map((s) => s.id)) ?? initialScreen : initialScreen,
|
|
2631
|
+
initialComponent,
|
|
2761
2632
|
settingsButton
|
|
2762
2633
|
});
|
|
2763
2634
|
}
|
|
2764
|
-
function Workbench({ fw,
|
|
2765
|
-
const { index, loaded, status, removed, put, drop, undo, dismiss, retry } =
|
|
2766
|
-
const [place, go] =
|
|
2767
|
-
const engine =
|
|
2635
|
+
function Workbench({ fw, initial, save, title, initialScreen, initialComponent, settingsButton }) {
|
|
2636
|
+
const { index, loaded, status, removed, put, drop, undo, dismiss, retry } = useBindings(initial, save);
|
|
2637
|
+
const [place, go] = React14.useState({ screen: initialScreen, component: initialComponent });
|
|
2638
|
+
const engine = React14.useMemo(() => createEngine2(fw, index), [fw, index]);
|
|
2768
2639
|
const screen = place.screen && engine.SCR.get(place.screen) || fw.registry.screens[0];
|
|
2769
2640
|
const comp = screen && place.component ? engine.compOf(screen.id, place.component) : undefined;
|
|
2770
2641
|
const screenId = screen?.id;
|
|
2771
|
-
const select =
|
|
2772
|
-
const putHere =
|
|
2642
|
+
const select = React14.useCallback((component) => go({ screen: screenId, component }), [go, screenId]);
|
|
2643
|
+
const putHere = React14.useCallback((b) => {
|
|
2773
2644
|
if (screenId)
|
|
2774
2645
|
put(screenId, b);
|
|
2775
2646
|
}, [put, screenId]);
|
|
2776
|
-
const dropHere =
|
|
2647
|
+
const dropHere = React14.useCallback((c, s) => {
|
|
2777
2648
|
if (screenId)
|
|
2778
2649
|
drop(screenId, c, s);
|
|
2779
2650
|
}, [drop, screenId]);
|
|
@@ -2861,87 +2732,9 @@ function Workbench({ fw, binds, title, initialScreen, settingsButton }) {
|
|
|
2861
2732
|
]
|
|
2862
2733
|
});
|
|
2863
2734
|
}
|
|
2864
|
-
// src/studio/BindingWidget.tsx
|
|
2865
|
-
import * as React16 from "react";
|
|
2866
|
-
import { jsx as jsx19, jsxs as jsxs17, Fragment as Fragment11 } from "react/jsx-runtime";
|
|
2867
|
-
var fallbackStore = localStorageAdapter2();
|
|
2868
|
-
function BindingWidget({ framework, docsUrl, title }) {
|
|
2869
|
-
const runtime = useBindingRuntime();
|
|
2870
|
-
const dialog = React16.useRef(null);
|
|
2871
|
-
const fab = React16.useRef(null);
|
|
2872
|
-
const [state, setState] = React16.useState("closed");
|
|
2873
|
-
const [screen, setScreen] = React16.useState();
|
|
2874
|
-
const open = () => {
|
|
2875
|
-
setScreen(runtime.activeScreen());
|
|
2876
|
-
setState("open");
|
|
2877
|
-
};
|
|
2878
|
-
const requestClose = React16.useCallback(() => setState((s) => s === "open" ? "closing" : s), []);
|
|
2879
|
-
React16.useLayoutEffect(() => {
|
|
2880
|
-
if (state === "open" && !dialog.current?.open)
|
|
2881
|
-
dialog.current?.showModal();
|
|
2882
|
-
}, [state]);
|
|
2883
|
-
const finishClose = (e) => {
|
|
2884
|
-
if (e.target !== dialog.current || state !== "closing")
|
|
2885
|
-
return;
|
|
2886
|
-
dialog.current?.close();
|
|
2887
|
-
setState("closed");
|
|
2888
|
-
runtime.reloadSaved();
|
|
2889
|
-
fab.current?.focus();
|
|
2890
|
-
};
|
|
2891
|
-
return /* @__PURE__ */ jsxs17(Fragment11, {
|
|
2892
|
-
children: [
|
|
2893
|
-
/* @__PURE__ */ jsxs17("button", {
|
|
2894
|
-
ref: fab,
|
|
2895
|
-
type: "button",
|
|
2896
|
-
className: "abs-fab",
|
|
2897
|
-
onClick: open,
|
|
2898
|
-
"aria-haspopup": "dialog",
|
|
2899
|
-
"aria-expanded": state !== "closed",
|
|
2900
|
-
children: [
|
|
2901
|
-
/* @__PURE__ */ jsx19("span", {
|
|
2902
|
-
className: "abs-fab-ico",
|
|
2903
|
-
children: /* @__PURE__ */ jsx19(Icon, {
|
|
2904
|
-
name: "link",
|
|
2905
|
-
size: 18
|
|
2906
|
-
})
|
|
2907
|
-
}),
|
|
2908
|
-
/* @__PURE__ */ jsx19("span", {
|
|
2909
|
-
className: "abs-fab-label",
|
|
2910
|
-
children: /* @__PURE__ */ jsx19("span", {
|
|
2911
|
-
children: title ?? "Bind API"
|
|
2912
|
-
})
|
|
2913
|
-
})
|
|
2914
|
-
]
|
|
2915
|
-
}),
|
|
2916
|
-
state !== "closed" ? /* @__PURE__ */ jsx19("dialog", {
|
|
2917
|
-
ref: dialog,
|
|
2918
|
-
className: "abs-widget" + (state === "closing" ? " is-closing" : ""),
|
|
2919
|
-
"aria-label": title ?? "Bind API",
|
|
2920
|
-
onCancel: (e) => {
|
|
2921
|
-
e.preventDefault();
|
|
2922
|
-
requestClose();
|
|
2923
|
-
},
|
|
2924
|
-
onClick: (e) => {
|
|
2925
|
-
if (e.target === e.currentTarget)
|
|
2926
|
-
requestClose();
|
|
2927
|
-
},
|
|
2928
|
-
onAnimationEnd: finishClose,
|
|
2929
|
-
children: /* @__PURE__ */ jsx19(BindingStudio, {
|
|
2930
|
-
framework,
|
|
2931
|
-
docsUrl,
|
|
2932
|
-
title,
|
|
2933
|
-
initialScreen: screen,
|
|
2934
|
-
persistence: runtime.persistence ?? fallbackStore,
|
|
2935
|
-
onClose: requestClose
|
|
2936
|
-
})
|
|
2937
|
-
}) : null
|
|
2938
|
-
]
|
|
2939
|
-
});
|
|
2940
|
-
}
|
|
2941
2735
|
export {
|
|
2942
|
-
BindingStudio
|
|
2943
|
-
BindingWidget
|
|
2736
|
+
BindingStudio
|
|
2944
2737
|
};
|
|
2945
2738
|
|
|
2946
|
-
//# debugId=
|
|
2739
|
+
//# debugId=033A885DE9DD742964756E2164756E21
|
|
2947
2740
|
//# sourceMappingURL=index.js.map
|