Skip to content

Commit b830757

Browse files
committed
Add IDE theme sync for preview panel
Detect dark/light theme from IDE editor colors and apply matching styles to the preview. Updates dynamically when user switches themes.
1 parent dcb6e69 commit b830757

File tree

1 file changed

+51
-11
lines changed

1 file changed

+51
-11
lines changed

src/main/kotlin/org/phpcollective/djot/preview/DjotPreviewPanel.kt

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ package org.phpcollective.djot.preview
22

33
import com.intellij.openapi.Disposable
44
import com.intellij.openapi.application.ApplicationManager
5+
import com.intellij.openapi.editor.colors.EditorColorsListener
6+
import com.intellij.openapi.editor.colors.EditorColorsManager
7+
import com.intellij.openapi.editor.colors.EditorColorsScheme
58
import com.intellij.openapi.editor.event.DocumentEvent
69
import com.intellij.openapi.editor.event.DocumentListener
710
import com.intellij.openapi.fileEditor.FileDocumentManager
811
import com.intellij.openapi.project.Project
912
import com.intellij.openapi.vfs.VirtualFile
1013
import com.intellij.ui.jcef.JBCefBrowser
14+
import com.intellij.util.messages.MessageBusConnection
1115
import java.awt.BorderLayout
1216
import java.util.concurrent.atomic.AtomicBoolean
1317
import javax.swing.JComponent
@@ -24,6 +28,7 @@ class DjotPreviewPanel(
2428
private val updatePending = AtomicBoolean(false)
2529
private val updateTimer: Timer
2630
private var initialized = false
31+
private val messageBusConnection: MessageBusConnection
2732

2833
private val documentListener = object : DocumentListener {
2934
override fun documentChanged(event: DocumentEvent) {
@@ -46,23 +51,50 @@ class DjotPreviewPanel(
4651
val document = FileDocumentManager.getInstance().getDocument(file)
4752
document?.addDocumentListener(documentListener, this)
4853

54+
// Listen for theme changes
55+
messageBusConnection = ApplicationManager.getApplication().messageBus.connect(this)
56+
messageBusConnection.subscribe(EditorColorsManager.TOPIC, EditorColorsListener {
57+
updateTheme()
58+
})
59+
4960
// Initial render - load the HTML shell with djot.js
5061
loadPreviewShell()
5162
}
5263

5364
val component: JComponent get() = panel
5465

66+
private fun isDarkTheme(): Boolean {
67+
val scheme = EditorColorsManager.getInstance().globalScheme
68+
val background = scheme.defaultBackground
69+
// Calculate luminance to determine if dark
70+
val luminance = (0.299 * background.red + 0.587 * background.green + 0.114 * background.blue) / 255
71+
return luminance < 0.5
72+
}
73+
5574
private fun scheduleUpdate() {
5675
updatePending.set(true)
5776
updateTimer.restart()
5877
}
5978

79+
private fun updateTheme() {
80+
if (!initialized) return
81+
val isDark = isDarkTheme()
82+
ApplicationManager.getApplication().invokeLater {
83+
browser.cefBrowser.executeJavaScript(
84+
"document.body.classList.toggle('dark', $isDark); document.body.classList.toggle('light', ${!isDark});",
85+
browser.cefBrowser.url,
86+
0
87+
)
88+
}
89+
}
90+
6091
private fun loadPreviewShell() {
6192
val document = FileDocumentManager.getInstance().getDocument(file)
6293
val content = document?.text ?: ""
6394
val escapedContent = escapeForJs(content)
95+
val isDark = isDarkTheme()
6496

65-
browser.loadHTML(createPreviewHtml(escapedContent))
97+
browser.loadHTML(createPreviewHtml(escapedContent, isDark))
6698
initialized = true
6799
}
68100

@@ -97,7 +129,8 @@ class DjotPreviewPanel(
97129
.replace("\r", "\n")
98130
}
99131

100-
private fun createPreviewHtml(initialContent: String): String {
132+
private fun createPreviewHtml(initialContent: String, isDark: Boolean): String {
133+
val themeClass = if (isDark) "dark" else "light"
101134
return """
102135
<!DOCTYPE html>
103136
<html>
@@ -114,15 +147,20 @@ class DjotPreviewPanel(
114147
color: #333;
115148
background: #fff;
116149
}
117-
@media (prefers-color-scheme: dark) {
118-
body { background: #1e1e1e; color: #d4d4d4; }
119-
a { color: #6db3f2; }
120-
code, pre { background: #2d2d2d; }
121-
blockquote { border-color: #444; color: #aaa; }
122-
table, th, td { border-color: #444; }
123-
th { background: #2d2d2d; }
124-
hr { border-color: #444; }
150+
body.dark {
151+
background: #1e1e1e;
152+
color: #d4d4d4;
125153
}
154+
body.dark a { color: #6db3f2; }
155+
body.dark code, body.dark pre { background: #2d2d2d; }
156+
body.dark blockquote { border-color: #444; color: #aaa; }
157+
body.dark table, body.dark th, body.dark td { border-color: #444; }
158+
body.dark th { background: #2d2d2d; }
159+
body.dark hr { border-color: #444; }
160+
body.dark h1 { color: #e0e0e0; border-bottom-color: #6db3f2; }
161+
body.dark h2 { color: #c0c0c0; border-bottom-color: #444; }
162+
body.dark h3, body.dark h4, body.dark h5, body.dark h6 { color: #a0a0a0; }
163+
body.dark mark { background: #5a5000; color: #fff; }
126164
h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; margin-top: 0; }
127165
h2 { color: #34495e; border-bottom: 1px solid #bdc3c7; padding-bottom: 5px; }
128166
h3, h4, h5, h6 { color: #7f8c8d; }
@@ -178,10 +216,12 @@ class DjotPreviewPanel(
178216
#content { min-height: 100px; }
179217
#error { color: #dc3545; background: #f8d7da; padding: 10px; border-radius: 5px; display: none; }
180218
#loading { color: #666; font-style: italic; }
219+
body.dark #error { background: #5a2d2d; color: #f8d7da; }
220+
body.dark #loading { color: #aaa; }
181221
</style>
182222
<script src="https://cdn.jsdelivr.net/npm/@djot/[email protected]/dist/djot.min.js"></script>
183223
</head>
184-
<body>
224+
<body class="$themeClass">
185225
<div id="error"></div>
186226
<div id="content"><div id="loading">Loading...</div></div>
187227
<script>

0 commit comments

Comments
 (0)