@kecan0406/ttheme 1.1.4 → 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() {
@@ -679,16 +1332,45 @@ __tt_rotate() {
679
1332
  __tt_announce
680
1333
  }
681
1334
 
1335
+ __tt_pv_norm() { REPLY=${(L)1//[^[:alnum:]]/} }
1336
+
1337
+ __tt_pv_seek() {
1338
+ local t part key REPLY
1339
+ for t in ${(k)TTHEME_PALETTE}; do
1340
+ key=""
1341
+ for part in "$t" "${TTHEME_GROUP[$t]:-Other}" "${TTHEME_NATIVE[$t]}" "${TTHEME_CATALOG[$t]}" "${(@s:|:)TTHEME_NATIVE_NAMES[$t]}" "${TTHEME_CHARACTER[$t]}"; do
1342
+ [[ -n $part ]] || continue
1343
+ __tt_pv_norm "$part"
1344
+ key+="|$REPLY"
1345
+ done
1346
+ pvseek[$t]=$key
1347
+ done
1348
+ }
1349
+
1350
+ __tt_pv_alias() {
1351
+ REPLY=""
1352
+ [[ -n $nflt && -n $2 ]] || return 0
1353
+ local main
1354
+ __tt_pv_norm "$1"
1355
+ main=$REPLY
1356
+ __tt_pv_norm "$2"
1357
+ [[ $main != *$nflt* && $REPLY == *$nflt* ]] && REPLY=$2 || REPLY=""
1358
+ return 0
1359
+ }
1360
+
682
1361
  __tt_pv_rows() {
683
1362
  rtype=() rval=() rcnt=()
684
- local g t c gm ruled=""
1363
+ local g t c ruled="" nflt="" REPLY
685
1364
  local -a ts cs loose inside
1365
+ if [[ -n $flt ]]; then
1366
+ (( ${#pvseek} )) || __tt_pv_seek
1367
+ __tt_pv_norm "$flt"
1368
+ nflt=$REPLY
1369
+ fi
686
1370
  for g in $groups; do
687
1371
  ts=()
688
- gm=""
689
- [[ -n $flt && ${(L)g} == *$flt* ]] && gm=1
690
1372
  for t in ${=gthemes[$g]}; do
691
- [[ -z $flt || -n $gm || $t == *$flt* || ${(L)TTHEME_CATALOG[$t]} == *$flt* ]] && ts+=($t)
1373
+ [[ -z $flt || ${pvseek[$t]} == *$nflt* ]] && ts+=($t)
692
1374
  done
693
1375
  [[ -n $flt ]] && (( ! ${#ts} )) && continue
694
1376
  if [[ $g == *@* && -z $ruled ]]; then
@@ -753,9 +1435,16 @@ __tt_pv_goto() {
753
1435
  cur=1
754
1436
  }
755
1437
 
1438
+ __tt_pv_shows() {
1439
+ REPLY=${bgview[$1]:-$1}
1440
+ [[ -n $tune && $1 == "$tune" ]] && REPLY=$tpick
1441
+ return 0
1442
+ }
1443
+
756
1444
  __tt_pv_focus() {
757
1445
  [[ ${rtype[cur]} == thm ]] || return 0
758
- (( resized )) || [[ ${rval[cur]} == "$bgname" ]] || __tt_pv_bg_show "${rval[cur]}"
1446
+ __tt_pv_shows ${rval[cur]}
1447
+ (( resized )) || [[ $REPLY == "$bgname" ]] || __tt_pv_bg_show "$REPLY"
759
1448
  local spec=${TTHEME_PALETTE[${rval[cur]}]}
760
1449
  [[ $spec == "$applied" ]] && return 0
761
1450
  __tt_pv_paint "$spec"
@@ -854,8 +1543,20 @@ __tt_pv_hl() {
854
1543
  REPLY=${t[1,${#pre}]}$'\e[1;4m'$ac${t[${#pre}+1,${#pre}+${#flt}]}$'\e[22;24;39m'$2${t[${#pre}+${#flt}+1,-1]}
855
1544
  }
856
1545
 
1546
+ __tt_pv_fit() {
1547
+ local extra=$1
1548
+ REPLY=""
1549
+ [[ -n $extra ]] || return 0
1550
+ while [[ -n $extra ]] && (( ${(m)#extra} + 3 > w )); do
1551
+ extra=${extra[1,-2]}
1552
+ done
1553
+ [[ -n $extra ]] || return 0
1554
+ (( w -= ${(m)#extra} + 2 ))
1555
+ REPLY=" "$d$extra$r
1556
+ }
1557
+
857
1558
  __tt_pv_row() {
858
- local t=${rval[$1]} b=$'\e[1m' d=$'\e[2m' r=$'\e[22;24;39m' z=$'\e[0m' on="" gut=" " base="" lead=$'\e[2m' mark=" " name arrow=▸ ind=" "
1559
+ local t=${rval[$1]} b=$'\e[1m' d=$'\e[2m' r=$'\e[22;24;39m' z=$'\e[0m' on="" gut=" " base="" lead=$'\e[2m' mark=" " name arrow=▸ ind=" " hl extra part
859
1560
  local -i w
860
1561
  (( color )) || b= d= r= z= lead=
861
1562
  if (( $1 == cur )); then
@@ -878,9 +1579,13 @@ __tt_pv_row() {
878
1579
  lead=""
879
1580
  fi
880
1581
  __tt_pv_hl "$name" "$base"
1582
+ hl=$REPLY
881
1583
  w=$(( lw - 6 - ${(m)#name} - ${#rcnt[$1]} ))
1584
+ __tt_pv_alias "$t" "${TTHEME_NATIVE[${${=gthemes[$t]}[1]}]}"
1585
+ __tt_pv_fit "$REPLY"
1586
+ extra=$REPLY
882
1587
  (( w < 1 )) && w=1
883
- REPLY=$gut$on" "$lead$arrow$r" "$base$REPLY$r${(l:w:: :)}$d${rcnt[$1]}$r" "$z
1588
+ REPLY=$gut$on" "$lead$arrow$r" "$base$hl$r$extra${(l:w:: :)}$d${rcnt[$1]}$r" "$z
884
1589
  return 0
885
1590
  fi
886
1591
  if [[ ${rtype[$1]} == cat ]]; then
@@ -907,16 +1612,36 @@ __tt_pv_row() {
907
1612
  name=${t##*/}
908
1613
  [[ -n ${TTHEME_CATALOG[$t]} && ${TTHEME_GROUP[$t]} == *@* ]] && ind=" "
909
1614
  __tt_pv_hl "$name" "$base"
1615
+ hl=$REPLY
1616
+ extra=""
1617
+ for part in "${(@s:|:)TTHEME_NATIVE_NAMES[$t]}" "${TTHEME_CHARACTER[$t]}"; do
1618
+ __tt_pv_alias "$name" "$part"
1619
+ [[ -n $REPLY ]] && { extra=$REPLY; break }
1620
+ done
910
1621
  if (( color )); then
911
1622
  [[ -n ${tstrip[$t]} ]] || __tt_pv_strip $t
912
1623
  w=$(( lw - 16 - ${#ind} - ${#name} ))
1624
+ __tt_pv_fit "$extra"
913
1625
  (( w < 1 )) && w=1
914
- REPLY=$gut$on"$ind$mark $base$REPLY$r${(l:w:: :)}${tstrip[$t]} "$z
1626
+ REPLY=$gut$on"$ind$mark $base$hl$r$REPLY${(l:w:: :)}${tstrip[$t]} "$z
915
1627
  else
916
- REPLY="$gut$ind$mark $REPLY"
1628
+ REPLY="$gut$ind$mark $hl${extra:+ $extra}"
917
1629
  fi
918
1630
  }
919
1631
 
1632
+ __tt_pv_bg_title() {
1633
+ local by=${bgby[$tpick]} ref=${bgfrom[$tpick]} url=${bgurl[$tpick]} mark="" sgr=""
1634
+ local -i room=$1
1635
+ [[ -n $ref && $url == http(s|)://* ]] && (( ${TTHEME_LINKS[(Ie)$TTHEME_ADAPTER]} )) && mark="⧉ "
1636
+ REPLY=Background${by:+ · $by}${ref:+ · $mark$ref}$2
1637
+ (( ${#REPLY} > room )) && REPLY=Background${by:+ · $by}
1638
+ (( ${#REPLY} > room )) && REPLY=Background
1639
+ [[ -n $mark && $REPLY == *"$mark$ref"* ]] || return 0
1640
+ (( color )) && [[ -n ${TTHEME_SITE_ANSI[${ref%% *}]} ]] && sgr=$'\e[3'${TTHEME_SITE_ANSI[${ref%% *}]}m
1641
+ REPLY=${REPLY/"$mark$ref"/$sgr⧉${sgr:+$'\e[39m'} $'\e]8;;'$url$'\e\\'$ref$'\e]8;;\e\\'}
1642
+ return 0
1643
+ }
1644
+
920
1645
  __tt_pv_head() {
921
1646
  local b=$'\e[1m' d=$'\e[2m' z=$'\e[0m' right=$6
922
1647
  (( color )) || b= d= z=
@@ -939,17 +1664,21 @@ __tt_pv_foot() {
939
1664
  if (( help )); then
940
1665
  badge=KEYS right=$b"? esc"$z$d" close"$z
941
1666
  elif [[ -n $pick ]]; then
942
- local a=$plabel[1] c=$plabel[2]
943
- if (( color )); then
944
- if (( pk == 1 )); then a=$on$a$z c=$d$c$z; else a=$d$a$z c=$on$c$z; fi
945
- else
946
- if (( pk == 1 )); then a="[${${a# }% }]"; else c="[${${c# }% }]"; fi
947
- fi
948
- 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
949
1677
  if [[ $mode == pin ]]; then
950
- __tt_tilde "$PWD"
1678
+ badge=PIN
1679
+ __tt_pin_base "${pkeys[pk]}"
1680
+ __tt_dir_label "$REPLY"
951
1681
  note=$REPLY
952
- (( pk == 2 )) && note+="/**"
953
1682
  elif (( pk == 1 )); then
954
1683
  note="Until this tab closes"
955
1684
  else
@@ -958,14 +1687,14 @@ __tt_pv_foot() {
958
1687
  kk=(enter) kl=(confirm)
959
1688
  right=$b"esc"$z$d" back"$z
960
1689
  elif [[ -n $tune ]]; then
961
- badge=TUNE kk=(↑↓ ←→) kl=(field step)
1690
+ badge=TUNE kk=(↑↓ ←→ '=' +) kl=(field step reset "reset all")
962
1691
  if (( tf == 2 )); then
963
1692
  kl[2]=move kk+=(1-9) kl+=(place)
964
1693
  else
965
1694
  kk+=(⇧←→) kl+=(×10)
966
1695
  fi
967
- kk+=(space '=' enter)
968
- if (( bgoff[$tune] )); then kl+=(show default keep); else kl+=(hide default keep); fi
1696
+ kk+=(space enter)
1697
+ if (( bgoff[$tpick] )); then kl+=(show keep); else kl+=(hide keep); fi
969
1698
  __tt_pv_bg_images $tune
970
1699
  (( REPLY > 1 )) && { kk+=(', .' D); kl+=("image ×$REPLY" remove) }
971
1700
  __tt_pv_bg_findable $tune && { kk+=(f); kl+=(find) }
@@ -991,6 +1720,7 @@ __tt_pv_foot() {
991
1720
  elif __tt_pv_bg_findable ${rval[cur]}; then
992
1721
  kk+=(tab) kl+=("find bg")
993
1722
  fi
1723
+ (( split )) && { kk+=(⇧←→); kl+=(example) }
994
1724
  [[ -n $flt ]] && { kk+=(bksp); kl+=(edit) }
995
1725
  fi
996
1726
  kk+=('?') kl+=(keys)
@@ -1050,15 +1780,17 @@ __tt_pv_foot() {
1050
1780
  __tt_pv_help() {
1051
1781
  local back="the tab" z=$'\e[0m' b=$'\e[1m' blank
1052
1782
  [[ -n ${TTHEME_PALETTE[$cn]} ]] && back=$cn
1053
- local -a hk=(Move Series Filter Apply) hv=(
1783
+ local -a hk=(Move Series Filter "" Example Apply) hv=(
1054
1784
  "↑↓ home end pgup pgdn"
1055
1785
  "←→ · enter or space on a series"
1056
- "a-z 0-9 - · bksp · ctrl-u clears"
1786
+ "Any text · bksp · ctrl-u clears"
1787
+ "Names and titles, in Japanese too"
1788
+ "⇧←→ ${(Lj:, :)TTHEME_SCENES}"
1057
1789
  "enter · esc restores $back"
1058
1790
  )
1059
1791
  if (( bgcw )); then
1060
- hk+=("Tune bg" "" "" "" "")
1061
- hv+=("tab · finds one on the boorus, or takes your own, if none" "↑↓ field ←→ step ⇧←→ ×10 1-9 place" "space hides · = default · f adds one from the boorus or your own" "enter keeps · esc undoes" ", . other saved images · D removes this one")
1792
+ hk+=("Tune bg" "" "" "" "" "")
1793
+ hv+=("tab · finds one on the boorus, or takes your own, if none" "↑↓ field ←→ step ⇧←→ ×10 1-9 place" "= resets a field · + resets all" "space hides · enter keeps · esc undoes" ", . other pictures · D removes one" "f adds one from the boorus or your own")
1062
1794
  fi
1063
1795
  hk+=(Config "")
1064
1796
  hv+=("alt-c · ↑↓ setting ←→ value" "enter saves · esc undoes")
@@ -1090,45 +1822,82 @@ __tt_pv_help() {
1090
1822
  }
1091
1823
 
1092
1824
  __tt_pv_specimen() {
1093
- local -a sp=(${=applied}) ln=() fg=()
1825
+ local -a sp=(${=applied}) lines parts
1094
1826
  (( ${#sp} >= 20 )) || return 0
1095
- local z=$'\e[0m' d=$'\e[2m' sel cu cell line="" lo hi pr bd
1096
- local -i i r cw=4
1827
+ local z=$'\e[0m' d=$'\e[2m' cell hex fore line text piece role tabs="" built="" key="$applied|$painted|$scene|$sw|$sc|$ph"
1828
+ local -A rs=(p "" d $'\e[2m' b $'\e[1m')
1829
+ local -i i r=6 cw=4 k n=${#TTHEME_SCENES} at
1830
+ (( n )) || return 0
1831
+ if [[ $key == "$pvscene" ]]; then
1832
+ out+=$pvscenes
1833
+ return 0
1834
+ fi
1835
+ at=$(( (scene % n + n) % n + 1 ))
1097
1836
  if [[ $applied == "$painted" ]]; then
1098
- fg=($'\e[31m' $'\e[32m' $'\e[33m' $'\e[34m' $'\e[35m') bd=$'\e[1;34m'
1837
+ for (( i = 0; i < 16; i++ )); do
1838
+ k=$(( i < 8 ? 30 + i : 82 + i ))
1839
+ rs[$i]=$'\e['$k'm' rs[B$i]=$'\e[1;'$k'm' rs[K$i]=$'\e[30;'$(( k + 10 ))'m'
1840
+ done
1099
1841
  else
1100
- for i in 6 7 8 9 10 17; do
1101
- lo=${sp[i]#\#}
1102
- printf -v cell '\e[38;2;%d;%d;%dm' $((16#${lo:0:2})) $((16#${lo:2:2})) $((16#${lo:4:2}))
1103
- fg+=($cell)
1842
+ fore=${sp[5]#\#}
1843
+ for (( i = 0; i < 16; i++ )); do
1844
+ hex=${sp[i+5]#\#}
1845
+ printf -v cell '\e[38;2;%d;%d;%dm' $((16#${hex:0:2})) $((16#${hex:2:2})) $((16#${hex:4:2}))
1846
+ rs[$i]=$cell
1847
+ printf -v cell '\e[38;2;%d;%d;%d;48;2;%d;%d;%dm' $((16#${fore:0:2})) $((16#${fore:2:2})) $((16#${fore:4:2})) \
1848
+ $((16#${hex:0:2})) $((16#${hex:2:2})) $((16#${hex:4:2}))
1849
+ rs[K$i]=$cell
1850
+ hex=${sp[(i < 8 ? i + 13 : i + 5)]#\#}
1851
+ printf -v cell '\e[1;38;2;%d;%d;%dm' $((16#${hex:0:2})) $((16#${hex:2:2})) $((16#${hex:4:2}))
1852
+ rs[B$i]=$cell
1853
+ done
1854
+ fi
1855
+ printf -v cell '\e[48;2;%d;%d;%dm' $((16#${sp[4]:1:2})) $((16#${sp[4]:3:2})) $((16#${sp[4]:5:2}))
1856
+ rs[s]=$cell
1857
+ printf -v cell '\e[48;2;%d;%d;%dm' $((16#${sp[3]:1:2})) $((16#${sp[3]:3:2})) $((16#${sp[3]:5:2}))
1858
+ rs[c]=$cell
1859
+ if (( ${#${(j: :)TTHEME_SCENES}} <= sw )); then
1860
+ for (( k = 1; k <= n; k++ )); do
1861
+ (( k > 1 )) && tabs+=" "
1862
+ if (( k == at )); then
1863
+ tabs+=$'\e[1m'$ac$TTHEME_SCENES[k]$z
1864
+ else
1865
+ tabs+=$d$TTHEME_SCENES[k]$z
1866
+ fi
1104
1867
  done
1105
- bd=$'\e[1m'$fg[6]
1868
+ else
1869
+ tabs=$'\e[1m'$ac$TTHEME_SCENES[at]$z" "$d"$at/$n"$z
1106
1870
  fi
1107
- pr=$fg[2]'❯'$z' '
1871
+ built+=$'\e[2;'$sc'H'$tabs
1108
1872
  (( sw < 44 )) && cw=3
1109
1873
  (( sw >= 56 )) && cw=$(( (sw + 1) / 8 - 1 ))
1110
1874
  (( cw > 7 )) && cw=7
1111
- printf -v sel '\e[48;2;%d;%d;%dm' $((16#${sp[4]:1:2})) $((16#${sp[4]:3:2})) $((16#${sp[4]:5:2}))
1112
- printf -v cu '\e[48;2;%d;%d;%dm' $((16#${sp[3]:1:2})) $((16#${sp[3]:3:2})) $((16#${sp[3]:5:2}))
1875
+ line=""
1113
1876
  for (( i = 0; i < 8; i++ )); do
1114
- lo=${sp[i+5]#\#} hi=${sp[i+13]#\#}
1877
+ fore=${sp[i+5]#\#} hex=${sp[i+13]#\#}
1115
1878
  printf -v cell '\e[38;2;%d;%d;%d;48;2;%d;%d;%dm%s\e[0m ' \
1116
- $((16#${lo:0:2})) $((16#${lo:2:2})) $((16#${lo:4:2})) \
1117
- $((16#${hi:0:2})) $((16#${hi:2:2})) $((16#${hi:4:2})) "${(l:cw::▀:)}"
1879
+ $((16#${fore:0:2})) $((16#${fore:2:2})) $((16#${fore:4:2})) \
1880
+ $((16#${hex:0:2})) $((16#${hex:2:2})) $((16#${hex:4:2})) "${(l:cw::▀:)}"
1118
1881
  line+=$cell
1119
1882
  done
1120
- out+=$'\e[4;'$sc'H'$line
1121
- ln=($bd'~/code/demo'$z$d' on '$z$fg[5]'main'$fg[2]' +2'$fg[3]' ~1'$z "${pr}git status -sb")
1122
- (( sw >= 44 )) && ln+=('## '$fg[2]'main'$z'...'$fg[1]'origin/main'$fg[3]' [ahead 1]'$z)
1123
- ln+=($fg[1]' M'$z' src/main.rs' $fg[1]'??'$z' notes.md' "")
1124
- (( sw >= 44 )) && ln+=("${pr}ls" $bd'docs'$z' '$bd'src'$z' '$bd'tests'$z' Cargo.toml README.md' "")
1125
- ln+=("${pr}cargo test" $fg[2]' ✓'$z' parses config' $fg[1]' ✗'$z' renders frame')
1126
- (( sw >= 44 )) && ln[-1]+=$d' expected '$z$fg[2]'3'$z$d', received '$z$fg[1]'2'$z
1127
- ln+=("" "${pr}echo ${sel}selected text${z} ${cu} ${z}")
1128
- for (( r = 1; r <= ${#ln}; r++ )); do
1129
- (( r + 5 < ph )) || break
1130
- out+=$'\e['$(( r + 5 ))';'$sc'H'${ln[r]}
1883
+ built+=$'\e[4;'$sc'H'$line
1884
+ lines=("${(@P)${:-TTHEME_SCENE_$at}}")
1885
+ for line in "${lines[@]}"; do
1886
+ [[ $line == «w»* ]] && (( sw < 44 )) && continue
1887
+ [[ $line == «n»* ]] && (( sw >= 44 )) && continue
1888
+ (( r < ph )) || break
1889
+ parts=("${(@s:«:)line}")
1890
+ text=$parts[1]
1891
+ for piece in "${(@)parts[2,-1]}"; do
1892
+ role=${piece%%»*}
1893
+ [[ $role == [wn] ]] && role=p
1894
+ text+=$z${rs[${role:-p}]}${piece#*»}
1895
+ done
1896
+ built+=$'\e['$r';'$sc'H'$text$z
1897
+ (( ++r ))
1131
1898
  done
1899
+ pvscene=$key pvscenes=$built
1900
+ out+=$built
1132
1901
  }
1133
1902
 
1134
1903
  __tt_pv_tune() {
@@ -1137,8 +1906,8 @@ __tt_pv_tune() {
1137
1906
  local name
1138
1907
  case $key in
1139
1908
  $'\x03') return 1 ;;
1140
- up) (( tf > 1 )) && tf=$(( tf - 1 )) ;;
1141
- down) (( tf < 3 )) && tf=$(( tf + 1 )) ;;
1909
+ up) tf=$(( tf > 1 ? tf - 1 : 3 )) ;;
1910
+ down) tf=$(( tf < 3 ? tf + 1 : 1 )) ;;
1142
1911
  left|right|sleft|sright)
1143
1912
  [[ $key == *left ]] && n=-1
1144
1913
  [[ $key == s* ]] && (( tf != 2 )) && n=$(( n * 10 ))
@@ -1149,32 +1918,54 @@ __tt_pv_tune() {
1149
1918
  __tt_pv_bg_adjust at $key
1150
1919
  ;;
1151
1920
  ' ') __tt_pv_bg_adjust on ;;
1152
- '=') __tt_pv_bg_adjust def ;;
1153
- ',') __tt_pv_bg_image prev ;;
1154
- '.') __tt_pv_bg_image next ;;
1155
- D) __tt_pv_bg_image drop ;;
1921
+ '=') __tt_pv_bg_adjust def $tf ;;
1922
+ '+') __tt_pv_bg_adjust def ;;
1923
+ ',') __tt_pv_bg_pick -1 ;;
1924
+ '.') __tt_pv_bg_pick 1 ;;
1925
+ D) __tt_pv_bg_drop ;;
1156
1926
  f)
1157
1927
  __tt_pv_bg_findable $tune || return 0
1158
1928
  name=$tune
1159
1929
  __tt_pv_untune
1160
1930
  __tt_pv_bg_find $name
1161
- tune=$name tf=1 tsnap="${bgsize[$name]} ${bgpos[$name]} ${bgop[$name]} ${bgoff[$name]}"
1162
- ;;
1163
- $'\r'|$'\n')
1164
- if [[ "${bgsize[$tune]} ${bgpos[$tune]} ${bgop[$tune]} ${bgoff[$tune]}" != "$tsnap" ]]; then
1165
- bgedit[$tune]=1 msg="Kept · saved when preview closes" msgt=200
1166
- fi
1167
- tune=""
1931
+ __tt_pv_tune_open $name
1168
1932
  ;;
1933
+ $'\r'|$'\n') __tt_pv_tune_keep ;;
1169
1934
  esc) __tt_pv_untune ;;
1170
1935
  '?') help=1 ;;
1171
1936
  esac
1172
1937
  return 0
1173
1938
  }
1174
1939
 
1940
+ __tt_pv_tune_keep() {
1941
+ local img was=${bgview[$tune]:-$tune}
1942
+ local -i changed=0
1943
+ for img in ${(k)tsnaps}; do
1944
+ [[ "${bgsize[$img]} ${bgpos[$img]} ${bgop[$img]} ${bgoff[$img]}" == "${tsnaps[$img]}" ]] && continue
1945
+ bgedit[$img]=1 changed=1
1946
+ done
1947
+ if [[ $tpick != "$was" ]]; then
1948
+ changed=1
1949
+ if [[ $tpick == "$tune" ]]; then
1950
+ unset "bgview[$tune]" "bgswap[$tune]"
1951
+ else
1952
+ bgview[$tune]=$tpick bgswap[$tune]=${tpick##*:}
1953
+ fi
1954
+ fi
1955
+ (( changed )) && msg="Kept · saved when preview closes" msgt=200
1956
+ __tt_pv_bg_strip_off
1957
+ tune="" tpick="" tsnaps=() bgname=""
1958
+ }
1959
+
1175
1960
  __tt_pv_untune() {
1176
- local -a s=(${=tsnap})
1177
- bgsize[$tune]=$s[1] bgpos[$tune]=$s[2] bgop[$tune]=$s[3] bgoff[$tune]=$s[4] bgname="" tune=""
1961
+ local img
1962
+ local -a s
1963
+ for img in ${(k)tsnaps}; do
1964
+ s=(${=tsnaps[$img]})
1965
+ bgsize[$img]=$s[1] bgpos[$img]=$s[2] bgop[$img]=$s[3] bgoff[$img]=$s[4]
1966
+ done
1967
+ __tt_pv_bg_strip_off
1968
+ tune="" tpick="" tsnaps=() bgname=""
1178
1969
  }
1179
1970
 
1180
1971
  __tt_pv_regroup() {
@@ -1190,8 +1981,8 @@ __tt_pv_regroup() {
1190
1981
  __tt_pv_conf() {
1191
1982
  case $key in
1192
1983
  $'\x03') return 1 ;;
1193
- up) (( cf > 1 )) && cf=$(( cf - 1 )) ;;
1194
- down) (( cf < ${#cvars} )) && cf=$(( cf + 1 )) ;;
1984
+ up) cf=$(( cf > 1 ? cf - 1 : ${#cvars} )) ;;
1985
+ down) cf=$(( cf < ${#cvars} ? cf + 1 : 1 )) ;;
1195
1986
  left|sleft) __tt_pv_conf_step -1 ;;
1196
1987
  right|sright) __tt_pv_conf_step 1 ;;
1197
1988
  $'\r'|$'\n') __tt_pv_conf_save ;;
@@ -1318,24 +2109,89 @@ __tt_pv_conf_panel() {
1318
2109
  }
1319
2110
 
1320
2111
  __tt_pv_flush() {
2112
+ local REPLY
1321
2113
  print -rn -- "$out"
1322
2114
  if (( wiped )); then
1323
2115
  local bn=$bgname
1324
- [[ ${rtype[cur]} == thm ]] && bn=${rval[cur]}
2116
+ if [[ ${rtype[cur]} == thm ]]; then
2117
+ __tt_pv_shows ${rval[cur]}
2118
+ bn=$REPLY
2119
+ fi
1325
2120
  [[ -n $bn ]] && __tt_pv_bg_show "$bn" 1
1326
2121
  fi
1327
- printf '\e[?2026l'
2122
+ if [[ -n $bgstrip ]]; then
2123
+ __tt_pv_bg_strip ${=bgstrip}
2124
+ elif (( ${#bgthumb} )); then
2125
+ __tt_pv_bg_strip_off
2126
+ fi
2127
+ printf '\e[1;%dH\e[?2026l' $(( 4 + ${(m)#flt} ))
2128
+ }
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[@]}")
1328
2169
  }
1329
2170
 
1330
2171
  __tt_pv_draw() {
1331
- local out line cnt ex ag="" acat="" ac="" sb="" dd="" zz="" state=on src="" REPLY
2172
+ local out line cnt ex ag="" acat="" ac="" sb="" dd="" zz="" state=on src="" nflt="" rsub="" REPLY
1332
2173
  local -i lw sw split sc se=$(( pw - 2 )) h k i N=${#rval} mt=${#TTHEME_ORDER} wiped=0
2174
+ local -a held
2175
+ bgstrip=""
2176
+ if [[ -n $flt ]]; then
2177
+ __tt_pv_norm "$flt"
2178
+ nflt=$REPLY
2179
+ fi
1333
2180
  __tt_pv_lw
1334
2181
  lw=$reply[1] sw=$reply[2]
1335
2182
  split=$(( sw > 0 )) sc=$(( pw - 1 - sw ))
1336
2183
  h=$(( ph - 5 ))
1337
2184
  [[ -n $tune ]] && (( ! split )) && h=$(( ph - 14 ))
1338
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
1339
2195
  (( h < 1 )) && h=1
1340
2196
  (( cur < top )) && top=$cur
1341
2197
  (( cur >= top + h )) && top=$(( cur - h + 1 ))
@@ -1407,7 +2263,7 @@ __tt_pv_draw() {
1407
2263
  [[ -n $flt ]] && tail=$'\e['$(( lw - ${#cnt} ))'G'$cnt
1408
2264
  if [[ $1 == hint ]] && (( ! wiped )); then
1409
2265
  print -rn -- $'\e[H\e[0m\e['$lw'X'$line$tail
1410
- printf '\e[?2026l'
2266
+ printf '\e[1;%dH\e[?2026l' $(( 4 + ${(m)#flt} ))
1411
2267
  return 0
1412
2268
  fi
1413
2269
  out+=$line$'\e[K'$tail$'\n\e[K\n'
@@ -1433,13 +2289,16 @@ __tt_pv_draw() {
1433
2289
  if (( help && sw >= 48 )); then
1434
2290
  __tt_pv_head 1 $sc $se Keys
1435
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
1436
2297
  elif [[ -n $tune ]]; then
1437
- (( bgoff[$tune] )) && state=off
1438
- src=Background
1439
- [[ -n ${bgfrom[$tune]} ]] && src+=" · ${bgfrom[$tune]}"
1440
- (( ${#tune} + ${#src} + 6 > sw )) && src=Background
1441
- __tt_pv_head 1 $sc $se $tune "$src" $state
1442
- __tt_pv_bg_panel $tune 4 $sc $se
2298
+ (( bgoff[$tpick] )) && state=off
2299
+ __tt_pv_bg_title $(( sw - ${#tune} - 6 ))
2300
+ __tt_pv_head 1 $sc $se $tune "$REPLY" $state
2301
+ __tt_pv_bg_panel $tpick 4 $sc $se
1443
2302
  elif (( conf )); then
1444
2303
  __tt_tilde "$TTHEME_CONFIG"
1445
2304
  src=$REPLY
@@ -1454,13 +2313,23 @@ __tt_pv_draw() {
1454
2313
  fi
1455
2314
  (( help && sw < 48 )) && __tt_pv_help
1456
2315
  else
1457
- if [[ -n $tune ]]; then
1458
- (( bgoff[$tune] )) && state=off
1459
- src=Background
1460
- [[ -n ${bgfrom[$tune]} ]] && src+=" · ${bgfrom[$tune]}"
1461
- (( ${#tune} + ${#src} + 6 > lw )) && src=Background
1462
- __tt_pv_head $(( ph - 9 )) 1 $lw $tune "$src" $state
1463
- __tt_pv_bg_panel $tune $(( ph - 8 )) 1 $lw
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
2322
+ (( bgoff[$tpick] )) && state=off
2323
+ held=(${=bgpics[$tune]})
2324
+ src=""
2325
+ if (( ${#held} > 1 )); then
2326
+ k=${held[(Ie)${${tpick#$tune}#:}]}
2327
+ (( k )) || k=${held[(Ie)${bgact[$tune]}]}
2328
+ src=" · $k/${#held}"
2329
+ fi
2330
+ __tt_pv_bg_title $(( lw - ${#tune} - 6 )) "$src"
2331
+ __tt_pv_head $(( ph - 9 )) 1 $lw $tune "$REPLY" $state
2332
+ __tt_pv_bg_panel $tpick $(( ph - 8 )) 1 $lw
1464
2333
  elif (( conf )); then
1465
2334
  __tt_pv_head $(( ph - 7 )) 1 $lw Config
1466
2335
  __tt_pv_conf_panel $(( ph - 6 )) 1 $lw
@@ -1594,6 +2463,7 @@ __tt_pv_read() {
1594
2463
  "6~") key=pgdn ;;
1595
2464
  "1;2C") key=sright ;;
1596
2465
  "1;2D") key=sleft ;;
2466
+ Z) key=stab ;;
1597
2467
  *) key=nop ;;
1598
2468
  esac
1599
2469
  }
@@ -1602,7 +2472,8 @@ __tt_pv_pick() {
1602
2472
  case $key in
1603
2473
  $'\x03') return 1 ;;
1604
2474
  esc) pick="" ;;
1605
- left|right|$'\t') pk=$(( 3 - pk )) ;;
2475
+ left|stab) pk=$(( (pk + ${#plabel} - 2) % ${#plabel} + 1 )) ;;
2476
+ right|$'\t') pk=$(( pk % ${#plabel} + 1 )) ;;
1606
2477
  $'\r'|$'\n')
1607
2478
  sel=$pick picked=$pk
1608
2479
  return 1
@@ -1662,29 +2533,50 @@ __tt_pv_handle() {
1662
2533
  __tt_pv_toggle
1663
2534
  fi
1664
2535
  ;;
1665
- up) (( cur > 1 )) && cur=$(( cur - 1 )); [[ ${rtype[cur]} == rule ]] && cur=$(( cur - 1 )) ;;
1666
- down) (( cur < ${#rval} )) && cur=$(( cur + 1 )); [[ ${rtype[cur]} == rule ]] && cur=$(( cur + 1 )) ;;
2536
+ up)
2537
+ if (( cur > 1 )); then
2538
+ cur=$(( cur - 1 ))
2539
+ [[ ${rtype[cur]} == rule ]] && cur=$(( cur - 1 ))
2540
+ elif (( ${#rval} )); then
2541
+ cur=${#rval}
2542
+ fi
2543
+ ;;
2544
+ down)
2545
+ if (( cur < ${#rval} )); then
2546
+ cur=$(( cur + 1 ))
2547
+ [[ ${rtype[cur]} == rule ]] && cur=$(( cur + 1 ))
2548
+ elif (( ${#rval} )); then
2549
+ cur=1
2550
+ fi
2551
+ ;;
1667
2552
  right) [[ ${rtype[cur]} == (hdr|cat) && -z ${exp[${rval[cur]}]} ]] && __tt_pv_toggle ;;
1668
2553
  left) __tt_pv_left ;;
1669
2554
  home) cur=1 ;;
1670
2555
  end) (( ${#rval} )) && cur=${#rval} ;;
1671
2556
  pgup)
1672
- cur=$(( cur - ph + 5 ))
1673
- (( cur < 1 )) && cur=1
1674
- [[ ${rtype[cur]} == rule ]] && cur=$(( cur - 1 ))
2557
+ if (( cur <= 1 )); then
2558
+ (( ${#rval} )) && cur=${#rval}
2559
+ else
2560
+ cur=$(( cur - ph + 5 ))
2561
+ (( cur < 1 )) && cur=1
2562
+ [[ ${rtype[cur]} == rule ]] && cur=$(( cur - 1 ))
2563
+ fi
1675
2564
  ;;
1676
2565
  pgdn)
1677
- cur=$(( cur + ph - 5 ))
1678
- (( cur > ${#rval} )) && cur=${#rval}
1679
- (( cur < 1 )) && cur=1
1680
- [[ ${rtype[cur]} == rule ]] && cur=$(( cur + 1 ))
2566
+ if (( cur >= ${#rval} )); then
2567
+ cur=1
2568
+ else
2569
+ cur=$(( cur + ph - 5 ))
2570
+ (( cur > ${#rval} )) && cur=${#rval}
2571
+ [[ ${rtype[cur]} == rule ]] && cur=$(( cur + 1 ))
2572
+ fi
1681
2573
  ;;
1682
2574
  ' ') [[ ${rtype[cur]} == (hdr|cat) ]] && __tt_pv_toggle ;;
1683
2575
  $'\t')
1684
2576
  [[ ${rtype[cur]} == thm ]] || return 0
1685
2577
  name=${rval[cur]}
1686
2578
  if __tt_pv_bg_state $name || { __tt_pv_bg_findable $name && __tt_pv_bg_find $name }; then
1687
- tune=$name tf=1 tsnap="${bgsize[$name]} ${bgpos[$name]} ${bgop[$name]} ${bgoff[$name]}"
2579
+ __tt_pv_tune_open $name
1688
2580
  elif __tt_pv_bg_findable $name; then
1689
2581
  :
1690
2582
  elif (( bgcw )); then
@@ -1698,6 +2590,8 @@ __tt_pv_handle() {
1698
2590
  done
1699
2591
  ;;
1700
2592
  '?') help=1 ;;
2593
+ sleft) (( --scene )) ;;
2594
+ sright) (( ++scene )) ;;
1701
2595
  $'\x7f'|$'\x08')
1702
2596
  if [[ -n $flt ]]; then
1703
2597
  name=""
@@ -1707,9 +2601,9 @@ __tt_pv_handle() {
1707
2601
  __tt_pv_first "$name"
1708
2602
  fi
1709
2603
  ;;
1710
- [a-zA-Z0-9-])
2604
+ [[:print:]])
1711
2605
  __tt_pv_lw
1712
- (( ${#flt} >= reply[1] - 12 )) && return 0
2606
+ (( ${(m)#flt} + ${(m)#key} > reply[1] - 12 )) && return 0
1713
2607
  name=""
1714
2608
  [[ ${rtype[cur]} == thm ]] && name=${rval[cur]}
1715
2609
  flt+=${(L)key}
@@ -1721,19 +2615,21 @@ __tt_pv_handle() {
1721
2615
  }
1722
2616
 
1723
2617
  __tt_preview() {
1724
- emulate -L zsh
1725
- local mode=$1
2618
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2619
+ local mode=$1 pdir=${2:-$PWD}
1726
2620
  if [[ ! -t 0 || ! -t 1 ]]; then
1727
2621
  print -u2 "ttheme ${mode:-preview}: needs a terminal"
1728
2622
  return 1
1729
2623
  fi
1730
2624
  local orig=$TTHEME_SPEC applied=$TTHEME_SPEC painted=$TTHEME_SPEC flt="" sel="" cn="" cdot="" key="" REPLY="" cur=1 top=1 color=0 pw=80 ph=24 resized=1 expal="" exgrp="" exnext=0 gstep=0 gseed=0 tick=0
1731
2625
  local pick="" pk=1 pkdef=1 picked=0 canpick=0 bgcw=0 bgch=0 bgname="" bgshown="" bginc="" osd="" osdt=0
1732
- local tune="" tsnap="" tf=1 help=0 msg="" msgt=0 an="" bgrel=0 bgmx=0 bgmy=0 bganchor=0 bgcut="" bgnext=0 bgbytes=0
2626
+ local tune="" tpick="" tf=1 help=0 msg="" msgt=0 an="" bgrel=0 bgmx=0 bgmy=0 bganchor=0 bgcut="" bgnext=0 bgbytes=0 bgstrip="" pvscene="" pvscenes=""
2627
+ local -i scene=0
1733
2628
  local -a bgorder=()
1734
- local -A bgfrom=() bgsent=() bgcost=() bgdim=() bgsrc=() bgfill=() bgfocus=() bgsize=() bgpos=() bgop=() bgdef=() bgoff=() bgbase=() bgload=() bgshot=() bgshotkey=() bgedit=() bgtunef=() bgofff=() bgimages=()
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=()
2630
+ local -A bgpic=() bgpics=() bgact=() bgview=() bgswap=() bgthumb=() tsnaps=() pvseek=()
1735
2631
  local conf=0 cf=1
1736
- local -a plabel=(" This tab " " Default ") csnap=()
2632
+ local -a plabel=(" This tab " " Default ") pkeys=() reach=() csnap=()
1737
2633
  local -a cvars=(TTHEME_TAB_PALETTE TTHEME_ANNOUNCE TTHEME_FX TTHEME_SORT TTHEME_BG_BLUR) clabel=("New tabs" Announce "Search fx" Sort Blur)
1738
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")
1739
2635
  local -A cnote=(
@@ -1745,7 +2641,7 @@ __tt_preview() {
1745
2641
  TTHEME_BG_BLUR:2 "Pictures soften behind the text" TTHEME_BG_BLUR:3 "Pictures blur behind the text"
1746
2642
  TTHEME_BG_BLUR:4 "Pictures blur well behind the text"
1747
2643
  )
1748
- [[ $mode == pin ]] && pkdef=2 plabel=(" This directory " " And below ")
2644
+ [[ $mode == pin ]] && __tt_pin_scopes "$pdir"
1749
2645
  [[ $mode == init ]] && pkdef=2
1750
2646
  __tt_pv_canpick
1751
2647
  __tt_pv_size
@@ -1761,8 +2657,8 @@ __tt_preview() {
1761
2657
  {
1762
2658
  tty=$(stty -g 2>/dev/null && stty -echo -icanon min 1 time 0 2>/dev/null)
1763
2659
  TTHEME_RAW=$(( ${#tty} > 0 ))
1764
- printf '\e[?2026h\e[?1049h\e[?7l\e[?25l'
1765
2660
  __tt_pv_bg_open
2661
+ printf '\e[?2026h\e[?1049h\e[?7l\e[?25l'
1766
2662
  while :; do
1767
2663
  printf '\e[?2026h'
1768
2664
  if (( tick )); then
@@ -1805,23 +2701,26 @@ __tt_preview() {
1805
2701
  __tt_pv_bg_save
1806
2702
  if [[ -n $spec ]]; then
1807
2703
  TTHEME_SPEC=$spec
1808
- __tt_announce
1809
2704
  if [[ $mode == pin ]]; then
1810
- __tt_pin_save "$sel" $picked "$orig"
1811
- __tt_shown "$sel" && __tt_reload
2705
+ __tt_pin_save "$sel" "${pkeys[picked]}" "$orig"
2706
+ [[ $TTHEME_SPEC == "$orig" ]] || __tt_announce
2707
+ __tt_sync
1812
2708
  elif (( picked == 2 )); then
2709
+ __tt_announce
1813
2710
  __tt_keep "$sel"
1814
2711
  else
2712
+ __tt_announce
1815
2713
  __tt_shown "$sel" force && __tt_reload
1816
2714
  fi
1817
2715
  else
1818
- (( ${#bgedit} )) && [[ -n ${TTHEME_PALETTE[$cn]} ]] && { __tt_shown "$cn" && __tt_reload }
2716
+ (( ${#bgedit} + ${#bgswap} )) && [[ -n ${TTHEME_PALETTE[$cn]} ]] && { __tt_shown "$cn" && __tt_reload }
1819
2717
  fi
1820
2718
  }
1821
2719
  return 0
1822
2720
  }
1823
2721
 
1824
2722
  ttheme() {
2723
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
1825
2724
  if (( $# > 1 && ${${argv[2,-1]}[(I)(-h|--help)]} )); then
1826
2725
  __tt_verb_help $1
1827
2726
  return
@@ -1868,17 +2767,25 @@ ttheme() {
1868
2767
  __tt_resolve "$2" || return 1
1869
2768
  __tt_keep "$REPLY" ;;
1870
2769
  preview) __tt_preview ;;
1871
- pin) __tt_preview pin ;;
1872
- 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 ;;
1873
2779
  esac
1874
2780
  }
1875
2781
 
1876
- if (( $+functions[compdef] )); then
2782
+ if (( ${+functions[compdef]} )); then
1877
2783
  __tt_complete() {
2784
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
1878
2785
  local -a reply
1879
2786
  if (( CURRENT == 2 )); then
1880
2787
  compadd -- $TTHEME_VERBS help
1881
- 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
1882
2789
  compadd -- $TTHEME_ORDER
1883
2790
  elif [[ $words[2] == new && $words[CURRENT-1] == --from ]]; then
1884
2791
  compadd -- $TTHEME_ORDER
@@ -1886,9 +2793,11 @@ if (( $+functions[compdef] )); then
1886
2793
  _files -/
1887
2794
  elif [[ $words[2] == new ]]; then
1888
2795
  compadd -- --from --in
1889
- elif [[ $words[2] == market && CURRENT == 3 ]]; then
2796
+ elif [[ $words[2] == market && $CURRENT == 3 ]]; then
1890
2797
  compadd -- add remove search init
1891
- 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
1892
2801
  _files -/
1893
2802
  elif [[ $words[2] == add && $words[CURRENT-1] == --market ]]; then
1894
2803
  _files -/
@@ -1900,7 +2809,9 @@ if (( $+functions[compdef] )); then
1900
2809
  compdef __tt_complete ttheme
1901
2810
  fi
1902
2811
 
1903
- if __tt_active; then
2812
+ () {
2813
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
2814
+ __tt_active || return 0
1904
2815
  [[ -n $TMUX ]] && __tt_tmux
1905
2816
  () {
1906
2817
  local REPLY
@@ -1933,9 +2844,10 @@ if __tt_active; then
1933
2844
  __tt_sync
1934
2845
  fi
1935
2846
  __tt_announce
1936
- fi
2847
+ }
1937
2848
 
1938
2849
  () {
2850
+ emulate -L zsh ${=${options[xtrace]:#off}:+-o xtrace}
1939
2851
  local f
1940
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
1941
2853
  [[ -r $f && ! $f.zwc -nt $f ]] || continue