@itsthecsguy/linework 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/NOTICE +11 -0
- package/README.md +164 -2
- package/assets/drawio/LICENSE +201 -0
- package/assets/drawio/VERSION +1 -0
- package/assets/drawio/favicon.ico +0 -0
- package/assets/drawio/images/1x1.png +0 -0
- package/assets/drawio/images/2x2.png +0 -0
- package/assets/drawio/images/3x3.png +0 -0
- package/assets/drawio/images/ajax-loader.gif +0 -0
- package/assets/drawio/images/android-chrome-512x512.png +0 -0
- package/assets/drawio/images/apple-touch-icon.png +0 -0
- package/assets/drawio/images/aui-wait.gif +0 -0
- package/assets/drawio/images/broken.png +0 -0
- package/assets/drawio/images/browserconfig.xml +9 -0
- package/assets/drawio/images/close.png +0 -0
- package/assets/drawio/images/cloud-connector-atlas.png +0 -0
- package/assets/drawio/images/collapsed.gif +0 -0
- package/assets/drawio/images/delete.png +0 -0
- package/assets/drawio/images/document-google-drive-icon-80.png +0 -0
- package/assets/drawio/images/draw-search.png +0 -0
- package/assets/drawio/images/drawio.svg +19 -0
- package/assets/drawio/images/drawlogo-color.svg +50 -0
- package/assets/drawio/images/drawlogo-gray.svg +18 -0
- package/assets/drawio/images/drawlogo-text-bottom.svg +75 -0
- package/assets/drawio/images/drawlogo.svg +18 -0
- package/assets/drawio/images/drawlogo128.png +0 -0
- package/assets/drawio/images/drawlogo144.png +0 -0
- package/assets/drawio/images/drawlogo256.png +0 -0
- package/assets/drawio/images/drawlogo48-gray.png +0 -0
- package/assets/drawio/images/drawlogo48.png +0 -0
- package/assets/drawio/images/drawlogo80.png +0 -0
- package/assets/drawio/images/droptarget.png +0 -0
- package/assets/drawio/images/edit.gif +0 -0
- package/assets/drawio/images/edit16.png +0 -0
- package/assets/drawio/images/edit32.png +0 -0
- package/assets/drawio/images/edit80.png +0 -0
- package/assets/drawio/images/embed-icon.png +0 -0
- package/assets/drawio/images/expanded.gif +0 -0
- package/assets/drawio/images/facebook.png +0 -0
- package/assets/drawio/images/favicon-16x16.png +0 -0
- package/assets/drawio/images/favicon-32x32.png +0 -0
- package/assets/drawio/images/file.png +0 -0
- package/assets/drawio/images/folder.png +0 -0
- package/assets/drawio/images/gadget-screenshot.png +0 -0
- package/assets/drawio/images/gadget-thumb.png +0 -0
- package/assets/drawio/images/github-logo-white.svg +6 -0
- package/assets/drawio/images/github-logo.svg +17 -0
- package/assets/drawio/images/gitlab-logo-white.svg +1 -0
- package/assets/drawio/images/gitlab-logo.svg +1 -0
- package/assets/drawio/images/glyphicons_blogger.png +0 -0
- package/assets/drawio/images/glyphicons_facebook.png +0 -0
- package/assets/drawio/images/glyphicons_github.png +0 -0
- package/assets/drawio/images/glyphicons_google.png +0 -0
- package/assets/drawio/images/glyphicons_star.png +0 -0
- package/assets/drawio/images/glyphicons_twitter.png +0 -0
- package/assets/drawio/images/glyphicons_youtube.png +0 -0
- package/assets/drawio/images/google-drive-connector-atlas.png +0 -0
- package/assets/drawio/images/google-drive-connector-atlas.svg +121 -0
- package/assets/drawio/images/google-drive-logo-white.svg +9 -0
- package/assets/drawio/images/google-drive-logo.svg +17 -0
- package/assets/drawio/images/google-share.png +0 -0
- package/assets/drawio/images/icon-192-maskable.png +0 -0
- package/assets/drawio/images/icon-192.png +0 -0
- package/assets/drawio/images/icon-512-maskable.png +0 -0
- package/assets/drawio/images/icon-512.png +0 -0
- package/assets/drawio/images/icon-search.svg +21 -0
- package/assets/drawio/images/img-hi-res.png +0 -0
- package/assets/drawio/images/img-lo-res.png +0 -0
- package/assets/drawio/images/list.svg +16 -0
- package/assets/drawio/images/locked.png +0 -0
- package/assets/drawio/images/log.png +0 -0
- package/assets/drawio/images/logo-confluence.png +0 -0
- package/assets/drawio/images/logo-flat-small.png +0 -0
- package/assets/drawio/images/logo-flat.png +0 -0
- package/assets/drawio/images/logo-jira.png +0 -0
- package/assets/drawio/images/logo-small.gif +0 -0
- package/assets/drawio/images/logo-white.png +0 -0
- package/assets/drawio/images/logo.png +0 -0
- package/assets/drawio/images/manifest.json +25 -0
- package/assets/drawio/images/move.png +0 -0
- package/assets/drawio/images/notion-logo-white.svg +1 -0
- package/assets/drawio/images/notion-logo.svg +1 -0
- package/assets/drawio/images/onedrive-connector-atlas.png +0 -0
- package/assets/drawio/images/onedrive-connector-atlas.svg +248 -0
- package/assets/drawio/images/onedrive-logo-white.svg +29 -0
- package/assets/drawio/images/onedrive-logo.svg +29 -0
- package/assets/drawio/images/osa_database.png +0 -0
- package/assets/drawio/images/osa_drive-harddisk.png +0 -0
- package/assets/drawio/images/plant-uml-atlas.png +0 -0
- package/assets/drawio/images/samepage-icon-color.svg +37 -0
- package/assets/drawio/images/save.png +0 -0
- package/assets/drawio/images/sidebar-android.png +0 -0
- package/assets/drawio/images/sidebar-archimate.png +0 -0
- package/assets/drawio/images/sidebar-arrows.png +0 -0
- package/assets/drawio/images/sidebar-arrows2.png +0 -0
- package/assets/drawio/images/sidebar-basic.png +0 -0
- package/assets/drawio/images/sidebar-c4.png +0 -0
- package/assets/drawio/images/sidebar-cabinets.png +0 -0
- package/assets/drawio/images/sidebar-cumulus.png +0 -0
- package/assets/drawio/images/sidebar-dfd.png +0 -0
- package/assets/drawio/images/sidebar-dynamics365.png +0 -0
- package/assets/drawio/images/sidebar-eip.png +0 -0
- package/assets/drawio/images/sidebar-er.png +0 -0
- package/assets/drawio/images/sidebar-floorplans.png +0 -0
- package/assets/drawio/images/sidebar-flowchart.png +0 -0
- package/assets/drawio/images/sidebar-gcp3.png +0 -0
- package/assets/drawio/images/sidebar-general.png +0 -0
- package/assets/drawio/images/sidebar-kubernetes.png +0 -0
- package/assets/drawio/images/sidebar-leanmapping.png +0 -0
- package/assets/drawio/images/sidebar-mockups.png +0 -0
- package/assets/drawio/images/sidebar-network.png +0 -0
- package/assets/drawio/images/sidebar-network2.png +0 -0
- package/assets/drawio/images/sidebar-openstack.png +0 -0
- package/assets/drawio/images/sidebar-sitemap.png +0 -0
- package/assets/drawio/images/sidebar-threatmodeling.png +0 -0
- package/assets/drawio/images/sidebar-uml.png +0 -0
- package/assets/drawio/images/sketch-logo128.png +0 -0
- package/assets/drawio/images/spin.gif +0 -0
- package/assets/drawio/images/sprites.png +0 -0
- package/assets/drawio/images/stop-flat-icon-80.png +0 -0
- package/assets/drawio/images/submenu.gif +0 -0
- package/assets/drawio/images/touch-submenu.png +0 -0
- package/assets/drawio/images/transparent.gif +0 -0
- package/assets/drawio/images/trello-logo-white-orange.svg +6 -0
- package/assets/drawio/images/trello-logo-white.svg +6 -0
- package/assets/drawio/images/trello-logo.svg +1 -0
- package/assets/drawio/images/tweet.png +0 -0
- package/assets/drawio/images/update16.png +0 -0
- package/assets/drawio/images/update32.png +0 -0
- package/assets/drawio/images/update80.png +0 -0
- package/assets/drawio/images/youtube.png +0 -0
- package/assets/drawio/img/clipart/Battery_0_128x128.png +0 -0
- package/assets/drawio/img/clipart/Battery_100_128x128.png +0 -0
- package/assets/drawio/img/clipart/Battery_50_128x128.png +0 -0
- package/assets/drawio/img/clipart/Battery_75_128x128.png +0 -0
- package/assets/drawio/img/clipart/Battery_allstates_128x128.png +0 -0
- package/assets/drawio/img/clipart/Bluetooth_128x128.png +0 -0
- package/assets/drawio/img/clipart/Earth_globe_128x128.png +0 -0
- package/assets/drawio/img/clipart/Empty_Folder_128x128.png +0 -0
- package/assets/drawio/img/clipart/Full_Folder_128x128.png +0 -0
- package/assets/drawio/img/clipart/Gear_128x128.png +0 -0
- package/assets/drawio/img/clipart/Keys_128x128.png +0 -0
- package/assets/drawio/img/clipart/Lock_128x128.png +0 -0
- package/assets/drawio/img/clipart/Mouse_Pointer_128x128.png +0 -0
- package/assets/drawio/img/clipart/Plug_128x128.png +0 -0
- package/assets/drawio/img/clipart/Ships_Wheel_128x128.png +0 -0
- package/assets/drawio/img/clipart/Star_128x128.png +0 -0
- package/assets/drawio/img/clipart/Tire_128x128.png +0 -0
- package/assets/drawio/index.html +92 -0
- package/assets/drawio/js/PostConfig.js +7 -0
- package/assets/drawio/js/PreConfig.js +13 -0
- package/assets/drawio/js/app.min.js +18466 -0
- package/assets/drawio/js/bootstrap.js +428 -0
- package/assets/drawio/js/diagramly/LibavoidRouting.js +4459 -0
- package/assets/drawio/js/export-init.js +21 -0
- package/assets/drawio/js/extensions.min.js +10478 -0
- package/assets/drawio/js/libavoid-js/LICENSE +505 -0
- package/assets/drawio/js/libavoid-js/libavoid-routing.js +1633 -0
- package/assets/drawio/js/libavoid-js/libavoid.min.js +59 -0
- package/assets/drawio/js/main.js +22 -0
- package/assets/drawio/js/rough/rough.min.js +1 -0
- package/assets/drawio/js/shapes-14-6-5.min.js +3457 -0
- package/assets/drawio/js/stencils.min.js +447 -0
- package/assets/drawio/mxgraph/css/common.css +147 -0
- package/assets/drawio/resources/dia.txt +2901 -0
- package/assets/drawio/styles/dark-default.xml +109 -0
- package/assets/drawio/styles/default-old.xml +109 -0
- package/assets/drawio/styles/default.xml +203 -0
- package/assets/drawio/styles/fonts/ArchitectsDaughter-Regular.ttf +0 -0
- package/assets/drawio/styles/grapheditor.css +4501 -0
- package/assets/drawio/styles/high-contrast.css +128 -0
- package/assets/drawio/styles/thumb_horz.png +0 -0
- package/assets/drawio/styles/thumb_vertical.png +0 -0
- package/assets/fonts/ibm-plex-mono-LICENSE.txt +93 -0
- package/assets/fonts/ibm-plex-sans-LICENSE.txt +93 -0
- package/assets/fonts/kalam-400.woff2 +0 -0
- package/assets/fonts/kalam-700.woff2 +0 -0
- package/assets/fonts/kalam-LICENSE.txt +93 -0
- package/assets/fonts/plex-mono-400.woff2 +0 -0
- package/assets/fonts/plex-mono-600.woff2 +0 -0
- package/assets/fonts/plex-sans-400.woff2 +0 -0
- package/assets/fonts/plex-sans-600.woff2 +0 -0
- package/dist/api.js +181 -0
- package/dist/cli.js +262 -0
- package/dist/drawio/icons.js +121 -0
- package/dist/drawio/merge.js +309 -0
- package/dist/drawio/read.js +180 -0
- package/dist/drawio/show.js +501 -0
- package/dist/drawio/theme.js +425 -0
- package/dist/drawio/write.js +300 -0
- package/dist/edit/server.js +212 -0
- package/dist/git.js +46 -0
- package/dist/import/mermaid.js +569 -0
- package/dist/layout/layout.js +212 -0
- package/dist/layout/sequence.js +333 -0
- package/dist/layout/text.js +73 -0
- package/dist/lint/check.js +184 -0
- package/dist/lint/fixlabels.js +128 -0
- package/dist/mcp/server.js +239 -0
- package/dist/publish.js +121 -0
- package/dist/render/browser.js +210 -0
- package/dist/render/fonts.js +66 -0
- package/dist/report.js +36 -0
- package/dist/spec/model.js +53 -0
- package/dist/spec/parse.js +497 -0
- package/package.json +71 -4
- package/skills/linework/SKILL.md +214 -0
- package/skills/linework/examples/billing-er.drawio.svg +4 -0
- package/skills/linework/examples/billing-er.yaml +31 -0
- package/skills/linework/examples/checkout.drawio.svg +4 -0
- package/skills/linework/examples/checkout.yaml +28 -0
- package/skills/linework/examples/pay-sequence.drawio.svg +4 -0
- package/skills/linework/examples/pay-sequence.yaml +28 -0
- package/skills/linework/examples/payment-state.drawio.svg +4 -0
- package/skills/linework/examples/payment-state.yaml +22 -0
- package/skills/linework/examples/refund-flow.drawio.svg +4 -0
- package/skills/linework/examples/refund-flow.yaml +29 -0
- package/skills/linework/examples/serverless-aws.drawio.svg +4 -0
- package/skills/linework/examples/serverless-aws.yaml +23 -0
- package/skills/linework/references/google-docs.md +46 -0
- package/skills/linework/references/spec.md +312 -0
- package/skills/linework/references/style-guide.md +53 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// Styles ("themes"). Output is plain draw.io style strings, so a person editing in
|
|
2
|
+
// draw.io sees and can change ordinary styles.
|
|
3
|
+
//
|
|
4
|
+
// Color rules, used by every style:
|
|
5
|
+
// - Nodes are neutral unless color means something. Semantic kinds (error, success,
|
|
6
|
+
// start/end) always carry their color. `soft` and `sketch` also tint by kind.
|
|
7
|
+
// - `color: <name>` tints a node, group or edge. `highlight: true` makes it the
|
|
8
|
+
// strongest thing on the page: a solid fill for nodes, a thick colored line for edges.
|
|
9
|
+
import { kindDef } from '../spec/model.js';
|
|
10
|
+
const SYSTEM = '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif';
|
|
11
|
+
export const FONT_STACK = `IBM Plex Sans, ${SYSTEM}`;
|
|
12
|
+
const SANS = { face: 'IBM Plex Sans', family: FONT_STACK, size: { label: 14, sub: 12, edge: 12, group: 12, field: 13 }, strong: 600, upperGroups: false };
|
|
13
|
+
const KALAM = { face: 'Kalam', family: `Kalam, Comic Sans MS, ${SYSTEM}`, size: { label: 16, sub: 14, edge: 14, group: 15, field: 15 }, strong: 700, upperGroups: false };
|
|
14
|
+
const MONO = {
|
|
15
|
+
face: 'IBM Plex Mono',
|
|
16
|
+
family: 'IBM Plex Mono, SF Mono, Menlo, Consolas, monospace',
|
|
17
|
+
size: { label: 13, sub: 11, edge: 11, group: 11, field: 12 },
|
|
18
|
+
strong: 600,
|
|
19
|
+
upperGroups: true,
|
|
20
|
+
};
|
|
21
|
+
/** Line height draw.io uses for HTML labels. */
|
|
22
|
+
export const LINE = 1.2;
|
|
23
|
+
export function typeface(theme = 'clean') {
|
|
24
|
+
return theme === 'sketch' ? KALAM : theme === 'blueprint' ? MONO : SANS;
|
|
25
|
+
}
|
|
26
|
+
/** Back-compat: sizes of the default style. */
|
|
27
|
+
export const font = SANS.size;
|
|
28
|
+
const LIGHT_SW = {
|
|
29
|
+
blue: { tint: '#EEF1FF', line: '#A9B8FF', ink: '#1E36B8', solid: '#2B50F5', edge: '#2B50F5' },
|
|
30
|
+
violet: { tint: '#F5F3FF', line: '#C4B5FD', ink: '#5B21B6', solid: '#7C3AED', edge: '#7C3AED' },
|
|
31
|
+
pink: { tint: '#FDF2F8', line: '#F9A8D4', ink: '#9D174D', solid: '#DB2777', edge: '#DB2777' },
|
|
32
|
+
red: { tint: '#FEF2F2', line: '#FCA5A5', ink: '#991B1B', solid: '#DC2626', edge: '#DC2626' },
|
|
33
|
+
orange: { tint: '#FFF7ED', line: '#FDBA74', ink: '#9A3412', solid: '#C2410C', edge: '#EA580C' },
|
|
34
|
+
amber: { tint: '#FFFBEB', line: '#FCD34D', ink: '#92400E', solid: '#B45309', edge: '#D97706' },
|
|
35
|
+
green: { tint: '#F0FDF4', line: '#86EFAC', ink: '#166534', solid: '#15803D', edge: '#16A34A' },
|
|
36
|
+
teal: { tint: '#F0FDFA', line: '#5EEAD4', ink: '#115E59', solid: '#0F766E', edge: '#0D9488' },
|
|
37
|
+
gray: { tint: '#F4F4F5', line: '#D4D4D8', ink: '#3F3F46', solid: '#52525B', edge: '#71717A' },
|
|
38
|
+
};
|
|
39
|
+
// Soft: borderless cards, so the tint has to carry the color on its own.
|
|
40
|
+
const SOFT_TINT = {
|
|
41
|
+
blue: '#E0E6FF',
|
|
42
|
+
violet: '#EDE9FE',
|
|
43
|
+
pink: '#FCE7F3',
|
|
44
|
+
red: '#FEE2E2',
|
|
45
|
+
orange: '#FFEDD5',
|
|
46
|
+
amber: '#FEF3C7',
|
|
47
|
+
green: '#DCFCE7',
|
|
48
|
+
teal: '#CCFBF1',
|
|
49
|
+
gray: '#F4F4F5',
|
|
50
|
+
};
|
|
51
|
+
const DARK_SW = {
|
|
52
|
+
blue: { tint: '#141B47', line: '#2A3A99', ink: '#C7D0FF', solid: '#2B50F5', edge: '#7D93FF' },
|
|
53
|
+
violet: { tint: '#2E1065', line: '#5B21B6', ink: '#DDD6FE', solid: '#7C3AED', edge: '#A78BFA' },
|
|
54
|
+
pink: { tint: '#500724', line: '#9D174D', ink: '#FBCFE8', solid: '#DB2777', edge: '#F472B6' },
|
|
55
|
+
red: { tint: '#450A0A', line: '#991B1B', ink: '#FECACA', solid: '#DC2626', edge: '#F87171' },
|
|
56
|
+
orange: { tint: '#431407', line: '#9A3412', ink: '#FED7AA', solid: '#C2410C', edge: '#FB923C' },
|
|
57
|
+
amber: { tint: '#451A03', line: '#92400E', ink: '#FDE68A', solid: '#B45309', edge: '#FBBF24' },
|
|
58
|
+
green: { tint: '#052E16', line: '#166534', ink: '#BBF7D0', solid: '#15803D', edge: '#4ADE80' },
|
|
59
|
+
teal: { tint: '#042F2E', line: '#115E59', ink: '#99F6E4', solid: '#0F766E', edge: '#2DD4BF' },
|
|
60
|
+
gray: { tint: '#27272A', line: '#52525B', ink: '#E4E4E7', solid: '#52525B', edge: '#A1A1AA' },
|
|
61
|
+
};
|
|
62
|
+
// Blueprint: light lines on deep blue. Color only changes the line.
|
|
63
|
+
const BP_LINE = {
|
|
64
|
+
blue: '#93C5FD',
|
|
65
|
+
violet: '#C4B5FD',
|
|
66
|
+
pink: '#F9A8D4',
|
|
67
|
+
red: '#FCA5A5',
|
|
68
|
+
orange: '#FDBA74',
|
|
69
|
+
amber: '#FDE68A',
|
|
70
|
+
green: '#86EFAC',
|
|
71
|
+
teal: '#5EEAD4',
|
|
72
|
+
gray: '#CBD5E1',
|
|
73
|
+
};
|
|
74
|
+
function lightPaint(sw, neutral, text, muted, accent) {
|
|
75
|
+
return (color, highlight) => {
|
|
76
|
+
if (highlight) {
|
|
77
|
+
const s = sw[color === 'neutral' || color === 'ink' ? accent : color];
|
|
78
|
+
return { fill: s.solid, stroke: s.solid, text: '#FFFFFF', sub: 'rgba(255,255,255,0.78)', strokeWidth: 1 };
|
|
79
|
+
}
|
|
80
|
+
if (color === 'ink')
|
|
81
|
+
return { fill: text, stroke: text, text: '#FFFFFF', sub: muted, strokeWidth: 1 };
|
|
82
|
+
if (color === 'neutral')
|
|
83
|
+
return { ...neutral, text, sub: muted, strokeWidth: 1 };
|
|
84
|
+
const s = sw[color];
|
|
85
|
+
return { fill: s.tint, stroke: s.line, text, sub: s.ink, strokeWidth: 1 };
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const CLEAN = {
|
|
89
|
+
name: 'clean',
|
|
90
|
+
type: SANS,
|
|
91
|
+
canvas: '#FFFFFF',
|
|
92
|
+
text: '#17171C',
|
|
93
|
+
muted: '#6B6B76',
|
|
94
|
+
edge: '#A0A0AB',
|
|
95
|
+
edgeText: '#52525C',
|
|
96
|
+
edgeWidth: 1.25,
|
|
97
|
+
accent: 'blue',
|
|
98
|
+
kindColors: false,
|
|
99
|
+
radius: 10,
|
|
100
|
+
group: { fill: '#F5F5F7', nested: '#EBEBEF', stroke: 'none', dashed: false, text: '#6B6B76' },
|
|
101
|
+
note: { fill: '#FFF4CC', stroke: 'none', text: '#5C4300' },
|
|
102
|
+
well: '#F3F3F5',
|
|
103
|
+
paint: lightPaint(LIGHT_SW, { fill: '#FFFFFF', stroke: '#DADAE0' }, '#17171C', '#6B6B76', 'blue'),
|
|
104
|
+
edgePaint: (color, hl) => {
|
|
105
|
+
const s = LIGHT_SW[color ?? 'blue'];
|
|
106
|
+
if (hl)
|
|
107
|
+
return { stroke: s.edge, text: s.ink, width: 2 };
|
|
108
|
+
return color ? { stroke: s.edge, text: s.ink, width: 1.25 } : { stroke: CLEAN.edge, text: CLEAN.edgeText, width: 1.25 };
|
|
109
|
+
},
|
|
110
|
+
extra: '',
|
|
111
|
+
};
|
|
112
|
+
const SOFT = {
|
|
113
|
+
...CLEAN,
|
|
114
|
+
name: 'soft',
|
|
115
|
+
kindColors: true,
|
|
116
|
+
radius: 12,
|
|
117
|
+
group: { fill: '#F7F7F9', nested: '#EFEFF3', stroke: 'none', dashed: false, text: '#6B6B76' },
|
|
118
|
+
paint: (color, hl) => {
|
|
119
|
+
if (hl || color === 'ink')
|
|
120
|
+
return CLEAN.paint(color, hl);
|
|
121
|
+
const c = color === 'neutral' ? 'gray' : color;
|
|
122
|
+
return { fill: SOFT_TINT[c], stroke: 'none', text: c === 'gray' ? '#17171C' : LIGHT_SW[c].ink, sub: c === 'gray' ? '#6B6B76' : LIGHT_SW[c].ink + 'B3', strokeWidth: 1 };
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const DARK = {
|
|
126
|
+
name: 'dark',
|
|
127
|
+
type: SANS,
|
|
128
|
+
canvas: '#0D1117',
|
|
129
|
+
text: '#E6EDF3',
|
|
130
|
+
muted: '#9198A1',
|
|
131
|
+
edge: '#6E7681',
|
|
132
|
+
edgeText: '#9198A1',
|
|
133
|
+
edgeWidth: 1.25,
|
|
134
|
+
accent: 'blue',
|
|
135
|
+
kindColors: false,
|
|
136
|
+
radius: 10,
|
|
137
|
+
group: { fill: '#151B23', nested: '#1C232D', stroke: 'none', dashed: false, text: '#9198A1' },
|
|
138
|
+
note: { fill: '#3A2F0B', stroke: 'none', text: '#F8E3A1' },
|
|
139
|
+
well: '#262C36',
|
|
140
|
+
paint: (color, hl) => {
|
|
141
|
+
if (hl) {
|
|
142
|
+
const s = DARK_SW[color === 'neutral' || color === 'ink' ? 'blue' : color];
|
|
143
|
+
return { fill: s.solid, stroke: s.solid, text: '#FFFFFF', sub: 'rgba(255,255,255,0.78)', strokeWidth: 1 };
|
|
144
|
+
}
|
|
145
|
+
if (color === 'ink')
|
|
146
|
+
return { fill: '#E6EDF3', stroke: '#E6EDF3', text: '#0D1117', sub: '#59636E', strokeWidth: 1 };
|
|
147
|
+
if (color === 'neutral')
|
|
148
|
+
return { fill: '#212830', stroke: '#3D444D', text: '#E6EDF3', sub: '#9198A1', strokeWidth: 1 };
|
|
149
|
+
const s = DARK_SW[color];
|
|
150
|
+
return { fill: s.tint, stroke: s.line, text: '#E6EDF3', sub: s.ink, strokeWidth: 1 };
|
|
151
|
+
},
|
|
152
|
+
edgePaint: (color, hl) => {
|
|
153
|
+
const s = DARK_SW[color ?? 'blue'];
|
|
154
|
+
if (hl)
|
|
155
|
+
return { stroke: s.edge, text: s.ink, width: 2 };
|
|
156
|
+
return color ? { stroke: s.edge, text: s.ink, width: 1.25 } : { stroke: DARK.edge, text: DARK.edgeText, width: 1.25 };
|
|
157
|
+
},
|
|
158
|
+
extra: '',
|
|
159
|
+
};
|
|
160
|
+
const SKETCH_INK = '#1E1E1E';
|
|
161
|
+
const SKETCH = {
|
|
162
|
+
name: 'sketch',
|
|
163
|
+
type: KALAM,
|
|
164
|
+
canvas: '#FFFFFF',
|
|
165
|
+
text: SKETCH_INK,
|
|
166
|
+
muted: '#5C5C66',
|
|
167
|
+
edge: '#3F3F46',
|
|
168
|
+
edgeText: '#3F3F46',
|
|
169
|
+
edgeWidth: 1.5,
|
|
170
|
+
accent: 'red',
|
|
171
|
+
kindColors: true,
|
|
172
|
+
radius: 8,
|
|
173
|
+
group: { fill: 'none', stroke: '#71717A', dashed: true, text: '#3F3F46' },
|
|
174
|
+
note: { fill: '#FEF08A', stroke: SKETCH_INK, text: SKETCH_INK },
|
|
175
|
+
well: '#E4E4E7',
|
|
176
|
+
paint: (color, hl) => {
|
|
177
|
+
// Solid pastel fills: hachure lines look the part but run through the text.
|
|
178
|
+
const solid = 'fillStyle=solid;';
|
|
179
|
+
if (color === 'ink')
|
|
180
|
+
return { fill: SKETCH_INK, stroke: SKETCH_INK, text: '#FFFFFF', sub: '#5C5C66', strokeWidth: 1.5, extra: solid };
|
|
181
|
+
if (hl) {
|
|
182
|
+
const s = LIGHT_SW[color === 'neutral' ? 'red' : color];
|
|
183
|
+
return { fill: s.line, stroke: s.solid, text: SKETCH_INK, sub: s.ink, strokeWidth: 2.5, extra: solid };
|
|
184
|
+
}
|
|
185
|
+
if (color === 'neutral')
|
|
186
|
+
return { fill: '#FFFFFF', stroke: SKETCH_INK, text: SKETCH_INK, sub: '#5C5C66', strokeWidth: 1.5, extra: solid };
|
|
187
|
+
return { fill: SOFT_TINT[color], stroke: SKETCH_INK, text: SKETCH_INK, sub: '#5C5C66', strokeWidth: 1.5, extra: solid };
|
|
188
|
+
},
|
|
189
|
+
edgePaint: (color, hl) => {
|
|
190
|
+
if (!color && !hl)
|
|
191
|
+
return { stroke: SKETCH.edge, text: SKETCH.edgeText, width: 1.5 };
|
|
192
|
+
const s = LIGHT_SW[color ?? 'red'];
|
|
193
|
+
return { stroke: s.solid, text: s.ink, width: hl ? 2.5 : 1.5 };
|
|
194
|
+
},
|
|
195
|
+
extra: 'sketch=1;curveFitting=1;jiggle=2;',
|
|
196
|
+
};
|
|
197
|
+
const BP_BG = '#0F3460';
|
|
198
|
+
const BP_INK = '#EAF2FF';
|
|
199
|
+
const BP_HI = '#FFD43B';
|
|
200
|
+
const BLUEPRINT = {
|
|
201
|
+
name: 'blueprint',
|
|
202
|
+
type: MONO,
|
|
203
|
+
canvas: BP_BG,
|
|
204
|
+
text: BP_INK,
|
|
205
|
+
muted: '#9DB8DF',
|
|
206
|
+
edge: '#9DB8DF',
|
|
207
|
+
edgeText: '#C9DBF5',
|
|
208
|
+
edgeWidth: 1.25,
|
|
209
|
+
accent: 'amber',
|
|
210
|
+
kindColors: false,
|
|
211
|
+
radius: 4,
|
|
212
|
+
group: { fill: 'none', stroke: '#5B82B8', dashed: true, text: '#9DB8DF' },
|
|
213
|
+
note: { fill: '#16467E', stroke: '#5B82B8', text: BP_INK },
|
|
214
|
+
well: '#16467E',
|
|
215
|
+
paint: (color, hl) => {
|
|
216
|
+
if (hl)
|
|
217
|
+
return { fill: BP_HI, stroke: BP_HI, text: BP_BG, sub: '#3A4A63', strokeWidth: 1 };
|
|
218
|
+
if (color === 'ink')
|
|
219
|
+
return { fill: BP_INK, stroke: BP_INK, text: BP_BG, sub: BP_BG, strokeWidth: 1 };
|
|
220
|
+
if (color === 'neutral')
|
|
221
|
+
return { fill: BP_BG, stroke: BP_INK, text: BP_INK, sub: '#9DB8DF', strokeWidth: 1.25 };
|
|
222
|
+
return { fill: BP_BG, stroke: BP_LINE[color], text: BP_INK, sub: BP_LINE[color], strokeWidth: 1.25 };
|
|
223
|
+
},
|
|
224
|
+
edgePaint: (color, hl) => {
|
|
225
|
+
if (hl)
|
|
226
|
+
return { stroke: BP_HI, text: BP_HI, width: 2 };
|
|
227
|
+
return color ? { stroke: BP_LINE[color], text: BP_LINE[color], width: 1.25 } : { stroke: BLUEPRINT.edge, text: BLUEPRINT.edgeText, width: 1.25 };
|
|
228
|
+
},
|
|
229
|
+
extra: '',
|
|
230
|
+
};
|
|
231
|
+
// C4-PlantUML's colours for architecture kinds; other diagram types use `clean`.
|
|
232
|
+
const C4_KIND = {
|
|
233
|
+
person: { fill: '#08427B', stroke: '#073B6F', text: '#FFFFFF', type: 'Person' },
|
|
234
|
+
external: { fill: '#999999', stroke: '#8A8A8A', text: '#FFFFFF', type: 'External system' },
|
|
235
|
+
};
|
|
236
|
+
const C4_CONTAINER = { fill: '#438DD5', stroke: '#3C7FC0', text: '#FFFFFF', type: 'Container' };
|
|
237
|
+
const C4 = { ...CLEAN, name: 'c4', group: { fill: 'none', stroke: '#444444', dashed: true, text: '#444444' }, edge: '#707070', edgeText: '#444444' };
|
|
238
|
+
const THEMES_BY_NAME = { clean: CLEAN, soft: SOFT, dark: DARK, sketch: SKETCH, blueprint: BLUEPRINT, c4: C4 };
|
|
239
|
+
export function palette(theme = 'clean') {
|
|
240
|
+
return THEMES_BY_NAME[theme] ?? CLEAN;
|
|
241
|
+
}
|
|
242
|
+
const fontPart = (t, size, color) => `fontFamily=${t.type.family};fontSize=${size};fontColor=${color};`;
|
|
243
|
+
/** How a node of this kind looks. */
|
|
244
|
+
export function paintFor(type, kind, theme, look = {}) {
|
|
245
|
+
const t = palette(theme);
|
|
246
|
+
if (theme === 'c4' && type === 'architecture' && !look.highlight && !look.color) {
|
|
247
|
+
const c = C4_KIND[kind] ?? C4_CONTAINER;
|
|
248
|
+
return { fill: c.fill, stroke: c.stroke, text: c.text, sub: 'rgba(255,255,255,0.8)', strokeWidth: 1 };
|
|
249
|
+
}
|
|
250
|
+
const k = kindDef(type, kind);
|
|
251
|
+
// A highlight is the style's accent unless the spec names a color.
|
|
252
|
+
if (look.highlight)
|
|
253
|
+
return t.paint(look.color ?? 'neutral', true);
|
|
254
|
+
const base = look.color ?? (k.color === 'ink' || k.color === 'neutral' || t.kindColors || k.semantic ? k.color : 'neutral');
|
|
255
|
+
return t.paint(base, false);
|
|
256
|
+
}
|
|
257
|
+
const dashes = (t) => (t.name === 'sketch' ? 'dashed=1;dashPattern=8 6;' : 'dashed=1;dashPattern=5 4;');
|
|
258
|
+
export function nodeStyle(type, kind, theme = 'clean', look = {}) {
|
|
259
|
+
const k = kindDef(type, kind);
|
|
260
|
+
const t = palette(theme);
|
|
261
|
+
const c = paintFor(type, kind, theme, look);
|
|
262
|
+
// Soft cards have no border; a dashed kind still needs one to show its dashes.
|
|
263
|
+
const stroke = c.stroke === 'none' && k.dashed ? '#A1A1AA' : c.stroke;
|
|
264
|
+
const base = `html=1;whiteSpace=wrap;spacingLeft=${PAD_X - 4};spacingRight=${PAD_X - 4};${fontPart(t, t.type.size.label, c.text)}` +
|
|
265
|
+
`fillColor=${c.fill};strokeColor=${stroke};strokeWidth=${c.strokeWidth};` +
|
|
266
|
+
(k.dashed ? dashes(t) : '') +
|
|
267
|
+
t.extra +
|
|
268
|
+
(c.extra ?? '');
|
|
269
|
+
const r = t.radius;
|
|
270
|
+
switch (k.shape) {
|
|
271
|
+
case 'cylinder':
|
|
272
|
+
return `shape=cylinder3;boundedLbl=1;backgroundOutline=1;size=7;${base}`;
|
|
273
|
+
case 'pill':
|
|
274
|
+
return `rounded=1;arcSize=50;${base}`;
|
|
275
|
+
case 'diamond':
|
|
276
|
+
return `rhombus;${base}spacingLeft=0;spacingRight=0;`;
|
|
277
|
+
case 'parallelogram':
|
|
278
|
+
return `shape=parallelogram;perimeter=parallelogramPerimeter;fixedSize=1;size=14;${base}`;
|
|
279
|
+
case 'start':
|
|
280
|
+
return `ellipse;aspect=fixed;${base}`;
|
|
281
|
+
case 'end':
|
|
282
|
+
return `ellipse;shape=endState;aspect=fixed;${base}`;
|
|
283
|
+
case 'table': {
|
|
284
|
+
// An entity: header (startSize) with field rows stacked inside.
|
|
285
|
+
const neutral = !look.highlight && !look.color && !(t.kindColors && k.color !== 'neutral');
|
|
286
|
+
const head = neutral && theme !== 'c4' ? t.well : c.fill;
|
|
287
|
+
const border = stroke === 'none' ? head : neutral && theme !== 'c4' ? palette(theme).paint('neutral', false).stroke : stroke;
|
|
288
|
+
return (`shape=swimlane;childLayout=stackLayout;horizontal=1;startSize=${ER_HEAD};horizontalStack=0;resizeParent=1;resizeParentMax=0;` +
|
|
289
|
+
`resizeLast=0;collapsible=0;marginBottom=0;rounded=1;absoluteArcSize=1;arcSize=${r + 2};swimlaneFillColor=${t.canvas};fontStyle=1;` +
|
|
290
|
+
base.replace(/fillColor=[^;]*;strokeColor=[^;]*;/, `fillColor=${head};strokeColor=${border};`));
|
|
291
|
+
}
|
|
292
|
+
default:
|
|
293
|
+
return `rounded=1;absoluteArcSize=1;arcSize=${r * 2};${base}`;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** Horizontal text padding inside a node, each side. */
|
|
297
|
+
export const PAD_X = 16;
|
|
298
|
+
/** Vertical text padding inside a node, each side. */
|
|
299
|
+
export const PAD_Y = 12;
|
|
300
|
+
export const ER_HEAD = 36;
|
|
301
|
+
export const ER_ROW = 28;
|
|
302
|
+
export function fieldStyle(theme = 'clean') {
|
|
303
|
+
const t = palette(theme);
|
|
304
|
+
return ('text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=0;spacingRight=0;overflow=fill;' +
|
|
305
|
+
`rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;${fontPart(t, t.type.size.field, t.text)}`);
|
|
306
|
+
}
|
|
307
|
+
export function groupStyle(theme = 'clean', look = {}, depth = 0) {
|
|
308
|
+
const t = palette(theme);
|
|
309
|
+
let { fill, stroke, text } = t.group;
|
|
310
|
+
if (depth > 0 && t.group.nested)
|
|
311
|
+
fill = t.group.nested;
|
|
312
|
+
if (look.color || look.highlight) {
|
|
313
|
+
const p = t.paint(look.color ?? t.accent, false);
|
|
314
|
+
fill = theme === 'blueprint' || theme === 'sketch' ? 'none' : p.fill;
|
|
315
|
+
// A colored ground carries the color; only a highlight adds an outline.
|
|
316
|
+
stroke = look.highlight ? t.edgePaint(look.color, true).stroke : t.group.stroke === 'none' ? 'none' : p.stroke;
|
|
317
|
+
text = p.sub;
|
|
318
|
+
}
|
|
319
|
+
return (`rounded=1;absoluteArcSize=1;arcSize=${t.radius * 2 + 4};html=1;whiteSpace=wrap;container=1;collapsible=0;` +
|
|
320
|
+
`fillColor=${fill};strokeColor=${stroke};strokeWidth=${look.highlight ? 1.5 : 1};` +
|
|
321
|
+
(t.group.dashed ? dashes(t) : '') +
|
|
322
|
+
'verticalAlign=top;align=left;spacingLeft=14;spacingTop=9;' +
|
|
323
|
+
`${fontPart(t, t.type.size.group, text)}fontStyle=1;${t.extra}`);
|
|
324
|
+
}
|
|
325
|
+
/** Group title HTML: sentence case, except blueprint's drafting capitals. */
|
|
326
|
+
export function groupLabel(label, theme = 'clean') {
|
|
327
|
+
return typeface(theme).upperGroups ? `<span style="text-transform:uppercase;letter-spacing:0.06em">${esc(label)}</span>` : esc(label);
|
|
328
|
+
}
|
|
329
|
+
const ARROW = 'endArrow=block;endFill=1;endSize=5;';
|
|
330
|
+
const START_ARROW = 'startArrow=block;startFill=1;startSize=5;';
|
|
331
|
+
export function edgeStyle(style, theme = 'clean', look = {}, labelBg) {
|
|
332
|
+
const t = palette(theme);
|
|
333
|
+
const p = t.edgePaint(look.color, !!look.highlight);
|
|
334
|
+
const ends = style === 'line' ? 'endArrow=none;startArrow=none;' : style === 'bidir' ? `${ARROW}${START_ARROW}` : `${ARROW}startArrow=none;`;
|
|
335
|
+
return ('edgeStyle=orthogonalEdgeStyle;rounded=1;arcSize=16;html=1;' +
|
|
336
|
+
`strokeColor=${p.stroke};strokeWidth=${p.width};${ends}` +
|
|
337
|
+
(style === 'async' ? dashes(t) : '') +
|
|
338
|
+
`${fontPart(t, t.type.size.edge, p.text)}${look.highlight ? 'fontStyle=1;' : ''}labelBackgroundColor=${labelBg ?? t.canvas};labelPadding=1 5;${t.extra}`);
|
|
339
|
+
}
|
|
340
|
+
const ER_END = { one: 'ERmandOne', zero_or_one: 'ERzeroToOne', many: 'ERzeroToMany', one_or_many: 'ERoneToMany' };
|
|
341
|
+
export function erEdgeStyle(card, theme = 'clean', look = {}) {
|
|
342
|
+
const t = palette(theme);
|
|
343
|
+
const p = t.edgePaint(look.color, !!look.highlight);
|
|
344
|
+
return ('edgeStyle=orthogonalEdgeStyle;rounded=1;arcSize=16;html=1;' +
|
|
345
|
+
`strokeColor=${p.stroke};strokeWidth=${p.width};startArrow=${ER_END[card.from]};endArrow=${ER_END[card.to]};startFill=0;endFill=0;startSize=10;endSize=10;` +
|
|
346
|
+
(card.identifying ? '' : dashes(t)) +
|
|
347
|
+
`${fontPart(t, t.type.size.edge, p.text)}labelBackgroundColor=${t.canvas};labelPadding=1 5;${t.extra}`);
|
|
348
|
+
}
|
|
349
|
+
export function cardFromArrow(arrow) {
|
|
350
|
+
return Object.entries(ER_END).find(([, v]) => v === arrow)?.[0] ?? undefined;
|
|
351
|
+
}
|
|
352
|
+
export function lifelineStyle(kind, theme = 'clean', headH = 48, look = {}) {
|
|
353
|
+
const t = palette(theme);
|
|
354
|
+
const c = paintFor('sequence', kind, theme, look);
|
|
355
|
+
const k = kindDef('sequence', kind);
|
|
356
|
+
const stroke = c.stroke === 'none' ? (k.dashed ? '#A1A1AA' : c.fill) : c.stroke;
|
|
357
|
+
// The lifeline's dashed stem uses the stroke colour: keep it quiet even when the head is loud.
|
|
358
|
+
return (`shape=umlLifeline;perimeter=lifelinePerimeter;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;size=${headH};` +
|
|
359
|
+
`html=1;whiteSpace=wrap;rounded=1;absoluteArcSize=1;arcSize=${t.radius * 2};fillColor=${c.fill};strokeColor=${stroke};strokeWidth=${Math.min(1.5, c.strokeWidth)};` +
|
|
360
|
+
`spacingLeft=${PAD_X - 4};spacingRight=${PAD_X - 4};` +
|
|
361
|
+
(k.dashed ? dashes(t) : '') +
|
|
362
|
+
`${fontPart(t, t.type.size.label, c.text)}${t.extra}${c.extra ?? ''}`);
|
|
363
|
+
}
|
|
364
|
+
export function activationStyle(kind, theme = 'clean', look = {}) {
|
|
365
|
+
const t = palette(theme);
|
|
366
|
+
// A highlighted participant's bar is tinted, not solid: the head already shouts.
|
|
367
|
+
const c = look.highlight ? t.paint(look.color ?? (theme === 'sketch' ? 'red' : t.accent), false) : paintFor('sequence', kind, theme, look);
|
|
368
|
+
const neutral = c.stroke === t.paint('neutral', false).stroke && c.fill === t.paint('neutral', false).fill;
|
|
369
|
+
const fill = neutral ? t.well : c.fill;
|
|
370
|
+
const stroke = c.stroke === 'none' ? fill : neutral ? t.edge : c.stroke;
|
|
371
|
+
return `html=1;points=[];perimeter=orthogonalPerimeter;outlineConnect=0;targetShapes=umlLifeline;portConstraint=eastwest;fillColor=${fill};strokeColor=${stroke};strokeWidth=1;${t.extra}${c.extra ?? ''}`;
|
|
372
|
+
}
|
|
373
|
+
export function messageStyle(style, theme = 'clean', self = false, look = {}) {
|
|
374
|
+
const t = palette(theme);
|
|
375
|
+
const p = t.edgePaint(look.color, !!look.highlight);
|
|
376
|
+
const arrow = style === 'sync' ? 'endArrow=block;endFill=1;' : 'endArrow=open;endFill=0;';
|
|
377
|
+
return (`html=1;verticalAlign=bottom;${arrow}endSize=6;strokeWidth=${p.width};strokeColor=${p.stroke};` +
|
|
378
|
+
(style === 'reply' ? dashes(t) : '') +
|
|
379
|
+
(self ? 'edgeStyle=orthogonalEdgeStyle;rounded=1;align=left;spacingLeft=6;verticalAlign=middle;' : '') +
|
|
380
|
+
`${fontPart(t, t.type.size.edge, p.text)}${look.highlight ? 'fontStyle=1;' : ''}labelBackgroundColor=${t.canvas};labelPadding=1 5;${t.extra}`);
|
|
381
|
+
}
|
|
382
|
+
export function frameStyle(theme = 'clean') {
|
|
383
|
+
const t = palette(theme);
|
|
384
|
+
const line = t.name === 'dark' ? '#3D444D' : t.name === 'clean' || t.name === 'soft' || t.name === 'c4' ? '#DADAE0' : t.muted;
|
|
385
|
+
// The operator (alt, loop, opt) sits in the corner as a small label, not a UML tab.
|
|
386
|
+
return (`rounded=1;absoluteArcSize=1;arcSize=${t.radius * 2};whiteSpace=wrap;html=1;container=0;verticalAlign=top;align=left;spacing=0;spacingLeft=10;spacingTop=4;` +
|
|
387
|
+
`fillColor=none;strokeColor=${line};strokeWidth=1;${fontPart(t, t.type.size.group, t.muted)}fontStyle=1;${t.extra}`);
|
|
388
|
+
}
|
|
389
|
+
export function guardStyle(theme = 'clean') {
|
|
390
|
+
const t = palette(theme);
|
|
391
|
+
return `text;html=1;align=left;verticalAlign=middle;labelBackgroundColor=${t.canvas};${fontPart(t, t.type.size.edge, t.muted)}`;
|
|
392
|
+
}
|
|
393
|
+
export function separatorStyle(theme = 'clean') {
|
|
394
|
+
const t = palette(theme);
|
|
395
|
+
return `endArrow=none;dashed=1;dashPattern=4 4;html=1;strokeColor=${t.name === 'dark' ? '#3D444D' : t.name === 'clean' || t.name === 'soft' || t.name === 'c4' ? '#DADAE0' : t.muted};`;
|
|
396
|
+
}
|
|
397
|
+
export function noteStyle(theme = 'clean') {
|
|
398
|
+
const t = palette(theme);
|
|
399
|
+
const shape = t.name === 'sketch' ? 'shape=note;size=10;' : `rounded=1;absoluteArcSize=1;arcSize=${t.radius};`;
|
|
400
|
+
return `${shape}whiteSpace=wrap;html=1;fillColor=${t.note.fill};strokeColor=${t.note.stroke};strokeWidth=1;${fontPart(t, t.type.size.edge, t.note.text)}align=left;spacingLeft=10;spacingRight=10;${t.extra}`;
|
|
401
|
+
}
|
|
402
|
+
const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
403
|
+
/** HTML label: strong name, optional quieter tech line. C4 adds the element type. */
|
|
404
|
+
export function nodeLabel(label, tech, opts = {}) {
|
|
405
|
+
const theme = opts.theme ?? 'clean';
|
|
406
|
+
const tf = typeface(theme);
|
|
407
|
+
const main = `<span style="font-weight:${tf.strong}">${esc(label)}</span>`;
|
|
408
|
+
const sub = (text, color) => `${main}<br><span style="font-size:${tf.size.sub}px;color:${color}">${esc(text)}</span>`;
|
|
409
|
+
const p = paintFor(opts.type ?? 'architecture', opts.kind ?? '', theme, opts);
|
|
410
|
+
if (theme === 'c4' && opts.type === 'architecture' && !opts.highlight && !opts.color) {
|
|
411
|
+
const t = (C4_KIND[opts.kind ?? ''] ?? C4_CONTAINER).type;
|
|
412
|
+
return sub(`[${tech ? `${t}: ${tech}` : t}]`, p.sub);
|
|
413
|
+
}
|
|
414
|
+
return tech ? sub(tech, p.sub) : main;
|
|
415
|
+
}
|
|
416
|
+
/** One ER field row: name on the left, type and key markers on the right. */
|
|
417
|
+
export function fieldLabel(name, type, flags, theme = 'clean') {
|
|
418
|
+
const t = palette(theme);
|
|
419
|
+
const keys = flags.filter((f) => f === 'pk' || f === 'fk').map((f) => f.toUpperCase());
|
|
420
|
+
const rest = flags.filter((f) => f !== 'pk' && f !== 'fk');
|
|
421
|
+
const nameHtml = keys.includes('PK') ? `<span style="font-weight:${t.type.strong}">${esc(name)}</span>` : esc(name);
|
|
422
|
+
const right = [type, ...rest, ...keys].filter(Boolean).join(' ');
|
|
423
|
+
return `<table style="width:100%;height:100%;border-collapse:collapse"><tr><td style="padding:0 6px 0 ${FIELD_PAD}px">${nameHtml}</td><td style="text-align:right;padding:0 ${FIELD_PAD}px 0 6px;color:${t.muted}">${esc(right)}</td></tr></table>`;
|
|
424
|
+
}
|
|
425
|
+
export const FIELD_PAD = 14;
|