@ox-content/vite-plugin 2.16.0 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/youtube2.mjs CHANGED
@@ -1,27 +1,16 @@
1
- import { unified } from "unified";
2
- import rehypeParse from "rehype-parse";
3
- import rehypeStringify from "rehype-stringify";
1
+ import { t as importNapiModule } from "./napi.mjs";
4
2
  //#region src/plugins/youtube.ts
5
3
  /**
6
4
  * YouTube Plugin - Privacy-enhanced iframe embedding
7
5
  *
8
- * Transforms <YouTube> components into responsive iframe embeds
9
- * using youtube-nocookie.com for enhanced privacy.
10
- */
11
- const defaultOptions = {
12
- privacyEnhanced: true,
13
- aspectRatio: "16/9",
14
- allowFullscreen: true,
15
- lazyLoad: true
16
- };
17
- /**
18
- * Get element attribute value.
6
+ * Transforms <YouTube> components into responsive iframe embeds using
7
+ * youtube-nocookie.com for enhanced privacy.
8
+ *
9
+ * The HTML rewrite is performed in Rust (`transformYoutubeEmbeds` in
10
+ * @ox-content/napi), replacing the previous rehype parse/stringify
11
+ * round-trip. This module keeps the public TS surface and a cheap marker
12
+ * check so pages without a `<youtube>` element never cross the NAPI boundary.
19
13
  */
20
- function getAttribute(el, name) {
21
- const value = el.properties?.[name];
22
- if (typeof value === "string") return value;
23
- if (Array.isArray(value)) return value.join(" ");
24
- }
25
14
  /**
26
15
  * Extract YouTube video ID from various URL formats.
27
16
  */
@@ -34,77 +23,11 @@ function extractVideoId(input) {
34
23
  return null;
35
24
  }
36
25
  /**
37
- * Build YouTube embed URL with parameters.
38
- */
39
- function buildEmbedUrl(videoId, options, params) {
40
- const domain = options.privacyEnhanced ? "www.youtube-nocookie.com" : "www.youtube.com";
41
- const url = new URL(`https://${domain}/embed/${videoId}`);
42
- if (params) for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);
43
- return url.toString();
44
- }
45
- /**
46
- * Create YouTube embed element.
47
- */
48
- function createYouTubeElement(videoId, options, title, start) {
49
- const params = {};
50
- if (start) params.start = start;
51
- const iframe = {
52
- type: "element",
53
- tagName: "iframe",
54
- properties: {
55
- src: buildEmbedUrl(videoId, options, params),
56
- title: title || `YouTube video ${videoId}`,
57
- allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
58
- referrerpolicy: "strict-origin-when-cross-origin",
59
- allowfullscreen: options.allowFullscreen || void 0,
60
- loading: options.lazyLoad ? "lazy" : void 0
61
- },
62
- children: []
63
- };
64
- return {
65
- type: "element",
66
- tagName: "div",
67
- properties: {
68
- className: ["ox-youtube"],
69
- style: `aspect-ratio: ${options.aspectRatio};`
70
- },
71
- children: [iframe]
72
- };
73
- }
74
- /**
75
- * Rehype plugin to transform YouTube components.
76
- */
77
- function rehypeYouTube(options) {
78
- return (tree) => {
79
- const visit = (node) => {
80
- if ("children" in node) for (let i = 0; i < node.children.length; i++) {
81
- const child = node.children[i];
82
- if (child.type === "element") if (child.tagName.toLowerCase() === "youtube") {
83
- const id = getAttribute(child, "id");
84
- const url = getAttribute(child, "url");
85
- const title = getAttribute(child, "title");
86
- const start = getAttribute(child, "start");
87
- const videoId = id ? extractVideoId(id) : url ? extractVideoId(url) : null;
88
- if (videoId) {
89
- const youtubeElement = createYouTubeElement(videoId, options, title, start);
90
- node.children[i] = youtubeElement;
91
- }
92
- } else visit(child);
93
- }
94
- };
95
- visit(tree);
96
- };
97
- }
98
- /**
99
26
  * Transform YouTube components in HTML.
100
27
  */
101
28
  async function transformYouTube(html, options) {
102
- const mergedOptions = {
103
- ...defaultOptions,
104
- ...options
105
- };
106
- const result = await unified().use(rehypeParse, { fragment: true }).use(rehypeYouTube, mergedOptions).use(rehypeStringify).process(html);
107
- return String(result);
29
+ if (!/<youtube/i.test(html)) return html;
30
+ return (await importNapiModule()).transformYoutubeEmbeds(html, options);
108
31
  }
109
32
  //#endregion
110
33
  export { transformYouTube as n, extractVideoId as t };
@@ -1 +1 @@
1
- {"version":3,"file":"youtube2.mjs","names":[],"sources":["../src/plugins/youtube.ts"],"sourcesContent":["/**\n * YouTube Plugin - Privacy-enhanced iframe embedding\n *\n * Transforms <YouTube> components into responsive iframe embeds\n * using youtube-nocookie.com for enhanced privacy.\n */\n\nimport { unified } from \"unified\";\nimport rehypeParse from \"rehype-parse\";\nimport rehypeStringify from \"rehype-stringify\";\nimport type { Root, Element, Properties } from \"hast\";\n\nexport interface YouTubeOptions {\n /** Use privacy-enhanced mode (youtube-nocookie.com). Default: true */\n privacyEnhanced?: boolean;\n /** Default aspect ratio. Default: \"16/9\" */\n aspectRatio?: string;\n /** Allow fullscreen. Default: true */\n allowFullscreen?: boolean;\n /** Lazy load iframe. Default: true */\n lazyLoad?: boolean;\n}\n\nconst defaultOptions: Required<YouTubeOptions> = {\n privacyEnhanced: true,\n aspectRatio: \"16/9\",\n allowFullscreen: true,\n lazyLoad: true,\n};\n\n/**\n * Get element attribute value.\n */\nfunction getAttribute(el: Element, name: string): string | undefined {\n const value = el.properties?.[name];\n if (typeof value === \"string\") return value;\n if (Array.isArray(value)) return value.join(\" \");\n return undefined;\n}\n\n/**\n * Extract YouTube video ID from various URL formats.\n */\nexport function extractVideoId(input: string): string | null {\n // Already a video ID (11 characters, alphanumeric + _ -)\n if (/^[a-zA-Z0-9_-]{11}$/.test(input)) {\n return input;\n }\n\n // Full URL patterns\n const patterns = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/|youtube\\.com\\/v\\/)([a-zA-Z0-9_-]{11})/,\n /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]{11})/,\n ];\n\n for (const pattern of patterns) {\n const match = input.match(pattern);\n if (match) return match[1];\n }\n\n return null;\n}\n\n/**\n * Build YouTube embed URL with parameters.\n */\nfunction buildEmbedUrl(\n videoId: string,\n options: Required<YouTubeOptions>,\n params?: Record<string, string>,\n): string {\n const domain = options.privacyEnhanced ? \"www.youtube-nocookie.com\" : \"www.youtube.com\";\n const url = new URL(`https://${domain}/embed/${videoId}`);\n\n // Add any custom parameters\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n }\n\n return url.toString();\n}\n\n/**\n * Create YouTube embed element.\n */\nfunction createYouTubeElement(\n videoId: string,\n options: Required<YouTubeOptions>,\n title?: string,\n start?: string,\n): Element {\n const params: Record<string, string> = {};\n if (start) {\n params.start = start;\n }\n\n const embedUrl = buildEmbedUrl(videoId, options, params);\n\n const iframeProps: Properties = {\n src: embedUrl,\n title: title || `YouTube video ${videoId}`,\n allow:\n \"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\",\n referrerpolicy: \"strict-origin-when-cross-origin\",\n allowfullscreen: options.allowFullscreen || undefined,\n loading: options.lazyLoad ? \"lazy\" : undefined,\n };\n\n const iframe: Element = {\n type: \"element\",\n tagName: \"iframe\",\n properties: iframeProps,\n children: [],\n };\n\n return {\n type: \"element\",\n tagName: \"div\",\n properties: {\n className: [\"ox-youtube\"],\n style: `aspect-ratio: ${options.aspectRatio};`,\n },\n children: [iframe],\n };\n}\n\n/**\n * Rehype plugin to transform YouTube components.\n */\nfunction rehypeYouTube(options: Required<YouTubeOptions>) {\n return (tree: Root) => {\n const visit = (node: Root | Element) => {\n if (\"children\" in node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n\n if (child.type === \"element\") {\n // Check for <YouTube> component\n if (child.tagName.toLowerCase() === \"youtube\") {\n const id = getAttribute(child, \"id\");\n const url = getAttribute(child, \"url\");\n const title = getAttribute(child, \"title\");\n const start = getAttribute(child, \"start\");\n\n // Extract video ID from id or url attribute\n const videoId = id ? extractVideoId(id) : url ? extractVideoId(url) : null;\n\n if (videoId) {\n const youtubeElement = createYouTubeElement(videoId, options, title, start);\n node.children[i] = youtubeElement;\n }\n } else {\n visit(child);\n }\n }\n }\n }\n };\n\n visit(tree);\n };\n}\n\n/**\n * Transform YouTube components in HTML.\n */\nexport async function transformYouTube(html: string, options?: YouTubeOptions): Promise<string> {\n const mergedOptions = { ...defaultOptions, ...options };\n\n const result = await unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeYouTube, mergedOptions)\n .use(rehypeStringify)\n .process(html);\n\n return String(result);\n}\n"],"mappings":";;;;;;;;;;AAuBA,MAAM,iBAA2C;CAC/C,iBAAiB;CACjB,aAAa;CACb,iBAAiB;CACjB,UAAU;CACX;;;;AAKD,SAAS,aAAa,IAAa,MAAkC;CACnE,MAAM,QAAQ,GAAG,aAAa;CAC9B,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,MAAM,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAK,IAAI;;;;;AAOlD,SAAgB,eAAe,OAA8B;CAE3D,IAAI,sBAAsB,KAAK,MAAM,EACnC,OAAO;CAST,KAAK,MAAM,WAAW,CAJpB,sGACA,4CAG4B,EAAE;EAC9B,MAAM,QAAQ,MAAM,MAAM,QAAQ;EAClC,IAAI,OAAO,OAAO,MAAM;;CAG1B,OAAO;;;;;AAMT,SAAS,cACP,SACA,SACA,QACQ;CACR,MAAM,SAAS,QAAQ,kBAAkB,6BAA6B;CACtE,MAAM,MAAM,IAAI,IAAI,WAAW,OAAO,SAAS,UAAU;CAGzD,IAAI,QACF,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAC/C,IAAI,aAAa,IAAI,KAAK,MAAM;CAIpC,OAAO,IAAI,UAAU;;;;;AAMvB,SAAS,qBACP,SACA,SACA,OACA,OACS;CACT,MAAM,SAAiC,EAAE;CACzC,IAAI,OACF,OAAO,QAAQ;CAejB,MAAM,SAAkB;EACtB,MAAM;EACN,SAAS;EACT,YAAY;GAZZ,KAHe,cAAc,SAAS,SAAS,OAGlC;GACb,OAAO,SAAS,iBAAiB;GACjC,OACE;GACF,gBAAgB;GAChB,iBAAiB,QAAQ,mBAAmB,KAAA;GAC5C,SAAS,QAAQ,WAAW,SAAS,KAAA;GAMd;EACvB,UAAU,EAAE;EACb;CAED,OAAO;EACL,MAAM;EACN,SAAS;EACT,YAAY;GACV,WAAW,CAAC,aAAa;GACzB,OAAO,iBAAiB,QAAQ,YAAY;GAC7C;EACD,UAAU,CAAC,OAAO;EACnB;;;;;AAMH,SAAS,cAAc,SAAmC;CACxD,QAAQ,SAAe;EACrB,MAAM,SAAS,SAAyB;GACtC,IAAI,cAAc,MAChB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;IAC7C,MAAM,QAAQ,KAAK,SAAS;IAE5B,IAAI,MAAM,SAAS,WAEjB,IAAI,MAAM,QAAQ,aAAa,KAAK,WAAW;KAC7C,MAAM,KAAK,aAAa,OAAO,KAAK;KACpC,MAAM,MAAM,aAAa,OAAO,MAAM;KACtC,MAAM,QAAQ,aAAa,OAAO,QAAQ;KAC1C,MAAM,QAAQ,aAAa,OAAO,QAAQ;KAG1C,MAAM,UAAU,KAAK,eAAe,GAAG,GAAG,MAAM,eAAe,IAAI,GAAG;KAEtE,IAAI,SAAS;MACX,MAAM,iBAAiB,qBAAqB,SAAS,SAAS,OAAO,MAAM;MAC3E,KAAK,SAAS,KAAK;;WAGrB,MAAM,MAAM;;;EAOtB,MAAM,KAAK;;;;;;AAOf,eAAsB,iBAAiB,MAAc,SAA2C;CAC9F,MAAM,gBAAgB;EAAE,GAAG;EAAgB,GAAG;EAAS;CAEvD,MAAM,SAAS,MAAM,SAAS,CAC3B,IAAI,aAAa,EAAE,UAAU,MAAM,CAAC,CACpC,IAAI,eAAe,cAAc,CACjC,IAAI,gBAAgB,CACpB,QAAQ,KAAK;CAEhB,OAAO,OAAO,OAAO"}
1
+ {"version":3,"file":"youtube2.mjs","names":[],"sources":["../src/plugins/youtube.ts"],"sourcesContent":["/**\n * YouTube Plugin - Privacy-enhanced iframe embedding\n *\n * Transforms <YouTube> components into responsive iframe embeds using\n * youtube-nocookie.com for enhanced privacy.\n *\n * The HTML rewrite is performed in Rust (`transformYoutubeEmbeds` in\n * @ox-content/napi), replacing the previous rehype parse/stringify\n * round-trip. This module keeps the public TS surface and a cheap marker\n * check so pages without a `<youtube>` element never cross the NAPI boundary.\n */\n\nimport { importNapiModule } from \"../napi\";\n\nexport interface YouTubeOptions {\n /** Use privacy-enhanced mode (youtube-nocookie.com). Default: true */\n privacyEnhanced?: boolean;\n /** Default aspect ratio. Default: \"16/9\" */\n aspectRatio?: string;\n /** Allow fullscreen. Default: true */\n allowFullscreen?: boolean;\n /** Lazy load iframe. Default: true */\n lazyLoad?: boolean;\n}\n\n/**\n * Extract YouTube video ID from various URL formats.\n */\nexport function extractVideoId(input: string): string | null {\n // Already a video ID (11 characters, alphanumeric + _ -)\n if (/^[a-zA-Z0-9_-]{11}$/.test(input)) {\n return input;\n }\n\n // Full URL patterns\n const patterns = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/|youtube\\.com\\/v\\/)([a-zA-Z0-9_-]{11})/,\n /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]{11})/,\n ];\n\n for (const pattern of patterns) {\n const match = input.match(pattern);\n if (match) return match[1];\n }\n\n return null;\n}\n\n/**\n * Transform YouTube components in HTML.\n */\nexport async function transformYouTube(html: string, options?: YouTubeOptions): Promise<string> {\n // Cheap marker check: skip the NAPI call entirely when there's no\n // `<youtube>` element (the common case). The Rust side guards the same way,\n // but short-circuiting here avoids marshalling the whole document across\n // the boundary.\n if (!/<youtube/i.test(html)) {\n return html;\n }\n\n const mod = await importNapiModule();\n return mod.transformYoutubeEmbeds(html, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,SAAgB,eAAe,OAA8B;CAE3D,IAAI,sBAAsB,KAAK,MAAM,EACnC,OAAO;CAST,KAAK,MAAM,WAAW,CAJpB,sGACA,4CAG4B,EAAE;EAC9B,MAAM,QAAQ,MAAM,MAAM,QAAQ;EAClC,IAAI,OAAO,OAAO,MAAM;;CAG1B,OAAO;;;;;AAMT,eAAsB,iBAAiB,MAAc,SAA2C;CAK9F,IAAI,CAAC,YAAY,KAAK,KAAK,EACzB,OAAO;CAIT,QAAO,MADW,kBAAkB,EACzB,uBAAuB,MAAM,QAAQ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ox-content/vite-plugin",
3
- "version": "2.16.0",
3
+ "version": "2.26.0",
4
4
  "description": "Vite plugin for Ox Content - High-performance Markdown processing with Environment API",
5
5
  "keywords": [
6
6
  "environment-api",
@@ -14,13 +14,14 @@
14
14
  "author": "ubugeeei",
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "https://github.com/ubugeeei/ox-content.git",
17
+ "url": "https://github.com/ubugeeei-prod/ox-content.git",
18
18
  "directory": "npm/vite-plugin-ox-content"
19
19
  },
20
20
  "bin": {
21
- "ox-content-migrate-vitepress": "./dist/vitepress-cli.mjs"
21
+ "ox-content-migrate-vitepress": "./bin/migrate-vitepress.mjs"
22
22
  },
23
23
  "files": [
24
+ "bin",
24
25
  "dist"
25
26
  ],
26
27
  "type": "module",
@@ -57,7 +58,7 @@
57
58
  "shiki": "^4.0.2",
58
59
  "typescript": "^6.0.3",
59
60
  "unified": "^11.0.5",
60
- "@ox-content/napi": "2.16.0"
61
+ "@ox-content/napi": "2.26.0"
61
62
  },
62
63
  "devDependencies": {
63
64
  "@playwright/test": "^1.60.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"mermaid.mjs","names":[],"sources":["../src/napi.ts","../src/plugins/mermaid.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\ntype NapiModule = typeof import(\"@ox-content/napi\");\nconst requireNapi = createRequire(import.meta.url);\n\nfunction getDefaultExport(value: unknown): object | undefined {\n if (!value || typeof value !== \"object\" || !(\"default\" in value)) {\n return undefined;\n }\n\n const defaultExport = value.default;\n return defaultExport && typeof defaultExport === \"object\" ? defaultExport : undefined;\n}\n\nfunction normalizeNapiModule(mod: NapiModule): NapiModule {\n const defaultExport = getDefaultExport(mod);\n return defaultExport\n ? ({\n ...defaultExport,\n ...mod,\n } as NapiModule)\n : mod;\n}\n\nexport async function importNapiModule(): Promise<NapiModule> {\n return normalizeNapiModule((await import(\"@ox-content/napi\")) as NapiModule);\n}\n\nlet syncNapiModule: NapiModule | null | undefined;\n\nexport function importNapiModuleSync(): NapiModule {\n if (syncNapiModule) {\n return syncNapiModule;\n }\n\n if (syncNapiModule === null) {\n throw new Error(\n \"[ox-content] @ox-content/napi is required. Please ensure the NAPI module is built.\",\n );\n }\n\n try {\n const mod = requireNapi(\"@ox-content/napi\") as NapiModule;\n syncNapiModule = normalizeNapiModule(mod);\n return syncNapiModule;\n } catch {\n syncNapiModule = null;\n throw new Error(\n \"[ox-content] @ox-content/napi is required. Please ensure the NAPI module is built.\",\n );\n }\n}\n","/**\n * Mermaid Plugin - Native Rust renderer via NAPI\n *\n * Renders mermaid code blocks to SVG using the native Rust renderer\n * via NAPI. Delegates to the NAPI `transformMermaid` function which\n * extracts mermaid code blocks from HTML and renders them using mmdc.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { importNapiModule } from \"../napi\";\n\nexport interface MermaidOptions {\n /** Mermaid theme. Default: \"neutral\" */\n theme?: \"default\" | \"dark\" | \"forest\" | \"neutral\" | \"base\";\n}\n\n/** Cached NAPI bindings */\nlet napiBindings: {\n transformMermaid: (html: string, mmdcPath: string) => { html: string; errors: string[] };\n} | null = null;\n\nlet napiLoadAttempted = false;\n\nasync function loadNapi() {\n if (napiLoadAttempted) return napiBindings;\n napiLoadAttempted = true;\n try {\n const binding = (await importNapiModule()) as unknown as NonNullable<typeof napiBindings>;\n if (typeof binding.transformMermaid !== \"function\") {\n napiBindings = null;\n return null;\n }\n napiBindings = binding;\n return binding;\n } catch {\n napiBindings = null;\n return null;\n }\n}\n\nlet cachedMmdcPath: string | null | undefined;\n\nfunction resolveMmdcPath(): string | null {\n if (cachedMmdcPath !== undefined) return cachedMmdcPath;\n\n // 1. Resolve via import.meta.resolve (works in pnpm strict mode)\n // @mermaid-js/mermaid-cli exports ./src/index.js; cli.js is in the same dir\n try {\n const entry = import.meta.resolve(\"@mermaid-js/mermaid-cli\");\n const cliPath = fileURLToPath(new URL(\"./cli.js\", entry));\n if (existsSync(cliPath)) {\n cachedMmdcPath = cliPath;\n return cachedMmdcPath;\n }\n } catch {\n // not resolvable\n }\n\n // 2. Fallback: node_modules/.bin/mmdc relative to cwd\n const binPath = join(process.cwd(), \"node_modules\", \".bin\", \"mmdc\");\n if (existsSync(binPath)) {\n cachedMmdcPath = binPath;\n return cachedMmdcPath;\n }\n\n cachedMmdcPath = null;\n return null;\n}\n\n/**\n * Transforms mermaid code blocks in HTML to rendered SVG diagrams.\n * Uses the native Rust NAPI transformMermaid function.\n */\nexport async function transformMermaidStatic(\n html: string,\n _options?: MermaidOptions,\n): Promise<string> {\n const napi = await loadNapi();\n if (!napi) {\n return html;\n }\n\n const mmdcPath = resolveMmdcPath();\n if (!mmdcPath) {\n console.warn(\"[ox-content] mmdc not found, skipping mermaid rendering\");\n return html;\n }\n\n try {\n const result = napi.transformMermaid(html, mmdcPath);\n for (const error of result.errors) {\n console.warn(\"[ox-content] Mermaid render error:\", error);\n }\n return result.html;\n } catch (err) {\n console.warn(\"[ox-content] Mermaid transform error:\", err);\n return html;\n }\n}\n\n/**\n * @deprecated No longer used. Mermaid rendering is now done at build time via NAPI.\n */\nexport const mermaidClientScript = \"\";\n"],"mappings":";;;;;AAGA,MAAM,cAAc,cAAc,OAAO,KAAK,IAAI;AAElD,SAAS,iBAAiB,OAAoC;CAC5D,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,aAAa,QACxD;CAGF,MAAM,gBAAgB,MAAM;CAC5B,OAAO,iBAAiB,OAAO,kBAAkB,WAAW,gBAAgB,KAAA;;AAG9E,SAAS,oBAAoB,KAA6B;CACxD,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,OAAO,gBACF;EACC,GAAG;EACH,GAAG;EACJ,GACD;;AAGN,eAAsB,mBAAwC;CAC5D,OAAO,oBAAqB,MAAM,OAAO,oBAAmC;;AAG9E,IAAI;AAEJ,SAAgB,uBAAmC;CACjD,IAAI,gBACF,OAAO;CAGT,IAAI,mBAAmB,MACrB,MAAM,IAAI,MACR,qFACD;CAGH,IAAI;EAEF,iBAAiB,oBADL,YAAY,mBACgB,CAAC;EACzC,OAAO;SACD;EACN,iBAAiB;EACjB,MAAM,IAAI,MACR,qFACD;;;;;;;;;;;;;AC9BL,IAAI,eAEO;AAEX,IAAI,oBAAoB;AAExB,eAAe,WAAW;CACxB,IAAI,mBAAmB,OAAO;CAC9B,oBAAoB;CACpB,IAAI;EACF,MAAM,UAAW,MAAM,kBAAkB;EACzC,IAAI,OAAO,QAAQ,qBAAqB,YAAY;GAClD,eAAe;GACf,OAAO;;EAET,eAAe;EACf,OAAO;SACD;EACN,eAAe;EACf,OAAO;;;AAIX,IAAI;AAEJ,SAAS,kBAAiC;CACxC,IAAI,mBAAmB,KAAA,GAAW,OAAO;CAIzC,IAAI;EACF,MAAM,QAAQ,OAAO,KAAK,QAAQ,0BAA0B;EAC5D,MAAM,UAAU,cAAc,IAAI,IAAI,YAAY,MAAM,CAAC;EACzD,IAAI,WAAW,QAAQ,EAAE;GACvB,iBAAiB;GACjB,OAAO;;SAEH;CAKR,MAAM,UAAU,KAAK,QAAQ,KAAK,EAAE,gBAAgB,QAAQ,OAAO;CACnE,IAAI,WAAW,QAAQ,EAAE;EACvB,iBAAiB;EACjB,OAAO;;CAGT,iBAAiB;CACjB,OAAO;;;;;;AAOT,eAAsB,uBACpB,MACA,UACiB;CACjB,MAAM,OAAO,MAAM,UAAU;CAC7B,IAAI,CAAC,MACH,OAAO;CAGT,MAAM,WAAW,iBAAiB;CAClC,IAAI,CAAC,UAAU;EACb,QAAQ,KAAK,0DAA0D;EACvE,OAAO;;CAGT,IAAI;EACF,MAAM,SAAS,KAAK,iBAAiB,MAAM,SAAS;EACpD,KAAK,MAAM,SAAS,OAAO,QACzB,QAAQ,KAAK,sCAAsC,MAAM;EAE3D,OAAO,OAAO;UACP,KAAK;EACZ,QAAQ,KAAK,yCAAyC,IAAI;EAC1D,OAAO;;;;;;AAOX,MAAa,sBAAsB"}