@kecan0406/ttheme 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.3",
2
+ "version": "1.2.5",
3
3
  "gate": [
4
4
  {
5
5
  "rule": "foreground",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kecan0406/ttheme",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "ttheme": "bin/ttheme.js"
@@ -45,8 +45,19 @@ __tt_repaint() {
45
45
 
46
46
  __tt_apply() { __tt_osc_apply "$@" }
47
47
 
48
+ __tt_wear() {
49
+ local REPLY=$2 name
50
+ [[ -n $REPLY ]] || __tt_name_of "$1"
51
+ name=$REPLY
52
+ TTHEME_SPEC=$1
53
+ __tt_apply "$1"
54
+ [[ -n ${TTHEME_PALETTE[$name]} ]] && __tt_shown "$name" $3 && __tt_reload
55
+ }
56
+
48
57
  __tt_pv_paint() { __tt_apply "$1" && painted=$1 }
49
58
 
59
+ __tt_pv_claim() { : }
60
+
50
61
  __tt_shown() { return 1 }
51
62
 
52
63
  __tt_unshown() { : }
@@ -55,6 +66,8 @@ __tt_reloaded() { : }
55
66
 
56
67
  __tt_worn() { : }
57
68
 
69
+ __tt_blurred() { : }
70
+
58
71
  __tt_keepable() { return 1 }
59
72
 
60
73
  __tt_paints() { return 0 }
@@ -1,4 +1,5 @@
1
1
  typeset -g TTHEME_GHOSTTY_PID=""
2
+ typeset -gi TTHEME_GHOSTTY_SENT=0
2
3
 
3
4
  __tt_reload() {
4
5
  local wired
@@ -16,23 +17,31 @@ __tt_pictured() {
16
17
  return 0
17
18
  }
18
19
 
20
+ __tt_ghostty_owner() {
21
+ local pid=$1 ppid comm
22
+ REPLY=0
23
+ while (( pid > 1 )); do
24
+ read -r ppid comm <<< "$(ps -o ppid=,comm= -p $pid)"
25
+ if [[ ${comm:t} == ghostty ]]; then
26
+ REPLY=$pid
27
+ return 0
28
+ fi
29
+ pid=$ppid
30
+ done
31
+ return 1
32
+ }
33
+
19
34
  __tt_reload_ghostty() {
20
- local pid=$PPID ppid comm owner=$TTHEME_GHOSTTY_PID
21
- (( TTHEME_TMUX )) && { pid=$(tmux display -p '#{client_pid}' 2>/dev/null) owner="" }
22
- if [[ -z $owner ]]; then
23
- owner=0
24
- while (( pid > 1 )); do
25
- read -r ppid comm <<< "$(ps -o ppid=,comm= -p $pid)"
26
- if [[ ${comm:t} == ghostty ]]; then
27
- owner=$pid
28
- break
29
- fi
30
- pid=$ppid
31
- done
32
- (( TTHEME_TMUX )) || TTHEME_GHOSTTY_PID=$owner
35
+ local REPLY owner=$TTHEME_GHOSTTY_PID
36
+ if (( TTHEME_TMUX )); then
37
+ __tt_ghostty_owner "$(tmux display -p '#{client_pid}' 2>/dev/null)"
38
+ owner=$REPLY
39
+ elif [[ -z $owner ]]; then
40
+ __tt_ghostty_owner $PPID
41
+ owner=$REPLY TTHEME_GHOSTTY_PID=$REPLY
33
42
  fi
34
- (( owner )) && kill -USR2 $owner 2>/dev/null && return
35
- pkill -USR2 -x ghostty 2>/dev/null
43
+ (( owner )) && kill -USR2 $owner 2>/dev/null && { TTHEME_GHOSTTY_SENT=1; return 0 }
44
+ pkill -USR2 -x ghostty 2>/dev/null && TTHEME_GHOSTTY_SENT=1
36
45
  }
37
46
 
38
47
  __tt_ghostty_shown() {
@@ -1,11 +1,260 @@
1
1
  source $TTHEME_HOME/adapters/_bg.zsh
2
2
 
3
+ typeset -g TTHEME_GHOSTTY_TABS=$TTHEME_STATE_DIR/ghostty TTHEME_GHOSTTY_FRONT="" TTHEME_GHOSTTY_BLUR="" TTHEME_GHOSTTY_HEARD="" TTHEME_GHOSTTY_CODE=""
4
+ typeset -gF TTHEME_GHOSTTY_ASKED=0 TTHEME_GHOSTTY_WANT=0 TTHEME_GHOSTTY_HUSH=0 TTHEME_GHOSTTY_TOLD=0 TTHEME_GHOSTTY_NONE_AT=0
5
+ typeset -gi TTHEME_GHOSTTY_IDLE=1 TTHEME_GHOSTTY_TICK=0 TTHEME_GHOSTTY_NONE=0 TTHEME_GHOSTTY_FOLLOWS=-1 TTHEME_GHOSTTY_WFD=0 TTHEME_GHOSTTY_WATCHER=0
6
+ typeset -gA TTHEME_GHOSTTY_SEEN=() TTHEME_GHOSTTY_RUN=()
7
+ typeset -g TTHEME_GHOSTTY_JS='ObjC.import("AppKit");function run(v){var p=+v[0],E=$.NSAppleEventDescriptor,G="com.mitchellh.ghostty",f=function(s){return(s.charCodeAt(0)<<24)|(s.charCodeAt(1)<<16)|(s.charCodeAt(2)<<8)|s.charCodeAt(3)},a=p?$.NSRunningApplication.runningApplicationWithProcessIdentifier(p):$.NSRunningApplication.runningApplicationsWithBundleIdentifier(G).firstObject;if(!a||a.isNil())return p?"gone":"none";var s=$.NSString.stringWithContentsOfFileEncodingError(a.bundleURL.path.js+"/Contents/Resources/Ghostty.sdef",4,null);if(s.isNil()||s.js.indexOf("code=\"Gtty\"")<0)return"blind";var t=E.descriptorWithProcessIdentifier(a.processIdentifier);function q(c,o){var d=E.recordDescriptor;d.setDescriptorForKeyword(E.descriptorWithTypeCode(f("prop")),f("want"));d.setDescriptorForKeyword(o,f("from"));d.setDescriptorForKeyword(E.descriptorWithEnumCode(f("prop")),f("form"));d.setDescriptorForKeyword(E.descriptorWithTypeCode(f(c)),f("seld"));return d.coerceToDescriptorType(f("obj "))}function g(o){var e=E.appleEventWithEventClassEventIDTargetDescriptorReturnIDTransactionID(f("core"),f("getd"),t,-1,0);e.setParamDescriptorForKeyword(o,f("----"));var r=e.sendEventWithOptionsTimeoutError(3,5,null);return!r||r.isNil()||!r.paramDescriptorForKeyword(f("errn")).isNil()?null:r.paramDescriptorForKeyword(f("----"))}var n=E.nullDescriptor,m=q("GTfT",q("GWsT",q("GFWn",n))),w=g(q("pisf",n)),y=g(q("Gtty",m)),i=g(q("Gpid",m));if(!w||!y||!i||w.isNil()||y.isNil()||i.isNil())return"none";return(w.booleanValue?1:0)+" "+y.stringValue.js+" "+i.int32Value}'
8
+ typeset -g TTHEME_GHOSTTY_WATCH='ObjC.import("AppKit");ObjC.import("CoreGraphics");ObjC.import("unistd");function run(v){var p=+v[0],u=$.getppid(),E=$.NSAppleEventDescriptor,W=$.NSWorkspace.sharedWorkspace,L=$.NSRunLoop.currentRunLoop,o=$.NSFileHandle.fileHandleWithStandardOutput,a=$.NSRunningApplication.runningApplicationWithProcessIdentifier(p),f=function(s){return(s.charCodeAt(0)<<24)|(s.charCodeAt(1)<<16)|(s.charCodeAt(2)<<8)|s.charCodeAt(3)},S=$.kCGWindowListOptionOnScreenOnly|$.kCGWindowListExcludeDesktopElements,A=$.kCGWindowListOptionOnScreenAboveWindow,w=0,b=-1,h=false,z=0,k=0,c={};if(!a||a.isNil())return;var t=E.descriptorWithProcessIdentifier(p);function q(y,x){var d=E.recordDescriptor;d.setDescriptorForKeyword(E.descriptorWithTypeCode(f("prop")),f("want"));d.setDescriptorForKeyword(x,f("from"));d.setDescriptorForKeyword(E.descriptorWithEnumCode(f("prop")),f("form"));d.setDescriptorForKeyword(E.descriptorWithTypeCode(f(y)),f("seld"));return d.coerceToDescriptorType(f("obj "))}function g(x){var e=E.appleEventWithEventClassEventIDTargetDescriptorReturnIDTransactionID(f("core"),f("getd"),t,-1,0);e.setParamDescriptorForKeyword(x,f("----"));var r=e.sendEventWithOptionsTimeoutError(3,5,null);return!r||r.isNil()||!r.paramDescriptorForKeyword(f("errn")).isNil()?null:r.paramDescriptorForKeyword(f("----"))}var m=q("GTfT",q("GWsT",q("GFWn",E.nullDescriptor)));function mine(d){return d.objectForKey("kCGWindowOwnerPID").intValue===p&&d.objectForKey("kCGWindowLayer").intValue===0}function top(){var l=ObjC.castRefToObject($.CGWindowListCopyWindowInfo(S,0));for(var j=0;j<+l.count;j++){var d=l.objectAtIndex(j);if(mine(d))return d.objectForKey("kCGWindowNumber").intValue}return 0}function n(k,x){return+$.CFArrayGetCount($.CGWindowListCreate(k,x))}function idle(){return Math.min($.CGEventSourceSecondsSinceLastEventType(0,1),$.CGEventSourceSecondsSinceLastEventType(0,10),$.CGEventSourceSecondsSinceLastEventType(0,12))}function now(){return $.NSDate.date.timeIntervalSince1970.toFixed(6)}function say(s){o.writeData($(s+"\n").dataUsingEncoding(4))}while(!a.terminated&&$.getppid()===u){var x=w?n(A,w):-2,v=!w||x!==b||!n($.kCGWindowListOptionIncludingWindow,w);if(v||z++%40===0){L.runUntilDate($.NSDate.date);h=W.frontmostApplication.processIdentifier===p;var e=top();w=e;b=e?n(A,e):-1;if(!e){var d=$.NSRunningApplication.runningApplicationWithProcessIdentifier(p);if(!d||d.isNil())break}if(h&&e&&e!==k){k=e;var at=now();if(c[e])say("1 "+c[e]+" "+at);var y=g(q("Gtty",m)),i=g(q("Gpid",m));if(y&&i&&!y.isNil()&&!i.isNil()){var r=y.stringValue.js+" "+i.int32Value;if(r!==c[e])say("1 "+r+" "+now());if(top()===e)c[e]=r}}}if(!h)k=0;delay(!w?1:!h?0.25:idle()<1.5?0.05:0.15)}}'
9
+
3
10
  __tt_follows_focus() { return 0 }
4
11
 
12
+ __tt_follows_prompt() { return 0 }
13
+
5
14
  __tt_keepable() { return 0 }
6
15
 
7
16
  __tt_bg_shown() { __tt_ghostty_shown }
8
17
 
18
+ __tt_ghostty_key() { REPLY=${${1#/dev/}//\//-} }
19
+
20
+ __tt_ghostty_wears() {
21
+ REPLY=""
22
+ [[ -n $1 ]] && __tt_name_of "$1"
23
+ [[ -n ${TTHEME_PALETTE[$REPLY]} ]] || REPLY=""
24
+ }
25
+
26
+ __tt_ghostty_modules() {
27
+ zmodload -F zsh/files b:zf_rm b:zf_mkdir
28
+ zmodload -F zsh/zselect b:zselect
29
+ zmodload -F zsh/datetime p:EPOCHREALTIME p:EPOCHSECONDS
30
+ } 2>/dev/null
31
+
32
+ __tt_ghostty_follows() {
33
+ local -a at
34
+ (( TTHEME_GHOSTTY_FOLLOWS < 0 )) || return $(( ! TTHEME_GHOSTTY_FOLLOWS ))
35
+ TTHEME_GHOSTTY_FOLLOWS=0
36
+ [[ $TERM_PROGRAM == ghostty && -o interactive && -n $TTY ]] && (( $+commands[osascript] && ! TTHEME_TMUX )) || return 1
37
+ if zstat -A at +mtime -- $TTHEME_GHOSTTY_TABS/.blind 2>/dev/null && [[ "$(<$TTHEME_GHOSTTY_TABS/.blind)" == "${TERM_PROGRAM_VERSION:--}" ]]; then
38
+ zmodload -F zsh/datetime p:EPOCHSECONDS 2>/dev/null
39
+ (( EPOCHSECONDS - at[1] < 86400 )) && return 1
40
+ fi
41
+ TTHEME_GHOSTTY_FOLLOWS=1
42
+ }
43
+
44
+ __tt_ghostty_publish() {
45
+ local f line REPLY
46
+ __tt_ghostty_follows || return 0
47
+ line="$$ ${TTHEME_STARTUP:--} ${1:--} $TTY"
48
+ __tt_ghostty_key $TTY
49
+ f=$TTHEME_GHOSTTY_TABS/$REPLY
50
+ [[ -r $f && "$(<$f)" == "$line" ]] && return 0
51
+ if [[ ! -d $TTHEME_GHOSTTY_TABS ]]; then
52
+ zmodload -F zsh/files b:zf_mkdir 2>/dev/null
53
+ zf_mkdir -p $TTHEME_GHOSTTY_TABS 2>/dev/null || return 0
54
+ fi
55
+ __tt_put $f "$line" 2>/dev/null
56
+ return 0
57
+ }
58
+
59
+ __tt_worn() {
60
+ local REPLY
61
+ (( TTHEME_RAW )) && return 0
62
+ __tt_ghostty_wears "$1"
63
+ __tt_ghostty_publish "$REPLY"
64
+ }
65
+
66
+ __tt_prompted() {
67
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
68
+ local REPLY
69
+ __tt_ghostty_wears "$TTHEME_SPEC"
70
+ __tt_ghostty_publish "$REPLY"
71
+ __tt_ghostty_follow
72
+ }
73
+
74
+ __tt_blurred() {
75
+ __tt_ghostty_follows && [[ -d $TTHEME_GHOSTTY_TABS ]] && : >| $TTHEME_GHOSTTY_TABS/.blur 2>/dev/null
76
+ return 0
77
+ }
78
+
79
+ __tt_ghostty_follow() {
80
+ local fd lock=$TTHEME_GHOSTTY_TABS/.follow
81
+ __tt_ghostty_follows && [[ -d $TTHEME_GHOSTTY_TABS ]] || return 0
82
+ if [[ -e $TTHEME_GHOSTTY_TABS/.blind ]]; then
83
+ TTHEME_GHOSTTY_FOLLOWS=-1
84
+ __tt_ghostty_follows || return 0
85
+ fi
86
+ [[ -e $lock ]] || : >>| $lock 2>/dev/null || return 0
87
+ zsystem flock -t 0 -f fd $lock 2>/dev/null || return 0
88
+ zsystem flock -u $fd
89
+ zsh -fc 'source $1; __tt_ghostty_follower' zsh $TTHEME_HOME/ttheme.zsh </dev/null >/dev/null 2>&1 &!
90
+ }
91
+
92
+ __tt_ghostty_ask() {
93
+ REPLY=$(osascript -l JavaScript -e $TTHEME_GHOSTTY_JS ${TTHEME_GHOSTTY_PID:-0} 2>/dev/null)
94
+ TTHEME_GHOSTTY_ASKED=$EPOCHREALTIME
95
+ [[ -n $REPLY ]]
96
+ }
97
+
98
+ __tt_ghostty_check() {
99
+ local REPLY shown conf=${TTHEME_CONFIG:h}/backgrounds/shown.conf
100
+ local -a at
101
+ integer n
102
+ __tt_ghostty_modules
103
+ for (( n = 0; n < 3; n++ )); do
104
+ at=("")
105
+ zstat -F %s.%N -A at +mtime -- $conf 2>/dev/null
106
+ shown=$at[1]
107
+ __tt_ghostty_ask || return 0
108
+ at=("")
109
+ zstat -F %s.%N -A at +mtime -- $conf 2>/dev/null
110
+ [[ $at[1] == "$shown" ]] && break
111
+ done
112
+ __tt_ghostty_take "$REPLY"
113
+ }
114
+
115
+ __tt_ghostty_take() {
116
+ local REPLY key pal
117
+ local -a ans rec at
118
+ local -F now=$EPOCHREALTIME
119
+ [[ $1 == gone ]] && return 1
120
+ if [[ $1 == none ]]; then
121
+ (( TTHEME_GHOSTTY_PID )) && ! kill -0 $TTHEME_GHOSTTY_PID 2>/dev/null && return 1
122
+ (( TTHEME_GHOSTTY_NONE )) || TTHEME_GHOSTTY_NONE_AT=$now
123
+ (( ++TTHEME_GHOSTTY_NONE ))
124
+ fi
125
+ if [[ $1 == blind ]] || { [[ $1 == none ]] && (( TTHEME_GHOSTTY_NONE >= 5 && now - TTHEME_GHOSTTY_NONE_AT >= 30 )) }; then
126
+ __tt_put $TTHEME_GHOSTTY_TABS/.blind "${TERM_PROGRAM_VERSION:--}" 2>/dev/null
127
+ return 1
128
+ fi
129
+ [[ $1 == none ]] && return 0
130
+ TTHEME_GHOSTTY_NONE=0
131
+ ans=(${=1})
132
+ if [[ $ans[1] != 1 || -z $ans[2] ]]; then
133
+ TTHEME_GHOSTTY_FRONT=""
134
+ return 0
135
+ fi
136
+ if [[ -n $ans[4] ]]; then
137
+ at=("")
138
+ zstat -F %s.%N -A at +mtime -- ${TTHEME_CONFIG:h}/backgrounds/shown.conf 2>/dev/null
139
+ [[ -n $at[1] ]] && (( at[1] > ans[4] )) && return 0
140
+ fi
141
+ __tt_ghostty_key $ans[2]
142
+ key=$REPLY
143
+ TTHEME_GHOSTTY_FRONT=$key TTHEME_GHOSTTY_IDLE=1
144
+ [[ -r $TTHEME_GHOSTTY_TABS/$key ]] || return 0
145
+ rec=(${=${"$(<$TTHEME_GHOSTTY_TABS/$key)"}})
146
+ [[ $rec[1] == "$ans[3]" ]] || TTHEME_GHOSTTY_IDLE=0
147
+ pal=${rec[3]#-}
148
+ [[ $rec[2] != - && -z $TTHEME_STARTUP ]] && pal=""
149
+ [[ -n $pal && -n ${TTHEME_PALETTE[$pal]} ]] || return 0
150
+ __tt_shown "$pal" force && __tt_reload_ghostty
151
+ return 0
152
+ }
153
+
154
+ __tt_ghostty_tick() {
155
+ local f key
156
+ local -a at rec
157
+ local -F now=$EPOCHREALTIME
158
+ integer seen
159
+ [[ -n $TTHEME_GHOSTTY_CODE ]] && zstat -F %s.%N -A at +mtime -- $TTHEME_HOME/adapters/ghostty.zsh 2>/dev/null && [[ $at[1] != "$TTHEME_GHOSTTY_CODE" ]] && return 1
160
+ zstat -F %s.%N -A at +mtime -- $TTHEME_HOME/palettes.zsh 2>/dev/null && [[ $at[1] != "$TTHEME_PALETTES_AT" ]] && __tt_palettes_load
161
+ if zstat -F %s.%N -A at +mtime -- $TTHEME_GHOSTTY_TABS/.blur 2>/dev/null && [[ $at[1] != "$TTHEME_GHOSTTY_BLUR" ]]; then
162
+ [[ -n $TTHEME_GHOSTTY_BLUR ]] && (( now - TTHEME_GHOSTTY_TOLD >= 1 )) && TTHEME_GHOSTTY_WANT=$now
163
+ TTHEME_GHOSTTY_BLUR=$at[1]
164
+ fi
165
+ for f in $TTHEME_GHOSTTY_TABS/[^.]*(N); do
166
+ key=${f:t}
167
+ rec=(${=${"$(<$f)"}})
168
+ zstat -F %s.%N -A at +atime -- ${rec[4]:-/nonexistent} 2>/dev/null || continue
169
+ if [[ $at[1] == "${TTHEME_GHOSTTY_SEEN[$key]}" ]]; then
170
+ TTHEME_GHOSTTY_RUN[$key]=0
171
+ continue
172
+ fi
173
+ seen=${+TTHEME_GHOSTTY_SEEN[$key]}
174
+ TTHEME_GHOSTTY_SEEN[$key]=$at[1]
175
+ (( seen )) || continue
176
+ if [[ $key == "$TTHEME_GHOSTTY_FRONT" ]]; then
177
+ TTHEME_GHOSTTY_HUSH=$now
178
+ elif (( ++TTHEME_GHOSTTY_RUN[$key] < 5 && now - TTHEME_GHOSTTY_TOLD >= 1 )); then
179
+ TTHEME_GHOSTTY_WANT=$now
180
+ fi
181
+ done
182
+ if (( ! TTHEME_GHOSTTY_WFD && TTHEME_GHOSTTY_HUSH && now - TTHEME_GHOSTTY_HUSH >= 0.3 && now - TTHEME_GHOSTTY_ASKED >= (TTHEME_GHOSTTY_IDLE ? 10 : 2) )); then
183
+ TTHEME_GHOSTTY_HUSH=0 TTHEME_GHOSTTY_WANT=$now
184
+ fi
185
+ (( TTHEME_GHOSTTY_WANT && now - TTHEME_GHOSTTY_ASKED >= 0.2 && ( ! TTHEME_GHOSTTY_WFD || now - TTHEME_GHOSTTY_WANT >= 0.4 ) )) || return 0
186
+ TTHEME_GHOSTTY_WANT=0
187
+ __tt_ghostty_check
188
+ }
189
+
190
+ __tt_ghostty_live() {
191
+ local f pid
192
+ integer live=0
193
+ (( TTHEME_GHOSTTY_PID )) && ! kill -0 $TTHEME_GHOSTTY_PID 2>/dev/null && return 1
194
+ for f in $TTHEME_GHOSTTY_TABS/[^.]*(N); do
195
+ pid=${"$(<$f)"%% *}
196
+ if [[ $pid == <-> ]] && kill -0 $pid 2>/dev/null; then
197
+ live=1
198
+ else
199
+ zf_rm -f -- $f 2>/dev/null
200
+ unset "TTHEME_GHOSTTY_SEEN[${f:t}]" "TTHEME_GHOSTTY_RUN[${f:t}]"
201
+ fi
202
+ done
203
+ (( live ))
204
+ }
205
+
206
+ __tt_ghostty_watch() {
207
+ (( TTHEME_GHOSTTY_PID )) || return 0
208
+ coproc osascript -l JavaScript -e $TTHEME_GHOSTTY_WATCH $TTHEME_GHOSTTY_PID 2>/dev/null
209
+ TTHEME_GHOSTTY_WATCHER=$!
210
+ exec {TTHEME_GHOSTTY_WFD}<&p
211
+ }
212
+
213
+ __tt_ghostty_heard() {
214
+ local chunk line
215
+ integer rc
216
+ sysread -t 0 -i $TTHEME_GHOSTTY_WFD chunk 2>/dev/null
217
+ rc=$?
218
+ if (( rc == 2 || rc == 5 )); then
219
+ exec {TTHEME_GHOSTTY_WFD}<&-
220
+ TTHEME_GHOSTTY_WFD=0 TTHEME_GHOSTTY_HEARD=""
221
+ return 0
222
+ fi
223
+ TTHEME_GHOSTTY_HEARD+=$chunk
224
+ while [[ $TTHEME_GHOSTTY_HEARD == *$'\n'* ]]; do
225
+ line=${TTHEME_GHOSTTY_HEARD%%$'\n'*}
226
+ TTHEME_GHOSTTY_HEARD=${TTHEME_GHOSTTY_HEARD#*$'\n'}
227
+ TTHEME_GHOSTTY_TOLD=$EPOCHREALTIME TTHEME_GHOSTTY_WANT=0
228
+ __tt_ghostty_take "$line" || return 1
229
+ done
230
+ return 0
231
+ }
232
+
233
+ __tt_ghostty_follower() {
234
+ emulate -L zsh
235
+ local fd REPLY
236
+ local -a at
237
+ __tt_ghostty_modules
238
+ zsystem flock -t 0 -f fd $TTHEME_GHOSTTY_TABS/.follow 2>/dev/null || return 0
239
+ trap '' HUP
240
+ zstat -F %s.%N -A at +mtime -- $TTHEME_HOME/adapters/ghostty.zsh 2>/dev/null && TTHEME_GHOSTTY_CODE=$at[1]
241
+ __tt_ghostty_owner $PPID
242
+ TTHEME_GHOSTTY_PID=$REPLY
243
+ __tt_ghostty_check || return 0
244
+ __tt_ghostty_watch
245
+ while [[ -e $TTHEME_HOME/palettes.zsh ]]; do
246
+ __tt_ghostty_tick || break
247
+ (( ++TTHEME_GHOSTTY_TICK % 50 )) || __tt_ghostty_live || break
248
+ if (( TTHEME_GHOSTTY_WFD )); then
249
+ zselect -t 50 -r $TTHEME_GHOSTTY_WFD && { __tt_ghostty_heard || break }
250
+ else
251
+ zselect -t 10
252
+ fi
253
+ done
254
+ (( TTHEME_GHOSTTY_WATCHER )) && kill $TTHEME_GHOSTTY_WATCHER 2>/dev/null
255
+ return 0
256
+ }
257
+
9
258
  __tt_reset_reloaded() {
10
259
  local REPLY
11
260
  integer i
@@ -21,6 +270,8 @@ __tt_reset_reloaded() {
21
270
 
22
271
  __tt_shown() {
23
272
  local dir=${TTHEME_CONFIG:h}/backgrounds was REPLY
273
+ __tt_ghostty_publish "$1"
274
+ [[ $2 == force ]] || (( TTHEME_FRONT || TTHEME_TMUX )) || return 1
24
275
  __tt_bg_shown
25
276
  was=$REPLY
26
277
  [[ $was == $1 ]] && return 1
@@ -29,6 +280,41 @@ __tt_shown() {
29
280
  __tt_put $dir/shown.conf "config-file = ?${${1/@/--}/\//--}.conf"
30
281
  }
31
282
 
283
+ __tt_ghostty_took() {
284
+ local f=${TTHEME_CONFIG:h}/backgrounds/shown.conf
285
+ local -A st
286
+ local -F t
287
+ zmodload -F zsh/datetime p:EPOCHREALTIME 2>/dev/null
288
+ t=$EPOCHREALTIME
289
+ while zstat -H st -F %s.%N -- $f 2>/dev/null && [[ ! $st[atime] > $st[mtime] ]]; do
290
+ (( EPOCHREALTIME - t < 0.1 )) || return 0
291
+ done
292
+ t=$EPOCHREALTIME
293
+ while (( EPOCHREALTIME - t < 0.003 )); do :; done
294
+ }
295
+
296
+ __tt_wear() {
297
+ local REPLY=$2 name
298
+ [[ -n $REPLY ]] || __tt_name_of "$1"
299
+ name=$REPLY
300
+ TTHEME_SPEC=$1
301
+ if [[ -n ${TTHEME_PALETTE[$name]} ]] && __tt_shown "$name" $3; then
302
+ TTHEME_GHOSTTY_SENT=0
303
+ __tt_reload
304
+ (( TTHEME_GHOSTTY_SENT )) && __tt_ghostty_took
305
+ __tt_apply "$1"
306
+ return 0
307
+ fi
308
+ __tt_apply "$1"
309
+ return 1
310
+ }
311
+
312
+ __tt_pv_claim() {
313
+ TTHEME_GHOSTTY_SENT=0
314
+ __tt_shown "$1" force && __tt_reload && (( TTHEME_GHOSTTY_SENT )) && __tt_ghostty_took
315
+ return 0
316
+ }
317
+
32
318
  __tt_bg_cells() {
33
319
  local REPLY resp line v f
34
320
  local -i px=2 py=2 fs=0 sc=2
package/shell/ttheme.zsh CHANGED
@@ -19,9 +19,21 @@ __tt_fresh() {
19
19
  [[ -e $TTHEME_HOME ]] || __tt_gone
20
20
  return 0
21
21
  fi
22
- [[ $at[1] == "$TTHEME_PALETTES_AT" ]] && return 0
23
- __tt_palettes_load && { __tt_follow "$start" && __tt_sync; __tt_reloaded }
24
- [[ -n $was && -z $TTHEME_STARTUP && -n $TTHEME_SPEC ]] && __tt_active && __tt_off_here
22
+ if [[ $at[1] != "$TTHEME_PALETTES_AT" ]]; then
23
+ __tt_palettes_load && { __tt_follow "$start" && __tt_sync; __tt_reloaded }
24
+ [[ -n $was && -z $TTHEME_STARTUP && -n $TTHEME_SPEC ]] && __tt_active && __tt_off_here
25
+ fi
26
+ at=("")
27
+ zstat -F %s.%N -A at +mtime -- $TTHEME_PINS_FILE 2>/dev/null
28
+ [[ $at[1] == "$TTHEME_PINS_AT" ]] && return 0
29
+ __tt_pins_load
30
+ __tt_active && __tt_dir_sync
31
+ }
32
+
33
+ __tt_prompt() {
34
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
35
+ (( TTHEME_FRONT < 0 )) || TTHEME_FRONT=0
36
+ __tt_fresh
25
37
  }
26
38
 
27
39
  __tt_follow() {
@@ -36,7 +48,7 @@ __tt_gone() {
36
48
  __tt_reload
37
49
  __tt_reset_reloaded $bg
38
50
  print -n $'\e[?1004l'
39
- for hook in precmd:__tt_fresh precmd:__tt_precmd precmd:__tt_prompted precmd:__tt_unmux preexec:__tt_preexec preexec:__tt_mux chpwd:__tt_chpwd; do
51
+ for hook in precmd:__tt_prompt precmd:__tt_precmd precmd:__tt_prompted precmd:__tt_unmux preexec:__tt_preexec preexec:__tt_mux chpwd:__tt_chpwd; do
40
52
  add-zsh-hook -d ${hook%%:*} ${hook#*:}
41
53
  done
42
54
  (( $+functions[add-zle-hook-widget] )) && add-zle-hook-widget -d line-init __tt_line_init
@@ -65,7 +77,7 @@ typeset -g TTHEME_CONFIG=${XDG_CONFIG_HOME:-$HOME/.config}/ttheme/config.zsh
65
77
  [[ -r $TTHEME_CONFIG ]] && source $TTHEME_CONFIG
66
78
 
67
79
  typeset -g TTHEME_PINS_FILE=${TTHEME_CONFIG:h}/pins
68
- typeset -g TTHEME_PINS_RAW=""
80
+ typeset -g TTHEME_PINS_RAW="" TTHEME_PINS_AT=""
69
81
  typeset -gA TTHEME_PINS=()
70
82
  typeset -g TTHEME_PIN="" TTHEME_PIN_SPEC="" TTHEME_BASE_SPEC=""
71
83
 
@@ -75,6 +87,9 @@ __tt_pins_load() {
75
87
  emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
76
88
  setopt localoptions extendedglob
77
89
  local raw="" line key name
90
+ local -a at=("")
91
+ zstat -F %s.%N -A at +mtime -- $TTHEME_PINS_FILE 2>/dev/null
92
+ TTHEME_PINS_AT=$at[1]
78
93
  [[ -r $TTHEME_PINS_FILE ]] && raw="$(<$TTHEME_PINS_FILE)"
79
94
  [[ $raw == "$TTHEME_PINS_RAW" ]] && return 0
80
95
  TTHEME_PINS_RAW=$raw
@@ -146,7 +161,7 @@ __tt_active() {
146
161
  }
147
162
 
148
163
  typeset -g TTHEME_HEARD=""
149
- typeset -gi TTHEME_RECHECK=0 TTHEME_FOCUS=0
164
+ typeset -gi TTHEME_RECHECK=0 TTHEME_FOCUS=0 TTHEME_FRONT=-1
150
165
 
151
166
  __tt_put() {
152
167
  local f=${1:A}
@@ -298,19 +313,21 @@ __tt_dir_rule() {
298
313
  __tt_dir_sync() {
299
314
  local REPLY="" spec=""
300
315
  __tt_dir_rule "$PWD" || REPLY=""
301
- [[ $REPLY == "$TTHEME_PIN" ]] && return 0
302
316
  if [[ -n $REPLY ]]; then
303
- [[ -n $TTHEME_PIN ]] || TTHEME_BASE_SPEC=$TTHEME_SPEC
304
317
  spec=${TTHEME_PALETTE[$TTHEME_PINS[$REPLY]]}
305
- elif [[ $TTHEME_SPEC == "$TTHEME_PIN_SPEC" ]]; then
306
- spec=$TTHEME_BASE_SPEC
307
- [[ -n $spec ]] || { __tt_osc_reset; TTHEME_SPEC= }
318
+ [[ $REPLY == "$TTHEME_PIN" && $spec == "$TTHEME_PIN_SPEC" ]] && return 0
319
+ [[ -n $TTHEME_PIN ]] || TTHEME_BASE_SPEC=$TTHEME_SPEC
320
+ else
321
+ [[ -n $TTHEME_PIN ]] || return 0
322
+ if [[ $TTHEME_SPEC == "$TTHEME_PIN_SPEC" ]]; then
323
+ spec=$TTHEME_BASE_SPEC
324
+ [[ -n $spec ]] || { __tt_osc_reset; TTHEME_SPEC= }
325
+ fi
308
326
  fi
309
327
  TTHEME_PIN=$REPLY TTHEME_PIN_SPEC=$spec
310
328
  [[ -n $spec && $spec != "$TTHEME_SPEC" ]] || return 0
311
- __tt_apply "$spec"
312
- TTHEME_SPEC=$spec
313
- __tt_sync
329
+ __tt_wear "$spec"
330
+ return 0
314
331
  }
315
332
 
316
333
  __tt_chpwd() {
@@ -353,6 +370,7 @@ __tt_rewear() {
353
370
 
354
371
  __tt_focus() {
355
372
  emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
373
+ TTHEME_FRONT=1
356
374
  __tt_fresh
357
375
  if (( TTHEME_TMUX )); then
358
376
  __tt_rewear
@@ -375,7 +393,8 @@ __tt_unmux() {
375
393
 
376
394
  __tt_blur() {
377
395
  emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
378
- :
396
+ TTHEME_FRONT=0
397
+ __tt_blurred
379
398
  }
380
399
 
381
400
  __tt_line_init() {
@@ -410,12 +429,14 @@ __tt_pins_write() {
410
429
  done
411
430
  if (( ! ${#lines} )); then
412
431
  rm -f $TTHEME_PINS_FILE || return 1
413
- TTHEME_PINS_RAW=""
432
+ TTHEME_PINS_RAW="" TTHEME_PINS_AT=""
414
433
  return 0
415
434
  fi
416
435
  mkdir -p ${TTHEME_PINS_FILE:h} || return 1
417
436
  __tt_put $TTHEME_PINS_FILE "${lines[@]}" || return 1
418
437
  TTHEME_PINS_RAW="$(<$TTHEME_PINS_FILE)"
438
+ local -a at
439
+ zstat -F %s.%N -A at +mtime -- $TTHEME_PINS_FILE 2>/dev/null && TTHEME_PINS_AT=$at[1]
419
440
  }
420
441
 
421
442
  __tt_pin_base() { REPLY=${${${1%/\*\*}:-/}:a} }
@@ -498,11 +519,11 @@ __tt_pin_save() {
498
519
  fi
499
520
  if [[ $spec != "$TTHEME_SPEC" ]]; then
500
521
  if [[ -n $spec ]]; then
501
- __tt_apply "$spec"
522
+ __tt_wear "$spec"
502
523
  else
503
524
  __tt_osc_reset
525
+ TTHEME_SPEC=
504
526
  fi
505
- TTHEME_SPEC=$spec
506
527
  fi
507
528
  __tt_dir_label "$base"
508
529
  k=$REPLY
@@ -1205,7 +1226,6 @@ __tt_catalog() {
1205
1226
  }
1206
1227
 
1207
1228
  __tt_switch() {
1208
- local spec
1209
1229
  __tt_cli "$@" || return
1210
1230
  [[ -r $TTHEME_HOME/palettes.zsh ]] && __tt_palettes_load && __tt_reloaded
1211
1231
  if ! __tt_active; then
@@ -1215,11 +1235,7 @@ __tt_switch() {
1215
1235
  if [[ $1 == off ]]; then
1216
1236
  __tt_off_here reload
1217
1237
  elif [[ -n $TTHEME_STARTUP ]]; then
1218
- spec=${TTHEME_PALETTE[$TTHEME_STARTUP]}
1219
- __tt_apply "$spec"
1220
- TTHEME_SPEC=$spec
1221
- __tt_shown "$TTHEME_STARTUP" force
1222
- __tt_reload
1238
+ __tt_wear "${TTHEME_PALETTE[$TTHEME_STARTUP]}" "$TTHEME_STARTUP" force || __tt_reload
1223
1239
  fi
1224
1240
  }
1225
1241
 
@@ -1321,9 +1337,7 @@ __tt_next() {
1321
1337
  __tt_rotate() {
1322
1338
  local REPLY
1323
1339
  __tt_next
1324
- __tt_apply "$REPLY"
1325
- TTHEME_SPEC=$REPLY
1326
- __tt_worn_shown force && __tt_reload
1340
+ __tt_wear "$REPLY" "" force
1327
1341
  __tt_announce
1328
1342
  }
1329
1343
 
@@ -2688,6 +2702,7 @@ __tt_preview() {
2688
2702
  done
2689
2703
  } always {
2690
2704
  local spec=${sel:+${TTHEME_PALETTE[$sel]}}
2705
+ [[ -n $spec && $mode != pin ]] && (( ! ${#bgedit} && ! ${#bgswap} )) && __tt_pv_claim "$sel"
2691
2706
  printf '\e[?2026h'
2692
2707
  __tt_pv_bg_close
2693
2708
  if [[ -n $spec ]]; then
@@ -2758,19 +2773,17 @@ ttheme() {
2758
2773
  case $1 in
2759
2774
  browse|list|add|remove|update|market|new|edit|check|share) __tt_catalog "$@"; return ;;
2760
2775
  on|off) __tt_switch "$@"; return ;;
2776
+ info) TTHEME_ZSH=$ZSH_VERSION __tt_cli "$@"; return ;;
2761
2777
  esac
2762
2778
  __tt_arity "$@" || return
2763
2779
  [[ $1 == config ]] && { __tt_config; return }
2764
2780
  (( ${#TTHEME_PALETTE} )) || { __tt_empty; return }
2765
2781
 
2766
- local REPLY spec
2782
+ local REPLY
2767
2783
  case $1 in
2768
2784
  use)
2769
2785
  __tt_resolve "$2" || return 1
2770
- spec=${TTHEME_PALETTE[$REPLY]}
2771
- __tt_apply "$spec"
2772
- TTHEME_SPEC=$spec
2773
- __tt_shown "$REPLY" force && __tt_reload
2786
+ __tt_wear "${TTHEME_PALETTE[$REPLY]}" "$REPLY" force
2774
2787
  __tt_announce ;;
2775
2788
  next) __tt_rotate ;;
2776
2789
  default)
@@ -2841,7 +2854,7 @@ fi
2841
2854
  zle -N __tt_line_init
2842
2855
  add-zle-hook-widget line-init __tt_line_init
2843
2856
  add-zsh-hook chpwd __tt_chpwd
2844
- add-zsh-hook precmd __tt_fresh
2857
+ add-zsh-hook precmd __tt_prompt
2845
2858
  if (( ! TTHEME_TMUX )); then
2846
2859
  add-zsh-hook preexec __tt_mux
2847
2860
  add-zsh-hook precmd __tt_unmux