@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/LICENSE +21 -0
- package/README.md +102 -2
- package/dist/app/app.css +2 -0
- package/dist/app/app.js +116 -0
- package/dist/mcp.js +247 -0
- package/package.json +56 -4
- package/skill/takesix/SKILL.md +161 -0
- package/skill/takesix/assets/viewer.css +2 -0
- package/skill/takesix/assets/viewer.js +14 -0
- package/skill/takesix/references/format.md +154 -0
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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*/
|