@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.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.0";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cniot/mdd-editor",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "模型驱动编辑器",
5
5
  "scripts": {
6
6
  "build": "vite build"
@@ -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>
@@ -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 {