vite-plugin-opencode-assistant 1.0.91 → 1.0.93

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.
@@ -75,6 +75,29 @@ function generateBridgeScript(options) {
75
75
  const mergedSettings = mergeSettings(import_shared.DEFAULT_OPENCODE_SETTINGS, settings);
76
76
  return `
77
77
  (function() {
78
+ // === \u52AB\u6301 matchMedia\uFF0C\u5F3A\u5236\u684C\u9762\u7AEF\u5E03\u5C40\u4EE5\u6E32\u67D3\u5BA1\u67E5\u9762\u677F ===
79
+ // \u53EA\u52AB\u6301 opencode \u5224\u65AD\u684C\u9762\u7AEF\u7684\u5A92\u4F53\u67E5\u8BE2 (min-width: 768px)\uFF0C\u4E0D\u5F71\u54CD\u5176\u4ED6\u67E5\u8BE2
80
+ (function() {
81
+ var _origMatchMedia = window.matchMedia.bind(window);
82
+ var DESKTOP_QUERY = '(min-width: 768px)';
83
+ window.matchMedia = function(query) {
84
+ var result = _origMatchMedia(query);
85
+ if (query === DESKTOP_QUERY) {
86
+ return {
87
+ get matches() { return true; },
88
+ get media() { return result.media; },
89
+ onchange: null,
90
+ addListener: function(cb) { result.addListener(cb); },
91
+ removeListener: function(cb) { result.removeListener(cb); },
92
+ addEventListener: function(t, cb) { result.addEventListener(t, cb); },
93
+ removeEventListener: function(t, cb) { result.removeEventListener(t, cb); },
94
+ dispatchEvent: function(e) { return result.dispatchEvent(e); }
95
+ };
96
+ }
97
+ return result;
98
+ };
99
+ })();
100
+
78
101
  const STORAGE_KEYS = ${JSON.stringify(import_shared.OPENCODE_STORAGE_KEYS)};
79
102
  const THEME_KEY = STORAGE_KEYS.COLOR_SCHEME;
80
103
  const SETTINGS_KEY = STORAGE_KEYS.SETTINGS;
@@ -93,7 +116,27 @@ function generateBridgeScript(options) {
93
116
  }
94
117
 
95
118
  // \u521D\u59CB\u5316\u8BBE\u7F6E
96
- localStorage.setItem(SETTINGS_KEY, JSON.stringify(initialConfig.settings));
119
+ // \u6DF1\u5EA6\u5408\u5E76 initialConfig.settings \u5230\u5DF2\u6709\u914D\u7F6E\uFF1A\u53EA\u8986\u76D6\u63D2\u4EF6\u58F0\u660E\u7684\u5B57\u6BB5\uFF0C
120
+ // opencode \u81EA\u5DF1\u7BA1\u7406\u7684\u5176\u4ED6\u914D\u7F6E\u4E0D\u53D7\u5F71\u54CD
121
+ function deepMerge(target, source) {
122
+ for (var key in source) {
123
+ if (!{}.hasOwnProperty.call(source, key)) continue;
124
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
125
+ if (!target[key] || typeof target[key] !== "object") target[key] = {};
126
+ deepMerge(target[key], source[key]);
127
+ } else {
128
+ target[key] = source[key];
129
+ }
130
+ }
131
+ return target;
132
+ }
133
+
134
+ try {
135
+ var existing = JSON.parse(localStorage.getItem(SETTINGS_KEY) || "{}");
136
+ localStorage.setItem(SETTINGS_KEY, JSON.stringify(deepMerge(existing, initialConfig.settings)));
137
+ } catch {
138
+ localStorage.setItem(SETTINGS_KEY, JSON.stringify(initialConfig.settings));
139
+ }
97
140
 
98
141
  // === \u4E3B\u9898\u540C\u6B65\u51FD\u6570 ===
99
142
  function getTheme() {
@@ -151,6 +194,10 @@ function generateBridgeScript(options) {
151
194
  if (event.data && event.data.type === ${JSON.stringify(import_shared.WIDGET_MSG.SELECT_MODE_CHANGE)}) {
152
195
  handleSelectModeChange(event.data.selectMode);
153
196
  }
197
+
198
+ if (event.data && event.data.type === ${JSON.stringify(import_shared.WIDGET_MSG.REVIEW_PANEL_TOGGLE)}) {
199
+ handleReviewPanelToggle(event.data.visible);
200
+ }
154
201
  });
155
202
 
156
203
  // === \u952E\u76D8\u4E8B\u4EF6\u8F6C\u53D1\uFF08\u7528\u4E8E\u9000\u51FA\u9009\u62E9\u6A21\u5F0F\uFF09 ===
@@ -204,6 +251,85 @@ function generateBridgeScript(options) {
204
251
  button[data-slot="dropdown-menu-trigger"][icon="dot-grid"] {
205
252
  display: none !important;
206
253
  }
254
+ [data-component="icon-button-v2"][aria-label="\u66F4\u591A\u9009\u9879"],
255
+ [data-component="icon-button-v2"][aria-label="More options"] {
256
+ display: none !important;
257
+ }
258
+ \`;
259
+
260
+ // === \u5BA1\u67E5\u9762\u677F\u8986\u76D6\u6837\u5F0F ===
261
+ // \u5BA1\u67E5\u9762\u677F\u5168\u5C4F\uFF0C\u4F1A\u8BDD\u9762\u677F\u6D6E\u5728\u53F3\u4E0B\u89D2\uFF08\u7C7B\u4F3C\u63D2\u4EF6\u6700\u5C0F\u5316\u72B6\u6001\uFF09
262
+ const reviewPanelStyles = \`
263
+ .opencode-review-panel-overlay [data-ref="panel-row"] {
264
+ flex-direction: column !important;
265
+ gap: 8px !important;
266
+ padding: 8px !important;
267
+ }
268
+
269
+ /* \u5BA1\u67E5\u9762\u677F\u5360\u6EE1\u5269\u4F59\u7A7A\u95F4 */
270
+ .opencode-review-panel-overlay [data-ref="side-panel-container"] {
271
+ flex: 1 !important;
272
+ width: 100% !important;
273
+ transition: none !important;
274
+ min-height: 0 !important;
275
+ }
276
+
277
+ /* \u4F1A\u8BDD\u9762\u677F\u6D6E\u5728\u53F3\u4E0B\u89D2 */
278
+ .opencode-review-panel-overlay [data-ref="session-panel"] {
279
+ position: fixed !important;
280
+ bottom: 16px !important;
281
+ right: 16px !important;
282
+ width: 380px !important;
283
+ max-height: 70vh !important;
284
+ display: flex !important;
285
+ flex-direction: column !important;
286
+ z-index: 600 !important;
287
+ border-radius: 12px !important;
288
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2) !important;
289
+ overflow: hidden !important;
290
+ transition: none;
291
+ }
292
+ .opencode-review-panel-overlay [data-ref="session-panel"] * {
293
+ max-width: none !important;
294
+ }
295
+
296
+ /* \u9690\u85CF\u4F1A\u8BDD\u9762\u677F\u5185\u7684\u6807\u9898 */
297
+ .opencode-review-panel-overlay [data-ref="session-panel"] [data-session-title] {
298
+ display: none !important;
299
+ }
300
+
301
+ /* \u9690\u85CF\u6D6E\u52A8\u4F1A\u8BDD\uFF08\u7528 visibility+opacity \u907F\u514D\u5207\u6362\u95EA\u70C1\uFF09 */
302
+ .opencode-review-panel-overlay.hide-chat [data-ref="session-panel"] {
303
+ visibility: hidden !important;
304
+ opacity: 0 !important;
305
+ pointer-events: none !important;
306
+ }
307
+
308
+ /* \u5BA1\u67E5\u9762\u677F\u53F3\u4E0A\u89D2\u60AC\u6D6E\u5207\u6362\u6309\u94AE */
309
+ .opencode-chat-toggle-btn {
310
+ position: fixed !important;
311
+ top: 16px !important;
312
+ right: 16px !important;
313
+ z-index: 700 !important;
314
+ width: 36px !important;
315
+ height: 36px !important;
316
+ border-radius: 8px !important;
317
+ border: none !important;
318
+ background: var(--v2-background-bg-base, #1e1e1e) !important;
319
+ color: var(--v2-icon-icon-base, #ccc) !important;
320
+ cursor: pointer !important;
321
+ display: flex !important;
322
+ align-items: center !important;
323
+ justify-content: center !important;
324
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
325
+ transition: background 0.15s !important;
326
+ }
327
+ .opencode-chat-toggle-btn:hover {
328
+ background: var(--v2-overlay-simple-overlay-hover, #333) !important;
329
+ }
330
+ .opencode-chat-toggle-btn.active {
331
+ background: var(--v2-overlay-simple-overlay-pressed, #444) !important;
332
+ }
207
333
  \`;
208
334
 
209
335
  function injectMinimizeStyles() {
@@ -214,9 +340,18 @@ function generateBridgeScript(options) {
214
340
  document.head.appendChild(style);
215
341
  }
216
342
 
343
+ function injectReviewPanelStyles() {
344
+ if (document.getElementById('opencode-review-panel-styles')) return;
345
+ const style = document.createElement('style');
346
+ style.id = 'opencode-review-panel-styles';
347
+ style.textContent = reviewPanelStyles;
348
+ document.head.appendChild(style);
349
+ }
350
+
217
351
  // === \u6700\u5C0F\u5316\u72B6\u6001\u5904\u7406 ===
218
352
  let savedMinimizedState = null;
219
353
  let savedPromptDockVisibleState = null;
354
+ let savedReviewPanelState = null;
220
355
 
221
356
  function handleMinimizeStateChange(minimized) {
222
357
  savedMinimizedState = minimized;
@@ -236,6 +371,106 @@ function generateBridgeScript(options) {
236
371
  document.documentElement.classList.remove('opencode-prompt-dock-hidden');
237
372
  }
238
373
  }
374
+
375
+ // === \u5BA1\u67E5\u9762\u677F\u8986\u76D6\u72B6\u6001\u5904\u7406 ===
376
+ // \u7B56\u7565\uFF1A\u7EAF CSS class \u63A7\u5236\u3002\u9996\u6B21\u6253\u5F00\u65F6\u7ED9\u5173\u952E\u5143\u7D20\u6253 data-ref \u6807\u8BB0\uFF0C
377
+ // \u540E\u7EED\u53EA\u9700 toggle class\u3002\u5173\u95ED\u65F6\u79FB\u9664 class\uFF0CCSS \u89C4\u5219\u81EA\u52A8\u6062\u590D\u539F\u59CB\u5E03\u5C40\u3002
378
+ // \u4E0D\u4FEE\u6539\u4EFB\u4F55 inline style\uFF0C\u907F\u514D\u4FDD\u5B58/\u6062\u590D\u7684\u65F6\u673A\u548C DOM \u5F15\u7528\u95EE\u9898\u3002
379
+
380
+ function ensureReviewPanelRefs() {
381
+ var reviewPanel = document.querySelector('[data-component="session-review-v2"]')
382
+ || document.querySelector('[data-component="session-review"]');
383
+ if (!reviewPanel) return false;
384
+
385
+ // \u5411\u4E0A\u904D\u5386\u627E\u5230 panelRow
386
+ var panelRow = reviewPanel.parentElement;
387
+ while (panelRow) {
388
+ var cls = panelRow.className || '';
389
+ if (cls.indexOf('flex-col') !== -1 && cls.indexOf('md:flex-row') !== -1) break;
390
+ panelRow = panelRow.parentElement;
391
+ }
392
+ if (!panelRow) return false;
393
+ panelRow.setAttribute('data-ref', 'panel-row');
394
+
395
+ // \u627E\u5230\u4F1A\u8BDD\u9762\u677F\u548C\u4FA7\u8FB9\u9762\u677F\u5BB9\u5668
396
+ for (var i = 0; i < panelRow.children.length; i++) {
397
+ var child = panelRow.children[i];
398
+ if (child.nodeType !== 1) continue;
399
+ var c = child.className || '';
400
+ if (c.indexOf('@container') !== -1) {
401
+ child.setAttribute('data-ref', 'session-panel');
402
+ } else if (c.indexOf('min-w-0') !== -1 && c.indexOf('flex-col') !== -1) {
403
+ child.setAttribute('data-ref', 'side-panel-container');
404
+ }
405
+ }
406
+
407
+ return true;
408
+ }
409
+
410
+ function injectChatToggleBtn() {
411
+ if (document.getElementById('opencode-chat-toggle-btn')) return;
412
+ var btn = document.createElement('button');
413
+ btn.id = 'opencode-chat-toggle-btn';
414
+ btn.className = 'opencode-chat-toggle-btn';
415
+ btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>';
416
+ btn.title = '\u5C55\u5F00\u804A\u5929\u9762\u677F';
417
+ btn.onclick = function() {
418
+ document.documentElement.classList.toggle('hide-chat');
419
+ btn.classList.toggle('active');
420
+ btn.title = btn.classList.contains('active') ? '\u9690\u85CF\u804A\u5929\u9762\u677F' : '\u5C55\u5F00\u804A\u5929\u9762\u677F';
421
+ };
422
+ document.body.appendChild(btn);
423
+ }
424
+
425
+ function removeChatToggleBtn() {
426
+ var btn = document.getElementById('opencode-chat-toggle-btn');
427
+ if (btn) btn.remove();
428
+ }
429
+
430
+ function handleReviewPanelToggle(visible) {
431
+ // \u5982\u679C\u72B6\u6001\u6CA1\u6709\u53D8\u5316\uFF0C\u8DF3\u8FC7\uFF08\u907F\u514D MutationObserver \u91CD\u590D\u89E6\u53D1\uFF09
432
+ if (savedReviewPanelState === visible) return;
433
+ savedReviewPanelState = visible;
434
+
435
+ if (visible) {
436
+ // \u5148\u70B9\u51FB\u539F\u751F\u6309\u94AE\u6253\u5F00\u9762\u677F\uFF0C\u8BA9 DOM \u6E32\u67D3\u51FA\u6765
437
+ var reviewBtn = document.querySelector('[aria-controls="review-panel"]');
438
+ if (reviewBtn && reviewBtn.getAttribute('aria-expanded') !== 'true') {
439
+ reviewBtn.click();
440
+ }
441
+
442
+ // \u7B49 DOM \u5C31\u7EEA\u540E\u6253\u6807\u8BB0\u5E76\u5E94\u7528 CSS
443
+ var attempts = 0;
444
+ function tryApply() {
445
+ if (ensureReviewPanelRefs()) {
446
+ document.documentElement.classList.add('opencode-review-panel-overlay', 'hide-chat');
447
+ injectChatToggleBtn();
448
+ return;
449
+ }
450
+ attempts++;
451
+ if (attempts < 20) requestAnimationFrame(tryApply);
452
+ }
453
+ requestAnimationFrame(tryApply);
454
+ } else {
455
+ // \u5173\u95ED\u524D\u5148\u5C06\u4F1A\u8BDD\u9762\u677F\u5BBD\u5EA6\u8BBE\u4E3A 100%\uFF0C\u907F\u514D SolidJS \u5F02\u6B65\u6E32\u67D3\u7684\u5BBD\u5EA6\u8DF3\u53D8
456
+ var sessionPanel = document.querySelector('[data-ref="session-panel"]');
457
+ if (sessionPanel) sessionPanel.style.width = '100%';
458
+ document.body.offsetHeight; // \u5F3A\u5236\u56DE\u6D41
459
+
460
+ document.documentElement.classList.remove('opencode-review-panel-overlay');
461
+ document.documentElement.classList.remove('hide-chat');
462
+ removeChatToggleBtn();
463
+
464
+ // \u7B49\u4E00\u5E27\u8BA9 CSS \u5E03\u5C40\u53D8\u5316\u5148\u6E32\u67D3\uFF0C\u518D\u5173\u95ED\u539F\u751F\u9762\u677F
465
+ // \u907F\u514D SolidJS \u5F02\u6B65 DOM \u53D8\u5316\u548C CSS \u5E03\u5C40\u53D8\u5316\u53D1\u751F\u5728\u540C\u4E00\u5E27\u5BFC\u81F4\u89C6\u89C9\u8DF3\u52A8
466
+ requestAnimationFrame(function() {
467
+ var reviewBtn = document.querySelector('[aria-controls="review-panel"]');
468
+ if (reviewBtn && reviewBtn.getAttribute('aria-expanded') === 'true') {
469
+ reviewBtn.click();
470
+ }
471
+ });
472
+ }
473
+ }
239
474
 
240
475
  // === \u5E94\u7528\u4FDD\u5B58\u7684\u72B6\u6001 ===
241
476
  function applySavedStates() {
@@ -245,6 +480,9 @@ function generateBridgeScript(options) {
245
480
  if (savedPromptDockVisibleState !== null) {
246
481
  handlePromptDockVisibilityChange(savedPromptDockVisibleState);
247
482
  }
483
+ if (savedReviewPanelState !== null) {
484
+ handleReviewPanelToggle(savedReviewPanelState);
485
+ }
248
486
  }
249
487
 
250
488
  // === \u4FDD\u5B58\u8F93\u5165\u6846\u5149\u6807\u4F4D\u7F6E ===
@@ -390,6 +628,7 @@ function generateBridgeScript(options) {
390
628
  // === \u5C31\u7EEA\u901A\u77E5 ===
391
629
  function init() {
392
630
  injectMinimizeStyles();
631
+ injectReviewPanelStyles();
393
632
  if (window.parent !== window) {
394
633
  window.parent.postMessage({ type: ${JSON.stringify(import_shared.WIDGET_MSG.READY)} }, "*");
395
634
  }
@@ -183,7 +183,8 @@ Please install OpenCode first:
183
183
  if (((_a = this.webProcess) == null ? void 0 : _a.exitCode) !== null && ((_b = this.webProcess) == null ? void 0 : _b.exitCode) !== void 0) {
184
184
  throw new Error(`OpenCode process exited with code ${this.webProcess.exitCode}`);
185
185
  }
186
- log.info(`OpenCode Web started at ${webUrl}`);
186
+ const version = yield (0, import_system.getOpenCodeVersion)();
187
+ log.info(`OpenCode Web started at ${webUrl}${version ? ` (opencode ${version})` : ""}`);
187
188
  } catch (e) {
188
189
  log.error("OpenCode Web failed to start", { error: e });
189
190
  this.sendTaskUpdate("web_start_timeout");
@@ -89,19 +89,21 @@ function setupContextEndpoint(server, ctx) {
89
89
  let body = "";
90
90
  req.on("data", (chunk) => body += chunk);
91
91
  req.on("end", () => {
92
- var _a;
92
+ var _a, _b, _c;
93
93
  try {
94
94
  const data = JSON.parse(body);
95
95
  ctx.pageContext = {
96
96
  url: data.url || "",
97
97
  title: data.title || "",
98
- tabId: data.tabId,
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,
99
101
  selectedElements: data.selectedElements || []
100
102
  };
101
103
  log.debug("Context updated", {
102
104
  url: ctx.pageContext.url,
103
105
  title: ctx.pageContext.title,
104
- selectedElementsCount: ((_a = ctx.pageContext.selectedElements) == null ? void 0 : _a.length) || 0
106
+ selectedElementsCount: ((_c = ctx.pageContext.selectedElements) == null ? void 0 : _c.length) || 0
105
107
  });
106
108
  if (ctx.pageContext.selectedElements && ctx.pageContext.selectedElements.length > 0) {
107
109
  log.debug("Selected elements details", {
@@ -50,6 +50,7 @@ __export(system_exports, {
50
50
  checkOpenCodeInstalled: () => checkOpenCodeInstalled,
51
51
  findAvailablePort: () => findAvailablePort,
52
52
  findGitRoot: () => findGitRoot,
53
+ getOpenCodeVersion: () => getOpenCodeVersion,
53
54
  isPortAvailable: () => isPortAvailable,
54
55
  killOrphanOpenCodeProcesses: () => killOrphanOpenCodeProcesses,
55
56
  waitForServer: () => waitForServer
@@ -121,6 +122,26 @@ function checkOpenCodeInstalled() {
121
122
  });
122
123
  });
123
124
  }
125
+ function getOpenCodeVersion() {
126
+ return new Promise((resolve) => {
127
+ var _a;
128
+ const proc = (0, import_child_process.spawn)("opencode", ["--version"], { stdio: "pipe" });
129
+ let output = "";
130
+ (_a = proc.stdout) == null ? void 0 : _a.on("data", (data) => {
131
+ output += data.toString();
132
+ });
133
+ proc.on("close", (code) => {
134
+ if (code === 0 && output.trim()) {
135
+ resolve(output.trim());
136
+ } else {
137
+ resolve(null);
138
+ }
139
+ });
140
+ proc.on("error", () => {
141
+ resolve(null);
142
+ });
143
+ });
144
+ }
124
145
  function isPortAvailable(port, hostname) {
125
146
  return __async(this, null, function* () {
126
147
  return new Promise((resolve) => {
@@ -302,6 +323,7 @@ function killOrphanProcessesOnUnix(resolve, timer) {
302
323
  checkOpenCodeInstalled,
303
324
  findAvailablePort,
304
325
  findGitRoot,
326
+ getOpenCodeVersion,
305
327
  isPortAvailable,
306
328
  killOrphanOpenCodeProcesses,
307
329
  waitForServer
@@ -1,6 +1,7 @@
1
1
  import type { ResultPromise } from "execa";
2
2
  export declare function waitForServer(url: string, timeout?: number, process?: ResultPromise): Promise<void>;
3
3
  export declare function checkOpenCodeInstalled(): Promise<boolean>;
4
+ export declare function getOpenCodeVersion(): Promise<string | null>;
4
5
  export declare function isPortAvailable(port: number, hostname?: string): Promise<boolean>;
5
6
  export declare function findAvailablePort(startPort: number, hostname?: string, maxTries?: number): Promise<number>;
6
7
  export declare function killOrphanOpenCodeProcesses(): Promise<number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-opencode-assistant",
3
- "version": "1.0.91",
3
+ "version": "1.0.93",
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/shared": "1.0.91",
41
- "@vite-plugin-opencode-assistant/opencode": "1.0.91",
42
- "@vite-plugin-opencode-assistant/components": "1.0.91"
40
+ "@vite-plugin-opencode-assistant/opencode": "1.0.93",
41
+ "@vite-plugin-opencode-assistant/components": "1.0.93",
42
+ "@vite-plugin-opencode-assistant/shared": "1.0.93"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "vite": ">=5.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@vite-plugin-opencode-assistant/client": "1.0.91"
48
+ "@vite-plugin-opencode-assistant/client": "1.0.93"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "pagoda-cli build && vite build -c vite.client.config.ts",