ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

JetBrains 插件空状态页 Feedback Support 按钮实现:从 VS Code 对齐到 IntelliJ Popup 落地

2026/9/11 22:49:02 拓冰建站 浏览量
JetBrains 插件空状态页 Feedback  Support 按钮实现:从 VS Code 对齐到 IntelliJ Popup 落地 JetBrains 插件空状态页 Feedback Support 按钮实现从 VS Code 对齐到 IntelliJ Popup 落地【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode导读本文基于 Kilo 仓库内.kilo/plans/1780256109130-stellar-forest.md这一份 JetBrains 插件功能实现计划展开完整讲解如何把 VS Code 端空状态页empty state的Feedback Support按钮与反馈弹层移植到 JetBrains 插件实现 GitHub Issues、Discord、Customer Support 三个跳转入口并满足点击外部 / 按 Esc 关闭、非模态、不抢焦点等交互要求。文章不仅逐条展开计划的 6 个实施步骤本地化、UI 扩展、Popup 构建、测试、发布说明、验证还对照当前仓库中已落地的源码EmptySessionFeedback.kt、EmptySessionPanel.kt、EmptySessionPanelTest.kt及 i18n 资源给出计划与最终实现之间的差异读者可据此在 JetBrains 插件前端复现或继续改进同类功能。一、功能目标与整体背景该计划的目标非常明确把 VS Code 端空状态页上的Feedback Support入口原样搬到 JetBrains 插件中弹出一个**非模态non-modal**小弹层提供三个与 VS Code 完全一致的目的地GitHub Issueshttps://github.com/Kilo-Org/kilocode/issues/new/chooseDiscordhttps://kilo.ai/discordCustomer Supporthttps://kilo.ai/support弹层必须满足两个交互约束点击弹层外部时隐藏按下 Escape 时隐藏。从仓库现状看该计划已经落地packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/empty/EmptySessionFeedback.kt完整实现了按钮、弹层内容与三个 URL 常量且feedback.urls()返回的正是上述三个 URL对应测试 EmptySessionPanelTest.kt 中也有test feedback popup content opens expected destinations与test feedback discord action has icon两个用例守护行为。二、调研发现入口定位与 VS Code 参考实现计划的第一步是定位落点与参考实现仓库现状印证了全部结论JetBrains 空状态组件位于 EmptySessionPanel.kt计划书写时为.../session/ui/EmptySessionPanel.kt落地时位于ui/empty/子包。它已经渲染了 Kilo logo、欢迎文案、最近会话列表Recents和Show History按钮是添加 Feedback 按钮的正确位置。从源码可见该面板是一个BorderLayoutPanel南侧SOUTH区域由Stack.vertical垂直堆叠Centerizer(historyButton)与Centerizer(feedback.button)构成Feedback 按钮正好排在Show History之下、保持居中布局。VS Code 侧三件套为参考基准按钮packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx计划标注位置弹层内容FeedbackDialog.tsx——SolidJS 组件内含 Kilo Logo、feedback.dialog.message文案以及 GitHubprimary 变体 github 图标、Discordsecondary discord 图标、Supportsecondary help 图标三个全宽按钮外加一个 ghost 变体的 Cancel 按钮点击任一动作通过vscode.postMessage({ type: openExternal, url })打开外部链接并关闭弹层。样式packages/kilo-vscode/webview-ui/src/styles/welcome.cssIntelliJ Popup API已在本地 IntelliJ 源码计划注明$INTELLIJ_REPO中确认可用JBPopupFactory.getInstance().createComponentPopupBuilder(content, focusComponent)setModalContext(false)非模态上下文setRequestFocus(false)/setFocusable(false)让弹层不抢焦点setCancelOnClickOutside(true)点击外部关闭setCancelKeyEnabled(true)Esc 关闭setCancelOnWindowDeactivation(true)/setCancelOnOtherWindowOpen(true)窗口失活或其它窗口打开时清理弹层需要特别说明的是最终实现采用了createBalloonBuilder而非计划中的createComponentPopupBuilder在 EmptySessionFeedback.kt 中弹层由JBPopupFactory.getInstance().createBalloonBuilder(content)构建并通过.setHideOnClickOutside(true)、.setHideOnKeyOutside(true)、.setHideOnAction(true)、.setHideOnFrameResize(true)实现点击外部、键盘Esc、动作触发与窗口缩放时的隐藏——与计划的交互目标一致但 API 形态不同这是实现相对计划的一处实际偏差。平台图标AllIcons可用性Feedback 按钮AllIcons.Ide.FeedbackGitHub 动作AllIcons.Vcs.Vendors.GithubSupport/帮助动作AllIcons.Actions.Help或AllIcons.General.ContextHelpDiscord 没有平台图标需要借用 VS Code / kilo-ui 现有的 Discord SVG 素材改造成 IntelliJ 兼容的 SVG 颜色必要时提供深色变体。仓库中已落地两个资源文件discord.svg 与 discord_dark.svg并通过IconLoader.getIcon(/icons/discord.svg, EmptySessionPanel::class.java)加载。三、实施步骤详解计划原文 源码对照步骤 1添加本地化 Bundle Keys计划要求在frontend/src/main/resources/messages/KiloBundle.properties添加基英文键Key值feedback.buttonFeedback and Supportfeedback.dialog.messageWed love to hear your feedback or help with any issues youre experiencing.feedback.dialog.githubReport an issue on GitHubfeedback.dialog.discordJoin our Discord communityfeedback.dialog.supportCustomer Support现有 Cancel 文案若已存在则复用否则仅在必要时新增common.cancelCancel。仓库现状确认这些键已写入 KiloBundle.properties第 54–58 行且消息目录下已存在 18 个本地化变体文件KiloBundle_ar.properties、KiloBundle_zh_CN.properties、KiloBundle_ja.properties、KiloBundle_ko.properties、KiloBundle_de.properties、KiloBundle_fr.properties等计划要求在能力允许范围内复用 VS Code i18n 的既有翻译并以英文回退兜底。实际按钮文案在实现中通过KiloBundle.message(feedback.button)读取测试test feedback button uses localized text and icon也据此断言。步骤 2扩展 EmptySessionPanel UI计划对新增按钮提出了明确的外观规格要求与 VS Code 概念对齐反馈图标 Feedback Support文本虚线、链接色边框透明背景手型光标悬停时填充链接色并反转前景/背景以形成对比样式必须使用 IntelliJ 主题 API 而非硬编码颜色链接色用JBUI.CurrentTheme.Link.Foreground.ENABLED、悬停可用...HOVERED背景取自既有会话样式或UiStyle.Colors.editorBackground()间距用UiStyle.Gap/JBUI.Borders圆角在绘制时用UiStyle.Arc.component()或JBUI.getInt(Button.arc, 6)。按钮需放在Show History下方、保持居中布局Swing 变更保持在 EDT 上执行并维持 retained-component 行为。落地实现中EmptySessionPanel.kt 的做法是让FeedbackButton直接继承面板内部的ShowHistoryButton一个自定义JButton子类因此天然继承了其既有行为isFocusable false、isContentAreaFilled false、isBorderPainted false、isOpaque false、手型光标以及paintComponent中基于JBUI.CurrentTheme.ActionButton.hoverBackground()与JBUI.getInt(Button.arc, 6)的圆角悬停填充。feedback.button在构造时挂上addActionListener { popup() }并作为Centerizer包裹项加入南侧 actions 栈见上文源码第 153–159 行。步骤 3实现 Feedback Popup前端 Swing 代码计划给出的弹层内容结构与 VS Code 弹层逐一对应顶部 Kilo logo使用既有/icons/kilo-content.svg消息文案GitHub、Discord、Customer Support 三个动作按钮/行且每个动作都必须有图标GitHub →AllIcons.Vcs.Vendors.GithubDiscord → 借用的 Discord SVGCustomer Support →AllIcons.Actions.Help或AllIcons.General.ContextHelp可选 Cancel 按钮Esc / 点击外部已能关闭弹层构建计划版配置项如下均来自 IntelliJ Popup APIJBPopupFactory.createComponentPopupBuilder(content, null) .setModalContext(false) // 非模态 .setRequestFocus(false) // 不请求焦点 .setFocusable(false) // 不可聚焦除非需要 Tab 遍历 .setCancelOnClickOutside(true) // 点击外部关闭 .setCancelKeyEnabled(true) // Esc 关闭 .setCancelOnWindowDeactivation(true) // 窗口失活清理 .setCancelOnOtherWindowOpen(true) // 其他窗口打开清理 .setResizable(false) .setMovable(false) .showUnderneathOf(feedbackButton) // 展示在按钮下方打开链接统一使用BrowserUtil.browse(url)并在打开 URL 后关闭弹层。Discord 图标资源按插件既有命名模式放入frontend/src/main/resources/icons/如discord.svg与discord_dark.svg用IconLoader.getIcon(/icons/discord.svg, EmptySessionPanel::class.java)或小型 Kilo 自有图标对象加载。源码现状对照EmptySessionFeedback.kt 的content(open)静态方法完整实现了上述结构顶部 logo点击打开https://kilocode.ai且为手型光标、居中消息、三个ActionButton竖排堆叠GitHub 用AllIcons.Vcs.Vendors.Github、Discord 用DISCORD_ICON、Support 用AllIcons.Actions.Help。popup()方法采用createBalloonBuilder变体实现见第二节的差异说明调用链为点击按钮 →popup()→ 若已有弹层则先hide()→ 构建 content →popup.show(point, Balloon.Position.below)展示在按钮正下方点击动作行时执行注入的browse(url)回调生产环境为BrowserUtil::browse测试环境可注入假实现并hide()。弹层通过Disposer与面板生命周期绑定dispose()时隐藏弹层避免泄漏。步骤 4测试覆盖EmptySessionPanelTest.kt计划要求的测试点在 EmptySessionPanelTest.kt 中均有对应用例按钮文案使用 i18ntest feedback button uses localized text and icon断言panel.feedbackText() KiloBundle.message(feedback.button)且图标非空。手型光标与边框语义test action controls use hand cursor and no show history outline断言feedbackCursor() Cursor.HAND_CURSOR且feedbackBorderPainted()为 false。可注入浏览回调面板构造参数browse: (String) - Unit BrowserUtil::browseEmptySessionPanel.kt 第 62 行即为测试预留的注入点feedbackContent { opened.add(it) }把 URL 收集进内存列表测试因此无需真正启动浏览器。三个动作标签与 URL 映射test feedback popup content opens expected destinations通过UIUtil.uiTraverser遍历弹层内容中的所有JButton断言三个按钮文案依次为feedback.dialog.github / feedback.dialog.discord / feedback.dialog.support的本地化值逐个doClick()后断言收集到的 URL 与panel.feedbackUrls()完全一致即与 VS Code 相同的三个 URL。Discord 必须有图标test feedback discord action has icon定位文案为 Discord 的按钮并断言icon ! null防止回归成纯文本行。最小模式minimaltest minimal mode shows only logo and feedback断言在 worktree/subagent 编辑器标签等最小化表面仅保留 logo 与 feedback隐藏历史、描述与最近会话test full mode shows logo feedback history description and recents则验证完整模式。测试基类为BasePlatformTestCase通过FakeAppRpcApi、FakeSessionRpcApi、FakeWorkspaceRpcApi构造最小可运行环境flush()用runBlocking { delay(100); UIUtil.dispatchAllInvocationEvents() }派发 EDT 事件。步骤 5发布说明Release Note由于这是 JetBrains 插件的用户可见功能计划要求新增 patch changeset放在.changeset/除非仓库存在 JetBrains 专属的发布说明机制取代 changesets建议文案Add Feedback Support to the JetBrains empty session screen.步骤 6验证命令计划给出的验证流程均在packages/kilo-jetbrains/目录下执行# 1) 聚焦前端/JetBrains 测试 ./gradlew :frontend:test --tests ai.kilocode.client.session.ui.EmptySessionPanelTest # 2) JetBrains 类型检查 bun run typecheck # 或 ./gradlew typecheck若仅改动前端 UI 且测试通过则无需后端/SDK 代码生成。测试全名中的类已按落地后的包路径ai.kilocode.client.session.ui.EmptySessionPanelTest核对无误。四、约束与边界Notes计划明确划定了实现边界防止范围蔓延不得引入 Kotlin UI DSL、Compose 或 JCEF弹层必须用 Swing IntelliJ Platform API 完成保持与既有前端技术栈一致。不得修改 shared opencode 文件本功能全部工作限制在packages/kilo-jetbrains/内外加一个 changeset。这与仓库的架构隔离规则相符。优先使用平台图标但 Discord 必须显式借用/新增图标资源因为AllIcons不提供 Discord 图标而弹层需要在视觉上与 VS Code 的按钮集保持一致。五、实现现状小结对照仓库当前状态这份计划已完整落地且实现细节与计划存在两处值得关注的差异均为从源码结构观察到的结论Popup API 形态不同计划推演的是createComponentPopupBuilder系列 API最终实现改用JBPopupFactory.createBalloonBuilder借助 Balloon 的setHideOnClickOutside(true)、setHideOnKeyOutside(true)、setHideOnAction(true)、setHideOnFrameResize(true)达成同样的“点击外部/Esc 关闭、动作后关闭”语义并通过Disposer.register(popup) { balloon null }维护弹层引用生命周期。路径与包结构变化计划中的EmptySessionPanel.kt实际落在.../session/ui/empty/子包测试类名与./gradlew过滤参数相应更新。对于希望复刻或扩展该功能的开发者可沿这条阅读路径深入EmptySessionPanel.kt按钮挂载与 retained-component 行为→ EmptySessionFeedback.kt弹层内容与 Balloon 构建→ EmptySessionPanelTest.kt行为契约与 URL 映射→ KiloBundle.properties文案键→ discord.svg借用的图标资源并可与 VS Code 侧的 FeedbackDialog.tsx 对照理解两端交互与视觉的一致性目标。【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考