@kecan0406/ttheme 1.1.5 → 1.1.6

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.
package/shell/ttheme.zsh CHANGED
@@ -3,6 +3,7 @@ typeset -g TTHEME_HOME=${${(%):-%x}:A:h} TTHEME_PALETTES_AT=""
3
3
  zmodload -F zsh/stat b:zstat 2>/dev/null
4
4
 
5
5
  __tt_palettes_load() {
6
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
6
7
  local -a at
7
8
  unset TTHEME_PALETTE TTHEME_GROUP TTHEME_CATALOG TTHEME_NATIVE TTHEME_SRC
8
9
  source $TTHEME_HOME/palettes.zsh || return 1
@@ -11,6 +12,7 @@ __tt_palettes_load() {
11
12
  }
12
13
 
13
14
  __tt_fresh() {
15
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
14
16
  local -a at
15
17
  local was=$TTHEME_STARTUP
16
18
  if ! zstat -F %s.%N -A at +mtime -- $TTHEME_HOME/palettes.zsh 2>/dev/null; then
@@ -64,6 +66,7 @@ typeset -g TTHEME_PIN="" TTHEME_PIN_SPEC="" TTHEME_BASE_SPEC=""
64
66
  __tt_tilde() { REPLY=${1/#$HOME\//\~/} }
65
67
 
66
68
  __tt_pins_load() {
69
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
67
70
  setopt localoptions extendedglob
68
71
  local raw="" line key name
69
72
  [[ -r $TTHEME_PINS_FILE ]] && raw="$(<$TTHEME_PINS_FILE)"
@@ -74,6 +77,7 @@ __tt_pins_load() {
74
77
  line=${line%%[[:space:]]##}
75
78
  [[ $line == [~/]*[[:space:]]* ]] || continue
76
79
  name=${line##*[[:space:]]} key=${${line%[[:space:]]*}%%[[:space:]]##}
80
+ [[ $key == \~ ]] && key=$HOME
77
81
  TTHEME_PINS[${key/#\~\//$HOME/}]=$name
78
82
  [[ -n ${TTHEME_PALETTE[$name]} ]] || print -u2 "ttheme: unknown palette '$name' pinned to $key"
79
83
  done
@@ -93,27 +97,30 @@ __tt_pins_load
93
97
 
94
98
  typeset -g TTHEME_STATE_DIR=${XDG_STATE_HOME:-$HOME/.local/state}/ttheme
95
99
 
96
- if [[ $TERM_PROGRAM == WarpTerminal ]]; then
97
- typeset -g TTHEME_ADAPTER=warp
98
- elif [[ -n $GHOSTTY_RESOURCES_DIR || $TERM_PROGRAM == ghostty ]]; then
99
- typeset -g TTHEME_ADAPTER=ghostty
100
- elif [[ -n $KITTY_WINDOW_ID ]]; then
101
- typeset -g TTHEME_ADAPTER=kitty
102
- elif [[ -n $WEZTERM_PANE ]]; then
103
- typeset -g TTHEME_ADAPTER=wezterm
104
- elif [[ -n $ALACRITTY_WINDOW_ID ]]; then
105
- typeset -g TTHEME_ADAPTER=alacritty
106
- elif [[ -n $ITERM_SESSION_ID || $TERM_PROGRAM == iTerm.app ]]; then
107
- typeset -g TTHEME_ADAPTER=iterm2
108
- elif [[ $TERM_PROGRAM == Apple_Terminal ]]; then
109
- typeset -g TTHEME_ADAPTER=terminal-app
110
- elif [[ -n $WT_SESSION && -z $TERM_PROGRAM ]]; then
111
- typeset -g TTHEME_ADAPTER=windows-terminal
112
- elif [[ $TERM == foot* ]]; then
113
- typeset -g TTHEME_ADAPTER=foot
114
- else
115
- typeset -g TTHEME_ADAPTER=unknown
116
- fi
100
+ () {
101
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
102
+ if [[ $TERM_PROGRAM == WarpTerminal ]]; then
103
+ typeset -g TTHEME_ADAPTER=warp
104
+ elif [[ -n $GHOSTTY_RESOURCES_DIR || $TERM_PROGRAM == ghostty ]]; then
105
+ typeset -g TTHEME_ADAPTER=ghostty
106
+ elif [[ -n $KITTY_WINDOW_ID ]]; then
107
+ typeset -g TTHEME_ADAPTER=kitty
108
+ elif [[ -n $WEZTERM_PANE ]]; then
109
+ typeset -g TTHEME_ADAPTER=wezterm
110
+ elif [[ -n $ALACRITTY_WINDOW_ID ]]; then
111
+ typeset -g TTHEME_ADAPTER=alacritty
112
+ elif [[ -n $ITERM_SESSION_ID || $TERM_PROGRAM == iTerm.app ]]; then
113
+ typeset -g TTHEME_ADAPTER=iterm2
114
+ elif [[ $TERM_PROGRAM == Apple_Terminal ]]; then
115
+ typeset -g TTHEME_ADAPTER=terminal-app
116
+ elif [[ -n $WT_SESSION && -z $TERM_PROGRAM ]]; then
117
+ typeset -g TTHEME_ADAPTER=windows-terminal
118
+ elif [[ $TERM == foot* ]]; then
119
+ typeset -g TTHEME_ADAPTER=foot
120
+ else
121
+ typeset -g TTHEME_ADAPTER=unknown
122
+ fi
123
+ }
117
124
 
118
125
  typeset -g TTHEME_GHOSTTY_PID="" TTHEME_TMUX=0 TTHEME_MUXED=0
119
126
 
@@ -151,6 +158,18 @@ __tt_active() {
151
158
  typeset -g TTHEME_HEARD=""
152
159
  typeset -gi TTHEME_RECHECK=0 TTHEME_FOCUS=0
153
160
 
161
+ __tt_put() {
162
+ local f=${1:A}
163
+ shift
164
+ if zmodload -F zsh/files b:zf_mv 2>/dev/null; then
165
+ print -rl -- "$@" >| $f.$$ && zf_mv -f -- $f.$$ $f && return 0
166
+ else
167
+ print -rl -- "$@" >| $f.$$ && command mv -f -- $f.$$ $f && return 0
168
+ fi
169
+ command rm -f -- $f.$$ 2>/dev/null
170
+ return 1
171
+ }
172
+
154
173
  __tt_recall() {
155
174
  local f=$TTHEME_STATE_DIR/colors.$TTHEME_ADAPTER line
156
175
  REPLY=""
@@ -165,7 +184,7 @@ __tt_remember() {
165
184
  [[ -n $1 && -z $TTHEME_PAINTED ]] && (( ! TTHEME_TMUX )) || return 0
166
185
  [[ -r $f && "$(<$f)" == "$line" ]] && return 0
167
186
  [[ -d $TTHEME_STATE_DIR ]] || mkdir -p $TTHEME_STATE_DIR 2>/dev/null
168
- print -r -- "$line" 2>/dev/null > $f
187
+ __tt_put $f "$line" 2>/dev/null
169
188
  }
170
189
 
171
190
  __tt_listen() {
@@ -274,11 +293,12 @@ __tt_dir_rule() {
274
293
  [[ -n ${TTHEME_PALETTE[$TTHEME_PINS[$k]]} ]] || continue
275
294
  base=${${k%/\*\*}:A}
276
295
  if [[ $k == *"/**" ]]; then
277
- [[ $p == "$base" || $p == "$base"/* ]] || continue
296
+ [[ $p == "$base" || $p == "${base%/}"/* ]] || continue
278
297
  else
279
298
  [[ $p == "$base" ]] || continue
280
299
  fi
281
- (( ${#base} > bestlen )) && { best=$k; bestlen=${#base} }
300
+ (( ${#base} > bestlen )) || [[ ${#base} == $bestlen && $k != *"/**" ]] || continue
301
+ best=$k bestlen=${#base}
282
302
  done
283
303
  [[ -n $best ]] || return 1
284
304
  REPLY=$best
@@ -304,6 +324,7 @@ __tt_dir_sync() {
304
324
  }
305
325
 
306
326
  __tt_chpwd() {
327
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
307
328
  __tt_pins_load
308
329
  __tt_dir_sync
309
330
  }
@@ -316,11 +337,17 @@ __tt_sync() {
316
337
  __tt_shown "$REPLY" && __tt_reload
317
338
  }
318
339
 
319
- __tt_precmd() { __tt_sync }
340
+ __tt_precmd() {
341
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
342
+ __tt_sync
343
+ }
320
344
 
321
345
  __tt_focus_on() { printf '\e[?1004h' }
322
346
 
323
- __tt_preexec() { printf '\e[?1004l' }
347
+ __tt_preexec() {
348
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
349
+ printf '\e[?1004l'
350
+ }
324
351
 
325
352
  __tt_rewear() {
326
353
  local REPLY
@@ -332,6 +359,7 @@ __tt_rewear() {
332
359
  }
333
360
 
334
361
  __tt_focus() {
362
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
335
363
  __tt_fresh
336
364
  if (( TTHEME_TMUX )); then
337
365
  __tt_rewear
@@ -340,17 +368,25 @@ __tt_focus() {
340
368
  fi
341
369
  }
342
370
 
343
- __tt_mux() { [[ ${${(z)3}[1]:t} == tmux ]] && TTHEME_MUXED=1 }
371
+ __tt_mux() {
372
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
373
+ [[ ${${(z)3}[1]:t} == tmux ]] && TTHEME_MUXED=1
374
+ }
344
375
 
345
376
  __tt_unmux() {
377
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
346
378
  (( TTHEME_MUXED )) || return 0
347
379
  __tt_rewear force
348
380
  TTHEME_MUXED=0
349
381
  }
350
382
 
351
- __tt_blur() { : }
383
+ __tt_blur() {
384
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
385
+ :
386
+ }
352
387
 
353
388
  __tt_line_init() {
389
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
354
390
  (( TTHEME_RECHECK )) && __tt_recheck
355
391
  __tt_typed
356
392
  (( TTHEME_FOCUS )) && __tt_focus_on
@@ -385,56 +421,673 @@ __tt_pins_write() {
385
421
  return 0
386
422
  fi
387
423
  mkdir -p ${TTHEME_PINS_FILE:h} || return 1
388
- print -rl -- "${lines[@]}" > $TTHEME_PINS_FILE || return 1
424
+ __tt_put $TTHEME_PINS_FILE "${lines[@]}" || return 1
389
425
  TTHEME_PINS_RAW="$(<$TTHEME_PINS_FILE)"
390
426
  }
391
427
 
428
+ __tt_pin_base() { REPLY=${${${1%/\*\*}:-/}:a} }
429
+
430
+ __tt_dir_label() {
431
+ if [[ $1 == "${HOME:a}" ]]; then
432
+ REPLY='~'
433
+ else
434
+ __tt_tilde "$1"
435
+ fi
436
+ }
437
+
438
+ __tt_pin_scope() {
439
+ if [[ $1 == *"/**" ]]; then
440
+ REPLY="and below"
441
+ else
442
+ REPLY="this directory"
443
+ fi
444
+ }
445
+
446
+ __tt_repo() {
447
+ local at=${1:a}
448
+ while [[ $at != / ]]; do
449
+ [[ -e $at/.git ]] && break
450
+ at=${at:h}
451
+ done
452
+ [[ $at != / && $at != "${HOME:a}" ]] || return 1
453
+ REPLY=$at
454
+ }
455
+
456
+ __tt_pin_cover() {
457
+ local k b best=""
458
+ local -i len=-1
459
+ for k in ${(k)TTHEME_PINS}; do
460
+ [[ $k == /* && $k == *"/**" && -n ${TTHEME_PALETTE[$TTHEME_PINS[$k]]} ]] || continue
461
+ __tt_pin_base "$k"
462
+ b=$REPLY
463
+ [[ $1 == "$b" || $1 == "${b%/}"/* ]] || continue
464
+ (( ${#b} > len )) && { best=$k len=${#b} }
465
+ done
466
+ [[ -n $best ]] || return 1
467
+ REPLY=$best
468
+ }
469
+
470
+ __tt_pin_scopes() {
471
+ local d=${1:a} k
472
+ plabel=(" This directory " " And below ") pkeys=("$d" "${d%/}/**") pkdef=2
473
+ if __tt_repo "$d" && [[ $REPLY != "$d" ]]; then
474
+ plabel+=(" Repository ") pkeys+=("$REPLY/**")
475
+ fi
476
+ for k in ${(k)TTHEME_PINS}; do
477
+ [[ $k == /* ]] || continue
478
+ __tt_pin_base "$k"
479
+ [[ $REPLY == "$d" ]] || continue
480
+ if [[ $k == *"/**" ]]; then pkdef=2; else pkdef=1; fi
481
+ done
482
+ }
483
+
392
484
  __tt_pin_save() {
393
- local name=$1 key=$PWD REPLY
394
- (( $2 == 2 )) && key="$PWD/**"
395
- unset "TTHEME_PINS[$PWD]" "TTHEME_PINS[$PWD/**]"
485
+ local name=$1 key=$2 k base spec REPLY
486
+ __tt_pin_base "$key"
487
+ base=$REPLY
488
+ for k in ${(k)TTHEME_PINS}; do
489
+ [[ $k == /* ]] || continue
490
+ __tt_pin_base "$k"
491
+ [[ $REPLY == "$base" ]] && unset "TTHEME_PINS[$k]"
492
+ done
396
493
  TTHEME_PINS[$key]=$name
397
494
  if ! __tt_pins_write; then
398
495
  print -u2 "ttheme pin: could not write $TTHEME_PINS_FILE"
399
496
  return 1
400
497
  fi
401
498
  [[ -n $TTHEME_PIN ]] || TTHEME_BASE_SPEC=$3
402
- TTHEME_PIN=$key TTHEME_PIN_SPEC=${TTHEME_PALETTE[$name]}
403
- __tt_tilde "$key"
499
+ if __tt_dir_rule "$PWD"; then
500
+ spec=${TTHEME_PALETTE[$TTHEME_PINS[$REPLY]]}
501
+ TTHEME_PIN=$REPLY TTHEME_PIN_SPEC=$spec
502
+ else
503
+ spec=$TTHEME_BASE_SPEC
504
+ TTHEME_PIN="" TTHEME_PIN_SPEC=""
505
+ fi
506
+ if [[ $spec != "$TTHEME_SPEC" ]]; then
507
+ if [[ -n $spec ]]; then
508
+ __tt_apply "$spec"
509
+ else
510
+ __tt_osc_reset
511
+ fi
512
+ TTHEME_SPEC=$spec
513
+ fi
514
+ __tt_dir_label "$base"
515
+ k=$REPLY
516
+ __tt_pin_scope "$key"
404
517
  if __tt_color; then
405
- printf '\033[2mPinned · %s → %s\033[0m\n' "$REPLY" "$name"
518
+ printf '\033[2mPinned · %s → %s · %s\033[0m\n' "$name" "$k" "$REPLY"
406
519
  else
407
- print -r -- "Pinned · $REPLY → $name"
520
+ print -r -- "Pinned · $name → $k · $REPLY"
521
+ fi
522
+ if [[ $TTHEME_PIN != "$key" ]]; then
523
+ __tt_here_line Here
524
+ print -r -- "$REPLY"
408
525
  fi
409
526
  }
410
527
 
411
- __tt_unpin() {
412
- local key REPLY
413
- for key in "$PWD" "$PWD/**"; do
414
- [[ -n ${TTHEME_PINS[$key]} ]] || continue
415
- unset "TTHEME_PINS[$key]"
416
- if ! __tt_pins_write; then
417
- print -u2 "ttheme unpin: could not write $TTHEME_PINS_FILE"
418
- return 1
419
- fi
420
- __tt_dir_sync
421
- __tt_tilde "$key"
528
+ __tt_unpin_drop() {
529
+ local k REPLY where
530
+ local -a gone=()
531
+ for k in "$@"; do
532
+ [[ -n ${TTHEME_PINS[$k]} ]] || continue
533
+ __tt_pin_base "$k"
534
+ __tt_dir_label "$REPLY"
535
+ where=$REPLY
536
+ __tt_pin_scope "$k"
537
+ gone+=("Unpinned · ${TTHEME_PINS[$k]} on $where · $REPLY")
538
+ unset "TTHEME_PINS[$k]"
539
+ done
540
+ if ! __tt_pins_write; then
541
+ print -u2 "ttheme unpin: could not write $TTHEME_PINS_FILE"
542
+ return 1
543
+ fi
544
+ __tt_dir_sync
545
+ for k in $gone; do
422
546
  if __tt_color; then
423
- printf '\033[2mUnpinned · %s\033[0m\n' "$REPLY"
547
+ print -r -- $'\e[2m'"$k"$'\e[0m'
424
548
  else
425
- print -r -- "Unpinned · $REPLY"
549
+ print -r -- "$k"
426
550
  fi
427
- return 0
428
551
  done
429
- if __tt_dir_rule "$PWD"; then
430
- __tt_tilde "$REPLY"
431
- print -u2 "ttheme unpin: nothing pinned here — $REPLY covers this directory"
552
+ __tt_here_line Here
553
+ print -r -- "$REPLY"
554
+ }
555
+
556
+ __tt_unpin_then() {
557
+ local k
558
+ local -A TTHEME_PINS=("${(@kv)TTHEME_PINS}")
559
+ for k in "${@:2}"; do
560
+ unset "TTHEME_PINS[$k]"
561
+ done
562
+ __tt_here_fit "Then here" $1
563
+ }
564
+
565
+ __tt_key() {
566
+ local c seq=""
567
+ REPLY=""
568
+ read -sk 1 c || return 1
569
+ case $c in
570
+ $'\e')
571
+ if ! read -sk 1 -t 0.05 c; then
572
+ REPLY=esc
573
+ elif [[ $c == '[' || $c == O ]]; then
574
+ while read -sk 1 -t 0.05 c; do
575
+ seq+=$c
576
+ [[ $c == [A-Za-z~] ]] && break
577
+ done
578
+ case $seq in
579
+ C) REPLY=right ;;
580
+ D) REPLY=left ;;
581
+ Z) REPLY=stab ;;
582
+ *) REPLY=nop ;;
583
+ esac
584
+ else
585
+ REPLY=nop
586
+ fi
587
+ ;;
588
+ $'\t') REPLY=tab ;;
589
+ $'\r'|$'\n') REPLY=enter ;;
590
+ q|$'\x03') REPLY=esc ;;
591
+ *) REPLY=$c ;;
592
+ esac
593
+ }
594
+
595
+ __tt_unpin_block() {
596
+ local b=$'\e[1m' d=$'\e[2m' z=$'\e[0m' on=$'\e[7;1m' bar plain keys top k home=${HOME:a} here=${PWD:a} htip="" REPLY
597
+ local -A own=() below=() parent=() mark=() tstrip=()
598
+ local -a roots=() lp=() lpw=() ll=() lat=() lpal=() lnote=() lflag=() reply gone=(${(f)drops[$1]})
599
+ local -i i width=$(( (COLUMNS > 0 ? COLUMNS : 80) - 1 )) room=$(( (LINES > 0 ? LINES : 24) - 3 ))
600
+ (( color )) || b= d= z= on=
601
+ if (( color )); then
602
+ bar=$'\e[7;1m UNPIN \e[0m ' plain=" UNPIN "
432
603
  else
604
+ bar="[UNPIN]" plain=$bar
605
+ fi
606
+ for (( i = 1; i <= ${#lab}; i++ )); do
607
+ plain+=" ${lab[i]}"
608
+ if (( i != $1 )); then
609
+ bar+=" "$d${lab[i]}$z
610
+ elif (( color )); then
611
+ bar+=" "$on${lab[i]}$z
612
+ else
613
+ bar+=" [${${lab[i]# }% }]"
614
+ fi
615
+ done
616
+ keys="←→ choose · enter unpin · esc keep"
617
+ (( ${(m)#plain} + 3 + ${(m)#keys} <= width )) && bar+=" "$d$keys$z
618
+ __tt_clip "$bar" $width
619
+ bar=$REPLY
620
+ for k in $gone; do
621
+ __tt_pin_base "$k"
622
+ mark[$REPLY]=drop
623
+ done
624
+ top=${PWD:a}
625
+ [[ -n $cover ]] && __tt_pin_base "$cover" && top=$REPLY
626
+ __tt_map_build "$top"
627
+ __tt_map_fit $width
628
+ (( ${#reply} > room )) && reply=("${(@)reply[1,room-1]}" " …")
629
+ block=("$bar" "${reply[@]}")
630
+ __tt_unpin_then $width $gone
631
+ block+=("${reply[@]}")
632
+ }
633
+
634
+ __tt_unpin_paint() {
635
+ local out=$'\r'
636
+ (( $1 > 1 )) && out+=$'\e['$(( $1 - 1 ))'A'
637
+ out+=${(pj:\e[K\n:)block}$'\e[K\e[J'
638
+ print -rn -- "$out"
639
+ }
640
+
641
+ __tt_unpin_choose() {
642
+ local tty="" REPLY
643
+ local -i n=0 at=1
644
+ local -a block=()
645
+ chosen=0
646
+ {
647
+ tty=$(stty -g 2>/dev/null && stty -echo -icanon min 1 time 0 2>/dev/null)
648
+ print -rn -- $'\e[?25l'
649
+ while :; do
650
+ __tt_unpin_block $at
651
+ __tt_unpin_paint $n
652
+ n=${#block}
653
+ __tt_key || break
654
+ case $REPLY in
655
+ left|stab) at=$(( (at + ${#lab} - 2) % ${#lab} + 1 )) ;;
656
+ right|tab) at=$(( at % ${#lab} + 1 )) ;;
657
+ enter) chosen=$at; break ;;
658
+ esc) break ;;
659
+ esac
660
+ done
661
+ } always {
662
+ REPLY=$'\r'
663
+ (( n > 1 )) && REPLY+=$'\e['$(( n - 1 ))'A'
664
+ print -rn -- "$REPLY"$'\e[J\e[?25h'
665
+ [[ -n $tty ]] && stty "$tty" 2>/dev/null
666
+ }
667
+ (( chosen ))
668
+ }
669
+
670
+ __tt_unpin_options() {
671
+ local d=${PWD:a} k REPLY
672
+ local -a both
673
+ mine=() under=() lab=() drops=() cover=""
674
+ for k in ${(k)TTHEME_PINS}; do
675
+ [[ $k == /* ]] || continue
676
+ __tt_pin_base "$k"
677
+ if [[ $REPLY == "$d" ]]; then
678
+ mine+=("$k")
679
+ elif [[ $REPLY == "${d%/}"/* ]]; then
680
+ under+=("$k")
681
+ fi
682
+ done
683
+ [[ $d != / ]] && __tt_pin_cover "${d:h}" && cover=$REPLY
684
+ if (( ${#mine} )); then
685
+ lab+=(" This directory ") drops+=("${(pj:\n:)mine}")
686
+ fi
687
+ if (( ${#under} )); then
688
+ both=($mine $under)
689
+ lab+=(" And below ") drops+=("${(pj:\n:)both}")
690
+ fi
691
+ if [[ -n $cover ]]; then
692
+ __tt_pin_base "$cover"
693
+ __tt_dir_label "$REPLY"
694
+ lab+=(" $REPLY ") drops+=("$cover")
695
+ fi
696
+ return 0
697
+ }
698
+
699
+ __tt_unpin() {
700
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
701
+ local d k cover="" REPLY
702
+ local -a mine=() under=() lab=() drops=()
703
+ local -i chosen=0 color=0
704
+ __tt_color && color=1
705
+ __tt_pins_load
706
+ if (( $# )); then
707
+ d=$1
708
+ [[ $d == \~ || $d == \~/* ]] && d=$HOME${d#\~}
709
+ d=${d:a}
710
+ for k in ${(k)TTHEME_PINS}; do
711
+ [[ $k == /* ]] || continue
712
+ __tt_pin_base "$k"
713
+ [[ $REPLY == "$d" ]] && mine+=("$k")
714
+ done
715
+ if (( ${#mine} )); then
716
+ __tt_unpin_drop $mine
717
+ return
718
+ fi
719
+ __tt_dir_label "$d"
720
+ k=$REPLY
721
+ if __tt_pin_cover "$d"; then
722
+ __tt_pin_base "$REPLY"
723
+ __tt_dir_label "$REPLY"
724
+ print -u2 -r -- "ttheme unpin: nothing pinned to $k — the pin on $REPLY and below covers it"
725
+ else
726
+ print -u2 -r -- "ttheme unpin: nothing pinned to $k"
727
+ fi
728
+ return 1
729
+ fi
730
+ __tt_unpin_options
731
+ if (( ! ${#lab} )); then
433
732
  print -u2 "ttheme unpin: nothing pinned here"
733
+ return 1
734
+ fi
735
+ if (( ${#lab} == 1 && ${#mine} )); then
736
+ __tt_unpin_drop $mine
737
+ return
738
+ fi
739
+ if [[ ! -t 0 || ! -t 1 ]]; then
740
+ if (( ${#mine} )); then
741
+ __tt_unpin_drop $mine
742
+ return
743
+ fi
744
+ if [[ -n $cover ]]; then
745
+ __tt_pin_base "$cover"
746
+ __tt_dir_label "$REPLY"
747
+ print -u2 -r -- "ttheme unpin: nothing pinned here — the pin on $REPLY and below covers it: ttheme unpin $REPLY"
748
+ elif (( ${#under} == 1 )); then
749
+ print -u2 -r -- "ttheme unpin: nothing pinned here — 1 pin below it: ttheme pins maps it"
750
+ else
751
+ print -u2 -r -- "ttheme unpin: nothing pinned here — ${#under} pins below it: ttheme pins maps them"
752
+ fi
753
+ return 1
434
754
  fi
755
+ __tt_unpin_choose || return 1
756
+ __tt_unpin_drop ${(f)drops[chosen]}
757
+ }
758
+
759
+ __tt_clip() {
760
+ local c out=""
761
+ local -i i n=${#1} w=0 cw
762
+ REPLY=$1
763
+ for (( i = 1; i <= n; i++ )); do
764
+ c=${1[i]}
765
+ if [[ $c == $'\e' ]]; then
766
+ while (( i < n )) && [[ ${1[i]} != [A-Za-z] || ${1[i]} == $'\e' ]]; do (( i++ )); done
767
+ continue
768
+ fi
769
+ (( w += ${(m)#c} ))
770
+ done
771
+ (( w <= $2 )) && return 0
772
+ w=0
773
+ for (( i = 1; i <= n; i++ )); do
774
+ c=${1[i]}
775
+ if [[ $c == $'\e' ]]; then
776
+ out+=$c
777
+ while (( i < n )); do
778
+ (( i++ ))
779
+ out+=${1[i]}
780
+ [[ ${1[i]} == [A-Za-z] ]] && break
781
+ done
782
+ continue
783
+ fi
784
+ cw=${(m)#c}
785
+ (( w + cw > $2 - 1 )) && break
786
+ out+=$c
787
+ (( w += cw ))
788
+ done
789
+ REPLY=$out…
790
+ [[ $1 == *$'\e'* ]] && REPLY+=$'\e[0m'
435
791
  return 1
436
792
  }
437
793
 
794
+ __tt_map_tip() {
795
+ local -a p=(${=TTHEME_PALETTE[$1]})
796
+ local c=${p[3]#\#}
797
+ REPLY=""
798
+ (( color && ${#p} >= 20 )) || return 0
799
+ printf -v REPLY '\e[38;2;%d;%d;%dm' $((16#${c:0:2})) $((16#${c:2:2})) $((16#${c:4:2}))
800
+ }
801
+
802
+ __tt_map_chip() {
803
+ local -a p=(${=TTHEME_PALETTE[$1]})
804
+ local bg=${p[1]#\#} fg=${p[2]#\#}
805
+ if (( ! color )); then
806
+ printf -v REPLY '%-*s' $2 "$1"
807
+ elif (( ${#p} >= 20 )); then
808
+ printf -v REPLY '\e[48;2;%d;%d;%d;38;2;%d;%d;%dm %-*s \e[0m' $((16#${bg:0:2})) $((16#${bg:2:2})) $((16#${bg:4:2})) \
809
+ $((16#${fg:0:2})) $((16#${fg:2:2})) $((16#${fg:4:2})) $2 "$1"
810
+ else
811
+ printf -v REPLY '\e[2m %-*s \e[0m' $2 "$1"
812
+ fi
813
+ }
814
+
815
+ __tt_here_line() {
816
+ local rule name where
817
+ if ! __tt_dir_rule "$PWD"; then
818
+ reply=("$1 · no pin" "the tab's own palette")
819
+ REPLY="$1 · no pin — the tab's own palette"
820
+ return 0
821
+ fi
822
+ rule=$REPLY name=$TTHEME_PINS[$REPLY]
823
+ __tt_pin_base "$rule"
824
+ if [[ ${REPLY:A} == "${PWD:A}" ]]; then
825
+ where="this directory"
826
+ else
827
+ __tt_dir_label "$REPLY"
828
+ where=$REPLY
829
+ fi
830
+ [[ $rule == *"/**" ]] && where+=" and below"
831
+ __tt_map_chip "$name" ${#name}
832
+ reply=("$1 · $REPLY" "pinned to $where")
833
+ REPLY="$1 · $REPLY pinned to $where"
834
+ }
835
+
836
+ __tt_here_fit() {
837
+ __tt_here_line "$1"
838
+ __tt_clip "$REPLY" $2 && { reply=("$REPLY"); return 0 }
839
+ __tt_clip "${reply[1]}" $2
840
+ reply[1]=$REPLY
841
+ __tt_clip " ${reply[2]}" $2
842
+ reply[2]=$REPLY
843
+ }
844
+
845
+ __tt_map_under() {
846
+ local b name
847
+ local -i len=-1
848
+ REPLY=""
849
+ for b in ${(k)below}; do
850
+ name=${below[$b]}
851
+ [[ -n ${TTHEME_PALETTE[$name]} && ( $1 == "$b" || $1 == "${b%/}"/* ) ]] || continue
852
+ (( ${#b} > len )) && { REPLY=$name len=${#b} }
853
+ done
854
+ }
855
+
856
+ __tt_map_link() {
857
+ local at=$1 root=/
858
+ if [[ -n $top ]]; then
859
+ root=$top
860
+ elif [[ $at == "$home" || $at == "$home"/* ]]; then
861
+ root=$home
862
+ fi
863
+ (( ${roots[(Ie)$root]} )) || roots+=("$root")
864
+ while [[ $at != "$root" && -z ${parent[$at]} ]]; do
865
+ parent[$at]=${at:h}
866
+ at=${at:h}
867
+ done
868
+ }
869
+
870
+ __tt_map_chain() {
871
+ local at=$1 lbl=$2
872
+ local -a ks
873
+ while [[ -z ${own[$at]}${below[$at]} && $at != "$here" ]]; do
874
+ ks=(${(k)parent[(Re)$at]})
875
+ (( ${#ks} == 1 )) || break
876
+ at=$ks[1] lbl+=/${at:t}
877
+ done
878
+ reply=("$at" "${lbl:-/}")
879
+ }
880
+
881
+ __tt_map_line() {
882
+ local at=$1 name=${own[$1]:-${below[$1]}} note="" flag=""
883
+ if [[ -n $name ]]; then
884
+ if [[ -n ${own[$at]} ]]; then
885
+ note="this directory"
886
+ [[ -n ${below[$at]} ]] && note+=" · ${below[$at]} below"
887
+ else
888
+ note="and below"
889
+ fi
890
+ [[ -n ${TTHEME_PALETTE[$name]} ]] || flag="not installed"
891
+ [[ -d $at ]] || flag+="${flag:+ · }no such directory"
892
+ fi
893
+ lp+=("$2") lpw+=($3) ll+=("$4") lat+=("$at") lpal+=("$name") lnote+=("$note") lflag+=("$flag")
894
+ }
895
+
896
+ __tt_map_walk() {
897
+ local at=$1 pre=$2 tip z=$'\e[0m' REPLY
898
+ local -i i
899
+ local -a ks=(${(oi)${(k)parent[(Re)$1]}}) reply
900
+ (( color )) || z=
901
+ __tt_map_under "$at"
902
+ __tt_map_tip "$REPLY"
903
+ tip=$REPLY
904
+ (( color )) && [[ -z $tip ]] && tip=$'\e[2m'
905
+ for (( i = 1; i <= ${#ks}; i++ )); do
906
+ __tt_map_chain "$ks[i]" "${ks[i]:t}"
907
+ if (( i < ${#ks} )); then
908
+ __tt_map_line "$reply[1]" "$pre$tip├─ $z" $(( $3 + 3 )) "$reply[2]"
909
+ __tt_map_walk "$reply[1]" "$pre$tip│ $z" $(( $3 + 3 ))
910
+ else
911
+ __tt_map_line "$reply[1]" "$pre$tip└─ $z" $(( $3 + 3 )) "$reply[2]"
912
+ __tt_map_walk "$reply[1]" "$pre " $(( $3 + 3 ))
913
+ fi
914
+ done
915
+ }
916
+
917
+ __tt_map_build() {
918
+ local top=$1 k base root REPLY
919
+ local -a order=()
920
+ own=() below=() parent=() roots=() lp=() lpw=() ll=() lat=() lpal=() lnote=() lflag=() htip=""
921
+ for k in ${(k)TTHEME_PINS}; do
922
+ [[ $k == /* ]] || continue
923
+ __tt_pin_base "$k"
924
+ base=$REPLY
925
+ if [[ $k == *"/**" ]]; then
926
+ below[$base]=$TTHEME_PINS[$k]
927
+ else
928
+ own[$base]=$TTHEME_PINS[$k]
929
+ fi
930
+ [[ -z $top || $base == "$top" || $base == "${top%/}"/* ]] && __tt_map_link "$base"
931
+ done
932
+ [[ -z $top || $here == "$top" || $here == "${top%/}"/* ]] && __tt_map_link "$here"
933
+ __tt_dir_rule "$PWD" && __tt_map_tip "$TTHEME_PINS[$REPLY]" && htip=$REPLY
934
+ if [[ -n $top ]]; then
935
+ order=("$top")
936
+ else
937
+ (( ${roots[(Ie)$home]} )) && order+=("$home")
938
+ [[ $home != / ]] && (( ${roots[(Ie)/]} )) && order+=(/)
939
+ fi
940
+ for root in $order; do
941
+ if [[ $root == / ]]; then
942
+ __tt_map_chain / ""
943
+ else
944
+ __tt_dir_label "$root"
945
+ __tt_map_chain "$root" "$REPLY"
946
+ fi
947
+ __tt_map_line "$reply[1]" "" 0 "$reply[2]"
948
+ __tt_map_walk "$reply[1]" "" 0
949
+ done
950
+ }
951
+
952
+ __tt_map_fit() {
953
+ local b=$'\e[1m' d=$'\e[2m' y=$'\e[33m' r=$'\e[31m' z=$'\e[0m' lab line tag tip note REPLY
954
+ local -i width=$1 i lvl maxw namew need wr cap
955
+ local -a lw=() rw=() cut=()
956
+ (( color )) || b= d= y= r= z=
957
+ reply=()
958
+ for (( i = 1; i <= ${#ll}; i++ )); do
959
+ lw+=($(( lpw[i] + ${(m)#ll[i]} )))
960
+ [[ ${lat[i]} == "$here" ]] && (( lw[i] += 7 ))
961
+ (( ${#lpal[i]} > namew )) && namew=${#lpal[i]}
962
+ done
963
+ for (( lvl = 0; lvl < 4; lvl++ )); do
964
+ rw=() maxw=0 need=0
965
+ for (( i = 1; i <= ${#ll}; i++ )); do
966
+ wr=0
967
+ if [[ -n $lpal[i] ]]; then
968
+ wr=$(( 2 + namew + 2 * color ))
969
+ (( lvl == 0 && color )) && (( wr += 13 ))
970
+ note=${lnote[i]}
971
+ (( lvl == 2 )) && note=${${note//this directory/only}//and below/below}
972
+ (( lvl < 3 )) && (( wr += 2 + ${(m)#note} ))
973
+ [[ -n $lflag[i] ]] && (( wr += 3 + ${(m)#lflag[i]} ))
974
+ tag=${mark[$lat[i]]}
975
+ [[ $tag == new ]] && (( wr += 5 ))
976
+ [[ $tag == drop ]] && (( wr += 9 ))
977
+ (( lw[i] > maxw )) && maxw=$lw[i]
978
+ fi
979
+ rw+=($wr)
980
+ done
981
+ for (( i = 1; i <= ${#ll}; i++ )); do
982
+ if (( rw[i] )); then
983
+ (( maxw + rw[i] > need )) && need=$(( maxw + rw[i] ))
984
+ else
985
+ (( lw[i] > need )) && need=$lw[i]
986
+ fi
987
+ done
988
+ (( need <= width )) && break
989
+ done
990
+ (( lvl > 3 )) && lvl=3
991
+ cut=()
992
+ if (( need > width )); then
993
+ cap=0
994
+ for (( i = 1; i <= ${#ll}; i++ )); do
995
+ (( rw[i] > cap )) && cap=$rw[i]
996
+ done
997
+ cap=$(( width - cap ))
998
+ maxw=0
999
+ for (( i = 1; i <= ${#ll}; i++ )); do
1000
+ wr=$(( rw[i] ? cap : width ))
1001
+ lab=${ll[i]}
1002
+ if (( lw[i] > wr )); then
1003
+ while [[ -n $lab ]] && (( lw[i] - ${(m)#ll[i]} + ${(m)#lab} + 1 > wr )); do
1004
+ lab=${lab[2,-1]}
1005
+ done
1006
+ lab=…$lab
1007
+ lw[i]=$(( lw[i] - ${(m)#ll[i]} + ${(m)#lab} ))
1008
+ fi
1009
+ cut+=("$lab")
1010
+ (( rw[i] && lw[i] > maxw )) && maxw=$lw[i]
1011
+ done
1012
+ else
1013
+ cut=("${ll[@]}")
1014
+ fi
1015
+ for (( i = 1; i <= ${#ll}; i++ )); do
1016
+ if [[ -n $lpal[i] ]]; then
1017
+ line=${lp[i]}$b${cut[i]}$z
1018
+ else
1019
+ line=${lp[i]}$d${cut[i]}$z
1020
+ fi
1021
+ [[ ${lat[i]} == "$here" ]] && line+=" "$htip$b"← here"$z
1022
+ if [[ -n $lpal[i] ]]; then
1023
+ __tt_map_chip "$lpal[i]" $namew
1024
+ line+=${(l:maxw - lw[i] + 2:: :)}$REPLY
1025
+ if (( lvl == 0 && color )); then
1026
+ if [[ -n ${TTHEME_PALETTE[$lpal[i]]} ]]; then
1027
+ [[ -n ${tstrip[$lpal[i]]} ]] || __tt_pv_strip "$lpal[i]"
1028
+ line+=" "${tstrip[$lpal[i]]}
1029
+ else
1030
+ line+=${(l:13:: :)}
1031
+ fi
1032
+ fi
1033
+ note=${lnote[i]}
1034
+ (( lvl == 2 )) && note=${${note//this directory/only}//and below/below}
1035
+ (( lvl < 3 )) && line+=" "$d$note$z
1036
+ [[ -n $lflag[i] ]] && line+=$y" · "$lflag[i]$z
1037
+ tag=${mark[$lat[i]]}
1038
+ if [[ $tag == new ]]; then
1039
+ __tt_map_tip "$lpal[i]"
1040
+ line+=" "$b$REPLY"new"$z
1041
+ elif [[ $tag == drop ]]; then
1042
+ line+=" "$r"✕ unpin"$z
1043
+ fi
1044
+ fi
1045
+ while [[ $line == *' ' ]]; do line=${line% }; done
1046
+ reply+=("$line")
1047
+ done
1048
+ }
1049
+
1050
+ __tt_map_tree() {
1051
+ local home=${HOME:a} here=${PWD:a} htip="" REPLY
1052
+ local -A own=() below=() parent=() mark=() tstrip=()
1053
+ local -a roots=() lp=() lpw=() ll=() lat=() lpal=() lnote=() lflag=() reply
1054
+ local -i width=$(( COLUMNS > 0 ? COLUMNS : 80 ))
1055
+ __tt_map_build ""
1056
+ __tt_map_fit $width
1057
+ print -rl -- "${reply[@]}"
1058
+ print
1059
+ __tt_here_line Here
1060
+ __tt_clip "$REPLY" $width
1061
+ print -r -- "$REPLY"
1062
+ __tt_tilde "$TTHEME_PINS_FILE"
1063
+ if (( color )); then
1064
+ __tt_clip $'\e[2m'"ttheme pin picks one here · ttheme unpin drops one · $REPLY"$'\e[0m' $width
1065
+ else
1066
+ __tt_clip "ttheme pin picks one here · ttheme unpin drops one · $REPLY" $width
1067
+ fi
1068
+ print -r -- "$REPLY"
1069
+ }
1070
+
1071
+ __tt_pins_map() {
1072
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
1073
+ local k REPLY
1074
+ local -i color=0
1075
+ __tt_pins_load
1076
+ if [[ ! -t 1 ]]; then
1077
+ for k in ${(oi)${(k)TTHEME_PINS}}; do
1078
+ __tt_tilde "$k"
1079
+ print -r -- "$REPLY"$'\t'"$TTHEME_PINS[$k]"
1080
+ done
1081
+ return 0
1082
+ fi
1083
+ if (( ! ${#TTHEME_PINS} )); then
1084
+ print -r -- 'No pins yet — `ttheme pin` picks a palette for this directory'
1085
+ return 0
1086
+ fi
1087
+ __tt_color && color=1
1088
+ __tt_map_tree
1089
+ }
1090
+
438
1091
  __tt_keep() {
439
1092
  local note
440
1093
  note=$(__tt_cli default "$1") || return 1
@@ -577,7 +1230,7 @@ __tt_config() {
577
1230
  local blur
578
1231
  if [[ ! -e $TTHEME_CONFIG ]]; then
579
1232
  mkdir -p ${TTHEME_CONFIG:h} || return 1
580
- print -r -- "$TTHEME_CONFIG_TEMPLATE" > $TTHEME_CONFIG
1233
+ __tt_put $TTHEME_CONFIG "$TTHEME_CONFIG_TEMPLATE"
581
1234
  fi
582
1235
  blur=${(M)${(@f)"$(<$TTHEME_CONFIG)"}:#': ${TTHEME_BG_BLUR:='*}
583
1236
  ${=${VISUAL:-${EDITOR:-vi}}} $TTHEME_CONFIG || return
@@ -607,7 +1260,7 @@ __tt_config_write() {
607
1260
  local -i i
608
1261
  if [[ ! -e $TTHEME_CONFIG ]]; then
609
1262
  mkdir -p ${TTHEME_CONFIG:h} 2>/dev/null || return 1
610
- print -r -- "$TTHEME_CONFIG_TEMPLATE" 2>/dev/null > $TTHEME_CONFIG || return 1
1263
+ __tt_put $TTHEME_CONFIG "$TTHEME_CONFIG_TEMPLATE" 2>/dev/null || return 1
611
1264
  fi
612
1265
  [[ -r $TTHEME_CONFIG && -w $TTHEME_CONFIG ]] || return 1
613
1266
  for line in "${(@f)$(<$TTHEME_CONFIG)}"; do
@@ -631,7 +1284,7 @@ __tt_config_write() {
631
1284
  __tt_config_line $name ${want[$name]}
632
1285
  out+=("" "$doc" "$REPLY")
633
1286
  done
634
- print -rl -- "${out[@]}" 2>/dev/null > $TTHEME_CONFIG
1287
+ __tt_put $TTHEME_CONFIG "${out[@]}" 2>/dev/null
635
1288
  }
636
1289
 
637
1290
  __tt_resolve() {
@@ -979,7 +1632,7 @@ __tt_pv_row() {
979
1632
  __tt_pv_bg_title() {
980
1633
  local by=${bgby[$tpick]} ref=${bgfrom[$tpick]} url=${bgurl[$tpick]} mark="" sgr=""
981
1634
  local -i room=$1
982
- [[ -n $ref && $url == http(s|)://* && $TTHEME_ADAPTER == (ghostty|kitty|wezterm|alacritty|iterm2|windows-terminal|foot) ]] && mark="⧉ "
1635
+ [[ -n $ref && $url == http(s|)://* ]] && (( ${TTHEME_LINKS[(Ie)$TTHEME_ADAPTER]} )) && mark="⧉ "
983
1636
  REPLY=Background${by:+ · $by}${ref:+ · $mark$ref}$2
984
1637
  (( ${#REPLY} > room )) && REPLY=Background${by:+ · $by}
985
1638
  (( ${#REPLY} > room )) && REPLY=Background
@@ -1011,17 +1664,21 @@ __tt_pv_foot() {
1011
1664
  if (( help )); then
1012
1665
  badge=KEYS right=$b"? esc"$z$d" close"$z
1013
1666
  elif [[ -n $pick ]]; then
1014
- local a=$plabel[1] c=$plabel[2]
1015
- if (( color )); then
1016
- if (( pk == 1 )); then a=$on$a$z c=$d$c$z; else a=$d$a$z c=$on$c$z; fi
1017
- else
1018
- if (( pk == 1 )); then a="[${${a# }% }]"; else c="[${${c# }% }]"; fi
1019
- fi
1020
- badge=APPLY lead="$pick → $a $c"
1667
+ badge=APPLY lead="$pick →"
1668
+ for (( i = 1; i <= ${#plabel}; i++ )); do
1669
+ if (( i != pk )); then
1670
+ lead+=" "$d${plabel[i]}$z
1671
+ elif (( color )); then
1672
+ lead+=" "$on${plabel[i]}$z
1673
+ else
1674
+ lead+=" [${${plabel[i]# }% }]"
1675
+ fi
1676
+ done
1021
1677
  if [[ $mode == pin ]]; then
1022
- __tt_tilde "$PWD"
1678
+ badge=PIN
1679
+ __tt_pin_base "${pkeys[pk]}"
1680
+ __tt_dir_label "$REPLY"
1023
1681
  note=$REPLY
1024
- (( pk == 2 )) && note+="/**"
1025
1682
  elif (( pk == 1 )); then
1026
1683
  note="Until this tab closes"
1027
1684
  else
@@ -1470,10 +2127,51 @@ __tt_pv_flush() {
1470
2127
  printf '\e[1;%dH\e[?2026l' $(( 4 + ${(m)#flt} ))
1471
2128
  }
1472
2129
 
2130
+ __tt_pv_reach() {
2131
+ local key=${pkeys[pk]} base top k home=${HOME:a} here=${PWD:a} htip="" d=$'\e[2m' z=$'\e[0m' REPLY
2132
+ local -A own=() below=() parent=() mark=() tstrip=()
2133
+ local -a roots=() lp=() lpw=() ll=() lat=() lpal=() lnote=() lflag=() reply was=()
2134
+ local -A TTHEME_PINS=("${(@kv)TTHEME_PINS}")
2135
+ local -i room=$2
2136
+ (( color )) || d= z=
2137
+ (( room < 2 )) && room=2
2138
+ __tt_pin_base "$key"
2139
+ base=$REPLY
2140
+ for k in ${(k)TTHEME_PINS}; do
2141
+ [[ $k == /* ]] || continue
2142
+ __tt_pin_base "$k"
2143
+ [[ $REPLY == "$base" ]] || continue
2144
+ if [[ $k != "$key" || $TTHEME_PINS[$k] != "$pick" ]]; then
2145
+ __tt_pin_scope "$k"
2146
+ was+=("${TTHEME_PINS[$k]} · $REPLY")
2147
+ fi
2148
+ unset "TTHEME_PINS[$k]"
2149
+ done
2150
+ TTHEME_PINS[$key]=$pick
2151
+ top=$base
2152
+ [[ $base != / ]] && __tt_pin_cover "${base:h}" && __tt_pin_base "$REPLY" && top=$REPLY
2153
+ mark[$base]=new
2154
+ __tt_map_build "$top"
2155
+ __tt_map_fit $1
2156
+ (( ${#reply} > room )) && reply=("${(@)reply[1,room-1]}" " …")
2157
+ reach=("${reply[@]}" "")
2158
+ __tt_dir_label "$base"
2159
+ base=$REPLY
2160
+ for k in $was; do
2161
+ __tt_clip "${d}Replaces ${k% · *} on $base · ${k##* · }$z" $1
2162
+ reach+=("$REPLY")
2163
+ done
2164
+ __tt_pin_scope "$key"
2165
+ __tt_clip "→ $base · $REPLY" $(( $1 - ${(m)#pick} - 2 ))
2166
+ rsub=$REPLY
2167
+ __tt_here_fit "Then here" $1
2168
+ reach+=("${reply[@]}")
2169
+ }
2170
+
1473
2171
  __tt_pv_draw() {
1474
- local out line cnt ex ag="" acat="" ac="" sb="" dd="" zz="" state=on src="" nflt="" REPLY
2172
+ local out line cnt ex ag="" acat="" ac="" sb="" dd="" zz="" state=on src="" nflt="" rsub="" REPLY
1475
2173
  local -i lw sw split sc se=$(( pw - 2 )) h k i N=${#rval} mt=${#TTHEME_ORDER} wiped=0
1476
- local -a pk
2174
+ local -a held
1477
2175
  bgstrip=""
1478
2176
  if [[ -n $flt ]]; then
1479
2177
  __tt_pv_norm "$flt"
@@ -1485,6 +2183,15 @@ __tt_pv_draw() {
1485
2183
  h=$(( ph - 5 ))
1486
2184
  [[ -n $tune ]] && (( ! split )) && h=$(( ph - 14 ))
1487
2185
  (( conf && ! split )) && h=$(( ph - 12 ))
2186
+ reach=()
2187
+ if [[ -n $pick && $mode == pin ]]; then
2188
+ if (( split )); then
2189
+ __tt_pv_reach $sw $(( ph - 6 ))
2190
+ else
2191
+ __tt_pv_reach $se $(( ph - 13 ))
2192
+ h=$(( ph - 6 - ${#reach} ))
2193
+ fi
2194
+ fi
1488
2195
  (( h < 1 )) && h=1
1489
2196
  (( cur < top )) && top=$cur
1490
2197
  (( cur >= top + h )) && top=$(( cur - h + 1 ))
@@ -1582,6 +2289,11 @@ __tt_pv_draw() {
1582
2289
  if (( help && sw >= 48 )); then
1583
2290
  __tt_pv_head 1 $sc $se Keys
1584
2291
  __tt_pv_help
2292
+ elif (( ${#reach} )); then
2293
+ __tt_pv_head 1 $sc $se "$pick" "$rsub"
2294
+ for (( k = 1; k <= ${#reach} && k + 2 < ph; k++ )); do
2295
+ out+=$'\e['$(( k + 2 ))';'$sc'H'${reach[k]}
2296
+ done
1585
2297
  elif [[ -n $tune ]]; then
1586
2298
  (( bgoff[$tpick] )) && state=off
1587
2299
  __tt_pv_bg_title $(( sw - ${#tune} - 6 ))
@@ -1601,14 +2313,19 @@ __tt_pv_draw() {
1601
2313
  fi
1602
2314
  (( help && sw < 48 )) && __tt_pv_help
1603
2315
  else
1604
- if [[ -n $tune ]]; then
2316
+ if (( ${#reach} )); then
2317
+ __tt_pv_head $(( ph - 1 - ${#reach} )) 1 $se "$pick" "$rsub"
2318
+ for (( k = 1; k <= ${#reach}; k++ )); do
2319
+ out+=$'\e['$(( ph - 1 - ${#reach} + k ))';1H'${reach[k]}
2320
+ done
2321
+ elif [[ -n $tune ]]; then
1605
2322
  (( bgoff[$tpick] )) && state=off
1606
- pk=(${=bgpics[$tune]})
2323
+ held=(${=bgpics[$tune]})
1607
2324
  src=""
1608
- if (( ${#pk} > 1 )); then
1609
- k=${pk[(Ie)${${tpick#$tune}#:}]}
1610
- (( k )) || k=${pk[(Ie)${bgact[$tune]}]}
1611
- src=" · $k/${#pk}"
2325
+ if (( ${#held} > 1 )); then
2326
+ k=${held[(Ie)${${tpick#$tune}#:}]}
2327
+ (( k )) || k=${held[(Ie)${bgact[$tune]}]}
2328
+ src=" · $k/${#held}"
1612
2329
  fi
1613
2330
  __tt_pv_bg_title $(( lw - ${#tune} - 6 )) "$src"
1614
2331
  __tt_pv_head $(( ph - 9 )) 1 $lw $tune "$REPLY" $state
@@ -1755,7 +2472,8 @@ __tt_pv_pick() {
1755
2472
  case $key in
1756
2473
  $'\x03') return 1 ;;
1757
2474
  esc) pick="" ;;
1758
- left|right|$'\t'|stab) pk=$(( 3 - pk )) ;;
2475
+ left|stab) pk=$(( (pk + ${#plabel} - 2) % ${#plabel} + 1 )) ;;
2476
+ right|$'\t') pk=$(( pk % ${#plabel} + 1 )) ;;
1759
2477
  $'\r'|$'\n')
1760
2478
  sel=$pick picked=$pk
1761
2479
  return 1
@@ -1897,8 +2615,8 @@ __tt_pv_handle() {
1897
2615
  }
1898
2616
 
1899
2617
  __tt_preview() {
1900
- emulate -L zsh
1901
- local mode=$1
2618
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2619
+ local mode=$1 pdir=${2:-$PWD}
1902
2620
  if [[ ! -t 0 || ! -t 1 ]]; then
1903
2621
  print -u2 "ttheme ${mode:-preview}: needs a terminal"
1904
2622
  return 1
@@ -1911,7 +2629,7 @@ __tt_preview() {
1911
2629
  local -A bgfrom=() bgurl=() bgby=() bgsent=() bgcost=() bgdim=() bgsrc=() bgfill=() bgfocus=() bgsize=() bgpos=() bgop=() bgdef=() bgoff=() bgbase=() bgload=() bgshot=() bgshotkey=() bgedit=() bgtunef=() bgofff=() bgimages=()
1912
2630
  local -A bgpic=() bgpics=() bgact=() bgview=() bgswap=() bgthumb=() tsnaps=() pvseek=()
1913
2631
  local conf=0 cf=1
1914
- local -a plabel=(" This tab " " Default ") csnap=()
2632
+ local -a plabel=(" This tab " " Default ") pkeys=() reach=() csnap=()
1915
2633
  local -a cvars=(TTHEME_TAB_PALETTE TTHEME_ANNOUNCE TTHEME_FX TTHEME_SORT TTHEME_BG_BLUR) clabel=("New tabs" Announce "Search fx" Sort Blur)
1916
2634
  local -a cchoice=("off seq" "1 0" "typewriter decode glitch" "abc series" "0 1 2 3 4") cshow=("off seq" "on off" "typewriter decode glitch" "abc series" "off 1px 2px 3px 4px")
1917
2635
  local -A cnote=(
@@ -1923,7 +2641,7 @@ __tt_preview() {
1923
2641
  TTHEME_BG_BLUR:2 "Pictures soften behind the text" TTHEME_BG_BLUR:3 "Pictures blur behind the text"
1924
2642
  TTHEME_BG_BLUR:4 "Pictures blur well behind the text"
1925
2643
  )
1926
- [[ $mode == pin ]] && pkdef=2 plabel=(" This directory " " And below ")
2644
+ [[ $mode == pin ]] && __tt_pin_scopes "$pdir"
1927
2645
  [[ $mode == init ]] && pkdef=2
1928
2646
  __tt_pv_canpick
1929
2647
  __tt_pv_size
@@ -1939,8 +2657,8 @@ __tt_preview() {
1939
2657
  {
1940
2658
  tty=$(stty -g 2>/dev/null && stty -echo -icanon min 1 time 0 2>/dev/null)
1941
2659
  TTHEME_RAW=$(( ${#tty} > 0 ))
1942
- printf '\e[?2026h\e[?1049h\e[?7l\e[?25l'
1943
2660
  __tt_pv_bg_open
2661
+ printf '\e[?2026h\e[?1049h\e[?7l\e[?25l'
1944
2662
  while :; do
1945
2663
  printf '\e[?2026h'
1946
2664
  if (( tick )); then
@@ -1983,13 +2701,15 @@ __tt_preview() {
1983
2701
  __tt_pv_bg_save
1984
2702
  if [[ -n $spec ]]; then
1985
2703
  TTHEME_SPEC=$spec
1986
- __tt_announce
1987
2704
  if [[ $mode == pin ]]; then
1988
- __tt_pin_save "$sel" $picked "$orig"
1989
- __tt_shown "$sel" && __tt_reload
2705
+ __tt_pin_save "$sel" "${pkeys[picked]}" "$orig"
2706
+ [[ $TTHEME_SPEC == "$orig" ]] || __tt_announce
2707
+ __tt_sync
1990
2708
  elif (( picked == 2 )); then
2709
+ __tt_announce
1991
2710
  __tt_keep "$sel"
1992
2711
  else
2712
+ __tt_announce
1993
2713
  __tt_shown "$sel" force && __tt_reload
1994
2714
  fi
1995
2715
  else
@@ -2000,6 +2720,7 @@ __tt_preview() {
2000
2720
  }
2001
2721
 
2002
2722
  ttheme() {
2723
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2003
2724
  if (( $# > 1 && ${${argv[2,-1]}[(I)(-h|--help)]} )); then
2004
2725
  __tt_verb_help $1
2005
2726
  return
@@ -2046,17 +2767,25 @@ ttheme() {
2046
2767
  __tt_resolve "$2" || return 1
2047
2768
  __tt_keep "$REPLY" ;;
2048
2769
  preview) __tt_preview ;;
2049
- pin) __tt_preview pin ;;
2050
- unpin) __tt_unpin ;;
2770
+ pin)
2771
+ if [[ -n $2 ]]; then
2772
+ REPLY=$2
2773
+ [[ $REPLY == \~ || $REPLY == \~/* ]] && REPLY=$HOME${REPLY#\~}
2774
+ [[ -d $REPLY ]] || { print -u2 -r -- "ttheme pin: no such directory: $2"; return 1 }
2775
+ fi
2776
+ __tt_preview pin ${REPLY:+${REPLY:a}} ;;
2777
+ unpin) __tt_unpin "${@:2}" ;;
2778
+ pins) __tt_pins_map ;;
2051
2779
  esac
2052
2780
  }
2053
2781
 
2054
- if (( $+functions[compdef] )); then
2782
+ if (( ${+functions[compdef]} )); then
2055
2783
  __tt_complete() {
2784
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2056
2785
  local -a reply
2057
2786
  if (( CURRENT == 2 )); then
2058
2787
  compadd -- $TTHEME_VERBS help
2059
- elif [[ $words[2] == remove || ( $words[2] == (use|default|edit|check|share) && CURRENT == 3 ) ]]; then
2788
+ elif [[ $words[2] == remove || ( $words[2] == (use|default|edit|check|share) && $CURRENT == 3 ) ]]; then
2060
2789
  compadd -- $TTHEME_ORDER
2061
2790
  elif [[ $words[2] == new && $words[CURRENT-1] == --from ]]; then
2062
2791
  compadd -- $TTHEME_ORDER
@@ -2064,9 +2793,11 @@ if (( $+functions[compdef] )); then
2064
2793
  _files -/
2065
2794
  elif [[ $words[2] == new ]]; then
2066
2795
  compadd -- --from --in
2067
- elif [[ $words[2] == market && CURRENT == 3 ]]; then
2796
+ elif [[ $words[2] == market && $CURRENT == 3 ]]; then
2068
2797
  compadd -- add remove search init
2069
- elif [[ $words[2] == market && $words[3] == (add|init) && CURRENT == 4 ]]; then
2798
+ elif [[ $words[2] == market && $words[3] == (add|init) && $CURRENT == 4 ]]; then
2799
+ _files -/
2800
+ elif [[ $words[2] == (pin|unpin) && $CURRENT == 3 ]]; then
2070
2801
  _files -/
2071
2802
  elif [[ $words[2] == add && $words[CURRENT-1] == --market ]]; then
2072
2803
  _files -/
@@ -2078,7 +2809,9 @@ if (( $+functions[compdef] )); then
2078
2809
  compdef __tt_complete ttheme
2079
2810
  fi
2080
2811
 
2081
- if __tt_active; then
2812
+ () {
2813
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2814
+ __tt_active || return 0
2082
2815
  [[ -n $TMUX ]] && __tt_tmux
2083
2816
  () {
2084
2817
  local REPLY
@@ -2111,9 +2844,10 @@ if __tt_active; then
2111
2844
  __tt_sync
2112
2845
  fi
2113
2846
  __tt_announce
2114
- fi
2847
+ }
2115
2848
 
2116
2849
  () {
2850
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2117
2851
  local f
2118
2852
  for f in $TTHEME_HOME/ttheme.zsh $TTHEME_HOME/palettes.zsh $TTHEME_HOME/adapters/_osc.zsh $TTHEME_HOME/adapters/_bg.zsh $TTHEME_HOME/adapters/$TTHEME_ADAPTER.zsh; do
2119
2853
  [[ -r $f && ! $f.zwc -nt $f ]] || continue