@cniot/mdd-editor 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs.js +17 -17
- package/build/index.es.js +90 -2
- package/package.json +1 -1
- package/src/ai/LocalAIDrawer.jsx +6 -0
- package/src/ai/bridgeClient.js +92 -0
package/build/index.es.js
CHANGED
|
@@ -30059,7 +30059,18 @@ function FtpBuild({ schema, moduleMap }) {
|
|
|
30059
30059
|
}
|
|
30060
30060
|
const DEFAULT_BRIDGE_URL = "http://127.0.0.1:17678";
|
|
30061
30061
|
const trimEndSlash = (value = "") => value.replace(/\/+$/, "");
|
|
30062
|
+
const RELAY_CHANNEL = "mdd-ai-bridge";
|
|
30063
|
+
const isLoopbackURL = (value = "") => /^http:\/\/(127(?:\.\d{1,3}){3}|localhost)(?::\d+)?/i.test(value);
|
|
30064
|
+
function shouldUseBridgeRelay(baseURL) {
|
|
30065
|
+
return typeof window !== "undefined" && !window.isSecureContext && isLoopbackURL(baseURL);
|
|
30066
|
+
}
|
|
30067
|
+
function createRequestId() {
|
|
30068
|
+
return `${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
30069
|
+
}
|
|
30062
30070
|
async function request(baseURL, path, options = {}) {
|
|
30071
|
+
if (shouldUseBridgeRelay(baseURL)) {
|
|
30072
|
+
return requestViaRelay(baseURL, path, options);
|
|
30073
|
+
}
|
|
30063
30074
|
const res = await fetch(`${trimEndSlash(baseURL)}${path}`, {
|
|
30064
30075
|
...options,
|
|
30065
30076
|
headers: {
|
|
@@ -30079,6 +30090,79 @@ async function request(baseURL, path, options = {}) {
|
|
|
30079
30090
|
}
|
|
30080
30091
|
return data;
|
|
30081
30092
|
}
|
|
30093
|
+
function requestViaRelay(baseURL, path, options = {}) {
|
|
30094
|
+
const relayURL = `${trimEndSlash(baseURL)}/relay`;
|
|
30095
|
+
const requestId = createRequestId();
|
|
30096
|
+
const relayWindow = window.open(
|
|
30097
|
+
relayURL,
|
|
30098
|
+
"mdd-ai-bridge-relay",
|
|
30099
|
+
"width=520,height=320,menubar=no,toolbar=no,location=no,status=no"
|
|
30100
|
+
);
|
|
30101
|
+
if (!relayWindow) {
|
|
30102
|
+
throw new Error("\u6D4F\u89C8\u5668\u62E6\u622A\u4E86\u672C\u5730 Bridge \u7A97\u53E3\uFF0C\u8BF7\u5141\u8BB8\u5F39\u7A97\u540E\u91CD\u8BD5");
|
|
30103
|
+
}
|
|
30104
|
+
return new Promise((resolve, reject) => {
|
|
30105
|
+
let settled = false;
|
|
30106
|
+
let ready = false;
|
|
30107
|
+
const cleanup = () => {
|
|
30108
|
+
window.removeEventListener("message", handleMessage);
|
|
30109
|
+
clearTimeout(timeoutTimer);
|
|
30110
|
+
clearTimeout(fallbackTimer);
|
|
30111
|
+
};
|
|
30112
|
+
const postRequest = () => {
|
|
30113
|
+
relayWindow.postMessage(
|
|
30114
|
+
{
|
|
30115
|
+
channel: RELAY_CHANNEL,
|
|
30116
|
+
type: "request",
|
|
30117
|
+
id: requestId,
|
|
30118
|
+
path,
|
|
30119
|
+
options
|
|
30120
|
+
},
|
|
30121
|
+
trimEndSlash(baseURL)
|
|
30122
|
+
);
|
|
30123
|
+
};
|
|
30124
|
+
const finish = (callback, value) => {
|
|
30125
|
+
if (settled)
|
|
30126
|
+
return;
|
|
30127
|
+
settled = true;
|
|
30128
|
+
cleanup();
|
|
30129
|
+
callback(value);
|
|
30130
|
+
};
|
|
30131
|
+
const handleMessage = (event) => {
|
|
30132
|
+
var _a2;
|
|
30133
|
+
if (event.origin !== trimEndSlash(baseURL))
|
|
30134
|
+
return;
|
|
30135
|
+
if (event.source !== relayWindow)
|
|
30136
|
+
return;
|
|
30137
|
+
const message = event.data || {};
|
|
30138
|
+
if (message.channel !== RELAY_CHANNEL)
|
|
30139
|
+
return;
|
|
30140
|
+
if (message.type === "ready" && !ready) {
|
|
30141
|
+
ready = true;
|
|
30142
|
+
postRequest();
|
|
30143
|
+
return;
|
|
30144
|
+
}
|
|
30145
|
+
if (message.type !== "response" || message.id !== requestId)
|
|
30146
|
+
return;
|
|
30147
|
+
if (!message.ok) {
|
|
30148
|
+
finish(
|
|
30149
|
+
reject,
|
|
30150
|
+
new Error(((_a2 = message.data) == null ? void 0 : _a2.message) || `\u8BF7\u6C42\u672C\u5730 AI Bridge \u5931\u8D25: ${message.status || 0}`)
|
|
30151
|
+
);
|
|
30152
|
+
return;
|
|
30153
|
+
}
|
|
30154
|
+
finish(resolve, message.data);
|
|
30155
|
+
};
|
|
30156
|
+
const timeoutTimer = setTimeout(() => {
|
|
30157
|
+
finish(reject, new Error("\u672C\u5730 AI Bridge relay \u54CD\u5E94\u8D85\u65F6\uFF0C\u8BF7\u786E\u8BA4\u670D\u52A1\u5DF2\u542F\u52A8"));
|
|
30158
|
+
}, 3e4);
|
|
30159
|
+
const fallbackTimer = setTimeout(() => {
|
|
30160
|
+
if (!ready)
|
|
30161
|
+
postRequest();
|
|
30162
|
+
}, 500);
|
|
30163
|
+
window.addEventListener("message", handleMessage);
|
|
30164
|
+
});
|
|
30165
|
+
}
|
|
30082
30166
|
function normalizeBridgeConfig(config2) {
|
|
30083
30167
|
if (config2 === false) {
|
|
30084
30168
|
return {
|
|
@@ -30402,6 +30486,10 @@ function LocalAIDrawer(props) {
|
|
|
30402
30486
|
}
|
|
30403
30487
|
};
|
|
30404
30488
|
React$1.useEffect(() => {
|
|
30489
|
+
if (shouldUseBridgeRelay(baseURL)) {
|
|
30490
|
+
setStatus("\u70B9\u51FB\u6309\u94AE\u540E\u901A\u8FC7\u672C\u5730 relay \u8FDE\u63A5");
|
|
30491
|
+
return;
|
|
30492
|
+
}
|
|
30405
30493
|
checkHealth();
|
|
30406
30494
|
}, []);
|
|
30407
30495
|
return /* @__PURE__ */ React$1.createElement("div", {
|
|
@@ -30447,7 +30535,7 @@ function LocalAIDrawer(props) {
|
|
|
30447
30535
|
className: "mdd-local-ai-title"
|
|
30448
30536
|
}, "\u4F7F\u7528\u65B9\u5F0F"), /* @__PURE__ */ React$1.createElement("div", {
|
|
30449
30537
|
className: "mdd-local-ai-tips"
|
|
30450
|
-
}, /* @__PURE__ */ React$1.createElement("div", null, "1. \u9996\u6B21\u4F7F\u7528\u5148\u5168\u5C40\u5B89\u88C5\uFF1Anpm i -g @cniot/mdd-ai-bridge\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "2. \u5B89\u88C5\u540E\u5728\u4EFB\u610F\u76EE\u5F55\u8FD0\u884C\uFF1Amdd-ai-bridge\u3002\u4E5F\u53EF\u4EE5\u76F4\u63A5\u8FD0\u884C\uFF1Anpx @cniot/mdd-ai-bridge\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "3. \u70B9\u51FB\u201C\u53D1\u9001\u5230\u672C\u5730 AI\u201D\uFF0C\u5F53\u524D\u9875\u9762\u4F1A\u5199\u5165\u672C\u5730\u5DE5\u4F5C\u533A\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "4. \u7528 Cursor\u3001Qoder\u3001Codex CLI \u7B49\u5DE5\u5177\u6253\u5F00\u76EE\u5F55\u5E76\u4FEE\u6539\u6587\u4EF6\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "5. \u70B9\u51FB\u201C\u540C\u6B65\u672C\u5730\u4FEE\u6539\u201D\uFF0C\u786E\u8BA4\u9884\u89C8\u540E\u7EE7\u7EED\u4F7F\u7528\u539F\u4FDD\u5B58\u6309\u94AE\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "\u672C\u5730\u5DE5\u4F5C\u533A\u9ED8\u8BA4\u5728 ~/.mdd-ai-workspace\u3002"))));
|
|
30538
|
+
}, /* @__PURE__ */ React$1.createElement("div", null, "1. \u9996\u6B21\u4F7F\u7528\u5148\u5168\u5C40\u5B89\u88C5\uFF1Anpm i -g @cniot/mdd-ai-bridge\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "2. \u5B89\u88C5\u540E\u5728\u4EFB\u610F\u76EE\u5F55\u8FD0\u884C\uFF1Amdd-ai-bridge\u3002\u4E5F\u53EF\u4EE5\u76F4\u63A5\u8FD0\u884C\uFF1Anpx @cniot/mdd-ai-bridge\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "3. \u70B9\u51FB\u201C\u53D1\u9001\u5230\u672C\u5730 AI\u201D\uFF0C\u5F53\u524D\u9875\u9762\u4F1A\u5199\u5165\u672C\u5730\u5DE5\u4F5C\u533A\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "4. \u7528 Cursor\u3001Qoder\u3001Codex CLI \u7B49\u5DE5\u5177\u6253\u5F00\u76EE\u5F55\u5E76\u4FEE\u6539\u6587\u4EF6\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "5. \u70B9\u51FB\u201C\u540C\u6B65\u672C\u5730\u4FEE\u6539\u201D\uFF0C\u786E\u8BA4\u9884\u89C8\u540E\u7EE7\u7EED\u4F7F\u7528\u539F\u4FDD\u5B58\u6309\u94AE\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "\u7EBF\u4E0A HTTP \u9875\u9762\u4F1A\u81EA\u52A8\u6253\u5F00\u672C\u5730 Bridge relay \u7A97\u53E3\uFF0C\u8BF7\u5141\u8BB8\u6D4F\u89C8\u5668\u5F39\u7A97\u3002"), /* @__PURE__ */ React$1.createElement("div", null, "\u672C\u5730\u5DE5\u4F5C\u533A\u9ED8\u8BA4\u5728 ~/.mdd-ai-workspace\u3002"))));
|
|
30451
30539
|
}
|
|
30452
30540
|
var index = "";
|
|
30453
30541
|
const isDebug = ((_b = (_a = window == null ? void 0 : window.location) == null ? void 0 : _a.search) == null ? void 0 : _b.indexOf("debug")) >= 0 || ((_c = window.localStorage) == null ? void 0 : _c.__l4_location_search_debug__) === "true";
|
|
@@ -30986,7 +31074,7 @@ function getDefaultIndexStyle() {
|
|
|
30986
31074
|
`;
|
|
30987
31075
|
}
|
|
30988
31076
|
const name = "@cniot/mdd-editor";
|
|
30989
|
-
const version = "0.3.
|
|
31077
|
+
const version = "0.3.1";
|
|
30990
31078
|
const description = "\u6A21\u578B\u9A71\u52A8\u7F16\u8F91\u5668";
|
|
30991
31079
|
const scripts = {
|
|
30992
31080
|
build: "vite build"
|
package/package.json
CHANGED
package/src/ai/LocalAIDrawer.jsx
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
openPageWorkspace,
|
|
9
9
|
pullPage,
|
|
10
10
|
pushPage,
|
|
11
|
+
shouldUseBridgeRelay,
|
|
11
12
|
} from './bridgeClient';
|
|
12
13
|
import { buildPageIR, getPageCode } from './pageIR';
|
|
13
14
|
import { EVENT_KEY } from '$src/common/const';
|
|
@@ -152,6 +153,10 @@ export default function LocalAIDrawer(props) {
|
|
|
152
153
|
};
|
|
153
154
|
|
|
154
155
|
React.useEffect(() => {
|
|
156
|
+
if (shouldUseBridgeRelay(baseURL)) {
|
|
157
|
+
setStatus('点击按钮后通过本地 relay 连接');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
155
160
|
checkHealth();
|
|
156
161
|
}, []);
|
|
157
162
|
|
|
@@ -203,6 +208,7 @@ export default function LocalAIDrawer(props) {
|
|
|
203
208
|
<div>3. 点击“发送到本地 AI”,当前页面会写入本地工作区。</div>
|
|
204
209
|
<div>4. 用 Cursor、Qoder、Codex CLI 等工具打开目录并修改文件。</div>
|
|
205
210
|
<div>5. 点击“同步本地修改”,确认预览后继续使用原保存按钮。</div>
|
|
211
|
+
<div>线上 HTTP 页面会自动打开本地 Bridge relay 窗口,请允许浏览器弹窗。</div>
|
|
206
212
|
<div>本地工作区默认在 ~/.mdd-ai-workspace。</div>
|
|
207
213
|
</div>
|
|
208
214
|
</CnCard>
|
package/src/ai/bridgeClient.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
export const DEFAULT_BRIDGE_URL = 'http://127.0.0.1:17678';
|
|
2
2
|
|
|
3
3
|
const trimEndSlash = (value = '') => value.replace(/\/+$/, '');
|
|
4
|
+
const RELAY_CHANNEL = 'mdd-ai-bridge';
|
|
5
|
+
|
|
6
|
+
const isLoopbackURL = (value = '') => /^http:\/\/(127(?:\.\d{1,3}){3}|localhost)(?::\d+)?/i.test(value);
|
|
7
|
+
|
|
8
|
+
export function shouldUseBridgeRelay(baseURL) {
|
|
9
|
+
return typeof window !== 'undefined' && !window.isSecureContext && isLoopbackURL(baseURL);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createRequestId() {
|
|
13
|
+
return `${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
14
|
+
}
|
|
4
15
|
|
|
5
16
|
async function request(baseURL, path, options = {}) {
|
|
17
|
+
if (shouldUseBridgeRelay(baseURL)) {
|
|
18
|
+
return requestViaRelay(baseURL, path, options);
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
const res = await fetch(`${trimEndSlash(baseURL)}${path}`, {
|
|
7
22
|
...options,
|
|
8
23
|
headers: {
|
|
@@ -25,6 +40,83 @@ async function request(baseURL, path, options = {}) {
|
|
|
25
40
|
return data;
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
function requestViaRelay(baseURL, path, options = {}) {
|
|
44
|
+
const relayURL = `${trimEndSlash(baseURL)}/relay`;
|
|
45
|
+
const requestId = createRequestId();
|
|
46
|
+
const relayWindow = window.open(
|
|
47
|
+
relayURL,
|
|
48
|
+
'mdd-ai-bridge-relay',
|
|
49
|
+
'width=520,height=320,menubar=no,toolbar=no,location=no,status=no',
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (!relayWindow) {
|
|
53
|
+
throw new Error('浏览器拦截了本地 Bridge 窗口,请允许弹窗后重试');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
let settled = false;
|
|
58
|
+
let ready = false;
|
|
59
|
+
|
|
60
|
+
const cleanup = () => {
|
|
61
|
+
window.removeEventListener('message', handleMessage);
|
|
62
|
+
clearTimeout(timeoutTimer);
|
|
63
|
+
clearTimeout(fallbackTimer);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const postRequest = () => {
|
|
67
|
+
relayWindow.postMessage(
|
|
68
|
+
{
|
|
69
|
+
channel: RELAY_CHANNEL,
|
|
70
|
+
type: 'request',
|
|
71
|
+
id: requestId,
|
|
72
|
+
path,
|
|
73
|
+
options,
|
|
74
|
+
},
|
|
75
|
+
trimEndSlash(baseURL),
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const finish = (callback, value) => {
|
|
80
|
+
if (settled) return;
|
|
81
|
+
settled = true;
|
|
82
|
+
cleanup();
|
|
83
|
+
callback(value);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleMessage = (event) => {
|
|
87
|
+
if (event.origin !== trimEndSlash(baseURL)) return;
|
|
88
|
+
if (event.source !== relayWindow) return;
|
|
89
|
+
const message = event.data || {};
|
|
90
|
+
if (message.channel !== RELAY_CHANNEL) return;
|
|
91
|
+
|
|
92
|
+
if (message.type === 'ready' && !ready) {
|
|
93
|
+
ready = true;
|
|
94
|
+
postRequest();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (message.type !== 'response' || message.id !== requestId) return;
|
|
99
|
+
if (!message.ok) {
|
|
100
|
+
finish(
|
|
101
|
+
reject,
|
|
102
|
+
new Error(message.data?.message || `请求本地 AI Bridge 失败: ${message.status || 0}`),
|
|
103
|
+
);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
finish(resolve, message.data);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const timeoutTimer = setTimeout(() => {
|
|
110
|
+
finish(reject, new Error('本地 AI Bridge relay 响应超时,请确认服务已启动'));
|
|
111
|
+
}, 30000);
|
|
112
|
+
const fallbackTimer = setTimeout(() => {
|
|
113
|
+
if (!ready) postRequest();
|
|
114
|
+
}, 500);
|
|
115
|
+
|
|
116
|
+
window.addEventListener('message', handleMessage);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
28
120
|
export function normalizeBridgeConfig(config) {
|
|
29
121
|
if (config === false) {
|
|
30
122
|
return {
|