Compare commits

...

22 Commits

Author SHA1 Message Date
bcbd2a9077 feat: non-interactive imports 2025-06-15 21:42:45 +02:00
0b8066923b improvement: Calendar preview: weeks start on Mondays 2025-06-15 21:07:15 +02:00
06020740cc improvements: presentation
- collection label before time
- UTF8 arrow insead of `->`
2025-06-14 23:11:55 +02:00
903c870dba improvement: rearranged keys 2025-06-14 22:30:21 +02:00
4a17512819 bugfix: status was from vjournal not vevent 2025-06-13 23:07:31 +02:00
16193b5554 bug fix: recognize all-day events, improvment: view source with v 2025-06-13 22:39:23 +02:00
e09c38ee29 ctrl-g in dayview 2025-06-13 15:44:11 +02:00
7ed2df2399 feat: git support 2025-06-13 15:31:59 +02:00
23cbe26d94 improvement: week view with ctrl-h and ctrl-l 2025-06-13 14:58:49 +02:00
13aebae71f bug fix: return from dayview 2025-06-13 14:57:17 +02:00
93317350f1 bugfix: cleanup at tend 2025-06-13 14:51:56 +02:00
e67fcca02c feat: import 2025-06-13 14:47:01 +02:00
735665bb92 extended description of configuration 2025-06-13 14:12:29 +02:00
0ffa57373a feat:change tz from within application 2025-06-13 14:07:44 +02:00
6d8520a016 non-recursive, clean 2025-06-13 13:04:35 +02:00
ff898c84c8 feat:location support 2025-06-12 21:47:30 +02:00
dc88d5a965 feat: dayview: jump to next/previous day 2025-06-12 15:22:27 +02:00
cdc008e361 bugfix: corrected week calculation (iso) 2025-06-12 14:57:36 +02:00
7549acb20c bugfix: handle wrong date input in entry creation 2025-06-12 12:44:55 +02:00
aee1a1bf24 improvement: no jump to "day" line in search view 2025-06-12 11:40:47 +02:00
4ebcbe36e3 bugfix: disable up/down limits in search view 2025-06-12 11:39:14 +02:00
acc231027b update readme 2025-06-11 22:27:53 +02:00
11 changed files with 1217 additions and 544 deletions

View File

@@ -1,4 +1,7 @@
A [fzf](https://github.com/junegunn/fzf)-based **calendar** application with CalDav support. A [fzf](https://github.com/junegunn/fzf)-based **calendar** application with CalDav support.
If you are interested in this, then you may also be interested in the
corresponding journaling application
[fzf-vjour](https://github.com/baumea/fzf-vjour).
Description and Use Case Description and Use Case
------------------------ ------------------------
@@ -60,37 +63,77 @@ item_types = ["VEVENT"]
... ...
``` ```
Here is the complete list of configuration options:
```
### ROOT: Directory containing the collections
### COLLECTION_LABELS: Mappings between collections and labels
### SYNC_CMD (optional): Synchronization command
### DAY_START (optional): Hour of start of the day (defaults to 8)
### DAY_END (optional): Hour of end of the day (defaults to 18)
### EDITOR (optional): Your favorite editor, is usually already exported
### TZ (optional): Your favorite timezone, usually system's choice
### LC_TIME (optional): Your favorite locale for date and time
### ZI_DIR (optional): Location of tzdata, defaults to /usr/share/zoneinfo
```
Usage Usage
----- -----
Use the default `fzf` keys to navigate your calendar entries, e.g., `ctrl-j`
and `ctrl-k` for going down/up in the list.
After starting `fzf-vcal`, you are presented with a view on the current week. After starting `fzf-vcal`, you are presented with a view on the current week.
You can navigate that week using `j` and `h` for going down and up.
Hit `<enter>` on any day, and you will see all entries for that date, including Hit `<enter>` on any day, and you will see all entries for that date, including
previews. In both, the week and day views, you can add entries by hitting previews. In both, the week and day views, you can add entries by hitting
`ctrl-n`. `ctrl-n`.
Here is the list of available keybindings: Here is the list of all available keybindings:
| Key | View | Action |
| --- | ---- | ------ | ### Week view
| `enter` | week view | Switch to day view |
| `ctrl-n` | week view | Make a new entry | | Key | Action |
| any letter | week view | Search in the list of all entries | | --- | ------ |
| `backspace` on empty query | week view | Undo search | | `enter` | open day |
| `ctrl-u` | week view | Go back one week | | `j` | down |
| `ctrl-d` | week view | Go forth one week | | `k` | up |
| `ctrl-alt-u` | week view | Go back one month | | `l` | go to next week |
| `ctrl-alt-d` | week view | Go forth one month | | `h` | go to previous week |
| `ctrl-s` | week view | Run the synchronization command | | `ctrl-l` | go to next month |
| `ctrl-l` | week view | Go to current week | | `ctrl-h` | go to previous month |
| `ctrl-g` | week view | Goto date | | `alt-l` | go to next year |
| `enter` | day view | Open selected calendar entry in your favorite `$EDITOR` | | `alt-h` | go to previous year |
| `ctrl-n` | day view | Make a new entry | | `ctrl-r` | reload and go to week that contains `today` |
| `esc`, `backspace` or `q` | day view | Go back to week view | | `ctrl-g` | interactively go to specified week |
| `ctrl-s` | day view | Run the synchronization command | | `ctrl-t` | set timezon |
| `ctrl-alt-d` | day view | Delete selected entry | | `ctrl-s` | synchronize |
| `j` | day view | Scroll down in preview window | | `ctrl-n` | add new entry |
| `k` | day view | Scroll up in preview window | | `\` | search all appointment s|
| `w` | day view | Toggle line wrap in preview window ||
### Day view
| Key | Action |
| --- | ------ |
| `enter` | edit appointment |
| `j` | down |
| `k` | up |
| `l` | go to next day |
| `h` | go to previous day |
| `ctrl-l` | go to next week |
| `ctrl-h` | go to previous week |
| `alt-l` | go to next month |
| `alt-h` | go to previous month |
| `ctrl-r` | reload and go to `today` |
| `ctrl-g` | interactively go to specified day |
| `ctrl-t` | set timezon |
| `ctrl-s` | synchronize |
| `ctrl-n` | add new entry |
| `ctrl-alt-d` | delete entry |
| `w` | toggle line wrap in preview |
| `ctrl-d` | down in preview |
| `ctrl-u` | up in preview |
| `alt-v` | view raw iCalendar file |
| `esc` | return to week view, you can also do this with `q` or `backspace` |
### There is more
You may also invoke the script with `--help` to see further command-line options. You may also invoke the script with `--help` to see further command-line options.
@@ -98,6 +141,10 @@ Also, you may set `LC_TIME` to your preferred language, and `TZ` to your
preferred timezone. The latter is in particular helpful if you want to take a preferred timezone. The latter is in particular helpful if you want to take a
look at your calendar relative to being in another timezone. look at your calendar relative to being in another timezone.
Git support
-----------
You can track your events with `git` by simply running `fzf-vcal --git-init`.
License License
------- -------
This project is licensed under the [MIT License](./LICENSE). This project is licensed under the [MIT License](./LICENSE).

19
src/awk/calshift.awk Normal file
View File

@@ -0,0 +1,19 @@
BEGIN {
ORS = ""
W3 = " "
W17 = W3 W3 W3 W3 W3 " "
}
NR == 1 { i++; print $0 "\n"; next }
NR == 2 { i++; print substr($0, 4, 17) " " substr($0, 1, 3) " \n"; next }
NR == 3 && /^ 1/ { print W17; }
NR == 3 && /^ / { print substr($0, 4, 17); next }
/[0-9]/ {
i++
print " " substr($0, 1, 3) " \n" substr($0, 4, 17)
}
END {
i++
print " " W3 " \n"
for (i; i<8; i++)
print " " W17 W3 " \n"
}

View File

@@ -1,26 +1,25 @@
# 11:00|13:00|1748422800|1748430000|fpath|desc... # $s|$e|$starttime|$endtime|$fpath|$collection|$description
# 00:00|00:00|1748296800|1748383200|fpath|desc... function allday(collection, desc) {
function allday(desc) { return collection " " ITALIC FAINT " (allday) " OFF desc
return ITALIC FAINT " (allday) " OFF desc
} }
function endstoday(stop, desc) { function endstoday(stop, collection, desc) {
return CYAN " -- " stop OFF ": " desc return collection " " CYAN " -- " stop OFF ": " desc
} }
function slice(start, stop, desc) { function slice(start, stop, collection, desc) {
if (stop == "00:00") if (stop == "00:00")
return CYAN start " -- " OFF ": " desc return collection " " CYAN start " -- " OFF ": " desc
else else
return CYAN start OFF " -- " CYAN stop OFF ": " desc return collection " " CYAN start OFF " -- " CYAN stop OFF ": " desc
} }
function hrline(hour) { function hrline(hour) {
hour = hour < 10 ? "0"hour : hour hour = hour < 10 ? "0"hour : hour
print hour, "", "", "", FAINT hour ":00 ----------------------" OFF print today, hour, "", "", "", " " FAINT hour ":00 ----------------------" OFF
} }
function hrlines(start, stop, h, starth, stoph, tmp, i) { function hrlines(start, stop, h, starth, stoph, tmp, i) {
starth = substr(start, 1, 2) starth = substr(start, 1, 2)
stoph = substr(stop, 1, 2) stoph = substr(stop, 1, 2)
tmp = substr(start, 4, 2) == "00" ? 0 : 1 tmp = substr(start, 4, 2) == "00" ? 0 : 1
for (i=h; i < starth + tmp; i++) for (i=h; i < starth + tmp && i < dayend; i++)
hrline(i) hrline(i)
tmp = substr(stop, 4, 2) == "00" ? 0 : 1 tmp = substr(stop, 4, 2) == "00" ? 0 : 1
if (stoph + tmp < daystart) if (stoph + tmp < daystart)
@@ -39,11 +38,11 @@ BEGIN {
OFF = "\033[m" OFF = "\033[m"
OFS = "|" OFS = "|"
} }
$1 == "00:00" && $2 == "00:00" { print $1, $3, $4, $5, allday($6); next } $1 == "00:00" && $2 == "00:00" { print today, $1, $3, $4, $5, allday($6, $7); next }
$1 == "00:00" { print $1, $3, $4, $5, endstoday($2, $6); next } $1 == "00:00" { print today, $1, $3, $4, $5, endstoday($2, $6, $7); next }
$1 ~ /^[0-9]{2}:[0-9]{2}$/ { $1 ~ /^[0-9]{2}:[0-9]{2}$/ {
daystart = hrlines($1, $2, daystart, starth, stoph, tmp, i) daystart = hrlines($1, $2, daystart, starth, stoph, tmp, i)
print $1, $3, $4, $5, slice($1, $2, $6) print today, $1, $3, $4, $5, slice($1, $2, $6, $7)
} }
END { END {
hrlines(dayend":00", 0, daystart, starth, stoph, tmp, i) hrlines(dayend":00", 0, daystart, starth, stoph, tmp, i)

View File

@@ -1,17 +1,27 @@
BEGIN { FS="|"; i=0; dlt = -259200; spw = 604800; } BEGIN { FS="|" }
NR == FNR { NR == FNR {
i = i + 1; i = i + 1;
from[i] = int(($1 + dlt)/ spw); from_year[i] = $1
from_week[i] = $2
getline; getline;
to[i] = int(($1 + dlt) / spw); to_year[i] = $1
to_week[i] = $2
next next
} # Load start and end week numbers from first file } # Load start and end week numbers from first file
{ {
if (from[FNR] > to[FNR]) year_i = from_year[FNR]
print "FNR", FNR, ":", from[FNR],"-",to[FNR], " ",$0; week_i = from_week[FNR]
for(i=from[FNR]; i<=to[FNR]; i++) { year_end = to_year[FNR]
week[i] = week[i] " " $5 week_end = to_week[FNR]
while(year_i <= year_end && (year_i < year_end || week_i <= week_end)) {
label = year_i"|"week_i
week[label] = week[label] " " $5
week_i++
if (week_i > 53) {
week_ = 1
year_i++
}
} }
} }
END { for (i in week) print i week[i]; } END { for (label in week) print label week[label]; }

View File

@@ -27,10 +27,18 @@ BEGIN {
desc { desc = desc "\\n" $0; next; } desc { desc = desc "\\n" $0; next; }
{ {
from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""; from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : "";
if (!from)
exit 1
getline getline
to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""; to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : "";
if (!to)
exit 1
getline getline
location = substr($0, 1, 2) == "@ " ? substr($0, 3) : ""
if (location) getline
summary = substr($0, 1, 2) == "# " ? substr($0, 3) : "" summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
if (!summary)
exit 1
getline # This line should be empty getline # This line should be empty
getline # First line of description getline # First line of description
desc = $0; desc = $0;
@@ -41,36 +49,53 @@ END {
# If nanoseconds are not 0, then we assume user enterd "tomorrow" or # If nanoseconds are not 0, then we assume user enterd "tomorrow" or
# something the like, and we make this a date entry, as opposed to a # something the like, and we make this a date entry, as opposed to a
# date-time entry. # date-time entry.
from = from ? from : "now" # Similalry, if the time is 00:00, we make this a date, as opposed to a
# date-time entry.
gsub("\"", "\\\"", from)
cmd = "date -d \"" from "\" +\"%N\""; cmd = "date -d \"" from "\" +\"%N\"";
cmd | getline n
close(cmd)
n = n + 0
cmd = "date -d \"" from "\" +\"%H%M\"";
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (t == 0) { if (n != 0 || t == 0) {
from_type = "DATE-TIME"
cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} else {
from_type = "DATE" from_type = "DATE"
cmd = "date -d \"" from "\" +\"%Y%m%d\""; cmd = "date -d \"" from "\" +\"%Y%m%d\"";
} else {
from_type = "DATE-TIME"
cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} }
cmd | getline from suc = cmd | getline from
close(cmd) close(cmd)
if (suc != 1) {
exit 1
}
# #
to = to ? to : "now" gsub("\"", "\\\"", to)
cmd = "date -d \"" to "\" +\"%N\""; cmd = "date -d \"" to "\" +\"%N\"";
cmd | getline n
close(cmd)
n = n + 0
cmd = "date -d \"" to "\" +\"%H%M\"";
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (t == 0) { if (n != 0 || t == 0) {
to_type = "DATE-TIME"
cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} else {
to_type = "DATE" to_type = "DATE"
cmd = "date -d \"" to "\" +\"%Y%m%d\""; cmd = "date -d \"" to "\" +\"%Y%m%d\"";
} else {
to_type = "DATE-TIME"
cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} }
cmd | getline to suc = cmd | getline to
close(cmd) close(cmd)
if (suc != 1) {
exit 1
}
escape(summary); escape(summary);
escape(location);
escape(desc); escape(desc);
# print ical # print ical
@@ -85,11 +110,12 @@ END {
print "CREATED:" zulu; print "CREATED:" zulu;
print "SEQUENCE:1"; print "SEQUENCE:1";
print "LAST-MODIFIED:" zulu; print "LAST-MODIFIED:" zulu;
print "STATUS:FINAL"; print "STATUS:VEVENT";
print "DTSTART;VALUE=" from_type ":" from print "DTSTART;VALUE=" from_type ":" from
print "DTEND;VALUE=" to_type ":" to print "DTEND;VALUE=" to_type ":" to
if (summary) print_fold("SUMMARY:", summary, i, s); if (summary) print_fold("SUMMARY:", summary, i, s);
if (desc) print_fold("DESCRIPTION:", desc, i, s); if (desc) print_fold("DESCRIPTION:", desc, i, s);
if (location) print_fold("LOCATION:", location, i, s);
print "END:VEVENT" print "END:VEVENT"
print "END:VCALENDAR" print "END:VCALENDAR"
} }

16
src/awk/set.awk Normal file
View File

@@ -0,0 +1,16 @@
function escape(str)
{
gsub("\\\\", "\\\\", str)
gsub(";", "\\\\;", str)
gsub(",", "\\\\,", str)
return str
}
BEGIN { FS = "[:;]"; }
/^BEGIN:VEVENT$/ { inside = 1 }
/^END:VEVENT$/ { inside = 0 }
$1 == field && inside { con = 1; duplic = 1; print field ":" escape(value); next }
$1 == field && duplic { con = 1; next }
/^ / && con { next }
/^ / && con { next }
/^[^ ]/ && con { con = 0 }
{ print }

View File

@@ -36,47 +36,70 @@ ENDFILE {
# If nanoseconds are not 0, then we assume user enterd "tomorrow" or # If nanoseconds are not 0, then we assume user enterd "tomorrow" or
# something the like, and we make this a date entry, as opposed to a # something the like, and we make this a date entry, as opposed to a
# date-time entry. # date-time entry.
from = from ? from : "now" gsub("\"", "\\\"", from)
cmd = "date -d \"" from "\" +\"%N\""; cmd = "date -d \"" from "\" +\"%N\"";
cmd | getline n
close(cmd)
n = n + 0
cmd = "date -d \"" from "\" +\"%H%M\"";
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (t == 0) { if (n != 0 || t == 0) {
from_type = "DATE-TIME"
cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} else {
from_type = "DATE" from_type = "DATE"
cmd = "date -d \"" from "\" +\"%Y%m%d\""; cmd = "date -d \"" from "\" +\"%Y%m%d\"";
} else {
from_type = "DATE-TIME"
cmd = "date -d \"" from "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} }
cmd | getline from suc = cmd | getline from
close(cmd) close(cmd)
if (suc != 1) {
exit 1
}
# #
to = to ? to : "now" gsub("\"", "\\\"", to)
cmd = "date -d \"" to "\" +\"%N\""; cmd = "date -d \"" to "\" +\"%N\"";
cmd | getline n
close(cmd)
n = n + 0
cmd = "date -d \"" to "\" +\"%H%M\"";
cmd | getline t cmd | getline t
close(cmd) close(cmd)
t = t + 0 t = t + 0
if (t == 0) { if (n != 0 || t == 0) {
to_type = "DATE-TIME"
cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} else {
to_type = "DATE" to_type = "DATE"
cmd = "date -d \"" to "\" +\"%Y%m%d\""; cmd = "date -d \"" to "\" +\"%Y%m%d\"";
} else {
to_type = "DATE-TIME"
cmd = "date -d \"" to "\" +\"@%s\" | xargs date -u +\"%Y%m%dT%H%M00Z\" -d"
} }
cmd | getline to suc = cmd | getline to
close(cmd) close(cmd)
if (suc != 1) {
exit 1
} }
escape(summary); escape(summary);
escape(location);
escape(desc); escape(desc);
}
} }
NR == FNR && desc { desc = desc "\\n" $0; next; } NR == FNR && desc { desc = desc "\\n" $0; next; }
NR == FNR { NR == FNR {
from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : ""; from = substr($0, 1, 6) == "::: |>" ? substr($0, 8) : "";
if (!from)
exit 1
getline getline
to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : ""; to = substr($0, 1, 6) == "::: <|" ? substr($0, 8) : "";
if (!to)
exit 1
getline getline
location = substr($0, 1, 2) == "@ " ? substr($0, 3) : ""
if (location) getline
summary = substr($0, 1, 2) == "# " ? substr($0, 3) : "" summary = substr($0, 1, 2) == "# " ? substr($0, 3) : ""
if (!summary)
exit 1
getline # This line should be empty getline # This line should be empty
getline # First line of description getline # First line of description
desc = $0; desc = $0;
@@ -86,7 +109,7 @@ NR == FNR {
/^BEGIN:VEVENT$/ { inside = 1; print; next } /^BEGIN:VEVENT$/ { inside = 1; print; next }
/^X-ALT-DESC/ && inside { next } # drop this alternative description /^X-ALT-DESC/ && inside { next } # drop this alternative description
/^ / && inside { next } # drop this folded line (the only content with folded lines will be updated) /^ / && inside { next } # drop this folded line (the only content with folded lines will be updated)
/^(DTSTART|DTEND|SUMMARY|CATEGORIES|DESCRIPTION|LAST-MODIFIED)/ && inside { next } # skip for now, we will write updated fields at the end /^(DTSTART|DTEND|SUMMARY|LOCATION|CATEGORIES|DESCRIPTION|LAST-MODIFIED)/ && inside { next } # skip for now, we will write updated fields at the end
/^SEQUENCE/ && inside { seq = $2; next } # store sequence number and skip /^SEQUENCE/ && inside { seq = $2; next } # store sequence number and skip
/^END:VEVENT$/ { /^END:VEVENT$/ {
seq = seq ? seq + 1 : 1 seq = seq ? seq + 1 : 1
@@ -96,6 +119,7 @@ NR == FNR {
print "DTEND;VALUE=" to_type ":" to print "DTEND;VALUE=" to_type ":" to
print_fold("SUMMARY:", summary, i, s) print_fold("SUMMARY:", summary, i, s)
print_fold("DESCRIPTION:", desc, i, s) print_fold("DESCRIPTION:", desc, i, s)
print_fold("LOCATION:", location, i, s)
inside = "" inside = ""
} }
{ print } { print }

View File

@@ -12,7 +12,7 @@ BEGIN {
} }
/^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next } /^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next }
/^[0-7] 00:00 -- / { dayline = dayline " <--" $4 " " c(); next } /^[0-7] 00:00 -- / { dayline = dayline " <--" $4 " " c(); next }
/^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "--> " c(); next } /^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "" c(); next }
/^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next } /^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next }
/^[0-7]$/ && dayline { print "+", startofweek " +" $0-1 " days", "", dayline; } /^[0-7]$/ && dayline { print "+", startofweek " +" $0-1 " days", "", dayline; }
/^[0-7]$/ { /^[0-7]$/ {

File diff suppressed because it is too large Load Diff