@backstage/plugin-techdocs 1.0.1-next.1 → 1.1.0-next.3

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/dist/index.esm.js CHANGED
@@ -1,2145 +1,40 @@
1
- import { createApiRef, useApi, configApiRef, createRouteRef, useRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, fetchApiRef, createRoutableExtension, useApp } from '@backstage/core-plugin-api';
2
- import { ResponseError, NotFoundError } from '@backstage/errors';
3
- import { EventSourcePolyfill } from 'event-source-polyfill';
4
- import React, { useState, useCallback, useEffect, useReducer, useRef, useMemo, createContext, useContext } from 'react';
5
- import { useNavigate as useNavigate$1, useParams, Routes, Route } from 'react-router-dom';
6
- import { withStyles, Tooltip, ThemeProvider, SvgIcon, makeStyles, ListItemText, ListItem, Divider, TextField, InputAdornment, IconButton, CircularProgress, createStyles, Button, Drawer, Grid, Typography, useTheme, lighten, alpha, Card, CardMedia, CardContent, CardActions } from '@material-ui/core';
7
- import { scmIntegrationsApiRef } from '@backstage/integration-react';
8
- import { Link, LogViewer, ErrorPage, Progress, SidebarPinStateContext, sidebarConfig, HeaderLabel, Header, ItemCardGrid, ItemCardHeader, Button as Button$1, WarningPanel, CodeSnippet, SubvalueCell, Table, EmptyState, PageWithHeader, Content, ContentHeader, SupportButton, Page, MissingAnnotationEmptyState } from '@backstage/core-components';
9
- import { replaceGitHubUrlType } from '@backstage/integration';
10
- import FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined';
11
- import ReactDOM from 'react-dom';
12
- import parseGitUrl from 'git-url-parse';
13
- import MenuIcon from '@material-ui/icons/Menu';
14
- import DOMPurify from 'dompurify';
15
- import TextTruncate from 'react-text-truncate';
16
- import { SearchContextProvider, useSearch } from '@backstage/plugin-search';
17
- import SearchIcon from '@material-ui/icons/Search';
18
- import Autocomplete from '@material-ui/lab/Autocomplete';
19
- import { useNavigate, useOutlet } from 'react-router';
20
- import useDebounce from 'react-use/lib/useDebounce';
21
- import { Alert } from '@material-ui/lab';
22
- import Close from '@material-ui/icons/Close';
23
- import useAsync from 'react-use/lib/useAsync';
24
- import useAsyncRetry from 'react-use/lib/useAsyncRetry';
25
- import CodeIcon from '@material-ui/icons/Code';
26
- import { RELATION_OWNED_BY } from '@backstage/catalog-model';
27
- import { getEntityRelations, EntityRefLink, EntityRefLinks, useEntityList, humanizeEntityRef, useStarredEntities, CATALOG_FILTER_EXISTS, EntityListProvider, CatalogFilterLayout, UserListPicker, EntityOwnerPicker, EntityTagPicker, useEntity } from '@backstage/plugin-catalog-react';
28
- import useCopyToClipboard from 'react-use/lib/useCopyToClipboard';
29
- import { capitalize } from 'lodash';
30
- import ShareIcon from '@material-ui/icons/Share';
31
- import { withStyles as withStyles$1 } from '@material-ui/styles';
32
- import Star from '@material-ui/icons/Star';
33
- import StarBorder from '@material-ui/icons/StarBorder';
34
-
35
- const techdocsStorageApiRef = createApiRef({
36
- id: "plugin.techdocs.storageservice"
37
- });
38
- const techdocsApiRef = createApiRef({
39
- id: "plugin.techdocs.service"
40
- });
41
-
42
- class TechDocsClient {
43
- constructor(options) {
44
- this.configApi = options.configApi;
45
- this.discoveryApi = options.discoveryApi;
46
- this.fetchApi = options.fetchApi;
47
- }
48
- async getApiOrigin() {
49
- return await this.discoveryApi.getBaseUrl("techdocs");
50
- }
51
- async getTechDocsMetadata(entityId) {
52
- const { kind, namespace, name } = entityId;
53
- const apiOrigin = await this.getApiOrigin();
54
- const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;
55
- const request = await this.fetchApi.fetch(`${requestUrl}`);
56
- if (!request.ok) {
57
- throw await ResponseError.fromResponse(request);
58
- }
59
- return await request.json();
60
- }
61
- async getEntityMetadata(entityId) {
62
- const { kind, namespace, name } = entityId;
63
- const apiOrigin = await this.getApiOrigin();
64
- const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;
65
- const request = await this.fetchApi.fetch(`${requestUrl}`);
66
- if (!request.ok) {
67
- throw await ResponseError.fromResponse(request);
68
- }
69
- return await request.json();
70
- }
71
- }
72
- class TechDocsStorageClient {
73
- constructor(options) {
74
- this.configApi = options.configApi;
75
- this.discoveryApi = options.discoveryApi;
76
- this.identityApi = options.identityApi;
77
- this.fetchApi = options.fetchApi;
78
- }
79
- async getApiOrigin() {
80
- return await this.discoveryApi.getBaseUrl("techdocs");
81
- }
82
- async getStorageUrl() {
83
- var _a;
84
- return (_a = this.configApi.getOptionalString("techdocs.storageUrl")) != null ? _a : `${await this.discoveryApi.getBaseUrl("techdocs")}/static/docs`;
85
- }
86
- async getBuilder() {
87
- return this.configApi.getString("techdocs.builder");
88
- }
89
- async getEntityDocs(entityId, path) {
90
- const { kind, namespace, name } = entityId;
91
- const storageUrl = await this.getStorageUrl();
92
- const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;
93
- const request = await this.fetchApi.fetch(`${url.endsWith("/") ? url : `${url}/`}index.html`);
94
- let errorMessage = "";
95
- switch (request.status) {
96
- case 404:
97
- errorMessage = "Page not found. ";
98
- if (!path) {
99
- errorMessage += "This could be because there is no index.md file in the root of the docs directory of this repository.";
100
- }
101
- throw new NotFoundError(errorMessage);
102
- case 500:
103
- errorMessage = "Could not generate documentation or an error in the TechDocs backend. ";
104
- throw new Error(errorMessage);
105
- }
106
- return request.text();
107
- }
108
- async syncEntityDocs(entityId, logHandler = () => {
109
- }) {
110
- const { kind, namespace, name } = entityId;
111
- const apiOrigin = await this.getApiOrigin();
112
- const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;
113
- const { token } = await this.identityApi.getCredentials();
114
- return new Promise((resolve, reject) => {
115
- const source = new EventSourcePolyfill(url, {
116
- withCredentials: true,
117
- headers: token ? { Authorization: `Bearer ${token}` } : {}
118
- });
119
- source.addEventListener("log", (e) => {
120
- if (e.data) {
121
- logHandler(JSON.parse(e.data));
122
- }
123
- });
124
- source.addEventListener("finish", (e) => {
125
- let updated = false;
126
- if (e.data) {
127
- ({ updated } = JSON.parse(e.data));
128
- }
129
- resolve(updated ? "updated" : "cached");
130
- });
131
- source.onerror = (e) => {
132
- source.close();
133
- switch (e.status) {
134
- case 404:
135
- reject(new NotFoundError(e.message));
136
- return;
137
- default:
138
- reject(new Error(e.data));
139
- return;
140
- }
141
- };
142
- });
143
- }
144
- async getBaseUrl(oldBaseUrl, entityId, path) {
145
- const { kind, namespace, name } = entityId;
146
- const apiOrigin = await this.getApiOrigin();
147
- const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`;
148
- return new URL(oldBaseUrl, newBaseUrl.endsWith("/") ? newBaseUrl : `${newBaseUrl}/`).toString();
149
- }
150
- }
151
-
152
- const isSvgNeedingInlining = (attrName, attrVal, apiOrigin) => {
153
- const isSrcToSvg = attrName === "src" && attrVal.endsWith(".svg");
154
- const isRelativeUrl = !attrVal.match(/^([a-z]*:)?\/\//i);
155
- const pointsToOurBackend = attrVal.startsWith(apiOrigin);
156
- return isSrcToSvg && (isRelativeUrl || pointsToOurBackend);
157
- };
158
- const addBaseUrl = ({
159
- techdocsStorageApi,
160
- entityId,
161
- path
162
- }) => {
163
- return async (dom) => {
164
- const apiOrigin = await techdocsStorageApi.getApiOrigin();
165
- const updateDom = async (list, attributeName) => {
166
- for (const elem of list) {
167
- if (elem.hasAttribute(attributeName)) {
168
- const elemAttribute = elem.getAttribute(attributeName);
169
- if (!elemAttribute)
170
- return;
171
- const newValue = await techdocsStorageApi.getBaseUrl(elemAttribute, entityId, path);
172
- if (isSvgNeedingInlining(attributeName, elemAttribute, apiOrigin)) {
173
- try {
174
- const svg = await fetch(newValue, { credentials: "include" });
175
- const svgContent = await svg.text();
176
- elem.setAttribute(attributeName, `data:image/svg+xml;base64,${btoa(svgContent)}`);
177
- } catch (e) {
178
- elem.setAttribute("alt", `Error: ${elemAttribute}`);
179
- }
180
- } else {
181
- elem.setAttribute(attributeName, newValue);
182
- }
183
- }
184
- }
185
- };
186
- await Promise.all([
187
- updateDom(dom.querySelectorAll("img"), "src"),
188
- updateDom(dom.querySelectorAll("script"), "src"),
189
- updateDom(dom.querySelectorAll("source"), "src"),
190
- updateDom(dom.querySelectorAll("link"), "href"),
191
- updateDom(dom.querySelectorAll("a[download]"), "href")
192
- ]);
193
- return dom;
194
- };
195
- };
196
-
197
- const addGitFeedbackLink = (scmIntegrationsApi) => {
198
- return (dom) => {
199
- const sourceAnchor = dom.querySelector('[title="Edit this page"]');
200
- if (!sourceAnchor || !sourceAnchor.href) {
201
- return dom;
202
- }
203
- const sourceURL = new URL(sourceAnchor.href);
204
- const integration = scmIntegrationsApi.byUrl(sourceURL);
205
- if ((integration == null ? void 0 : integration.type) !== "github" && (integration == null ? void 0 : integration.type) !== "gitlab") {
206
- return dom;
207
- }
208
- const title = dom.querySelector("article>h1").childNodes[0].textContent;
209
- const issueTitle = encodeURIComponent(`Documentation Feedback: ${title}`);
210
- const issueDesc = encodeURIComponent(`Page source:
211
- ${sourceAnchor.href}
212
-
213
- Feedback:`);
214
- const gitUrl = (integration == null ? void 0 : integration.type) === "github" ? replaceGitHubUrlType(sourceURL.href, "blob") : sourceURL.href;
215
- const gitInfo = parseGitUrl(gitUrl);
216
- const repoPath = `/${gitInfo.organization}/${gitInfo.name}`;
217
- const feedbackLink = sourceAnchor.cloneNode();
218
- switch (integration == null ? void 0 : integration.type) {
219
- case "gitlab":
220
- feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?issue[title]=${issueTitle}&issue[description]=${issueDesc}`;
221
- break;
222
- case "github":
223
- feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?title=${issueTitle}&body=${issueDesc}`;
224
- break;
225
- default:
226
- return dom;
227
- }
228
- ReactDOM.render(React.createElement(FeedbackOutlinedIcon), feedbackLink);
229
- feedbackLink.style.paddingLeft = "5px";
230
- feedbackLink.title = "Leave feedback for this page";
231
- feedbackLink.id = "git-feedback-link";
232
- sourceAnchor == null ? void 0 : sourceAnchor.insertAdjacentElement("beforebegin", feedbackLink);
233
- return dom;
234
- };
235
- };
236
-
237
- const addSidebarToggle = () => {
238
- return (dom) => {
239
- const mkdocsToggleSidebar = dom.querySelector('.md-header label[for="__drawer"]');
240
- const article = dom.querySelector("article");
241
- if (!mkdocsToggleSidebar || !article) {
242
- return dom;
243
- }
244
- const toggleSidebar = mkdocsToggleSidebar.cloneNode();
245
- ReactDOM.render(React.createElement(MenuIcon), toggleSidebar);
246
- toggleSidebar.id = "toggle-sidebar";
247
- toggleSidebar.title = "Toggle Sidebar";
248
- toggleSidebar.classList.add("md-content__button");
249
- toggleSidebar.style.setProperty("padding", "0 0 0 5px");
250
- toggleSidebar.style.setProperty("margin", "0.4rem 0 0.4rem 0.4rem");
251
- article == null ? void 0 : article.prepend(toggleSidebar);
252
- return dom;
253
- };
254
- };
255
-
256
- const rewriteDocLinks = () => {
257
- return (dom) => {
258
- const updateDom = (list, attributeName) => {
259
- Array.from(list).filter((elem) => elem.hasAttribute(attributeName)).forEach((elem) => {
260
- const elemAttribute = elem.getAttribute(attributeName);
261
- if (elemAttribute) {
262
- if (elemAttribute.match(/^https?:\/\//i)) {
263
- elem.setAttribute("target", "_blank");
264
- }
265
- try {
266
- const normalizedWindowLocation = normalizeUrl(window.location.href);
267
- elem.setAttribute(attributeName, new URL(elemAttribute, normalizedWindowLocation).toString());
268
- } catch (_e) {
269
- elem.replaceWith(elem.textContent || elemAttribute);
270
- }
271
- }
272
- });
273
- };
274
- updateDom(Array.from(dom.getElementsByTagName("a")), "href");
275
- return dom;
276
- };
277
- };
278
- function normalizeUrl(input) {
279
- const url = new URL(input);
280
- if (!url.pathname.endsWith("/") && !url.pathname.endsWith(".html")) {
281
- url.pathname += "/";
282
- }
283
- return url.toString();
284
- }
285
-
286
- const addLinkClickListener = ({
287
- baseUrl,
288
- onClick
289
- }) => {
290
- return (dom) => {
291
- Array.from(dom.getElementsByTagName("a")).forEach((elem) => {
292
- elem.addEventListener("click", (e) => {
293
- const target = elem;
294
- const href = target.getAttribute("href");
295
- if (!href)
296
- return;
297
- if (href.startsWith(baseUrl) && !elem.hasAttribute("download")) {
298
- e.preventDefault();
299
- onClick(e, href);
300
- }
301
- });
302
- });
303
- return dom;
304
- };
305
- };
306
-
307
- const CopyToClipboardTooltip = withStyles((theme) => ({
308
- tooltip: {
309
- fontSize: "inherit",
310
- color: theme.palette.text.primary,
311
- margin: 0,
312
- padding: theme.spacing(0.5),
313
- backgroundColor: "transparent",
314
- boxShadow: "none"
315
- }
316
- }))(Tooltip);
317
- const CopyToClipboardIcon = () => /* @__PURE__ */ React.createElement(SvgIcon, null, /* @__PURE__ */ React.createElement("path", {
318
- d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"
319
- }));
320
- const CopyToClipboardButton = ({ text }) => {
321
- const [open, setOpen] = useState(false);
322
- const handleClick = useCallback(() => {
323
- navigator.clipboard.writeText(text);
324
- setOpen(true);
325
- }, [text]);
326
- const handleClose = useCallback(() => {
327
- setOpen(false);
328
- }, [setOpen]);
329
- return /* @__PURE__ */ React.createElement(CopyToClipboardTooltip, {
330
- title: "Copied to clipboard",
331
- placement: "left",
332
- open,
333
- onClose: handleClose,
334
- leaveDelay: 1e3
335
- }, /* @__PURE__ */ React.createElement("button", {
336
- className: "md-clipboard md-icon",
337
- onClick: handleClick
338
- }, /* @__PURE__ */ React.createElement(CopyToClipboardIcon, null)));
339
- };
340
- const copyToClipboard = (theme) => {
341
- return (dom) => {
342
- var _a;
343
- const codes = dom.querySelectorAll("pre > code");
344
- for (const code of codes) {
345
- const text = code.textContent || "";
346
- const container = document.createElement("div");
347
- (_a = code == null ? void 0 : code.parentElement) == null ? void 0 : _a.prepend(container);
348
- ReactDOM.render(/* @__PURE__ */ React.createElement(ThemeProvider, {
349
- theme
350
- }, /* @__PURE__ */ React.createElement(CopyToClipboardButton, {
351
- text
352
- })), container);
353
- }
354
- return dom;
355
- };
356
- };
357
-
358
- const removeMkdocsHeader = () => {
359
- return (dom) => {
360
- var _a;
361
- (_a = dom.querySelector(".md-header")) == null ? void 0 : _a.remove();
362
- return dom;
363
- };
364
- };
365
-
366
- const simplifyMkdocsFooter = () => {
367
- return (dom) => {
368
- var _a, _b;
369
- (_a = dom.querySelector(".md-footer .md-copyright")) == null ? void 0 : _a.remove();
370
- (_b = dom.querySelector(".md-footer-copyright")) == null ? void 0 : _b.remove();
371
- return dom;
372
- };
373
- };
374
-
375
- const onCssReady = ({
376
- docStorageUrl,
377
- onLoading,
378
- onLoaded
379
- }) => {
380
- return (dom) => {
381
- const cssPages = Array.from(dom.querySelectorAll('head > link[rel="stylesheet"]')).filter((elem) => {
382
- var _a;
383
- return (_a = elem.getAttribute("href")) == null ? void 0 : _a.startsWith(docStorageUrl);
384
- });
385
- let count = cssPages.length;
386
- if (count > 0) {
387
- onLoading(dom);
388
- }
389
- cssPages.forEach((cssPage) => cssPage.addEventListener("load", () => {
390
- count -= 1;
391
- if (count === 0) {
392
- onLoaded(dom);
393
- }
394
- }));
395
- return dom;
396
- };
397
- };
398
-
399
- const TECHDOCS_CSS = /main\.[A-Fa-f0-9]{8}\.min\.css$/;
400
- const GOOGLE_FONTS = /^https:\/\/fonts\.googleapis\.com/;
401
- const GSTATIC_FONTS = /^https:\/\/fonts\.gstatic\.com/;
402
- const safeLinksHook = (node) => {
403
- if (node.nodeName && node.nodeName === "LINK") {
404
- const href = node.getAttribute("href") || "";
405
- if (href.match(TECHDOCS_CSS)) {
406
- node.setAttribute("rel", "stylesheet");
407
- }
408
- if (href.match(GOOGLE_FONTS)) {
409
- node.setAttribute("rel", "stylesheet");
410
- }
411
- if (href.match(GSTATIC_FONTS)) {
412
- node.setAttribute("rel", "preconnect");
413
- }
414
- }
415
- return node;
416
- };
417
- const filterIframeHook = (allowedIframeHosts) => (node) => {
418
- if (node.nodeName === "IFRAME") {
419
- const src = node.getAttribute("src");
420
- if (!src) {
421
- node.remove();
422
- return node;
423
- }
424
- try {
425
- const srcUrl = new URL(src);
426
- const isMatch = allowedIframeHosts.some((host) => srcUrl.host === host);
427
- if (!isMatch) {
428
- node.remove();
429
- }
430
- } catch (error) {
431
- console.warn(`Invalid iframe src, ${error}`);
432
- node.remove();
433
- }
434
- }
435
- return node;
436
- };
437
- const sanitizeDOM = (config) => {
438
- const allowedIframeHosts = (config == null ? void 0 : config.getOptionalStringArray("allowedIframeHosts")) || [];
439
- return (dom) => {
440
- DOMPurify.addHook("afterSanitizeAttributes", safeLinksHook);
441
- const addTags = ["link"];
442
- if (allowedIframeHosts.length > 0) {
443
- DOMPurify.addHook("beforeSanitizeElements", filterIframeHook(allowedIframeHosts));
444
- addTags.push("iframe");
445
- }
446
- return DOMPurify.sanitize(dom.innerHTML, {
447
- ADD_TAGS: addTags,
448
- FORBID_TAGS: ["style"],
449
- WHOLE_DOCUMENT: true,
450
- RETURN_DOM: true
451
- });
452
- };
453
- };
454
-
455
- const injectCss = ({ css }) => {
456
- return (dom) => {
457
- dom.getElementsByTagName("head")[0].insertAdjacentHTML("beforeend", `<style>${css}</style>`);
458
- return dom;
459
- };
460
- };
461
-
462
- const scrollIntoAnchor = () => {
463
- return (dom) => {
464
- setTimeout(() => {
465
- var _a;
466
- if (window.location.hash) {
467
- const hash = window.location.hash.slice(1);
468
- (_a = dom == null ? void 0 : dom.querySelector(`[id="${hash}"]`)) == null ? void 0 : _a.scrollIntoView();
469
- }
470
- }, 200);
471
- return dom;
472
- };
473
- };
474
-
475
- const transform = async (html, transformers) => {
476
- let dom;
477
- if (typeof html === "string") {
478
- dom = new DOMParser().parseFromString(html, "text/html").documentElement;
479
- } else if (html instanceof Element) {
480
- dom = html;
481
- } else {
482
- throw new Error("dom is not a recognized type");
483
- }
484
- for (const transformer of transformers) {
485
- dom = await transformer(dom);
486
- }
487
- return dom;
488
- };
489
-
490
- const useStyles$3 = makeStyles({
491
- flexContainer: {
492
- flexWrap: "wrap"
493
- },
494
- itemText: {
495
- width: "100%",
496
- marginBottom: "1rem"
497
- }
498
- });
499
- const TechDocsSearchResultListItem = (props) => {
500
- const {
501
- result,
502
- lineClamp = 5,
503
- asListItem = true,
504
- asLink = true,
505
- title
506
- } = props;
507
- const classes = useStyles$3();
508
- const TextItem = () => {
509
- var _a;
510
- return /* @__PURE__ */ React.createElement(ListItemText, {
511
- className: classes.itemText,
512
- primaryTypographyProps: { variant: "h6" },
513
- primary: title ? title : `${result.title} | ${(_a = result.entityTitle) != null ? _a : result.name} docs`,
514
- secondary: /* @__PURE__ */ React.createElement(TextTruncate, {
515
- line: lineClamp,
516
- truncateText: "\u2026",
517
- text: result.text,
518
- element: "span"
519
- })
520
- });
521
- };
522
- const LinkWrapper = ({ children }) => asLink ? /* @__PURE__ */ React.createElement(Link, {
523
- to: result.location
524
- }, children) : /* @__PURE__ */ React.createElement(React.Fragment, null, children);
525
- const ListItemWrapper = ({ children }) => asListItem ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ListItem, {
526
- alignItems: "flex-start",
527
- className: classes.flexContainer
528
- }, children), /* @__PURE__ */ React.createElement(Divider, {
529
- component: "li"
530
- })) : /* @__PURE__ */ React.createElement(React.Fragment, null, children);
531
- return /* @__PURE__ */ React.createElement(LinkWrapper, null, /* @__PURE__ */ React.createElement(ListItemWrapper, null, /* @__PURE__ */ React.createElement(TextItem, null)));
532
- };
533
-
534
- const useStyles$2 = makeStyles({
535
- root: {
536
- width: "100%"
537
- }
538
- });
539
- const TechDocsSearchBar = (props) => {
540
- const { entityId, debounceTime = 150 } = props;
541
- const [open, setOpen] = useState(false);
542
- const navigate = useNavigate();
543
- const {
544
- term,
545
- setTerm,
546
- setFilters,
547
- result: { loading, value: searchVal }
548
- } = useSearch();
549
- const classes = useStyles$2();
550
- const [options, setOptions] = useState([]);
551
- useEffect(() => {
552
- let mounted = true;
553
- if (mounted && searchVal) {
554
- const searchResults = searchVal.results.slice(0, 10);
555
- setOptions(searchResults);
556
- }
557
- return () => {
558
- mounted = false;
559
- };
560
- }, [loading, searchVal]);
561
- const [value, setValue] = useState(term);
562
- useDebounce(() => setTerm(value), debounceTime, [value]);
563
- const { kind, name, namespace } = entityId;
564
- useEffect(() => {
565
- setFilters((prevFilters) => {
566
- return {
567
- ...prevFilters,
568
- kind,
569
- namespace,
570
- name
571
- };
572
- });
573
- }, [kind, namespace, name, setFilters]);
574
- const handleQuery = (e) => {
575
- if (!open) {
576
- setOpen(true);
577
- }
578
- setValue(e.target.value);
579
- };
580
- const handleSelection = (_, selection) => {
581
- if (selection == null ? void 0 : selection.document) {
582
- const { location } = selection.document;
583
- navigate(location);
584
- }
585
- };
586
- return /* @__PURE__ */ React.createElement(Autocomplete, {
587
- classes: { root: classes.root },
588
- "data-testid": "techdocs-search-bar",
589
- size: "small",
590
- open,
591
- getOptionLabel: () => "",
592
- filterOptions: (x) => {
593
- return x;
594
- },
595
- onClose: () => {
596
- setOpen(false);
597
- },
598
- onFocus: () => {
599
- setOpen(true);
600
- },
601
- onChange: handleSelection,
602
- blurOnSelect: true,
603
- noOptionsText: "No results found",
604
- value: null,
605
- options,
606
- renderOption: ({ document }) => /* @__PURE__ */ React.createElement(TechDocsSearchResultListItem, {
607
- result: document,
608
- lineClamp: 3,
609
- asListItem: false,
610
- asLink: false,
611
- title: document.title
612
- }),
613
- loading,
614
- renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
615
- ...params,
616
- "data-testid": "techdocs-search-bar-input",
617
- variant: "outlined",
618
- fullWidth: true,
619
- placeholder: `Search ${entityId.name} docs`,
620
- value,
621
- onChange: handleQuery,
622
- InputProps: {
623
- ...params.InputProps,
624
- startAdornment: /* @__PURE__ */ React.createElement(InputAdornment, {
625
- position: "start"
626
- }, /* @__PURE__ */ React.createElement(IconButton, {
627
- "aria-label": "Query",
628
- disabled: true
629
- }, /* @__PURE__ */ React.createElement(SearchIcon, null))),
630
- endAdornment: /* @__PURE__ */ React.createElement(React.Fragment, null, loading ? /* @__PURE__ */ React.createElement(CircularProgress, {
631
- color: "inherit",
632
- size: 20
633
- }) : null, params.InputProps.endAdornment)
634
- }
635
- })
636
- });
637
- };
638
- const TechDocsSearch = (props) => {
639
- const initialState = {
640
- term: "",
641
- types: ["techdocs"],
642
- pageCursor: "",
643
- filters: props.entityId
644
- };
645
- return /* @__PURE__ */ React.createElement(SearchContextProvider, {
646
- initialState
647
- }, /* @__PURE__ */ React.createElement(TechDocsSearchBar, {
648
- ...props
649
- }));
650
- };
651
-
652
- const useDrawerStyles = makeStyles((theme) => createStyles({
653
- paper: {
654
- width: "100%",
655
- [theme.breakpoints.up("sm")]: {
656
- width: "75%"
657
- },
658
- [theme.breakpoints.up("md")]: {
659
- width: "50%"
660
- },
661
- padding: theme.spacing(2.5)
662
- },
663
- root: {
664
- height: "100%",
665
- overflow: "hidden"
666
- },
667
- logs: {
668
- background: theme.palette.background.default
669
- }
670
- }));
671
- const TechDocsBuildLogsDrawerContent = ({
672
- buildLog,
673
- onClose
674
- }) => {
675
- const classes = useDrawerStyles();
676
- const logText = buildLog.length === 0 ? "Waiting for logs..." : buildLog.join("\n");
677
- return /* @__PURE__ */ React.createElement(Grid, {
678
- container: true,
679
- direction: "column",
680
- className: classes.root,
681
- spacing: 0,
682
- wrap: "nowrap"
683
- }, /* @__PURE__ */ React.createElement(Grid, {
684
- item: true,
685
- container: true,
686
- justifyContent: "space-between",
687
- alignItems: "center",
688
- spacing: 0,
689
- wrap: "nowrap"
690
- }, /* @__PURE__ */ React.createElement(Typography, {
691
- variant: "h5"
692
- }, "Build Details"), /* @__PURE__ */ React.createElement(IconButton, {
693
- key: "dismiss",
694
- title: "Close the drawer",
695
- onClick: onClose,
696
- color: "inherit"
697
- }, /* @__PURE__ */ React.createElement(Close, null))), /* @__PURE__ */ React.createElement(LogViewer, {
698
- text: logText,
699
- classes: { root: classes.logs }
700
- }));
701
- };
702
- const TechDocsBuildLogs = ({ buildLog }) => {
703
- const classes = useDrawerStyles();
704
- const [open, setOpen] = useState(false);
705
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Button, {
706
- color: "inherit",
707
- onClick: () => setOpen(true)
708
- }, "Show Build Logs"), /* @__PURE__ */ React.createElement(Drawer, {
709
- classes: { paper: classes.paper },
710
- anchor: "right",
711
- open,
712
- onClose: () => setOpen(false)
713
- }, /* @__PURE__ */ React.createElement(TechDocsBuildLogsDrawerContent, {
714
- buildLog,
715
- onClose: () => setOpen(false)
716
- })));
717
- };
718
-
719
- const TechDocsNotFound = ({ errorMessage }) => {
720
- const techdocsBuilder = useApi(configApiRef).getOptionalString("techdocs.builder");
721
- let additionalInfo = "";
722
- if (techdocsBuilder !== "local") {
723
- additionalInfo = "Note that techdocs.builder is not set to 'local' in your config, which means this Backstage app will not generate docs if they are not found. Make sure the project's docs are generated and published by some external process (e.g. CI/CD pipeline). Or change techdocs.builder to 'local' to generate docs from this Backstage instance.";
724
- }
725
- return /* @__PURE__ */ React.createElement(ErrorPage, {
726
- status: "404",
727
- statusMessage: errorMessage || "Documentation not found",
728
- additionalInfo
729
- });
730
- };
731
-
732
- const useStyles$1 = makeStyles((theme) => ({
733
- root: {
734
- marginBottom: theme.spacing(2)
735
- },
736
- message: {
737
- wordBreak: "break-word",
738
- overflowWrap: "anywhere"
739
- }
740
- }));
741
- const TechDocsStateIndicator = () => {
742
- let StateAlert = null;
743
- const classes = useStyles$1();
744
- const {
745
- state,
746
- contentReload,
747
- contentErrorMessage,
748
- syncErrorMessage,
749
- buildLog
750
- } = useTechDocsReader();
751
- const ReaderProgress = state === "CHECKING" ? /* @__PURE__ */ React.createElement(Progress, null) : null;
752
- if (state === "INITIAL_BUILD") {
753
- StateAlert = /* @__PURE__ */ React.createElement(Alert, {
754
- classes: { root: classes.root },
755
- variant: "outlined",
756
- severity: "info",
757
- icon: /* @__PURE__ */ React.createElement(CircularProgress, {
758
- size: "24px"
759
- }),
760
- action: /* @__PURE__ */ React.createElement(TechDocsBuildLogs, {
761
- buildLog
762
- })
763
- }, "Documentation is accessed for the first time and is being prepared. The subsequent loads are much faster.");
764
- }
765
- if (state === "CONTENT_STALE_REFRESHING") {
766
- StateAlert = /* @__PURE__ */ React.createElement(Alert, {
767
- variant: "outlined",
768
- severity: "info",
769
- icon: /* @__PURE__ */ React.createElement(CircularProgress, {
770
- size: "24px"
771
- }),
772
- action: /* @__PURE__ */ React.createElement(TechDocsBuildLogs, {
773
- buildLog
774
- }),
775
- classes: { root: classes.root }
776
- }, "A newer version of this documentation is being prepared and will be available shortly.");
777
- }
778
- if (state === "CONTENT_STALE_READY") {
779
- StateAlert = /* @__PURE__ */ React.createElement(Alert, {
780
- variant: "outlined",
781
- severity: "success",
782
- action: /* @__PURE__ */ React.createElement(Button, {
783
- color: "inherit",
784
- onClick: () => contentReload()
785
- }, "Refresh"),
786
- classes: { root: classes.root }
787
- }, "A newer version of this documentation is now available, please refresh to view.");
788
- }
789
- if (state === "CONTENT_STALE_ERROR") {
790
- StateAlert = /* @__PURE__ */ React.createElement(Alert, {
791
- variant: "outlined",
792
- severity: "error",
793
- action: /* @__PURE__ */ React.createElement(TechDocsBuildLogs, {
794
- buildLog
795
- }),
796
- classes: { root: classes.root, message: classes.message }
797
- }, "Building a newer version of this documentation failed.", " ", syncErrorMessage);
798
- }
799
- if (state === "CONTENT_NOT_FOUND") {
800
- StateAlert = /* @__PURE__ */ React.createElement(React.Fragment, null, syncErrorMessage && /* @__PURE__ */ React.createElement(Alert, {
801
- variant: "outlined",
802
- severity: "error",
803
- action: /* @__PURE__ */ React.createElement(TechDocsBuildLogs, {
804
- buildLog
805
- }),
806
- classes: { root: classes.root, message: classes.message }
807
- }, "Building a newer version of this documentation failed.", " ", syncErrorMessage), /* @__PURE__ */ React.createElement(TechDocsNotFound, {
808
- errorMessage: contentErrorMessage
809
- }));
810
- }
811
- return /* @__PURE__ */ React.createElement(React.Fragment, null, ReaderProgress, StateAlert);
812
- };
813
-
814
- function calculateDisplayState({
815
- contentLoading,
816
- content,
817
- activeSyncState
818
- }) {
819
- if (contentLoading) {
820
- return "CHECKING";
821
- }
822
- if (activeSyncState === "BUILD_READY_RELOAD") {
823
- return "CHECKING";
824
- }
825
- if (!content && activeSyncState === "CHECKING") {
826
- return "CHECKING";
827
- }
828
- if (!content && activeSyncState === "BUILDING") {
829
- return "INITIAL_BUILD";
830
- }
831
- if (!content) {
832
- return "CONTENT_NOT_FOUND";
833
- }
834
- if (activeSyncState === "BUILDING") {
835
- return "CONTENT_STALE_REFRESHING";
836
- }
837
- if (activeSyncState === "BUILD_READY") {
838
- return "CONTENT_STALE_READY";
839
- }
840
- if (activeSyncState === "ERROR") {
841
- return "CONTENT_STALE_ERROR";
842
- }
843
- return "CONTENT_FRESH";
844
- }
845
- function reducer(oldState, action) {
846
- const newState = { ...oldState };
847
- switch (action.type) {
848
- case "sync":
849
- if (action.state === "CHECKING") {
850
- newState.buildLog = [];
851
- }
852
- newState.activeSyncState = action.state;
853
- newState.syncError = action.syncError;
854
- break;
855
- case "contentLoading":
856
- newState.contentLoading = true;
857
- newState.contentError = void 0;
858
- break;
859
- case "content":
860
- if (typeof action.path === "string") {
861
- newState.path = action.path;
862
- }
863
- newState.contentLoading = false;
864
- newState.content = action.content;
865
- newState.contentError = action.contentError;
866
- break;
867
- case "buildLog":
868
- newState.buildLog = newState.buildLog.concat(action.log);
869
- break;
870
- default:
871
- throw new Error();
872
- }
873
- if (["BUILD_READY", "BUILD_READY_RELOAD"].includes(newState.activeSyncState) && ["contentLoading", "content"].includes(action.type)) {
874
- newState.activeSyncState = "UP_TO_DATE";
875
- newState.buildLog = [];
876
- }
877
- return newState;
878
- }
879
- function useReaderState(kind, namespace, name, path) {
880
- var _a, _b;
881
- const [state, dispatch] = useReducer(reducer, {
882
- activeSyncState: "CHECKING",
883
- path,
884
- contentLoading: true,
885
- buildLog: []
886
- });
887
- const techdocsStorageApi = useApi(techdocsStorageApiRef);
888
- const { retry: contentReload } = useAsyncRetry(async () => {
889
- dispatch({ type: "contentLoading" });
890
- try {
891
- const entityDocs = await techdocsStorageApi.getEntityDocs({ kind, namespace, name }, path);
892
- dispatch({ type: "content", content: entityDocs, path });
893
- return entityDocs;
894
- } catch (e) {
895
- dispatch({ type: "content", contentError: e, path });
896
- }
897
- return void 0;
898
- }, [techdocsStorageApi, kind, namespace, name, path]);
899
- const contentRef = useRef({
900
- content: void 0,
901
- reload: () => {
902
- }
903
- });
904
- contentRef.current = { content: state.content, reload: contentReload };
905
- useAsync(async () => {
906
- dispatch({ type: "sync", state: "CHECKING" });
907
- const buildingTimeout = setTimeout(() => {
908
- dispatch({ type: "sync", state: "BUILDING" });
909
- }, 1e3);
910
- try {
911
- const result = await techdocsStorageApi.syncEntityDocs({
912
- kind,
913
- namespace,
914
- name
915
- }, (log) => {
916
- dispatch({ type: "buildLog", log });
917
- });
918
- switch (result) {
919
- case "updated":
920
- if (!contentRef.current.content) {
921
- contentRef.current.reload();
922
- dispatch({ type: "sync", state: "BUILD_READY_RELOAD" });
923
- } else {
924
- dispatch({ type: "sync", state: "BUILD_READY" });
925
- }
926
- break;
927
- case "cached":
928
- dispatch({ type: "sync", state: "UP_TO_DATE" });
929
- break;
930
- default:
931
- dispatch({
932
- type: "sync",
933
- state: "ERROR",
934
- syncError: new Error("Unexpected return state")
935
- });
936
- break;
937
- }
938
- } catch (e) {
939
- dispatch({ type: "sync", state: "ERROR", syncError: e });
940
- } finally {
941
- clearTimeout(buildingTimeout);
942
- }
943
- }, [kind, name, namespace, techdocsStorageApi, dispatch, contentRef]);
944
- const displayState = useMemo(() => calculateDisplayState({
945
- activeSyncState: state.activeSyncState,
946
- contentLoading: state.contentLoading,
947
- content: state.content
948
- }), [state.activeSyncState, state.content, state.contentLoading]);
949
- return {
950
- state: displayState,
951
- contentReload,
952
- path: state.path,
953
- content: state.content,
954
- contentErrorMessage: (_a = state.contentError) == null ? void 0 : _a.toString(),
955
- syncErrorMessage: (_b = state.syncError) == null ? void 0 : _b.toString(),
956
- buildLog: state.buildLog
957
- };
958
- }
959
-
960
- const useStyles = makeStyles((theme) => ({
961
- searchBar: {
962
- maxWidth: "calc(100% - 16rem * 2 - 2.4rem)",
963
- marginTop: 0,
964
- marginBottom: theme.spacing(1),
965
- marginLeft: "calc(16rem + 1.2rem)",
966
- "@media screen and (max-width: 76.1875em)": {
967
- marginLeft: "0",
968
- maxWidth: "100%"
969
- }
970
- }
971
- }));
972
- const TechDocsReaderContext = createContext({});
973
- const TechDocsReaderProvider = ({
974
- children,
975
- entityRef
976
- }) => {
977
- const { "*": path } = useParams();
978
- const { kind, namespace, name } = entityRef;
979
- const value = useReaderState(kind, namespace, name, path);
980
- return /* @__PURE__ */ React.createElement(TechDocsReaderContext.Provider, {
981
- value
982
- }, children);
983
- };
984
- const withTechDocsReaderProvider = (Component, entityRef) => (props) => /* @__PURE__ */ React.createElement(TechDocsReaderProvider, {
985
- entityRef
986
- }, /* @__PURE__ */ React.createElement(Component, {
987
- ...props
988
- }));
989
- const useTechDocsReader = () => useContext(TechDocsReaderContext);
990
- const headings = ["h1", "h2", "h3", "h4", "h5", "h6"];
991
- const useTechDocsReaderDom = (entityRef) => {
992
- const navigate = useNavigate$1();
993
- const theme = useTheme();
994
- const techdocsStorageApi = useApi(techdocsStorageApiRef);
995
- const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
996
- const techdocsSanitizer = useApi(configApiRef);
997
- const { namespace = "", kind = "", name = "" } = entityRef;
998
- const { state, path, content: rawPage } = useTechDocsReader();
999
- const isDarkTheme = theme.palette.type === "dark";
1000
- const [sidebars, setSidebars] = useState();
1001
- const [dom, setDom] = useState(null);
1002
- const { isPinned } = useContext(SidebarPinStateContext);
1003
- const updateSidebarPosition = useCallback(() => {
1004
- if (!dom || !sidebars)
1005
- return;
1006
- const mdTabs = dom.querySelector(".md-container > .md-tabs");
1007
- const sidebarsCollapsed = window.matchMedia("screen and (max-width: 76.1875em)").matches;
1008
- const newTop = Math.max(dom.getBoundingClientRect().top, 0);
1009
- sidebars.forEach((sidebar) => {
1010
- if (sidebarsCollapsed) {
1011
- sidebar.style.top = "0px";
1012
- } else if (mdTabs) {
1013
- sidebar.style.top = `${newTop + mdTabs.getBoundingClientRect().height}px`;
1014
- } else {
1015
- sidebar.style.top = `${newTop}px`;
1016
- }
1017
- });
1018
- }, [dom, sidebars]);
1019
- useEffect(() => {
1020
- updateSidebarPosition();
1021
- window.addEventListener("scroll", updateSidebarPosition, true);
1022
- window.addEventListener("resize", updateSidebarPosition);
1023
- return () => {
1024
- window.removeEventListener("scroll", updateSidebarPosition, true);
1025
- window.removeEventListener("resize", updateSidebarPosition);
1026
- };
1027
- }, [updateSidebarPosition, state]);
1028
- const updateFooterWidth = useCallback(() => {
1029
- if (!dom)
1030
- return;
1031
- const footer = dom.querySelector(".md-footer");
1032
- if (footer) {
1033
- footer.style.width = `${dom.getBoundingClientRect().width}px`;
1034
- }
1035
- }, [dom]);
1036
- useEffect(() => {
1037
- updateFooterWidth();
1038
- window.addEventListener("resize", updateFooterWidth);
1039
- return () => {
1040
- window.removeEventListener("resize", updateFooterWidth);
1041
- };
1042
- });
1043
- const preRender = useCallback((rawContent, contentPath) => transform(rawContent, [
1044
- sanitizeDOM(techdocsSanitizer.getOptionalConfig("techdocs.sanitizer")),
1045
- addBaseUrl({
1046
- techdocsStorageApi,
1047
- entityId: {
1048
- kind,
1049
- name,
1050
- namespace
1051
- },
1052
- path: contentPath
1053
- }),
1054
- rewriteDocLinks(),
1055
- addSidebarToggle(),
1056
- removeMkdocsHeader(),
1057
- simplifyMkdocsFooter(),
1058
- addGitFeedbackLink(scmIntegrationsApi),
1059
- injectCss({
1060
- css: `
1061
- /*
1062
- As the MkDocs output is rendered in shadow DOM, the CSS variable definitions on the root selector are not applied. Instead, they have to be applied on :host.
1063
- As there is no way to transform the served main*.css yet (for example in the backend), we have to copy from main*.css and modify them.
1064
- */
1065
- :host {
1066
- /* FONT */
1067
- --md-default-fg-color: ${theme.palette.text.primary};
1068
- --md-default-fg-color--light: ${theme.palette.text.secondary};
1069
- --md-default-fg-color--lighter: ${lighten(theme.palette.text.secondary, 0.7)};
1070
- --md-default-fg-color--lightest: ${lighten(theme.palette.text.secondary, 0.3)};
1071
-
1072
- /* BACKGROUND */
1073
- --md-default-bg-color:${theme.palette.background.default};
1074
- --md-default-bg-color--light: ${theme.palette.background.paper};
1075
- --md-default-bg-color--lighter: ${lighten(theme.palette.background.paper, 0.7)};
1076
- --md-default-bg-color--lightest: ${lighten(theme.palette.background.paper, 0.3)};
1077
-
1078
- /* PRIMARY */
1079
- --md-primary-fg-color: ${theme.palette.primary.main};
1080
- --md-primary-fg-color--light: ${theme.palette.primary.light};
1081
- --md-primary-fg-color--dark: ${theme.palette.primary.dark};
1082
- --md-primary-bg-color: ${theme.palette.primary.contrastText};
1083
- --md-primary-bg-color--light: ${lighten(theme.palette.primary.contrastText, 0.7)};
1084
-
1085
- /* ACCENT */
1086
- --md-accent-fg-color: var(--md-primary-fg-color);
1087
-
1088
- /* SHADOW */
1089
- --md-shadow-z1: ${theme.shadows[1]};
1090
- --md-shadow-z2: ${theme.shadows[2]};
1091
- --md-shadow-z3: ${theme.shadows[3]};
1092
-
1093
- /* EXTENSIONS */
1094
- --md-admonition-fg-color: var(--md-default-fg-color);
1095
- --md-admonition-bg-color: var(--md-default-bg-color);
1096
- /* Admonitions and others are using SVG masks to define icons. These masks are defined as CSS variables. */
1097
- --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z"/></svg>');
1098
- --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 5h16v2H4V5m0 4h16v2H4V9m0 4h16v2H4v-2m0 4h10v2H4v-2z"/></svg>');
1099
- --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10A10 10 0 0012 2z"/></svg>');
1100
- --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17.55 11.2c-.23-.3-.5-.56-.76-.82-.65-.6-1.4-1.03-2.03-1.66C13.3 7.26 13 4.85 13.91 3c-.91.23-1.75.75-2.45 1.32-2.54 2.08-3.54 5.75-2.34 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.22.1-.46.04-.64-.12a.83.83 0 01-.15-.17c-1.1-1.43-1.28-3.48-.53-5.12C5.89 10 5 12.3 5.14 14.47c.04.5.1 1 .27 1.5.14.6.4 1.2.72 1.73 1.04 1.73 2.87 2.97 4.84 3.22 2.1.27 4.35-.12 5.96-1.6 1.8-1.66 2.45-4.32 1.5-6.6l-.13-.26c-.2-.46-.47-.87-.8-1.25l.05-.01m-3.1 6.3c-.28.24-.73.5-1.08.6-1.1.4-2.2-.16-2.87-.82 1.19-.28 1.89-1.16 2.09-2.05.17-.8-.14-1.46-.27-2.23-.12-.74-.1-1.37.18-2.06.17.38.37.76.6 1.06.76 1 1.95 1.44 2.2 2.8.04.14.06.28.06.43.03.82-.32 1.72-.92 2.27h.01z"/></svg>');
1101
- --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2m-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
1102
- --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.07 11.25l-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 00-2-2 2 2 0 00-2 2H8a4 4 0 014-4 4 4 0 014 4 3.2 3.2 0 01-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 002 12a10 10 0 0010 10 10 10 0 0010-10c0-5.53-4.5-10-10-10z"/></svg>');
1103
- --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 14h-2v-4h2m0 8h-2v-2h2M1 21h22L12 2 1 21z"/></svg>');
1104
- --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2c5.53 0 10 4.47 10 10s-4.47 10-10 10S2 17.53 2 12 6.47 2 12 2m3.59 5L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41 15.59 7z"/></svg>');
1105
- --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11.5 20l4.86-9.73H13V4l-5 9.73h3.5V20M12 2c2.75 0 5.1 1 7.05 2.95C21 6.9 22 9.25 22 12s-1 5.1-2.95 7.05C17.1 21 14.75 22 12 22s-5.1-1-7.05-2.95C3 17.1 2 14.75 2 12s1-5.1 2.95-7.05C6.9 3 9.25 2 12 2z"/></svg>');
1106
- --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 00-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 00-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z"/></svg>');
1107
- --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 01.75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z"/></svg>');
1108
- --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z"/></svg>');
1109
- --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.42L5.83 13H21V7h-2z"/></svg>');
1110
- --md-details-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42z"/></svg>');
1111
- --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></svg>');
1112
- --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
1113
- --md-nav-icon--prev: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>');
1114
- --md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42z"/></svg>');
1115
- --md-toc-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 9h14V7H3v2m0 4h14v-2H3v2m0 4h14v-2H3v2m16 0h2v-2h-2v2m0-10v2h2V7h-2m0 6h2v-2h-2v2z"/></svg>');
1116
- --md-clipboard-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 21H8V7h11m0-2H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m-3-4H4a2 2 0 0 0-2 2v14h2V3h12V1z"/></svg>');
1117
- --md-search-result-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h7c-.41-.25-.8-.56-1.14-.9-.33-.33-.61-.7-.86-1.1H6V4h7v5h5v1.18c.71.16 1.39.43 2 .82V8l-6-6m6.31 16.9c1.33-2.11.69-4.9-1.4-6.22-2.11-1.33-4.91-.68-6.22 1.4-1.34 2.11-.69 4.89 1.4 6.22 1.46.93 3.32.93 4.79.02L22 23.39 23.39 22l-3.08-3.1m-3.81.1a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5z"/></svg>');
1118
- --md-source-forks-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm0 2.122a2.25 2.25 0 1 0-1.5 0v.878A2.25 2.25 0 0 0 5.75 8.5h1.5v2.128a2.251 2.251 0 1 0 1.5 0V8.5h1.5a2.25 2.25 0 0 0 2.25-2.25v-.878a2.25 2.25 0 1 0-1.5 0v.878a.75.75 0 0 1-.75.75h-4.5A.75.75 0 0 1 5 6.25v-.878zm3.75 7.378a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm3-8.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z"/></svg>');
1119
- --md-source-repositories-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 1 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 0 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 0 1 1-1h8zM5 12.25v3.25a.25.25 0 0 0 .4.2l1.45-1.087a.25.25 0 0 1 .3 0L8.6 15.7a.25.25 0 0 0 .4-.2v-3.25a.25.25 0 0 0-.25-.25h-3.5a.25.25 0 0 0-.25.25z"/></svg>');
1120
- --md-source-stars-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.75.75 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694v.001z"/></svg>');
1121
- --md-source-version-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M2.5 7.775V2.75a.25.25 0 0 1 .25-.25h5.025a.25.25 0 0 1 .177.073l6.25 6.25a.25.25 0 0 1 0 .354l-5.025 5.025a.25.25 0 0 1-.354 0l-6.25-6.25a.25.25 0 0 1-.073-.177zm-1.5 0V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.75 1.75 0 0 1 1 7.775zM6 5a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></svg>');
1122
- --md-version-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc.--><path d="m310.6 246.6-127.1 128c-7.1 6.3-15.3 9.4-23.5 9.4s-16.38-3.125-22.63-9.375l-127.1-128C.224 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75s3.12 25.75-6.08 34.85z"/></svg>');
1123
- }
1124
-
1125
- :host > * {
1126
- /* CODE */
1127
- --md-code-fg-color: ${theme.palette.text.primary};
1128
- --md-code-bg-color: ${theme.palette.background.paper};
1129
- --md-code-hl-color: ${alpha(theme.palette.warning.main, 0.5)};
1130
- --md-code-hl-keyword-color: ${isDarkTheme ? theme.palette.primary.light : theme.palette.primary.dark};
1131
- --md-code-hl-function-color: ${isDarkTheme ? theme.palette.secondary.light : theme.palette.secondary.dark};
1132
- --md-code-hl-string-color: ${isDarkTheme ? theme.palette.success.light : theme.palette.success.dark};
1133
- --md-code-hl-number-color: ${isDarkTheme ? theme.palette.error.light : theme.palette.error.dark};
1134
- --md-code-hl-constant-color: var(--md-code-hl-function-color);
1135
- --md-code-hl-special-color: var(--md-code-hl-function-color);
1136
- --md-code-hl-name-color: var(--md-code-fg-color);
1137
- --md-code-hl-comment-color: var(--md-default-fg-color--light);
1138
- --md-code-hl-generic-color: var(--md-default-fg-color--light);
1139
- --md-code-hl-variable-color: var(--md-default-fg-color--light);
1140
- --md-code-hl-operator-color: var(--md-default-fg-color--light);
1141
- --md-code-hl-punctuation-color: var(--md-default-fg-color--light);
1142
-
1143
- /* TYPESET */
1144
- --md-typeset-font-size: 1rem;
1145
- --md-typeset-color: var(--md-default-fg-color);
1146
- --md-typeset-a-color: var(--md-accent-fg-color);
1147
- --md-typeset-table-color: ${theme.palette.text.primary};
1148
- --md-typeset-del-color: ${isDarkTheme ? alpha(theme.palette.error.dark, 0.5) : alpha(theme.palette.error.light, 0.5)};
1149
- --md-typeset-ins-color: ${isDarkTheme ? alpha(theme.palette.success.dark, 0.5) : alpha(theme.palette.success.light, 0.5)};
1150
- --md-typeset-mark-color: ${isDarkTheme ? alpha(theme.palette.warning.dark, 0.5) : alpha(theme.palette.warning.light, 0.5)};
1151
- }
1152
-
1153
- @media screen and (max-width: 76.1875em) {
1154
- :host > * {
1155
- /* TYPESET */
1156
- --md-typeset-font-size: .9rem;
1157
- }
1158
- }
1159
-
1160
- @media screen and (max-width: 600px) {
1161
- :host > * {
1162
- /* TYPESET */
1163
- --md-typeset-font-size: .7rem;
1164
- }
1165
- }
1166
- `
1167
- }),
1168
- injectCss({
1169
- css: `
1170
- body {
1171
- --md-text-color: var(--md-default-fg-color);
1172
- --md-text-link-color: var(--md-accent-fg-color);
1173
- --md-text-font-family: ${theme.typography.fontFamily};
1174
- font-family: var(--md-text-font-family);
1175
- background-color: unset;
1176
- }
1177
- `
1178
- }),
1179
- injectCss({
1180
- css: `
1181
- .md-grid {
1182
- max-width: 100%;
1183
- margin: 0;
1184
- }
1185
-
1186
- .md-nav {
1187
- font-size: calc(var(--md-typeset-font-size) * 0.9);
1188
- }
1189
- .md-nav__link {
1190
- display: flex;
1191
- align-items: center;
1192
- justify-content: space-between;
1193
- }
1194
- .md-nav__icon {
1195
- height: 20px !important;
1196
- width: 20px !important;
1197
- margin-left:${theme.spacing(1)}px;
1198
- }
1199
- .md-nav__icon svg {
1200
- margin: 0;
1201
- width: 20px !important;
1202
- height: 20px !important;
1203
- }
1204
- .md-nav__icon:after {
1205
- width: 20px !important;
1206
- height: 20px !important;
1207
- }
1208
-
1209
- .md-main__inner {
1210
- margin-top: 0;
1211
- }
1212
-
1213
- .md-sidebar {
1214
- bottom: 75px;
1215
- position: fixed;
1216
- width: 16rem;
1217
- overflow-y: auto;
1218
- overflow-x: hidden;
1219
- scrollbar-color: rgb(193, 193, 193) #eee;
1220
- scrollbar-width: thin;
1221
- }
1222
- .md-sidebar::-webkit-scrollbar {
1223
- width: 5px;
1224
- }
1225
- .md-sidebar::-webkit-scrollbar-button {
1226
- width: 5px;
1227
- height: 5px;
1228
- }
1229
- .md-sidebar::-webkit-scrollbar-track {
1230
- background: #eee;
1231
- border: 1 px solid rgb(250, 250, 250);
1232
- box-shadow: 0px 0px 3px #dfdfdf inset;
1233
- border-radius: 3px;
1234
- }
1235
- .md-sidebar::-webkit-scrollbar-thumb {
1236
- width: 5px;
1237
- background: rgb(193, 193, 193);
1238
- border: transparent;
1239
- border-radius: 3px;
1240
- }
1241
- .md-sidebar::-webkit-scrollbar-thumb:hover {
1242
- background: rgb(125, 125, 125);
1243
- }
1244
- .md-sidebar--secondary {
1245
- right: ${theme.spacing(3)}px;
1246
- }
1247
- .md-sidebar__scrollwrap {
1248
- overflow: unset !important;
1249
- }
1250
-
1251
- .md-content {
1252
- max-width: calc(100% - 16rem * 2);
1253
- margin-left: 16rem;
1254
- margin-bottom: 50px;
1255
- }
1256
-
1257
- .md-footer {
1258
- position: fixed;
1259
- bottom: 0px;
1260
- }
1261
- .md-footer__title {
1262
- background-color: unset;
1263
- }
1264
- .md-footer__link, .md-footer-nav__link {
1265
- width: 16rem;
1266
- }
1267
-
1268
- .md-dialog {
1269
- background-color: unset;
1270
- }
1271
-
1272
- @media screen and (min-width: 76.25em) {
1273
- .md-sidebar {
1274
- height: auto;
1275
- }
1276
- }
1277
-
1278
- @media screen and (max-width: 76.1875em) {
1279
- .md-nav {
1280
- transition: none !important;
1281
- background-color: var(--md-default-bg-color)
1282
- }
1283
- .md-nav--primary .md-nav__title {
1284
- cursor: auto;
1285
- color: var(--md-default-fg-color);
1286
- font-weight: 700;
1287
- white-space: normal;
1288
- line-height: 1rem;
1289
- height: auto;
1290
- display: flex;
1291
- flex-flow: column;
1292
- row-gap: 1.6rem;
1293
- padding: 1.2rem .8rem .8rem;
1294
- background-color: var(--md-default-bg-color);
1295
- }
1296
- .md-nav--primary .md-nav__title~.md-nav__list {
1297
- box-shadow: none;
1298
- }
1299
- .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child {
1300
- border-top: none;
1301
- }
1302
- .md-nav--primary .md-nav__title .md-nav__button {
1303
- display: none;
1304
- }
1305
- .md-nav--primary .md-nav__title .md-nav__icon {
1306
- color: var(--md-default-fg-color);
1307
- position: static;
1308
- height: auto;
1309
- margin: 0 0 0 -0.2rem;
1310
- }
1311
- .md-nav--primary > .md-nav__title [for="none"] {
1312
- padding-top: 0;
1313
- }
1314
- .md-nav--primary .md-nav__item {
1315
- border-top: none;
1316
- }
1317
- .md-nav--primary :is(.md-nav__title,.md-nav__item) {
1318
- font-size : var(--md-typeset-font-size);
1319
- }
1320
- .md-nav .md-source {
1321
- display: none;
1322
- }
1323
-
1324
- .md-sidebar {
1325
- height: 100%;
1326
- }
1327
- .md-sidebar--primary {
1328
- width: 16rem !important;
1329
- z-index: 200;
1330
- left: ${isPinned ? `calc(-16rem + ${sidebarConfig.drawerWidthOpen}px)` : `calc(-16rem + ${sidebarConfig.drawerWidthClosed}px)`} !important;
1331
- }
1332
- .md-sidebar--secondary:not([hidden]) {
1333
- display: none;
1334
- }
1335
- [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary {
1336
- transform: translateX(16rem);
1337
- }
1338
-
1339
- .md-content {
1340
- max-width: 100%;
1341
- margin-left: 0;
1342
- }
1343
- .md-content__inner {
1344
- margin: 0;
1345
- }
1346
- .md-content__inner .highlighttable {
1347
- max-width: 100%;
1348
- margin: 1em 0;
1349
- }
1350
-
1351
- .md-header__button {
1352
- margin: 0.4rem 0;
1353
- margin-left: 0.4rem;
1354
- padding: 0;
1355
- }
1356
-
1357
- .md-overlay {
1358
- left: 0;
1359
- }
1360
-
1361
- .md-footer {
1362
- position: static;
1363
- padding-left: 0;
1364
- }
1365
- .md-footer__link, .md-footer-nav__link {
1366
- /* footer links begin to overlap at small sizes without setting width */
1367
- width: 50%;
1368
- }
1369
- }
1370
-
1371
- @media screen and (max-width: 600px) {
1372
- .md-sidebar--primary {
1373
- left: -16rem !important;
1374
- width: 16rem;
1375
- }
1376
- .md-sidebar--primary .md-sidebar__scrollwrap {
1377
- bottom: ${sidebarConfig.mobileSidebarHeight}px;
1378
- }
1379
- }
1380
- `
1381
- }),
1382
- injectCss({
1383
- css: `
1384
- .md-typeset {
1385
- font-size: var(--md-typeset-font-size);
1386
- }
1387
-
1388
- ${headings.reduce((style, heading) => {
1389
- const styles = theme.typography[heading];
1390
- const { lineHeight, fontFamily, fontWeight, fontSize } = styles;
1391
- const calculate = (value) => {
1392
- let factor = 1;
1393
- if (typeof value === "number") {
1394
- factor = value / 16 * 0.6;
1395
- }
1396
- if (typeof value === "string") {
1397
- factor = value.replace("rem", "");
1398
- }
1399
- return `calc(${factor} * var(--md-typeset-font-size))`;
1400
- };
1401
- return style.concat(`
1402
- .md-typeset ${heading} {
1403
- color: var(--md-default-fg-color);
1404
- line-height: ${lineHeight};
1405
- font-family: ${fontFamily};
1406
- font-weight: ${fontWeight};
1407
- font-size: ${calculate(fontSize)};
1408
- }
1409
- `);
1410
- }, "")}
1411
-
1412
- .md-typeset .md-content__button {
1413
- color: var(--md-default-fg-color);
1414
- }
1415
-
1416
- .md-typeset hr {
1417
- border-bottom: 0.05rem dotted ${theme.palette.divider};
1418
- }
1419
-
1420
- .md-typeset details {
1421
- font-size: var(--md-typeset-font-size) !important;
1422
- }
1423
- .md-typeset details summary {
1424
- padding-left: 2.5rem !important;
1425
- }
1426
- .md-typeset details summary:before,
1427
- .md-typeset details summary:after {
1428
- top: 50% !important;
1429
- width: 20px !important;
1430
- height: 20px !important;
1431
- transform: rotate(0deg) translateY(-50%) !important;
1432
- }
1433
- .md-typeset details[open] > summary:after {
1434
- transform: rotate(90deg) translateX(-50%) !important;
1435
- }
1436
-
1437
- .md-typeset blockquote {
1438
- color: var(--md-default-fg-color--light);
1439
- border-left: 0.2rem solid var(--md-default-fg-color--light);
1440
- }
1441
-
1442
- .md-typeset table:not([class]) {
1443
- font-size: var(--md-typeset-font-size);
1444
- border: 1px solid var(--md-default-fg-color);
1445
- border-bottom: none;
1446
- border-collapse: collapse;
1447
- }
1448
- .md-typeset table:not([class]) th {
1449
- font-weight: bold;
1450
- }
1451
- .md-typeset table:not([class]) td, .md-typeset table:not([class]) th {
1452
- border-bottom: 1px solid var(--md-default-fg-color);
1453
- }
1454
-
1455
- .md-typeset pre > code::-webkit-scrollbar-thumb {
1456
- background-color: hsla(0, 0%, 0%, 0.32);
1457
- }
1458
- .md-typeset pre > code::-webkit-scrollbar-thumb:hover {
1459
- background-color: hsla(0, 0%, 0%, 0.87);
1460
- }
1461
- `
1462
- }),
1463
- injectCss({
1464
- css: `
1465
- /*
1466
- Disable CSS animations on link colors as they lead to issues in dark mode.
1467
- The dark mode color theme is applied later and theirfore there is always an animation from light to dark mode when navigation between pages.
1468
- */
1469
- .md-dialog, .md-nav__link, .md-footer__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink {
1470
- transition: none;
1471
- }
1472
- `
1473
- }),
1474
- injectCss({
1475
- css: `
1476
- /* HIGHLIGHT */
1477
- .highlight .md-clipboard:after {
1478
- content: unset;
1479
- }
1480
-
1481
- .highlight .nx {
1482
- color: ${isDarkTheme ? "#ff53a3" : "#ec407a"};
1483
- }
1484
-
1485
- /* CODE HILITE */
1486
- .codehilite .gd {
1487
- background-color: ${isDarkTheme ? "rgba(248,81,73,0.65)" : "#fdd"};
1488
- }
1489
-
1490
- .codehilite .gi {
1491
- background-color: ${isDarkTheme ? "rgba(46,160,67,0.65)" : "#dfd"};
1492
- }
1493
-
1494
- /* TABBED */
1495
- .tabbed-set>input:nth-child(1):checked~.tabbed-labels>:nth-child(1),
1496
- .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2),
1497
- .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3),
1498
- .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4),
1499
- .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5),
1500
- .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6),
1501
- .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7),
1502
- .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8),
1503
- .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9),
1504
- .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10),
1505
- .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11),
1506
- .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12),
1507
- .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13),
1508
- .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14),
1509
- .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15),
1510
- .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16),
1511
- .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17),
1512
- .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18),
1513
- .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19),
1514
- .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20) {
1515
- color: var(--md-accent-fg-color);
1516
- border-color: var(--md-accent-fg-color);
1517
- }
1518
-
1519
- /* TASK-LIST */
1520
- .task-list-control .task-list-indicator::before {
1521
- background-color: ${theme.palette.action.disabledBackground};
1522
- }
1523
- .task-list-control [type="checkbox"]:checked + .task-list-indicator:before {
1524
- background-color: ${theme.palette.success.main};
1525
- }
1526
-
1527
- /* ADMONITION */
1528
- .admonition {
1529
- font-size: var(--md-typeset-font-size) !important;
1530
- }
1531
- .admonition .admonition-title {
1532
- padding-left: 2.5rem !important;
1533
- }
1534
-
1535
- .admonition .admonition-title:before {
1536
- top: 50% !important;
1537
- width: 20px !important;
1538
- height: 20px !important;
1539
- transform: translateY(-50%) !important;
1540
- }
1541
- `
1542
- })
1543
- ]), [
1544
- kind,
1545
- name,
1546
- namespace,
1547
- scmIntegrationsApi,
1548
- techdocsSanitizer,
1549
- techdocsStorageApi,
1550
- theme,
1551
- isDarkTheme,
1552
- isPinned
1553
- ]);
1554
- const postRender = useCallback(async (transformedElement) => transform(transformedElement, [
1555
- scrollIntoAnchor(),
1556
- copyToClipboard(theme),
1557
- addLinkClickListener({
1558
- baseUrl: window.location.origin,
1559
- onClick: (event, url) => {
1560
- var _a, _b;
1561
- const modifierActive = event.ctrlKey || event.metaKey;
1562
- const parsedUrl = new URL(url);
1563
- if (parsedUrl.hash) {
1564
- if (modifierActive) {
1565
- window.open(`${parsedUrl.pathname}${parsedUrl.hash}`, "_blank");
1566
- } else {
1567
- navigate(`${parsedUrl.pathname}${parsedUrl.hash}`);
1568
- (_a = transformedElement == null ? void 0 : transformedElement.querySelector(`#${parsedUrl.hash.slice(1)}`)) == null ? void 0 : _a.scrollIntoView();
1569
- }
1570
- } else {
1571
- if (modifierActive) {
1572
- window.open(parsedUrl.pathname, "_blank");
1573
- } else {
1574
- navigate(parsedUrl.pathname);
1575
- (_b = transformedElement == null ? void 0 : transformedElement.querySelector(".md-content__inner")) == null ? void 0 : _b.scrollIntoView();
1576
- }
1577
- }
1578
- }
1579
- }),
1580
- onCssReady({
1581
- docStorageUrl: await techdocsStorageApi.getApiOrigin(),
1582
- onLoading: (renderedElement) => {
1583
- renderedElement.style.setProperty("opacity", "0");
1584
- },
1585
- onLoaded: (renderedElement) => {
1586
- var _a;
1587
- renderedElement.style.removeProperty("opacity");
1588
- (_a = renderedElement.querySelector(".md-nav__title")) == null ? void 0 : _a.removeAttribute("for");
1589
- setSidebars(Array.from(renderedElement.querySelectorAll(".md-sidebar")));
1590
- }
1591
- })
1592
- ]), [theme, navigate, techdocsStorageApi]);
1593
- useEffect(() => {
1594
- if (!rawPage)
1595
- return () => {
1596
- };
1597
- let shouldReplaceContent = true;
1598
- preRender(rawPage, path).then(async (preTransformedDomElement) => {
1599
- if (!(preTransformedDomElement == null ? void 0 : preTransformedDomElement.innerHTML)) {
1600
- return;
1601
- }
1602
- if (!shouldReplaceContent) {
1603
- return;
1604
- }
1605
- window.scroll({ top: 0 });
1606
- const postTransformedDomElement = await postRender(preTransformedDomElement);
1607
- setDom(postTransformedDomElement);
1608
- });
1609
- return () => {
1610
- shouldReplaceContent = false;
1611
- };
1612
- }, [rawPage, path, preRender, postRender]);
1613
- return dom;
1614
- };
1615
- const TheReader = ({
1616
- entityRef,
1617
- onReady = () => {
1618
- },
1619
- withSearch = true
1620
- }) => {
1621
- var _a, _b;
1622
- const classes = useStyles();
1623
- const dom = useTechDocsReaderDom(entityRef);
1624
- const shadowDomRef = useRef(null);
1625
- const onReadyRef = useRef(onReady);
1626
- useEffect(() => {
1627
- onReadyRef.current = onReady;
1628
- }, [onReady]);
1629
- useEffect(() => {
1630
- if (!dom || !shadowDomRef.current)
1631
- return;
1632
- const shadowDiv = shadowDomRef.current;
1633
- const shadowRoot = shadowDiv.shadowRoot || shadowDiv.attachShadow({ mode: "open" });
1634
- Array.from(shadowRoot.children).forEach((child) => shadowRoot.removeChild(child));
1635
- shadowRoot.appendChild(dom);
1636
- onReadyRef.current();
1637
- }, [dom]);
1638
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TechDocsStateIndicator, null), withSearch && ((_b = (_a = shadowDomRef == null ? void 0 : shadowDomRef.current) == null ? void 0 : _a.shadowRoot) == null ? void 0 : _b.innerHTML) && /* @__PURE__ */ React.createElement(Grid, {
1639
- container: true,
1640
- className: classes.searchBar
1641
- }, /* @__PURE__ */ React.createElement(TechDocsSearch, {
1642
- entityId: entityRef
1643
- })), /* @__PURE__ */ React.createElement("div", {
1644
- "data-testid": "techdocs-content-shadowroot",
1645
- ref: shadowDomRef
1646
- }));
1647
- };
1648
- const Reader = (props) => {
1649
- const { entityRef, onReady = () => {
1650
- }, withSearch = true } = props;
1651
- return /* @__PURE__ */ React.createElement(TechDocsReaderProvider, {
1652
- entityRef
1653
- }, /* @__PURE__ */ React.createElement(TheReader, {
1654
- entityRef,
1655
- onReady,
1656
- withSearch
1657
- }));
1658
- };
1659
-
1660
- const rootRouteRef = createRouteRef({
1661
- id: "techdocs:index-page"
1662
- });
1663
- const rootDocsRouteRef = createRouteRef({
1664
- id: "techdocs:reader-page",
1665
- params: ["namespace", "kind", "name"]
1666
- });
1667
- const rootCatalogDocsRouteRef = createRouteRef({
1668
- id: "techdocs:catalog-reader-view"
1669
- });
1670
-
1671
- const TechDocsReaderPageHeader = (props) => {
1672
- const { entityRef, entityMetadata, techDocsMetadata, children } = props;
1673
- const { name } = entityRef;
1674
- const { site_name: siteName, site_description: siteDescription } = techDocsMetadata || {};
1675
- const { locationMetadata, spec } = entityMetadata || {};
1676
- const lifecycle = spec == null ? void 0 : spec.lifecycle;
1677
- const ownedByRelations = entityMetadata ? getEntityRelations(entityMetadata, RELATION_OWNED_BY) : [];
1678
- const docsRootLink = useRouteRef(rootRouteRef)();
1679
- const labels = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(HeaderLabel, {
1680
- label: "Component",
1681
- value: /* @__PURE__ */ React.createElement(EntityRefLink, {
1682
- color: "inherit",
1683
- entityRef,
1684
- defaultKind: "Component"
1685
- })
1686
- }), ownedByRelations.length > 0 && /* @__PURE__ */ React.createElement(HeaderLabel, {
1687
- label: "Owner",
1688
- value: /* @__PURE__ */ React.createElement(EntityRefLinks, {
1689
- color: "inherit",
1690
- entityRefs: ownedByRelations,
1691
- defaultKind: "group"
1692
- })
1693
- }), lifecycle ? /* @__PURE__ */ React.createElement(HeaderLabel, {
1694
- label: "Lifecycle",
1695
- value: lifecycle
1696
- }) : null, locationMetadata && locationMetadata.type !== "dir" && locationMetadata.type !== "file" ? /* @__PURE__ */ React.createElement(HeaderLabel, {
1697
- label: "",
1698
- value: /* @__PURE__ */ React.createElement("a", {
1699
- href: locationMetadata.target,
1700
- target: "_blank",
1701
- rel: "noopener noreferrer"
1702
- }, /* @__PURE__ */ React.createElement(CodeIcon, {
1703
- style: { marginTop: "-25px", fill: "#fff" }
1704
- }))
1705
- }) : null);
1706
- return /* @__PURE__ */ React.createElement(Header, {
1707
- title: siteName ? siteName : ".",
1708
- pageTitleOverride: siteName || name,
1709
- subtitle: siteDescription && siteDescription !== "None" ? siteDescription : "",
1710
- type: "Docs",
1711
- typeLink: docsRootLink
1712
- }, labels, children);
1713
- };
1714
-
1715
- function toLowerMaybe(str, config) {
1716
- return config.getOptionalBoolean("techdocs.legacyUseCaseSensitiveTripletPaths") ? str : str.toLocaleLowerCase("en-US");
1717
- }
1718
-
1719
- const DocsCardGrid = (props) => {
1720
- const { entities } = props;
1721
- const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
1722
- const config = useApi(configApiRef);
1723
- if (!entities)
1724
- return null;
1725
- return /* @__PURE__ */ React.createElement(ItemCardGrid, {
1726
- "data-testid": "docs-explore"
1727
- }, !(entities == null ? void 0 : entities.length) ? null : entities.map((entity, index) => {
1728
- var _a, _b;
1729
- return /* @__PURE__ */ React.createElement(Card, {
1730
- key: index
1731
- }, /* @__PURE__ */ React.createElement(CardMedia, null, /* @__PURE__ */ React.createElement(ItemCardHeader, {
1732
- title: (_a = entity.metadata.title) != null ? _a : entity.metadata.name
1733
- })), /* @__PURE__ */ React.createElement(CardContent, null, entity.metadata.description), /* @__PURE__ */ React.createElement(CardActions, null, /* @__PURE__ */ React.createElement(Button$1, {
1734
- to: getRouteToReaderPageFor({
1735
- namespace: toLowerMaybe((_b = entity.metadata.namespace) != null ? _b : "default", config),
1736
- kind: toLowerMaybe(entity.kind, config),
1737
- name: toLowerMaybe(entity.metadata.name, config)
1738
- }),
1739
- color: "primary",
1740
- "data-testid": "read_docs"
1741
- }, "Read Docs")));
1742
- }));
1743
- };
1744
-
1745
- const EntityListDocsGrid = () => {
1746
- const { loading, error, entities } = useEntityList();
1747
- if (error) {
1748
- return /* @__PURE__ */ React.createElement(WarningPanel, {
1749
- severity: "error",
1750
- title: "Could not load available documentation."
1751
- }, /* @__PURE__ */ React.createElement(CodeSnippet, {
1752
- language: "text",
1753
- text: error.toString()
1754
- }));
1755
- }
1756
- if (loading || !entities) {
1757
- return /* @__PURE__ */ React.createElement(Progress, null);
1758
- }
1759
- entities.sort((a, b) => {
1760
- var _a, _b;
1761
- return ((_a = a.metadata.title) != null ? _a : a.metadata.name).localeCompare((_b = b.metadata.title) != null ? _b : b.metadata.name);
1762
- });
1763
- return /* @__PURE__ */ React.createElement(DocsCardGrid, {
1764
- entities
1765
- });
1766
- };
1767
-
1768
- const YellowStar = withStyles$1({
1769
- root: {
1770
- color: "#f3ba37"
1771
- }
1772
- })(Star);
1773
- const actionFactories = {
1774
- createCopyDocsUrlAction(copyToClipboard) {
1775
- return (row) => {
1776
- return {
1777
- icon: () => /* @__PURE__ */ React.createElement(ShareIcon, {
1778
- fontSize: "small"
1779
- }),
1780
- tooltip: "Click to copy documentation link to clipboard",
1781
- onClick: () => copyToClipboard(`${window.location.origin}${row.resolved.docsUrl}`)
1782
- };
1783
- };
1784
- },
1785
- createStarEntityAction(isStarredEntity, toggleStarredEntity) {
1786
- return ({ entity }) => {
1787
- const isStarred = isStarredEntity(entity);
1788
- return {
1789
- cellStyle: { paddingLeft: "1em" },
1790
- icon: () => isStarred ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(StarBorder, null),
1791
- tooltip: isStarred ? "Remove from favorites" : "Add to favorites",
1792
- onClick: () => toggleStarredEntity(entity)
1793
- };
1794
- };
1795
- }
1796
- };
1797
-
1798
- function customTitle(entity) {
1799
- return entity.metadata.title || entity.metadata.name;
1800
- }
1801
- const columnFactories = {
1802
- createNameColumn() {
1803
- return {
1804
- title: "Document",
1805
- field: "entity.metadata.name",
1806
- highlight: true,
1807
- render: (row) => /* @__PURE__ */ React.createElement(SubvalueCell, {
1808
- value: /* @__PURE__ */ React.createElement(Link, {
1809
- to: row.resolved.docsUrl
1810
- }, customTitle(row.entity)),
1811
- subvalue: row.entity.metadata.description
1812
- })
1813
- };
1814
- },
1815
- createOwnerColumn() {
1816
- return {
1817
- title: "Owner",
1818
- field: "resolved.ownedByRelationsTitle",
1819
- render: ({ resolved }) => /* @__PURE__ */ React.createElement(EntityRefLinks, {
1820
- entityRefs: resolved.ownedByRelations,
1821
- defaultKind: "group"
1822
- })
1823
- };
1824
- },
1825
- createTypeColumn() {
1826
- return {
1827
- title: "Type",
1828
- field: "entity.spec.type"
1829
- };
1830
- }
1831
- };
1832
-
1833
- const DocsTable = (props) => {
1834
- const { entities, title, loading, columns, actions } = props;
1835
- const [, copyToClipboard] = useCopyToClipboard();
1836
- const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
1837
- const config = useApi(configApiRef);
1838
- if (!entities)
1839
- return null;
1840
- const documents = entities.map((entity) => {
1841
- var _a;
1842
- const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
1843
- return {
1844
- entity,
1845
- resolved: {
1846
- docsUrl: getRouteToReaderPageFor({
1847
- namespace: toLowerMaybe((_a = entity.metadata.namespace) != null ? _a : "default", config),
1848
- kind: toLowerMaybe(entity.kind, config),
1849
- name: toLowerMaybe(entity.metadata.name, config)
1850
- }),
1851
- ownedByRelations,
1852
- ownedByRelationsTitle: ownedByRelations.map((r) => humanizeEntityRef(r, { defaultKind: "group" })).join(", ")
1853
- }
1854
- };
1855
- });
1856
- const defaultColumns = [
1857
- columnFactories.createNameColumn(),
1858
- columnFactories.createOwnerColumn(),
1859
- columnFactories.createTypeColumn()
1860
- ];
1861
- const defaultActions = [
1862
- actionFactories.createCopyDocsUrlAction(copyToClipboard)
1863
- ];
1864
- return /* @__PURE__ */ React.createElement(React.Fragment, null, loading || documents && documents.length > 0 ? /* @__PURE__ */ React.createElement(Table, {
1865
- isLoading: loading,
1866
- options: {
1867
- paging: true,
1868
- pageSize: 20,
1869
- search: true,
1870
- actionsColumnIndex: -1
1871
- },
1872
- data: documents,
1873
- columns: columns || defaultColumns,
1874
- actions: actions || defaultActions,
1875
- title: title ? `${title} (${documents.length})` : `All (${documents.length})`
1876
- }) : /* @__PURE__ */ React.createElement(EmptyState, {
1877
- missing: "data",
1878
- title: "No documents to show",
1879
- description: "Create your own document. Check out our Getting Started Information",
1880
- action: /* @__PURE__ */ React.createElement(Button$1, {
1881
- color: "primary",
1882
- to: "https://backstage.io/docs/features/techdocs/getting-started",
1883
- variant: "contained"
1884
- }, "DOCS")
1885
- }));
1886
- };
1887
- DocsTable.columns = columnFactories;
1888
- DocsTable.actions = actionFactories;
1889
-
1890
- const EntityListDocsTable = (props) => {
1891
- var _a, _b;
1892
- const { columns, actions } = props;
1893
- const { loading, error, entities, filters } = useEntityList();
1894
- const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
1895
- const [, copyToClipboard] = useCopyToClipboard();
1896
- const title = capitalize((_b = (_a = filters.user) == null ? void 0 : _a.value) != null ? _b : "all");
1897
- const defaultActions = [
1898
- actionFactories.createCopyDocsUrlAction(copyToClipboard),
1899
- actionFactories.createStarEntityAction(isStarredEntity, toggleStarredEntity)
1900
- ];
1901
- if (error) {
1902
- return /* @__PURE__ */ React.createElement(WarningPanel, {
1903
- severity: "error",
1904
- title: "Could not load available documentation."
1905
- }, /* @__PURE__ */ React.createElement(CodeSnippet, {
1906
- language: "text",
1907
- text: error.toString()
1908
- }));
1909
- }
1910
- return /* @__PURE__ */ React.createElement(DocsTable, {
1911
- title,
1912
- entities,
1913
- loading,
1914
- actions: actions || defaultActions,
1915
- columns
1916
- });
1917
- };
1918
- EntityListDocsTable.columns = columnFactories;
1919
- EntityListDocsTable.actions = actionFactories;
1920
-
1921
- const TechDocsPageWrapper = (props) => {
1922
- var _a;
1923
- const { children } = props;
1924
- const configApi = useApi(configApiRef);
1925
- const generatedSubtitle = `Documentation available in ${(_a = configApi.getOptionalString("organization.name")) != null ? _a : "Backstage"}`;
1926
- return /* @__PURE__ */ React.createElement(PageWithHeader, {
1927
- title: "Documentation",
1928
- subtitle: generatedSubtitle,
1929
- themeId: "documentation"
1930
- }, children);
1931
- };
1932
-
1933
- class TechDocsFilter {
1934
- getCatalogFilters() {
1935
- return {
1936
- "metadata.annotations.backstage.io/techdocs-ref": CATALOG_FILTER_EXISTS
1937
- };
1938
- }
1939
- }
1940
- const TechDocsPicker = () => {
1941
- const { updateFilters } = useEntityList();
1942
- useEffect(() => {
1943
- updateFilters({
1944
- techdocs: new TechDocsFilter()
1945
- });
1946
- }, [updateFilters]);
1947
- return null;
1948
- };
1949
-
1950
- const DefaultTechDocsHome = (props) => {
1951
- const { initialFilter = "all", columns, actions } = props;
1952
- return /* @__PURE__ */ React.createElement(TechDocsPageWrapper, null, /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, {
1953
- title: ""
1954
- }, /* @__PURE__ */ React.createElement(SupportButton, null, "Discover documentation in your ecosystem.")), /* @__PURE__ */ React.createElement(EntityListProvider, null, /* @__PURE__ */ React.createElement(CatalogFilterLayout, null, /* @__PURE__ */ React.createElement(CatalogFilterLayout.Filters, null, /* @__PURE__ */ React.createElement(TechDocsPicker, null), /* @__PURE__ */ React.createElement(UserListPicker, {
1955
- initialFilter
1956
- }), /* @__PURE__ */ React.createElement(EntityOwnerPicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement(CatalogFilterLayout.Content, null, /* @__PURE__ */ React.createElement(EntityListDocsTable, {
1957
- actions,
1958
- columns
1959
- }))))));
1960
- };
1961
-
1962
- const techdocsPlugin = createPlugin({
1963
- id: "techdocs",
1964
- apis: [
1965
- createApiFactory({
1966
- api: techdocsStorageApiRef,
1967
- deps: {
1968
- configApi: configApiRef,
1969
- discoveryApi: discoveryApiRef,
1970
- identityApi: identityApiRef,
1971
- fetchApi: fetchApiRef
1972
- },
1973
- factory: ({ configApi, discoveryApi, identityApi, fetchApi }) => new TechDocsStorageClient({
1974
- configApi,
1975
- discoveryApi,
1976
- identityApi,
1977
- fetchApi
1978
- })
1979
- }),
1980
- createApiFactory({
1981
- api: techdocsApiRef,
1982
- deps: {
1983
- configApi: configApiRef,
1984
- discoveryApi: discoveryApiRef,
1985
- fetchApi: fetchApiRef
1986
- },
1987
- factory: ({ configApi, discoveryApi, fetchApi }) => new TechDocsClient({
1988
- configApi,
1989
- discoveryApi,
1990
- fetchApi
1991
- })
1992
- })
1993
- ],
1994
- routes: {
1995
- root: rootRouteRef,
1996
- docRoot: rootDocsRouteRef,
1997
- entityContent: rootCatalogDocsRouteRef
1998
- }
1999
- });
2000
- const TechdocsPage = techdocsPlugin.provide(createRoutableExtension({
2001
- name: "TechdocsPage",
2002
- component: () => Promise.resolve().then(function () { return Router$1; }).then((m) => m.Router),
2003
- mountPoint: rootRouteRef
2004
- }));
2005
- const EntityTechdocsContent = techdocsPlugin.provide(createRoutableExtension({
2006
- name: "EntityTechdocsContent",
2007
- component: () => Promise.resolve().then(function () { return Router$1; }).then((m) => m.EmbeddedDocsRouter),
2008
- mountPoint: rootCatalogDocsRouteRef
2009
- }));
2010
- const TechDocsCustomHome = techdocsPlugin.provide(createRoutableExtension({
2011
- name: "TechDocsCustomHome",
2012
- component: () => import('./esm/TechDocsCustomHome-a2958b1a.esm.js').then((m) => m.TechDocsCustomHome),
2013
- mountPoint: rootRouteRef
2014
- }));
2015
- const TechDocsIndexPage$2 = techdocsPlugin.provide(createRoutableExtension({
2016
- name: "TechDocsIndexPage",
2017
- component: () => Promise.resolve().then(function () { return TechDocsIndexPage$1; }).then((m) => m.TechDocsIndexPage),
2018
- mountPoint: rootRouteRef
2019
- }));
2020
- const TechDocsReaderPage$2 = techdocsPlugin.provide(createRoutableExtension({
2021
- name: "TechDocsReaderPage",
2022
- component: () => Promise.resolve().then(function () { return TechDocsReaderPage$1; }).then((m) => m.TechDocsReaderPage),
2023
- mountPoint: rootDocsRouteRef
2024
- }));
2025
-
2026
- const TechDocsIndexPage = () => {
2027
- const outlet = useOutlet();
2028
- return outlet || /* @__PURE__ */ React.createElement(DefaultTechDocsHome, null);
2029
- };
2030
-
2031
- var TechDocsIndexPage$1 = /*#__PURE__*/Object.freeze({
2032
- __proto__: null,
2033
- TechDocsIndexPage: TechDocsIndexPage
2034
- });
2035
-
2036
- const TechDocsReaderPage = (props) => {
2037
- const { children } = props;
2038
- const { NotFoundErrorPage } = useApp().getComponents();
2039
- const outlet = useOutlet();
2040
- const [documentReady, setDocumentReady] = useState(false);
2041
- const { namespace, kind, name } = useParams();
2042
- const techdocsApi = useApi(techdocsApiRef);
2043
- const { value: techdocsMetadataValue } = useAsync(() => {
2044
- if (documentReady) {
2045
- return techdocsApi.getTechDocsMetadata({ kind, namespace, name });
2046
- }
2047
- return Promise.resolve(void 0);
2048
- }, [kind, namespace, name, techdocsApi, documentReady]);
2049
- const { value: entityMetadataValue, error: entityMetadataError } = useAsync(() => {
2050
- return techdocsApi.getEntityMetadata({ kind, namespace, name });
2051
- }, [kind, namespace, name, techdocsApi]);
2052
- const onReady = useCallback(() => {
2053
- setDocumentReady(true);
2054
- }, [setDocumentReady]);
2055
- if (entityMetadataError)
2056
- return /* @__PURE__ */ React.createElement(NotFoundErrorPage, null);
2057
- if (!children)
2058
- return outlet || /* @__PURE__ */ React.createElement(Page, {
2059
- themeId: "documentation"
2060
- }, /* @__PURE__ */ React.createElement(TechDocsReaderPageHeader, {
2061
- techDocsMetadata: techdocsMetadataValue,
2062
- entityMetadata: entityMetadataValue,
2063
- entityRef: {
2064
- kind,
2065
- namespace,
2066
- name
2067
- }
2068
- }), /* @__PURE__ */ React.createElement(Content, {
2069
- "data-testid": "techdocs-content"
2070
- }, /* @__PURE__ */ React.createElement(Reader, {
2071
- onReady,
2072
- entityRef: {
2073
- kind,
2074
- namespace,
2075
- name
2076
- }
2077
- })));
2078
- return /* @__PURE__ */ React.createElement(Page, {
2079
- themeId: "documentation"
2080
- }, children instanceof Function ? children({
2081
- techdocsMetadataValue,
2082
- entityMetadataValue,
2083
- entityRef: { kind, namespace, name },
2084
- onReady
2085
- }) : children);
2086
- };
2087
-
2088
- var TechDocsReaderPage$1 = /*#__PURE__*/Object.freeze({
2089
- __proto__: null,
2090
- TechDocsReaderPage: TechDocsReaderPage
2091
- });
2092
-
2093
- const EntityPageDocs = ({ entity }) => {
2094
- var _a;
2095
- const config = useApi(configApiRef);
2096
- return /* @__PURE__ */ React.createElement(Reader, {
2097
- withSearch: false,
2098
- entityRef: {
2099
- namespace: toLowerMaybe((_a = entity.metadata.namespace) != null ? _a : "default", config),
2100
- kind: toLowerMaybe(entity.kind, config),
2101
- name: toLowerMaybe(entity.metadata.name, config)
2102
- }
2103
- });
2104
- };
2105
-
2106
- const TECHDOCS_ANNOTATION = "backstage.io/techdocs-ref";
2107
- const isTechDocsAvailable = (entity) => {
2108
- var _a, _b;
2109
- return Boolean((_b = (_a = entity == null ? void 0 : entity.metadata) == null ? void 0 : _a.annotations) == null ? void 0 : _b[TECHDOCS_ANNOTATION]);
2110
- };
2111
- const Router = () => {
2112
- return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
2113
- path: "/",
2114
- element: /* @__PURE__ */ React.createElement(TechDocsIndexPage, null)
2115
- }), /* @__PURE__ */ React.createElement(Route, {
2116
- path: "/:namespace/:kind/:name/*",
2117
- element: /* @__PURE__ */ React.createElement(TechDocsReaderPage, null)
2118
- }));
2119
- };
2120
- const EmbeddedDocsRouter = () => {
2121
- var _a;
2122
- const { entity } = useEntity();
2123
- const projectId = (_a = entity.metadata.annotations) == null ? void 0 : _a[TECHDOCS_ANNOTATION];
2124
- if (!projectId) {
2125
- return /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, {
2126
- annotation: TECHDOCS_ANNOTATION
2127
- });
2128
- }
2129
- return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
2130
- path: "/*",
2131
- element: /* @__PURE__ */ React.createElement(EntityPageDocs, {
2132
- entity
2133
- })
2134
- }));
2135
- };
2136
-
2137
- var Router$1 = /*#__PURE__*/Object.freeze({
2138
- __proto__: null,
2139
- isTechDocsAvailable: isTechDocsAvailable,
2140
- Router: Router,
2141
- EmbeddedDocsRouter: EmbeddedDocsRouter
2142
- });
2143
-
2144
- export { DefaultTechDocsHome, DocsCardGrid, DocsTable, EmbeddedDocsRouter, EntityListDocsGrid, EntityListDocsTable, EntityTechdocsContent, Reader, Router, TechDocsClient, TechDocsCustomHome, TechDocsIndexPage$2 as TechDocsIndexPage, TechDocsPageWrapper, TechDocsPicker, TechDocsReaderPage$2 as TechDocsReaderPage, TechDocsReaderPageHeader, TechDocsSearch, TechDocsSearchResultListItem, TechDocsStateIndicator, TechDocsStorageClient, TechdocsPage, isTechDocsAvailable, techdocsPlugin as plugin, techdocsApiRef, techdocsPlugin, techdocsStorageApiRef, useTechDocsReader, useTechDocsReaderDom, withTechDocsReaderProvider };
1
+ export { y as DefaultTechDocsHome, a as DocsCardGrid, D as DocsTable, C as EmbeddedDocsRouter, v as EntityListDocsGrid, x as EntityListDocsTable, E as EntityTechdocsContent, R as Reader, B as Router, j as TechDocsClient, d as TechDocsCustomHome, e as TechDocsIndexPage, T as TechDocsPageWrapper, z as TechDocsPicker, c as TechDocsReaderLayout, g as TechDocsReaderPage, m as TechDocsReaderPageContent, l as TechDocsReaderPageHeader, p as TechDocsReaderPageSubheader, n as TechDocsReaderProvider, s as TechDocsSearch, r as TechDocsSearchResultListItem, q as TechDocsStateIndicator, k as TechDocsStorageClient, f as TechdocsPage, A as isTechDocsAvailable, t as plugin, i as techdocsApiRef, t as techdocsPlugin, h as techdocsStorageApiRef, u as useTechDocsReader, o as useTechDocsReaderDom, w as withTechDocsReaderProvider } from './esm/index-fba8389b.esm.js';
2
+ import '@backstage/core-plugin-api';
3
+ import '@backstage/errors';
4
+ import 'event-source-polyfill';
5
+ import 'react';
6
+ import 'react-router-dom';
7
+ import '@backstage/core-components';
8
+ import '@backstage/plugin-techdocs-react';
9
+ import 'jss';
10
+ import '@material-ui/core';
11
+ import '@material-ui/styles';
12
+ import 'react-text-truncate';
13
+ import '@backstage/plugin-search-react';
14
+ import '@material-ui/icons/Search';
15
+ import '@material-ui/lab/Autocomplete';
16
+ import 'react-router';
17
+ import 'react-use/lib/useDebounce';
18
+ import '@material-ui/lab';
19
+ import '@material-ui/icons/Close';
20
+ import 'react-use/lib/useAsync';
21
+ import 'react-use/lib/useAsyncRetry';
22
+ import '@material-ui/core/styles';
23
+ import '@backstage/integration-react';
24
+ import '@backstage/integration';
25
+ import '@material-ui/icons/FeedbackOutlined';
26
+ import 'react-dom';
27
+ import 'git-url-parse';
28
+ import '@material-ui/icons/Menu';
29
+ import 'dompurify';
30
+ import 'react-helmet';
31
+ import '@material-ui/icons/Code';
32
+ import '@backstage/plugin-catalog-react';
33
+ import '@backstage/catalog-model';
34
+ import 'react-use/lib/useCopyToClipboard';
35
+ import 'lodash';
36
+ import '@material-ui/icons/Share';
37
+ import '@material-ui/icons/Star';
38
+ import '@material-ui/icons/StarBorder';
39
+ import '@backstage/core-app-api';
2145
40
  //# sourceMappingURL=index.esm.js.map