aboutsummaryrefslogtreecommitdiff
path: root/extras/HOME/.local/bin/quick-clip.sh
blob: 49dfe484ffea7025e498b83f8412918c9b2c41ac (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/env bash

# set -euo pipefail

# --- Config -----------------------------------------------------------------
: "${XDG_CONFIG_HOME:=${HOME}/.config}"
SNIPPETS_DIR="${XDG_CONFIG_HOME}/quick-clip"
SNIPPETS_FILE="${SNIPPETS_DIR}/snippets.tsv"
PROMPT="Quick Clip"
SHOW_NOTIFICATIONS="yes"   # set to "no" to disable notify-send
ROFI_CMD=(rofi -markup-rows -dmenu -i -p "${PROMPT}" -lines 30 -theme-str '* {theme-color: #ffffdd;}' \
  -kb-custom-1 'Alt+d' \
  -kb-custom-2 'Alt+r'
  )

# --- Helpers ----------------------------------------------------------------
copy() {
  # Copy stdin to clipboard using best available tool.
  if [ -n "${WAYLAND_DISPLAY:-}" ] && command -v wl-copy >/dev/null 2>&1; then
    wl-copy --primary && wl-copy # copy to both clipboards on Wayland
  elif command -v xclip >/dev/null 2>&1; then
    xclip -selection clipboard
    printf "%s" "$CLIPBUF" | xclip -selection primary || true
  elif command -v xsel >/dev/null 2>&1; then
    xsel --clipboard --input
    printf "%s" "$CLIPBUF" | xsel --primary --input || true
  else
    echo "quick-clip: no clipboard tool found (need wl-copy, xclip, or xsel)" >&2
    exit 1
  fi
}

notify() {
  if [ "${SHOW_NOTIFICATIONS}" = "yes" ] && command -v notify-send >/dev/null 2>&1; then
    notify-send "${PROMPT}" "$1"
  fi
}

ensure_files() {
  mkdir -p "${SNIPPETS_DIR}"
  if [ ! -f "${SNIPPETS_FILE}" ]; then
    cat >"${SNIPPETS_FILE}" <<'TPL'
# Example snippets (TSV):
# Label	Value
Email	josh@example.com
GitHub	https://github.com/Josh
# Single-field lines copy the whole line:
+1 (555) 867-5309
TPL
  fi
}

open_editor() {
  local editor
  editor="${EDITOR:-}" 
  if [ -z "$editor" ]; then
    if command -v vim >/dev/null 2>&1; then editor=vim
    elif command -v nano >/dev/null 2>&1; then editor=nano
    elif command -v vi >/dev/null 2>&1; then editor=vi
    else
      printf "quick-clip: set $EDITOR or install nano/vim.\n" >&2
      exit 1
    fi
  fi
  alacritty --class "floating-terminal" -e "$editor" "${SNIPPETS_FILE}"
}

add_snippet_via_rofi() {
  local label value
  label=$(printf '' | rofi -dmenu -i -p "new label" -lines 0 || true)
  [ -z "$label" ] && return 0
  value=$(printf '' | rofi -dmenu -i -p "new value" -lines 0 || true)
  [ -z "$value" ] && return 0
  printf '%s\t%s\n' "$label" "$value" >>"${SNIPPETS_FILE}"
  notify "Added snippet: $label"
}

add_snippet() {
  if ! command -v zenity >/dev/null 2>&1; then
    echo "quick-clip: zenity not found (install zenity or change add flow)" >&2
    notify "zenity not found"
    return 1
  fi
  local label value
  label=$(zenity --entry --title="$PROMPT" --text="New snippet label:" 2>/dev/null) || return 0
  value=$(zenity --entry --title="$PROMPT" --text="Value for '$label':" 2>/dev/null) || return 0
  [ -z "$value" ] && return 0
  if [ -z "$label" ] ; then
    printf "%s" "$value"
  else
    printf '%s\t%s\n' "$label" "$value" >>"${SNIPPETS_FILE}"
  fi

  notify "Added snippet: $label"
}

add_snippet_via_yad() {
  if ! command -v yad >/dev/null 2>&1; then
    echo "quick-clip: yad not found (install yad or change add flow)" >&2
    notify "yad not found"
    return 1
  fi
  local out label value
  out=$(yad --form \
            --title="$PROMPT" \
            --center --on-top \
            --window-icon="accessories-text-editor" \
            --field="Label" \
            --field="Value" \
            --borders=10 \
            --separator=$'🔨' 2>/dev/null) || return 0
  IFS=$'🔨' read -r label value <<<"${out%🔨}"
  echo "label: $label, value: $value"
  if [ -z "$label" ]; then
    printf '%s\n' "$value" >>"${SNIPPETS_FILE}"
    notify "Added snippet: $value"
  else
    printf '%s\t%s\n' "$label" "$value" >>"${SNIPPETS_FILE}"
    notify "Added snippet: $label"
  fi
}



# Given a chosen line, determine the text to copy.
extract_value() {
  # If line contains a TAB, take field 2; else copy entire line.
  awk -F "\t" 'NF>=2 {print $2; exit} NF==1 {print $0; exit}'
}

# --- Main -------------------------------------------------------------------
ensure_files

SPEC_ADD="α Add new snippet"
SPEC_EDIT="ε Edit snippets"

# Build menu: actions then snippets (skip comments/blank lines)
mapfile -t MENU < <(
  printf "<b><span foreground='#8888ff'>Snippets</span></b>\t\n"
  awk 'BEGIN{FS="\t"} /^[[:space:]]*#/ {next} NF==0 {next} {print $0}' "${SNIPPETS_FILE}"
  for f in "$HOME/.local/lib/quick-clip"/*; do
    if [ -f "$f" ] && [ -x "$f" ]; then
      "$f"
    fi
  done
  printf "\n<b><span foreground='#8888ff'>Actions</span></b>\t\n"
  printf '%s\n' "$SPEC_ADD" "$SPEC_EDIT"
)

# Show menu; allow custom input
SELECTION=$(printf '%s\n' "${MENU[@]}" | "${ROFI_CMD[@]}")

ec="$?"

echo "EC: $ec"
if [[ "$ec" -eq 10 ]] ; then # Delete the entry
  tfile=/tmp/$(mktemp 'XXXX.tmp')
  grep -Fxv "$SELECTION" "$SNIPPETS_FILE" > "$tfile"
  if [[ "$?" -ne 0 ]] ; then
    notify "Could not delete"
  else
    mv "$tfile" "$SNIPPETS_FILE" 
    notify "Deleted Snippet: $SELECTION"
  fi

  exit 0
fi

if [[ "$ec" -eq 11 ]] ; then # Run the entry
  val=$(extract_value <<< "$SELECTION")
  sh -c "$val"
  exit 0
fi

# If empty (Esc), quit
[ -z "${SELECTION}" ] && exit 0

case "${SELECTION}" in
  $SPEC_ADD)
    add_snippet_via_yad
    exit 0
    ;;
  $SPEC_EDIT)
    open_editor
    exit 0
    ;;
  *)
    ;;
 esac

# If the selection equals a menu entry that came from the file and contains a TAB,
# copy field 2; otherwise, treat it as free text.
CLIPBUF=$(printf '%s' "${SELECTION}" | extract_value)

printf '%s' "${CLIPBUF}" | copy
notify "Copied to clipboard"