@the-forge-flow/visual-explainer-pi 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MonsieurBarti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ <div align="center">
2
+ <img src="https://raw.githubusercontent.com/MonsieurBarti/The-Forge-Flow-CC/refs/heads/main/assets/forge-banner.png" alt="The Forge Flow - Visual Explainer PI" width="100%">
3
+
4
+ <h1>🎨 Visual Explainer PI</h1>
5
+
6
+ <p>
7
+ <strong>PI extension for generating beautiful HTML visualizations</strong>
8
+ </p>
9
+
10
+ <p>
11
+ <a href="https://github.com/MonsieurBarti/visual-explainer-pi/actions/workflows/ci.yml">
12
+ <img src="https://img.shields.io/github/actions/workflow/status/MonsieurBarti/visual-explainer-pi/ci.yml?label=CI&style=flat-square" alt="CI Status">
13
+ </a>
14
+ <a href="https://www.npmjs.com/package/@the-forge-flow/visual-explainer-pi">
15
+ <img src="https://img.shields.io/npm/v/@the-forge-flow/visual-explainer-pi?style=flat-square" alt="npm version">
16
+ </a>
17
+ <a href="LICENSE">
18
+ <img src="https://img.shields.io/github/license/MonsieurBarti/visual-explainer-pi?style=flat-square" alt="License">
19
+ </a>
20
+ </p>
21
+ </div>
22
+
23
+ ---
24
+
25
+ Turns complex terminal output into styled, self-contained HTML pages for diagrams, architecture overviews, diff reviews, data tables, and project recaps. Built on top of the design system from [nicobailon/visual-explainer](https://github.com/nicobailon/visual-explainer).
26
+
27
+ ## ✨ Features
28
+
29
+ - **🎨 11 Visual Types**: architecture, flowchart, sequence, ER, state, table, diff, plan, timeline, dashboard, slides
30
+ - **🖌️ 8 Aesthetic Palettes**: blueprint, editorial, paper, terminal, dracula, nord, solarized, gruvbox
31
+ - **📄 Self-Contained HTML**: single file with embedded CSS/JS, opens in any browser
32
+ - **🧜 Mermaid Integration**: automatic diagrams with zoom/pan controls
33
+ - **📊 Data Tables**: responsive tables with sticky headers and status indicators
34
+ - **🤖 PI-native**: seamless integration with PI's tool system, abort-signal aware, output truncated to PI's limits
35
+
36
+ ## 📦 Installation
37
+
38
+ ### 1. Install the extension with `pi install`
39
+
40
+ PI discovers the extension automatically once installed as a pi package. By default this installs globally into `~/.pi/agent/`; pass `-l` to install into the current project (`.pi/`) instead.
41
+
42
+ **From npm (recommended):**
43
+
44
+ ```bash
45
+ pi install npm:@the-forge-flow/visual-explainer-pi
46
+ ```
47
+
48
+ **From GitHub (tracks `main`):**
49
+
50
+ ```bash
51
+ pi install git:github.com/MonsieurBarti/visual-explainer-pi
52
+ ```
53
+
54
+ **Pin to a specific version:**
55
+
56
+ ```bash
57
+ # npm — pin to a published version
58
+ pi install npm:@the-forge-flow/visual-explainer-pi@0.1.0
59
+
60
+ # git — pin to a release tag
61
+ pi install git:github.com/MonsieurBarti/visual-explainer-pi@visual-explainer-pi-v0.1.0
62
+ ```
63
+
64
+ Then reload PI with `/reload` (or restart it). On the next session you should see a notification that `Visual explainer ready`.
65
+
66
+ **Manage installed packages:**
67
+
68
+ ```bash
69
+ pi list # show installed packages
70
+ pi update # update non-pinned packages
71
+ pi remove npm:@the-forge-flow/visual-explainer-pi
72
+ pi config # enable/disable individual extensions, skills, prompts, themes
73
+ ```
74
+
75
+ > For project-scoped installs, package filtering, and more, see the [pi packages doc](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/packages.md).
76
+
77
+ ## 🚀 Usage
78
+
79
+ The extension registers a single `generate_visual` tool that the LLM can call directly. It also exposes two slash commands to reopen recently generated files.
80
+
81
+ ### `generate_visual`
82
+
83
+ ```typescript
84
+ // Render a technical architecture diagram
85
+ generate_visual({
86
+ type: "architecture",
87
+ title: "Auth System Overview",
88
+ aesthetic: "blueprint",
89
+ content: {
90
+ sections: [
91
+ { label: "Edge", content: "<div class=\"inner-card\">API Gateway</div>" },
92
+ { label: "Core", content: "<div class=\"inner-card\">Auth Service</div>", isHero: true },
93
+ ],
94
+ },
95
+ });
96
+
97
+ // Render a Mermaid flowchart
98
+ generate_visual({
99
+ type: "flowchart",
100
+ title: "Data Pipeline",
101
+ aesthetic: "editorial",
102
+ content: "graph LR; A[Ingest] --> B[Transform] --> C[Store]",
103
+ });
104
+
105
+ // Render a comparison table
106
+ generate_visual({
107
+ type: "table",
108
+ title: "API Endpoints",
109
+ aesthetic: "paper",
110
+ content: [
111
+ { method: "GET", path: "/users", status: "stable" },
112
+ { method: "POST", path: "/users", status: "beta" },
113
+ ],
114
+ });
115
+ ```
116
+
117
+ ### Tool Parameters
118
+
119
+ | Parameter | Type | Description |
120
+ |-----------|------|-------------|
121
+ | `type` | enum | Visual type: `architecture`, `flowchart`, `sequence`, `er`, `state`, `table`, `diff`, `plan`, `timeline`, `dashboard`, `slides`, `mermaid_custom` |
122
+ | `content` | string \| array | Raw content (mermaid syntax, markdown) or structured data rows |
123
+ | `title` | string | Title for the visualization |
124
+ | `aesthetic` | enum | Palette: `blueprint`, `editorial`, `paper`, `terminal`, `dracula`, `nord`, `solarized`, `gruvbox` |
125
+ | `theme` | enum | `light`, `dark`, or `auto` |
126
+ | `filename` | string | Optional output filename (auto-generated if omitted) |
127
+
128
+ ### Slash Commands
129
+
130
+ - `/visual-reopen <n>` — re-open a recently generated visual by index (1-10)
131
+ - `/visual-list` — list recently generated visuals
132
+
133
+ ### Output Location
134
+
135
+ Generated HTML files are saved to `~/.agent/diagrams/` and automatically opened in your default browser.
136
+
137
+ ## 🏗️ Architecture
138
+
139
+ ```
140
+ ┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
141
+ │ LLM Request│────▶│ generate_visual │────▶│ Browser │
142
+ │ (via PI) │ │ (defineTool) │ │ (HTML) │
143
+ └─────────────┘ └────────┬─────────┘ └─────────────┘
144
+
145
+ ┌────────▼─────────┐
146
+ │ Palette + Template│
147
+ │ HTML Generation │
148
+ │ File Written │
149
+ └───────────────────┘
150
+ ```
151
+
152
+ Every tool call:
153
+
154
+ 1. Validates parameters (`type`, `content`, `aesthetic`, `theme`).
155
+ 2. Picks a template (`architecture`, `mermaid`, `data-table`) and a palette.
156
+ 3. Generates a self-contained HTML document with embedded CSS/JS.
157
+ 4. Truncates output to PI's ~50KB/2000-line limit via `truncateHead`.
158
+ 5. Writes the file to `~/.agent/diagrams/` and opens it in the default browser.
159
+
160
+ ## 🎨 Design Principles
161
+
162
+ Inherited from [nicobailon/visual-explainer](https://github.com/nicobailon/visual-explainer):
163
+
164
+ - **Typography First** — distinctive font pairings (never Inter as primary)
165
+ - **Warm Palettes** — terracotta, sage, teal, rose (never indigo/violet/pink defaults)
166
+ - **Depth Hierarchy** — hero → elevated → default → recessed visual weight
167
+ - **No AI Slop** — forbidden: gradient text, emoji headers, neon glows, animated shadows
168
+ - **Accessibility** — respects `prefers-reduced-motion`, semantic HTML tables
169
+
170
+ ## 🧪 Development
171
+
172
+ ```bash
173
+ # Install dependencies (also wires lefthook git hooks)
174
+ bun install
175
+
176
+ # Run tests
177
+ bun test
178
+
179
+ # Lint & format
180
+ bun run check
181
+
182
+ # Typecheck
183
+ bun run typecheck
184
+
185
+ # Build for publish
186
+ bun run build
187
+ ```
188
+
189
+ ## 📁 Project Structure
190
+
191
+ ```
192
+ src/
193
+ ├── index.ts # Extension entry, tool registration, state
194
+ ├── types.ts # TypeScript type definitions
195
+ ├── templates/
196
+ │ ├── architecture.ts # CSS Grid card layout
197
+ │ ├── data-table.ts # HTML table template
198
+ │ ├── mermaid.ts # Mermaid diagram wrapper
199
+ │ └── shared.ts # Palettes, fonts, CSS variables
200
+ └── utils/
201
+ ├── browser-open.ts # Cross-platform browser launcher
202
+ ├── file-writer.ts # Temp file + state management
203
+ └── validators.ts # Input validation
204
+ ```
205
+
206
+ ## 🤝 Contributing
207
+
208
+ 1. Fork the repository
209
+ 2. Create your feature branch (`git checkout -b feat/amazing`)
210
+ 3. Commit with conventional commits (`git commit -m "feat: add something"`)
211
+ 4. Push to the branch (`git push origin feat/amazing`)
212
+ 5. Open a Pull Request
213
+
214
+ ## 🙏 Credits
215
+
216
+ This extension re-uses the visual design system (palettes, typography, layout primitives) from [**nicobailon/visual-explainer**](https://github.com/nicobailon/visual-explainer) by [Nico Bailon](https://github.com/nicobailon). The PI integration layer (tool API, validators, templates re-implementation, tests) is original work. Huge thanks to Nico for the beautiful aesthetic foundation.
217
+
218
+ ## 📜 License
219
+
220
+ MIT © [MonsieurBarti](https://github.com/MonsieurBarti)
221
+
222
+ ---
223
+
224
+ <div align="center">
225
+ <sub>Built with ⚡ by <a href="https://github.com/MonsieurBarti">MonsieurBarti</a></sub>
226
+ </div>
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Visual Explainer PI Extension
3
+ *
4
+ * Generates beautiful, self-contained HTML pages for diagrams, architecture,
5
+ * diff reviews, plan audits, data tables, and project recaps.
6
+ *
7
+ * Based on nicobailon/visual-explainer design principles.
8
+ */
9
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ /**
11
+ * Main extension export
12
+ */
13
+ export default function visualExplainerExtension(pi: ExtensionAPI): void;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA+JlE;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAAC,EAAE,EAAE,YAAY,QA6IhE"}
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Visual Explainer PI Extension
3
+ *
4
+ * Generates beautiful, self-contained HTML pages for diagrams, architecture,
5
+ * diff reviews, plan audits, data tables, and project recaps.
6
+ *
7
+ * Based on nicobailon/visual-explainer design principles.
8
+ */
9
+ import { StringEnum } from "@mariozechner/pi-ai";
10
+ import { defineTool, truncateHead } from "@mariozechner/pi-coding-agent";
11
+ import { Type } from "@sinclair/typebox";
12
+ import { generateArchitectureTemplate } from "./templates/architecture.js";
13
+ import { generateTableTemplate } from "./templates/data-table.js";
14
+ import { generateMermaidTemplate } from "./templates/mermaid.js";
15
+ import { openInBrowser } from "./utils/browser-open.js";
16
+ import { createInitialState, writeHtmlFile } from "./utils/file-writer.js";
17
+ import { generateDefaultFilename, sanitizeFilename, validateParams } from "./utils/validators.js";
18
+ // Extension state
19
+ const state = createInitialState();
20
+ /**
21
+ * Generate visual HTML based on type and content
22
+ */
23
+ async function generateVisual(params, pi) {
24
+ // Determine if dark mode
25
+ const isDark = params.theme === "dark" || (params.theme === "auto" && prefersDarkMode());
26
+ // Generate HTML based on type
27
+ let html;
28
+ switch (params.type) {
29
+ case "architecture":
30
+ html = generateArchitectureTemplate(params.title, params.content, params.aesthetic ?? "blueprint", isDark);
31
+ break;
32
+ case "flowchart":
33
+ case "sequence":
34
+ case "er":
35
+ case "state":
36
+ case "mermaid_custom":
37
+ html = generateMermaidTemplate(params.title, {
38
+ mermaidSyntax: params.content,
39
+ caption: `${params.type} diagram`,
40
+ }, params.aesthetic ?? "blueprint", isDark);
41
+ break;
42
+ case "table":
43
+ html = generateTableTemplate(params.title, params.content, params.aesthetic ?? "blueprint", isDark);
44
+ break;
45
+ case "diff":
46
+ // For now, treat diff as architecture with special handling
47
+ html = generateArchitectureTemplate(params.title, {
48
+ sections: [
49
+ {
50
+ label: "Changes Overview",
51
+ content: `<div class="inner-card"><div class="title">${escapeHtml(params.content).substring(0, 100)}...</div></div>`,
52
+ },
53
+ ],
54
+ }, params.aesthetic ?? "blueprint", isDark);
55
+ break;
56
+ case "plan":
57
+ html = generateArchitectureTemplate(params.title, {
58
+ sections: [
59
+ {
60
+ label: "Implementation Plan",
61
+ content: `<div class="callout">${escapeHtml(params.content).substring(0, 200)}...</div>`,
62
+ isHero: true,
63
+ },
64
+ ],
65
+ }, params.aesthetic ?? "blueprint", isDark);
66
+ break;
67
+ case "timeline":
68
+ case "dashboard":
69
+ case "slides":
70
+ // Fallback to architecture template for now
71
+ console.warn(`Type ${params.type} not fully implemented, using architecture template`);
72
+ html = generateArchitectureTemplate(params.title, {
73
+ sections: [
74
+ {
75
+ label: params.type,
76
+ content: `<div class="inner-card"><div class="title">Content</div><div class="desc">${escapeHtml(String(params.content)).substring(0, 200)}...</div></div>`,
77
+ },
78
+ ],
79
+ }, params.aesthetic ?? "blueprint", isDark);
80
+ break;
81
+ default:
82
+ throw new Error(`Unsupported visual type: ${params.type}`);
83
+ }
84
+ // Safety check: truncate if too large
85
+ const truncated = truncateHead(html, { maxBytes: 50000, maxLines: 2000 });
86
+ // Generate filename
87
+ const filename = params.filename
88
+ ? sanitizeFilename(params.filename)
89
+ : generateDefaultFilename(params.title);
90
+ // Write file
91
+ const filePath = await writeHtmlFile(filename, truncated.content, state);
92
+ // Open in browser
93
+ await openInBrowser(filePath, pi);
94
+ // Generate preview snippet (first 500 chars of content summary)
95
+ const previewSnippet = `Generated ${params.type} visualization: ${params.title}`;
96
+ return {
97
+ filePath,
98
+ previewSnippet,
99
+ url: `file://${filePath}`,
100
+ };
101
+ }
102
+ function prefersDarkMode() {
103
+ // This runs on the server, so we default to light
104
+ // In a real implementation, this could check system preferences
105
+ return false;
106
+ }
107
+ function escapeHtml(str) {
108
+ return str
109
+ .replace(/&/g, "&amp;")
110
+ .replace(/</g, "&lt;")
111
+ .replace(/>/g, "&gt;")
112
+ .replace(/"/g, "&quot;")
113
+ .replace(/'/g, "&#039;");
114
+ }
115
+ /**
116
+ * Main extension export
117
+ */
118
+ export default function visualExplainerExtension(pi) {
119
+ // Initialize on session start
120
+ pi.on("session_start", async (_event, ctx) => {
121
+ if (ctx.hasUI) {
122
+ ctx.ui.notify("Visual explainer ready", "info");
123
+ }
124
+ });
125
+ // Register generate_visual tool
126
+ const generateVisualTool = defineTool({
127
+ name: "generate_visual",
128
+ label: "Generate Visual",
129
+ description: "Generate beautiful, self-contained HTML pages for diagrams, architecture overviews, diff reviews, data tables, and visual explanations. Opens result in browser. Based on nicobailon/visual-explainer design principles.",
130
+ promptSnippet: "Create a visual diagram/architecture/table",
131
+ promptGuidelines: [
132
+ "Use this tool when the user asks for diagrams, architecture views, or data tables",
133
+ "Proactively use for complex tables (4+ rows, 3+ columns) instead of ASCII",
134
+ "Choose aesthetic based on context: blueprint (technical), editorial (formal), paper (warm), terminal (retro), dracula/nord/solarized/gruvbox (IDE themes)",
135
+ "Content can be structured data (for tables) or mermaid syntax (for diagrams)",
136
+ ],
137
+ parameters: Type.Object({
138
+ type: StringEnum([
139
+ "architecture",
140
+ "flowchart",
141
+ "sequence",
142
+ "er",
143
+ "state",
144
+ "table",
145
+ "diff",
146
+ "plan",
147
+ "timeline",
148
+ "dashboard",
149
+ "slides",
150
+ "mermaid_custom",
151
+ ], { description: "Type of visualization to generate" }),
152
+ content: Type.Union([
153
+ Type.String({ description: "Raw content (mermaid syntax, markdown)" }),
154
+ Type.Array(Type.Record(Type.String(), Type.Unknown()), {
155
+ description: "Structured data rows for tables",
156
+ }),
157
+ ]),
158
+ title: Type.String({ description: "Title for the visualization" }),
159
+ aesthetic: Type.Optional(StringEnum([
160
+ "blueprint",
161
+ "editorial",
162
+ "paper",
163
+ "terminal",
164
+ "dracula",
165
+ "nord",
166
+ "solarized",
167
+ "gruvbox",
168
+ ], { description: "Visual aesthetic/theme palette" })),
169
+ theme: Type.Optional(StringEnum(["light", "dark", "auto"], {
170
+ description: "Color theme mode",
171
+ })),
172
+ filename: Type.Optional(Type.String({ description: "Output filename (auto-generated if omitted)" })),
173
+ }),
174
+ async execute(_toolCallId, rawParams, _signal, _onUpdate, _ctx) {
175
+ // Validate parameters
176
+ const params = validateParams(rawParams);
177
+ // Generate visual
178
+ const result = await generateVisual(params, pi);
179
+ return {
180
+ content: [
181
+ {
182
+ type: "text",
183
+ text: `${result.previewSnippet}\n\nOpened in browser: ${result.url}\nFile: ${result.filePath}`,
184
+ },
185
+ ],
186
+ details: {
187
+ type: params.type,
188
+ title: params.title,
189
+ aesthetic: params.aesthetic,
190
+ theme: params.theme,
191
+ filePath: result.filePath,
192
+ url: result.url,
193
+ },
194
+ };
195
+ },
196
+ });
197
+ pi.registerTool(generateVisualTool);
198
+ // Register reopen command for recent files
199
+ pi.registerCommand("visual-reopen", {
200
+ description: "Re-open a recently generated visual by index (1-10)",
201
+ handler: async (args, ctx) => {
202
+ const index = Number.parseInt(args, 10) - 1;
203
+ if (index < 0 || index >= state.recentFiles.length) {
204
+ if (ctx.hasUI) {
205
+ ctx.ui.notify(`Invalid index. Use 1-${state.recentFiles.length}`, "error");
206
+ }
207
+ return;
208
+ }
209
+ const filePath = state.recentFiles[index];
210
+ await openInBrowser(filePath, pi);
211
+ if (ctx.hasUI) {
212
+ ctx.ui.notify(`Re-opened: ${filePath}`, "info");
213
+ }
214
+ },
215
+ });
216
+ // Register list command
217
+ pi.registerCommand("visual-list", {
218
+ description: "List recently generated visuals",
219
+ handler: async (_args, ctx) => {
220
+ if (state.recentFiles.length === 0) {
221
+ if (ctx.hasUI) {
222
+ ctx.ui.notify("No recent visuals", "warning");
223
+ }
224
+ return;
225
+ }
226
+ const list = state.recentFiles.map((f, i) => `${i + 1}. ${f}`).join("\n");
227
+ if (ctx.hasUI) {
228
+ ctx.ui.notify(`Recent visuals:\n${list}`, "info");
229
+ }
230
+ },
231
+ });
232
+ // Cleanup on shutdown
233
+ pi.on("session_shutdown", async () => {
234
+ // Note: We don't delete the HTML files - they persist for user reference
235
+ // Only cleanup would be if we created any temp directories
236
+ state.tempDirs = [];
237
+ });
238
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Architecture template - CSS Grid card layout for text-heavy system overviews
3
+ * Based on nicobailon/visual-explainer architecture.html
4
+ */
5
+ import type { Aesthetic } from "../types.js";
6
+ export interface ArchitectureSection {
7
+ label: string;
8
+ labelColor?: "accent" | "green" | "orange" | "teal" | "plum";
9
+ content: string;
10
+ isHero?: boolean;
11
+ isRecessed?: boolean;
12
+ }
13
+ export interface ArchitectureContent {
14
+ sections: ArchitectureSection[];
15
+ flowArrows?: {
16
+ label: string;
17
+ }[];
18
+ threeColumn?: ArchitectureSection[];
19
+ pipeline?: {
20
+ steps: {
21
+ num: string;
22
+ name: string;
23
+ detail: string;
24
+ color: string;
25
+ }[];
26
+ legend?: {
27
+ color: string;
28
+ label: string;
29
+ }[];
30
+ };
31
+ }
32
+ export declare function generateArchitectureTemplate(title: string, content: ArchitectureContent, aesthetic: Aesthetic, isDark: boolean): string;
33
+ //# sourceMappingURL=architecture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.d.ts","sourceRoot":"","sources":["../../src/templates/architecture.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,aAAa,CAAC;AAUtD,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACpC,QAAQ,CAAC,EAAE;QACV,KAAK,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACtE,MAAM,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC5C,CAAC;CACF;AAED,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,OAAO,GACb,MAAM,CAQR"}