vite-plugin-opencode-assistant 1.0.10 → 1.0.12

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.
@@ -46,6 +46,16 @@ function utf8ToBase64(str) {
46
46
  const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
47
47
  return btoa(binString);
48
48
  }
49
+ let proxyUrl = "";
50
+ function toProxyUrl(url) {
51
+ if (!url || !proxyUrl) return url;
52
+ try {
53
+ const urlObj = new URL(url, window.location.origin);
54
+ return `${proxyUrl}${urlObj.pathname}${urlObj.search}`;
55
+ } catch (e) {
56
+ return url;
57
+ }
58
+ }
49
59
  let config = {};
50
60
  const scriptTag = document.querySelector(`script[${CONFIG_DATA_ATTR}]`);
51
61
  if (scriptTag) {
@@ -74,15 +84,19 @@ const App = {
74
84
  const selectedElements = ref([]);
75
85
  const widgetRef = ref(null);
76
86
  const {
77
- webUrl = "",
78
87
  position = "bottom-right",
79
- theme = "auto",
88
+ theme: initialTheme = "auto",
80
89
  open: autoOpen = false,
81
90
  sessionUrl: initialSessionUrl = "",
91
+ proxyUrl: configProxyUrl = "",
82
92
  lazy = false,
83
93
  hotkey = "ctrl+k",
84
94
  cwd = ""
85
95
  } = config;
96
+ if (configProxyUrl) {
97
+ proxyUrl = configProxyUrl;
98
+ }
99
+ const theme = ref(initialTheme);
86
100
  const isWaitingForSession = ref(!initialSessionUrl);
87
101
  const computedLoading = computed(() => loading.value || isWaitingForSession.value);
88
102
  let servicesStarted = !lazy;
@@ -93,7 +107,7 @@ const App = {
93
107
  };
94
108
  currentSessionId.value = extractSessionId(initialSessionUrl);
95
109
  if (servicesStarted && initialSessionUrl) {
96
- iframeSrc.value = initialSessionUrl;
110
+ iframeSrc.value = toProxyUrl(initialSessionUrl);
97
111
  }
98
112
  try {
99
113
  const stored = sessionStorage.getItem("__opencode_selected_elements__");
@@ -134,9 +148,14 @@ const App = {
134
148
  try {
135
149
  const response = yield fetch("/__opencode_sessions__", { method: "POST" });
136
150
  const newSession = yield response.json();
137
- yield loadSessions();
151
+ sessions.value.unshift({
152
+ id: newSession.id,
153
+ title: "\u65B0\u4F1A\u8BDD",
154
+ updatedAt: Date.now()
155
+ });
138
156
  currentSessionId.value = newSession.id;
139
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
157
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
158
+ loadSessions();
140
159
  } catch (e) {
141
160
  showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
142
161
  }
@@ -150,7 +169,7 @@ const App = {
150
169
  if (sessions.value.length > 0) {
151
170
  const nextSession = sessions.value[0];
152
171
  currentSessionId.value = nextSession.id;
153
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
172
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
154
173
  } else {
155
174
  currentSessionId.value = null;
156
175
  iframeSrc.value = "";
@@ -164,7 +183,7 @@ const App = {
164
183
  if (currentSessionId.value === session.id) return;
165
184
  currentSessionId.value = session.id;
166
185
  loading.value = true;
167
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
186
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
168
187
  setTimeout(() => {
169
188
  loading.value = false;
170
189
  }, 500);
@@ -180,7 +199,7 @@ const App = {
180
199
  updateContext(true);
181
200
  } else if (data.type === "SESSION_READY") {
182
201
  if (data.sessionUrl && !iframeSrc.value) {
183
- iframeSrc.value = data.sessionUrl;
202
+ iframeSrc.value = toProxyUrl(data.sessionUrl);
184
203
  currentSessionId.value = extractSessionId(data.sessionUrl);
185
204
  }
186
205
  isWaitingForSession.value = false;
@@ -220,7 +239,7 @@ const App = {
220
239
  if (data.success) {
221
240
  servicesStarted = true;
222
241
  if (data.sessionUrl) {
223
- iframeSrc.value = data.sessionUrl;
242
+ iframeSrc.value = toProxyUrl(data.sessionUrl);
224
243
  currentSessionId.value = extractSessionId(data.sessionUrl);
225
244
  isWaitingForSession.value = false;
226
245
  }
@@ -295,7 +314,7 @@ const App = {
295
314
  return h(OpenCodeWidget, {
296
315
  ref: widgetRef,
297
316
  position,
298
- theme,
317
+ theme: theme.value,
299
318
  open: open.value,
300
319
  selectMode: selectMode.value,
301
320
  sessionListCollapsed: sessionListCollapsed.value,
@@ -314,6 +333,12 @@ const App = {
314
333
  "onUpdate:sessionListCollapsed": (val) => {
315
334
  sessionListCollapsed.value = val;
316
335
  },
336
+ "onUpdate:theme": (val) => {
337
+ theme.value = val;
338
+ },
339
+ "onToggle-theme": (val) => {
340
+ theme.value = val;
341
+ },
317
342
  "onCreate-session": createSession,
318
343
  "onDelete-session": deleteSession,
319
344
  "onSelect-session": selectSession,
@@ -0,0 +1,11 @@
1
+ import http from "http";
2
+ import { type OpenCodeSettings, type OpenCodeLanguage } from "@vite-plugin-opencode-assistant/shared";
3
+ export interface ProxyServerOptions {
4
+ /** 主题模式 */
5
+ theme?: "light" | "dark" | "auto";
6
+ /** OpenCode 界面语言 */
7
+ language?: OpenCodeLanguage;
8
+ /** OpenCode 内部设置 */
9
+ settings?: OpenCodeSettings;
10
+ }
11
+ export declare function startProxyServer(targetUrl: string, port: number, options?: ProxyServerOptions): http.Server;
@@ -0,0 +1,194 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ import http from "http";
21
+ import {
22
+ createLogger,
23
+ DEFAULT_OPENCODE_SETTINGS,
24
+ OPENCODE_STORAGE_KEYS
25
+ } from "@vite-plugin-opencode-assistant/shared";
26
+ const log = createLogger("ProxyServer");
27
+ function mergeSettings(defaultSettings, userSettings) {
28
+ if (!userSettings) return defaultSettings;
29
+ const result = __spreadValues({}, defaultSettings);
30
+ if (userSettings.general) {
31
+ result.general = __spreadValues(__spreadValues({}, defaultSettings.general), userSettings.general);
32
+ }
33
+ if (userSettings.appearance) {
34
+ result.appearance = userSettings.appearance;
35
+ }
36
+ if (userSettings.permissions) {
37
+ result.permissions = userSettings.permissions;
38
+ }
39
+ if (userSettings.notifications) {
40
+ result.notifications = userSettings.notifications;
41
+ }
42
+ if (userSettings.sounds) {
43
+ result.sounds = userSettings.sounds;
44
+ }
45
+ return result;
46
+ }
47
+ function generateBridgeScript(options) {
48
+ const { theme = "auto", language, settings } = options;
49
+ const mergedSettings = mergeSettings(DEFAULT_OPENCODE_SETTINGS, settings);
50
+ return `
51
+ (function() {
52
+ const STORAGE_KEYS = ${JSON.stringify(OPENCODE_STORAGE_KEYS)};
53
+ const THEME_KEY = STORAGE_KEYS.COLOR_SCHEME;
54
+ const SETTINGS_KEY = STORAGE_KEYS.SETTINGS;
55
+
56
+ // === \u521D\u59CB\u5316\u914D\u7F6E ===
57
+ const initialConfig = {
58
+ theme: ${JSON.stringify(theme)},
59
+ language: ${JSON.stringify(language || null)},
60
+ settings: ${JSON.stringify(mergedSettings)}
61
+ };
62
+
63
+ // \u521D\u59CB\u5316\u4E3B\u9898
64
+ if (initialConfig.theme && initialConfig.theme !== "auto") {
65
+ localStorage.setItem(THEME_KEY, initialConfig.theme);
66
+ document.documentElement.setAttribute("data-color-scheme", initialConfig.theme);
67
+ }
68
+
69
+ // \u521D\u59CB\u5316\u8BBE\u7F6E
70
+ localStorage.setItem(SETTINGS_KEY, JSON.stringify(initialConfig.settings));
71
+
72
+ // === \u4E3B\u9898\u540C\u6B65\u51FD\u6570 ===
73
+ function getTheme() {
74
+ try {
75
+ return localStorage.getItem(THEME_KEY) || "system";
76
+ } catch {
77
+ return "system";
78
+ }
79
+ }
80
+
81
+ function setTheme(theme) {
82
+ try {
83
+ const oldTheme = localStorage.getItem(THEME_KEY);
84
+ localStorage.setItem(THEME_KEY, theme);
85
+ document.documentElement.setAttribute('data-color-scheme', theme);
86
+
87
+ if (oldTheme !== theme) {
88
+ window.dispatchEvent(new StorageEvent('storage', {
89
+ key: THEME_KEY,
90
+ oldValue: oldTheme,
91
+ newValue: theme,
92
+ url: window.location.href
93
+ }));
94
+ }
95
+ } catch {
96
+ // ignore
97
+ }
98
+ }
99
+
100
+ // === \u6D88\u606F\u76D1\u542C ===
101
+ window.addEventListener("message", function(event) {
102
+ if (event.data && event.data.type === "OPENCODE_SET_THEME") {
103
+ setTheme(event.data.theme);
104
+ }
105
+ });
106
+
107
+ // === \u5C31\u7EEA\u901A\u77E5 ===
108
+ window.addEventListener("load", function() {
109
+ if (window.parent !== window) {
110
+ window.parent.postMessage({ type: "OPENCODE_READY" }, "*");
111
+ }
112
+ });
113
+ })();
114
+ `;
115
+ }
116
+ function startProxyServer(targetUrl, port, options = {}) {
117
+ const target = new URL(targetUrl);
118
+ const bridgeScript = generateBridgeScript(options);
119
+ const server = http.createServer((req, res) => {
120
+ if (req.url === "/__opencode_bridge__.js") {
121
+ const body = bridgeScript;
122
+ res.writeHead(200, {
123
+ "content-type": "application/javascript; charset=utf-8",
124
+ "cache-control": "no-store",
125
+ "content-length": Buffer.byteLength(body)
126
+ });
127
+ res.end(body);
128
+ return;
129
+ }
130
+ const options2 = {
131
+ hostname: target.hostname,
132
+ port: target.port,
133
+ path: req.url,
134
+ method: req.method,
135
+ headers: __spreadProps(__spreadValues({}, req.headers), {
136
+ host: target.host,
137
+ // Don't accept compressed responses so we can modify HTML
138
+ "accept-encoding": "identity"
139
+ })
140
+ };
141
+ const proxyReq = http.request(options2, (proxyRes) => {
142
+ var _a;
143
+ const rawContentType = proxyRes.headers["content-type"];
144
+ const contentType = Array.isArray(rawContentType) ? (_a = rawContentType[0]) != null ? _a : "" : rawContentType != null ? rawContentType : "";
145
+ if (contentType.includes("text/html")) {
146
+ const chunks = [];
147
+ proxyRes.on("data", (chunk) => {
148
+ chunks.push(chunk);
149
+ });
150
+ proxyRes.on("end", () => {
151
+ let body = Buffer.concat(chunks).toString("utf-8");
152
+ if (body.match(/<\/head>/i)) {
153
+ body = body.replace(
154
+ /<\/head>/i,
155
+ '<script src="/__opencode_bridge__.js"></script></head>'
156
+ );
157
+ } else if (body.match(/<\/body>/i)) {
158
+ body = body.replace(
159
+ /<\/body>/i,
160
+ '<script src="/__opencode_bridge__.js"></script></body>'
161
+ );
162
+ } else {
163
+ body += '<script src="/__opencode_bridge__.js"></script>';
164
+ }
165
+ const headers = {};
166
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
167
+ if (value !== void 0 && key !== "content-encoding" && key !== "transfer-encoding" && key !== "content-length") {
168
+ headers[key] = value;
169
+ }
170
+ }
171
+ headers["content-length"] = Buffer.byteLength(body);
172
+ res.writeHead(proxyRes.statusCode || 200, headers);
173
+ res.end(body);
174
+ });
175
+ } else {
176
+ res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
177
+ proxyRes.pipe(res);
178
+ }
179
+ });
180
+ proxyReq.on("error", (err) => {
181
+ log.error("Proxy error", { error: err.message, url: req.url });
182
+ res.writeHead(502);
183
+ res.end("Proxy error");
184
+ });
185
+ req.pipe(proxyReq);
186
+ });
187
+ server.listen(port, () => {
188
+ log.info(`Proxy server started on port ${port} -> ${targetUrl}`);
189
+ });
190
+ return server;
191
+ }
192
+ export {
193
+ startProxyServer
194
+ };
@@ -7,12 +7,15 @@ export declare class OpenCodeService {
7
7
  private api;
8
8
  private sseClients;
9
9
  private onPortAllocated;
10
+ private onProxyPortAllocated;
10
11
  webProcess: ResultPromise | null;
11
12
  actualWebPort: number;
13
+ actualProxyPort: number;
12
14
  isStarted: boolean;
13
15
  private startPromise;
14
16
  sessionUrl: string | null;
15
- constructor(config: Required<OpenCodeOptions>, api: OpenCodeAPI, sseClients: Set<http.ServerResponse>, onPortAllocated: (port: number) => void);
17
+ private proxyServer;
18
+ constructor(config: Required<OpenCodeOptions>, api: OpenCodeAPI, sseClients: Set<http.ServerResponse>, onPortAllocated: (port: number) => void, onProxyPortAllocated: (port: number) => void);
16
19
  start(corsOrigins?: string[], contextApiUrl?: string, viteOrigin?: string): Promise<void>;
17
20
  stop(): Promise<void>;
18
21
  }
@@ -22,26 +22,36 @@ var __async = (__this, __arguments, generator) => {
22
22
  });
23
23
  };
24
24
  import { prepareOpenCodeRuntime, startOpenCodeWeb } from "@vite-plugin-opencode-assistant/opencode";
25
- import { SERVER_START_TIMEOUT, createLogger } from "@vite-plugin-opencode-assistant/shared";
25
+ import {
26
+ DEFAULT_PROXY_PORT,
27
+ SERVER_START_TIMEOUT,
28
+ createLogger
29
+ } from "@vite-plugin-opencode-assistant/shared";
26
30
  import {
27
31
  checkOpenCodeInstalled,
28
32
  findAvailablePort,
29
33
  killOrphanOpenCodeProcesses,
30
34
  waitForServer
31
35
  } from "../utils/system.js";
36
+ import { startProxyServer } from "./proxy-server.js";
32
37
  const log = createLogger("Service");
33
38
  class OpenCodeService {
34
- constructor(config, api, sseClients, onPortAllocated) {
39
+ constructor(config, api, sseClients, onPortAllocated, onProxyPortAllocated) {
35
40
  __publicField(this, "config", config);
36
41
  __publicField(this, "api", api);
37
42
  __publicField(this, "sseClients", sseClients);
38
43
  __publicField(this, "onPortAllocated", onPortAllocated);
44
+ __publicField(this, "onProxyPortAllocated", onProxyPortAllocated);
39
45
  __publicField(this, "webProcess", null);
40
46
  __publicField(this, "actualWebPort");
47
+ __publicField(this, "actualProxyPort");
41
48
  __publicField(this, "isStarted", false);
42
49
  __publicField(this, "startPromise", null);
43
50
  __publicField(this, "sessionUrl", null);
51
+ __publicField(this, "proxyServer", null);
52
+ var _a;
44
53
  this.actualWebPort = config.webPort;
54
+ this.actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
45
55
  }
46
56
  start(corsOrigins, contextApiUrl, viteOrigin) {
47
57
  return __async(this, null, function* () {
@@ -54,6 +64,7 @@ class OpenCodeService {
54
64
  return this.startPromise;
55
65
  }
56
66
  this.startPromise = (() => __async(this, null, function* () {
67
+ var _a, _b, _c;
57
68
  const timer = log.timer("startServices", {
58
69
  corsOrigins,
59
70
  contextApiUrl,
@@ -116,6 +127,24 @@ Please install OpenCode first:
116
127
  log.info(`Waiting for OpenCode Web to become ready at ${webUrl}...`);
117
128
  yield waitForServer(webUrl, SERVER_START_TIMEOUT);
118
129
  log.info(`OpenCode Web started at ${webUrl}`);
130
+ this.actualProxyPort = yield findAvailablePort(
131
+ (_a = this.config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT,
132
+ this.config.hostname
133
+ );
134
+ this.onProxyPortAllocated(this.actualProxyPort);
135
+ if (this.actualProxyPort !== ((_b = this.config.proxyPort) != null ? _b : DEFAULT_PROXY_PORT)) {
136
+ log.info(
137
+ `Proxy port ${(_c = this.config.proxyPort) != null ? _c : DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
138
+ );
139
+ } else {
140
+ log.debug(`Using proxy port ${this.actualProxyPort}`);
141
+ }
142
+ this.proxyServer = startProxyServer(webUrl, this.actualProxyPort, {
143
+ theme: this.config.theme,
144
+ language: this.config.language,
145
+ settings: this.config.settings
146
+ });
147
+ timer.checkpoint("Proxy server started");
119
148
  yield this.api.warmupChromeMcp(viteOrigin);
120
149
  timer.checkpoint("Chrome MCP warmup complete");
121
150
  try {
@@ -147,6 +176,11 @@ Please install OpenCode first:
147
176
  return __async(this, null, function* () {
148
177
  const timer = log.timer("stopServices");
149
178
  log.info("Stopping OpenCode services...");
179
+ if (this.proxyServer) {
180
+ log.debug("Closing proxy server");
181
+ this.proxyServer.close();
182
+ this.proxyServer = null;
183
+ }
150
184
  if (this.webProcess) {
151
185
  log.debug("Killing web process", { pid: this.webProcess.pid });
152
186
  this.webProcess.kill("SIGTERM");
@@ -2,6 +2,7 @@ import type { PageContext, SessionInfo } from "@vite-plugin-opencode-assistant/s
2
2
  import type http from "http";
3
3
  export interface EndpointContext {
4
4
  get sessionUrl(): string | null;
5
+ get webUrl(): string | null;
5
6
  get sseClients(): Set<http.ServerResponse>;
6
7
  get pageContext(): PageContext;
7
8
  set pageContext(ctx: PageContext);
package/es/index.js CHANGED
@@ -38,6 +38,7 @@ import Inspector from "unplugin-vue-inspector/vite";
38
38
  import {
39
39
  CONTEXT_API_PATH,
40
40
  DEFAULT_CONFIG,
41
+ DEFAULT_PROXY_PORT,
41
42
  createLogger,
42
43
  setVerbose
43
44
  } from "@vite-plugin-opencode-assistant/shared";
@@ -59,16 +60,26 @@ function opencodePlugin(options = {}) {
59
60
  return plugins;
60
61
  }
61
62
  function createOpenCodePlugin(options = {}) {
63
+ var _a;
62
64
  const config = __spreadValues(__spreadValues({}, DEFAULT_CONFIG), options);
63
65
  setVerbose(config.verbose);
64
66
  const log = createLogger("Plugin");
65
67
  let actualWebPort = config.webPort;
68
+ let actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
66
69
  let pageContext = { url: "", title: "" };
67
70
  const sseClients = /* @__PURE__ */ new Set();
68
71
  const api = new OpenCodeAPI(config.hostname, () => actualWebPort, config.warmupChromeMcp);
69
- const service = new OpenCodeService(config, api, sseClients, (port) => {
70
- actualWebPort = port;
71
- });
72
+ const service = new OpenCodeService(
73
+ config,
74
+ api,
75
+ sseClients,
76
+ (port) => {
77
+ actualWebPort = port;
78
+ },
79
+ (port) => {
80
+ actualProxyPort = port;
81
+ }
82
+ );
72
83
  return {
73
84
  name: "vite-plugin-opencode",
74
85
  apply(_viteConfig, env) {
@@ -77,12 +88,15 @@ function createOpenCodePlugin(options = {}) {
77
88
  },
78
89
  configureServer(server) {
79
90
  return __async(this, null, function* () {
80
- var _a2, _b;
91
+ var _a2, _b2;
81
92
  const timer = log.timer("configureServer");
82
93
  setupMiddlewares(server, {
83
94
  get sessionUrl() {
84
95
  return service.sessionUrl;
85
96
  },
97
+ get webUrl() {
98
+ return actualWebPort ? `http://${config.hostname}:${actualWebPort}` : null;
99
+ },
86
100
  get sseClients() {
87
101
  return sseClients;
88
102
  },
@@ -131,7 +145,7 @@ function createOpenCodePlugin(options = {}) {
131
145
  log.error("Failed to start services", { error: e });
132
146
  }
133
147
  }));
134
- (_b = server.httpServer) == null ? void 0 : _b.on("close", () => {
148
+ (_b2 = server.httpServer) == null ? void 0 : _b2.on("close", () => {
135
149
  log.debug("HTTP server closing");
136
150
  service.stop();
137
151
  });
@@ -149,6 +163,7 @@ function createOpenCodePlugin(options = {}) {
149
163
  const timer = log.timer("transformIndexHtml");
150
164
  const widget = injectWidget({
151
165
  webUrl: `http://${config.hostname}:${actualWebPort}`,
166
+ proxyUrl: `http://${config.hostname}:${actualProxyPort}`,
152
167
  serverUrl: `http://${config.hostname}:${actualWebPort}`,
153
168
  position: config.position,
154
169
  theme: config.theme,
@@ -46,6 +46,16 @@ function utf8ToBase64(str) {
46
46
  const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
47
47
  return btoa(binString);
48
48
  }
49
+ let proxyUrl = "";
50
+ function toProxyUrl(url) {
51
+ if (!url || !proxyUrl) return url;
52
+ try {
53
+ const urlObj = new URL(url, window.location.origin);
54
+ return `${proxyUrl}${urlObj.pathname}${urlObj.search}`;
55
+ } catch (e) {
56
+ return url;
57
+ }
58
+ }
49
59
  let config = {};
50
60
  const scriptTag = document.querySelector(`script[${import_shared.CONFIG_DATA_ATTR}]`);
51
61
  if (scriptTag) {
@@ -74,15 +84,19 @@ const App = {
74
84
  const selectedElements = (0, import_vue.ref)([]);
75
85
  const widgetRef = (0, import_vue.ref)(null);
76
86
  const {
77
- webUrl = "",
78
87
  position = "bottom-right",
79
- theme = "auto",
88
+ theme: initialTheme = "auto",
80
89
  open: autoOpen = false,
81
90
  sessionUrl: initialSessionUrl = "",
91
+ proxyUrl: configProxyUrl = "",
82
92
  lazy = false,
83
93
  hotkey = "ctrl+k",
84
94
  cwd = ""
85
95
  } = config;
96
+ if (configProxyUrl) {
97
+ proxyUrl = configProxyUrl;
98
+ }
99
+ const theme = (0, import_vue.ref)(initialTheme);
86
100
  const isWaitingForSession = (0, import_vue.ref)(!initialSessionUrl);
87
101
  const computedLoading = (0, import_vue.computed)(() => loading.value || isWaitingForSession.value);
88
102
  let servicesStarted = !lazy;
@@ -93,7 +107,7 @@ const App = {
93
107
  };
94
108
  currentSessionId.value = extractSessionId(initialSessionUrl);
95
109
  if (servicesStarted && initialSessionUrl) {
96
- iframeSrc.value = initialSessionUrl;
110
+ iframeSrc.value = toProxyUrl(initialSessionUrl);
97
111
  }
98
112
  try {
99
113
  const stored = sessionStorage.getItem("__opencode_selected_elements__");
@@ -134,9 +148,14 @@ const App = {
134
148
  try {
135
149
  const response = yield fetch("/__opencode_sessions__", { method: "POST" });
136
150
  const newSession = yield response.json();
137
- yield loadSessions();
151
+ sessions.value.unshift({
152
+ id: newSession.id,
153
+ title: "\u65B0\u4F1A\u8BDD",
154
+ updatedAt: Date.now()
155
+ });
138
156
  currentSessionId.value = newSession.id;
139
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
157
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
158
+ loadSessions();
140
159
  } catch (e) {
141
160
  showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
142
161
  }
@@ -150,7 +169,7 @@ const App = {
150
169
  if (sessions.value.length > 0) {
151
170
  const nextSession = sessions.value[0];
152
171
  currentSessionId.value = nextSession.id;
153
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
172
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
154
173
  } else {
155
174
  currentSessionId.value = null;
156
175
  iframeSrc.value = "";
@@ -164,7 +183,7 @@ const App = {
164
183
  if (currentSessionId.value === session.id) return;
165
184
  currentSessionId.value = session.id;
166
185
  loading.value = true;
167
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
186
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
168
187
  setTimeout(() => {
169
188
  loading.value = false;
170
189
  }, 500);
@@ -180,7 +199,7 @@ const App = {
180
199
  updateContext(true);
181
200
  } else if (data.type === "SESSION_READY") {
182
201
  if (data.sessionUrl && !iframeSrc.value) {
183
- iframeSrc.value = data.sessionUrl;
202
+ iframeSrc.value = toProxyUrl(data.sessionUrl);
184
203
  currentSessionId.value = extractSessionId(data.sessionUrl);
185
204
  }
186
205
  isWaitingForSession.value = false;
@@ -220,7 +239,7 @@ const App = {
220
239
  if (data.success) {
221
240
  servicesStarted = true;
222
241
  if (data.sessionUrl) {
223
- iframeSrc.value = data.sessionUrl;
242
+ iframeSrc.value = toProxyUrl(data.sessionUrl);
224
243
  currentSessionId.value = extractSessionId(data.sessionUrl);
225
244
  isWaitingForSession.value = false;
226
245
  }
@@ -295,7 +314,7 @@ const App = {
295
314
  return (0, import_vue.h)(import_components.OpenCodeWidget, {
296
315
  ref: widgetRef,
297
316
  position,
298
- theme,
317
+ theme: theme.value,
299
318
  open: open.value,
300
319
  selectMode: selectMode.value,
301
320
  sessionListCollapsed: sessionListCollapsed.value,
@@ -314,6 +333,12 @@ const App = {
314
333
  "onUpdate:sessionListCollapsed": (val) => {
315
334
  sessionListCollapsed.value = val;
316
335
  },
336
+ "onUpdate:theme": (val) => {
337
+ theme.value = val;
338
+ },
339
+ "onToggle-theme": (val) => {
340
+ theme.value = val;
341
+ },
317
342
  "onCreate-session": createSession,
318
343
  "onDelete-session": deleteSession,
319
344
  "onSelect-session": selectSession,