@kecan0406/ttheme 1.0.33 → 1.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.33",
2
+ "version": "1.0.35",
3
3
  "gate": [
4
4
  {
5
5
  "rule": "foreground",
@@ -46,21 +46,6 @@
46
46
  "margin": 0.03,
47
47
  "stands": 12
48
48
  },
49
- "font": {
50
- "family": "JetBrainsMono Nerd Font",
51
- "size": 14,
52
- "codepointMap": [
53
- {
54
- "range": "U+AC00-U+D7A3",
55
- "family": "D2Coding"
56
- },
57
- {
58
- "range": "U+3130-U+318F",
59
- "family": "D2Coding"
60
- }
61
- ]
62
- },
63
- "shader": "cursor_tail.glsl",
64
49
  "palettes": [
65
50
  {
66
51
  "name": "neutral",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kecan0406/ttheme",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "ttheme": "bin/ttheme.js"
@@ -8,12 +8,10 @@
8
8
  "files": [
9
9
  "bin",
10
10
  "dist/manifest.json",
11
- "dist/ghostty/ttheme.conf",
12
11
  "shell",
13
- "!shell/check.zsh",
14
- "ghostty/shaders"
12
+ "!shell/check.zsh"
15
13
  ],
16
- "description": "Character terminal palettes for ghostty, kitty, alacritty, wezterm and iTerm2",
14
+ "description": "Character terminal palettes for Ghostty, kitty, Alacritty, WezTerm, iTerm2, Windows Terminal and Warp",
17
15
  "license": "MIT",
18
16
  "author": "kecan0406",
19
17
  "repository": {
@@ -31,6 +31,8 @@ __tt_osc_reset() {
31
31
  return 0
32
32
  }
33
33
 
34
+ __tt_reset_reloaded() { __tt_osc_reset }
35
+
34
36
  __tt_repaint() {
35
37
  if [[ -n $TTHEME_SPEC ]]; then
36
38
  __tt_apply "$TTHEME_SPEC"
@@ -4,6 +4,19 @@ __tt_keepable() { return 0 }
4
4
 
5
5
  __tt_bg_shown() { __tt_ghostty_shown }
6
6
 
7
+ __tt_reset_reloaded() {
8
+ local REPLY
9
+ integer i
10
+ __tt_osc_reset
11
+ [[ -n $1 ]] && (( ${TTHEME_TERMINALS[(Ie)ghostty]} && ! TTHEME_TMUX )) || return 0
12
+ for (( i = 0; i < 20; i++ )); do
13
+ __tt_query_bg || return 0
14
+ [[ ${(L)REPLY} == ${(L)1} ]] || return 0
15
+ sleep 0.05
16
+ __tt_osc_reset
17
+ done
18
+ }
19
+
7
20
  __tt_shown() {
8
21
  local dir=${TTHEME_CONFIG:h}/backgrounds was REPLY
9
22
  __tt_bg_shown
@@ -17,7 +30,7 @@ __tt_shown() {
17
30
  __tt_bg_cells() {
18
31
  local fd saved resp="" line c v f
19
32
  local -i px=2 py=2 fs=0 sc=2
20
- for f in ${XDG_CONFIG_HOME:-$HOME/.config}/ghostty/config ${TTHEME_CONFIG:h}/ttheme.conf; do
33
+ for f in ${XDG_CONFIG_HOME:-$HOME/.config}/ghostty/config; do
21
34
  [[ -r $f ]] || continue
22
35
  for line in "${(@f)$(<$f)}"; do
23
36
  v=${${line#*=}// /}
@@ -8,4 +8,4 @@ if [[ ${TTHEME_TAB_PALETTE:-off} != off ]]; then
8
8
  export TTHEME_SPEC=$REPLY
9
9
  fi
10
10
 
11
- exec /bin/zsh -l
11
+ exec ${SHELL:-/bin/zsh} -l
package/shell/ttheme.zsh CHANGED
@@ -12,8 +12,36 @@ __tt_palettes_load() {
12
12
 
13
13
  __tt_fresh() {
14
14
  local -a at
15
- zstat -F %s.%N -A at +mtime -- $TTHEME_HOME/palettes.zsh 2>/dev/null || return 0
16
- [[ $at[1] == "$TTHEME_PALETTES_AT" ]] || { __tt_palettes_load && __tt_reloaded }
15
+ local was=$TTHEME_STARTUP
16
+ if ! zstat -F %s.%N -A at +mtime -- $TTHEME_HOME/palettes.zsh 2>/dev/null; then
17
+ [[ -e $TTHEME_HOME ]] || __tt_gone
18
+ return 0
19
+ fi
20
+ [[ $at[1] == "$TTHEME_PALETTES_AT" ]] && return 0
21
+ __tt_palettes_load && __tt_reloaded
22
+ [[ -n $was && -z $TTHEME_STARTUP && -n $TTHEME_SPEC ]] && __tt_active && __tt_off_here
23
+ }
24
+
25
+ __tt_gone() {
26
+ local bg=${TTHEME_SPEC%% *} hook
27
+ TTHEME_SPEC=
28
+ __tt_reload
29
+ __tt_reset_reloaded $bg
30
+ print -n $'\e[?1004l'
31
+ for hook in precmd:__tt_fresh precmd:__tt_precmd precmd:__tt_unmux preexec:__tt_preexec preexec:__tt_mux chpwd:__tt_chpwd; do
32
+ add-zsh-hook -d ${hook%%:*} ${hook#*:}
33
+ done
34
+ unfunction ttheme
35
+ }
36
+
37
+ __tt_off_here() {
38
+ local bg=${TTHEME_SPEC%% *}
39
+ TTHEME_SPEC=
40
+ if __tt_shown "" || [[ $1 == reload ]]; then
41
+ __tt_reload
42
+ fi
43
+ __tt_unshown force
44
+ __tt_reset_reloaded $bg
17
45
  }
18
46
 
19
47
  if [[ -r $TTHEME_HOME/palettes.zsh ]]; then
@@ -134,7 +162,7 @@ __tt_spec() {
134
162
 
135
163
  __tt_name_of() { REPLY=${${(k)TTHEME_PALETTE[(re)$1]}:-custom} }
136
164
 
137
- __tt_color() { [[ -t 1 && -z $NO_COLOR ]] }
165
+ __tt_color() { [[ -t 1 && -z $NO_COLOR && $TERM != dumb ]] }
138
166
 
139
167
  __tt_palette_line() {
140
168
  local name=$1 marker=$2
@@ -379,34 +407,60 @@ __tt_menu() {
379
407
  [[ $k == $cur ]] && mark=$gutter
380
408
  __tt_palette_line "$k" "$mark"
381
409
  done
382
- printf '\n\033[2mttheme <name> paints this tab · ttheme preview · ttheme help\033[0m\n'
410
+ printf '\n\033[2mttheme use <palette> paints this tab · ttheme preview · ttheme help\033[0m\n'
383
411
  }
384
412
 
385
413
  __tt_help() {
386
- print -r -- 'ttheme — character terminal palettes
414
+ local v
415
+ print -r -- $'ttheme — character terminal palettes\n'
416
+ print -r -- " ${(r:16:):-ttheme}list every palette, grouped, with previews"
417
+ for v in $TTHEME_VERBS; do
418
+ if [[ $v == - ]]; then
419
+ print
420
+ else
421
+ print -r -- " ${(r:16:)${:-ttheme $v}}$TTHEME_VERB_ABOUT[$v]"
422
+ fi
423
+ done
424
+ print -r -- $'\nttheme <command> --help describes one command · ttheme --version prints the version'
425
+ }
387
426
 
388
- ttheme list every palette, grouped, with previews
389
- ttheme homura paint this tab (a unique prefix works: ttheme ho)
390
- ttheme preview browse live — focus repaints, enter keeps (this tab or default), esc restores, ? lists keys
391
- ttheme next advance this tab to the next palette
392
- ttheme default make a palette the one new tabs open with
393
- ttheme off open new tabs in the terminal'"'"'s own colors — ttheme on wears the default again
394
- ttheme pin pick a palette for this directory — cd into it repaints, cd out restores
395
- ttheme unpin drop the palette pinned to this directory
396
- ttheme config edit settings in $EDITOR — they apply in new tabs
427
+ __tt_usage() {
428
+ print -r -- "Usage: ttheme ${${:-$1 $TTHEME_VERB_ARGS[$1]}% }"$'\n\n'"$TTHEME_VERB_ABOUT[$1]"
429
+ }
397
430
 
398
- ttheme browse pick palettes from the catalog in a live picker
399
- ttheme list show the catalog, marking what is installed
400
- ttheme add install palettes from the catalog
401
- ttheme remove uninstall palettes
402
- ttheme update refresh the catalog from the registry'
431
+ __tt_verb_help() {
432
+ if (( ${TTHEME_SHELL_VERBS[(Ie)$1]} )); then
433
+ __tt_usage $1
434
+ elif [[ -n $1 && -n ${TTHEME_VERB_ABOUT[$1]} ]]; then
435
+ __tt_cli $1 --help
436
+ else
437
+ __tt_help
438
+ fi
439
+ }
440
+
441
+ __tt_misuse() {
442
+ print -u2 -r -- "ttheme $1: $2"$'\n'
443
+ __tt_usage $1 >&2
444
+ return 1
445
+ }
446
+
447
+ __tt_arity() {
448
+ local verb=$1
449
+ shift
450
+ local -a want=(${=TTHEME_VERB_ARGS[$verb]})
451
+ local least=${#${(M)want:#\<*}} most=${#want}
452
+ (( ${#${(M)want:#*...*}} )) && most=$#
453
+ (( $# >= least )) || { __tt_misuse $verb "missing $want[$# + 1]"; return }
454
+ (( $# <= most )) || { __tt_misuse $verb "too many arguments: ${argv[most + 1, -1]}"; return }
403
455
  }
404
456
 
405
457
  __tt_cli() {
406
- TTHEME_FIND_RATING=$TTHEME_FIND_RATING TTHEME_FIND_BLOCK=$TTHEME_FIND_BLOCK \
407
- TTHEME_FIND_POSTS=$TTHEME_FIND_POSTS TTHEME_FIND_SOLO=$TTHEME_FIND_SOLO TTHEME_FIND_ORDER=$TTHEME_FIND_ORDER TTHEME_FIND_SETS=$TTHEME_FIND_SETS \
408
- TTHEME_FIND_HOSTS=$TTHEME_FIND_HOSTS TTHEME_FIND_UNBLOCK=$TTHEME_FIND_UNBLOCK TTHEME_FIND_CUTOUTS=$TTHEME_FIND_CUTOUTS TTHEME_FIND_REMOVE_BG=$TTHEME_FIND_REMOVE_BG \
409
- node $TTHEME_HOME/ttheme.js "$@"
458
+ local k
459
+ local -a pass=()
460
+ for k in $TTHEME_SETTINGS; do
461
+ (( $+parameters[$k] )) && pass+=("$k=${(P)k}")
462
+ done
463
+ env $pass node $TTHEME_HOME/ttheme.js "$@"
410
464
  }
411
465
 
412
466
  __tt_catalog() {
@@ -421,18 +475,20 @@ __tt_catalog() {
421
475
 
422
476
  __tt_switch() {
423
477
  local spec
424
- __tt_catalog $1 || return
425
- __tt_active || return 0
478
+ __tt_cli "$@" || return
479
+ [[ -r $TTHEME_HOME/palettes.zsh ]] && __tt_palettes_load && __tt_reloaded
480
+ if ! __tt_active; then
481
+ __tt_reload
482
+ return 0
483
+ fi
426
484
  if [[ $1 == off ]]; then
427
- __tt_osc_reset
428
- TTHEME_SPEC=
429
- __tt_shown "" && __tt_reload
430
- __tt_unshown force
485
+ __tt_off_here reload
431
486
  elif [[ -n $TTHEME_STARTUP ]]; then
432
487
  spec=${TTHEME_PALETTE[$TTHEME_STARTUP]}
433
488
  __tt_apply "$spec"
434
489
  TTHEME_SPEC=$spec
435
- __tt_shown "$TTHEME_STARTUP" force && __tt_reload
490
+ __tt_shown "$TTHEME_STARTUP" force
491
+ __tt_reload
436
492
  fi
437
493
  }
438
494
 
@@ -1559,7 +1615,21 @@ __tt_preview() {
1559
1615
  }
1560
1616
 
1561
1617
  ttheme() {
1562
- if [[ $TTHEME_ADAPTER == warp && $1 != (-h|--help|help|browse|list|add|remove|update|config|default|on|off) ]]; then
1618
+ if (( $# > 1 && ${${argv[2,-1]}[(I)(-h|--help)]} )); then
1619
+ __tt_verb_help $1
1620
+ return
1621
+ fi
1622
+ case $1 in
1623
+ -h|--help) __tt_help; return 0 ;;
1624
+ -V|--version) __tt_cli --version; return ;;
1625
+ help) __tt_verb_help $2; return 0 ;;
1626
+ -*) print -u2 "ttheme: unknown option $1 — see \`ttheme help\`"; return 1 ;;
1627
+ esac
1628
+ if (( $# )) && [[ -z ${TTHEME_VERB_ABOUT[$1]} ]]; then
1629
+ print -u2 "ttheme: unknown command '$1' — see \`ttheme help\`"
1630
+ return 1
1631
+ fi
1632
+ if [[ $TTHEME_ADAPTER == warp && $1 != (browse|list|add|remove|update|config|default|on|off) ]]; then
1563
1633
  print -u2 "ttheme: Warp wears one theme app-wide and paints no tab background of its own — \`ttheme default <palette>\` puts one on every Warp window"
1564
1634
  return 1
1565
1635
  fi
@@ -1570,41 +1640,43 @@ ttheme() {
1570
1640
  fi
1571
1641
 
1572
1642
  case $1 in
1573
- -h|--help|help) __tt_help; return 0 ;;
1574
1643
  browse|list|add|remove|update) __tt_catalog "$@"; return ;;
1575
- config) __tt_config; return ;;
1644
+ on|off) __tt_switch "$@"; return ;;
1576
1645
  esac
1577
-
1646
+ __tt_arity "$@" || return
1647
+ [[ $1 == config ]] && { __tt_config; return }
1578
1648
  (( ${#TTHEME_PALETTE} )) || { __tt_empty; return }
1579
1649
 
1650
+ local REPLY spec
1580
1651
  case $1 in
1581
- next) __tt_rotate; return ;;
1652
+ use)
1653
+ __tt_resolve "$2" || return 1
1654
+ spec=${TTHEME_PALETTE[$REPLY]}
1655
+ __tt_apply "$spec"
1656
+ TTHEME_SPEC=$spec
1657
+ __tt_shown "$REPLY" force && __tt_reload
1658
+ __tt_announce ;;
1659
+ next) __tt_rotate ;;
1582
1660
  default)
1583
- [[ -n $2 ]] || { print -u2 "ttheme default: name a palette — see \`ttheme help\`"; return 1 }
1584
- local REPLY
1585
1661
  __tt_resolve "$2" || return 1
1586
- __tt_keep "$REPLY"
1587
- return ;;
1588
- on|off) __tt_switch $1; return ;;
1589
- preview) __tt_preview; return ;;
1590
- pin) __tt_preview pin; return ;;
1591
- unpin) __tt_unpin; return ;;
1592
- -*) print -u2 "ttheme: unknown option $1 — see \`ttheme help\`"; return 1 ;;
1662
+ __tt_keep "$REPLY" ;;
1663
+ preview) __tt_preview ;;
1664
+ pin) __tt_preview pin ;;
1665
+ unpin) __tt_unpin ;;
1593
1666
  esac
1594
-
1595
- local REPLY
1596
- __tt_resolve "$1" || return 1
1597
- local spec=${TTHEME_PALETTE[$REPLY]}
1598
- __tt_apply "$spec"
1599
- TTHEME_SPEC=$spec
1600
- __tt_shown "$REPLY" force && __tt_reload
1601
- __tt_announce
1602
1667
  }
1603
1668
 
1604
1669
  if (( $+functions[compdef] )); then
1605
1670
  __tt_complete() {
1606
- (( CURRENT == 2 )) && compadd -- preview next default on off pin unpin config help browse list add remove update
1607
- compadd -- $TTHEME_ORDER
1671
+ local -a reply
1672
+ if (( CURRENT == 2 )); then
1673
+ compadd -- ${TTHEME_VERBS:#-} help
1674
+ elif [[ $words[2] == remove || ( $words[2] == (use|default) && CURRENT == 3 ) ]]; then
1675
+ compadd -- $TTHEME_ORDER
1676
+ elif [[ $words[2] == add ]]; then
1677
+ reply=(${${${(M)${(f)"$(__tt_cli list --json 2>/dev/null)"}:#*\"name\": *}#*\"name\": \"}%%\"*})
1678
+ compadd -- ${reply:|TTHEME_ORDER}
1679
+ fi
1608
1680
  }
1609
1681
  compdef __tt_complete ttheme
1610
1682
  fi
@@ -1,9 +0,0 @@
1
- # ttheme — shared font and shader, identical for every palette.
2
- # Pick colors with: theme = <palette>
3
- font-family = JetBrainsMono Nerd Font
4
- font-size = 14
5
- font-codepoint-map = U+AC00-U+D7A3=D2Coding
6
- font-codepoint-map = U+3130-U+318F=D2Coding
7
-
8
- custom-shader = ~/.config/ghostty/shaders/cursor_tail.glsl
9
- custom-shader-animation = true
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Sahaj Bhatt
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,31 +0,0 @@
1
- # Shaders
2
-
3
- Ghostty is the only Tier-1 terminal that runs GLSL, so nothing here applies to
4
- kitty, Alacritty, WezTerm or iTerm2. A theme picks one with `[ghostty] shader`
5
- in its TOML; the built config wires it up as `custom-shader`.
6
-
7
- Shaders apply to the whole Ghostty instance, not per tab — so switching a tab's
8
- palette does not switch its shader.
9
-
10
- ## Included
11
-
12
- Both come from [sahaj-b/ghostty-cursor-shaders][sahaj] (MIT — see `LICENSE`).
13
-
14
- | File | Effect | Local change |
15
- |---|---|---|
16
- | `cursor_tail.glsl` | comet trail behind the cursor | `DURATION` 0.09 → 0.05 (shorter tail) |
17
- | `ripple_cursor.glsl` | expanding ring on cursor move | fixed `COLOR` → theme cursor via `iCurrentCursorColor` |
18
-
19
- Trail length lives in `DURATION` and `MAX_TRAIL_LENGTH` inside the file.
20
-
21
- ## Not included
22
-
23
- These are in use locally but ship no license, so they are not redistributed
24
- here. Drop them into this directory yourself if you want them:
25
-
26
- - `bloom.glsl`, `bloom-subtle.glsl` — [qwerasd205's gist][bloom]
27
- - `gradient-background.glsl`, `animated-gradient-shader.glsl` — [unkn0wncode][unk]
28
-
29
- [sahaj]: https://github.com/sahaj-b/ghostty-cursor-shaders
30
- [bloom]: https://gist.github.com/qwerasd205/c3da6c610c8ffe17d6d2d3cc7068f17f
31
- [unk]: https://github.com/unkn0wncode
@@ -1,243 +0,0 @@
1
- // cursor_tail.glsl — from sahaj-b/ghostty-cursor-shaders (MIT).
2
- // https://github.com/sahaj-b/ghostty-cursor-shaders
3
- // See LICENSE in this directory. Local change: DURATION tuned.
4
-
5
- // sRGB -> Linear conversion (needed because Ghostty passes sRGB values but the shader pipeline operates in linear color space)
6
- vec3 sRGBToLinear(vec3 c) {
7
- return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c));
8
- }
9
-
10
- // --- CONFIGURATION ---
11
- vec4 TRAIL_COLOR = vec4(sRGBToLinear(iCurrentCursorColor.rgb), iCurrentCursorColor.a); // for custom color: vec4(0.2, 0.6, 1.0, 0.5); (wrap in sRGBToLinear for correct brightness)
12
- const float DURATION = 0.05; // in seconds
13
- const float MAX_TRAIL_LENGTH = 0.2;
14
- const float THRESHOLD_MIN_DISTANCE = 1.5; // min distance to show trail (units of cursor width)
15
- const float BLUR = 2.0; // blur size in pixels (for antialiasing)
16
-
17
- // --- CONSTANTS for easing functions ---
18
- const float PI = 3.14159265359;
19
- const float C1_BACK = 1.70158;
20
- const float C2_BACK = C1_BACK * 1.525;
21
- const float C3_BACK = C1_BACK + 1.0;
22
- const float C4_ELASTIC = (2.0 * PI) / 3.0;
23
- const float C5_ELASTIC = (2.0 * PI) / 4.5;
24
- const float SPRING_STIFFNESS = 9.0;
25
- const float SPRING_DAMPING = 0.9;
26
-
27
- // --- EASING FUNCTIONS ---
28
-
29
- // // Linear
30
- // float ease(float x) {
31
- // return x;
32
- // }
33
-
34
- // // EaseOutQuad
35
- // float ease(float x) {
36
- // return 1.0 - (1.0 - x) * (1.0 - x);
37
- // }
38
-
39
- // // EaseOutCubic
40
- // float ease(float x) {
41
- // return 1.0 - pow(1.0 - x, 3.0);
42
- // }
43
-
44
-
45
- // // EaseOutQuart
46
- // float ease(float x) {
47
- // return 1.0 - pow(1.0 - x, 4.0);
48
- // }
49
-
50
- // // EaseOutQuint
51
- // float ease(float x) {
52
- // return 1.0 - pow(1.0 - x, 5.0);
53
- // }
54
-
55
- // // EaseOutSine
56
- // float ease(float x) {
57
- // return sin((x * PI) / 2.0);
58
- // }
59
-
60
- // // EaseOutExpo
61
- // float ease(float x) {
62
- // return x == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * x);
63
- // }
64
-
65
- // EaseOutCirc
66
- float ease(float x) {
67
- return sqrt(1.0 - pow(x - 1.0, 2.0));
68
- }
69
-
70
- // // EaseOutBack
71
- // float ease(float x) {
72
- // return 1.0 + C3_BACK * pow(x - 1.0, 3.0) + C1_BACK * pow(x - 1.0, 2.0);
73
- // }
74
-
75
- // // EaseOutElastic
76
- // float ease(float x) {
77
- // return x == 0.0 ? 0.0
78
- // : x == 1.0 ? 1.0
79
- // : pow(2.0, -10.0 * x) * sin((x * 10.0 - 0.75) * C4_ELASTIC) + 1.0;
80
- // }
81
-
82
- // Parametric Spring
83
- // float ease(float x) {
84
- // x = clamp(x, 0.0, 1.0);
85
- // float decay = exp(-SPRING_DAMPING * SPRING_STIFFNESS * x);
86
- // float freq = sqrt(SPRING_STIFFNESS * (1.0 - SPRING_DAMPING * SPRING_DAMPING));
87
- // float osc = cos(freq * 6.283185 * x) + (SPRING_DAMPING * sqrt(SPRING_STIFFNESS) / freq) * sin(freq * 6.283185 * x);
88
- // return 1.0 - decay * osc;
89
- // }
90
-
91
- float getSdfRectangle(in vec2 p, in vec2 xy, in vec2 b)
92
- {
93
- vec2 d = abs(p - xy) - b;
94
- return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
95
- }
96
-
97
- // Based on Inigo Quilez's 2D distance functions article: https://iquilezles.org/articles/distfunctions2d/
98
- // Potencially optimized by eliminating conditionals and loops to enhance performance and reduce branching
99
-
100
- float seg(in vec2 p, in vec2 a, in vec2 b, inout float s, float d) {
101
- vec2 e = b - a;
102
- vec2 w = p - a;
103
- vec2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
104
- float segd = dot(p - proj, p - proj);
105
- d = min(d, segd);
106
-
107
- float c0 = step(0.0, p.y - a.y);
108
- float c1 = 1.0 - step(0.0, p.y - b.y);
109
- float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x);
110
- float allCond = c0 * c1 * c2;
111
- float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2);
112
- float flip = mix(1.0, -1.0, step(0.5, allCond + noneCond));
113
- s *= flip;
114
- return d;
115
- }
116
-
117
- float getSdfParallelogram(in vec2 p, in vec2 v0, in vec2 v1, in vec2 v2, in vec2 v3) {
118
- float s = 1.0;
119
- float d = dot(p - v0, p - v0);
120
-
121
- d = seg(p, v0, v3, s, d);
122
- d = seg(p, v1, v0, s, d);
123
- d = seg(p, v2, v1, s, d);
124
- d = seg(p, v3, v2, s, d);
125
-
126
- return s * sqrt(d);
127
- }
128
-
129
- vec2 normalize(vec2 value, float isPosition) {
130
- return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y;
131
- }
132
-
133
- float antialising(float distance) {
134
- return 1. - smoothstep(0., normalize(vec2(BLUR, BLUR), 0.).x, distance);
135
- }
136
-
137
- float determineIfTopRightIsLeading(vec2 a, vec2 b) {
138
- float condition1 = step(b.x, a.x) * step(a.y, b.y); // a.x < b.x && a.y > b.y
139
- float condition2 = step(a.x, b.x) * step(b.y, a.y); // a.x > b.x && a.y < b.y
140
-
141
- // if neither condition is met, return 1 (else case)
142
- return 1.0 - max(condition1, condition2);
143
- }
144
-
145
- vec2 getRectangleCenter(vec4 rectangle) {
146
- return vec2(rectangle.x + (rectangle.z / 2.), rectangle.y - (rectangle.w / 2.));
147
- }
148
-
149
-
150
- void mainImage(out vec4 fragColor, in vec2 fragCoord){
151
- #if !defined(WEB)
152
- fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy);
153
- #endif
154
-
155
- // normalization & setup(-1, 1 coords)
156
- vec2 vu = normalize(fragCoord, 1.);
157
- vec2 offsetFactor = vec2(-.5, 0.5);
158
-
159
- vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.));
160
- vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.));
161
-
162
- vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor);
163
- vec2 centerCP = previousCursor.xy - (previousCursor.zw * offsetFactor);
164
-
165
- vec2 delta = centerCP - centerCC;
166
- float lineLength = length(delta);
167
-
168
- float sdfCurrentCursor = getSdfRectangle(vu, centerCC, currentCursor.zw * 0.5);
169
-
170
- vec4 newColor = vec4(fragColor);
171
-
172
- float minDist = currentCursor.w * THRESHOLD_MIN_DISTANCE;
173
- float progress = clamp((iTime - iTimeCursorChange) / DURATION, 0.0, 1.0);
174
- if (lineLength > minDist) {
175
- // ANIMATION logic
176
-
177
- float head_eased = 0.0;
178
- float tail_eased = 0.0;
179
-
180
- float tail_delay_factor = MAX_TRAIL_LENGTH / lineLength;
181
-
182
- float isLongMove = step(MAX_TRAIL_LENGTH, lineLength);
183
-
184
- float head_eased_short = ease(progress);
185
- float tail_eased_short = ease(smoothstep(tail_delay_factor, 1.0, progress));
186
- float head_eased_long = 1.0;
187
- float tail_eased_long = ease(progress);
188
-
189
- head_eased = mix(head_eased_long, head_eased_short, isLongMove);
190
- tail_eased = mix(tail_eased_long, tail_eased_short, isLongMove);
191
-
192
- // detect straight moves
193
- vec2 delta_abs = abs(centerCC - centerCP);
194
- float threshold = 0.001;
195
- float isHorizontal = step(delta_abs.y, threshold);
196
- float isVertical = step(delta_abs.x, threshold);
197
- float isStraightMove = max(isHorizontal, isVertical);
198
-
199
- // -- Making the parallelogram sdf (diagonal move) --
200
-
201
- // animate the TOP-LEFT corners
202
- vec2 head_pos_tl = mix(previousCursor.xy, currentCursor.xy, head_eased);
203
- vec2 tail_pos_tl = mix(previousCursor.xy, currentCursor.xy, tail_eased);
204
-
205
- float isTopRightLeading = determineIfTopRightIsLeading(currentCursor.xy, previousCursor.xy);
206
- float isBottomLeftLeading = 1.0 - isTopRightLeading;
207
-
208
- // v0, v1 : "front" of the trail (head)
209
- vec2 v0 = vec2(head_pos_tl.x + currentCursor.z * isTopRightLeading, head_pos_tl.y - currentCursor.w);
210
- vec2 v1 = vec2(head_pos_tl.x + currentCursor.z * isBottomLeftLeading, head_pos_tl.y);
211
-
212
- // v2, v3: "back" of the trail (tail)
213
- vec2 v2 = vec2(tail_pos_tl.x + currentCursor.z * isBottomLeftLeading, tail_pos_tl.y);
214
- vec2 v3 = vec2(tail_pos_tl.x + currentCursor.z * isTopRightLeading, tail_pos_tl.y - previousCursor.w);
215
-
216
- float sdfTrail_diag = getSdfParallelogram(vu, v0, v1, v2, v3);
217
-
218
- // -- Making the rectangle sdf (straight move) --
219
-
220
- vec2 head_center = mix(centerCP, centerCC, head_eased);
221
- vec2 tail_center = mix(centerCP, centerCC, tail_eased);
222
-
223
- vec2 min_center = min(head_center, tail_center);
224
- vec2 max_center = max(head_center, tail_center);
225
-
226
- vec2 box_size = (max_center - min_center) + currentCursor.zw;
227
- vec2 box_center = (min_center + max_center) * 0.5;
228
-
229
- float sdfTrail_rect = getSdfRectangle(vu, box_center, box_size * 0.5);
230
-
231
- // -- FINAL SELECTING AND DRAWING --
232
- float sdfTrail = mix(sdfTrail_diag, sdfTrail_rect, isStraightMove);
233
-
234
- vec4 trail = TRAIL_COLOR;
235
- float trailAlpha = antialising(sdfTrail);
236
- newColor = mix(newColor, trail, trailAlpha);
237
-
238
- // punch hole
239
- newColor = mix(newColor, fragColor, step(sdfCurrentCursor, 0.));
240
- }
241
-
242
- fragColor = newColor;
243
- }