@dogsbay/format-vitepress 0.2.0-beta.101

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/nav.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * VitePress `themeConfig.sidebar` / `themeConfig.nav` → `NavItem[]`.
3
+ *
4
+ * Free of filesystem I/O: slug resolution is injected, so this module is unit
5
+ * testable and the loader stays the only thing that touches disk.
6
+ */
7
+ import type { NavItem } from "@dogsbay/types";
8
+ import type { RawNavItem, RawSidebar } from "./config.js";
9
+ export interface NavContext {
10
+ /** URL prefix for every internal href (`/docs`). */
11
+ hrefPrefix: string;
12
+ /**
13
+ * Slugs that actually exist, so a sidebar link to `/guide/` can be resolved
14
+ * to `guide/index` vs `guide` correctly and a dangling link can be reported
15
+ * rather than emitted as a 404.
16
+ */
17
+ hasSlug?: (slug: string) => boolean;
18
+ onWarn?: (msg: string) => void;
19
+ }
20
+ /**
21
+ * A VitePress sidebar `link` is a SITE PATH, not a file path: `/guide/` is
22
+ * `guide/index.md`, `/config/include` is `config/include.md`. Both forms may
23
+ * carry `.md` or `.html`, and `base` is not included.
24
+ */
25
+ export declare function linkToSlug(link: string): string;
26
+ export declare function slugToHref(slug: string, ctx: NavContext): string;
27
+ export declare function itemsToNav(items: RawNavItem[], ctx: NavContext): NavItem[];
28
+ /**
29
+ * Build the sidebar.
30
+ *
31
+ * The object form (`{'/guide/': [...], '/config/': [...]}`) is VitePress's
32
+ * multi-section shape: each key is a path prefix and only its own section is
33
+ * visible while the reader is under it. A Dogsbay sidebar shows everything, so
34
+ * each key becomes a top-level GROUP. Its label comes from the matching top
35
+ * `nav` entry when there is one (that is the name the site itself gives the
36
+ * section — "Guide", "Config", "Examples"), else the prettified path segment.
37
+ *
38
+ * A single-key object is spliced flat: wrapping the whole site in one group
39
+ * called "Docs" is noise, not structure.
40
+ */
41
+ export declare function buildNav(sidebar: RawSidebar | undefined, nav: RawNavItem[] | undefined, ctx: NavContext): NavItem[];
42
+ /**
43
+ * Fallback nav: a tree derived from the page slugs themselves, used when the
44
+ * config yields no sidebar at all (an unreadable config, or a site that
45
+ * relies entirely on VitePress's own auto-sidebar).
46
+ */
47
+ export declare function navFromSlugs(pages: {
48
+ slug: string;
49
+ title: string;
50
+ }[], ctx: NavContext): NavItem[];
51
+ //# sourceMappingURL=nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,MAAM,CAIhE;AAgCD,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,EAAE,CAQ1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,GAAG,EAAE,UAAU,EAAE,GAAG,SAAS,EAC7B,GAAG,EAAE,UAAU,GACd,OAAO,EAAE,CAsBX;AAyCD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EACxC,GAAG,EAAE,UAAU,GACd,OAAO,EAAE,CAsCX"}
package/dist/nav.js ADDED
@@ -0,0 +1,182 @@
1
+ const EXTERNAL_RE = /^[a-z][a-z0-9+.-]*:|^\/\//i;
2
+ /**
3
+ * A VitePress sidebar `link` is a SITE PATH, not a file path: `/guide/` is
4
+ * `guide/index.md`, `/config/include` is `config/include.md`. Both forms may
5
+ * carry `.md` or `.html`, and `base` is not included.
6
+ */
7
+ export function linkToSlug(link) {
8
+ let s = link.split("#")[0].split("?")[0];
9
+ s = s.replace(/^\/+/, "");
10
+ s = s.replace(/\.(md|html)$/i, "");
11
+ if (s.endsWith("/"))
12
+ s = `${s}index`;
13
+ if (s === "")
14
+ s = "index";
15
+ return s;
16
+ }
17
+ export function slugToHref(slug, ctx) {
18
+ const prefix = ctx.hrefPrefix.replace(/\/$/, "");
19
+ const clean = slug === "index" ? "" : slug.replace(/\/index$/, "");
20
+ return clean ? `${prefix}/${clean}` : prefix || "/";
21
+ }
22
+ function resolveHref(link, ctx) {
23
+ if (EXTERNAL_RE.test(link))
24
+ return link;
25
+ const anchor = link.includes("#") ? link.slice(link.indexOf("#")) : "";
26
+ const slug = linkToSlug(link);
27
+ if (ctx.hasSlug && !ctx.hasSlug(slug) && !ctx.hasSlug(`${slug}/index`)) {
28
+ ctx.onWarn?.(`sidebar link "${link}" has no matching page`);
29
+ }
30
+ return `${slugToHref(slug, ctx)}${anchor}`;
31
+ }
32
+ function itemToNav(item, ctx) {
33
+ const label = typeof item.text === "string" ? item.text : undefined;
34
+ const link = typeof item.link === "string" ? item.link : undefined;
35
+ const rawItems = Array.isArray(item.items) ? item.items : undefined;
36
+ const children = rawItems ? itemsToNav(rawItems, ctx) : undefined;
37
+ if (!label) {
38
+ // A nav dropdown may hold `{ component: … }` or a divider — nothing to
39
+ // render, but say so rather than dropping it invisibly.
40
+ ctx.onWarn?.(`sidebar item with no \`text\` skipped (keys: ${Object.keys(item).join(",")})`);
41
+ return null;
42
+ }
43
+ const nav = { label };
44
+ if (link)
45
+ nav.href = resolveHref(link, ctx);
46
+ if (children && children.length > 0)
47
+ nav.children = children;
48
+ if (!nav.href && !nav.children)
49
+ return null;
50
+ return nav;
51
+ }
52
+ export function itemsToNav(items, ctx) {
53
+ const out = [];
54
+ for (const item of items) {
55
+ if (!item || typeof item !== "object")
56
+ continue;
57
+ const nav = itemToNav(item, ctx);
58
+ if (nav)
59
+ out.push(nav);
60
+ }
61
+ return out;
62
+ }
63
+ /**
64
+ * Build the sidebar.
65
+ *
66
+ * The object form (`{'/guide/': [...], '/config/': [...]}`) is VitePress's
67
+ * multi-section shape: each key is a path prefix and only its own section is
68
+ * visible while the reader is under it. A Dogsbay sidebar shows everything, so
69
+ * each key becomes a top-level GROUP. Its label comes from the matching top
70
+ * `nav` entry when there is one (that is the name the site itself gives the
71
+ * section — "Guide", "Config", "Examples"), else the prettified path segment.
72
+ *
73
+ * A single-key object is spliced flat: wrapping the whole site in one group
74
+ * called "Docs" is noise, not structure.
75
+ */
76
+ export function buildNav(sidebar, nav, ctx) {
77
+ if (!sidebar)
78
+ return [];
79
+ if (Array.isArray(sidebar))
80
+ return itemsToNav(sidebar, ctx);
81
+ const keys = Object.keys(sidebar);
82
+ if (keys.length === 0)
83
+ return [];
84
+ if (keys.length === 1)
85
+ return itemsToNav(sidebar[keys[0]], ctx);
86
+ const labels = navLabels(nav ?? []);
87
+ // Sections are built in the config's own key order. An earlier version
88
+ // sorted by key length to assign labels and then re-sorted by a Map keyed on
89
+ // LABEL — so two keys that prettify to the same name (`/v1/guide/` and
90
+ // `/v2/guide/` both give "Guide") collided in that Map and the sections came
91
+ // out in the wrong order. The index travels WITH the item instead.
92
+ const out = [];
93
+ keys.forEach((key, idx) => {
94
+ const children = itemsToNav(sidebar[key], ctx);
95
+ if (children.length === 0)
96
+ return;
97
+ out.push({ label: labels.get(normalizeKey(key)) ?? prettifyKey(key), children, idx });
98
+ });
99
+ out.sort((a, b) => a.idx - b.idx);
100
+ return out.map(({ label, children }) => ({ label, children }));
101
+ }
102
+ /** Top-level `nav` entries, indexed by the path they point at. */
103
+ function navLabels(nav) {
104
+ const map = new Map();
105
+ const visit = (items) => {
106
+ for (const item of items) {
107
+ if (!item || typeof item !== "object")
108
+ continue;
109
+ const text = typeof item.text === "string" ? item.text : undefined;
110
+ const link = typeof item.link === "string" ? item.link : undefined;
111
+ const match = typeof item.activeMatch === "string" ? item.activeMatch : undefined;
112
+ if (text && (link || match) && !EXTERNAL_RE.test(link ?? "")) {
113
+ // `activeMatch` is the section's real extent when it is present;
114
+ // `link` points at the section landing page.
115
+ for (const p of [match, link]) {
116
+ if (!p)
117
+ continue;
118
+ const k = normalizeKey(p);
119
+ if (k && !map.has(k))
120
+ map.set(k, text);
121
+ }
122
+ }
123
+ if (Array.isArray(item.items))
124
+ visit(item.items);
125
+ }
126
+ };
127
+ visit(nav);
128
+ return map;
129
+ }
130
+ function normalizeKey(key) {
131
+ return key.replace(/^\^/, "").replace(/\$$/, "").replace(/^\/+|\/+$/g, "");
132
+ }
133
+ function prettifyKey(key) {
134
+ const seg = normalizeKey(key).split("/").filter(Boolean).pop() ?? "";
135
+ if (!seg)
136
+ return "Docs";
137
+ return seg
138
+ .split(/[-_]/)
139
+ .filter(Boolean)
140
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
141
+ .join(" ");
142
+ }
143
+ /**
144
+ * Fallback nav: a tree derived from the page slugs themselves, used when the
145
+ * config yields no sidebar at all (an unreadable config, or a site that
146
+ * relies entirely on VitePress's own auto-sidebar).
147
+ */
148
+ export function navFromSlugs(pages, ctx) {
149
+ const root = { label: "", items: [], groups: new Map() };
150
+ for (const page of [...pages].sort((a, b) => a.slug.localeCompare(b.slug))) {
151
+ if (page.slug === "index")
152
+ continue;
153
+ const parts = page.slug.split("/");
154
+ const file = parts.pop() ?? page.slug;
155
+ let group = root;
156
+ for (const part of parts) {
157
+ let next = group.groups.get(part);
158
+ if (!next) {
159
+ next = { label: prettifyKey(part), items: [], groups: new Map() };
160
+ group.groups.set(part, next);
161
+ }
162
+ group = next;
163
+ }
164
+ // A directory's `index` page becomes the group's own link, not a child.
165
+ if (file === "index" && group !== root) {
166
+ group.items.unshift({ label: page.title, href: slugToHref(page.slug, ctx) });
167
+ continue;
168
+ }
169
+ group.items.push({ label: page.title, href: slugToHref(page.slug, ctx) });
170
+ }
171
+ const collect = (g) => {
172
+ const out = [...g.items];
173
+ for (const child of g.groups.values()) {
174
+ const children = collect(child);
175
+ if (children.length > 0)
176
+ out.push({ label: child.label, children });
177
+ }
178
+ return out;
179
+ };
180
+ return collect(root);
181
+ }
182
+ //# sourceMappingURL=nav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav.js","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { TreeNode } from "@dogsbay/types";
2
+ import { type TranslateLedgerEntry, type TranslateOptions } from "./translate.js";
3
+ export interface ParseVitepressResult {
4
+ tree: TreeNode[];
5
+ frontmatter: Record<string, unknown>;
6
+ /** Frontmatter + translated body. */
7
+ markdown: string;
8
+ /** Translated body only. */
9
+ body: string;
10
+ ledger: TranslateLedgerEntry[];
11
+ components: Record<string, number>;
12
+ }
13
+ export declare function parseVitepress(src: string, opts?: TranslateOptions): ParseVitepressResult;
14
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAID,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,gBAAqB,GAC1B,oBAAoB,CAqBtB"}
package/dist/parse.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * VitePress page → { tree, frontmatter, markdown, body, ledger }.
3
+ *
4
+ * Pipeline (mirrors format-gitbook / format-myst):
5
+ * 1. Split YAML frontmatter off the body so the body transform can't
6
+ * disturb it.
7
+ * 2. Translate the VitePress body → Dogsbay-MD text.
8
+ * 3. Parse the translated Dogsbay-MD with the canonical `dogsbayMdToTree`.
9
+ *
10
+ * `markdown` (frontmatter + translated body) is what a migrate command writes
11
+ * to disk; `body` is the translated body alone.
12
+ */
13
+ import { dogsbayMdToTree } from "@dogsbay/format-dogsbay-md";
14
+ import { vitepressToDogsbayMd, } from "./translate.js";
15
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
16
+ export function parseVitepress(src, opts = {}) {
17
+ const normalized = src.replace(/\r\n?/g, "\n");
18
+ const fmMatch = normalized.match(FRONTMATTER_RE);
19
+ const fmBlock = fmMatch ? fmMatch[0] : "";
20
+ const rawBody = fmMatch ? normalized.slice(fmMatch[0].length) : normalized;
21
+ const { markdown: translatedBody, ledger, components } = vitepressToDogsbayMd(rawBody, opts);
22
+ const markdown = fmBlock
23
+ ? `${fmBlock.replace(/\n?$/, "\n")}\n${translatedBody}`
24
+ : translatedBody;
25
+ const { tree, frontmatter } = dogsbayMdToTree(markdown);
26
+ return {
27
+ tree,
28
+ frontmatter: frontmatter ?? {},
29
+ markdown,
30
+ body: translatedBody,
31
+ ledger,
32
+ components,
33
+ };
34
+ }
35
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EACL,oBAAoB,GAGrB,MAAM,gBAAgB,CAAC;AAaxB,MAAM,cAAc,GAAG,mCAAmC,CAAC;AAE3D,MAAM,UAAU,cAAc,CAC5B,GAAW,EACX,OAAyB,EAAE;IAE3B,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IAE3E,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7F,MAAM,QAAQ,GAAG,OAAO;QACtB,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,cAAc,EAAE;QACvD,CAAC,CAAC,cAAc,CAAC;IAEnB,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAExD,OAAO;QACL,IAAI;QACJ,WAAW,EAAE,WAAW,IAAI,EAAE;QAC9B,QAAQ;QACR,IAAI,EAAE,cAAc;QACpB,MAAM;QACN,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * VitePress Markdown → Dogsbay-MD text.
3
+ *
4
+ * VitePress markdown is CommonMark + `:::` containers + a handful of
5
+ * code-fence conventions + Vue. It is a near-superset of Dogsbay-MD, so this
6
+ * is a delimiter rewrite rather than a parser: once containers are renamed and
7
+ * fence metadata is normalized, `dogsbayMdToTree` defines correctness and we
8
+ * introduce zero new TreeNode types. Same pipeline as `format-gitbook` and
9
+ * `format-myst`.
10
+ *
11
+ * Everything this translator cannot map is recorded in a ledger. A migration
12
+ * that loses a construct silently is worse than one that says so.
13
+ */
14
+ export interface TranslateLedgerEntry {
15
+ construct: string;
16
+ note: string;
17
+ line: number;
18
+ }
19
+ export interface SnippetRequest {
20
+ /** Path exactly as written in the source (`@/foo.ts`, `./bar.md`). */
21
+ path: string;
22
+ /** `#region` name, when the reference named one. */
23
+ region?: string;
24
+ }
25
+ export interface TranslateOptions {
26
+ /**
27
+ * Resolve a `<<<` snippet or `<!--@include:-->` target to its text.
28
+ * Injected so this module stays free of filesystem I/O (and unit-testable);
29
+ * the loader wires it to `srcDir` + the page's own directory.
30
+ */
31
+ readSnippet?: (req: SnippetRequest) => string | undefined;
32
+ /**
33
+ * Rewrite an image/asset reference to where the EXPORTER will put the file.
34
+ *
35
+ * A reference must match how its consumer resolves it: VitePress serves
36
+ * `public/` at the site root and co-located images relative to the page,
37
+ * neither of which survives into `_assets/`. Without this the reference
38
+ * survives the conversion and the FILE does not, which looks exactly like
39
+ * success. Injected because only the loader knows the page's directory.
40
+ */
41
+ assetHref?: (src: string) => string;
42
+ /** Directory an include/snippet spec resolves to, for rebasing nested ones. */
43
+ resolveDir?: (spec: string) => string | undefined;
44
+ /** Rebuild these options anchored at another directory. */
45
+ optionsForDir?: (dir: string) => TranslateOptions;
46
+ }
47
+ export interface TranslateResult {
48
+ markdown: string;
49
+ ledger: TranslateLedgerEntry[];
50
+ /** Component tag → occurrence count, for the import summary. */
51
+ components: Record<string, number>;
52
+ }
53
+ export declare function vitepressToDogsbayMd(source: string, opts?: TranslateOptions): TranslateResult;
54
+ interface Ctx {
55
+ ledger: TranslateLedgerEntry[];
56
+ components: Record<string, number>;
57
+ opts: TranslateOptions;
58
+ /** Include-recursion depth, so a self-including fragment cannot loop. */
59
+ depth: number;
60
+ }
61
+ /**
62
+ * VitePress fence metadata → a single trailing `{…}` attrs block, which is
63
+ * what `dogsbayMdToTree` reads (markdown-it-attrs attaches it to the fence
64
+ * token; a bare `1,3-5` key becomes `props.highlights`).
65
+ *
66
+ * ts [vitest.config.ts] → ts {title="vitest.config.ts"}
67
+ * js{1,3-5} → js {1,3-5}
68
+ * ts:line-numbers=2 → ts {lineNumbers="2"}
69
+ * ts:no-line-numbers → ts
70
+ * js twoslash [a.js] → js {title="a.js"}
71
+ *
72
+ * The attrs block MUST be last — markdown-it-attrs only reads a trailing one.
73
+ */
74
+ export declare function translateFenceInfo(info: string, line: number, ctx?: Ctx): string;
75
+ export declare function splitRegion(spec: string): {
76
+ path: string;
77
+ region?: string;
78
+ };
79
+ export {};
80
+ //# sourceMappingURL=translate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAClD,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,gBAAgB,CAAC;CACnD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAqBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,gBAAqB,GAC1B,eAAe,CAKjB;AAcD,UAAU,GAAG;IACX,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAkcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,MAAM,CA4DhF;AA8YD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAI3E"}