Release v1.0.0: in-panel settings (gear icon), default URL 127.0.0.1:8899
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "com.prepeep"
|
group = "com.prepeep"
|
||||||
version = "1.0.0-SNAPSHOT"
|
version = "1.0.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
package com.harmness.toolwindow
|
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.ui.components.JBLabel
|
|
||||||
import com.intellij.ui.components.JBTextField
|
|
||||||
import com.intellij.util.ui.FormBuilder
|
|
||||||
import com.intellij.util.ui.UIUtil
|
|
||||||
import javax.swing.JComponent
|
|
||||||
import javax.swing.JPanel
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置面板 UI:编辑「起始地址」并即时生效。
|
|
||||||
*/
|
|
||||||
class HarmnessBrowserConfigurablePanel {
|
|
||||||
|
|
||||||
private val urlField = JBTextField()
|
|
||||||
|
|
||||||
val panel: JPanel = FormBuilder.createFormBuilder()
|
|
||||||
.addLabeledComponent("Start URL", urlField, 1, false)
|
|
||||||
.addComponentFillVertically(JBLabel(
|
|
||||||
"<html>Embedded browser will open this URL when the tool window is shown.<br>" +
|
|
||||||
"The target site must allow embedding (no strict <code>X-Frame-Options</code> / <code>CSP frame-ancestors</code>).<br>" +
|
|
||||||
"e.g. <b>http://127.0.0.1:8888</b></html>", UIUtil.ComponentStyle.SMALL, UIUtil.FontColor.NORMAL), 1)
|
|
||||||
.panel
|
|
||||||
|
|
||||||
init {
|
|
||||||
urlField.columns = 40
|
|
||||||
urlField.emptyText.text = "http://127.0.0.1:8888"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isModified(settings: SettingsState): Boolean =
|
|
||||||
urlField.text != settings.startUrl
|
|
||||||
|
|
||||||
fun apply(settings: SettingsState) {
|
|
||||||
settings.startUrl = urlField.text.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reset(settings: SettingsState) {
|
|
||||||
urlField.text = settings.startUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOG = Logger.getInstance(HarmnessBrowserConfigurablePanel::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,24 +18,19 @@ import java.awt.Desktop
|
|||||||
import java.awt.FlowLayout
|
import java.awt.FlowLayout
|
||||||
import java.awt.event.ActionEvent
|
import java.awt.event.ActionEvent
|
||||||
import javax.swing.AbstractAction
|
import javax.swing.AbstractAction
|
||||||
|
import javax.swing.BorderFactory
|
||||||
import javax.swing.JButton
|
import javax.swing.JButton
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.JPanel
|
import javax.swing.JPanel
|
||||||
import javax.swing.JTextField
|
import javax.swing.JTextField
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 嵌入浏览器面板。
|
* 嵌入浏览器面板。
|
||||||
*
|
*
|
||||||
* 职责:
|
* 两种视图模式,通过右上角「⚙」图标切换:
|
||||||
* - 使用 JBCefBrowser 渲染用户配置的起始地址(例如本地 Harness 地址)
|
* - [Mode.BROWSER]:显示配置好的网页(JCEF)
|
||||||
* - 工具栏:地址栏、前进/后退、刷新、设置入口(快捷跳转到 IDE Settings 的插件配置页)
|
* - [Mode.SETTINGS]:表单,可修改「起始地址」,点保存后自动切回浏览器并加载新地址
|
||||||
* - 通过 [JBCefJSQuery] 提供 JS ↔ 插件代码双向桥
|
|
||||||
* - 拦截并处理 _blank 弹窗(默认吞掉,可选交给系统浏览器)
|
|
||||||
*
|
|
||||||
* 由于 IntelliJ Platform 官方插件模型目前不允许在 JCEF 浏览器内部直接调用
|
|
||||||
* "打开当前项目文件" 之类的 API(无 DOM 访问权限),插件通过注入到页面中的
|
|
||||||
* 桥接函数 [JS_BRIDGE_NAME] 让网页(如 Harness 前端)发起请求,
|
|
||||||
* 再由本类把结果返回给页面。
|
|
||||||
*/
|
*/
|
||||||
class HarmnessBrowserPanel(
|
class HarmnessBrowserPanel(
|
||||||
private val project: com.intellij.openapi.project.Project,
|
private val project: com.intellij.openapi.project.Project,
|
||||||
@@ -49,6 +44,10 @@ class HarmnessBrowserPanel(
|
|||||||
private lateinit var query: JBCefJSQuery
|
private lateinit var query: JBCefJSQuery
|
||||||
private val loadEnded = java.util.concurrent.atomic.AtomicBoolean(false)
|
private val loadEnded = java.util.concurrent.atomic.AtomicBoolean(false)
|
||||||
|
|
||||||
|
private var mode = Mode.BROWSER
|
||||||
|
|
||||||
|
private enum class Mode { BROWSER, SETTINGS }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Disposer.register(parentDisposable, this)
|
Disposer.register(parentDisposable, this)
|
||||||
|
|
||||||
@@ -59,19 +58,37 @@ class HarmnessBrowserPanel(
|
|||||||
BorderLayout.CENTER)
|
BorderLayout.CENTER)
|
||||||
} else {
|
} else {
|
||||||
browser = JBCefBrowser()
|
browser = JBCefBrowser()
|
||||||
|
|
||||||
// 布局:顶部工具栏 + 浏览器
|
|
||||||
add(createToolbar(), BorderLayout.NORTH)
|
|
||||||
add(browser.component, BorderLayout.CENTER)
|
|
||||||
|
|
||||||
// 设置弹窗处理
|
|
||||||
setupPopupHandling()
|
setupPopupHandling()
|
||||||
// 设置 JS 桥
|
|
||||||
setupBridge()
|
setupBridge()
|
||||||
|
|
||||||
// 初始加载
|
// 顶部工具栏(右上角 ⚙)
|
||||||
|
add(createToolbar(), BorderLayout.NORTH)
|
||||||
|
// 初始为浏览器视图
|
||||||
|
showBrowserMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// 视图切换
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
private fun showBrowserMode() {
|
||||||
|
mode = Mode.BROWSER
|
||||||
|
removeAll()
|
||||||
|
add(createToolbar(), BorderLayout.NORTH)
|
||||||
|
add(browser.component, BorderLayout.CENTER)
|
||||||
|
revalidate()
|
||||||
|
repaint()
|
||||||
loadCurrentUrl()
|
loadCurrentUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showSettingsMode() {
|
||||||
|
mode = Mode.SETTINGS
|
||||||
|
removeAll()
|
||||||
|
add(createToolbar(), BorderLayout.NORTH)
|
||||||
|
add(createSettingsForm(), BorderLayout.CENTER)
|
||||||
|
revalidate()
|
||||||
|
repaint()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
@@ -94,12 +111,13 @@ class HarmnessBrowserPanel(
|
|||||||
val reload = JButton(object : AbstractAction("⟳") {
|
val reload = JButton(object : AbstractAction("⟳") {
|
||||||
override fun actionPerformed(e: ActionEvent) { cefBrowser.reload() }
|
override fun actionPerformed(e: ActionEvent) { cefBrowser.reload() }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 右上角:设置图标(⚙),点击在两种模式间切换
|
||||||
val settingsBtn = JButton(object : AbstractAction("⚙") {
|
val settingsBtn = JButton(object : AbstractAction("⚙") {
|
||||||
override fun actionPerformed(e: ActionEvent) { openSettings() }
|
override fun actionPerformed(e: ActionEvent) { toggleSettings() }
|
||||||
})
|
|
||||||
val externalBtn = JButton(object : AbstractAction("⧉") {
|
|
||||||
override fun actionPerformed(e: ActionEvent) { openInExternalBrowser() }
|
|
||||||
})
|
})
|
||||||
|
settingsBtn.toolTipText = if (mode == Mode.BROWSER) "设置" else "返回主界面"
|
||||||
|
settingsBtn.border = BorderFactory.createEmptyBorder(2, 6, 2, 6)
|
||||||
|
|
||||||
addressField = JTextField(settings.startUrl)
|
addressField = JTextField(settings.startUrl)
|
||||||
addressField.toolTipText = "当前页面地址(可手动输入后回车跳转)"
|
addressField.toolTipText = "当前页面地址(可手动输入后回车跳转)"
|
||||||
@@ -111,35 +129,81 @@ class HarmnessBrowserPanel(
|
|||||||
toolbar.add(reload)
|
toolbar.add(reload)
|
||||||
toolbar.add(addressField)
|
toolbar.add(addressField)
|
||||||
toolbar.add(settingsBtn)
|
toolbar.add(settingsBtn)
|
||||||
toolbar.add(externalBtn)
|
|
||||||
return toolbar
|
return toolbar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun toggleSettings() {
|
||||||
|
if (mode == Mode.BROWSER) {
|
||||||
|
showSettingsMode()
|
||||||
|
} else {
|
||||||
|
// 从设置页切回主界面时重新加载
|
||||||
|
showBrowserMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun navigate(url: String) {
|
private fun navigate(url: String) {
|
||||||
val fixedUrl = if (url.startsWith("http://") || url.startsWith("https://")) url else "http://$url"
|
val fixedUrl = if (url.startsWith("http://") || url.startsWith("https://")) url else "http://$url"
|
||||||
browser.loadURL(fixedUrl)
|
browser.loadURL(fixedUrl)
|
||||||
|
loadEnded.set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openSettings() {
|
// ------------------------------------------------------------------
|
||||||
val app = com.intellij.openapi.application.ApplicationManager.getApplication()
|
// 设置表单
|
||||||
val show = Runnable {
|
// ------------------------------------------------------------------
|
||||||
com.intellij.openapi.options.ShowSettingsUtil.getInstance()
|
|
||||||
.showSettingsDialog(project, "Harmness Browser")
|
private fun createSettingsForm(): JComponent {
|
||||||
|
val panel = JBPanel<JBPanel<*>>()
|
||||||
|
panel.layout = java.awt.GridBagLayout()
|
||||||
|
val gbc = java.awt.GridBagConstraints()
|
||||||
|
gbc.insets = java.awt.Insets(6, 8, 6, 8)
|
||||||
|
gbc.fill = java.awt.GridBagConstraints.HORIZONTAL
|
||||||
|
gbc.anchor = java.awt.GridBagConstraints.NORTH
|
||||||
|
|
||||||
|
val title = JBLabel("<html><h2>Settings</h2></html>")
|
||||||
|
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2
|
||||||
|
panel.add(title, gbc)
|
||||||
|
gbc.gridwidth = 1
|
||||||
|
|
||||||
|
val label = JBLabel("起始地址 (Start URL):")
|
||||||
|
gbc.gridx = 0; gbc.gridy = 1
|
||||||
|
panel.add(label, gbc)
|
||||||
|
|
||||||
|
val urlField = JTextField(settings.startUrl, 30)
|
||||||
|
gbc.gridx = 1; gbc.gridy = 1
|
||||||
|
panel.add(urlField, gbc)
|
||||||
|
|
||||||
|
val hint = JBLabel("<html><small>例如:http://127.0.0.1:8899 (本地 Harness 服务地址)</small></html>")
|
||||||
|
gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2
|
||||||
|
panel.add(hint, gbc)
|
||||||
|
gbc.gridwidth = 1
|
||||||
|
|
||||||
|
val saveBtn = JButton("保存")
|
||||||
|
saveBtn.addActionListener {
|
||||||
|
settings.startUrl = urlField.text.trim()
|
||||||
|
saveBtn.text = "已保存 ✓"
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
Thread.sleep(400)
|
||||||
|
saveBtn.text = "保存"
|
||||||
}
|
}
|
||||||
if (app.isDispatchThread) show.run() else app.invokeLater(show)
|
showBrowserMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openInExternalBrowser() {
|
val cancelBtn = JButton("取消")
|
||||||
val url = addressField.text.ifBlank { settings.startUrl }
|
cancelBtn.addActionListener {
|
||||||
if (Desktop.isDesktopSupported()) {
|
showBrowserMode()
|
||||||
try {
|
|
||||||
Desktop.getDesktop().browse(java.net.URI(url))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
BrowserUtil.browse(url)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BrowserUtil.browse(url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val btnPanel = JPanel(FlowLayout(FlowLayout.LEFT, 8, 0))
|
||||||
|
btnPanel.add(saveBtn)
|
||||||
|
btnPanel.add(cancelBtn)
|
||||||
|
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2
|
||||||
|
panel.add(btnPanel, gbc)
|
||||||
|
|
||||||
|
gbc.gridy = 4
|
||||||
|
gbc.weighty = 1.0
|
||||||
|
panel.add(JBPanel<JBPanel<*>>(), gbc)
|
||||||
|
|
||||||
|
return panel
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
@@ -224,10 +288,6 @@ class HarmnessBrowserPanel(
|
|||||||
loadEnded.set(false)
|
loadEnded.set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reloadFromSettings() {
|
|
||||||
loadCurrentUrl()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
loadEnded.set(false)
|
loadEnded.set(false)
|
||||||
if (::query.isInitialized) {
|
if (::query.isInitialized) {
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package com.harmness.toolwindow
|
|
||||||
|
|
||||||
import com.intellij.openapi.options.Configurable
|
|
||||||
import javax.swing.JComponent
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings configurable registered in plugin.xml.
|
|
||||||
* Shows the HarmnessBrowserConfigurablePanel under Tools → Harmness Browser.
|
|
||||||
*/
|
|
||||||
class HarmnessSettingsConfigurable : Configurable {
|
|
||||||
|
|
||||||
private var panel: HarmnessBrowserConfigurablePanel? = null
|
|
||||||
|
|
||||||
override fun getDisplayName(): String = "Harmness Browser"
|
|
||||||
|
|
||||||
override fun getHelpTopic(): String? = null
|
|
||||||
|
|
||||||
override fun createComponent(): JComponent {
|
|
||||||
panel = HarmnessBrowserConfigurablePanel()
|
|
||||||
return panel!!.panel
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isModified(): Boolean = panel?.isModified(SettingsState.getInstance()) ?: false
|
|
||||||
|
|
||||||
override fun apply() {
|
|
||||||
panel?.apply(SettingsState.getInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reset() {
|
|
||||||
panel?.reset(SettingsState.getInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun disposeUIResources() {
|
|
||||||
panel = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,18 +8,14 @@ import com.intellij.openapi.components.Storage
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插件设置:目前仅保存一个「浏览器起始地址」。
|
* 插件设置:仅保存「浏览器起始地址」。
|
||||||
*
|
|
||||||
* 该地址必须是一个允许被嵌入的 Web 应用地址(例如本地 Harness 服务地址),
|
|
||||||
* 并且需要在页面上开启允许被 iframe/嵌入浏览(X-Frame-Options / CSP frame-ancestors)
|
|
||||||
* 或者由 Harness 提供专门的无 frame 限制入口。
|
|
||||||
*/
|
*/
|
||||||
@State(name = "HarmnessToolwindowSettings", storages = [Storage("harmness-toolwindow.xml")])
|
@State(name = "HarmnessToolwindowSettings", storages = [Storage("harmness-toolwindow.xml")])
|
||||||
@Service(Service.Level.APP)
|
@Service(Service.Level.APP)
|
||||||
class SettingsState : PersistentStateComponent<SettingsState.State> {
|
class SettingsState : PersistentStateComponent<SettingsState.State> {
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
var startUrl: String = "http://127.0.0.1:8888",
|
var startUrl: String = DEFAULT_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var myState = State()
|
private var myState = State()
|
||||||
@@ -39,7 +35,8 @@ class SettingsState : PersistentStateComponent<SettingsState.State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_URL = "http://127.0.0.1:8888"
|
// 与 DSH Web GUI 默认端口一致
|
||||||
|
const val DEFAULT_URL = "http://127.0.0.1:8899"
|
||||||
|
|
||||||
fun getInstance(): SettingsState =
|
fun getInstance(): SettingsState =
|
||||||
ServiceManager.getService(SettingsState::class.java)
|
ServiceManager.getService(SettingsState::class.java)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
IDEA without switching to a separate browser window.
|
IDEA without switching to a separate browser window.
|
||||||
]]></description>
|
]]></description>
|
||||||
|
|
||||||
<idea-version since-build="242" until-build="251.*"/>
|
<idea-version since-build="242" until-build="263.*"/>
|
||||||
|
|
||||||
<depends>com.intellij.modules.platform</depends>
|
<depends>com.intellij.modules.platform</depends>
|
||||||
|
|
||||||
@@ -21,12 +21,6 @@
|
|||||||
anchor="right"
|
anchor="right"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<applicationConfigurable
|
|
||||||
parent="tools"
|
|
||||||
instance="com.harmness.toolwindow.HarmnessSettingsConfigurable"
|
|
||||||
id="com.harmness.toolwindow.HarmnessSettingsConfigurable"
|
|
||||||
displayName="Harmness Browser"/>
|
|
||||||
|
|
||||||
<applicationService
|
<applicationService
|
||||||
serviceImplementation="com.harmness.toolwindow.SettingsState"/>
|
serviceImplementation="com.harmness.toolwindow.SettingsState"/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user