@@ -3,6 +3,10 @@ import { isHexColor } from "../../../shared/lib/colorUtils";
33import { HAS_NATIVE_GLASS , IS_MAC } from "../../../platform/tauri/platform" ;
44import { readFlag , writeFlag } from "./storageFlags" ;
55import { applyUiScale , loadUiScale } from "./uiScale" ;
6+ import {
7+ applyPreparedNewThreadBackground ,
8+ clearPreparedNewThreadBackground ,
9+ } from "./newThreadBackgroundEffects" ;
610
711const ACCENT_COLOR_KEY = "monocode.accentColor" ;
812const THEME_HUE_KEY = "monocode.themeHue" ;
@@ -23,6 +27,7 @@ const CHAT_BACKGROUND_EMPTY_OPACITY_KEY = "monocode.chatBackgroundEmptyOpacity";
2327const CHAT_BACKGROUND_SESSION_OPACITY_KEY =
2428 "monocode.chatBackgroundSessionOpacity" ;
2529const CHAT_BACKGROUND_SCOPE_KEY = "monocode.chatBackgroundScope" ;
30+ const NEW_THREAD_BACKGROUND_EFFECT_KEY = "monocode.newThreadBackgroundEffect" ;
2631const CHANGES_VIEW_KEY = "monocode.changesView" ;
2732const SHOW_EXCLUDED_FILES_KEY = "monocode.showExcludedFiles" ;
2833let chatBackgroundRevision = Date . now ( ) ;
@@ -35,8 +40,38 @@ export type ColorScheme = "dark" | "light";
3540export type ThemePreference = ColorScheme | "system" ;
3641export type TranscriptLayout = "full" | "chat" ;
3742export type ChatBackgroundScope = "empty" | "all" ;
43+ export type NewThreadBackgroundEffect =
44+ "none" | "dither" | "ascii" | "halftone" | "scanlines" ;
3845export type ChangesView = "list" | "tree" ;
3946
47+ export const NEW_THREAD_BACKGROUND_EFFECTS : readonly NewThreadBackgroundEffect [ ] =
48+ [ "none" , "dither" , "ascii" , "halftone" , "scanlines" ] ;
49+
50+ export const NEW_THREAD_BACKGROUND_EFFECT_DEFAULT : NewThreadBackgroundEffect =
51+ "none" ;
52+
53+ export const NEW_THREAD_BACKGROUND_EFFECT_LABELS : Record <
54+ NewThreadBackgroundEffect ,
55+ string
56+ > = {
57+ none : "None" ,
58+ dither : "Dither" ,
59+ ascii : "ASCII" ,
60+ halftone : "Halftone" ,
61+ scanlines : "Scanlines" ,
62+ } ;
63+
64+ export const NEW_THREAD_BACKGROUND_EFFECT_DESCRIPTIONS : Record <
65+ NewThreadBackgroundEffect ,
66+ string
67+ > = {
68+ none : "Shows the original artwork." ,
69+ dither : "Rebuilds the artwork with a dithered color palette." ,
70+ ascii : "Recreates the artwork with colored characters on black." ,
71+ halftone : "Recreates the artwork with colored print dots on black." ,
72+ scanlines : "Adds a pronounced horizontal display-line texture." ,
73+ } ;
74+
4075export const THEME_PREFERENCE_DEFAULT : ThemePreference = "dark" ;
4176
4277export const ACCENT_COLOR_DEFAULT = null ;
@@ -324,6 +359,13 @@ export function applyThemePreference(value: ThemePreference): ColorScheme {
324359 window . dispatchEvent (
325360 new CustomEvent < ColorScheme > ( SCHEME_CHANGE_EVENT , { detail : next } ) ,
326361 ) ;
362+ const backgroundPath = loadChatBackgroundPath ( ) ;
363+ if (
364+ backgroundPath &&
365+ document . documentElement . classList . contains ( "has-chat-background" )
366+ ) {
367+ renderChatBackground ( backgroundPath ) ;
368+ }
327369 return next ;
328370}
329371
@@ -437,22 +479,77 @@ export function applyChatBackground(path: string | null) {
437479 const root = document . documentElement ;
438480 root . classList . toggle ( "has-chat-background" , ! ! path ) ;
439481 if ( ! path ) {
440- root . style . removeProperty ( "--chat-background-image" ) ;
482+ clearPreparedNewThreadBackground ( ) ;
441483 return null ;
442484 }
443485 chatBackgroundRevision += 1 ;
444- const src = chatBackgroundSrc ( path ) ;
445- root . style . setProperty (
446- "--chat-background-image" ,
447- `url(${ JSON . stringify ( src ) } )` ,
448- ) ;
486+ renderChatBackground ( path ) ;
449487 return path ;
450488}
451489
452490export function chatBackgroundSrc ( path : string | null ) : string | null {
453491 return path ? `${ convertFileSrc ( path ) } ?v=${ chatBackgroundRevision } ` : null ;
454492}
455493
494+ function isNewThreadBackgroundEffect (
495+ value : unknown ,
496+ ) : value is NewThreadBackgroundEffect {
497+ return NEW_THREAD_BACKGROUND_EFFECTS . includes (
498+ value as NewThreadBackgroundEffect ,
499+ ) ;
500+ }
501+
502+ export function loadNewThreadBackgroundEffect ( ) : NewThreadBackgroundEffect {
503+ try {
504+ const raw = localStorage . getItem ( NEW_THREAD_BACKGROUND_EFFECT_KEY ) ;
505+ return isNewThreadBackgroundEffect ( raw )
506+ ? raw
507+ : NEW_THREAD_BACKGROUND_EFFECT_DEFAULT ;
508+ } catch {
509+ return NEW_THREAD_BACKGROUND_EFFECT_DEFAULT ;
510+ }
511+ }
512+
513+ export function saveNewThreadBackgroundEffect (
514+ effect : NewThreadBackgroundEffect ,
515+ ) {
516+ try {
517+ localStorage . setItem ( NEW_THREAD_BACKGROUND_EFFECT_KEY , effect ) ;
518+ } catch {
519+ // private mode / quota
520+ }
521+ }
522+
523+ function renderChatBackground (
524+ path : string ,
525+ effect = loadNewThreadBackgroundEffect ( ) ,
526+ ) {
527+ const src = chatBackgroundSrc ( path ) ;
528+ if ( ! src ) return ;
529+ void applyPreparedNewThreadBackground (
530+ `${ path } ?v=${ chatBackgroundRevision } ` ,
531+ src ,
532+ effect ,
533+ isLightScheme ( ) ,
534+ ) ;
535+ }
536+
537+ export function applyNewThreadBackgroundEffect (
538+ effect : NewThreadBackgroundEffect ,
539+ ) {
540+ const path = loadChatBackgroundPath ( ) ;
541+ if ( path ) renderChatBackground ( path , effect ) ;
542+ return effect ;
543+ }
544+
545+ export function setNewThreadBackgroundEffect (
546+ effect : NewThreadBackgroundEffect ,
547+ ) {
548+ saveNewThreadBackgroundEffect ( effect ) ;
549+ applyNewThreadBackgroundEffect ( effect ) ;
550+ window . dispatchEvent ( new Event ( CHAT_BACKGROUND_PATH_CHANGE_EVENT ) ) ;
551+ }
552+
456553export function loadChatBackgroundOpacity ( ) : number {
457554 return loadChatBackgroundEmptyOpacity ( ) ;
458555}
0 commit comments