vite-plugin-opencode-assistant 1.1.18 → 1.1.20

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.
@@ -93,31 +93,14 @@ function startOpenCodeWeb(options) {
93
93
  var _a, _b;
94
94
  const { port, hostname, cwd, configDir, corsOrigins, contextApiUrl, logsApiUrl, logFilesJson } = options;
95
95
  const stateDir = createStateDirectory(cwd);
96
- const pluginsDir = import_path.default.join(stateDir, "plugins");
97
- const pluginPaths = [
98
- import_path.default.join(pluginsDir, "page-context.js"),
99
- import_path.default.join(pluginsDir, "vite-logs.js")
100
- ];
101
- if (logFilesJson) {
102
- pluginPaths.push(import_path.default.join(pluginsDir, "service-logs.js"));
103
- }
104
- const pluginPathsStr = pluginPaths.join(",");
105
96
  log.debug("Building process environment", {
106
97
  stateDir,
107
98
  configDir,
108
99
  contextApiUrl,
109
100
  logsApiUrl,
110
- logFilesJson,
111
- pluginPathsStr
112
- });
113
- const env = buildProcessEnv(
114
- stateDir,
115
- configDir,
116
- contextApiUrl,
117
- logsApiUrl,
118
- pluginPathsStr,
119
101
  logFilesJson
120
- );
102
+ });
103
+ const env = buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson);
121
104
  const args = ["serve", "--port", String(port), "--hostname", hostname];
122
105
  if (corsOrigins && corsOrigins.length > 0) {
123
106
  corsOrigins.forEach((origin) => {
@@ -184,11 +167,13 @@ function copyPluginFiles(sourceDir, targetDir) {
184
167
  }
185
168
  log.debug("All plugin files copied", { count: files.length, files });
186
169
  }
187
- function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, pluginPaths, logFilesJson) {
170
+ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson) {
188
171
  const env = __spreadProps(__spreadValues({}, Object.fromEntries(
189
172
  Object.entries(process.env).filter(([, v]) => v !== void 0)
190
173
  )), {
191
- XDG_STATE_HOME: stateDir
174
+ XDG_STATE_HOME: stateDir,
175
+ // 指向缓存目录,OpenCode 会从 <stateDir>/plugins/ 自动发现插件
176
+ OPENCODE_CONFIG_DIR: stateDir
192
177
  });
193
178
  if (configDir) {
194
179
  env.OPENCODE_CONFIG_DIR = configDir;
@@ -202,10 +187,6 @@ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, pluginP
202
187
  env.OPENCODE_VITE_LOGS_API_URL = logsApiUrl;
203
188
  log.debug("Set OPENCODE_VITE_LOGS_API_URL", { logsApiUrl });
204
189
  }
205
- if (pluginPaths) {
206
- env.OPENCODE_PLUGINS = pluginPaths;
207
- log.debug("Set OPENCODE_PLUGINS", { pluginPaths });
208
- }
209
190
  if (logFilesJson) {
210
191
  env.OPENCODE_LOG_FILES_JSON = logFilesJson;
211
192
  log.debug("Set OPENCODE_LOG_FILES_JSON", { logFilesJson });
@@ -57,13 +57,15 @@ function setupContextEndpoint(server, ctx) {
57
57
  return;
58
58
  }
59
59
  if (req.method === "GET") {
60
+ const pc = ctx.getPageContext();
61
+ log.debug(`[Context] GET \u2192 url=${pc.url} title=${pc.title} tabId=${pc.tabId}`);
60
62
  res.writeHead(200);
61
- res.end(JSON.stringify(ctx.pageContext));
63
+ res.end(JSON.stringify(pc));
62
64
  reqCtx.end(200);
63
65
  return;
64
66
  }
65
67
  if (req.method === "DELETE") {
66
- ctx.pageContext.selectedElements = [];
68
+ ctx.clearSelectedElements();
67
69
  log.debug("Selected elements cleared", { sseClients: ctx.sseClients.size });
68
70
  let sentCount = 0;
69
71
  ctx.sseClients.forEach((client) => {
@@ -92,22 +94,28 @@ function setupContextEndpoint(server, ctx) {
92
94
  var _a, _b, _c;
93
95
  try {
94
96
  const data = JSON.parse(body);
95
- ctx.pageContext = {
97
+ const tabId = data.tabId != null ? String(data.tabId) : "default";
98
+ const existing = ctx.getPageContext();
99
+ const newCtx = {
96
100
  url: data.url || "",
97
101
  title: data.title || "",
98
- // 保留已有的 tabId/tabIndex(避免页内 widget 覆盖 Side Panel 写入的值)
99
- tabId: (_a = data.tabId) != null ? _a : ctx.pageContext.tabId,
100
- tabIndex: (_b = data.tabIndex) != null ? _b : ctx.pageContext.tabIndex,
102
+ tabId: (_a = data.tabId) != null ? _a : existing.tabId,
103
+ tabIndex: (_b = data.tabIndex) != null ? _b : existing.tabIndex,
101
104
  selectedElements: data.selectedElements || []
102
105
  };
106
+ ctx.setPageContext(tabId, newCtx);
107
+ if (data.active) {
108
+ ctx.setActiveTabId(tabId);
109
+ }
103
110
  log.debug("Context updated", {
104
- url: ctx.pageContext.url,
105
- title: ctx.pageContext.title,
106
- selectedElementsCount: ((_c = ctx.pageContext.selectedElements) == null ? void 0 : _c.length) || 0
111
+ tabId,
112
+ url: newCtx.url,
113
+ title: newCtx.title,
114
+ selectedElementsCount: ((_c = newCtx.selectedElements) == null ? void 0 : _c.length) || 0
107
115
  });
108
- if (ctx.pageContext.selectedElements && ctx.pageContext.selectedElements.length > 0) {
116
+ if (newCtx.selectedElements && newCtx.selectedElements.length > 0) {
109
117
  log.debug("Selected elements details", {
110
- elements: ctx.pageContext.selectedElements.map((el) => {
118
+ elements: newCtx.selectedElements.map((el) => {
111
119
  var _a2;
112
120
  return {
113
121
  filePath: el.filePath,
@@ -3,8 +3,14 @@ import type http from "http";
3
3
  export interface EndpointContext {
4
4
  get webUrl(): string | null;
5
5
  get sseClients(): Set<http.ServerResponse>;
6
- get pageContext(): PageContext;
7
- set pageContext(ctx: PageContext);
6
+ /** 获取当前活跃 Tab 的上下文(扩展模式)或默认上下文(非扩展模式) */
7
+ getPageContext(): PageContext;
8
+ /** 写入某个 Tab 的上下文 */
9
+ setPageContext(tabId: string, ctx: PageContext): void;
10
+ /** 设置当前活跃 Tab ID */
11
+ setActiveTabId(tabId: string): void;
12
+ /** 清除选中元素(活跃 Tab) */
13
+ clearSelectedElements(): void;
8
14
  get isServiceStarted(): boolean;
9
15
  get currentTask(): {
10
16
  task: ServiceStartupTask;
package/lib/index.cjs CHANGED
@@ -94,7 +94,11 @@ function createOpenCodePlugin(options = {}) {
94
94
  let actualWebPort = config.webPort;
95
95
  let actualProxyPort = (_a = config.proxyPort) != null ? _a : import_shared.DEFAULT_PROXY_PORT;
96
96
  let projectRoot = "";
97
- let pageContext = { url: "", title: "" };
97
+ const pageContext = { url: "", title: "" };
98
+ const DEFAULT_TAB = "default";
99
+ const pageContexts = /* @__PURE__ */ new Map([[DEFAULT_TAB, pageContext]]);
100
+ let activeTabId = DEFAULT_TAB;
101
+ const pageKey = Math.random().toString(36).slice(2, 5);
98
102
  const serviceInstanceId = import_crypto.default.randomUUID();
99
103
  const sseClients = /* @__PURE__ */ new Set();
100
104
  const api = new import_api.OpenCodeAPI(
@@ -135,11 +139,25 @@ function createOpenCodePlugin(options = {}) {
135
139
  get sseClients() {
136
140
  return sseClients;
137
141
  },
138
- get pageContext() {
139
- return pageContext;
142
+ getPageContext() {
143
+ return pageContexts.get(activeTabId) || pageContexts.get(DEFAULT_TAB) || { url: "", title: "" };
140
144
  },
141
- set pageContext(ctx) {
142
- pageContext = ctx;
145
+ setPageContext(tabId, ctx) {
146
+ pageContexts.set(tabId || DEFAULT_TAB, ctx);
147
+ },
148
+ setActiveTabId(tabId) {
149
+ activeTabId = tabId;
150
+ },
151
+ clearSelectedElements() {
152
+ const ctx = pageContexts.get(activeTabId);
153
+ if (ctx) {
154
+ ctx.selectedElements = [];
155
+ pageContexts.set(activeTabId, ctx);
156
+ }
157
+ const defaultCtx = pageContexts.get(DEFAULT_TAB);
158
+ if (defaultCtx) {
159
+ defaultCtx.selectedElements = [];
160
+ }
143
161
  },
144
162
  get isServiceStarted() {
145
163
  return service.isStarted;
@@ -228,8 +246,10 @@ function createOpenCodePlugin(options = {}) {
228
246
  projectRoot,
229
247
  verbose: config.verbose
230
248
  });
249
+ const titleInject = `<script>(function(){var k="[${pageKey}]",d=document,p=!1,f=function(){if(p)return;var t=d.title;if(t.indexOf(k)===0)return;p=!0;d.title=k+t.replace(k,"");p=!1};f();new MutationObserver(f).observe(d.querySelector("title")||d.head,{childList:!0})})();</script>`;
231
250
  timer.end();
232
- return html.replace("</body>", `${widget}</body>`);
251
+ return html.replace("</body>", `${titleInject}
252
+ ${widget}</body>`);
233
253
  }
234
254
  };
235
255
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-opencode-assistant",
3
- "version": "1.1.18",
3
+ "version": "1.1.20",
4
4
  "description": "Embed OpenCode Web UI in your Vite dev server for real-time code modification and preview",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -37,15 +37,15 @@
37
37
  "dependencies": {
38
38
  "unplugin-vue-inspector": "^3.0.0",
39
39
  "execa": "^9.6.1",
40
- "@vite-plugin-opencode-assistant/opencode": "1.1.18",
41
- "@vite-plugin-opencode-assistant/components": "1.1.18",
42
- "@vite-plugin-opencode-assistant/shared": "1.1.18"
40
+ "@vite-plugin-opencode-assistant/opencode": "1.1.20",
41
+ "@vite-plugin-opencode-assistant/components": "1.1.20",
42
+ "@vite-plugin-opencode-assistant/shared": "1.1.20"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "vite": ">=5.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@vite-plugin-opencode-assistant/client": "1.1.18"
48
+ "@vite-plugin-opencode-assistant/client": "1.1.20"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "pagoda-cli build && vite build -c vite.client.config.ts",