@@ -2,12 +2,16 @@ package org.phpcollective.djot.preview
22
33import com.intellij.openapi.Disposable
44import 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
58import com.intellij.openapi.editor.event.DocumentEvent
69import com.intellij.openapi.editor.event.DocumentListener
710import com.intellij.openapi.fileEditor.FileDocumentManager
811import com.intellij.openapi.project.Project
912import com.intellij.openapi.vfs.VirtualFile
1013import com.intellij.ui.jcef.JBCefBrowser
14+ import com.intellij.util.messages.MessageBusConnection
1115import java.awt.BorderLayout
1216import java.util.concurrent.atomic.AtomicBoolean
1317import 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