@takesix/mcp 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp.js ADDED
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { basename, dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { tmpdir } from "node:os";
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { RESOURCE_MIME_TYPE, registerAppResource, registerAppTool } from "@modelcontextprotocol/ext-apps/server";
10
+ import { buildIndex, formatIssues, parseDocument, visibleLinks } from "@takesix/core";
11
+ import { z } from "zod";
12
+ // src/render.ts
13
+ var escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
14
+ /**
15
+ * One self-contained HTML page: the document as JSON plus the inlined viewer.
16
+ * `<` in the JSON is escaped, so no text in the document can close the
17
+ * <script> element and inject markup.
18
+ */
19
+ function renderHtml(doc, viewerJs, viewerCss) {
20
+ const json = JSON.stringify(doc).replace(/</g, "\\u003c");
21
+ return `<!doctype html>
22
+ <html lang="en">
23
+ <head>
24
+ <meta charset="utf-8">
25
+ <meta name="viewport" content="width=device-width, initial-scale=1">
26
+ <meta name="generator" content="takesix">
27
+ <title>${escapeHtml(doc.title)}</title>
28
+ <style>html, body, #root { height: 100%; margin: 0; }</style>
29
+ <style>${viewerCss}</style>
30
+ </head>
31
+ <body>
32
+ <div id="root"></div>
33
+ <script type="application/json" id="takesix-data">${json}<\/script>
34
+ <script>${viewerJs}<\/script>
35
+ </body>
36
+ </html>
37
+ `;
38
+ }
39
+ // src/summary.ts
40
+ /** One line about the shape of a valid document, to help an agent judge it. */
41
+ function summarize(doc) {
42
+ const index = buildIndex(doc);
43
+ const depth = Math.max(0, ...[...index.values()].map((e) => e.depth + 1));
44
+ const links = doc.links?.length ?? 0;
45
+ const top = visibleLinks(doc, null, index);
46
+ const merged = top.filter((l) => !l.direct).length;
47
+ return `${index.size} blocks on ${depth} level${depth === 1 ? "" : "s"}, ${links} link${links === 1 ? "" : "s"}; top level shows ${top.length} arrow${top.length === 1 ? "" : "s"}` + (merged ? ` (${merged} merged from nested links: check they are wanted there)` : "");
48
+ }
49
+ // src/mcp/guide.ts
50
+ var PREFACE = `# TakeSix over MCP
51
+
52
+ You have the takesix MCP tools. Where the guide below says to run
53
+ \`scripts/takesix.mjs\`, use the tools instead:
54
+
55
+ - \`validate\` instead of \`takesix.mjs validate\`: pass the document (or a
56
+ \`.takesix.json\` file path) and fix every problem it lists, by its JSON path.
57
+ - \`show\` instead of \`takesix.mjs render\`: it validates, writes one
58
+ self-contained HTML file and, in apps that support MCP Apps, shows the
59
+ diagram right in the conversation.
60
+
61
+ Write the document as JSON, validate it, then show it. Reply briefly: the
62
+ diagram is the answer.
63
+
64
+ ---
65
+
66
+ `;
67
+ /** The skill's text without its YAML frontmatter, preceded by the MCP preface. */
68
+ function guideText(skillMd) {
69
+ return PREFACE + skillMd.replace(/^---\n[\s\S]*?\n---\n/, "").trim() + "\n";
70
+ }
71
+ // src/mcp/shared.ts
72
+ var VIEW_URI = "ui://takesix/viewer";
73
+ /** Key in the `show` result's _meta: the full document for the view, kept out of the model's context. */
74
+ var DOCUMENT_META_KEY = "takesix/document";
75
+ // src/mcp/server.ts
76
+ var INSTRUCTIONS = `TakeSix turns an answer into a diagram: at most six blocks per screen, and any block opens into up to six more. Use it when the user asks to explain, map or summarize something with structure (a codebase, an architecture, a plan, a process, a document) and the answer would otherwise be long prose. Call \`guide\` once before writing your first diagram, then \`validate\` and \`show\`.`;
77
+ var source = {
78
+ document: z.record(z.string(), z.unknown()).optional().describe("The takesix document as a JSON object ({ \"takesix\": \"1.0\", \"title\", \"blocks\", \"links\" })"),
79
+ file: z.string().optional().describe("Path to a .takesix.json file, instead of `document`")
80
+ };
81
+ function fail(text) {
82
+ return {
83
+ ok: false,
84
+ error: {
85
+ isError: true,
86
+ content: [{
87
+ type: "text",
88
+ text
89
+ }]
90
+ }
91
+ };
92
+ }
93
+ function load(args) {
94
+ let input = args.document;
95
+ let file;
96
+ if (input === void 0) {
97
+ if (!args.file) return fail("Pass the document as `document`, or a path to a .takesix.json file as `file`.");
98
+ file = resolve(args.file);
99
+ try {
100
+ input = readFileSync(file, "utf8");
101
+ } catch (e) {
102
+ return fail(`Cannot read ${file}: ${e.message}`);
103
+ }
104
+ }
105
+ const result = parseDocument(input);
106
+ if (!result.ok) return fail(`Invalid takesix document. Fix these and try again:\n${formatIssues(result.issues)}`);
107
+ return {
108
+ ok: true,
109
+ doc: result.document,
110
+ warnings: result.issues.length ? formatIssues(result.issues) + "\n" : "",
111
+ file
112
+ };
113
+ }
114
+ var slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60) || "diagram";
115
+ /** Next to the source file, or in a takesix folder in the temp directory. */
116
+ function htmlPath(doc, file, output) {
117
+ if (output) return resolve(output);
118
+ if (file) return join(dirname(file), basename(file).replace(/(\.takesix)?\.json$/, "") + ".html");
119
+ return join(tmpdir(), "takesix", `${slug(doc.title)}.html`);
120
+ }
121
+ /** Page for the MCP App: the viewer, waiting for the host to send a document. */
122
+ function viewHtml(assets) {
123
+ return `<!doctype html>
124
+ <html lang="en">
125
+ <head>
126
+ <meta charset="utf-8">
127
+ <meta name="viewport" content="width=device-width, initial-scale=1">
128
+ <meta name="color-scheme" content="light dark">
129
+ <title>TakeSix</title>
130
+ <style>${assets.appCss}</style>
131
+ </head>
132
+ <body>
133
+ <div id="root"></div>
134
+ <script>${assets.appJs}<\/script>
135
+ </body>
136
+ </html>
137
+ `;
138
+ }
139
+ function createServer(assets) {
140
+ const server = new McpServer({
141
+ name: "takesix",
142
+ version: assets.version
143
+ }, { instructions: INSTRUCTIONS });
144
+ server.registerTool("guide", {
145
+ title: "How to write a TakeSix diagram",
146
+ description: "The format and the rules that make a TakeSix diagram readable. Read it once before writing your first diagram in a conversation.",
147
+ annotations: { readOnlyHint: true }
148
+ }, async () => ({ content: [{
149
+ type: "text",
150
+ text: guideText(assets.skillMd)
151
+ }] }));
152
+ server.registerTool("validate", {
153
+ title: "Check a TakeSix document",
154
+ description: "Checks a TakeSix document against the format: at most 6 blocks per level, unique ids, links between existing blocks. Lists every problem with its JSON path; on success, one line about the shape of the diagram.",
155
+ inputSchema: source,
156
+ annotations: { readOnlyHint: true }
157
+ }, async (args) => {
158
+ const loaded = load(args);
159
+ if (!loaded.ok) return loaded.error;
160
+ return { content: [{
161
+ type: "text",
162
+ text: `${loaded.warnings}OK: ${summarize(loaded.doc)}`
163
+ }] };
164
+ });
165
+ registerAppTool(server, "show", {
166
+ title: "Show a TakeSix diagram",
167
+ description: "Validates a TakeSix document and writes it as one self-contained HTML file (opens in any browser, no network). In apps that support MCP Apps the diagram also opens in the conversation. Returns the file path.",
168
+ inputSchema: {
169
+ ...source,
170
+ output: z.string().optional().describe("Where to write the HTML file. Default: next to `file`, or in a takesix folder in the temp directory")
171
+ },
172
+ _meta: { ui: { resourceUri: VIEW_URI } }
173
+ }, async (args) => {
174
+ const loaded = load(args);
175
+ if (!loaded.ok) return loaded.error;
176
+ const { doc } = loaded;
177
+ const out = htmlPath(doc, loaded.file, args.output);
178
+ try {
179
+ mkdirSync(dirname(out), { recursive: true });
180
+ writeFileSync(out, renderHtml(doc, assets.viewerJs, assets.viewerCss));
181
+ } catch (e) {
182
+ return fail(`Cannot write ${out}: ${e.message}`).error;
183
+ }
184
+ const summary = summarize(doc);
185
+ return {
186
+ content: [{
187
+ type: "text",
188
+ text: `${loaded.warnings}OK: ${summary}\nWrote ${out}`
189
+ }],
190
+ structuredContent: {
191
+ summary,
192
+ html: out
193
+ },
194
+ _meta: { [DOCUMENT_META_KEY]: doc }
195
+ };
196
+ });
197
+ registerAppResource(server, "TakeSix viewer", VIEW_URI, { description: "Interactive viewer for the diagram shown by `show`" }, async () => ({ contents: [{
198
+ uri: VIEW_URI,
199
+ mimeType: RESOURCE_MIME_TYPE,
200
+ text: viewHtml(assets)
201
+ }] }));
202
+ server.registerResource("format", "takesix://format", {
203
+ title: "TakeSix format specification",
204
+ description: "Every field of a TakeSix document, the validation rules and how links are shown",
205
+ mimeType: "text/markdown"
206
+ }, async (uri) => ({ contents: [{
207
+ uri: uri.href,
208
+ mimeType: "text/markdown",
209
+ text: assets.formatMd
210
+ }] }));
211
+ server.registerResource("schema", "takesix://schema", {
212
+ title: "TakeSix JSON Schema",
213
+ description: "JSON Schema of the TakeSix format 1.0",
214
+ mimeType: "application/schema+json"
215
+ }, async (uri) => ({ contents: [{
216
+ uri: uri.href,
217
+ mimeType: "application/schema+json",
218
+ text: assets.schemaJson
219
+ }] }));
220
+ server.registerPrompt("diagram", {
221
+ title: "Answer with a TakeSix diagram",
222
+ description: "Explain a subject as a TakeSix diagram instead of long text",
223
+ argsSchema: { subject: z.string().describe("What to explain, e.g. \"this repository\" or \"our release process\"") }
224
+ }, ({ subject }) => ({ messages: [{
225
+ role: "user",
226
+ content: {
227
+ type: "text",
228
+ text: `${guideText(assets.skillMd)}\n---\n\nExplain ${subject} as a TakeSix diagram, following the guide above.`
229
+ }
230
+ }] }));
231
+ return server;
232
+ }
233
+ // src/mcp/main.ts
234
+ var root = join(dirname(fileURLToPath(import.meta.url)), "..");
235
+ var read = (...path) => readFileSync(join(root, ...path), "utf8");
236
+ var require = createRequire(import.meta.url);
237
+ await createServer({
238
+ version: JSON.parse(read("package.json")).version,
239
+ skillMd: read("skill", "takesix", "SKILL.md"),
240
+ formatMd: read("skill", "takesix", "references", "format.md"),
241
+ schemaJson: readFileSync(require.resolve("@takesix/core/schema.json"), "utf8"),
242
+ viewerJs: read("skill", "takesix", "assets", "viewer.js"),
243
+ viewerCss: read("skill", "takesix", "assets", "viewer.css"),
244
+ appJs: read("dist", "app", "app.js"),
245
+ appCss: read("dist", "app", "app.css")
246
+ }).connect(new StdioServerTransport());
247
+ export {};
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@takesix/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "MCP server that lets AI agents answer with a TakeSix diagram instead of a wall of text: guide, validation and a viewer in the conversation",
5
+ "type": "module",
6
+ "bin": {
7
+ "takesix-mcp": "dist/mcp.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skill/takesix/SKILL.md",
12
+ "skill/takesix/references",
13
+ "skill/takesix/assets"
14
+ ],
15
+ "scripts": {
16
+ "build": "vite build --config vite.viewer.config.ts && vite build --config vite.cli.config.ts && vite build --config vite.app.config.ts && vite build --config vite.mcp.config.ts",
17
+ "prepare": "npm run build",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "vitest run"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "mcp-app",
24
+ "diagram",
25
+ "takesix",
26
+ "ai",
27
+ "agent",
28
+ "claude"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/vvapnik/takesix-agent.git"
33
+ },
34
+ "license": "MIT",
35
+ "engines": {
36
+ "node": ">=22"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/ext-apps": "^1.7.5",
40
+ "@modelcontextprotocol/sdk": "^1.32.2",
41
+ "@takesix/core": "^0.2.1",
42
+ "zod": "^4.6.7"
43
+ },
44
+ "devDependencies": {
45
+ "@commitlint/cli": "^21.2.4",
46
+ "@commitlint/config-conventional": "^21.2.4",
47
+ "@takesix/react": "^0.3.0",
48
+ "@types/node": "^24.19.2",
49
+ "@types/react": "^19.3.0",
50
+ "@types/react-dom": "^19.3.0",
51
+ "@vitejs/plugin-react": "^6.1.2",
52
+ "react": "^19.3.1",
53
+ "react-dom": "^19.3.1",
54
+ "typescript": "^7.0.2",
55
+ "vite": "^8.3.5",
56
+ "vitest": "^5.0.5"
57
+ }
58
+ }
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: takesix
3
+ description: Answer with a TakeSix diagram instead of a long wall of text — a zoomable diagram with at most six blocks per screen, saved as JSON and rendered to a single self-contained HTML file. Use this whenever the user asks to explain, map, summarize or give an overview of something with structure (a codebase, a system's architecture, a document, a data model, a plan or roadmap, a process, a comparison) and the honest answer would otherwise be many sections of prose; also when they ask for a TakeSix / takesix diagram, a ".takesix.json" file, a "diagram I can click into", or want to "visualize" such structure. Prefer it over long Markdown answers with nested headings and bullet lists.
4
+ compatibility: Needs Node.js 18+ to run the bundled scripts/takesix.mjs (no npm install).
5
+ ---
6
+
7
+ # TakeSix: answer with a diagram, not a wall of text
8
+
9
+ A TakeSix diagram shows **at most six blocks on a screen**. Every block can open
10
+ into up to six more, as deep as needed. The reader gets the gist on the first
11
+ screen and clicks for depth, instead of scrolling through pages of prose. Links
12
+ between blocks are drawn on every level; long text hides in a details panel.
13
+
14
+ Your answer is a JSON document. A bundled script checks it and turns it into one
15
+ HTML file that opens anywhere.
16
+
17
+ ## Workflow
18
+
19
+ 1. **Understand the subject first.** Read the code, document or plan you are
20
+ explaining. The diagram is only as good as the understanding behind it.
21
+ 2. **Design the tree before writing JSON.** Answer the user's question with the
22
+ top level: 3–6 blocks that together say the whole thing. Then, for each block,
23
+ ask "what would the reader want when they click this?" and give it up to six
24
+ children. Two or three levels are usually enough; stop when a block is
25
+ self-explanatory.
26
+ 3. **Write the document** to a file named `<topic>.takesix.json` (format below,
27
+ full spec in `references/format.md`).
28
+ 4. **Validate** and fix until it passes:
29
+ ```bash
30
+ node <this-skill-dir>/scripts/takesix.mjs validate <topic>.takesix.json
31
+ ```
32
+ Errors come with JSON paths (`/blocks/2/children: 7 blocks on one level…`).
33
+ On success it prints a one-line summary; if it says arrows on the top level
34
+ are *merged from nested links*, check those links are worth showing there
35
+ (see Links below).
36
+ 5. **Render** one self-contained HTML page:
37
+ ```bash
38
+ node <this-skill-dir>/scripts/takesix.mjs render <topic>.takesix.json -o <topic>.html
39
+ ```
40
+ The page has the viewer inlined: no network, no install. Where you can show
41
+ files or publish an HTML artifact, show this file; otherwise give its path.
42
+ 6. **Reply briefly.** Two or three sentences on what the diagram covers and where
43
+ to start, plus the file. Do not repeat the diagram's content as prose — that
44
+ is the wall of text the diagram replaces.
45
+
46
+ ## The format in brief
47
+
48
+ ```json
49
+ {
50
+ "takesix": "1.0",
51
+ "title": "Payments service",
52
+ "description": "How a payment flows from checkout to the bank, in four parts",
53
+ "blocks": [
54
+ {
55
+ "id": "api",
56
+ "icon": "⬡",
57
+ "title": "Public API",
58
+ "description": "REST endpoints the shop calls",
59
+ "size": "S",
60
+ "details": "Versioned by path (`/v1/…`). Errors follow **RFC 9457**.",
61
+ "children": [ { "id": "api-charge", "title": "POST /charges", "description": "Creates a charge" } ]
62
+ },
63
+ { "id": "worker", "title": "Settlement worker", "description": "Talks to the bank", "size": "L" }
64
+ ],
65
+ "links": [ { "from": "api-charge", "to": "worker", "label": "queue" } ]
66
+ }
67
+ ```
68
+
69
+ - **Document:** `takesix: "1.0"`, `title`, `description`, `blocks` (≤6), `links`.
70
+ - **Block:** `id` (unique in the whole document), `title`, `description`, and
71
+ optionally `icon` (one emoji or character), `size`, `details`, `children` (≤6).
72
+ - **Size** (`size`): `S` a third of the width, `M` a half (default — omit it),
73
+ `L` two thirds, `F` the full width. Blocks flow left to right and wrap.
74
+ - **Link:** `from`, `to` (block ids on any levels), optional `label`,
75
+ `direction`: `forward` (default), `both`, `none`.
76
+
77
+ ## How to make it readable
78
+
79
+ These rules come from reviewing real generated diagrams. They are about what
80
+ the reader sees, so keep the viewer in mind while writing.
81
+
82
+ **Headers and cards**
83
+ - The screen header shows the document's `title` + `description` on the top
84
+ level, and the opened block's `title` + `description` below it. Write a real
85
+ description for the document and for every block with children: it is the
86
+ first sentence the reader sees on that screen. Do not add an "Overview" block
87
+ for this.
88
+ - A card shows only icon, title, one line of description and "N more inside".
89
+ Children are not previewed, so the title and description must say what is
90
+ inside.
91
+ - `description` is one short line. Anything longer — explanations, code,
92
+ lists, caveats — goes into `details`. It is a small Markdown subset:
93
+ headings, paragraphs, `-` and `1.` lists, `code`, **bold**, *italic*, fenced
94
+ code and `https` links. **No tables and no HTML**: they show as raw text.
95
+ Write a table as a list (`- **0x01** Plant container`).
96
+
97
+ **Sizes**
98
+ - Sequences (plans, roadmaps, steps of a process): every block `F`, so each is
99
+ a row and the order reads top to bottom.
100
+ - Peers (services, modules, entities): `S` (three per row) or `M` (two per
101
+ row). Make the one block that matters most `L` or `F`.
102
+ - Fill rows: sizes in a row should add up to the full width (`S`+`S`+`S`,
103
+ `M`+`M`, `L`+`S`, `F`). A block that does not fit wraps and leaves a hole.
104
+
105
+ **Placement: related blocks side by side, paths that do not cross**
106
+ - Blocks are placed in array order, left to right, wrapping into rows. Order
107
+ them so that linked blocks are neighbours: next to each other in a row, or
108
+ directly above and below.
109
+ - An arrow should go from one block to the other without crossing a third
110
+ block. If it would, reorder the blocks or change sizes until the ends touch.
111
+ If no order works, the link is probably not worth drawing (see Links).
112
+ - Arrows should not cross each other either. Sketch the level as a grid and
113
+ trace each link before writing JSON; a chain (A → B → C) or a single hub
114
+ with neighbours around it never crosses, a web of links between many
115
+ blocks always does.
116
+
117
+ **Nesting follows meaning**
118
+ - If something is part of something else (a table of a database, an endpoint
119
+ of a service, a step of a stage), it goes into that block's `children`,
120
+ not beside it on the same level. The tree is the "contains / consists of"
121
+ relation; links are for everything else.
122
+ - Do not flatten parts and wholes onto one level just because there is room.
123
+
124
+ **Group by meaning, not to fill the screen**
125
+ - Six is a ceiling, not a target. Three blocks that each mean one thing beat
126
+ six where some are leftovers. Do not split a block or pull children up only
127
+ to use the free places.
128
+ - Each level should have one principle of division (by layer, by stage, by
129
+ domain). A block that does not fit it belongs in another parent, or in
130
+ `details`.
131
+
132
+ **Links: few, and only where they matter**
133
+ - Every link is drawn on every level where its ends are visible. A link between
134
+ blocks nested in different parents also appears one level up, as an arrow
135
+ between those parents (dashed, merged with others). Ten innocent-looking
136
+ links make a noisy top level.
137
+ - Draw a link only when the relationship is the point. Otherwise say it in a
138
+ `description` or `details` ("reads from the orders table").
139
+ - In a sequence, link only consecutive top blocks (1 → 2 → 3). A chain reads
140
+ better than a fan; a link that skips a row is drawn across the row between.
141
+ - Labels: arrows between rows have room for a couple of words; arrows between
142
+ blocks side by side have room for about five characters ("SQL", "REST").
143
+ Leave those unlabelled rather than overflow onto the cards.
144
+
145
+ **Structure**
146
+ - Six is a hard limit (the validator rejects seven). When you have more, group
147
+ them into a parent block that names what they share — but group by meaning
148
+ even when you are under six (see above).
149
+ - Use short, readable, stable ids (`api-charge`, `db-orders`).
150
+ - Icons are optional; when used, keep them consistent (one per kind of thing,
151
+ or status marks such as ✅ ⏳ ○ for plans).
152
+
153
+ ## Editing an existing diagram
154
+
155
+ When asked to change a `.takesix.json`, edit the JSON in place, keep existing
156
+ ids (links and readers rely on them), then validate and render again.
157
+
158
+ ## Reference
159
+
160
+ - `references/format.md` — the full format specification: every field,
161
+ validation rules, how links are shown on each level.
@@ -0,0 +1,2 @@
1
+ ._card_nq4yy_1{background:var(--ts-surface);border:1px solid var(--ts-border);border-radius:10px;flex-direction:column;transition:box-shadow .2s,border-color .2s;display:flex;position:relative;overflow:hidden;box-shadow:0 2px 8px #0000000f}._deleteBtn_nq4yy_13{width:22px;height:22px;color:var(--ts-icon);cursor:pointer;z-index:2;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;line-height:1;transition:background .15s,color .15s;display:flex;position:absolute;top:8px;right:8px}._deleteBtn_nq4yy_13:hover{background:var(--ts-danger-soft);color:var(--ts-danger)}._sizeBtn_nq4yy_38{min-width:22px;height:22px;color:var(--ts-icon);font:inherit;cursor:pointer;z-index:2;background:0 0;border:none;border-radius:11px;padding:0 4px;font-size:.75rem;font-weight:600;transition:background .15s,color .15s;position:absolute;top:8px;right:86px}._sizeBtn_nq4yy_38:hover{background:var(--ts-accent-soft);color:var(--ts-accent)}._resizeHandle_nq4yy_63{cursor:ew-resize;z-index:3;width:8px;position:absolute;top:0;bottom:0;right:0}._resizeHandle_nq4yy_63:hover,._cardResizing_nq4yy_74 ._resizeHandle_nq4yy_63{background:linear-gradient(to right, transparent 3px, var(--ts-accent) 3px, var(--ts-accent) 5px, transparent 5px)}._cardResizing_nq4yy_74{border-color:var(--ts-accent)}._detailsBtn_nq4yy_82{width:22px;height:22px;color:var(--ts-icon);cursor:pointer;z-index:2;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;transition:background .15s,color .15s;display:flex;position:absolute;top:8px;right:8px}._cardEditing_nq4yy_102 ._detailsBtn_nq4yy_82{right:60px}._detailsBtnFilled_nq4yy_106{color:var(--ts-text-faint)}._detailsBtn_nq4yy_82:hover{background:var(--ts-accent-soft);color:var(--ts-accent)}._connectBtn_nq4yy_115{width:22px;height:22px;color:var(--ts-icon);cursor:pointer;z-index:2;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;line-height:1;transition:background .15s,color .15s;display:flex;position:absolute;top:8px;right:34px}._connectBtn_nq4yy_115:hover{background:var(--ts-accent-soft);color:var(--ts-accent)}._connectBtnActive_nq4yy_140{background:var(--ts-accent);color:#fff}._connectBtnActive_nq4yy_140:hover{background:var(--ts-accent-hover);color:#fff}._cardConnectingSource_nq4yy_151{border-color:var(--ts-accent)!important;box-shadow:0 0 0 2px #3d6aff4d,0 2px 8px #0000000f!important}._cardConnectable_nq4yy_156{border-color:var(--ts-border-strong)}._cardConnectable_nq4yy_156:hover{border-color:var(--ts-accent)!important;box-shadow:0 0 0 2px #3d6aff26!important}._cardConnected_nq4yy_165{border-color:var(--ts-accent)!important;box-shadow:0 0 0 2px #3d6aff4d!important}._connectOverlay_nq4yy_171{cursor:crosshair;z-index:5;background:0 0;border:none;border-radius:10px;padding:0;position:absolute;inset:0}._cardUnlinkable_nq4yy_183{opacity:.45}._cardConnectable_nq4yy_156 ._inside_nq4yy_189,._cardConnected_nq4yy_165 ._inside_nq4yy_189{z-index:6;cursor:zoom-in;position:relative}._selectOverlay_nq4yy_196{cursor:pointer}._selectMark_nq4yy_201{border:1.5px solid var(--ts-border-strong);background:var(--ts-surface);color:#fff;text-align:center;border-radius:50%;width:18px;height:18px;font-size:11px;line-height:16px;position:absolute;top:10px;left:0}._cardSelected_nq4yy_217{border-color:var(--ts-accent)!important;box-shadow:0 0 0 2px #3d6aff4d!important}._cardSelected_nq4yy_217 ._selectMark_nq4yy_201{border-color:var(--ts-accent);background:var(--ts-accent)}._connectOverlayConnected_nq4yy_227{cursor:pointer}._cardEditing_nq4yy_102{padding-left:18px}._cardEditing_nq4yy_102 ._header_nq4yy_237{padding-right:116px}._cardWithDetails_nq4yy_241 ._header_nq4yy_237{padding-right:36px}._addInside_nq4yy_245{cursor:pointer;align-items:center;gap:4px;display:inline-flex}._addInside_nq4yy_245:hover{color:var(--ts-accent)}._dragHandle_nq4yy_257{cursor:grab;border-right:1px solid var(--ts-divider);background-color:var(--ts-sunken);background-image:radial-gradient(circle, var(--ts-border-strong) 1.2px, transparent 1.6px);z-index:2;background-position:3px 3px;background-size:6px 6px;border-radius:10px 0 0 10px;width:18px;transition:background-color .15s;position:absolute;top:0;bottom:0;left:0}._dragHandle_nq4yy_257:hover{background-color:var(--ts-accent-soft);background-image:radial-gradient(circle, var(--ts-accent) 1.2px, transparent 1.6px)}._dragHandle_nq4yy_257:active{cursor:grabbing}._dragHandle_nq4yy_257:focus-visible{outline:2px solid var(--ts-accent);outline-offset:-2px}._cardDragged_nq4yy_288{opacity:.4}._card_nq4yy_1[data-drop=before][data-axis=y]{box-shadow:0 -4px 0 -1px var(--ts-accent)}._card_nq4yy_1[data-drop=after][data-axis=y]{box-shadow:0 4px 0 -1px var(--ts-accent)}._card_nq4yy_1[data-drop=before][data-axis=x]{box-shadow:-4px 0 0 -1px var(--ts-accent)}._card_nq4yy_1[data-drop=after][data-axis=x]{box-shadow:4px 0 0 -1px var(--ts-accent)}._card_nq4yy_1[data-drop=inside]{box-shadow:0 0 0 2px var(--ts-accent);background:var(--ts-accent-soft)}._cardZoomable_nq4yy_300{cursor:zoom-in}._card_nq4yy_1:hover{border-color:var(--ts-border-strong);box-shadow:0 4px 16px #0000001a}._header_nq4yy_237{align-items:flex-start;gap:14px;padding:20px 24px 16px;display:flex}._icon_nq4yy_316{flex-shrink:0;margin-top:1px;font-size:1.5rem;line-height:1}._headerText_nq4yy_323{flex-direction:column;display:flex}._title_nq4yy_328{color:var(--ts-text);letter-spacing:.01em;margin:0 0 5px;font-size:1rem;font-weight:600}._description_nq4yy_336{color:var(--ts-text-muted);margin:0;font-size:.8rem;line-height:1.4}._card_nq4yy_1[data-size=S]{grid-column:span 2}._card_nq4yy_1[data-size=M]{grid-column:span 3}._card_nq4yy_1[data-size=L]{grid-column:span 4}._card_nq4yy_1[data-size=F]{grid-column:span 6}._cardWide_nq4yy_350{flex-direction:row;align-items:stretch}._cardWide_nq4yy_350 ._header_nq4yy_237{border-bottom:none;flex:1;padding:14px 20px}._cardWide_nq4yy_350 ._inside_nq4yy_189{align-self:center;padding:0 20px}._editable_nq4yy_366{border-bottom:1.5px dashed var(--ts-border-strong);cursor:text;border-radius:2px;outline:none;padding-bottom:1px;transition:border-color .15s,background .15s}._editable_nq4yy_366:empty:before{content:attr(data-placeholder);color:var(--ts-icon)}._editable_nq4yy_366:focus{border-bottom-color:var(--ts-accent);background:#3d6aff0d}._inside_nq4yy_189{all:unset;cursor:inherit;letter-spacing:.02em;color:var(--ts-text-faint);white-space:nowrap;align-self:flex-start;padding:0 24px 16px;font-size:.72rem;font-weight:500;transition:color .15s}._cardZoomable_nq4yy_300:hover ._inside_nq4yy_189{color:var(--ts-accent)}@container ts-level (width<=560px){._card_nq4yy_1:is([data-size=S],[data-size=M]):not(._cardEditing_nq4yy_102) ._description_nq4yy_336{display:none}._card_nq4yy_1:is([data-size=S],[data-size=M]) ._header_nq4yy_237{flex-direction:column;gap:6px;padding:12px 12px 8px}._card_nq4yy_1:is([data-size=S],[data-size=M]) ._title_nq4yy_328{overflow-wrap:anywhere;margin:0;font-size:.9rem}._card_nq4yy_1:is([data-size=S],[data-size=M]) ._inside_nq4yy_189{white-space:normal;padding:0 12px 12px}}._inside_nq4yy_189:focus-visible{outline:2px solid var(--ts-accent);outline-offset:2px;border-radius:4px}._popover_1b6n3_1{z-index:10;border:1px solid var(--ts-border);background:var(--ts-surface);min-width:240px;max-width:320px;max-height:60%;color:var(--ts-text);cursor:default;border-radius:10px;padding:12px;font-size:.78rem;position:absolute;overflow-y:auto;transform:translate(-50%,10px);box-shadow:0 8px 24px #0000001f}._close_1b6n3_19{all:unset;width:20px;height:20px;color:var(--ts-text-faint);cursor:pointer;border-radius:50%;justify-content:center;align-items:center;display:flex;position:absolute;top:8px;right:8px}._close_1b6n3_19:hover{background:var(--ts-bg);color:var(--ts-text)}._row_1b6n3_39+._row_1b6n3_39{border-top:1px solid var(--ts-divider);margin-top:12px;padding-top:12px}._ends_1b6n3_45{margin-bottom:8px;padding-right:24px;font-weight:500}._endsArrow_1b6n3_51{color:var(--ts-accent)}._label_1b6n3_55{box-sizing:border-box;border:1px solid var(--ts-border);background:var(--ts-surface);width:100%;font:inherit;color:inherit;border-radius:6px;outline:none;padding:6px 8px}._label_1b6n3_55:focus{border-color:var(--ts-accent)}._actions_1b6n3_71{align-items:center;gap:6px;margin-top:8px;display:flex}._directions_1b6n3_78{gap:2px;display:flex}._btn_1b6n3_83{border:1px solid var(--ts-border);background:var(--ts-surface);min-width:28px;height:26px;color:var(--ts-text-secondary);font:inherit;cursor:pointer;border-radius:6px;padding:0 8px}._btn_1b6n3_83:hover:not(:disabled){border-color:var(--ts-border-strong);color:var(--ts-text)}._btn_1b6n3_83:disabled{opacity:.4;cursor:default}._btnActive_1b6n3_105,._btnActive_1b6n3_105:hover:not(:disabled){background:var(--ts-strong);border-color:var(--ts-strong);color:var(--ts-on-strong)}._danger_1b6n3_112{margin-left:auto}._danger_1b6n3_112:hover:not(:disabled){border-color:var(--ts-danger-border);color:var(--ts-danger)}._panel_24xow_1{z-index:15;box-sizing:border-box;background:var(--ts-surface);border-left:1px solid var(--ts-border);width:min(380px,90%);color:var(--ts-text);cursor:default;padding:20px 22px;font-size:.85rem;animation:.18s ease-out _slideIn_24xow_1;position:absolute;top:0;bottom:0;right:0;overflow-y:auto;box-shadow:-8px 0 24px #00000014}@keyframes _slideIn_24xow_1{0%{opacity:0;transform:translate(24px)}}._close_24xow_24{all:unset;width:24px;height:24px;color:var(--ts-text-faint);cursor:pointer;border-radius:50%;justify-content:center;align-items:center;display:flex;position:absolute;top:14px;right:14px}._close_24xow_24:hover{background:var(--ts-bg);color:var(--ts-text)}._head_24xow_44{align-items:flex-start;gap:12px;margin-bottom:16px;padding-right:28px;display:flex}._icon_24xow_52{font-size:1.4rem;line-height:1}._title_24xow_57{margin:0 0 4px;font-size:1rem;font-weight:600}._description_24xow_63{color:var(--ts-text-muted);margin:0;font-size:.8rem;line-height:1.4}._editor_24xow_70{box-sizing:border-box;border:1px solid var(--ts-border);background:var(--ts-surface);width:100%;min-height:160px;color:inherit;resize:vertical;border-radius:8px;outline:none;padding:8px 10px;font:.8rem/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}._editor_24xow_70:focus{border-color:var(--ts-accent)}._editor_24xow_70+._body_24xow_88{border-top:1px dashed var(--ts-border);margin-top:14px;padding-top:14px}._body_24xow_88{line-height:1.55}._body_24xow_88>:first-child{margin-top:0}._body_24xow_88>:last-child{margin-bottom:0}._body_24xow_88 p,._body_24xow_88 ul,._body_24xow_88 ol,._body_24xow_88 pre{margin:0 0 10px}._body_24xow_88 ul,._body_24xow_88 ol{padding-left:20px}._body_24xow_88 h3,._body_24xow_88 h4,._body_24xow_88 h5{margin:14px 0 6px;font-size:.9rem}._body_24xow_88 a{color:var(--ts-accent)}._body_24xow_88 code{background:var(--ts-bg);border-radius:4px;padding:1px 4px;font:.78rem ui-monospace,SFMono-Regular,Menlo,monospace}._body_24xow_88 pre{background:var(--ts-bg);border-radius:6px;padding:8px 10px;overflow-x:auto}._body_24xow_88 pre code{background:0 0;padding:0}._empty_24xow_124{color:var(--ts-text-faint);margin:0}._viewer_1wq71_1{flex-direction:column;gap:6px;height:100%;display:flex}._breadcrumb_1wq71_10{cursor:zoom-out;z-index:2;flex-wrap:wrap;align-items:center;gap:2px;display:flex;position:relative}._crumbItem_1wq71_20{align-items:center;gap:2px;display:flex}._crumbSep_1wq71_26{color:var(--ts-icon);padding:0 6px;font-size:.8rem}._crumb_1wq71_20{all:unset;cursor:pointer;letter-spacing:.06em;text-transform:uppercase;color:var(--ts-text-faint);border-radius:4px;padding:2px 6px;font-size:.75rem;font-weight:500;transition:color .15s,background .15s}._crumb_1wq71_20:hover:not(:disabled){color:var(--ts-accent);background:var(--ts-accent-soft)}._crumb_1wq71_20:disabled{cursor:default}._crumbActive_1wq71_51{color:var(--ts-text)}._crumbDrop_1wq71_54{color:var(--ts-accent);background:var(--ts-accent-soft);box-shadow:0 0 0 1.5px var(--ts-accent)}._backdrop_1wq71_61{cursor:zoom-out;z-index:1;position:absolute;inset:0}._slide_1wq71_69{will-change:transform;z-index:2;flex-direction:column;flex:1;min-height:0;display:flex;position:relative}._slideCard_1wq71_80{background:var(--ts-surface);border:1px solid var(--ts-border);border-radius:10px;overflow:hidden;box-shadow:0 2px 8px #0000000f}._slideHeader_1wq71_90{flex-shrink:0;align-items:flex-start;gap:14px;padding:20px 24px 16px;display:flex}._headerDetailsBtn_1wq71_98{all:unset;border:1px solid var(--ts-border);color:var(--ts-text-secondary);cursor:pointer;border-radius:999px;align-self:center;align-items:center;gap:6px;margin-left:auto;padding:4px 10px;font-size:.75rem;display:flex}._headerDetailsBtn_1wq71_98:hover{border-color:var(--ts-accent);color:var(--ts-accent)}._slideHeaderTop_1wq71_119{padding:0 0 20px}._slideIcon_1wq71_123{flex-shrink:0;margin-top:1px;font-size:1.5rem;line-height:1}._slideHeaderText_1wq71_130{flex-direction:column;display:flex}._slideTitle_1wq71_135{color:var(--ts-text);letter-spacing:.01em;margin:0 0 5px;font-size:1rem;font-weight:600}._slideDescription_1wq71_143{color:var(--ts-text-muted);margin:0;font-size:.8rem;line-height:1.4}._gridArea_1wq71_152{flex-direction:column;flex:1;min-height:0;display:flex;position:relative;container:ts-level/inline-size}._arrowSvg_1wq71_162{pointer-events:none;z-index:3;width:100%;height:100%;position:absolute;inset:0;overflow:visible}._arrow_1wq71_162{fill:none;stroke:var(--ts-accent);stroke-width:2px;stroke-opacity:.7}._arrowSvgEditing_1wq71_180 ._arrowHit_1wq71_180{fill:none;stroke:#0000;stroke-width:14px;pointer-events:stroke;cursor:pointer}._arrowSvgEditing_1wq71_180 ._arrowHit_1wq71_180:hover+._arrow_1wq71_162{stroke-opacity:1;stroke-width:3px}._arrowSvgEditing_1wq71_180 ._arrowLabel_1wq71_193{pointer-events:auto;cursor:pointer}._arrowLabelOpen_1wq71_198{font-weight:700}._arrowIndirect_1wq71_202{stroke-dasharray:6 4}._arrowHead_1wq71_206{fill:var(--ts-accent)}._arrowLabel_1wq71_193{fill:var(--ts-accent);paint-order:stroke;stroke:var(--ts-bg);stroke-width:4px;stroke-linejoin:round;font-size:11px;font-weight:500}._empty_1wq71_220{color:var(--ts-text-faint);margin:auto;font-size:.85rem}._gridAreaNested_1wq71_227{border:1.5px dashed var(--ts-border-dashed);background:var(--ts-sunken);border-radius:7px;margin:0 10px 10px;padding:8px}._grid_1wq71_150{flex:1;grid-template-columns:repeat(6,minmax(0,1fr));grid-auto-rows:max-content;align-content:space-evenly;gap:16px 56px;min-height:0;display:grid}@container ts-level (width<=560px){._grid_1wq71_150{column-gap:16px}}._levelTools_1wq71_258{gap:8px;margin-bottom:10px;display:flex}._groupBtn_1wq71_264{border:1px solid var(--ts-border);background:var(--ts-surface);height:28px;color:var(--ts-text-secondary);font:inherit;cursor:pointer;border-radius:6px;padding:0 10px;font-size:.75rem}._groupBtn_1wq71_264:hover{border-color:var(--ts-border-strong);color:var(--ts-text)}._groupBtnActive_1wq71_281,._groupBtnActive_1wq71_281:hover{background:var(--ts-strong);border-color:var(--ts-strong);color:var(--ts-on-strong)}._fullHint_1wq71_288{color:var(--ts-text-faint);align-self:center;font-size:.72rem}._addBlock_1wq71_295{border:1.5px dashed var(--ts-border-dashed);color:var(--ts-border-strong);cursor:pointer;background:0 0;border-radius:10px;grid-column:span 2;justify-content:center;align-items:center;min-height:80px;transition:border-color .15s,color .15s,background .15s;display:flex}._addBlock_1wq71_295:hover{border-color:var(--ts-accent);color:var(--ts-accent);background:var(--ts-accent-soft)}._linkBar_1wq71_316{z-index:20;background:var(--ts-strong);color:var(--ts-on-strong);white-space:nowrap;border-radius:999px;align-items:center;gap:12px;padding:8px 10px 8px 16px;font-size:.78rem;display:flex;position:absolute;bottom:16px;left:50%;transform:translate(-50%);box-shadow:0 4px 16px #0000002e}._linkBarCancel_1wq71_334{all:unset;cursor:pointer;background:color-mix(in srgb, var(--ts-on-strong) 14%, transparent);border-radius:999px;padding:3px 10px;font-weight:500}._linkBarCancel_1wq71_334:hover:not(:disabled){background:color-mix(in srgb, var(--ts-on-strong) 24%, transparent)}._linkBarCancel_1wq71_334:disabled{opacity:.4;cursor:default}._takeSix_b98wp_4{--ts-bg:#f0f2f5;--ts-surface:#fff;--ts-sunken:#f7f9fc;--ts-border:#d0d7e2;--ts-border-strong:#b0bfda;--ts-border-dashed:#c4cedc;--ts-divider:#e8edf4;--ts-text:#1a2332;--ts-text-secondary:#5a6a7e;--ts-text-muted:#7a8a9e;--ts-text-faint:#8fa3b8;--ts-icon:#c0c8d4;--ts-accent:#3d6aff;--ts-accent-hover:#2a55e8;--ts-accent-soft:#e8eeff;--ts-strong:#1a2332;--ts-on-strong:#fff;--ts-danger:#b42318;--ts-danger-soft:#fee2e2;--ts-danger-border:#f5c2c2;--lightningcss-light:initial;--lightningcss-dark: ;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;box-sizing:border-box;background:var(--ts-bg);height:100%;min-height:0;color:var(--ts-text);padding:12px 40px 24px;font-family:Inter,system-ui,-apple-system,sans-serif;position:relative;overflow:hidden}._takeSix_b98wp_4[data-theme=dark]{--ts-bg:#0f141b;--ts-surface:#18202b;--ts-sunken:#141b24;--ts-border:#2c3644;--ts-border-strong:#3f4c5e;--ts-border-dashed:#364254;--ts-divider:#242d3a;--ts-text:#e6ebf2;--ts-text-secondary:#b3bfcd;--ts-text-muted:#94a3b5;--ts-text-faint:#7d8ca0;--ts-icon:#56647a;--ts-accent:#6b8cff;--ts-accent-hover:#8aa4ff;--ts-accent-soft:#1e2a48;--ts-strong:#e6ebf2;--ts-on-strong:#0f141b;--ts-danger:#f87171;--ts-danger-soft:#3a1d1f;--ts-danger-border:#7f2d2d;--lightningcss-light: ;--lightningcss-dark:initial;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}@media (prefers-color-scheme:dark){._takeSix_b98wp_4[data-theme=auto]{--ts-bg:#0f141b;--ts-surface:#18202b;--ts-sunken:#141b24;--ts-border:#2c3644;--ts-border-strong:#3f4c5e;--ts-border-dashed:#364254;--ts-divider:#242d3a;--ts-text:#e6ebf2;--ts-text-secondary:#b3bfcd;--ts-text-muted:#94a3b5;--ts-text-faint:#7d8ca0;--ts-icon:#56647a;--ts-accent:#6b8cff;--ts-accent-hover:#8aa4ff;--ts-accent-soft:#1e2a48;--ts-strong:#e6ebf2;--ts-on-strong:#0f141b;--ts-danger:#f87171;--ts-danger-soft:#3a1d1f;--ts-danger-border:#7f2d2d;--lightningcss-light: ;--lightningcss-dark:initial;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}
2
+ /*$vite$:1*/