@@ -15,7 +15,7 @@ import { useKokoConnectionStore } from "~/koko/stores/connection";
1515import { useKokoTerminalSettingsStore } from "~/koko/stores/terminalSettings" ;
1616import { MaxTimeout } from "~/koko/utils/config" ;
1717import { getDefaultTerminalConfig } from "~/koko/utils/guard" ;
18- import { terminalTheme } from "~/koko/utils/terminalTheme" ;
18+ import { appTerminalTheme , terminalTheme } from "~/koko/utils/terminalTheme" ;
1919import { formatMessage , getXTerminalLineContent , preprocessInput , updateIcon , writeBufferToTerminal } from "~/koko/utils/terminalUtils" ;
2020import {
2121 FORMATTER_MESSAGE_TYPE ,
@@ -58,6 +58,9 @@ export const useKokoTerminalSocket = () => {
5858 const terminalSettingsStore = useKokoTerminalSettingsStore ( ) ;
5959 const sessionCtxRef = inject ( connectorSessionKey , null ) ;
6060 const queryTerminalThemeName = computed ( ( ) => unref ( sessionCtxRef ) ?. terminalThemeName || "" ) ;
61+ // 未显式指定主题名(workspace 内嵌场景)时跟随应用主题;独立 /koko/connect 路由带主题名则维持原逻辑
62+ const followAppTheme = computed ( ( ) => ! ! unref ( sessionCtxRef ) && ! queryTerminalThemeName . value ) ;
63+ let themeObserver : MutationObserver | null = null ;
6164
6265 const fitAddon = new FitAddon ( ) ;
6366 const webglAddon = new WebglAddon ( ) ;
@@ -171,8 +174,9 @@ export const useKokoTerminalSocket = () => {
171174 terminalSettingsStore . setDefaultTerminalConfig ( "ctrlCAsCtrlZ" , sessionInfo . ctrlCAsCtrlZ ? "1" : "0" ) ;
172175 }
173176
177+ // 跟随应用主题时不让服务端 themeName 覆盖
174178 const effectiveThemeName = queryTerminalThemeName . value || sessionInfo . themeName ;
175- if ( effectiveThemeName ) {
179+ if ( effectiveThemeName && ! followAppTheme . value ) {
176180 nextTick ( ( ) => {
177181 terminalRef . value ! . options . theme = terminalTheme ( effectiveThemeName ) ;
178182 } ) ;
@@ -367,7 +371,8 @@ export const useKokoTerminalSocket = () => {
367371 rightClickSelectsWord : true ,
368372 scrollback : 5000 ,
369373 scrollOnUserInput : true ,
370- theme : terminalTheme ( defaultTerminalCfg . themeName ) ,
374+ theme : followAppTheme . value ? appTerminalTheme ( ) : terminalTheme ( defaultTerminalCfg . themeName ) ,
375+ minimumContrastRatio : 4.5 ,
371376 allowProposedApi : true ,
372377 customGlyphs : true
373378 } ) ;
@@ -390,10 +395,24 @@ export const useKokoTerminalSocket = () => {
390395 socketRef . value = ws . value ;
391396 } ;
392397
398+ // dark/light 切 class、preset 切 data-theme-preset、Luna 预设写内联 style,三种路径都在 <html> 属性上
399+ const observeAppTheme = ( ) => {
400+ if ( ! followAppTheme . value || ! import . meta. client ) return ;
401+ themeObserver = new MutationObserver ( ( ) => {
402+ if ( ! terminalRef . value ) return ;
403+ terminalRef . value . options . theme = appTerminalTheme ( ) ;
404+ } ) ;
405+ themeObserver . observe ( document . documentElement , {
406+ attributes : true ,
407+ attributeFilter : [ "class" , "data-theme-preset" , "style" ]
408+ } ) ;
409+ } ;
410+
393411 onMounted ( ( ) => {
394412 if ( ! containerRef . value ) return ;
395413 createTerminal ( ) ;
396414 createWebSocket ( ) ;
415+ observeAppTheme ( ) ;
397416 nextTick ( ( ) => {
398417 listenSocketEvent ( ) ;
399418 listenTerminalRefEvent ( ) ;
@@ -405,6 +424,7 @@ export const useKokoTerminalSocket = () => {
405424
406425 onUnmounted ( ( ) => {
407426 autoTerminalFit ( ) ;
427+ themeObserver ?. disconnect ( ) ;
408428 if ( pingInterval . value ) clearInterval ( pingInterval . value ) ;
409429 if ( warningInterval . value ) clearInterval ( warningInterval . value ) ;
410430 socketRef . value ?. close ( ) ;
0 commit comments