@meltemi/diagrams-cli 1.9.0 → 1.11.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/agent-setup/prompt.md +83 -0
- package/dist/assets-command.d.ts.map +1 -1
- package/dist/assets-command.js +15 -0
- package/dist/assets-command.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -1
- package/dist/cli.js.map +1 -1
- package/dist/export-command.d.ts.map +1 -1
- package/dist/export-command.js +16 -3
- package/dist/export-command.js.map +1 -1
- package/dist/init-command.d.ts.map +1 -1
- package/dist/init-command.js +6 -3
- package/dist/init-command.js.map +1 -1
- package/dist/messages/en.d.ts +45 -7
- package/dist/messages/en.d.ts.map +1 -1
- package/dist/messages/en.js +82 -9
- package/dist/messages/en.js.map +1 -1
- package/dist/messages/es.d.ts.map +1 -1
- package/dist/messages/es.js +79 -8
- package/dist/messages/es.js.map +1 -1
- package/dist/messages/index.d.ts +41 -6
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/skill-command.d.ts +48 -0
- package/dist/skill-command.d.ts.map +1 -0
- package/dist/skill-command.js +148 -0
- package/dist/skill-command.js.map +1 -0
- package/package.json +25 -18
- package/skills/.skills-sync.json +5 -0
- package/skills/meltemi-diagrams/SKILL.md +232 -0
- package/skills/meltemi-diagrams/examples/architecture.meltemi +73 -0
- package/skills/meltemi-diagrams/examples/platform.meltemi +139 -0
- package/skills/meltemi-diagrams/examples/sequence.meltemi +52 -0
- package/skills/meltemi-diagrams/examples/slide.meltemi +60 -0
- package/skills/meltemi-diagrams/examples/story.meltemi +53 -0
- package/skills/meltemi-diagrams/reference/c4.md +92 -0
- package/skills/meltemi-diagrams/reference/cloud.md +19 -0
- package/skills/meltemi-diagrams/reference/converting.md +101 -0
- package/skills/meltemi-diagrams/reference/free.md +327 -0
- package/skills/meltemi-diagrams/reference/language.md +245 -0
- package/skills/meltemi-diagrams/reference/layout.md +165 -0
- package/skills/meltemi-diagrams/reference/sequence.md +143 -0
- package/skills/meltemi-diagrams/reference/stories.md +94 -0
- package/skills/meltemi-diagrams/reference/themes.md +148 -0
- package/templates/kanban/diagrams/board.meltemi +57 -0
- package/templates/whiteboard/diagrams/whiteboard.meltemi +59 -0
package/dist/messages/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
export declare const messages: (locale?: import("@meltemi/diagrams-i18n").Locale) => {
|
|
7
7
|
help: {
|
|
8
8
|
main: (name: string, alias: string) => string;
|
|
9
|
+
skill: string;
|
|
9
10
|
init: (templates: string) => string;
|
|
10
11
|
import: string;
|
|
11
12
|
review: string;
|
|
@@ -109,7 +110,6 @@ export declare const messages: (locale?: import("@meltemi/diagrams-i18n").Locale
|
|
|
109
110
|
size: (max: number, size: string) => string;
|
|
110
111
|
backgroundHtml: string;
|
|
111
112
|
background: (background: string) => string;
|
|
112
|
-
mp4Transparent: string;
|
|
113
113
|
linkFormats: string;
|
|
114
114
|
link: (link: string) => string;
|
|
115
115
|
oneFile: (option: string) => string;
|
|
@@ -148,14 +148,47 @@ export declare const messages: (locale?: import("@meltemi/diagrams-i18n").Locale
|
|
|
148
148
|
};
|
|
149
149
|
folderSummary: (check: boolean, files: number, folder: string, parts: readonly string[]) => string;
|
|
150
150
|
};
|
|
151
|
+
skill: {
|
|
152
|
+
wrote: (path: string) => string;
|
|
153
|
+
subcommand: (given: string) => string;
|
|
154
|
+
tooMany: string;
|
|
155
|
+
userWithFolder: string;
|
|
156
|
+
unknownAgent: (agent: string, agents: readonly string[]) => string;
|
|
157
|
+
promptArguments: string;
|
|
158
|
+
exists: (path: string) => string;
|
|
159
|
+
};
|
|
151
160
|
init: {
|
|
152
161
|
templates: {
|
|
153
|
-
flow:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
flow: {
|
|
163
|
+
title: string;
|
|
164
|
+
description: string;
|
|
165
|
+
};
|
|
166
|
+
c4: {
|
|
167
|
+
title: string;
|
|
168
|
+
description: string;
|
|
169
|
+
};
|
|
170
|
+
sequence: {
|
|
171
|
+
title: string;
|
|
172
|
+
description: string;
|
|
173
|
+
};
|
|
174
|
+
free: {
|
|
175
|
+
title: string;
|
|
176
|
+
description: string;
|
|
177
|
+
};
|
|
178
|
+
kanban: {
|
|
179
|
+
title: string;
|
|
180
|
+
description: string;
|
|
181
|
+
};
|
|
182
|
+
whiteboard: {
|
|
183
|
+
title: string;
|
|
184
|
+
description: string;
|
|
185
|
+
};
|
|
186
|
+
purchase: {
|
|
187
|
+
title: string;
|
|
188
|
+
description: string;
|
|
189
|
+
};
|
|
158
190
|
};
|
|
191
|
+
contents: (counts: readonly (readonly ["models" | "views" | "diagrams" | "stories", number])[]) => string;
|
|
159
192
|
exists: (path: string) => string;
|
|
160
193
|
gitlab: string;
|
|
161
194
|
oneFolder: string;
|
|
@@ -179,6 +212,8 @@ export declare const messages: (locale?: import("@meltemi/diagrams-i18n").Locale
|
|
|
179
212
|
packs: string;
|
|
180
213
|
aliases: string;
|
|
181
214
|
fonts: string;
|
|
215
|
+
bundledFonts: string;
|
|
216
|
+
fontGroups: Readonly<Record<string, string>>;
|
|
182
217
|
noWords: string;
|
|
183
218
|
limit: string;
|
|
184
219
|
noMatch: (query: string) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAkB,CAAC;AAExC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAoBrD;AAED,4FAA4F;AAC5F,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** What the skill command needs from the CLI. */
|
|
2
|
+
export interface SkillContext {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly stdout: (text: string) => void;
|
|
5
|
+
readonly stderr: (text: string) => void;
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
/** A path as printed: relative to the working folder. */
|
|
8
|
+
readonly display: (path: string) => string;
|
|
9
|
+
/** The person's home folder, where `--user` installs; the system's by default. */
|
|
10
|
+
readonly home?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The agents the skill installs for, with the folder under the project (or the home folder) each
|
|
14
|
+
* reads skills from, as the vendors' pages name them (decision 0063): Claude Code reads
|
|
15
|
+
* `.claude/skills`; Codex, Cursor, GitHub Copilot, Gemini CLI and Devin Desktop (once Windsurf)
|
|
16
|
+
* read the cross-agent `.agents/skills`, which `agents` names for any other agent that reads it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SKILL_AGENTS: {
|
|
19
|
+
readonly claude: ".claude/skills";
|
|
20
|
+
readonly codex: ".agents/skills";
|
|
21
|
+
readonly cursor: ".agents/skills";
|
|
22
|
+
readonly copilot: ".agents/skills";
|
|
23
|
+
readonly gemini: ".agents/skills";
|
|
24
|
+
readonly windsurf: ".agents/skills";
|
|
25
|
+
readonly devin: ".agents/skills";
|
|
26
|
+
readonly agents: ".agents/skills";
|
|
27
|
+
};
|
|
28
|
+
export type SkillAgent = keyof typeof SKILL_AGENTS;
|
|
29
|
+
/** The skill the package ships. */
|
|
30
|
+
export declare const SKILL_NAME = "meltemi-diagrams";
|
|
31
|
+
/**
|
|
32
|
+
* The skill's text as an installed project reads it: the repository's `pnpm mdg <command>` becomes
|
|
33
|
+
* the installed command's `mdg <command>`, the repository's preview server the command's own, and
|
|
34
|
+
* the repository root the project's folder.
|
|
35
|
+
*/
|
|
36
|
+
export declare function installedText(text: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* The skill folders the named agents read, each once, in the table's order; `all` names every
|
|
39
|
+
* folder. The first name that is empty or unknown otherwise.
|
|
40
|
+
*/
|
|
41
|
+
export declare function skillFolders(names: readonly string[]): {
|
|
42
|
+
folders: string[];
|
|
43
|
+
} | {
|
|
44
|
+
unknown: string;
|
|
45
|
+
};
|
|
46
|
+
/** `meltemi-diagrams skill install` and `meltemi-diagrams skill prompt`. */
|
|
47
|
+
export declare function skillCommand(args: readonly string[], context: SkillContext): number;
|
|
48
|
+
//# sourceMappingURL=skill-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-command.d.ts","sourceRoot":"","sources":["../src/skill-command.ts"],"names":[],"mappings":"AAeA,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C,kFAAkF;IAClF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,YAAY,CAAC;AAKnD,mCAAmC;AACnC,eAAO,MAAM,UAAU,qBAAqB,CAAC;AAW7C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlD;AAsBD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAQ7C;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CA2DnF"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { argumentsError, messages } from "./messages/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* The agents the skill installs for, with the folder under the project (or the home folder) each
|
|
9
|
+
* reads skills from, as the vendors' pages name them (decision 0063): Claude Code reads
|
|
10
|
+
* `.claude/skills`; Codex, Cursor, GitHub Copilot, Gemini CLI and Devin Desktop (once Windsurf)
|
|
11
|
+
* read the cross-agent `.agents/skills`, which `agents` names for any other agent that reads it.
|
|
12
|
+
*/
|
|
13
|
+
export const SKILL_AGENTS = {
|
|
14
|
+
claude: ".claude/skills",
|
|
15
|
+
codex: ".agents/skills",
|
|
16
|
+
cursor: ".agents/skills",
|
|
17
|
+
copilot: ".agents/skills",
|
|
18
|
+
gemini: ".agents/skills",
|
|
19
|
+
windsurf: ".agents/skills",
|
|
20
|
+
devin: ".agents/skills",
|
|
21
|
+
agents: ".agents/skills",
|
|
22
|
+
};
|
|
23
|
+
/** The agents' names and `all`, as the help and the refusal list them. */
|
|
24
|
+
const AGENT_NAMES = [...Object.keys(SKILL_AGENTS), "all"];
|
|
25
|
+
/** The skill the package ships. */
|
|
26
|
+
export const SKILL_NAME = "meltemi-diagrams";
|
|
27
|
+
/** The folder of the shipped skills: `skills/` beside `src/` and `dist/`. */
|
|
28
|
+
const SKILL_ROOT = fileURLToPath(new URL("../skills/", import.meta.url));
|
|
29
|
+
/** The agent setup prompt the package ships, which `skill prompt` prints and the site serves. */
|
|
30
|
+
const PROMPT = fileURLToPath(new URL("../agent-setup/prompt.md", import.meta.url));
|
|
31
|
+
const EXIT_OK = 0;
|
|
32
|
+
const EXIT_USAGE = 2;
|
|
33
|
+
/**
|
|
34
|
+
* The skill's text as an installed project reads it: the repository's `pnpm mdg <command>` becomes
|
|
35
|
+
* the installed command's `mdg <command>`, the repository's preview server the command's own, and
|
|
36
|
+
* the repository root the project's folder.
|
|
37
|
+
*/
|
|
38
|
+
export function installedText(text) {
|
|
39
|
+
return text
|
|
40
|
+
.replaceAll("`pnpm preview` serves the repository at `http://localhost:4173`", "`mdg preview` serves the folder at `http://localhost:4173`")
|
|
41
|
+
.replaceAll("`pnpm preview`", "`mdg preview`")
|
|
42
|
+
.replaceAll("(from the repository root)", "(from the project's folder)")
|
|
43
|
+
.replaceAll("pnpm mdg ", "mdg ");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Copies the shipped skill to `destination`, rewriting its text for an installed project: the
|
|
47
|
+
* Markdown and the commands the examples' comments name.
|
|
48
|
+
*/
|
|
49
|
+
function writeSkill(destination) {
|
|
50
|
+
const source = join(SKILL_ROOT, SKILL_NAME);
|
|
51
|
+
mkdirSync(destination, { recursive: true });
|
|
52
|
+
cpSync(source, destination, { recursive: true });
|
|
53
|
+
const rewrite = (dir) => {
|
|
54
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
55
|
+
const path = join(dir, entry.name);
|
|
56
|
+
if (entry.isDirectory())
|
|
57
|
+
rewrite(path);
|
|
58
|
+
else if (entry.name.endsWith(".md") || entry.name.endsWith(".meltemi")) {
|
|
59
|
+
writeFileSync(path, installedText(readFileSync(path, "utf8")));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
rewrite(destination);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The skill folders the named agents read, each once, in the table's order; `all` names every
|
|
67
|
+
* folder. The first name that is empty or unknown otherwise.
|
|
68
|
+
*/
|
|
69
|
+
export function skillFolders(names) {
|
|
70
|
+
const wanted = new Set();
|
|
71
|
+
for (const name of names) {
|
|
72
|
+
if (name === "all")
|
|
73
|
+
Object.values(SKILL_AGENTS).forEach((folder) => wanted.add(folder));
|
|
74
|
+
else if (Object.hasOwn(SKILL_AGENTS, name))
|
|
75
|
+
wanted.add(SKILL_AGENTS[name]);
|
|
76
|
+
else
|
|
77
|
+
return { unknown: name };
|
|
78
|
+
}
|
|
79
|
+
return { folders: [...new Set(Object.values(SKILL_AGENTS))].filter((f) => wanted.has(f)) };
|
|
80
|
+
}
|
|
81
|
+
/** `meltemi-diagrams skill install` and `meltemi-diagrams skill prompt`. */
|
|
82
|
+
export function skillCommand(args, context) {
|
|
83
|
+
const fail = (message) => {
|
|
84
|
+
context.stderr(`${context.name} skill: ${message}\n`);
|
|
85
|
+
return EXIT_USAGE;
|
|
86
|
+
};
|
|
87
|
+
let options;
|
|
88
|
+
let positionals;
|
|
89
|
+
try {
|
|
90
|
+
const parsed = parseArgs({
|
|
91
|
+
args: [...args],
|
|
92
|
+
options: {
|
|
93
|
+
agent: { type: "string", multiple: true },
|
|
94
|
+
user: { type: "boolean", default: false },
|
|
95
|
+
force: { type: "boolean", default: false },
|
|
96
|
+
help: { type: "boolean", short: "h", default: false },
|
|
97
|
+
},
|
|
98
|
+
allowPositionals: true,
|
|
99
|
+
strict: true,
|
|
100
|
+
});
|
|
101
|
+
options = parsed.values;
|
|
102
|
+
positionals = parsed.positionals;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return fail(argumentsError(error));
|
|
106
|
+
}
|
|
107
|
+
const t = messages().skill;
|
|
108
|
+
if (options.help) {
|
|
109
|
+
context.stdout(messages().help.skill);
|
|
110
|
+
return EXIT_OK;
|
|
111
|
+
}
|
|
112
|
+
const [subcommand, folder, ...extra] = positionals;
|
|
113
|
+
if (subcommand === "prompt") {
|
|
114
|
+
const given = options.agent !== undefined || options.user || options.force;
|
|
115
|
+
if (folder !== undefined || given)
|
|
116
|
+
return fail(t.promptArguments);
|
|
117
|
+
// Byte for byte: the text is written for the installed command and is never rewritten.
|
|
118
|
+
context.stdout(readFileSync(PROMPT, "utf8"));
|
|
119
|
+
return EXIT_OK;
|
|
120
|
+
}
|
|
121
|
+
if (subcommand !== "install")
|
|
122
|
+
return fail(t.subcommand(subcommand ?? ""));
|
|
123
|
+
if (extra.length > 0)
|
|
124
|
+
return fail(t.tooMany);
|
|
125
|
+
if (options.user && folder !== undefined)
|
|
126
|
+
return fail(t.userWithFolder);
|
|
127
|
+
const names = (options.agent ?? ["all"]).flatMap((value) => value.split(",").map((name) => name.trim()));
|
|
128
|
+
const chosen = skillFolders(names);
|
|
129
|
+
if ("unknown" in chosen)
|
|
130
|
+
return fail(t.unknownAgent(chosen.unknown, AGENT_NAMES));
|
|
131
|
+
const base = options.user ? (context.home ?? homedir()) : resolve(context.cwd, folder ?? ".");
|
|
132
|
+
const destinations = chosen.folders.map((agentFolder) => join(base, agentFolder, SKILL_NAME));
|
|
133
|
+
// Nothing is written when any destination holds a copy, unless asked to replace it.
|
|
134
|
+
if (!options.force) {
|
|
135
|
+
const existing = destinations.find((path) => existsSync(path));
|
|
136
|
+
if (existing)
|
|
137
|
+
return fail(t.exists(context.display(existing)));
|
|
138
|
+
}
|
|
139
|
+
for (const destination of destinations) {
|
|
140
|
+
// Only the skill's own folder is replaced; the folders beside it stay.
|
|
141
|
+
if (options.force)
|
|
142
|
+
rmSync(destination, { recursive: true, force: true });
|
|
143
|
+
writeSkill(destination);
|
|
144
|
+
context.stdout(`${t.wrote(context.display(destination))}\n`);
|
|
145
|
+
}
|
|
146
|
+
return EXIT_OK;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=skill-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-command.js","sourceRoot":"","sources":["../src/skill-command.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meltemi/diagrams-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Command line interface (meltemi-diagrams, alias mdg) for .meltemi projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,31 +27,38 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
+
"agent-setup",
|
|
30
31
|
"dist",
|
|
32
|
+
"skills",
|
|
31
33
|
"templates"
|
|
32
34
|
],
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@meltemi/diagrams-assets": "1.
|
|
35
|
-
"@meltemi/diagrams-assist": "1.
|
|
36
|
-
"@meltemi/diagrams-capture": "1.
|
|
37
|
-
"@meltemi/diagrams-
|
|
38
|
-
"@meltemi/diagrams-
|
|
39
|
-
"@meltemi/diagrams-
|
|
40
|
-
"@meltemi/diagrams-
|
|
41
|
-
"@meltemi/diagrams-
|
|
42
|
-
"@meltemi/diagrams-
|
|
43
|
-
"@meltemi/diagrams-layout": "1.
|
|
44
|
-
"@meltemi/diagrams-
|
|
45
|
-
"@meltemi/diagrams-model": "1.
|
|
46
|
-
"@meltemi/diagrams-
|
|
47
|
-
"@meltemi/diagrams-motion": "1.
|
|
48
|
-
"@meltemi/diagrams-
|
|
49
|
-
"@meltemi/diagrams-render": "1.
|
|
50
|
-
"@meltemi/diagrams-
|
|
36
|
+
"@meltemi/diagrams-assets": "1.11.1",
|
|
37
|
+
"@meltemi/diagrams-assist": "1.11.1",
|
|
38
|
+
"@meltemi/diagrams-capture": "1.11.1",
|
|
39
|
+
"@meltemi/diagrams-edit": "1.11.1",
|
|
40
|
+
"@meltemi/diagrams-editor": "1.11.1",
|
|
41
|
+
"@meltemi/diagrams-catalog": "1.11.1",
|
|
42
|
+
"@meltemi/diagrams-fonts": "1.11.1",
|
|
43
|
+
"@meltemi/diagrams-i18n": "1.11.1",
|
|
44
|
+
"@meltemi/diagrams-language": "1.11.1",
|
|
45
|
+
"@meltemi/diagrams-layout": "1.11.1",
|
|
46
|
+
"@meltemi/diagrams-import": "1.11.1",
|
|
47
|
+
"@meltemi/diagrams-model": "1.11.1",
|
|
48
|
+
"@meltemi/diagrams-layout-elk": "1.11.1",
|
|
49
|
+
"@meltemi/diagrams-motion": "1.11.1",
|
|
50
|
+
"@meltemi/diagrams-player": "1.11.1",
|
|
51
|
+
"@meltemi/diagrams-render": "1.11.1",
|
|
52
|
+
"@meltemi/diagrams-project": "1.11.1"
|
|
51
53
|
},
|
|
52
54
|
"publishConfig": {
|
|
53
55
|
"access": "public"
|
|
54
56
|
},
|
|
57
|
+
"meltemiRelease": {
|
|
58
|
+
"repository": "askenaz-dev/meltemi-diagrams",
|
|
59
|
+
"sha": "bdcca7b5a0690c564978e045e6d6d43f84d379ab",
|
|
60
|
+
"tag": "v1.11.1"
|
|
61
|
+
},
|
|
55
62
|
"scripts": {
|
|
56
63
|
"build": "tsc -p tsconfig.build.json",
|
|
57
64
|
"typecheck": "tsc -p tsconfig.json"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: meltemi-diagrams
|
|
3
|
+
description: Use when asked to draw, document or convert a system or software architecture, sketch a whiteboard or kanban board, turn a diagram or a screenshot into code, or change a `.meltemi` file. Creates, edits, converts and exports Meltemi diagrams as code (`.meltemi` files) - flow sheets with groups, plain containers, data stores, decisions, notes, tags and boxes in 24 shapes with icons, laid out by ELK, assisted or by hand, with ports, constraints, styles and themes; free compositions (slides with boxes, text, icons, images, layers and links to flow views, and whiteboards with pen strokes, stickies and kanban lists); sequence diagrams (participants, calls, replies, activations, notes, alt/opt/loop/par fragments, scenarios); C4 models with context, container and component views; motion effects and stories that walk through a diagram scene by scene; SVG, PNG, PDF and HTML-player exports with export profiles. Imports Mermaid and PlantUML instead of retyping them, and turns draw.io, HTML or SVG pictures into sheets.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
version: 1.11.1
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Meltemi Diagrams
|
|
10
|
+
|
|
11
|
+
Diagrams as code for the Meltemi family. A sheet is a `.meltemi` text file; the `meltemi-diagrams` command
|
|
12
|
+
compiles it, validates it and exports SVG. The text is the source of truth: when a `.meltemi` file
|
|
13
|
+
can produce the image, never draw the image by hand and never edit the exported SVG.
|
|
14
|
+
|
|
15
|
+
## Workflow
|
|
16
|
+
|
|
17
|
+
Before you start: `pnpm mdg --version` says which version of the command line is there; where
|
|
18
|
+
`mdg` is not on the path, `npx @meltemi/diagrams-cli@latest <command>` runs the same command. New
|
|
19
|
+
sheets go under `diagrams/`, or where the project keeps them (the `sources` of its
|
|
20
|
+
`project.json`). Cloud commands run only when the person asks
|
|
21
|
+
([reference/cloud.md](reference/cloud.md)), and a token (`mdg_…`) is never printed, read, copied
|
|
22
|
+
or committed.
|
|
23
|
+
|
|
24
|
+
1. Collect the pieces from the request or the source material: elements (boxes, data stores,
|
|
25
|
+
decisions), bands (groups), arrows with their labels, annotations (notes, tags), what is
|
|
26
|
+
proposed versus in use, and the numbered steps of the main path. Choose the profile: `flow` for
|
|
27
|
+
an architecture or process that ELK can arrange, `free` for a slide or a hand-composed picture
|
|
28
|
+
with headings, icons and images, or a whiteboard with pen strokes, stickies and kanban columns
|
|
29
|
+
under `preset board` ([reference/free.md](reference/free.md)), `sequence` for an
|
|
30
|
+
interaction over time between participants ([reference/sequence.md](reference/sequence.md)),
|
|
31
|
+
a `model` with `c4` views for a C4 architecture (context, containers, components) shared by
|
|
32
|
+
several pictures ([reference/c4.md](reference/c4.md)).
|
|
33
|
+
To walk an audience through a diagram, add a `story` after it
|
|
34
|
+
([reference/stories.md](reference/stories.md)).
|
|
35
|
+
A Mermaid or PlantUML source is imported, never retyped: `pnpm mdg import source.mmd` writes
|
|
36
|
+
`source.meltemi` and reports what it approximated or dropped
|
|
37
|
+
([reference/converting.md](reference/converting.md)).
|
|
38
|
+
A flow sheet becomes a slide with `pnpm mdg convert sheet.meltemi --to free`. Sequence
|
|
39
|
+
diagrams have no layout modes or positions: skip step 2's layout choice and write the events in
|
|
40
|
+
order.
|
|
41
|
+
2. Write the file from the template below. Pick the layout mode:
|
|
42
|
+
- `layout auto` (the default) for a new sheet: write no positions; ELK arranges the boxes in the
|
|
43
|
+
`direction` (`LR`, `TB`, ...). Add constraints such as `row a, b` when you need alignment.
|
|
44
|
+
- `layout assisted` to pin some boxes with `place` and let ELK arrange the rest around them.
|
|
45
|
+
- `layout manual` when converting a picture or matching an original exactly: give every element
|
|
46
|
+
a `place`, and a `size` to any box whose width matters.
|
|
47
|
+
|
|
48
|
+
`pnpm mdg layout path/to/sheet.meltemi` writes ELK's positions into the file as `place`
|
|
49
|
+
statements (and turns `auto` into `assisted`) when you want to fine-tune them by hand. Keep the
|
|
50
|
+
labels in the language of the source.
|
|
51
|
+
For icons, find names with `pnpm mdg assets icons <words>` (for example `database`) and set
|
|
52
|
+
them with `style <id> { icon "tabler:database" }`; never invent an icon name. The icon goes
|
|
53
|
+
beside the text by default; `iconPosition right|top|bottom|center` moves it (`center` makes it
|
|
54
|
+
the picture of the box) and `iconSize 32` (8 to 128, or `auto`) sizes it. Architecture icons
|
|
55
|
+
with their label below, as provider diagrams draw them, are `shape icon`:
|
|
56
|
+
`style fn { icon "aws:lambda"; shape icon }` ([reference/free.md](reference/free.md)).
|
|
57
|
+
|
|
58
|
+
3. Validate: `pnpm mdg validate path/to/sheet.meltemi` (from the repository root). Fix every
|
|
59
|
+
diagnostic, each code explained in [reference/language.md](reference/language.md), until the
|
|
60
|
+
last line reads `Checked 1 file: 0 errors, 0 warnings.`, which is what success prints.
|
|
61
|
+
4. Export: `pnpm mdg export path/to/sheet.meltemi` writes `sheet.svg` beside the source.
|
|
62
|
+
`--stdout` prints it, `--theme editorial.light` gives a neutral sheet without the brand frame
|
|
63
|
+
and `--theme stage.light` a presentation sheet. `--format png` (with `--scale`, `--size 1920x1080`,
|
|
64
|
+
`--background transparent`) and `--format pdf` use the installed Edge or Chrome; an `export`
|
|
65
|
+
block in the file makes a delivery repeatable with `--profile <id>` or `--profiles`
|
|
66
|
+
([reference/language.md](reference/language.md)).
|
|
67
|
+
5. Review the SVG. When the person wants to look at it in a browser, start a server:
|
|
68
|
+
`pnpm preview` serves the repository at `http://localhost:4173` until stopped with Ctrl+C, and
|
|
69
|
+
tell them how to stop it. Check: no overlapping boxes, text fits its box, arrows leave and
|
|
70
|
+
enter the intended sides, labels are readable, a legend exists when colours carry meaning. The
|
|
71
|
+
export prints composition warnings (`layout.overlap`, `layout.label-under-connector`,
|
|
72
|
+
`layout.container-margin`, `layout.connector-overlap`); fix every one.
|
|
73
|
+
6. Iterate with small edits. Keep ids stable and keep the author's comments. Finish with
|
|
74
|
+
`pnpm mdg format path/to/sheet.meltemi` so the file has the canonical layout.
|
|
75
|
+
|
|
76
|
+
People who prefer a visual tool open the same file in VS Code with the Meltemi Diagrams extension
|
|
77
|
+
(`docs/editor.md`). It shows a canvas beside the code, and each drag, resize or inspector change is
|
|
78
|
+
a small edit of the text. A sheet you write stays editable there, and their changes come back as
|
|
79
|
+
ordinary text edits that you can review.
|
|
80
|
+
|
|
81
|
+
## Template
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
language "0.2"
|
|
85
|
+
|
|
86
|
+
diagram checkout: flow {
|
|
87
|
+
title "Checkout"
|
|
88
|
+
description "One sentence that becomes the <desc> of the SVG."
|
|
89
|
+
layout manual
|
|
90
|
+
|
|
91
|
+
group backend "Backend" {
|
|
92
|
+
api["Order API"] { description "REST · validates input" }
|
|
93
|
+
data orders "Orders DB" { description "PostgreSQL" }
|
|
94
|
+
}
|
|
95
|
+
client["Client app"] { description "web & mobile" }
|
|
96
|
+
tag jwt "JWT"
|
|
97
|
+
note limits "Limits to confirm\n500 rps per tenant"
|
|
98
|
+
|
|
99
|
+
client -> api: "1" as request
|
|
100
|
+
api -> orders: "2" as persist
|
|
101
|
+
|
|
102
|
+
placement {
|
|
103
|
+
place client at (120, 60); size client (160, 56)
|
|
104
|
+
place api at (420, 60); size api (200, 56)
|
|
105
|
+
place orders at (420, 200)
|
|
106
|
+
place jwt at (500, 32)
|
|
107
|
+
place limits at (700, 60)
|
|
108
|
+
label request at 50% offset (0, -16)
|
|
109
|
+
label persist at 50% offset (16, 0)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
style request, persist { fill "#0B1830"; color "#FFFFFF" }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Rules that save iterations
|
|
117
|
+
|
|
118
|
+
- Ids match `[A-Za-z_][A-Za-z0-9_]*` and are unique per diagram across elements and relations.
|
|
119
|
+
Labels carry the human text (`\n` breaks a label into lines); `description` is the smaller text
|
|
120
|
+
under the label, inside the box.
|
|
121
|
+
- Name every relation you need to route, position a label on, or style: `a -> b: "text" as id`.
|
|
122
|
+
- `place` is the centre of the box in pixels; y grows downwards. Groups fit their children unless
|
|
123
|
+
you give them both `place` and `size`.
|
|
124
|
+
- Default sizes: `node` 160 x 56, `decision` 160 x 80, `data` 160 x 64, widened automatically to
|
|
125
|
+
fit their text; a `shape` has its own default size ([reference/free.md](reference/free.md));
|
|
126
|
+
tags and notes size themselves from their text. Use `size` only to make boxes larger or equal;
|
|
127
|
+
if `layout.text-overflow` appears, use the width its hint gives.
|
|
128
|
+
- Shapes go on flow nodes and free boxes: `style api { shape hexagon }`, one of `rounded`,
|
|
129
|
+
`rectangle`, `square`, `ellipse`, `circle`, `diamond`, `cylinder`, `pill`, `triangle`,
|
|
130
|
+
`parallelogram`, `trapezoid`, `pentagon`, `hexagon`, `octagon`, `star`, `cloud`, `document`,
|
|
131
|
+
`callout`, `arrow`, `framed`, `flag`, `person`, `agent` (an AI agent: a robot head over a body
|
|
132
|
+
that holds the text), `icon` (the element drawn as its `icon` alone, 64 x 64, with the label
|
|
133
|
+
outside at `labelPosition`, `bottom` by default). Decisions, data stores, notes, tags and groups
|
|
134
|
+
draw their own shape and refuse it, and so do free texts, icons and images. Text stays inside
|
|
135
|
+
the shape and arrows end on its outline (on the icon's square for `shape icon`). `radius <n>`
|
|
136
|
+
(0 to 64) sets the corner radius of a node, box, decision, note or group, and rounds every
|
|
137
|
+
shape with corners: `rectangle` and `square` (square by default), the polygons' vertices (sharp
|
|
138
|
+
by default: `style h { shape hexagon; radius 10 }`); `radius 0` squares a rounded box,
|
|
139
|
+
`rule group { radius 0 }` squares every group. Circles, ellipses, pills, cylinders, clouds and
|
|
140
|
+
documents ignore it.
|
|
141
|
+
- `rotation <degrees>` (0 to 360, clockwise) turns an element of a flow or free diagram about the
|
|
142
|
+
centre of its box, text and icon included (`style label { rotation 270 }` for text up a side);
|
|
143
|
+
`place` and `size` stay those of the unturned box, and arrows meet the turned outline. Groups,
|
|
144
|
+
sequence diagrams and C4 views refuse it.
|
|
145
|
+
- Nested plain rectangles (a visual editor's containers) are untitled groups: `group g "" { ... }`
|
|
146
|
+
with `style g { fill "#dae8fc"; stroke "#6c8ebf"; radius 0 }`. They draw no title and reserve no
|
|
147
|
+
title band: their interior starts 16 px below the top edge instead of 40 px. A box meant to hold
|
|
148
|
+
others is such a group, never a `node` or `box` with boxes stacked on it: a stack is only drawn
|
|
149
|
+
on top, so the engine separates it and moving the outer box leaves the inner ones behind. A
|
|
150
|
+
group's `size` is the least the engine gives it in `layout auto`, its children centred.
|
|
151
|
+
- Boxes stacked on purpose, each strictly inside the one before, are declared outer first; give
|
|
152
|
+
the outer boxes empty labels (`box outer ""`) or labels away from the inner ones
|
|
153
|
+
(`textAlign left`, a separate `text`). A stack declared inner first, or two boxes of the same
|
|
154
|
+
size at the same place, is `layout.overlap`.
|
|
155
|
+
- A band behind boxes is an untitled group with `place`, `size` and `layer back` (with
|
|
156
|
+
`layers back, main`); boxes and groups on different layers are not checked against each other,
|
|
157
|
+
and the engine arranges boxes over the band, in `layout assisted` and, at the top level, in
|
|
158
|
+
`layout auto`.
|
|
159
|
+
- Arrows need no `route`: facing sides meet at the centre of their overlap, so boxes aligned in a
|
|
160
|
+
column get straight vertical arrows. Add `route id from a.east to b.north via [(x, y)]` only for
|
|
161
|
+
a bend.
|
|
162
|
+
- `place` is ignored in `layout auto` (`layout.placement-ignored`): write `layout manual` or
|
|
163
|
+
`layout assisted` whenever a sheet has positions.
|
|
164
|
+
- A box with named ports (`bus["Bus"] { port in west 30% }`) is entered at that exact point with
|
|
165
|
+
`route r from a.east to bus.in`. `routing orthogonal|polyline|bezier` changes the connector
|
|
166
|
+
shape for the diagram or, in a `style`, for one relation.
|
|
167
|
+
- Text takes a font, size and weight in every profile: `style api { font "Roboto"; fontSize 16;
|
|
168
|
+
fontWeight 600 }`, also in classes and rules (`rule note { font "Caveat" }`); icons, images and
|
|
169
|
+
pens refuse them. Name a bundled family, never a system one: Inter, IBM Plex Sans, Roboto, Open
|
|
170
|
+
Sans, Lato, Montserrat, Poppins, Nunito, Merriweather, Lora, Playfair Display, Cascadia Mono,
|
|
171
|
+
Roboto Mono, Source Code Pro, Patrick Hand, Caveat, Comic Neue (`pnpm mdg assets list` prints
|
|
172
|
+
them). A family nobody has is `layout.unmeasured-font` and measured with estimates.
|
|
173
|
+
- Dashed means proposed: one statement, `style a, b, c { line dashed }`.
|
|
174
|
+
- Numbered steps are relation labels `"1"`, `"2"` with `label r1 at 50% offset (16, 0)` and a
|
|
175
|
+
dark style; the narrative goes in comments next to the relations.
|
|
176
|
+
- A legend is a row of `tag` elements under the sheet with the category colours, plus a dashed
|
|
177
|
+
white tag for "proposed".
|
|
178
|
+
- One diagram per file unless they share a model; the SVG is named after the file.
|
|
179
|
+
- In `free` diagrams, a child's `place` is relative to its group's interior, every group has a
|
|
180
|
+
`place`, and the paint order is the source order within each layer: declare on-top objects last
|
|
181
|
+
or give them `layer front` (with `layers main, front`). A group with `layout column` or
|
|
182
|
+
`layout row` places its children itself, in source order: give them no `place`
|
|
183
|
+
(`layout.place-ignored`). Strokes are `pen` statements with their points, never lines with many
|
|
184
|
+
bends.
|
|
185
|
+
|
|
186
|
+
## When the result looks wrong
|
|
187
|
+
|
|
188
|
+
| Symptom | Fix |
|
|
189
|
+
| ------------------------------------------------ | -------------------------------------------------------------------------------- |
|
|
190
|
+
| `layout.missing-placement` | add `place <id> at (x, y)`; groups need `place` and `size` together or neither |
|
|
191
|
+
| `layout.placement-ignored` | the sheet has positions but no `layout`: add `layout manual` or `assisted` |
|
|
192
|
+
| `layout.constraint-conflict` | two placed boxes cannot satisfy the constraint; move one or drop the constraint |
|
|
193
|
+
| `layout.overlap` after `auto` or `assisted` | add `distance a, b 40` or pin one box with `place` |
|
|
194
|
+
| `layout.overlap` on boxes stacked on purpose | declaration order: declare the outer box before the boxes inside it |
|
|
195
|
+
| `layout.label-overlap` on a stack | the stacked labels coincide; leave the outer labels empty or move them |
|
|
196
|
+
| `prop.invalid-value` on `shape` | only `node` and `box` take `shape`; a decision is already a diamond |
|
|
197
|
+
| `layout.icon-missing`, a dashed square | `shape icon` needs an `icon` style on the element (or a rule or class) |
|
|
198
|
+
| `layout.label-position-ignored` | `labelPosition` needs `iconPosition center` or `shape icon`; remove it otherwise |
|
|
199
|
+
| a band pushes boxes or warns | make it an untitled group with `place`, `size` and `layer back` |
|
|
200
|
+
| `ref.unknown-relation` on `route`/`label` | the relation is anonymous: add `as <id>` to it |
|
|
201
|
+
| `prop.duplicate` in a style | the property is already set for that target in an earlier `style` |
|
|
202
|
+
| arrow crosses a box | add a waypoint with `route ... via [(x, y)]` or move the box |
|
|
203
|
+
| text wider than its box | add `size <id> (w, h)` or shorten the description |
|
|
204
|
+
| diagonal arrow where a straight one was expected | the two boxes do not overlap on that axis; align them or use a waypoint |
|
|
205
|
+
|
|
206
|
+
## Reference
|
|
207
|
+
|
|
208
|
+
- [reference/language.md](reference/language.md): grammar, vocabulary, diagnostics and fixes.
|
|
209
|
+
- [reference/layout.md](reference/layout.md): layout modes, constraints, ports, coordinates, sizes,
|
|
210
|
+
anchors, routes, groups, frame.
|
|
211
|
+
- [reference/themes.md](reference/themes.md): built-in themes, theme blocks, the brand frame.
|
|
212
|
+
- [reference/converting.md](reference/converting.md): importing Mermaid and PlantUML, turning any
|
|
213
|
+
other diagram or a screenshot into a sheet, with the checks that prove the conversion.
|
|
214
|
+
- [reference/free.md](reference/free.md): free compositions, the twenty-four shapes, icons in
|
|
215
|
+
boxes and as shapes, interior coordinates, plain containers, stacks and bands, layers, links,
|
|
216
|
+
images, the conversion from flow, and whiteboards: pen strokes, stickies and lists (kanban
|
|
217
|
+
boards).
|
|
218
|
+
- [reference/sequence.md](reference/sequence.md): sequence diagrams, fragments, scenarios,
|
|
219
|
+
spacing, their diagnostics and the mapping from Mermaid and PlantUML.
|
|
220
|
+
- [reference/c4.md](reference/c4.md): models, C4 views, levels and scopes, lifted and combined
|
|
221
|
+
relations, local labels, imported models and their diagnostics.
|
|
222
|
+
- [reference/stories.md](reference/stories.md): stories, scenes, actions, transitions, timing,
|
|
223
|
+
their diagnostics and frame exports.
|
|
224
|
+
- [reference/cloud.md](reference/cloud.md): sharing a project in the optional cloud service, only
|
|
225
|
+
when the person asks, and where its token lives.
|
|
226
|
+
- [examples/platform.meltemi](examples/platform.meltemi): a complete sheet using every construct.
|
|
227
|
+
- [examples/slide.meltemi](examples/slide.meltemi): a flow view and a free slide linked to it.
|
|
228
|
+
- [examples/sequence.meltemi](examples/sequence.meltemi): a payment sequence with a group, an
|
|
229
|
+
alternative, a loop, a note, an async message and two scenarios.
|
|
230
|
+
- [examples/architecture.meltemi](examples/architecture.meltemi): a model with a context, a
|
|
231
|
+
container and a component view.
|
|
232
|
+
- [examples/story.meltemi](examples/story.meltemi): a checkout flow and the story of a purchase.
|