@kekonic/diagrams-studio 1.0.0-rc.4
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 +47 -0
- package/dist/browser/assets/SourceEditor-CTsSCxbb.js +23 -0
- package/dist/browser/assets/codicon-Brq4_Ui5.ttf +0 -0
- package/dist/browser/assets/dist-xSRpPWs-.js +49 -0
- package/dist/browser/assets/editor-CPgU7loC.css +1 -0
- package/dist/browser/assets/editor.api-Pc7YbpSQ.js +423 -0
- package/dist/browser/assets/editor.worker-r79G8Edd.js +30 -0
- package/dist/browser/assets/format-CEbYvQUd.css +1 -0
- package/dist/browser/assets/format-CX-V5tOl.js +425 -0
- package/dist/browser/assets/github-dark-C-LZuMrd.js +1 -0
- package/dist/browser/assets/github-light-EUqPIrTm.js +1 -0
- package/dist/browser/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/browser/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/browser/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/browser/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/browser/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/browser/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/browser/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/browser/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/browser/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/browser/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/browser/assets/index-D15KDLkg.css +1 -0
- package/dist/browser/assets/index-D4UABSTV.js +3411 -0
- package/dist/browser/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/dist/browser/assets/inter-latin-500-normal-DIm8MnOp.js +1 -0
- package/dist/browser/assets/language-BHyJ3vcB.js +11 -0
- package/dist/browser/assets/monaco-features-DiflRtGt.css +1 -0
- package/dist/browser/assets/monaco-features-i8AvDstT.js +28 -0
- package/dist/browser/assets/wasm-BnjxR4X6.js +1 -0
- package/dist/browser/favicon.svg +8 -0
- package/dist/browser/index.html +17 -0
- package/dist/index.d.mts +173 -0
- package/dist/index.mjs +457 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
|
2
|
+
<rect width="48" height="48" fill="#432990"/>
|
|
3
|
+
<path d="M14 36V12H34" stroke="#f8f7ff" stroke-width="5" stroke-linejoin="miter"/>
|
|
4
|
+
<path d="M14 24H29" stroke="#f8f7ff" stroke-width="5" stroke-linejoin="miter"/>
|
|
5
|
+
<rect x="29.5" y="7.5" width="9" height="9" fill="#f8f7ff"/>
|
|
6
|
+
<rect x="24.5" y="19.5" width="9" height="9" fill="#f8f7ff"/>
|
|
7
|
+
<rect x="9.5" y="31.5" width="9" height="9" fill="#f8f7ff"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" data-theme="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="color-scheme" content="dark light" />
|
|
7
|
+
<meta name="kdiagram-icon-api" content="./__kdiagram/icons" />
|
|
8
|
+
<meta name="kdiagram-public-studio" content="https://diagrams.kekonic.com/studio/" />
|
|
9
|
+
<link rel="icon" href="./favicon.svg" type="image/svg+xml" />
|
|
10
|
+
<title>Kekonic Diagrams Studio</title>
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-D4UABSTV.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-D15KDLkg.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="app"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Diagnostic, GraphModel, InteractiveRenderOptions, RenderResult, RenderStats, SourceRange, ThemeMode } from "@kekonic/diagrams";
|
|
2
|
+
|
|
3
|
+
//#region src/protocol.d.ts
|
|
4
|
+
declare const STUDIO_PROTOCOL_VERSION: 1;
|
|
5
|
+
type StudioDocument = {
|
|
6
|
+
id: string;
|
|
7
|
+
path: string;
|
|
8
|
+
label: string;
|
|
9
|
+
revision: number;
|
|
10
|
+
source: string;
|
|
11
|
+
};
|
|
12
|
+
type StudioCapabilities = {
|
|
13
|
+
write: boolean;
|
|
14
|
+
export: true;
|
|
15
|
+
};
|
|
16
|
+
type StudioSelection = {
|
|
17
|
+
documentId: string;
|
|
18
|
+
range?: SourceRange;
|
|
19
|
+
graphElement?: {
|
|
20
|
+
type: "node" | "edge";
|
|
21
|
+
id: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
type StudioViewport = {
|
|
25
|
+
zoom: number;
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
};
|
|
29
|
+
type StudioPresentation = {
|
|
30
|
+
theme: "dark" | "light";
|
|
31
|
+
options: InteractiveRenderOptions;
|
|
32
|
+
};
|
|
33
|
+
type StudioRender = {
|
|
34
|
+
documentId: string;
|
|
35
|
+
revision: number;
|
|
36
|
+
ok: boolean;
|
|
37
|
+
svg?: string;
|
|
38
|
+
diagnostics: Diagnostic[];
|
|
39
|
+
graph?: GraphModel;
|
|
40
|
+
stats: RenderStats;
|
|
41
|
+
};
|
|
42
|
+
type StudioServerMessage = {
|
|
43
|
+
version: 1;
|
|
44
|
+
type: "ready";
|
|
45
|
+
sessionId: string;
|
|
46
|
+
documents: StudioDocument[];
|
|
47
|
+
activeDocumentId: string;
|
|
48
|
+
capabilities: StudioCapabilities;
|
|
49
|
+
presentation: StudioPresentation;
|
|
50
|
+
} | ({
|
|
51
|
+
version: 1;
|
|
52
|
+
type: "document";
|
|
53
|
+
reason: "open" | "external" | "saved";
|
|
54
|
+
} & StudioDocument) | ({
|
|
55
|
+
version: 1;
|
|
56
|
+
type: "render";
|
|
57
|
+
} & StudioRender) | {
|
|
58
|
+
version: 1;
|
|
59
|
+
type: "selection";
|
|
60
|
+
selection: StudioSelection;
|
|
61
|
+
} | {
|
|
62
|
+
version: 1;
|
|
63
|
+
type: "viewport";
|
|
64
|
+
viewport: StudioViewport;
|
|
65
|
+
} | {
|
|
66
|
+
version: 1;
|
|
67
|
+
type: "presentation";
|
|
68
|
+
presentation: StudioPresentation;
|
|
69
|
+
} | {
|
|
70
|
+
version: 1;
|
|
71
|
+
type: "saved";
|
|
72
|
+
documentId: string;
|
|
73
|
+
revision: number;
|
|
74
|
+
} | {
|
|
75
|
+
version: 1;
|
|
76
|
+
type: "error";
|
|
77
|
+
code: string;
|
|
78
|
+
message: string;
|
|
79
|
+
requestType?: string;
|
|
80
|
+
};
|
|
81
|
+
type StudioClientMessage = {
|
|
82
|
+
version: 1;
|
|
83
|
+
type: "open";
|
|
84
|
+
documentId: string;
|
|
85
|
+
} | {
|
|
86
|
+
version: 1;
|
|
87
|
+
type: "source";
|
|
88
|
+
documentId: string;
|
|
89
|
+
revision: number;
|
|
90
|
+
source: string;
|
|
91
|
+
} | {
|
|
92
|
+
version: 1;
|
|
93
|
+
type: "save";
|
|
94
|
+
documentId: string;
|
|
95
|
+
revision: number;
|
|
96
|
+
source: string;
|
|
97
|
+
} | {
|
|
98
|
+
version: 1;
|
|
99
|
+
type: "selection";
|
|
100
|
+
selection: StudioSelection;
|
|
101
|
+
} | {
|
|
102
|
+
version: 1;
|
|
103
|
+
type: "viewport";
|
|
104
|
+
viewport: StudioViewport;
|
|
105
|
+
} | {
|
|
106
|
+
version: 1;
|
|
107
|
+
type: "presentation";
|
|
108
|
+
presentation: StudioPresentation;
|
|
109
|
+
};
|
|
110
|
+
declare function parseStudioClientMessage(value: unknown): StudioClientMessage;
|
|
111
|
+
declare function studioMessageJson(message: StudioServerMessage): string;
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/preview.d.ts
|
|
114
|
+
type StudioRenderer = (source: string, options: InteractiveRenderOptions) => Promise<RenderResult>;
|
|
115
|
+
type StudioPreviewCoordinator = {
|
|
116
|
+
render(documentId: string, revision: number, source: string, options: InteractiveRenderOptions): Promise<StudioRender | undefined>;
|
|
117
|
+
invalidate(documentId: string): void;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Drops stale async renders so every host observes monotonically newer document results.
|
|
121
|
+
* Rendering stays injected, keeping this coordinator browser-safe and renderer-neutral.
|
|
122
|
+
*/
|
|
123
|
+
declare function createStudioPreviewCoordinator(renderer: StudioRenderer): StudioPreviewCoordinator;
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/state.d.ts
|
|
126
|
+
type StudioState = {
|
|
127
|
+
connected: boolean;
|
|
128
|
+
sessionId?: string;
|
|
129
|
+
documents: StudioDocument[];
|
|
130
|
+
activeDocumentId?: string;
|
|
131
|
+
capabilities: StudioCapabilities;
|
|
132
|
+
presentation: StudioPresentation;
|
|
133
|
+
render?: StudioRender;
|
|
134
|
+
selection?: StudioSelection;
|
|
135
|
+
viewport: StudioViewport;
|
|
136
|
+
error?: {
|
|
137
|
+
code: string;
|
|
138
|
+
message: string;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
declare const INITIAL_STUDIO_STATE: StudioState;
|
|
142
|
+
declare function reduceStudioMessage(state: StudioState, message: StudioServerMessage): StudioState;
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/presentation.d.ts
|
|
145
|
+
type StudioPresentationControls = {
|
|
146
|
+
/** Diagram palette authored through `render.theme`. */theme: "dark" | "light";
|
|
147
|
+
direction: "" | "LR" | "RL" | "TD" | "BT";
|
|
148
|
+
density: "" | "compact" | "normal" | "spacious";
|
|
149
|
+
spacingScale: number | null;
|
|
150
|
+
nodePlacement: "" | "straight" | "balanced" | "basic";
|
|
151
|
+
groupLayout: "" | "auto" | "compound" | "flat" | "swimlane";
|
|
152
|
+
modelOrder: "" | "on" | "off";
|
|
153
|
+
groupGap: "" | "40" | "90" | "170";
|
|
154
|
+
edgeGaps: "" | "tight" | "normal" | "wide";
|
|
155
|
+
edgeStyle: "" | "metro" | "rounded" | "orthogonal" | "straight" | "bezier";
|
|
156
|
+
crossings: "" | "jumps" | "smart" | "gaps" | "none";
|
|
157
|
+
roundedCorners: boolean;
|
|
158
|
+
showPorts: boolean;
|
|
159
|
+
showBounds: boolean;
|
|
160
|
+
};
|
|
161
|
+
declare const DEFAULT_STUDIO_PRESENTATION: StudioPresentationControls;
|
|
162
|
+
declare function buildStudioRenderOptions(options: StudioPresentationControls, diagramTheme?: ThemeMode): InteractiveRenderOptions;
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/source-settings.d.ts
|
|
165
|
+
type StudioSourceSetting = "theme" | "direction" | "density" | "groupLayout" | "edgeStyle" | "crossings";
|
|
166
|
+
type StudioSourceSettings = Pick<StudioPresentationControls, StudioSourceSetting>;
|
|
167
|
+
declare function readStudioSourceSettings(source: string): StudioSourceSettings;
|
|
168
|
+
/** Apply one end-user diagram setting as a minimal source edit. Empty values remove the setting. */
|
|
169
|
+
declare function updateStudioSourceSetting(source: string, setting: StudioSourceSetting, value: string): string;
|
|
170
|
+
declare function resetStudioSourceSettings(source: string): string;
|
|
171
|
+
//#endregion
|
|
172
|
+
export { DEFAULT_STUDIO_PRESENTATION, INITIAL_STUDIO_STATE, STUDIO_PROTOCOL_VERSION, type StudioCapabilities, type StudioClientMessage, type StudioDocument, type StudioPresentation, type StudioPresentationControls, type StudioPreviewCoordinator, type StudioRender, type StudioRenderer, type StudioSelection, type StudioServerMessage, type StudioSourceSetting, type StudioSourceSettings, type StudioState, type StudioViewport, buildStudioRenderOptions, createStudioPreviewCoordinator, parseStudioClientMessage, readStudioSourceSettings, reduceStudioMessage, resetStudioSourceSettings, studioMessageJson, updateStudioSourceSetting };
|
|
173
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { parse } from "@kekonic/diagrams-core";
|
|
2
|
+
//#region src/protocol.ts
|
|
3
|
+
const STUDIO_PROTOCOL_VERSION = 1;
|
|
4
|
+
function parseStudioClientMessage(value) {
|
|
5
|
+
if (!isRecord(value) || value.version !== 1 || typeof value.type !== "string") throw new Error("Expected a version 1 studio message");
|
|
6
|
+
switch (value.type) {
|
|
7
|
+
case "open": return {
|
|
8
|
+
version: 1,
|
|
9
|
+
type: "open",
|
|
10
|
+
documentId: requiredString(value.documentId, "documentId")
|
|
11
|
+
};
|
|
12
|
+
case "source":
|
|
13
|
+
case "save": return {
|
|
14
|
+
version: 1,
|
|
15
|
+
type: value.type,
|
|
16
|
+
documentId: requiredString(value.documentId, "documentId"),
|
|
17
|
+
revision: requiredRevision(value.revision),
|
|
18
|
+
source: requiredString(value.source, "source", true)
|
|
19
|
+
};
|
|
20
|
+
case "selection": return {
|
|
21
|
+
version: 1,
|
|
22
|
+
type: "selection",
|
|
23
|
+
selection: parseSelection(value.selection)
|
|
24
|
+
};
|
|
25
|
+
case "viewport": return {
|
|
26
|
+
version: 1,
|
|
27
|
+
type: "viewport",
|
|
28
|
+
viewport: parseViewport(value.viewport)
|
|
29
|
+
};
|
|
30
|
+
case "presentation": return {
|
|
31
|
+
version: 1,
|
|
32
|
+
type: "presentation",
|
|
33
|
+
presentation: parsePresentation(value.presentation)
|
|
34
|
+
};
|
|
35
|
+
default: throw new Error(`Unknown studio message type: ${value.type}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function studioMessageJson(message) {
|
|
39
|
+
return JSON.stringify(message);
|
|
40
|
+
}
|
|
41
|
+
function parseSelection(value) {
|
|
42
|
+
if (!isRecord(value)) throw new Error("selection must be an object");
|
|
43
|
+
const selection = { documentId: requiredString(value.documentId, "selection.documentId") };
|
|
44
|
+
if (value.range != null) selection.range = value.range;
|
|
45
|
+
if (isRecord(value.graphElement)) {
|
|
46
|
+
const type = value.graphElement.type;
|
|
47
|
+
if (type !== "node" && type !== "edge") throw new Error("Invalid graph element type");
|
|
48
|
+
selection.graphElement = {
|
|
49
|
+
type,
|
|
50
|
+
id: requiredString(value.graphElement.id, "selection.graphElement.id")
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return selection;
|
|
54
|
+
}
|
|
55
|
+
function parseViewport(value) {
|
|
56
|
+
if (!isRecord(value)) throw new Error("viewport must be an object");
|
|
57
|
+
const zoom = requiredFinite(value.zoom, "viewport.zoom");
|
|
58
|
+
if (zoom < .1 || zoom > 8) throw new Error("viewport.zoom must be between 0.1 and 8");
|
|
59
|
+
return {
|
|
60
|
+
zoom,
|
|
61
|
+
x: requiredFinite(value.x, "viewport.x"),
|
|
62
|
+
y: requiredFinite(value.y, "viewport.y")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function parsePresentation(value) {
|
|
66
|
+
if (!isRecord(value) || value.theme !== "dark" && value.theme !== "light") throw new Error("presentation.theme must be dark or light");
|
|
67
|
+
if (!isRecord(value.options)) throw new Error("presentation.options must be an object");
|
|
68
|
+
return {
|
|
69
|
+
theme: value.theme,
|
|
70
|
+
options: value.options
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function requiredString(value, label, allowEmpty = false) {
|
|
74
|
+
if (typeof value !== "string" || !allowEmpty && value.length === 0) throw new Error(`${label} must be a string`);
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function requiredRevision(value) {
|
|
78
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new Error("revision must be a non-negative safe integer");
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
function requiredFinite(value, label) {
|
|
82
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${label} must be finite`);
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function isRecord(value) {
|
|
86
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/preview.ts
|
|
90
|
+
/**
|
|
91
|
+
* Drops stale async renders so every host observes monotonically newer document results.
|
|
92
|
+
* Rendering stays injected, keeping this coordinator browser-safe and renderer-neutral.
|
|
93
|
+
*/
|
|
94
|
+
function createStudioPreviewCoordinator(renderer) {
|
|
95
|
+
const generations = /* @__PURE__ */ new Map();
|
|
96
|
+
return {
|
|
97
|
+
async render(documentId, revision, source, options) {
|
|
98
|
+
const generation = (generations.get(documentId) ?? 0) + 1;
|
|
99
|
+
generations.set(documentId, generation);
|
|
100
|
+
const result = await renderer(source, options);
|
|
101
|
+
if (generations.get(documentId) !== generation) return void 0;
|
|
102
|
+
return {
|
|
103
|
+
documentId,
|
|
104
|
+
revision,
|
|
105
|
+
ok: result.ok,
|
|
106
|
+
svg: result.svg,
|
|
107
|
+
diagnostics: uniqueDiagnostics(result.diagnostics),
|
|
108
|
+
graph: result.graph,
|
|
109
|
+
stats: result.stats
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
invalidate(documentId) {
|
|
113
|
+
generations.set(documentId, (generations.get(documentId) ?? 0) + 1);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function uniqueDiagnostics(diagnostics) {
|
|
118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
119
|
+
return diagnostics.filter((diagnostic) => {
|
|
120
|
+
const { start, end } = diagnostic.range;
|
|
121
|
+
const key = `${diagnostic.code}:${diagnostic.severity}:${start.offset}:${end.offset}:${diagnostic.message}`;
|
|
122
|
+
if (seen.has(key)) return false;
|
|
123
|
+
seen.add(key);
|
|
124
|
+
return true;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/state.ts
|
|
129
|
+
const INITIAL_STUDIO_STATE = {
|
|
130
|
+
connected: false,
|
|
131
|
+
documents: [],
|
|
132
|
+
capabilities: {
|
|
133
|
+
write: false,
|
|
134
|
+
export: true
|
|
135
|
+
},
|
|
136
|
+
presentation: {
|
|
137
|
+
theme: "dark",
|
|
138
|
+
options: { theme: "dark" }
|
|
139
|
+
},
|
|
140
|
+
viewport: {
|
|
141
|
+
zoom: 1,
|
|
142
|
+
x: 0,
|
|
143
|
+
y: 0
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
function reduceStudioMessage(state, message) {
|
|
147
|
+
switch (message.type) {
|
|
148
|
+
case "ready": return {
|
|
149
|
+
...state,
|
|
150
|
+
connected: true,
|
|
151
|
+
sessionId: message.sessionId,
|
|
152
|
+
documents: message.documents,
|
|
153
|
+
activeDocumentId: message.activeDocumentId,
|
|
154
|
+
capabilities: message.capabilities,
|
|
155
|
+
presentation: message.presentation,
|
|
156
|
+
error: void 0
|
|
157
|
+
};
|
|
158
|
+
case "document": {
|
|
159
|
+
const documents = state.documents.some((item) => item.id === message.id) ? state.documents.map((item) => item.id === message.id ? documentFromMessage(message) : item) : [...state.documents, documentFromMessage(message)];
|
|
160
|
+
return {
|
|
161
|
+
...state,
|
|
162
|
+
documents,
|
|
163
|
+
activeDocumentId: message.id,
|
|
164
|
+
error: void 0
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
case "render":
|
|
168
|
+
if (message.documentId !== state.activeDocumentId) return state;
|
|
169
|
+
if (state.render && message.revision < state.render.revision) return state;
|
|
170
|
+
return {
|
|
171
|
+
...state,
|
|
172
|
+
render: message
|
|
173
|
+
};
|
|
174
|
+
case "selection": return {
|
|
175
|
+
...state,
|
|
176
|
+
selection: message.selection
|
|
177
|
+
};
|
|
178
|
+
case "viewport": return {
|
|
179
|
+
...state,
|
|
180
|
+
viewport: message.viewport
|
|
181
|
+
};
|
|
182
|
+
case "presentation": return {
|
|
183
|
+
...state,
|
|
184
|
+
presentation: message.presentation
|
|
185
|
+
};
|
|
186
|
+
case "saved": return state;
|
|
187
|
+
case "error": return {
|
|
188
|
+
...state,
|
|
189
|
+
error: {
|
|
190
|
+
code: message.code,
|
|
191
|
+
message: message.message
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function documentFromMessage(message) {
|
|
197
|
+
return {
|
|
198
|
+
id: message.id,
|
|
199
|
+
path: message.path,
|
|
200
|
+
label: message.label,
|
|
201
|
+
revision: message.revision,
|
|
202
|
+
source: message.source
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/presentation.ts
|
|
207
|
+
const DEFAULT_STUDIO_PRESENTATION = {
|
|
208
|
+
theme: "dark",
|
|
209
|
+
direction: "",
|
|
210
|
+
density: "",
|
|
211
|
+
spacingScale: null,
|
|
212
|
+
nodePlacement: "",
|
|
213
|
+
groupLayout: "",
|
|
214
|
+
modelOrder: "",
|
|
215
|
+
groupGap: "",
|
|
216
|
+
edgeGaps: "",
|
|
217
|
+
edgeStyle: "",
|
|
218
|
+
crossings: "",
|
|
219
|
+
roundedCorners: false,
|
|
220
|
+
showPorts: false,
|
|
221
|
+
showBounds: false
|
|
222
|
+
};
|
|
223
|
+
const EDGE_GAP_PRESETS = {
|
|
224
|
+
tight: {
|
|
225
|
+
edgeNodeSpacing: 28,
|
|
226
|
+
edgeEdgeSpacing: 14,
|
|
227
|
+
edgeLabelSpacing: 10
|
|
228
|
+
},
|
|
229
|
+
normal: {
|
|
230
|
+
edgeNodeSpacing: 36,
|
|
231
|
+
edgeEdgeSpacing: 28,
|
|
232
|
+
edgeLabelSpacing: 16
|
|
233
|
+
},
|
|
234
|
+
wide: {
|
|
235
|
+
edgeNodeSpacing: 72,
|
|
236
|
+
edgeEdgeSpacing: 56,
|
|
237
|
+
edgeLabelSpacing: 28
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
function buildStudioRenderOptions(options, diagramTheme = options.theme) {
|
|
241
|
+
const layout = {};
|
|
242
|
+
if (options.spacingScale != null) layout.spacingScale = options.spacingScale;
|
|
243
|
+
if (options.density) layout.density = options.density;
|
|
244
|
+
if (options.direction) layout.direction = options.direction;
|
|
245
|
+
if (options.nodePlacement) layout.nodePlacement = options.nodePlacement;
|
|
246
|
+
if (options.groupLayout) layout.groupLayout = options.groupLayout;
|
|
247
|
+
if (options.modelOrder === "on") layout.considerModelOrder = true;
|
|
248
|
+
if (options.modelOrder === "off") layout.considerModelOrder = false;
|
|
249
|
+
if (options.groupGap) layout.groupGap = Number(options.groupGap);
|
|
250
|
+
if (options.edgeGaps) Object.assign(layout, EDGE_GAP_PRESETS[options.edgeGaps]);
|
|
251
|
+
const edges = {};
|
|
252
|
+
if (options.edgeStyle) edges.route = options.edgeStyle;
|
|
253
|
+
if (options.crossings) edges.crossings = options.crossings;
|
|
254
|
+
return {
|
|
255
|
+
theme: diagramTheme,
|
|
256
|
+
layout,
|
|
257
|
+
edges,
|
|
258
|
+
...options.roundedCorners ? { roundedCorners: true } : {},
|
|
259
|
+
debug: {
|
|
260
|
+
showPorts: options.showPorts,
|
|
261
|
+
showBounds: options.showBounds
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/source-settings.ts
|
|
267
|
+
const EMPTY_SETTINGS = {
|
|
268
|
+
theme: "dark",
|
|
269
|
+
direction: "",
|
|
270
|
+
density: "",
|
|
271
|
+
groupLayout: "",
|
|
272
|
+
edgeStyle: "",
|
|
273
|
+
crossings: ""
|
|
274
|
+
};
|
|
275
|
+
function readStudioSourceSettings(source) {
|
|
276
|
+
const diagram = firstDiagram(source);
|
|
277
|
+
if (!diagram) return { ...EMPTY_SETTINGS };
|
|
278
|
+
const result = { ...EMPTY_SETTINGS };
|
|
279
|
+
for (const statement of diagram.statements) if (statement.type === "Directive" && statement.name === "direction") result.direction = oneOf(String(statement.value ?? ""), [
|
|
280
|
+
"LR",
|
|
281
|
+
"RL",
|
|
282
|
+
"TD",
|
|
283
|
+
"BT"
|
|
284
|
+
]);
|
|
285
|
+
else if (statement.type === "Directive" && statement.name === "density") result.density = oneOf(String(statement.value ?? ""), [
|
|
286
|
+
"compact",
|
|
287
|
+
"normal",
|
|
288
|
+
"spacious"
|
|
289
|
+
]);
|
|
290
|
+
else if (statement.type === "LayoutBlock") {
|
|
291
|
+
const direction = stringProperty(statement.properties, "direction");
|
|
292
|
+
const density = stringProperty(statement.properties, "density");
|
|
293
|
+
if (direction) result.direction = oneOf(direction, [
|
|
294
|
+
"LR",
|
|
295
|
+
"RL",
|
|
296
|
+
"TD",
|
|
297
|
+
"BT"
|
|
298
|
+
]);
|
|
299
|
+
if (density) result.density = oneOf(density, [
|
|
300
|
+
"compact",
|
|
301
|
+
"normal",
|
|
302
|
+
"spacious"
|
|
303
|
+
]);
|
|
304
|
+
const groupLayout = stringProperty(statement.properties, "groupLayout");
|
|
305
|
+
if (groupLayout) result.groupLayout = oneOf(groupLayout, [
|
|
306
|
+
"auto",
|
|
307
|
+
"compound",
|
|
308
|
+
"flat",
|
|
309
|
+
"swimlane"
|
|
310
|
+
]);
|
|
311
|
+
} else if (statement.type === "EdgePolicyBlock") {
|
|
312
|
+
const edgeStyle = stringProperty(statement.properties, "route");
|
|
313
|
+
const crossings = stringProperty(statement.properties, "crossings");
|
|
314
|
+
if (edgeStyle) result.edgeStyle = oneOf(edgeStyle, [
|
|
315
|
+
"metro",
|
|
316
|
+
"rounded",
|
|
317
|
+
"orthogonal",
|
|
318
|
+
"straight",
|
|
319
|
+
"bezier"
|
|
320
|
+
]);
|
|
321
|
+
if (crossings) result.crossings = oneOf(crossings, [
|
|
322
|
+
"jumps",
|
|
323
|
+
"smart",
|
|
324
|
+
"gaps",
|
|
325
|
+
"none"
|
|
326
|
+
]);
|
|
327
|
+
} else if (statement.type === "RenderBlock") result.theme = oneOf(String(statement.properties.theme ?? "dark"), ["dark", "light"]) || "dark";
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
/** Apply one end-user diagram setting as a minimal source edit. Empty values remove the setting. */
|
|
331
|
+
function updateStudioSourceSetting(source, setting, value) {
|
|
332
|
+
const diagram = firstDiagram(source);
|
|
333
|
+
if (!diagram) return source;
|
|
334
|
+
if (setting === "direction" || setting === "density") {
|
|
335
|
+
const layout = diagram.statements.find((item) => item.type === "LayoutBlock");
|
|
336
|
+
if (value && layout?.properties[setting] != null) return updateBlock(source, diagram, "LayoutBlock", "layout", setting, value);
|
|
337
|
+
const withoutDirective = updateDirective(source, diagram, setting, value);
|
|
338
|
+
if (value) return withoutDirective;
|
|
339
|
+
const reparsed = firstDiagram(withoutDirective);
|
|
340
|
+
return reparsed ? updateBlock(withoutDirective, reparsed, "LayoutBlock", "layout", setting, "") : withoutDirective;
|
|
341
|
+
}
|
|
342
|
+
if (setting === "groupLayout") return updateBlock(source, diagram, "LayoutBlock", "layout", "groupLayout", value);
|
|
343
|
+
if (setting === "theme") return updateBlock(source, diagram, "RenderBlock", "render", "theme", value);
|
|
344
|
+
return updateBlock(source, diagram, "EdgePolicyBlock", "edges", setting === "edgeStyle" ? "route" : "crossings", value);
|
|
345
|
+
}
|
|
346
|
+
function resetStudioSourceSettings(source) {
|
|
347
|
+
return Object.keys(EMPTY_SETTINGS).reduce((current, setting) => updateStudioSourceSetting(current, setting, ""), source);
|
|
348
|
+
}
|
|
349
|
+
function firstDiagram(source) {
|
|
350
|
+
const first = parse(source).body[0];
|
|
351
|
+
return first?.type === "Diagram" ? first : null;
|
|
352
|
+
}
|
|
353
|
+
function updateDirective(source, diagram, name, value) {
|
|
354
|
+
const statement = diagram.statements.find((item) => item.type === "Directive" && item.name === name);
|
|
355
|
+
if (statement) {
|
|
356
|
+
if (!value) return removePolicyStatement(source, diagram, statement);
|
|
357
|
+
return replaceRange(source, statement.range.start.offset, statement.range.end.offset, `${name} ${value}`);
|
|
358
|
+
}
|
|
359
|
+
return value ? insertPolicyStatement(source, diagram, `${name} ${value}`, policyRank(name)) : source;
|
|
360
|
+
}
|
|
361
|
+
function updateBlock(source, diagram, type, keyword, property, value) {
|
|
362
|
+
const block = diagram.statements.find((item) => item.type === type);
|
|
363
|
+
if (!block) return value ? insertPolicyStatement(source, diagram, printBlock(keyword, { [property]: value }), policyRank(keyword)) : source;
|
|
364
|
+
const blockSource = source.slice(block.range.start.offset, block.range.end.offset);
|
|
365
|
+
const openingBrace = blockSource.indexOf("{");
|
|
366
|
+
const closingBrace = blockSource.lastIndexOf("}");
|
|
367
|
+
if (!blockSource.slice(openingBrace + 1, closingBrace).includes("\n")) {
|
|
368
|
+
const properties = { ...block.properties };
|
|
369
|
+
if (value) properties[property] = value;
|
|
370
|
+
else delete properties[property];
|
|
371
|
+
if (Object.keys(properties).length === 0) return removePolicyStatement(source, diagram, block);
|
|
372
|
+
return replaceRange(source, block.range.start.offset, block.range.end.offset, printBlock(keyword, properties));
|
|
373
|
+
}
|
|
374
|
+
const propertyLine = new RegExp(`^([\\t ]*${escapeRegex(property)}[\\t ]*:[\\t ]*)(.*?)([\\t ]*//.*)?$`, "m");
|
|
375
|
+
const match = propertyLine.exec(blockSource);
|
|
376
|
+
if (match) {
|
|
377
|
+
const updatedBlock = value ? blockSource.replace(propertyLine, `$1${value}$3`) : removeMatchedLine(blockSource, match.index, match[0].length);
|
|
378
|
+
if (!updatedBlock.slice(updatedBlock.indexOf("{") + 1, updatedBlock.lastIndexOf("}")).trim()) return removePolicyStatement(source, diagram, block);
|
|
379
|
+
return replaceRange(source, block.range.start.offset, block.range.end.offset, updatedBlock);
|
|
380
|
+
}
|
|
381
|
+
if (!value) return source;
|
|
382
|
+
const addition = `${indentationAt(source, block.range.start.offset)} ${property}: ${value}\n`;
|
|
383
|
+
const updatedBlock = `${blockSource.slice(0, closingBrace)}${addition}${blockSource.slice(closingBrace)}`;
|
|
384
|
+
return replaceRange(source, block.range.start.offset, block.range.end.offset, updatedBlock);
|
|
385
|
+
}
|
|
386
|
+
function printBlock(keyword, properties, indent = "") {
|
|
387
|
+
return `${keyword} {\n${Object.entries(properties).map(([key, value]) => `${indent} ${key}: ${printValue(value)}`).join("\n")}\n${indent}}`;
|
|
388
|
+
}
|
|
389
|
+
function insertPolicyStatement(source, diagram, statement, rank) {
|
|
390
|
+
const indent = `${indentationAt(source, diagram.range.start.offset)} `;
|
|
391
|
+
const rendered = statement.split("\n").map((line) => `${indent}${line}`).join("\n");
|
|
392
|
+
const target = diagram.statements.find((item) => policyRank(item) > rank);
|
|
393
|
+
if (target) {
|
|
394
|
+
const insertionOffset = source.lastIndexOf("\n", target.range.start.offset - 1) + 1;
|
|
395
|
+
const separatesContent = policyRank(target) === Number.POSITIVE_INFINITY;
|
|
396
|
+
return `${source.slice(0, insertionOffset)}${rendered}\n${separatesContent ? "\n" : ""}${source.slice(insertionOffset)}`;
|
|
397
|
+
}
|
|
398
|
+
const closingBrace = source.lastIndexOf("}", diagram.range.end.offset);
|
|
399
|
+
if (closingBrace < 0) return source;
|
|
400
|
+
return `${source.slice(0, closingBrace)}${rendered}\n${source.slice(closingBrace)}`;
|
|
401
|
+
}
|
|
402
|
+
function policyRank(value) {
|
|
403
|
+
const name = typeof value === "string" ? value : value.type === "Directive" ? value.name : value.type === "LayoutBlock" ? "layout" : value.type === "EdgePolicyBlock" ? "edges" : value.type === "RenderBlock" ? "render" : value.type === "PresentationBlock" ? "presentation" : "content";
|
|
404
|
+
const order = [
|
|
405
|
+
"direction",
|
|
406
|
+
"density",
|
|
407
|
+
"layout",
|
|
408
|
+
"edges",
|
|
409
|
+
"render",
|
|
410
|
+
"presentation"
|
|
411
|
+
];
|
|
412
|
+
return order.indexOf(name) === -1 ? Number.POSITIVE_INFINITY : order.indexOf(name);
|
|
413
|
+
}
|
|
414
|
+
function removeStatementLine(source, statement) {
|
|
415
|
+
const start = source.lastIndexOf("\n", statement.range.start.offset - 1) + 1;
|
|
416
|
+
const nextNewline = source.indexOf("\n", statement.range.end.offset);
|
|
417
|
+
return replaceRange(source, start, nextNewline < 0 ? statement.range.end.offset : nextNewline + 1, "");
|
|
418
|
+
}
|
|
419
|
+
function removePolicyStatement(source, diagram, statement) {
|
|
420
|
+
const updated = removeStatementLine(source, statement);
|
|
421
|
+
if (diagram.statements.some((item) => item !== statement && policyRank(item) < Number.POSITIVE_INFINITY)) return updated;
|
|
422
|
+
const firstStatement = firstDiagram(updated)?.statements[0];
|
|
423
|
+
if (!firstStatement) return updated;
|
|
424
|
+
const lineStart = updated.lastIndexOf("\n", firstStatement.range.start.offset - 1) + 1;
|
|
425
|
+
const preceding = updated.slice(0, lineStart);
|
|
426
|
+
return preceding.endsWith("\n\n") ? `${preceding.slice(0, -1)}${updated.slice(lineStart)}` : updated;
|
|
427
|
+
}
|
|
428
|
+
function indentationAt(source, offset) {
|
|
429
|
+
const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
|
|
430
|
+
return /^\s*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
|
|
431
|
+
}
|
|
432
|
+
function replaceRange(source, start, end, replacement) {
|
|
433
|
+
return `${source.slice(0, start)}${replacement}${source.slice(end)}`;
|
|
434
|
+
}
|
|
435
|
+
function stringProperty(properties, name) {
|
|
436
|
+
const value = properties[name];
|
|
437
|
+
return typeof value === "string" ? value : "";
|
|
438
|
+
}
|
|
439
|
+
function printValue(value) {
|
|
440
|
+
return Array.isArray(value) ? `[${value.join(", ")}]` : String(value);
|
|
441
|
+
}
|
|
442
|
+
function removeMatchedLine(source, start, length) {
|
|
443
|
+
const lineStart = source.lastIndexOf("\n", start - 1) + 1;
|
|
444
|
+
const nextNewline = source.indexOf("\n", start + length);
|
|
445
|
+
const lineEnd = nextNewline < 0 ? start + length : nextNewline + 1;
|
|
446
|
+
return `${source.slice(0, lineStart)}${source.slice(lineEnd)}`;
|
|
447
|
+
}
|
|
448
|
+
function escapeRegex(value) {
|
|
449
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
450
|
+
}
|
|
451
|
+
function oneOf(value, allowed) {
|
|
452
|
+
return allowed.includes(value) ? value : "";
|
|
453
|
+
}
|
|
454
|
+
//#endregion
|
|
455
|
+
export { DEFAULT_STUDIO_PRESENTATION, INITIAL_STUDIO_STATE, STUDIO_PROTOCOL_VERSION, buildStudioRenderOptions, createStudioPreviewCoordinator, parseStudioClientMessage, readStudioSourceSettings, reduceStudioMessage, resetStudioSourceSettings, studioMessageJson, updateStudioSourceSetting };
|
|
456
|
+
|
|
457
|
+
//# sourceMappingURL=index.mjs.map
|