@civilization/dsh-drawio 0.2.1 → 0.4.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/README.md +20 -6
- package/lib/client.js +361 -41
- package/package.json +2 -1
- package/skills/drawio-operation/SKILL.md +80 -0
- package/src/client/index.tsx +381 -46
- package/src/index.js +64 -13
- package/src/xml.js +119 -14
package/README.md
CHANGED
|
@@ -10,15 +10,29 @@
|
|
|
10
10
|
- 编辑器资源由 DSH Host 从本机提供,不连接 diagrams.net。
|
|
11
11
|
- 用户保存和 Agent 工具修改的是同一份 XML 文件。
|
|
12
12
|
- 文件读取、写入、路径限制和审批继续经过 DSH 服务。
|
|
13
|
+
- 提供 `drawio-operation` Skill 和 3 个模型专属工具。
|
|
13
14
|
|
|
14
|
-
## Agent
|
|
15
|
+
## Agent 工具与 Skill
|
|
15
16
|
|
|
16
|
-
- `
|
|
17
|
-
- `
|
|
17
|
+
- `drawio-operation` Skill:向模型注入 Draw.io 画板整体架构、工业级排版规范、现代配色方案、离线导出机制与最佳绘图工作流。
|
|
18
|
+
- `drawio_inspect`:把画板读取为页面、节点、连线和几何信息,自动提取去除 HTML 的纯文本标题与容器子节点列表。
|
|
19
|
+
- `drawio_edit`:批量新增、更新、删除节点与连线,支持 `add_page`、`rename_page`、`delete_page` 多页面管理,以及 `stylePatch` 局部增量更新样式。
|
|
18
20
|
- `drawio_write`:校验并写入完整的 Draw.io XML。
|
|
19
21
|
|
|
20
|
-
工具统一保存为未压缩 XML,便于 Agent 继续修改,也便于 Git
|
|
21
|
-
|
|
22
|
+
工具统一保存为未压缩 XML,便于 Agent 继续修改,也便于 Git 查看差异。画板具备真乐观并发控制(防静默覆盖冲突),并采用轻量版本感知机制自动同步 Agent 对同一文件的修改。
|
|
23
|
+
|
|
24
|
+
## 本地离线图片导出 (Export)
|
|
25
|
+
|
|
26
|
+
由于 Draw.io 原版导出功能依赖云端渲染服务(`export.diagrams.net`),在离线及局域网环境下无法正常工作。本插件基于 Draw.io 离线内核的 Embed 消息通道与 Canvas / SVG 渲染协议,实现了**100% 纯本地离线图片导出引擎**:
|
|
27
|
+
|
|
28
|
+
- **支持格式**:
|
|
29
|
+
- **PNG (超清 2x)**:采用高倍率抗锯齿光栅化渲染,支持透明背景切换,适合插入文档和交流汇报;
|
|
30
|
+
- **SVG (矢量图)**:提取无损矢量图形,任意放大不失真;
|
|
31
|
+
- **XML-PNG (可编辑图)**:在 PNG 图像中内嵌图表 XML 原数据,既能作为常规图片展示,又可随时拖回 Draw.io 继续二次编辑。
|
|
32
|
+
- **三大输出方式**:
|
|
33
|
+
- **保存到工作区**:在当前画板文件同级目录下自动生成同名图片文件(如 `arch.drawio` -> `arch.png`),自动触发 DSH 文件系统感知;
|
|
34
|
+
- **复制到剪贴板**:一键将 PNG 图片写入操作系统剪贴板(`navigator.clipboard`),方便在 Markdown、PRD 或聊天工具中直接 `Ctrl + V` 粘贴;
|
|
35
|
+
- **下载图片文件**:触发浏览器原生下载弹窗保存到本地磁盘。
|
|
22
36
|
|
|
23
37
|
## 开发验证
|
|
24
38
|
|
|
@@ -38,4 +52,4 @@ dsh web --port 3082 --no-open
|
|
|
38
52
|
已在 DSH 0.1.5-rc.2 上验证构建、XML 操作、本地静态资源加载和官方右侧栏注册。
|
|
39
53
|
|
|
40
54
|
当前内置的精简 Draw.io runtime 固定为 31.4.5。包含完整满血版内置图库(通用、流程图、UML、ER、BPMN、网络、Kubernetes、AWS、GCP、Cisco、电子、平面图等全部分类均可自由开启)以及 PlantUML 离线渲染模块;
|
|
41
|
-
云盘、在线图库、模板库、VSDX/Gliffy
|
|
55
|
+
云盘、在线图库、模板库、VSDX/Gliffy 导入未包含;已全面支持纯本地离线 Canvas + SVG 导出引擎。
|
package/lib/client.js
CHANGED
|
@@ -23,7 +23,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
23
23
|
var index_exports = {};
|
|
24
24
|
__export(index_exports, {
|
|
25
25
|
apply: () => apply,
|
|
26
|
-
inject: () => inject
|
|
26
|
+
inject: () => inject,
|
|
27
|
+
parseDrawioAddress: () => parseDrawioAddress
|
|
27
28
|
});
|
|
28
29
|
module.exports = __toCommonJS(index_exports);
|
|
29
30
|
var import_react = require("react");
|
|
@@ -51,39 +52,61 @@ function apply(ctx) {
|
|
|
51
52
|
}
|
|
52
53
|
function OfficialDrawioTabTitle({ useTabInfo }) {
|
|
53
54
|
const { tab } = useTabInfo();
|
|
55
|
+
const title = tab?.title || (tab?.navigation?.address ? fileName(parseDrawioAddress(tab.navigation.address)?.path ?? "Draw.io") : "Draw.io");
|
|
54
56
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
55
57
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CanvasIcon, { size: 15 }),
|
|
56
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children:
|
|
58
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: title })
|
|
57
59
|
] });
|
|
58
60
|
}
|
|
59
61
|
function OfficialDrawioTabBody({ sessionId, useSessions, useTabInfo }) {
|
|
60
62
|
const { tab } = useTabInfo();
|
|
61
|
-
const
|
|
62
|
-
const resource = (0, import_react.useMemo)(() => parseDrawioAddress(
|
|
63
|
+
const rawAddress = tab?.navigation?.address;
|
|
64
|
+
const resource = (0, import_react.useMemo)(() => parseDrawioAddress(rawAddress, sessionId), [rawAddress, sessionId]);
|
|
65
|
+
const activeCwd = useSessions((sessions) => sessions?.byId?.[sessionId]?.cwd);
|
|
66
|
+
const resourceCwd = useSessions((sessions) => resource?.sessionId ? sessions?.byId?.[resource.sessionId]?.cwd : void 0);
|
|
67
|
+
const cwd = activeCwd || resourceCwd;
|
|
68
|
+
const effectiveSessionId = sessionId || resource?.sessionId || "";
|
|
63
69
|
const [file, setFile] = (0, import_react.useState)(null);
|
|
64
70
|
const [error, setError] = (0, import_react.useState)(null);
|
|
65
|
-
const key = `${
|
|
71
|
+
const key = `${effectiveSessionId}\0${cwd ?? ""}\0${resource?.path ?? ""}`;
|
|
66
72
|
(0, import_react.useEffect)(() => {
|
|
67
|
-
if (!resource ||
|
|
73
|
+
if (!resource?.path || !effectiveSessionId || !cwd) return;
|
|
68
74
|
const controller = new AbortController();
|
|
69
75
|
setError(null);
|
|
70
|
-
void fsRead({ sessionId, cwd }, resource.path, controller.signal).then((
|
|
71
|
-
if (!controller.signal.aborted) setFile({ key, content });
|
|
76
|
+
void fsRead({ sessionId: effectiveSessionId, cwd }, resource.path, controller.signal).then((res) => {
|
|
77
|
+
if (!controller.signal.aborted) setFile({ key, content: res.content, version: res.version });
|
|
72
78
|
}).catch((reason) => {
|
|
73
79
|
if (!controller.signal.aborted) setError(messageOf(reason));
|
|
74
80
|
});
|
|
75
81
|
return () => controller.abort();
|
|
76
|
-
}, [key, resource?.
|
|
77
|
-
if (!resource || resource.
|
|
78
|
-
|
|
82
|
+
}, [key, resource?.path, effectiveSessionId, cwd]);
|
|
83
|
+
if (!resource || !resource.path) {
|
|
84
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
85
|
+
CenteredMessage,
|
|
86
|
+
{
|
|
87
|
+
title: "\u65E0\u6CD5\u6253\u5F00\u753B\u677F",
|
|
88
|
+
detail: rawAddress ? `\u65E0\u6CD5\u89E3\u6790 Draw.io \u6587\u4EF6\u5730\u5740\uFF1A${rawAddress}` : "\u65E0\u6CD5\u83B7\u53D6\u6587\u4EF6\u5BFC\u822A\u5730\u5740\u3002"
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (!cwd) {
|
|
93
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
94
|
+
CenteredMessage,
|
|
95
|
+
{
|
|
96
|
+
title: "\u65E0\u6CD5\u6253\u5F00\u753B\u677F",
|
|
97
|
+
detail: "\u5F53\u524D\u4F1A\u8BDD\u6CA1\u6709\u5173\u8054\u53EF\u7528\u7684\u5DE5\u4F5C\u533A\uFF0C\u8BF7\u5148\u5728 DSH \u4E2D\u6253\u5F00\u6216\u9009\u62E9\u4E00\u4E2A\u5DE5\u4F5C\u533A\u6587\u4EF6\u5939\u3002"
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
79
101
|
if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CenteredMessage, { title: "\u753B\u677F\u8F7D\u5165\u5931\u8D25", detail: error });
|
|
80
102
|
if (file?.key !== key) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CenteredMessage, { title: "\u6B63\u5728\u8F7D\u5165\u753B\u677F", detail: fileName(resource.path) });
|
|
81
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawioCanvas, { content: file.content, path: resource.path, scope: { sessionId, cwd } });
|
|
103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawioCanvas, { content: file.content, version: file.version, path: resource.path, scope: { sessionId: effectiveSessionId, cwd } });
|
|
82
104
|
}
|
|
83
|
-
function DrawioCanvas({ content, truncated, path, scope }) {
|
|
105
|
+
function DrawioCanvas({ content, version, truncated, path, scope }) {
|
|
84
106
|
const frame = (0, import_react.useRef)(null);
|
|
85
107
|
const latestFile = (0, import_react.useRef)(content ?? "");
|
|
86
108
|
const latestEditor = (0, import_react.useRef)(content ?? "");
|
|
109
|
+
const latestVersion = (0, import_react.useRef)(version);
|
|
87
110
|
const saveTimer = (0, import_react.useRef)(void 0);
|
|
88
111
|
const saveGeneration = (0, import_react.useRef)(0);
|
|
89
112
|
const [ready, setReady] = (0, import_react.useState)(false);
|
|
@@ -92,24 +115,46 @@ function DrawioCanvas({ content, truncated, path, scope }) {
|
|
|
92
115
|
const lastUserActivity = (0, import_react.useRef)(0);
|
|
93
116
|
const isInteracting = (0, import_react.useRef)(false);
|
|
94
117
|
const { dark, accent: accentColor } = useDshTheme();
|
|
95
|
-
const
|
|
118
|
+
const initialDark = (0, import_react.useRef)(dark);
|
|
119
|
+
const src = (0, import_react.useMemo)(() => `/dsh-drawio/runtime/index.html?embed=1&proto=json&spin=1&offline=1&local=1&ui=min&libraries=1&configure=1&noExitBtn=1&saveAndExit=0&dark=${initialDark.current ? "1" : "0"}`, []);
|
|
96
120
|
const post = (0, import_react.useCallback)((message) => {
|
|
97
121
|
frame.current?.contentWindow?.postMessage(JSON.stringify(message), window.location.origin);
|
|
98
122
|
}, []);
|
|
123
|
+
const reloadFromDisk = (0, import_react.useCallback)(async () => {
|
|
124
|
+
try {
|
|
125
|
+
setSaveState("loading");
|
|
126
|
+
const result = await fsRead(scope, path);
|
|
127
|
+
latestFile.current = result.content;
|
|
128
|
+
latestEditor.current = result.content;
|
|
129
|
+
latestVersion.current = result.version;
|
|
130
|
+
post({ action: "load", xml: result.content, autosave: 1, title: fileName(path), dark, noExitBtn: 1, saveAndExit: 0 });
|
|
131
|
+
setSaveState("saved");
|
|
132
|
+
setError(null);
|
|
133
|
+
} catch (reason) {
|
|
134
|
+
setError(messageOf(reason));
|
|
135
|
+
}
|
|
136
|
+
}, [scope, path, dark, post]);
|
|
99
137
|
const persist = (0, import_react.useCallback)(async (xml) => {
|
|
100
138
|
const generation = ++saveGeneration.current;
|
|
101
139
|
setSaveState("saving");
|
|
102
140
|
try {
|
|
103
|
-
await fsWrite(scope, path, xml);
|
|
141
|
+
const outcome = await fsWrite(scope, path, xml, latestVersion.current);
|
|
104
142
|
if (generation !== saveGeneration.current) return;
|
|
105
143
|
latestFile.current = xml;
|
|
106
144
|
latestEditor.current = xml;
|
|
145
|
+
if (outcome?.version) latestVersion.current = outcome.version;
|
|
107
146
|
setSaveState("saved");
|
|
108
147
|
setError(null);
|
|
109
148
|
} catch (reason) {
|
|
110
149
|
if (generation !== saveGeneration.current) return;
|
|
111
|
-
|
|
112
|
-
|
|
150
|
+
const msg = messageOf(reason);
|
|
151
|
+
if (msg.includes("modified by an external process") || msg.includes("409")) {
|
|
152
|
+
setSaveState("conflict");
|
|
153
|
+
setError("\u6587\u4EF6\u5DF2\u88AB\u5916\u90E8\u6216 AI \u4FEE\u6539\uFF0C\u5F53\u524D\u5B58\u5728\u5E76\u53D1\u51B2\u7A81\uFF0C\u70B9\u51FB\u53EF\u91CD\u65B0\u8F7D\u5165\u6700\u65B0\u5185\u5BB9");
|
|
154
|
+
} else {
|
|
155
|
+
setSaveState("error");
|
|
156
|
+
setError(msg);
|
|
157
|
+
}
|
|
113
158
|
}
|
|
114
159
|
}, [scope.sessionId, scope.cwd, path]);
|
|
115
160
|
const scheduleSave = (0, import_react.useCallback)((xml, immediate = false) => {
|
|
@@ -131,7 +176,8 @@ function DrawioCanvas({ content, truncated, path, scope }) {
|
|
|
131
176
|
(0, import_react.useEffect)(() => {
|
|
132
177
|
latestFile.current = content ?? "";
|
|
133
178
|
latestEditor.current = content ?? "";
|
|
134
|
-
|
|
179
|
+
latestVersion.current = version;
|
|
180
|
+
}, [content, version, path]);
|
|
135
181
|
const markActivity = (0, import_react.useCallback)(() => {
|
|
136
182
|
lastUserActivity.current = Date.now();
|
|
137
183
|
}, []);
|
|
@@ -162,6 +208,86 @@ function DrawioCanvas({ content, truncated, path, scope }) {
|
|
|
162
208
|
const timer = window.setInterval(handleFrameAttach, 800);
|
|
163
209
|
return () => window.clearInterval(timer);
|
|
164
210
|
}, []);
|
|
211
|
+
const [menuOpen, setMenuOpen] = (0, import_react.useState)(false);
|
|
212
|
+
const [exportFormat, setExportFormat] = (0, import_react.useState)("png");
|
|
213
|
+
const [isTransparent, setIsTransparent] = (0, import_react.useState)(false);
|
|
214
|
+
const [isExporting, setIsExporting] = (0, import_react.useState)(false);
|
|
215
|
+
const [toast, setToast] = (0, import_react.useState)(null);
|
|
216
|
+
const pendingExport = (0, import_react.useRef)(null);
|
|
217
|
+
const menuRef = (0, import_react.useRef)(null);
|
|
218
|
+
const showToast = (0, import_react.useCallback)((msg) => {
|
|
219
|
+
setToast(msg);
|
|
220
|
+
window.setTimeout(() => setToast((current) => current === msg ? null : current), 3200);
|
|
221
|
+
}, []);
|
|
222
|
+
const startExport = (0, import_react.useCallback)((target) => {
|
|
223
|
+
if (!ready || isExporting) return;
|
|
224
|
+
const ext = exportFormat === "svg" ? ".svg" : exportFormat === "xmlpng" ? ".drawio.png" : ".png";
|
|
225
|
+
const base = path.replace(/\.drawio$/i, "");
|
|
226
|
+
const targetPath = `${base}${ext}`;
|
|
227
|
+
pendingExport.current = {
|
|
228
|
+
format: exportFormat,
|
|
229
|
+
target,
|
|
230
|
+
targetPath
|
|
231
|
+
};
|
|
232
|
+
setIsExporting(true);
|
|
233
|
+
setMenuOpen(false);
|
|
234
|
+
post({
|
|
235
|
+
action: "export",
|
|
236
|
+
format: exportFormat,
|
|
237
|
+
scale: exportFormat === "svg" ? 1 : 2,
|
|
238
|
+
transparent: isTransparent,
|
|
239
|
+
border: 10,
|
|
240
|
+
spin: "\u6B63\u5728\u6E32\u67D3\u5BFC\u51FA\u56FE\u7247..."
|
|
241
|
+
});
|
|
242
|
+
}, [ready, isExporting, exportFormat, isTransparent, path, post]);
|
|
243
|
+
const handleExportResult = (0, import_react.useCallback)(async (pending, data) => {
|
|
244
|
+
try {
|
|
245
|
+
if (pending.target === "download") {
|
|
246
|
+
const link = document.createElement("a");
|
|
247
|
+
link.download = fileName(pending.targetPath);
|
|
248
|
+
link.href = data;
|
|
249
|
+
document.body.appendChild(link);
|
|
250
|
+
link.click();
|
|
251
|
+
document.body.removeChild(link);
|
|
252
|
+
showToast(`\u5DF2\u5F00\u59CB\u4E0B\u8F7D\uFF1A${fileName(pending.targetPath)}`);
|
|
253
|
+
} else if (pending.target === "clipboard") {
|
|
254
|
+
try {
|
|
255
|
+
if (pending.format === "svg") {
|
|
256
|
+
const svgText = data.startsWith("data:image/svg+xml") ? decodeURIComponent(data.split(",")[1] || "") : data;
|
|
257
|
+
await navigator.clipboard.writeText(svgText);
|
|
258
|
+
showToast("\u5DF2\u590D\u5236 SVG \u4EE3\u7801\u5230\u526A\u8D34\u677F");
|
|
259
|
+
} else {
|
|
260
|
+
const res = await fetch(data);
|
|
261
|
+
const blob = await res.blob();
|
|
262
|
+
await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
|
|
263
|
+
showToast("\u5DF2\u590D\u5236\u56FE\u7247\u5230\u526A\u8D34\u677F");
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
const link = document.createElement("a");
|
|
267
|
+
link.download = fileName(pending.targetPath);
|
|
268
|
+
link.href = data;
|
|
269
|
+
document.body.appendChild(link);
|
|
270
|
+
link.click();
|
|
271
|
+
document.body.removeChild(link);
|
|
272
|
+
showToast(`\u526A\u8D34\u677F\u65E0\u6743\u9650\uFF0C\u5DF2\u81EA\u52A8\u4E0B\u8F7D\uFF1A${fileName(pending.targetPath)}`);
|
|
273
|
+
}
|
|
274
|
+
} else if (pending.target === "workspace") {
|
|
275
|
+
const result = await fsSaveImage(scope, pending.targetPath, data);
|
|
276
|
+
showToast(`\u5DF2\u4FDD\u5B58\u5230\u5DE5\u4F5C\u533A\uFF1A${fileName(result.path)} (${Math.round(result.bytes / 1024)} KB)`);
|
|
277
|
+
}
|
|
278
|
+
} catch (reason) {
|
|
279
|
+
setError(`\u5BFC\u51FA\u5931\u8D25\uFF1A${messageOf(reason)}`);
|
|
280
|
+
}
|
|
281
|
+
}, [scope, showToast]);
|
|
282
|
+
(0, import_react.useEffect)(() => {
|
|
283
|
+
const handleOutsideClick = (e) => {
|
|
284
|
+
if (menuOpen && menuRef.current && !menuRef.current.contains(e.target)) {
|
|
285
|
+
setMenuOpen(false);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
window.addEventListener("pointerdown", handleOutsideClick);
|
|
289
|
+
return () => window.removeEventListener("pointerdown", handleOutsideClick);
|
|
290
|
+
}, [menuOpen]);
|
|
165
291
|
(0, import_react.useEffect)(() => {
|
|
166
292
|
setReady(false);
|
|
167
293
|
setSaveState("loading");
|
|
@@ -181,11 +307,18 @@ function DrawioCanvas({ content, truncated, path, scope }) {
|
|
|
181
307
|
setSaveState("saved");
|
|
182
308
|
} else if ((message.event === "autosave" || message.event === "save") && typeof message.xml === "string") {
|
|
183
309
|
scheduleSave(message.xml, message.event === "save");
|
|
310
|
+
} else if (message.event === "export" && typeof message.data === "string") {
|
|
311
|
+
const pending = pendingExport.current;
|
|
312
|
+
pendingExport.current = null;
|
|
313
|
+
setIsExporting(false);
|
|
314
|
+
if (pending) {
|
|
315
|
+
void handleExportResult(pending, message.data);
|
|
316
|
+
}
|
|
184
317
|
}
|
|
185
318
|
};
|
|
186
319
|
window.addEventListener("message", receive);
|
|
187
320
|
return () => window.removeEventListener("message", receive);
|
|
188
|
-
}, [src, dark, path, post, scheduleSave]);
|
|
321
|
+
}, [src, dark, path, post, scheduleSave, handleExportResult]);
|
|
189
322
|
(0, import_react.useEffect)(() => {
|
|
190
323
|
if (!ready || truncated) return;
|
|
191
324
|
let cancelled = false;
|
|
@@ -195,12 +328,19 @@ function DrawioCanvas({ content, truncated, path, scope }) {
|
|
|
195
328
|
if (saveTimer.current !== void 0) return;
|
|
196
329
|
if (typeof document !== "undefined" && document.hidden) return;
|
|
197
330
|
try {
|
|
198
|
-
const
|
|
331
|
+
const stat = await fsStat(scope, path);
|
|
332
|
+
if (cancelled || isInteracting.current || Date.now() - lastUserActivity.current < 1200) return;
|
|
333
|
+
if (stat.version && latestVersion.current && stat.version === latestVersion.current) return;
|
|
334
|
+
const res = await fsRead(scope, path);
|
|
199
335
|
if (cancelled || isInteracting.current || Date.now() - lastUserActivity.current < 1200) return;
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
336
|
+
if (res.content === latestFile.current || res.content === latestEditor.current) {
|
|
337
|
+
latestVersion.current = res.version;
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
latestFile.current = res.content;
|
|
341
|
+
latestEditor.current = res.content;
|
|
342
|
+
latestVersion.current = res.version;
|
|
343
|
+
post({ action: "load", xml: res.content, autosave: 1, title: fileName(path), dark, noExitBtn: 1, saveAndExit: 0 });
|
|
204
344
|
setSaveState("external");
|
|
205
345
|
window.setTimeout(() => {
|
|
206
346
|
if (!cancelled) setSaveState("saved");
|
|
@@ -219,16 +359,124 @@ function DrawioCanvas({ content, truncated, path, scope }) {
|
|
|
219
359
|
}, [ready, truncated, scope.sessionId, scope.cwd, path, dark, post]);
|
|
220
360
|
(0, import_react.useEffect)(() => () => {
|
|
221
361
|
if (saveTimer.current !== void 0) window.clearTimeout(saveTimer.current);
|
|
222
|
-
if (latestEditor.current !== latestFile.current) void fsWrite(scope, path, latestEditor.current, true);
|
|
362
|
+
if (latestEditor.current !== latestFile.current) void fsWrite(scope, path, latestEditor.current, latestVersion.current, true);
|
|
223
363
|
}, [scope.sessionId, scope.cwd, path]);
|
|
224
364
|
if (truncated) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CenteredMessage, { title: "\u65E0\u6CD5\u6253\u5F00\u753B\u677F", detail: "\u6587\u4EF6\u5185\u5BB9\u5DF2\u88AB\u622A\u65AD\uFF0C\u7EE7\u7EED\u7F16\u8F91\u53EF\u80FD\u635F\u574F\u539F\u6587\u4EF6\u3002" });
|
|
365
|
+
const targetExt = exportFormat === "svg" ? ".svg" : exportFormat === "xmlpng" ? ".drawio.png" : ".png";
|
|
366
|
+
const previewName = `${fileName(path).replace(/\.drawio$/i, "")}${targetExt}`;
|
|
225
367
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.root, children: [
|
|
226
368
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("iframe", { ref: frame, title: `${fileName(path)} Draw.io \u753B\u677F`, src, style: styles.frame }),
|
|
227
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
228
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
369
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: menuRef, style: styles.toolbar, children: [
|
|
370
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
371
|
+
"button",
|
|
372
|
+
{
|
|
373
|
+
type: "button",
|
|
374
|
+
onClick: () => setMenuOpen((open) => !open),
|
|
375
|
+
disabled: !ready || isExporting,
|
|
376
|
+
style: { ...styles.exportTrigger, ...isExporting ? styles.triggerActive : {} },
|
|
377
|
+
title: "\u5BFC\u51FA\u5F53\u524D\u753B\u677F\u4E3A\u56FE\u7247",
|
|
378
|
+
children: [
|
|
379
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CameraIcon, { size: 14 }),
|
|
380
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: isExporting ? "\u6B63\u5728\u5BFC\u51FA..." : "\u5BFC\u51FA\u56FE\u7247" }),
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.arrowIcon, children: "\u25BE" })
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
),
|
|
385
|
+
menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.exportMenu, children: [
|
|
386
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.menuHeader, children: [
|
|
387
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.menuTitle, children: "\u5BFC\u51FA\u753B\u677F\u56FE\u7247" }),
|
|
388
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.menuSub, children: previewName })
|
|
389
|
+
] }),
|
|
390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.formatRow, children: ["png", "svg", "xmlpng"].map((fmt) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
391
|
+
"button",
|
|
392
|
+
{
|
|
393
|
+
type: "button",
|
|
394
|
+
onClick: () => setExportFormat(fmt),
|
|
395
|
+
style: {
|
|
396
|
+
...styles.formatBtn,
|
|
397
|
+
...exportFormat === fmt ? { ...styles.formatBtnActive, borderColor: accentColor } : {}
|
|
398
|
+
},
|
|
399
|
+
children: fmt === "png" ? "PNG (\u8D85\u6E05)" : fmt === "svg" ? "SVG (\u77E2\u91CF)" : "XML-PNG"
|
|
400
|
+
},
|
|
401
|
+
fmt
|
|
402
|
+
)) }),
|
|
403
|
+
exportFormat !== "svg" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: styles.checkRow, children: [
|
|
404
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
405
|
+
"input",
|
|
406
|
+
{
|
|
407
|
+
type: "checkbox",
|
|
408
|
+
checked: isTransparent,
|
|
409
|
+
onChange: (e) => setIsTransparent(e.target.checked),
|
|
410
|
+
style: styles.checkbox
|
|
411
|
+
}
|
|
412
|
+
),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u900F\u660E\u80CC\u666F" })
|
|
414
|
+
] }) : null,
|
|
415
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actionCol, children: [
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
417
|
+
"button",
|
|
418
|
+
{
|
|
419
|
+
type: "button",
|
|
420
|
+
onClick: () => startExport("workspace"),
|
|
421
|
+
style: { ...styles.actionBtn, ...styles.actionBtnPrimary, background: accentColor },
|
|
422
|
+
children: [
|
|
423
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SaveIcon, { size: 13 }),
|
|
424
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
425
|
+
"\u4FDD\u5B58\u5230\u5DE5\u4F5C\u533A (",
|
|
426
|
+
targetExt,
|
|
427
|
+
")"
|
|
428
|
+
] })
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
),
|
|
432
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
433
|
+
"button",
|
|
434
|
+
{
|
|
435
|
+
type: "button",
|
|
436
|
+
onClick: () => startExport("clipboard"),
|
|
437
|
+
style: styles.actionBtn,
|
|
438
|
+
children: [
|
|
439
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyIcon, { size: 13 }),
|
|
440
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u590D\u5236\u5230\u526A\u8D34\u677F" })
|
|
441
|
+
]
|
|
442
|
+
}
|
|
443
|
+
),
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
445
|
+
"button",
|
|
446
|
+
{
|
|
447
|
+
type: "button",
|
|
448
|
+
onClick: () => startExport("download"),
|
|
449
|
+
style: styles.actionBtn,
|
|
450
|
+
children: [
|
|
451
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DownloadIcon, { size: 13 }),
|
|
452
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u4E0B\u8F7D\u56FE\u7247\u6587\u4EF6" })
|
|
453
|
+
]
|
|
454
|
+
}
|
|
455
|
+
)
|
|
456
|
+
] })
|
|
457
|
+
] }) : null
|
|
458
|
+
] }),
|
|
459
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.toast, children: [
|
|
460
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckIcon, { size: 14 }),
|
|
461
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: toast })
|
|
462
|
+
] }) : null,
|
|
463
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "status", "aria-live": "polite", style: { ...styles.status, ...saveState === "error" || saveState === "conflict" ? styles.statusError : {} }, children: [
|
|
464
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...styles.dot, background: saveState === "conflict" ? "var(--dsw-alias-warning, #e6a23c)" : accentColor } }),
|
|
229
465
|
statusText(saveState)
|
|
230
466
|
] }),
|
|
231
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
467
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
468
|
+
"button",
|
|
469
|
+
{
|
|
470
|
+
type: "button",
|
|
471
|
+
title: error,
|
|
472
|
+
onClick: () => {
|
|
473
|
+
if (saveState === "conflict") void reloadFromDisk();
|
|
474
|
+
setError(null);
|
|
475
|
+
},
|
|
476
|
+
style: styles.error,
|
|
477
|
+
children: saveState === "conflict" ? "\u26A0\uFE0F \u68C0\u6D4B\u5230\u5916\u90E8\u4FEE\u6539 \xB7 \u70B9\u51FB\u91CD\u65B0\u8F7D\u5165" : "\u4FDD\u5B58\u5931\u8D25 \xB7 \u70B9\u51FB\u5173\u95ED"
|
|
478
|
+
}
|
|
479
|
+
) : null
|
|
232
480
|
] });
|
|
233
481
|
}
|
|
234
482
|
function useDshTheme() {
|
|
@@ -269,12 +517,17 @@ function detectDark() {
|
|
|
269
517
|
if (numbers?.length === 3) return numbers[0] * 0.299 + numbers[1] * 0.587 + numbers[2] * 0.114 < 128;
|
|
270
518
|
return document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
271
519
|
}
|
|
520
|
+
async function fsStat(scope, path, signal) {
|
|
521
|
+
return call("stat", scope, { path }, false, signal);
|
|
522
|
+
}
|
|
272
523
|
async function fsRead(scope, path, signal) {
|
|
273
|
-
|
|
274
|
-
return value.content;
|
|
524
|
+
return call("read", scope, { path }, false, signal);
|
|
275
525
|
}
|
|
276
|
-
async function fsWrite(scope, path, content, keepalive = false) {
|
|
277
|
-
|
|
526
|
+
async function fsWrite(scope, path, content, version, keepalive = false) {
|
|
527
|
+
return call("write", scope, { path, content, ...version ? { version } : {} }, keepalive);
|
|
528
|
+
}
|
|
529
|
+
async function fsSaveImage(scope, path, data) {
|
|
530
|
+
return call("save-image", scope, { path, data });
|
|
278
531
|
}
|
|
279
532
|
async function call(method, scope, extra, keepalive = false, signal) {
|
|
280
533
|
const response = await fetch(`/dsh-drawio/api/${method}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ sessionId: scope.sessionId, ...scope.cwd ? { cwd: scope.cwd } : {}, ...extra }), keepalive, signal });
|
|
@@ -290,6 +543,34 @@ function CanvasIcon({ size = 16, className }) {
|
|
|
290
543
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8.4 7.8l3.2 4.4M8.2 6.2h3.6M6.2 8.2v3.6", stroke: "currentColor", strokeLinecap: "round" })
|
|
291
544
|
] });
|
|
292
545
|
}
|
|
546
|
+
function CameraIcon({ size = 16, className }) {
|
|
547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className, "aria-hidden": true, viewBox: "0 0 20 20", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6.5A1.5 1.5 0 0 1 5.5 5h1.8a1.5 1.5 0 0 0 1.2-.6l.5-.7A1.5 1.5 0 0 1 10.2 3h1.6a1.5 1.5 0 0 1 1.2.7l.5.7a1.5 1.5 0 0 0 1.2.6h1.8A1.5 1.5 0 0 1 18 6.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 2 15.5v-9z" }),
|
|
549
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10", cy: "11", r: "3.5" })
|
|
550
|
+
] });
|
|
551
|
+
}
|
|
552
|
+
function SaveIcon({ size = 16, className }) {
|
|
553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className, "aria-hidden": true, viewBox: "0 0 20 20", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
554
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 17H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h9.5l3.5 3.5V16a1 1 0 0 1-1 1z" }),
|
|
555
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 17v-6H7v6" }),
|
|
556
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 3v4h6" })
|
|
557
|
+
] });
|
|
558
|
+
}
|
|
559
|
+
function CopyIcon({ size = 16, className }) {
|
|
560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className, "aria-hidden": true, viewBox: "0 0 20 20", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
561
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "7", y: "7", width: "10", height: "10", rx: "1.5" }),
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V4" })
|
|
563
|
+
] });
|
|
564
|
+
}
|
|
565
|
+
function DownloadIcon({ size = 16, className }) {
|
|
566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className, "aria-hidden": true, viewBox: "0 0 20 20", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 3v10m0 0l-3.5-3.5M10 13l3.5-3.5" }),
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 14v2a1.5 1.5 0 0 0 1.5 1.5h11a1.5 1.5 0 0 0 1.5-1.5v-2" })
|
|
569
|
+
] });
|
|
570
|
+
}
|
|
571
|
+
function CheckIcon({ size = 16, className }) {
|
|
572
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className, "aria-hidden": true, viewBox: "0 0 20 20", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10.5l4 4 8-9" }) });
|
|
573
|
+
}
|
|
293
574
|
function CenteredMessage({ title, detail }) {
|
|
294
575
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.center, children: [
|
|
295
576
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: title }),
|
|
@@ -297,21 +578,43 @@ function CenteredMessage({ title, detail }) {
|
|
|
297
578
|
] });
|
|
298
579
|
}
|
|
299
580
|
var fileName = (path) => path.replace(/\\/g, "/").split("/").pop() || path;
|
|
300
|
-
function parseDrawioAddress(address) {
|
|
301
|
-
|
|
302
|
-
if (!address.startsWith(prefix)) return null;
|
|
581
|
+
function parseDrawioAddress(address, fallbackSessionId) {
|
|
582
|
+
if (!address || typeof address !== "string") return null;
|
|
303
583
|
try {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
584
|
+
let clean = address.trim();
|
|
585
|
+
let parsedSessionId = "";
|
|
586
|
+
if (clean.startsWith("dsh-resource://file/session/")) {
|
|
587
|
+
const rest = clean.slice("dsh-resource://file/session/".length);
|
|
588
|
+
const slashIdx = rest.indexOf("/");
|
|
589
|
+
if (slashIdx >= 0) {
|
|
590
|
+
parsedSessionId = decodeURIComponent(rest.slice(0, slashIdx));
|
|
591
|
+
clean = rest.slice(slashIdx + 1);
|
|
592
|
+
} else {
|
|
593
|
+
clean = rest;
|
|
594
|
+
}
|
|
595
|
+
} else if (clean.startsWith("dsh-resource://file/")) {
|
|
596
|
+
clean = clean.slice("dsh-resource://file/".length);
|
|
597
|
+
} else if (clean.startsWith("file:///")) {
|
|
598
|
+
clean = clean.slice("file:///".length);
|
|
599
|
+
}
|
|
600
|
+
let decodedPath = clean;
|
|
601
|
+
try {
|
|
602
|
+
decodedPath = decodeURIComponent(clean);
|
|
603
|
+
} catch {
|
|
604
|
+
}
|
|
605
|
+
decodedPath = decodedPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
606
|
+
if (!/\.drawio$/i.test(decodedPath)) return null;
|
|
607
|
+
if (decodedPath.includes("\0")) return null;
|
|
608
|
+
return {
|
|
609
|
+
sessionId: parsedSessionId || fallbackSessionId || "",
|
|
610
|
+
path: decodedPath
|
|
611
|
+
};
|
|
309
612
|
} catch {
|
|
310
613
|
return null;
|
|
311
614
|
}
|
|
312
615
|
}
|
|
313
616
|
var messageOf = (reason) => reason instanceof Error ? reason.message : String(reason);
|
|
314
|
-
var statusText = (state) => state === "loading" ? "\u6B63\u5728\u8F7D\u5165" : state === "saving" ? "\u6B63\u5728\u4FDD\u5B58" : state === "external" ? "\u5DF2\u540C\u6B65 AI \u4FEE\u6539" : state === "error" ? "\u4FDD\u5B58\u5931\u8D25" : "\u5DF2\u4FDD\u5B58";
|
|
617
|
+
var statusText = (state) => state === "loading" ? "\u6B63\u5728\u8F7D\u5165" : state === "saving" ? "\u6B63\u5728\u4FDD\u5B58" : state === "external" ? "\u5DF2\u540C\u6B65 AI \u4FEE\u6539" : state === "conflict" ? "\u68C0\u6D4B\u5230\u51B2\u7A81" : state === "error" ? "\u4FDD\u5B58\u5931\u8D25" : "\u5DF2\u4FDD\u5B58";
|
|
315
618
|
var fg = "var(--dsw-alias-label-primary, currentColor)";
|
|
316
619
|
var muted = "var(--dsw-alias-label-secondary, rgba(127,127,127,.9))";
|
|
317
620
|
var layer = "var(--dsw-alias-container-bg, Canvas)";
|
|
@@ -320,6 +623,23 @@ var accent = "var(--dsw-alias-brand-primary, #ed67ad)";
|
|
|
320
623
|
var styles = {
|
|
321
624
|
root: { position: "relative", width: "100%", height: "100%", minHeight: 0, overflow: "hidden", color: fg, background: layer },
|
|
322
625
|
frame: { display: "block", width: "100%", height: "100%", border: 0, background: layer },
|
|
626
|
+
toolbar: { position: "absolute", right: 12, top: 12, zIndex: 10, display: "flex", flexDirection: "column", alignItems: "flex-end" },
|
|
627
|
+
exportTrigger: { display: "flex", alignItems: "center", gap: 6, padding: "5px 10px", border: `1px solid ${border}`, borderRadius: 6, color: fg, background: layer, boxShadow: "var(--dsw-alias-shadow-sm, 0 2px 8px rgba(0,0,0,.12))", fontSize: 12, fontWeight: 500, cursor: "pointer", backdropFilter: "blur(8px)", transition: "all 0.15s ease" },
|
|
628
|
+
triggerActive: { opacity: 0.75, cursor: "wait" },
|
|
629
|
+
arrowIcon: { fontSize: 10, opacity: 0.6, marginLeft: 2 },
|
|
630
|
+
exportMenu: { marginTop: 6, width: 220, padding: 12, border: `1px solid ${border}`, borderRadius: 8, background: layer, boxShadow: "var(--dsw-alias-shadow-md, 0 6px 20px rgba(0,0,0,.18))", backdropFilter: "blur(12px)", display: "flex", flexDirection: "column", gap: 10 },
|
|
631
|
+
menuHeader: { display: "flex", flexDirection: "column", gap: 2, paddingBottom: 6, borderBottom: `1px solid ${border}` },
|
|
632
|
+
menuTitle: { fontSize: 12, fontWeight: 600, color: fg },
|
|
633
|
+
menuSub: { fontSize: 10, color: muted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
634
|
+
formatRow: { display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 4 },
|
|
635
|
+
formatBtn: { padding: "4px 2px", border: `1px solid ${border}`, borderRadius: 4, background: "transparent", color: muted, fontSize: 10.5, cursor: "pointer", textAlign: "center", transition: "all 0.1s ease" },
|
|
636
|
+
formatBtnActive: { color: fg, fontWeight: 600, background: "var(--dsw-alias-fill-quaternary, rgba(127,127,127,0.12))" },
|
|
637
|
+
checkRow: { display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: muted, cursor: "pointer", userSelect: "none" },
|
|
638
|
+
checkbox: { cursor: "pointer", margin: 0 },
|
|
639
|
+
actionCol: { display: "flex", flexDirection: "column", gap: 6, paddingTop: 4 },
|
|
640
|
+
actionBtn: { display: "flex", alignItems: "center", justifyContent: "center", gap: 6, padding: "6px 10px", border: `1px solid ${border}`, borderRadius: 5, background: "transparent", color: fg, fontSize: 11.5, cursor: "pointer", transition: "all 0.12s ease" },
|
|
641
|
+
actionBtnPrimary: { color: "#ffffff", border: "none", fontWeight: 500 },
|
|
642
|
+
toast: { position: "absolute", top: 12, left: "50%", transform: "translateX(-50%)", zIndex: 12, display: "flex", alignItems: "center", gap: 6, padding: "6px 14px", border: `1px solid ${border}`, borderRadius: 20, background: layer, color: fg, boxShadow: "var(--dsw-alias-shadow-md, 0 4px 14px rgba(0,0,0,.16))", fontSize: 12, backdropFilter: "blur(10px)", pointerEvents: "none" },
|
|
323
643
|
status: { position: "absolute", right: 10, bottom: 9, zIndex: 2, display: "flex", alignItems: "center", gap: 6, padding: "4px 8px", border: `1px solid ${border}`, borderRadius: 999, color: muted, background: layer, boxShadow: "var(--dsw-alias-shadow-sm, 0 2px 8px rgba(0,0,0,.12))", fontSize: 11, pointerEvents: "none" },
|
|
324
644
|
statusError: { color: "var(--dsw-alias-danger, #d84f5f)" },
|
|
325
645
|
dot: { width: 6, height: 6, borderRadius: 99, background: accent },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civilization/dsh-drawio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Local draw.io canvas and model tools for DeepSeek Harness",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"src",
|
|
15
15
|
"lib",
|
|
16
16
|
"vendor",
|
|
17
|
+
"skills",
|
|
17
18
|
"cordis.patch.yml",
|
|
18
19
|
"README.md",
|
|
19
20
|
"THIRD_PARTY_NOTICES.md",
|