vite-plugin-opencode-assistant 1.0.11 → 1.0.13

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.
@@ -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,221 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __export = (target, all) => {
25
+ for (var name in all)
26
+ __defProp(target, name, { get: all[name], enumerable: true });
27
+ };
28
+ var __copyProps = (to, from, except, desc) => {
29
+ if (from && typeof from === "object" || typeof from === "function") {
30
+ for (let key of __getOwnPropNames(from))
31
+ if (!__hasOwnProp.call(to, key) && key !== except)
32
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
33
+ }
34
+ return to;
35
+ };
36
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
37
+ // If the importer is in node compatibility mode or this is not an ESM
38
+ // file that has been converted to a CommonJS file using a Babel-
39
+ // compatible transform (i.e. "__esModule" has not been set), then set
40
+ // "default" to the CommonJS "module.exports" for node compatibility.
41
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
42
+ mod
43
+ ));
44
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
45
+ var proxy_server_exports = {};
46
+ __export(proxy_server_exports, {
47
+ startProxyServer: () => startProxyServer
48
+ });
49
+ module.exports = __toCommonJS(proxy_server_exports);
50
+ var import_http = __toESM(require("http"));
51
+ var import_shared = require("@vite-plugin-opencode-assistant/shared");
52
+ const log = (0, import_shared.createLogger)("ProxyServer");
53
+ function mergeSettings(defaultSettings, userSettings) {
54
+ if (!userSettings) return defaultSettings;
55
+ const result = __spreadValues({}, defaultSettings);
56
+ if (userSettings.general) {
57
+ result.general = __spreadValues(__spreadValues({}, defaultSettings.general), userSettings.general);
58
+ }
59
+ if (userSettings.appearance) {
60
+ result.appearance = userSettings.appearance;
61
+ }
62
+ if (userSettings.permissions) {
63
+ result.permissions = userSettings.permissions;
64
+ }
65
+ if (userSettings.notifications) {
66
+ result.notifications = userSettings.notifications;
67
+ }
68
+ if (userSettings.sounds) {
69
+ result.sounds = userSettings.sounds;
70
+ }
71
+ return result;
72
+ }
73
+ function generateBridgeScript(options) {
74
+ const { theme = "auto", language, settings } = options;
75
+ const mergedSettings = mergeSettings(import_shared.DEFAULT_OPENCODE_SETTINGS, settings);
76
+ return `
77
+ (function() {
78
+ const STORAGE_KEYS = ${JSON.stringify(import_shared.OPENCODE_STORAGE_KEYS)};
79
+ const THEME_KEY = STORAGE_KEYS.COLOR_SCHEME;
80
+ const SETTINGS_KEY = STORAGE_KEYS.SETTINGS;
81
+
82
+ // === \u521D\u59CB\u5316\u914D\u7F6E ===
83
+ const initialConfig = {
84
+ theme: ${JSON.stringify(theme)},
85
+ language: ${JSON.stringify(language || null)},
86
+ settings: ${JSON.stringify(mergedSettings)}
87
+ };
88
+
89
+ // \u521D\u59CB\u5316\u4E3B\u9898
90
+ if (initialConfig.theme && initialConfig.theme !== "auto") {
91
+ localStorage.setItem(THEME_KEY, initialConfig.theme);
92
+ document.documentElement.setAttribute("data-color-scheme", initialConfig.theme);
93
+ }
94
+
95
+ // \u521D\u59CB\u5316\u8BBE\u7F6E
96
+ localStorage.setItem(SETTINGS_KEY, JSON.stringify(initialConfig.settings));
97
+
98
+ // === \u4E3B\u9898\u540C\u6B65\u51FD\u6570 ===
99
+ function getTheme() {
100
+ try {
101
+ return localStorage.getItem(THEME_KEY) || "system";
102
+ } catch {
103
+ return "system";
104
+ }
105
+ }
106
+
107
+ function setTheme(theme) {
108
+ try {
109
+ const oldTheme = localStorage.getItem(THEME_KEY);
110
+ localStorage.setItem(THEME_KEY, theme);
111
+ document.documentElement.setAttribute('data-color-scheme', theme);
112
+
113
+ if (oldTheme !== theme) {
114
+ window.dispatchEvent(new StorageEvent('storage', {
115
+ key: THEME_KEY,
116
+ oldValue: oldTheme,
117
+ newValue: theme,
118
+ url: window.location.href
119
+ }));
120
+ }
121
+ } catch {
122
+ // ignore
123
+ }
124
+ }
125
+
126
+ // === \u6D88\u606F\u76D1\u542C ===
127
+ window.addEventListener("message", function(event) {
128
+ if (event.data && event.data.type === "OPENCODE_SET_THEME") {
129
+ setTheme(event.data.theme);
130
+ }
131
+ });
132
+
133
+ // === \u5C31\u7EEA\u901A\u77E5 ===
134
+ window.addEventListener("load", function() {
135
+ if (window.parent !== window) {
136
+ window.parent.postMessage({ type: "OPENCODE_READY" }, "*");
137
+ }
138
+ });
139
+ })();
140
+ `;
141
+ }
142
+ function startProxyServer(targetUrl, port, options = {}) {
143
+ const target = new URL(targetUrl);
144
+ const bridgeScript = generateBridgeScript(options);
145
+ const server = import_http.default.createServer((req, res) => {
146
+ if (req.url === "/__opencode_bridge__.js") {
147
+ const body = bridgeScript;
148
+ res.writeHead(200, {
149
+ "content-type": "application/javascript; charset=utf-8",
150
+ "cache-control": "no-store",
151
+ "content-length": Buffer.byteLength(body)
152
+ });
153
+ res.end(body);
154
+ return;
155
+ }
156
+ const options2 = {
157
+ hostname: target.hostname,
158
+ port: target.port,
159
+ path: req.url,
160
+ method: req.method,
161
+ headers: __spreadProps(__spreadValues({}, req.headers), {
162
+ host: target.host,
163
+ // Don't accept compressed responses so we can modify HTML
164
+ "accept-encoding": "identity"
165
+ })
166
+ };
167
+ const proxyReq = import_http.default.request(options2, (proxyRes) => {
168
+ var _a;
169
+ const rawContentType = proxyRes.headers["content-type"];
170
+ const contentType = Array.isArray(rawContentType) ? (_a = rawContentType[0]) != null ? _a : "" : rawContentType != null ? rawContentType : "";
171
+ if (contentType.includes("text/html")) {
172
+ const chunks = [];
173
+ proxyRes.on("data", (chunk) => {
174
+ chunks.push(chunk);
175
+ });
176
+ proxyRes.on("end", () => {
177
+ let body = Buffer.concat(chunks).toString("utf-8");
178
+ if (body.match(/<\/head>/i)) {
179
+ body = body.replace(
180
+ /<\/head>/i,
181
+ '<script src="/__opencode_bridge__.js"></script></head>'
182
+ );
183
+ } else if (body.match(/<\/body>/i)) {
184
+ body = body.replace(
185
+ /<\/body>/i,
186
+ '<script src="/__opencode_bridge__.js"></script></body>'
187
+ );
188
+ } else {
189
+ body += '<script src="/__opencode_bridge__.js"></script>';
190
+ }
191
+ const headers = {};
192
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
193
+ if (value !== void 0 && key !== "content-encoding" && key !== "transfer-encoding" && key !== "content-length") {
194
+ headers[key] = value;
195
+ }
196
+ }
197
+ headers["content-length"] = Buffer.byteLength(body);
198
+ res.writeHead(proxyRes.statusCode || 200, headers);
199
+ res.end(body);
200
+ });
201
+ } else {
202
+ res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
203
+ proxyRes.pipe(res);
204
+ }
205
+ });
206
+ proxyReq.on("error", (err) => {
207
+ log.error("Proxy error", { error: err.message, url: req.url });
208
+ res.writeHead(502);
209
+ res.end("Proxy error");
210
+ });
211
+ req.pipe(proxyReq);
212
+ });
213
+ server.listen(port, () => {
214
+ log.info(`Proxy server started on port ${port} -> ${targetUrl}`);
215
+ });
216
+ return server;
217
+ }
218
+ // Annotate the CommonJS export names for ESM import in node:
219
+ 0 && (module.exports = {
220
+ startProxyServer
221
+ });
@@ -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
  }
@@ -45,19 +45,25 @@ module.exports = __toCommonJS(service_exports);
45
45
  var import_opencode = require("@vite-plugin-opencode-assistant/opencode");
46
46
  var import_shared = require("@vite-plugin-opencode-assistant/shared");
47
47
  var import_system = require("../utils/system.js");
48
+ var import_proxy_server = require("./proxy-server.js");
48
49
  const log = (0, import_shared.createLogger)("Service");
49
50
  class OpenCodeService {
50
- constructor(config, api, sseClients, onPortAllocated) {
51
+ constructor(config, api, sseClients, onPortAllocated, onProxyPortAllocated) {
51
52
  __publicField(this, "config", config);
52
53
  __publicField(this, "api", api);
53
54
  __publicField(this, "sseClients", sseClients);
54
55
  __publicField(this, "onPortAllocated", onPortAllocated);
56
+ __publicField(this, "onProxyPortAllocated", onProxyPortAllocated);
55
57
  __publicField(this, "webProcess", null);
56
58
  __publicField(this, "actualWebPort");
59
+ __publicField(this, "actualProxyPort");
57
60
  __publicField(this, "isStarted", false);
58
61
  __publicField(this, "startPromise", null);
59
62
  __publicField(this, "sessionUrl", null);
63
+ __publicField(this, "proxyServer", null);
64
+ var _a;
60
65
  this.actualWebPort = config.webPort;
66
+ this.actualProxyPort = (_a = config.proxyPort) != null ? _a : import_shared.DEFAULT_PROXY_PORT;
61
67
  }
62
68
  start(corsOrigins, contextApiUrl, viteOrigin) {
63
69
  return __async(this, null, function* () {
@@ -70,6 +76,7 @@ class OpenCodeService {
70
76
  return this.startPromise;
71
77
  }
72
78
  this.startPromise = (() => __async(this, null, function* () {
79
+ var _a, _b, _c;
73
80
  const timer = log.timer("startServices", {
74
81
  corsOrigins,
75
82
  contextApiUrl,
@@ -132,6 +139,24 @@ Please install OpenCode first:
132
139
  log.info(`Waiting for OpenCode Web to become ready at ${webUrl}...`);
133
140
  yield (0, import_system.waitForServer)(webUrl, import_shared.SERVER_START_TIMEOUT);
134
141
  log.info(`OpenCode Web started at ${webUrl}`);
142
+ this.actualProxyPort = yield (0, import_system.findAvailablePort)(
143
+ (_a = this.config.proxyPort) != null ? _a : import_shared.DEFAULT_PROXY_PORT,
144
+ this.config.hostname
145
+ );
146
+ this.onProxyPortAllocated(this.actualProxyPort);
147
+ if (this.actualProxyPort !== ((_b = this.config.proxyPort) != null ? _b : import_shared.DEFAULT_PROXY_PORT)) {
148
+ log.info(
149
+ `Proxy port ${(_c = this.config.proxyPort) != null ? _c : import_shared.DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
150
+ );
151
+ } else {
152
+ log.debug(`Using proxy port ${this.actualProxyPort}`);
153
+ }
154
+ this.proxyServer = (0, import_proxy_server.startProxyServer)(webUrl, this.actualProxyPort, {
155
+ theme: this.config.theme,
156
+ language: this.config.language,
157
+ settings: this.config.settings
158
+ });
159
+ timer.checkpoint("Proxy server started");
135
160
  yield this.api.warmupChromeMcp(viteOrigin);
136
161
  timer.checkpoint("Chrome MCP warmup complete");
137
162
  try {
@@ -163,6 +188,11 @@ Please install OpenCode first:
163
188
  return __async(this, null, function* () {
164
189
  const timer = log.timer("stopServices");
165
190
  log.info("Stopping OpenCode services...");
191
+ if (this.proxyServer) {
192
+ log.debug("Closing proxy server");
193
+ this.proxyServer.close();
194
+ this.proxyServer = null;
195
+ }
166
196
  if (this.webProcess) {
167
197
  log.debug("Killing web process", { pid: this.webProcess.pid });
168
198
  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/lib/index.js CHANGED
@@ -84,16 +84,26 @@ function opencodePlugin(options = {}) {
84
84
  return plugins;
85
85
  }
86
86
  function createOpenCodePlugin(options = {}) {
87
+ var _a;
87
88
  const config = __spreadValues(__spreadValues({}, import_shared.DEFAULT_CONFIG), options);
88
89
  (0, import_shared.setVerbose)(config.verbose);
89
90
  const log = (0, import_shared.createLogger)("Plugin");
90
91
  let actualWebPort = config.webPort;
92
+ let actualProxyPort = (_a = config.proxyPort) != null ? _a : import_shared.DEFAULT_PROXY_PORT;
91
93
  let pageContext = { url: "", title: "" };
92
94
  const sseClients = /* @__PURE__ */ new Set();
93
95
  const api = new import_api.OpenCodeAPI(config.hostname, () => actualWebPort, config.warmupChromeMcp);
94
- const service = new import_service.OpenCodeService(config, api, sseClients, (port) => {
95
- actualWebPort = port;
96
- });
96
+ const service = new import_service.OpenCodeService(
97
+ config,
98
+ api,
99
+ sseClients,
100
+ (port) => {
101
+ actualWebPort = port;
102
+ },
103
+ (port) => {
104
+ actualProxyPort = port;
105
+ }
106
+ );
97
107
  return {
98
108
  name: "vite-plugin-opencode",
99
109
  apply(_viteConfig, env) {
@@ -102,12 +112,15 @@ function createOpenCodePlugin(options = {}) {
102
112
  },
103
113
  configureServer(server) {
104
114
  return __async(this, null, function* () {
105
- var _a2, _b;
115
+ var _a2, _b2;
106
116
  const timer = log.timer("configureServer");
107
117
  (0, import_endpoints.setupMiddlewares)(server, {
108
118
  get sessionUrl() {
109
119
  return service.sessionUrl;
110
120
  },
121
+ get webUrl() {
122
+ return actualWebPort ? `http://${config.hostname}:${actualWebPort}` : null;
123
+ },
111
124
  get sseClients() {
112
125
  return sseClients;
113
126
  },
@@ -156,7 +169,7 @@ function createOpenCodePlugin(options = {}) {
156
169
  log.error("Failed to start services", { error: e });
157
170
  }
158
171
  }));
159
- (_b = server.httpServer) == null ? void 0 : _b.on("close", () => {
172
+ (_b2 = server.httpServer) == null ? void 0 : _b2.on("close", () => {
160
173
  log.debug("HTTP server closing");
161
174
  service.stop();
162
175
  });
@@ -174,6 +187,7 @@ function createOpenCodePlugin(options = {}) {
174
187
  const timer = log.timer("transformIndexHtml");
175
188
  const widget = (0, import_injector.injectWidget)({
176
189
  webUrl: `http://${config.hostname}:${actualWebPort}`,
190
+ proxyUrl: `http://${config.hostname}:${actualProxyPort}`,
177
191
  serverUrl: `http://${config.hostname}:${actualWebPort}`,
178
192
  position: config.position,
179
193
  theme: config.theme,
package/lib/style.css CHANGED
@@ -1 +1 @@
1
- .opencode-widget{--oc-bg-main: #ffffff;--oc-bg-secondary: #f8f9fa;--oc-bg-tertiary: #f3f4f6;--oc-overlay-bg: rgba(255, 255, 255, .9);--oc-bg-inverse: #1e1e1e;--oc-text-primary: #282828;--oc-text-secondary: #4b5563;--oc-text-tertiary: #6b7280;--oc-text-placeholder: #9ca3af;--oc-text-inverse: #ffffff;--oc-border-primary: #e5e7eb;--oc-border-secondary: #d1d5db;--oc-primary: #3b82f6;--oc-primary-hover: #2563eb;--oc-primary-bg: rgba(59, 130, 246, .1);--oc-danger: #ef4444;--oc-danger-hover: #dc2626;--oc-danger-active: #b91c1c;--oc-success: #10b981;--oc-overlay: rgba(0, 0, 0, .5);--oc-tooltip-bg: #1e1e1e;--oc-dialog-overlay: rgba(0, 0, 0, .5);--oc-skeleton-bg: #e5e7eb;--oc-skeleton-gradient: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);--oc-shadow-sm: 0 2px 4px rgba(0, 0, 0, .1);--oc-shadow-md: 0 4px 12px rgba(0, 0, 0, .15);--oc-shadow-lg: 0 8px 32px rgba(0, 0, 0, .12);--oc-shadow-xl: 0 20px 60px rgba(0, 0, 0, .3);--oc-shadow-primary: 0 2px 4px rgba(59, 130, 246, .2);--oc-shadow-primary-hover: 0 4px 6px rgba(59, 130, 246, .3);--oc-shadow-danger: 0 4px 12px rgba(239, 68, 68, .3);--oc-trigger-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);--oc-trigger-bg-hover: linear-gradient(135deg, #764ba2 0%, #667eea 100%);--oc-trigger-bg-active: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);--oc-trigger-shadow: 0 4px 15px rgba(102, 126, 234, .4);--oc-trigger-shadow-hover: 0 6px 20px rgba(102, 126, 234, .6);--oc-trigger-shadow-active: 0 6px 20px rgba(240, 147, 251, .4);position:fixed;z-index:999999;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.opencode-widget.opencode-theme-dark{--oc-bg-main: #1a1a1a;--oc-bg-secondary: #1e1e1e;--oc-bg-tertiary: #282828;--oc-overlay-bg: rgba(26, 26, 26, .9);--oc-bg-inverse: #ffffff;--oc-text-primary: #f3f4f6;--oc-text-secondary: #d1d5db;--oc-text-tertiary: #9ca3af;--oc-text-placeholder: #6b7280;--oc-text-inverse: #282828;--oc-border-primary: #282828;--oc-border-secondary: #4b5563;--oc-primary: #3b82f6;--oc-primary-hover: #2563eb;--oc-primary-bg: rgba(59, 130, 246, .15);--oc-danger: #ef4444;--oc-danger-hover: #dc2626;--oc-danger-active: #b91c1c;--oc-success: #10b981;--oc-overlay: rgba(26, 26, 26, .9);--oc-tooltip-bg: #282828;--oc-dialog-overlay: rgba(0, 0, 0, .7);--oc-skeleton-bg: #151515;--oc-skeleton-gradient: linear-gradient(90deg, #282828 25%, #4b5563 50%, #282828 75%);--oc-shadow-sm: 0 2px 4px rgba(0, 0, 0, .3);--oc-shadow-md: 0 4px 12px rgba(0, 0, 0, .4);--oc-shadow-lg: 0 8px 32px rgba(0, 0, 0, .4);--oc-shadow-xl: 0 20px 60px rgba(0, 0, 0, .6);--oc-shadow-primary: 0 2px 4px rgba(59, 130, 246, .3);--oc-shadow-primary-hover: 0 4px 6px rgba(59, 130, 246, .4);--oc-shadow-danger: 0 4px 12px rgba(239, 68, 68, .4);--oc-trigger-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);--oc-trigger-bg-hover: linear-gradient(135deg, #764ba2 0%, #667eea 100%);--oc-trigger-bg-active: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);--oc-trigger-shadow: 0 4px 15px rgba(102, 126, 234, .5);--oc-trigger-shadow-hover: 0 6px 20px rgba(102, 126, 234, .7);--oc-trigger-shadow-active: 0 6px 20px rgba(240, 147, 251, .5)}.opencode-widget.bottom-right{bottom:20px;right:20px}.opencode-widget.bottom-left{bottom:20px;left:20px}.opencode-widget.top-right{top:20px;right:20px}.opencode-widget.top-left{top:20px;left:20px}.opencode-chat{position:absolute;width:700px;height:86vh;background:var(--oc-bg-main);border-radius:16px;box-shadow:var(--oc-shadow-lg);overflow:hidden;opacity:0;visibility:hidden;transform:translateY(20px) scale(.95);transition:all .3s ease;display:flex;flex-direction:column}.opencode-chat-content{display:flex;flex:1;overflow:hidden}.opencode-widget.bottom-right .opencode-chat{bottom:56px;right:0}.opencode-widget.bottom-left .opencode-chat{bottom:56px;left:0}.opencode-widget.top-right .opencode-chat{top:56px;right:0}.opencode-widget.top-left .opencode-chat{top:56px;left:0}.opencode-widget.bottom-right .opencode-selected-bubbles{bottom:56px;right:0}.opencode-widget.bottom-left .opencode-selected-bubbles{bottom:56px;left:0}.opencode-widget.top-right .opencode-selected-bubbles{top:56px;bottom:auto;right:0}.opencode-widget.top-left .opencode-selected-bubbles{top:56px;bottom:auto;left:0}.opencode-chat.open{opacity:1;visibility:visible;transform:translateY(0) scale(1)}.opencode-notification{position:absolute;top:20px;left:50%;transform:translate(-50%);padding:12px 20px;background:var(--oc-bg-main);color:var(--oc-text-primary);border:1px solid var(--oc-border-primary);border-radius:8px;font-size:14px;box-shadow:var(--oc-shadow-lg);animation:slideDown .3s ease;z-index:10000000}.opencode-dialog-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--oc-dialog-overlay);display:flex;align-items:center;justify-content:center;z-index:9999999;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.opencode-dialog{background:var(--oc-bg-main);border-radius:12px;padding:24px;min-width:320px;max-width:400px;box-shadow:var(--oc-shadow-xl);animation:scaleIn .2s ease}@keyframes scaleIn{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}.opencode-dialog-content{margin-bottom:20px}.opencode-dialog-message{font-size:15px;color:var(--oc-text-primary);line-height:1.5}.opencode-dialog-actions{display:flex;gap:12px;justify-content:flex-end}.opencode-dialog-btn{padding:10px 20px;border-radius:8px;border:none;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.opencode-dialog-btn.cancel{background:var(--oc-bg-tertiary);color:var(--oc-text-primary)}.opencode-dialog-btn.cancel:hover{background:var(--oc-text-primary);color:var(--oc-bg-main)}.opencode-dialog-btn.confirm{background:var(--oc-danger);color:#fff}.opencode-dialog-btn.confirm:hover{background:var(--oc-danger-hover)}@keyframes slideDown{0%{transform:translate(-50%) translateY(-100%);opacity:0}to{transform:translate(-50%) translateY(0);opacity:1}}.opencode-element-highlight{position:fixed;pointer-events:none;z-index:999998;display:none;transition:all .1s ease;border-radius:4px}#vue-inspector-container{display:none!important}.opencode-element-tooltip{position:fixed;background:var(--oc-tooltip-bg);color:#fff;padding:8px 12px;border-radius:6px;font-size:12px;z-index:9999998;display:none;box-shadow:var(--oc-shadow-md);max-width:300px;pointer-events:none}.opencode-tooltip-tag{font-weight:500;margin-bottom:4px;word-break:break-all}.opencode-tooltip-file{font-size:11px;color:var(--oc-text-placeholder);word-break:break-all}.opencode-element-highlight-temp{position:absolute;pointer-events:none;z-index:999998;border-radius:4px;animation:highlight-pulse 2s ease-out forwards}@keyframes highlight-pulse{0%{opacity:1;transform:scale(1)}50%{opacity:.8;transform:scale(1.02)}to{opacity:0;transform:scale(1)}}@media (max-width:768px){.opencode-chat{width:calc(100vw - 40px);height:calc(100vh - 100px)}}.opencode-iframe-container{flex:1;position:relative;overflow:hidden;display:flex;flex-direction:column;margin-top:-42px}.opencode-loading-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background:var(--oc-overlay-bg);display:none;flex-direction:column;align-items:center;justify-content:center;z-index:10;transition:opacity .3s ease}.opencode-loading-overlay.visible{display:flex}.opencode-loading-spinner{width:40px;height:40px;border:3px solid var(--oc-border-primary);border-top-color:var(--oc-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.opencode-loading-text{margin-top:12px;font-size:14px;color:var(--oc-text-placeholder)}.opencode-empty-state-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background:var(--oc-bg-secondary);display:none;flex-direction:column;align-items:center;justify-content:center;z-index:5;transition:opacity .3s ease;margin-top:42px}.opencode-empty-state-overlay.visible{display:flex}.opencode-empty-state-icon{color:var(--oc-text-placeholder);margin-bottom:16px}.opencode-empty-state-text{color:var(--oc-text-primary);font-size:16px;font-weight:500;margin-bottom:24px}.opencode-empty-state-btn{padding:10px 24px;border-radius:8px;border:none;background:var(--oc-primary);color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;box-shadow:var(--oc-shadow-primary)}.opencode-empty-state-btn:hover{background:var(--oc-primary-hover);transform:translateY(-1px);box-shadow:var(--oc-shadow-primary-hover)}.opencode-empty-state-btn:active{transform:translateY(0)}.opencode-iframe{width:100%;height:100%;border:none}.opencode-chat-header{position:relative;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:0 12px;height:40px;background:var(--oc-bg-secondary);border-bottom:1px solid var(--oc-border-primary);z-index:5}.opencode-chat-header-left{display:flex;align-items:center;gap:4px}.opencode-chat-header-title{font-size:14px;font-weight:600;color:var(--oc-text-primary);position:absolute;left:50%;transform:translate(-50%)}.opencode-chat-header-actions{display:flex;gap:4px}.opencode-header-btn{width:28px;height:28px;border-radius:6px;border:none;background:transparent;color:var(--oc-text-placeholder);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.opencode-header-btn:hover{background:var(--oc-bg-tertiary);color:var(--oc-text-primary)}.opencode-header-btn.close:hover{background:var(--oc-danger);color:#fff}.opencode-header-btn.select-btn.active{background:var(--oc-primary);color:#fff}.opencode-select-mode-hint{position:fixed;top:20px;left:50%;transform:translate(-50%);padding:10px 16px;background:var(--oc-danger);color:#fff;border-radius:8px;font-size:13px;box-shadow:var(--oc-shadow-danger);z-index:9999999;display:none;align-items:center;gap:12px}.opencode-select-mode-hint.visible{display:flex;animation:slideDown .3s ease}.opencode-hint-shortcut{padding:2px 6px;background:#fff3;border-radius:4px;font-size:12px}.opencode-selected-bubbles{position:absolute;display:none;flex-direction:column;gap:6px;max-width:220px;max-height:300px;overflow-y:auto}.opencode-selected-bubbles.visible{display:flex}.opencode-selected-bubble{display:flex;flex-direction:column;gap:2px;padding:8px 24px 8px 10px;background:var(--oc-bg-main);border:1px solid var(--oc-border-primary);border-radius:8px;font-size:12px;box-shadow:var(--oc-shadow-sm);position:relative;cursor:pointer;transition:all .2s}.opencode-selected-bubble:hover{border-color:var(--oc-primary);box-shadow:var(--oc-shadow-primary)}.opencode-bubble-text{color:var(--oc-text-primary);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-bubble-file{color:var(--oc-text-placeholder);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-bubble-remove{position:absolute;top:8px;right:6px;width:16px;height:16px;border-radius:50%;border:none;background:transparent;color:var(--oc-text-placeholder);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;transition:all .2s}.opencode-bubble-remove:hover{background:var(--oc-danger);color:#fff}.opencode-bubble-empty{padding:8px 12px;background:var(--oc-bg-main);border:1px dashed var(--oc-border-secondary);border-radius:8px;color:var(--oc-text-placeholder);font-size:12px;text-align:center}.opencode-right-toolbar{width:140px;background:var(--oc-bg-secondary);border-left:1px solid var(--oc-border-primary);display:flex;flex-direction:column;flex-shrink:0;transition:width .2s ease;overflow:hidden}.opencode-right-toolbar.collapsed{width:0;overflow:hidden}.opencode-right-toolbar.collapsed .opencode-selected-nodes-header,.opencode-right-toolbar.collapsed .opencode-selected-nodes,.opencode-right-toolbar.collapsed .opencode-clear-all-btn{display:none}.opencode-selected-nodes-header{padding:12px 8px 8px;border-bottom:1px solid var(--oc-border-primary)}.opencode-selected-nodes-title{font-size:14px;font-weight:600;color:var(--oc-text-primary);margin-bottom:4px}.opencode-selected-nodes-desc{font-size:11px;color:var(--oc-text-placeholder);line-height:1.4}.opencode-selected-nodes{flex:1;display:flex;flex-direction:column;padding:8px;gap:6px;overflow-y:auto;overflow-x:hidden}.opencode-selected-nodes:empty:before{content:"暂无选中元素";color:var(--oc-text-placeholder);font-size:12px;text-align:center;padding:20px 10px}.opencode-selected-node{display:flex;align-items:center;gap:8px;padding:8px 10px;background:var(--oc-bg-main);border:1px solid var(--oc-border-primary);border-radius:6px;font-size:12px;transition:all .2s}.opencode-selected-node:hover{border-color:var(--oc-primary);box-shadow:var(--oc-shadow-primary)}.opencode-node-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.opencode-node-text{color:var(--oc-text-primary);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-node-file{color:var(--oc-text-placeholder);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-node-remove{width:18px;height:18px;border-radius:4px;border:none;background:transparent;color:var(--oc-text-placeholder);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px;transition:all .2s;flex-shrink:0}.opencode-node-remove:hover{background:var(--oc-danger);color:#fff}.opencode-clear-all-btn{width:calc(100% - 16px);margin:8px;padding:8px 12px;border-radius:6px;border:none;background:var(--oc-danger);color:#fff;font-size:12px;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:4px;transition:all .2s}.opencode-clear-all-btn:hover{background:var(--oc-danger-hover);transform:scale(1.02)}.opencode-session-list{width:240px;background:var(--oc-bg-secondary);border-right:1px solid var(--oc-border-primary);display:flex;flex-direction:column;flex-shrink:0;transition:width .2s ease}.opencode-session-list.collapsed{width:0;overflow:hidden}.opencode-session-list.collapsed .opencode-session-list-header,.opencode-session-list.collapsed .opencode-session-list-content{display:none}.opencode-session-list-header{padding:16px;border-bottom:1px solid var(--oc-border-primary);display:flex;justify-content:space-between;align-items:center;font-weight:600;font-size:14px;color:var(--oc-text-primary)}.opencode-new-session-btn{width:28px;height:28px;border-radius:6px;border:none;background:var(--oc-primary);color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.opencode-new-session-btn:hover{background:var(--oc-primary-hover);transform:scale(1.05)}.opencode-session-list-content{flex:1;overflow-y:auto;padding:8px;position:relative}.opencode-session-list-loading-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background:var(--oc-overlay-bg);display:flex;align-items:center;justify-content:center;z-index:10;border-radius:8px}.opencode-loading-spinner.small{width:24px;height:24px;border-width:2px}.opencode-session-item{padding:12px;border-radius:8px;cursor:pointer;transition:all .2s;margin-bottom:4px;color:var(--oc-text-primary)}.opencode-session-item:hover{background:var(--oc-bg-tertiary)}.opencode-session-item.active{background:var(--oc-primary);color:#fff}.opencode-session-title{font-size:14px;font-weight:500;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-session-meta{font-size:12px;opacity:.6}.opencode-session-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.opencode-session-delete-btn{width:20px;height:20px;border-radius:4px;border:none;background:transparent;color:var(--oc-text-placeholder);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;opacity:0;flex-shrink:0}.opencode-session-item:hover .opencode-session-delete-btn{opacity:1}.opencode-session-delete-btn:hover{background:var(--oc-danger);color:#fff}.opencode-session-item.active .opencode-session-delete-btn{color:#ffffffb3}.opencode-session-item.active .opencode-session-delete-btn:hover{background:#fff3;color:#fff}.opencode-session-header-skeleton{padding:16px;border-bottom:1px solid var(--oc-border-primary);display:none;justify-content:space-between;align-items:center}.opencode-session-header-skeleton.visible{display:flex}.opencode-skeleton-header-title{height:18px;width:80px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:4px}.opencode-skeleton-header-btn{width:28px;height:28px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:6px}.opencode-session-skeleton{flex:1;overflow-y:auto;padding:8px;display:none}.opencode-session-skeleton.visible{display:block}.opencode-skeleton-item{padding:12px;border-radius:8px;margin-bottom:4px;background:var(--oc-skeleton-bg)}.opencode-skeleton-title{height:16px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:4px;margin-bottom:8px;width:70%}.opencode-skeleton-meta{height:12px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:4px;width:50%}.opencode-session-empty{padding:32px 16px;text-align:center;color:var(--oc-text-placeholder);font-size:13px}@keyframes skeleton-loading{0%{background-position:200% 0}to{background-position:-200% 0}}.opencode-button{width:44px;height:44px;border-radius:50%;background:var(--oc-trigger-bg);border:none;cursor:pointer;box-shadow:var(--oc-trigger-shadow);transition:all .3s ease;display:flex;align-items:center;justify-content:center;color:#fff;padding:0;position:relative}.opencode-button:before{content:"";position:absolute;top:-8px;left:-8px;right:-8px;bottom:-8px;border-radius:50%}.opencode-button:hover{transform:scale(1.1);box-shadow:var(--oc-trigger-shadow-hover);background:var(--oc-trigger-bg-hover)}.opencode-button.active{background:var(--oc-trigger-bg-active);box-shadow:var(--oc-trigger-shadow-active)}.opencode-button.active svg{transform:rotate(180deg)}.opencode-button svg{transition:transform .3s ease}.opencode-button.loading{animation:pulse 1s infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}
1
+ .opencode-widget{--oc-bg-main: #ffffff;--oc-bg-secondary: #f8f9fa;--oc-bg-tertiary: #f3f4f6;--oc-overlay-bg: rgba(255, 255, 255, .9);--oc-bg-inverse: #1e1e1e;--oc-text-primary: #282828;--oc-text-secondary: #4b5563;--oc-text-tertiary: #6b7280;--oc-text-placeholder: #9ca3af;--oc-text-inverse: #ffffff;--oc-border-primary: #e5e7eb;--oc-border-secondary: #d1d5db;--oc-primary: #3b82f6;--oc-primary-hover: #2563eb;--oc-primary-bg: rgba(59, 130, 246, .1);--oc-danger: #ef4444;--oc-danger-hover: #dc2626;--oc-danger-active: #b91c1c;--oc-success: #10b981;--oc-overlay: rgba(0, 0, 0, .5);--oc-tooltip-bg: #1e1e1e;--oc-dialog-overlay: rgba(0, 0, 0, .5);--oc-skeleton-bg: #e5e7eb;--oc-skeleton-gradient: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);--oc-shadow-sm: 0 2px 4px rgba(0, 0, 0, .1);--oc-shadow-md: 0 4px 12px rgba(0, 0, 0, .15);--oc-shadow-lg: 0 8px 32px rgba(0, 0, 0, .12);--oc-shadow-xl: 0 20px 60px rgba(0, 0, 0, .3);--oc-shadow-primary: 0 2px 4px rgba(59, 130, 246, .2);--oc-shadow-primary-hover: 0 4px 6px rgba(59, 130, 246, .3);--oc-shadow-danger: 0 4px 12px rgba(239, 68, 68, .3);--oc-trigger-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);--oc-trigger-bg-hover: linear-gradient(135deg, #764ba2 0%, #667eea 100%);--oc-trigger-bg-active: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);--oc-trigger-shadow: 0 4px 15px rgba(102, 126, 234, .4);--oc-trigger-shadow-hover: 0 6px 20px rgba(102, 126, 234, .6);--oc-trigger-shadow-active: 0 6px 20px rgba(240, 147, 251, .4);position:fixed;z-index:999999;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.opencode-widget.opencode-theme-dark{--oc-bg-main: #1a1a1a;--oc-bg-secondary: #1e1e1e;--oc-bg-tertiary: #282828;--oc-overlay-bg: rgba(26, 26, 26, .9);--oc-bg-inverse: #ffffff;--oc-text-primary: #f3f4f6;--oc-text-secondary: #d1d5db;--oc-text-tertiary: #9ca3af;--oc-text-placeholder: #6b7280;--oc-text-inverse: #282828;--oc-border-primary: #282828;--oc-border-secondary: #4b5563;--oc-primary: #3b82f6;--oc-primary-hover: #2563eb;--oc-primary-bg: rgba(59, 130, 246, .15);--oc-danger: #ef4444;--oc-danger-hover: #dc2626;--oc-danger-active: #b91c1c;--oc-success: #10b981;--oc-overlay: rgba(26, 26, 26, .9);--oc-tooltip-bg: #282828;--oc-dialog-overlay: rgba(0, 0, 0, .7);--oc-skeleton-bg: #151515;--oc-skeleton-gradient: linear-gradient(90deg, #282828 25%, #4b5563 50%, #282828 75%);--oc-shadow-sm: 0 2px 4px rgba(0, 0, 0, .3);--oc-shadow-md: 0 4px 12px rgba(0, 0, 0, .4);--oc-shadow-lg: 0 8px 32px rgba(0, 0, 0, .4);--oc-shadow-xl: 0 20px 60px rgba(0, 0, 0, .6);--oc-shadow-primary: 0 2px 4px rgba(59, 130, 246, .3);--oc-shadow-primary-hover: 0 4px 6px rgba(59, 130, 246, .4);--oc-shadow-danger: 0 4px 12px rgba(239, 68, 68, .4);--oc-trigger-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);--oc-trigger-bg-hover: linear-gradient(135deg, #764ba2 0%, #667eea 100%);--oc-trigger-bg-active: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);--oc-trigger-shadow: 0 4px 15px rgba(102, 126, 234, .5);--oc-trigger-shadow-hover: 0 6px 20px rgba(102, 126, 234, .7);--oc-trigger-shadow-active: 0 6px 20px rgba(240, 147, 251, .5)}.opencode-widget.bottom-right{bottom:20px;right:20px}.opencode-widget.bottom-left{bottom:20px;left:20px}.opencode-widget.top-right{top:20px;right:20px}.opencode-widget.top-left{top:20px;left:20px}.opencode-chat{position:absolute;width:700px;height:86vh;background:var(--oc-bg-main);border-radius:16px;box-shadow:var(--oc-shadow-lg);overflow:hidden;opacity:0;visibility:hidden;transform:translateY(20px) scale(.95);transition:all .3s ease;display:flex;flex-direction:column}.opencode-chat-content{display:flex;flex:1;overflow:hidden}.opencode-widget.bottom-right .opencode-chat{bottom:56px;right:0}.opencode-widget.bottom-left .opencode-chat{bottom:56px;left:0}.opencode-widget.top-right .opencode-chat{top:56px;right:0}.opencode-widget.top-left .opencode-chat{top:56px;left:0}.opencode-widget.bottom-right .opencode-selected-bubbles{bottom:56px;right:0}.opencode-widget.bottom-left .opencode-selected-bubbles{bottom:56px;left:0}.opencode-widget.top-right .opencode-selected-bubbles{top:56px;bottom:auto;right:0}.opencode-widget.top-left .opencode-selected-bubbles{top:56px;bottom:auto;left:0}.opencode-chat.open{opacity:1;visibility:visible;transform:translateY(0) scale(1)}.opencode-notification{position:absolute;top:20px;left:50%;transform:translate(-50%);padding:12px 24px;background:linear-gradient(135deg,#3b82f6,#2563eb);color:#fff;border-radius:10px;font-size:14px;font-weight:500;box-shadow:0 4px 16px #3b82f666,0 0 0 2px #3b82f633;animation:slideDown .3s ease;z-index:10000000;display:flex;align-items:center;gap:10px}.opencode-notification:before{content:"💡";font-size:16px}.opencode-dialog-overlay{position:fixed;inset:0;background:var(--oc-dialog-overlay);display:flex;align-items:center;justify-content:center;z-index:9999999;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.opencode-dialog{background:var(--oc-bg-main);border-radius:12px;padding:24px;min-width:320px;max-width:400px;box-shadow:var(--oc-shadow-xl);animation:scaleIn .2s ease}@keyframes scaleIn{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}.opencode-dialog-content{margin-bottom:20px}.opencode-dialog-message{font-size:15px;color:var(--oc-text-primary);line-height:1.5}.opencode-dialog-actions{display:flex;gap:12px;justify-content:flex-end}.opencode-dialog-btn{padding:10px 20px;border-radius:8px;border:none;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.opencode-dialog-btn.cancel{background:var(--oc-bg-tertiary);color:var(--oc-text-primary)}.opencode-dialog-btn.cancel:hover{background:var(--oc-text-primary);color:var(--oc-bg-main)}.opencode-dialog-btn.confirm{background:var(--oc-danger);color:#fff}.opencode-dialog-btn.confirm:hover{background:var(--oc-danger-hover)}@keyframes slideDown{0%{transform:translate(-50%) translateY(-100%);opacity:0}to{transform:translate(-50%) translateY(0);opacity:1}}.opencode-element-highlight{position:fixed;pointer-events:none;z-index:999998;display:none;transition:all .1s ease;border-radius:4px}#vue-inspector-container{display:none!important}.opencode-element-tooltip{position:fixed;background:var(--oc-tooltip-bg);color:#fff;padding:8px 12px;border-radius:6px;font-size:12px;z-index:9999998;display:none;box-shadow:var(--oc-shadow-md);max-width:300px;pointer-events:none}.opencode-tooltip-tag{font-weight:500;margin-bottom:4px;word-break:break-all}.opencode-tooltip-file{font-size:11px;color:var(--oc-text-placeholder);word-break:break-all}.opencode-element-highlight-temp{position:absolute;pointer-events:none;z-index:999998;border-radius:4px;animation:highlight-pulse 2s ease-out forwards}@keyframes highlight-pulse{0%{opacity:1;transform:scale(1)}50%{opacity:.8;transform:scale(1.02)}to{opacity:0;transform:scale(1)}}@media (max-width:768px){.opencode-chat{width:calc(100vw - 40px);height:calc(100vh - 100px)}}.opencode-iframe-container{flex:1;position:relative;overflow:hidden;display:flex;flex-direction:column;margin-top:-42px}.opencode-loading-overlay{position:absolute;inset:0;background:var(--oc-overlay-bg);display:none;flex-direction:column;align-items:center;justify-content:center;z-index:10;transition:opacity .3s ease}.opencode-loading-overlay.visible{display:flex}.opencode-loading-spinner{width:40px;height:40px;border:3px solid var(--oc-border-primary);border-top-color:var(--oc-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.opencode-loading-text{margin-top:12px;font-size:14px;color:var(--oc-text-placeholder)}.opencode-empty-state-overlay{position:absolute;inset:0;background:var(--oc-bg-secondary);display:none;flex-direction:column;align-items:center;justify-content:center;z-index:5;transition:opacity .3s ease;margin-top:42px}.opencode-empty-state-overlay.visible{display:flex}.opencode-empty-state-icon{color:var(--oc-text-placeholder);margin-bottom:16px}.opencode-empty-state-text{color:var(--oc-text-primary);font-size:16px;font-weight:500;margin-bottom:24px}.opencode-empty-state-btn{padding:10px 24px;border-radius:8px;border:none;background:var(--oc-primary);color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;box-shadow:var(--oc-shadow-primary)}.opencode-empty-state-btn:hover{background:var(--oc-primary-hover);transform:translateY(-1px);box-shadow:var(--oc-shadow-primary-hover)}.opencode-empty-state-btn:active{transform:translateY(0)}.opencode-iframe{width:100%;height:100%;border:none}.opencode-chat-header{position:relative;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:0 12px;height:40px;background:var(--oc-bg-secondary);border-bottom:1px solid var(--oc-border-primary);z-index:5}.opencode-chat-header-left{display:flex;align-items:center;gap:4px}.opencode-chat-header-title{font-size:14px;font-weight:600;color:var(--oc-text-primary);position:absolute;left:50%;transform:translate(-50%)}.opencode-chat-header-actions{display:flex;gap:4px}.opencode-header-btn{width:28px;height:28px;border-radius:6px;border:none;background:transparent;color:var(--oc-text-placeholder);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.opencode-header-btn:hover{background:var(--oc-bg-tertiary);color:var(--oc-text-primary)}.opencode-header-btn.close:hover{background:var(--oc-danger);color:#fff}.opencode-header-btn.select-btn.active,.opencode-header-btn.session-toggle.active{background:var(--oc-primary);color:#fff}.opencode-select-mode-hint{position:fixed;top:20px;left:50%;transform:translate(-50%);padding:14px 20px;background:linear-gradient(135deg,#ef4444,#dc2626);color:#fff;border-radius:12px;font-size:15px;font-weight:500;box-shadow:0 6px 20px #ef444480,0 0 0 3px #ef44444d;z-index:9999999;display:none;align-items:center;gap:12px;border:2px solid rgba(255,255,255,.3)}.opencode-select-mode-hint.visible{display:flex;animation:slideDown .3s ease,pulseHint 2s ease-in-out infinite}.opencode-hint-shortcut{padding:4px 10px;background:#ffffff40;border-radius:6px;font-size:13px;font-weight:600;border:1px solid rgba(255,255,255,.4)}@keyframes pulseHint{0%,to{box-shadow:0 6px 20px #ef444480,0 0 0 3px #ef44444d}50%{box-shadow:0 6px 20px #ef444499,0 0 0 6px #ef444466}}.opencode-selected-bubbles{position:absolute;display:none;flex-direction:column;gap:6px;max-width:220px;max-height:300px;overflow-y:auto}.opencode-selected-bubbles.visible{display:flex}.opencode-selected-bubble{display:flex;flex-direction:column;gap:2px;padding:8px 24px 8px 10px;background:var(--oc-bg-main);border:1px solid var(--oc-border-primary);border-radius:8px;font-size:12px;box-shadow:var(--oc-shadow-sm);position:relative;cursor:pointer;transition:all .2s}.opencode-selected-bubble:hover{border-color:var(--oc-primary);box-shadow:var(--oc-shadow-primary)}.opencode-bubble-text{color:var(--oc-text-primary);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-bubble-file{color:var(--oc-text-placeholder);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-bubble-remove{position:absolute;top:8px;right:6px;width:16px;height:16px;border-radius:50%;border:none;background:transparent;color:var(--oc-text-placeholder);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;transition:all .2s}.opencode-bubble-remove:hover{background:var(--oc-danger);color:#fff}.opencode-bubble-empty{padding:8px 12px;background:var(--oc-bg-main);border:1px dashed var(--oc-border-secondary);border-radius:8px;color:var(--oc-text-placeholder);font-size:12px;text-align:center}.opencode-right-toolbar{width:140px;background:var(--oc-bg-secondary);border-left:1px solid var(--oc-border-primary);display:flex;flex-direction:column;flex-shrink:0;transition:width .2s ease;overflow:hidden}.opencode-right-toolbar.collapsed{width:0;overflow:hidden}.opencode-right-toolbar.collapsed .opencode-selected-nodes-header,.opencode-right-toolbar.collapsed .opencode-selected-nodes,.opencode-right-toolbar.collapsed .opencode-clear-all-btn{display:none}.opencode-selected-nodes-header{padding:12px 8px 8px;border-bottom:1px solid var(--oc-border-primary)}.opencode-selected-nodes-title{font-size:14px;font-weight:600;color:var(--oc-text-primary);margin-bottom:4px}.opencode-selected-nodes-desc{font-size:11px;color:var(--oc-text-placeholder);line-height:1.4}.opencode-selected-nodes{flex:1;display:flex;flex-direction:column;padding:8px;gap:6px;overflow-y:auto;overflow-x:hidden}.opencode-selected-nodes:empty:before{content:"暂无选中元素";color:var(--oc-text-placeholder);font-size:12px;text-align:center;padding:20px 10px}.opencode-selected-node{display:flex;align-items:center;gap:8px;padding:8px 10px;background:var(--oc-bg-main);border:1px solid var(--oc-border-primary);border-radius:6px;font-size:12px;transition:all .2s}.opencode-selected-node:hover{border-color:var(--oc-primary);box-shadow:var(--oc-shadow-primary)}.opencode-node-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.opencode-node-text{color:var(--oc-text-primary);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-node-file{color:var(--oc-text-placeholder);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-node-remove{width:18px;height:18px;border-radius:4px;border:none;background:transparent;color:var(--oc-text-placeholder);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px;transition:all .2s;flex-shrink:0}.opencode-node-remove:hover{background:var(--oc-danger);color:#fff}.opencode-clear-all-btn{width:calc(100% - 16px);margin:8px;padding:8px 12px;border-radius:6px;border:none;background:var(--oc-danger);color:#fff;font-size:12px;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:4px;transition:all .2s}.opencode-clear-all-btn:hover{background:var(--oc-danger-hover);transform:scale(1.02)}.opencode-session-list{width:240px;background:var(--oc-bg-secondary);border-right:1px solid var(--oc-border-primary);display:flex;flex-direction:column;flex-shrink:0;transition:width .2s ease}.opencode-session-list.collapsed{width:0;overflow:hidden}.opencode-session-list.collapsed .opencode-session-list-header,.opencode-session-list.collapsed .opencode-session-list-content{display:none}.opencode-session-list-header{padding:16px;border-bottom:1px solid var(--oc-border-primary);display:flex;justify-content:space-between;align-items:center;font-weight:600;font-size:14px;color:var(--oc-text-primary)}.opencode-new-session-btn{width:28px;height:28px;border-radius:6px;border:none;background:var(--oc-primary);color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.opencode-new-session-btn:hover{background:var(--oc-primary-hover);transform:scale(1.05)}.opencode-session-list-content{flex:1;overflow-y:auto;padding:8px;position:relative}.opencode-session-list-loading-overlay{position:absolute;inset:0;background:var(--oc-overlay-bg);display:flex;align-items:center;justify-content:center;z-index:10;border-radius:8px}.opencode-loading-spinner.small{width:24px;height:24px;border-width:2px}.opencode-session-item{padding:12px;border-radius:8px;cursor:pointer;transition:transform .2s;margin-bottom:4px;color:var(--oc-text-primary)}.opencode-session-item:hover{background:var(--oc-bg-tertiary)}.opencode-session-item.active{background:var(--oc-primary);color:#fff;transition:none}.opencode-session-title{font-size:14px;font-weight:500;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opencode-session-meta{font-size:12px;opacity:.6}.opencode-session-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.opencode-session-delete-btn{width:20px;height:20px;border-radius:4px;border:none;background:transparent;color:var(--oc-text-placeholder);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;opacity:0;flex-shrink:0}.opencode-session-item:hover .opencode-session-delete-btn{opacity:1}.opencode-session-delete-btn:hover{background:var(--oc-danger);color:#fff}.opencode-session-item.active .opencode-session-delete-btn{color:#ffffffb3}.opencode-session-item.active .opencode-session-delete-btn:hover{background:#fff3;color:#fff}.opencode-session-header-skeleton{padding:16px;border-bottom:1px solid var(--oc-border-primary);display:none;justify-content:space-between;align-items:center}.opencode-session-header-skeleton.visible{display:flex}.opencode-skeleton-header-title{height:18px;width:80px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:4px}.opencode-skeleton-header-btn{width:28px;height:28px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:6px}.opencode-session-skeleton{flex:1;overflow-y:auto;padding:8px;display:none}.opencode-session-skeleton.visible{display:block}.opencode-skeleton-item{padding:12px;border-radius:8px;margin-bottom:4px;background:var(--oc-skeleton-bg)}.opencode-skeleton-title{height:16px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:4px;margin-bottom:8px;width:70%}.opencode-skeleton-meta{height:12px;background:var(--oc-skeleton-gradient);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:4px;width:50%}.opencode-session-empty{padding:32px 16px;text-align:center;color:var(--oc-text-placeholder);font-size:13px}@keyframes skeleton-loading{0%{background-position:200% 0}to{background-position:-200% 0}}.opencode-button{width:44px;height:44px;border-radius:50%;background:var(--oc-trigger-bg);border:none;cursor:pointer;box-shadow:var(--oc-trigger-shadow);transition:all .3s ease;display:flex;align-items:center;justify-content:center;color:#fff;padding:0;position:relative}.opencode-button:before{content:"";position:absolute;inset:-8px;border-radius:50%}.opencode-button:hover{transform:scale(1.1);box-shadow:var(--oc-trigger-shadow-hover);background:var(--oc-trigger-bg-hover)}.opencode-button.active{background:var(--oc-trigger-bg-active);box-shadow:var(--oc-trigger-shadow-active)}.opencode-button.active svg{transform:rotate(180deg)}.opencode-button svg{transition:transform .3s ease}.opencode-button.loading{animation:pulse 1s infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-opencode-assistant",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
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.js",
@@ -36,9 +36,9 @@
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
38
  "unplugin-vue-inspector": "^2.4.0",
39
- "@vite-plugin-opencode-assistant/components": "1.0.11",
40
- "@vite-plugin-opencode-assistant/opencode": "1.0.11",
41
- "@vite-plugin-opencode-assistant/shared": "1.0.11"
39
+ "@vite-plugin-opencode-assistant/opencode": "1.0.13",
40
+ "@vite-plugin-opencode-assistant/shared": "1.0.13",
41
+ "@vite-plugin-opencode-assistant/components": "1.0.13"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "vite": ">=4.0.0"