vite-plugin-opencode-assistant 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # vite-plugin-opencode-assistant
2
+
3
+ 在 Vite 开发环境中嵌入 OpenCode AI 助手,实现边聊天边改代码、即时预览的开发体验。
4
+
5
+ ## 它能做什么
6
+
7
+ - **悬浮 AI 面板** - 在页面右下角注入悬浮按钮,点击展开 OpenCode 对话界面
8
+ - **自动启动服务** - 自动启动本地 OpenCode Web 服务,无需手动操作
9
+ - **智能会话管理** - 自动复用当前项目的会话,或创建新会话
10
+ - **页面上下文同步** - 自动同步当前页面 URL、标题给 AI,SPA 路由切换时实时更新
11
+ - **元素选择器** - 通过快捷键在页面上点选元素,将组件源码位置信息传给 AI
12
+ - **主题同步** - 挂件主题与 OpenCode Web 主题实时同步
13
+ - **代理服务** - 内置代理服务器解决 iframe 跨域限制,确保 OpenCode Web 功能完整
14
+ - **Chrome DevTools 预热** - 启动时自动预热浏览器工具链,减少首次使用等待
15
+
16
+ ## 效果演示
17
+
18
+ 启动 Vite 开发服务器后:
19
+
20
+ 1. 页面右下角出现悬浮按钮
21
+ 2. 点击按钮展开 OpenCode 对话面板
22
+ 3. 直接在面板中与 AI 对话,修改代码
23
+ 4. Vite HMR 即时刷新,立即看到修改效果
24
+
25
+ ## 安装
26
+
27
+ ```bash
28
+ npm install -D vite-plugin-opencode-assistant
29
+ ```
30
+
31
+ ## 前置条件
32
+
33
+ 需要本机已安装 [OpenCode](https://opencode.ai) CLI:
34
+
35
+ ```bash
36
+ curl -fsSL https://opencode.ai/install | bash
37
+ ```
38
+
39
+ 或使用包管理器:
40
+
41
+ ```bash
42
+ npm i -g opencode-ai@latest
43
+ brew install anomalyco/tap/opencode
44
+ ```
45
+
46
+ 验证安装:
47
+
48
+ ```bash
49
+ opencode --version
50
+ ```
51
+
52
+ ## 快速开始
53
+
54
+ ### 最小配置
55
+
56
+ ```ts
57
+ import { defineConfig } from "vite";
58
+ import opencodeAssistant from "vite-plugin-opencode-assistant";
59
+
60
+ export default defineConfig({
61
+ plugins: [opencodeAssistant()],
62
+ });
63
+ ```
64
+
65
+ ### 完整配置示例
66
+
67
+ ```ts
68
+ import { defineConfig } from "vite";
69
+ import vue from "@vitejs/plugin-vue";
70
+ import opencodeAssistant from "vite-plugin-opencode-assistant";
71
+
72
+ export default defineConfig({
73
+ plugins: [
74
+ vue(),
75
+ opencodeAssistant({
76
+ enabled: true, // 是否启用插件
77
+ webPort: 5097, // OpenCode Web 服务端口
78
+ proxyPort: 6097, // 代理服务端口
79
+ hostname: "127.0.0.1", // 服务绑定地址
80
+ position: "bottom-right", // 悬浮按钮位置
81
+ theme: "auto", // 主题: light | dark | auto
82
+ open: false, // 是否自动展开面板
83
+ autoReload: true, // 是否启用自动重载提示
84
+ verbose: false, // 是否输出详细日志
85
+ hotkey: "ctrl+k", // 切换面板的快捷键
86
+ warmupChromeMcp: true, // 是否预热 Chrome DevTools MCP
87
+
88
+ // OpenCode 界面语言(可选)
89
+ language: "zh",
90
+
91
+ // OpenCode 内部设置(可选)
92
+ settings: {
93
+ general: {
94
+ showReasoningSummaries: true,
95
+ followup: "suggest",
96
+ },
97
+ appearance: {
98
+ fontSize: 14,
99
+ },
100
+ },
101
+ }),
102
+ ],
103
+ });
104
+ ```
105
+
106
+ 启动开发服务器:
107
+
108
+ ```bash
109
+ npm run dev
110
+ ```
111
+
112
+ ## 配置项
113
+
114
+ | 配置项 | 类型 | 默认值 | 说明 |
115
+ | ----------------- | --------- | ---------------- | -------------------------------------------------------------------------- |
116
+ | `enabled` | `boolean` | `true` | 是否启用插件 |
117
+ | `webPort` | `number` | `5097` | OpenCode Web 服务端口,被占用时自动向后寻找可用端口 |
118
+ | `proxyPort` | `number` | `6097` | 代理服务端口,用于解决 iframe 跨域限制 |
119
+ | `hostname` | `string` | `"127.0.0.1"` | 服务绑定地址 |
120
+ | `position` | `string` | `"bottom-right"` | 悬浮按钮位置:`bottom-right` \| `bottom-left` \| `top-right` \| `top-left` |
121
+ | `theme` | `string` | `"auto"` | 挂件主题:`light` \| `dark` \| `auto`(跟随系统) |
122
+ | `open` | `boolean` | `false` | 页面加载后是否自动展开面板 |
123
+ | `autoReload` | `boolean` | `true` | 是否显示自动重载提示 |
124
+ | `verbose` | `boolean` | `false` | 是否输出详细调试日志 |
125
+ | `hotkey` | `string` | `"ctrl+k"` | 切换面板的快捷键,macOS 支持 `cmd+k` |
126
+ | `warmupChromeMcp` | `boolean` | `true` | 启动后是否预热 Chrome DevTools MCP |
127
+ | `language` | `string` | - | OpenCode 界面语言,如 `zh`、`en`、`ja` 等 |
128
+ | `settings` | `object` | - | OpenCode 内部设置,详见下方说明 |
129
+
130
+ ### OpenCode 设置
131
+
132
+ `settings` 配置项用于自定义 OpenCode Web 的内部行为:
133
+
134
+ ```ts
135
+ {
136
+ general: {
137
+ autoSave?: boolean; // 自动保存
138
+ releaseNotes?: boolean; // 显示更新说明
139
+ followup?: "steer" | "suggest" | "none"; // 后续动作模式
140
+ showReasoningSummaries?: boolean; // 显示推理摘要
141
+ shellToolPartsExpanded?: boolean; // 默认展开 shell 工具
142
+ editToolPartsExpanded?: boolean; // 默认展开编辑工具
143
+ },
144
+ appearance: {
145
+ fontSize?: number; // 界面字体大小
146
+ mono?: string; // 代码字体
147
+ sans?: string; // 界面字体
148
+ },
149
+ permissions: {
150
+ autoApprove?: boolean; // 自动批准权限请求
151
+ },
152
+ notifications: {
153
+ agent?: boolean; // 智能体完成时通知
154
+ permissions?: boolean; // 权限请求时通知
155
+ errors?: boolean; // 错误时通知
156
+ },
157
+ sounds: {
158
+ agentEnabled?: boolean; // 启用智能体音效
159
+ agent?: string; // 智能体音效
160
+ permissionsEnabled?: boolean; // 启用权限音效
161
+ permissions?: string; // 权限音效
162
+ errorsEnabled?: boolean; // 启用错误音效
163
+ errors?: string; // 错误音效
164
+ },
165
+ }
166
+ ```
167
+
168
+ ## 使用指南
169
+
170
+ ### 打开对话面板
171
+
172
+ - 点击页面右下角悬浮按钮
173
+ - 或使用快捷键 `Ctrl/Cmd + K`
174
+
175
+ ### 会话管理
176
+
177
+ 面板左侧显示当前项目的会话列表,支持:
178
+
179
+ - 查看已有会话
180
+ - 新建会话
181
+ - 切换会话
182
+ - 删除会话
183
+
184
+ ### 页面上下文同步
185
+
186
+ 挂件自动同步以下信息:
187
+
188
+ - 当前页面 URL
189
+ - 当前页面标题
190
+ - 选中的页面元素
191
+
192
+ 监听的事件包括:
193
+
194
+ - `history.pushState` / `history.replaceState`
195
+ - `popstate` / `hashchange`
196
+ - `document.title` 变化
197
+
198
+ ### 元素选择模式
199
+
200
+ 通过快捷键(默认 `Ctrl/Cmd + P`)进入选择模式:
201
+
202
+ 1. 按下快捷键,鼠标变为选择状态
203
+ 2. 点击页面上的元素
204
+ 3. 元素信息(文件路径、行号、文本内容)自动传给 AI
205
+
206
+ **注意**:元素选择依赖 Vue Inspector,如果页面中没有可用的 Inspector,会提示无法使用该功能。
207
+
208
+ ## 浏览器端 API
209
+
210
+ 挂件在全局暴露 `window.OpenCodeWidget` 对象:
211
+
212
+ ```js
213
+ // 打开面板
214
+ window.OpenCodeWidget.open();
215
+
216
+ // 关闭面板
217
+ window.OpenCodeWidget.close();
218
+
219
+ // 切换面板
220
+ window.OpenCodeWidget.toggle();
221
+
222
+ // 显示通知
223
+ window.OpenCodeWidget.showNotification("代码已更新!");
224
+
225
+ // 手动同步上下文
226
+ window.OpenCodeWidget.updateContext();
227
+ ```
228
+
229
+ ## 内部接口
230
+
231
+ 插件在 Vite 开发服务器上注册以下内部接口:
232
+
233
+ | 路径 | 说明 |
234
+ | ------------------------- | ---------------------------------- |
235
+ | `/__opencode_widget__.js` | 浏览器端挂件脚本 |
236
+ | `/__opencode_start__` | 服务启动状态与会话地址 |
237
+ | `/__opencode_context__` | 页面上下文读写 |
238
+ | `/__opencode_sessions__` | 会话查询、创建、删除 |
239
+ | `/__opencode_events__` | SSE 事件流(会话就绪、节点清空等) |
240
+
241
+ ## 常见问题
242
+
243
+ ### OpenCode not installed
244
+
245
+ 确认命令行可以正常执行:
246
+
247
+ ```bash
248
+ opencode --version
249
+ ```
250
+
251
+ ### 端口冲突
252
+
253
+ 如果默认端口被占用,插件会自动向后寻找可用端口。也可以手动指定:
254
+
255
+ ```ts
256
+ opencodeAssistant({
257
+ webPort: 5001,
258
+ proxyPort: 5002,
259
+ });
260
+ ```
261
+
262
+ ### 元素选择不可用
263
+
264
+ 需要页面中有 Vue Inspector 钩子。插件已内置 `unplugin-vue-inspector`,无需额外配置。
265
+
266
+ ### 生产构建不包含挂件
267
+
268
+ 这是预期行为。插件仅在开发模式(`vite serve`)下工作,不会影响生产构建。
269
+
270
+ ## License
271
+
272
+ MIT
273
+
274
+ ## 相关链接
275
+
276
+ - [OpenCode](https://opencode.ai)
277
+ - [OpenCode GitHub](https://github.com/opencode-ai/opencode)
278
+ - [Vite Plugin API](https://vite.dev/guide/api-plugin.html)
@@ -106,8 +106,38 @@ function generateBridgeScript(options) {
106
106
  if (event.data && event.data.type === "OPENCODE_INSERT_FILE_PART") {
107
107
  insertFilePart(event.data.element);
108
108
  }
109
+
110
+ if (event.data && event.data.type === "minimize-state-change") {
111
+ handleMinimizeStateChange(event.data.minimized);
112
+ }
113
+
114
+ if (event.data && event.data.type === "prompt-dock-visibility-change") {
115
+ handlePromptDockVisibilityChange(event.data.visible);
116
+ }
109
117
  });
110
118
 
119
+ // === \u6700\u5C0F\u5316\u72B6\u6001\u5904\u7406 ===
120
+ function handleMinimizeStateChange(minimized) {
121
+ const dockSurface = document.querySelector('[data-dock-surface="tray"]');
122
+ const sessionTurnList = document.querySelector('[data-slot="session-turn-list"]');
123
+
124
+ if (dockSurface) {
125
+ dockSurface.style.display = minimized ? 'none' : '';
126
+ }
127
+
128
+ if (sessionTurnList) {
129
+ sessionTurnList.style.paddingBottom = minimized ? '10px' : '';
130
+ }
131
+ }
132
+
133
+ // === \u5BF9\u8BDD\u6846\u663E\u793A\u72B6\u6001\u5904\u7406 ===
134
+ function handlePromptDockVisibilityChange(visible) {
135
+ const promptDock = document.querySelector('[data-component="session-prompt-dock"]');
136
+ if (promptDock) {
137
+ promptDock.style.display = visible ? '' : 'none';
138
+ }
139
+ }
140
+
111
141
  // === \u4FDD\u5B58\u8F93\u5165\u6846\u5149\u6807\u4F4D\u7F6E ===
112
142
  let savedRange = null;
113
143