timely-calendar 1.0.3 → 1.1.0
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.
- checksums.yaml +4 -4
- data/lib/timely/ui/application.rb +66 -48
- data/lib/timely/ui/views/month.rb +3 -3
- data/lib/timely/version.rb +1 -1
- data/timely.gemspec +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c3e56d3da55afb67b478464bca8ebe80dbb0616f5c180f3f40e96969c9ef3d4
|
|
4
|
+
data.tar.gz: 0fbad81b0406bb69b21434c406d60ffdf663c9d94d8e8bec33bd51364eb492df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75eade25f0fdd21608c2b1bcdce7af993bd78a125e5a211df426629a225cf0a1725e8d6376e324529cc8321bca459809cf39ccb67d4bc9e366a871aa2a9fd0f7
|
|
7
|
+
data.tar.gz: 3e613797bf54d9b031818fa9a6bd91ee0412483f2e04bab7c70a380d531a0a9b04ae1c881c49eeafe3bc5e38b2e0adc378461ceae316911c6f59474cd7347fa6
|
|
@@ -22,6 +22,7 @@ module Timely
|
|
|
22
22
|
@selected_slot = now.hour * 2 + (now.min >= 30 ? 1 : 0)
|
|
23
23
|
@slot_offset = [@selected_slot - 5, 0].max # Show a few rows above current time
|
|
24
24
|
@events_by_date = {}
|
|
25
|
+
@_weather_date = Date.today
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def run
|
|
@@ -60,6 +61,15 @@ module Timely
|
|
|
60
61
|
end
|
|
61
62
|
# Check notifications on idle (runs inexpensively)
|
|
62
63
|
Notifications.check_and_notify(@db) rescue nil
|
|
64
|
+
# Refresh weather on new day
|
|
65
|
+
today = Date.today
|
|
66
|
+
if @_weather_date != today
|
|
67
|
+
@_weather_date = today
|
|
68
|
+
@weather_forecast = nil
|
|
69
|
+
@_weather_fetched_at = nil
|
|
70
|
+
load_events_for_range
|
|
71
|
+
render_all
|
|
72
|
+
end
|
|
63
73
|
# Check for Heathrow goto trigger
|
|
64
74
|
check_heathrow_goto
|
|
65
75
|
end
|
|
@@ -178,10 +188,17 @@ module Timely
|
|
|
178
188
|
# @selected_slot: 0-47 = time slots (00:00-23:30)
|
|
179
189
|
# negative = all-day event rows (-1 = first, -2 = second...)
|
|
180
190
|
def allday_count
|
|
191
|
+
# Return max all-day event count across the visible week (matches render_mid_pane)
|
|
181
192
|
@_allday_count_date ||= nil
|
|
182
193
|
if @_allday_count_date != @selected_date
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
week_start = @selected_date - (@selected_date.cwday - 1)
|
|
195
|
+
max = 0
|
|
196
|
+
7.times do |i|
|
|
197
|
+
day = week_start + i
|
|
198
|
+
n = (@events_by_date[day] || []).count { |e| e['all_day'].to_i == 1 }
|
|
199
|
+
max = n if n > max
|
|
200
|
+
end
|
|
201
|
+
@_allday_count = max
|
|
185
202
|
@_allday_count_date = @selected_date
|
|
186
203
|
end
|
|
187
204
|
@_allday_count
|
|
@@ -194,13 +211,14 @@ module Timely
|
|
|
194
211
|
|
|
195
212
|
def adjust_slot_offset
|
|
196
213
|
return unless @selected_slot >= 0
|
|
197
|
-
|
|
198
|
-
available_rows = @panes[:mid].h - 3 -
|
|
214
|
+
extra_rows = allday_count > 0 ? allday_count + 1 : 0
|
|
215
|
+
available_rows = @panes[:mid].h - 3 - extra_rows
|
|
199
216
|
available_rows = [available_rows, 1].max
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
217
|
+
scrolloff = 2
|
|
218
|
+
if @selected_slot - @slot_offset >= available_rows - scrolloff
|
|
219
|
+
@slot_offset = [@selected_slot - available_rows + scrolloff + 1, [48 - available_rows, 0].max].min
|
|
220
|
+
elsif @selected_slot - @slot_offset < scrolloff
|
|
221
|
+
@slot_offset = [@selected_slot - scrolloff, 0].max
|
|
204
222
|
end
|
|
205
223
|
end
|
|
206
224
|
|
|
@@ -217,8 +235,8 @@ module Timely
|
|
|
217
235
|
@selected_slot ||= (@config.get('work_hours.start', 8) rescue 8) * 2
|
|
218
236
|
@selected_slot = @selected_slot <= min_slot ? 47 : @selected_slot - 1
|
|
219
237
|
if @selected_slot == 47
|
|
220
|
-
|
|
221
|
-
available = [@panes[:mid].h - 3 -
|
|
238
|
+
extra = allday_count > 0 ? allday_count + 1 : 0
|
|
239
|
+
available = [@panes[:mid].h - 3 - extra, 1].max
|
|
222
240
|
@slot_offset = [48 - available, 0].max
|
|
223
241
|
end
|
|
224
242
|
adjust_slot_offset
|
|
@@ -251,8 +269,8 @@ module Timely
|
|
|
251
269
|
|
|
252
270
|
def go_slot_bottom
|
|
253
271
|
@selected_slot = 47
|
|
254
|
-
|
|
255
|
-
available_rows = @panes[:mid].h - 3 -
|
|
272
|
+
extra = allday_count > 0 ? allday_count + 1 : 0
|
|
273
|
+
available_rows = @panes[:mid].h - 3 - extra
|
|
256
274
|
available_rows = [available_rows, 1].max
|
|
257
275
|
@slot_offset = [48 - available_rows, 0].max
|
|
258
276
|
render_mid_pane
|
|
@@ -545,7 +563,7 @@ module Timely
|
|
|
545
563
|
|
|
546
564
|
# Info bar: top row with bg color
|
|
547
565
|
def render_info_bar
|
|
548
|
-
title = " Timely".
|
|
566
|
+
title = " Timely".bd
|
|
549
567
|
date_str = @selected_date.strftime(" %A, %B %d, %Y")
|
|
550
568
|
phase = Astronomy.moon_phase(@selected_date)
|
|
551
569
|
moon = " #{phase[:symbol]} #{phase[:phase_name]} (#{(phase[:illumination] * 100).round}%)"
|
|
@@ -688,11 +706,11 @@ module Timely
|
|
|
688
706
|
today_bg = @config.get('colors.today_bg', 246)
|
|
689
707
|
today_fg = @config.get('colors.today_fg', 232)
|
|
690
708
|
header = if is_sel && is_today
|
|
691
|
-
header.
|
|
709
|
+
header.bd.ul.fg(today_fg).bg(today_bg)
|
|
692
710
|
elsif is_sel
|
|
693
|
-
header.
|
|
711
|
+
header.bd.ul.fg(base_color).bg(sel_alt_a)
|
|
694
712
|
elsif is_today
|
|
695
|
-
header.
|
|
713
|
+
header.bd.ul.fg(today_fg).bg(today_bg)
|
|
696
714
|
else
|
|
697
715
|
header.fg(base_color)
|
|
698
716
|
end
|
|
@@ -729,7 +747,7 @@ module Timely
|
|
|
729
747
|
max_allday.times do |row|
|
|
730
748
|
allday_slot = -(max_allday - row) # top row = most negative, bottom = -1
|
|
731
749
|
is_row_selected = (@selected_slot == allday_slot)
|
|
732
|
-
parts = [is_row_selected ? " All".fg(255).
|
|
750
|
+
parts = [is_row_selected ? " All".fg(255).bd + " " : " " * time_col]
|
|
733
751
|
7.times do |col|
|
|
734
752
|
evt = week_allday[col][row]
|
|
735
753
|
day = week_start + col
|
|
@@ -741,7 +759,7 @@ module Timely
|
|
|
741
759
|
color = evt['calendar_color'] || 39
|
|
742
760
|
marker = is_at ? ">" : " "
|
|
743
761
|
entry = "#{marker}#{title}"[0, day_col - 1]
|
|
744
|
-
cell = cell_bg ? entry.fg(color).
|
|
762
|
+
cell = cell_bg ? entry.fg(color).bd.bg(cell_bg) : entry.fg(color)
|
|
745
763
|
else
|
|
746
764
|
cell = cell_bg ? " ".bg(cell_bg) : " "
|
|
747
765
|
end
|
|
@@ -777,7 +795,7 @@ module Timely
|
|
|
777
795
|
|
|
778
796
|
# Time label: highlight if selected
|
|
779
797
|
time_label = format("%02d:%02d ", hour, minute)
|
|
780
|
-
time_label = is_slot_selected ? time_label.fg(255).
|
|
798
|
+
time_label = is_slot_selected ? time_label.fg(255).bd : time_label.fg(238)
|
|
781
799
|
|
|
782
800
|
parts = [time_label]
|
|
783
801
|
7.times do |col|
|
|
@@ -809,7 +827,7 @@ module Timely
|
|
|
809
827
|
color = evt['calendar_color'] || 39
|
|
810
828
|
entry = "#{marker}#{title}"
|
|
811
829
|
entry = entry[0, day_col - 1] + "." if entry.length > day_col
|
|
812
|
-
cell = is_at_slot ? entry.fg(color).
|
|
830
|
+
cell = is_at_slot ? entry.fg(color).bd.bg(cell_bg) : entry.fg(color).bg(cell_bg)
|
|
813
831
|
else
|
|
814
832
|
cell = " ".bg(cell_bg)
|
|
815
833
|
end
|
|
@@ -851,7 +869,7 @@ module Timely
|
|
|
851
869
|
time_info = st.strftime('%a %Y-%m-%d %H:%M')
|
|
852
870
|
time_info += " - #{Time.at(evt['end_time'].to_i).strftime('%H:%M')}" if evt['end_time']
|
|
853
871
|
end
|
|
854
|
-
lines << " #{title}".fg(color).
|
|
872
|
+
lines << " #{title}".fg(color).bd + " #{time_info}".fg(252)
|
|
855
873
|
|
|
856
874
|
# Line 2: Location + Organizer + Calendar on same line
|
|
857
875
|
details = []
|
|
@@ -898,7 +916,7 @@ module Timely
|
|
|
898
916
|
|
|
899
917
|
else
|
|
900
918
|
# No events: show day summary
|
|
901
|
-
lines << " #{@selected_date.strftime('%A, %B %d, %Y')}".
|
|
919
|
+
lines << " #{@selected_date.strftime('%A, %B %d, %Y')}".bd
|
|
902
920
|
|
|
903
921
|
# Astronomical events (solstices, meteor showers, etc.)
|
|
904
922
|
lat = @config.get('location.lat', 59.9139)
|
|
@@ -986,7 +1004,7 @@ module Timely
|
|
|
986
1004
|
if calendars.size > 1
|
|
987
1005
|
cal_list = calendars.each_with_index.map { |c, i| "#{i + 1}:#{c['name']}" }.join(" ")
|
|
988
1006
|
default_idx = calendars.index(cal) || 0
|
|
989
|
-
blank_bottom(" New Event".fg(cal_color).
|
|
1007
|
+
blank_bottom(" New Event".fg(cal_color).bd)
|
|
990
1008
|
pick = bottom_ask(" Calendar (#{cal_list}): ", (default_idx + 1).to_s)
|
|
991
1009
|
return cancel_create if pick.nil?
|
|
992
1010
|
idx = pick.strip.to_i - 1
|
|
@@ -994,11 +1012,11 @@ module Timely
|
|
|
994
1012
|
cal_color = cal['color'] || 39
|
|
995
1013
|
end
|
|
996
1014
|
|
|
997
|
-
blank_bottom(" New Event on #{@selected_date.strftime('%A, %B %d, %Y')}".fg(cal_color).
|
|
1015
|
+
blank_bottom(" New Event on #{@selected_date.strftime('%A, %B %d, %Y')}".fg(cal_color).bd)
|
|
998
1016
|
title = bottom_ask(" Title: ", "")
|
|
999
1017
|
return cancel_create if title.nil? || title.strip.empty?
|
|
1000
1018
|
|
|
1001
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1019
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1002
1020
|
time_str = bottom_ask(" Start time (HH:MM or 'all day'): ", default_time)
|
|
1003
1021
|
return cancel_create if time_str.nil?
|
|
1004
1022
|
|
|
@@ -1013,7 +1031,7 @@ module Timely
|
|
|
1013
1031
|
minute = (parts[1] || 0).to_i
|
|
1014
1032
|
start_ts = Time.new(@selected_date.year, @selected_date.month, @selected_date.day, hour, minute, 0).to_i
|
|
1015
1033
|
|
|
1016
|
-
blank_bottom(" #{title.strip} at #{time_str.strip}".fg(cal_color).
|
|
1034
|
+
blank_bottom(" #{title.strip} at #{time_str.strip}".fg(cal_color).bd)
|
|
1017
1035
|
dur_str = bottom_ask(" Duration in minutes: ", "60")
|
|
1018
1036
|
return cancel_create if dur_str.nil?
|
|
1019
1037
|
duration = dur_str.strip.to_i
|
|
@@ -1022,12 +1040,12 @@ module Timely
|
|
|
1022
1040
|
end
|
|
1023
1041
|
|
|
1024
1042
|
# Location
|
|
1025
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1043
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1026
1044
|
location = bottom_ask(" Location (Enter to skip): ", "")
|
|
1027
1045
|
location = nil if location.nil? || location.strip.empty?
|
|
1028
1046
|
|
|
1029
1047
|
# Invitees
|
|
1030
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1048
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1031
1049
|
invitees_str = bottom_ask(" Invite (comma-separated emails, Enter to skip): ", "")
|
|
1032
1050
|
attendees = nil
|
|
1033
1051
|
if invitees_str && !invitees_str.strip.empty?
|
|
@@ -1035,7 +1053,7 @@ module Timely
|
|
|
1035
1053
|
end
|
|
1036
1054
|
|
|
1037
1055
|
# Attachments via rtfm --pick
|
|
1038
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1056
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1039
1057
|
attach_str = bottom_ask(" Add attachments? (y/N): ", "")
|
|
1040
1058
|
attachments = nil
|
|
1041
1059
|
if attach_str&.strip&.downcase == 'y'
|
|
@@ -1119,7 +1137,7 @@ module Timely
|
|
|
1119
1137
|
evt = event_at_selected_slot
|
|
1120
1138
|
return show_feedback("No event at this time slot", 245) unless evt
|
|
1121
1139
|
|
|
1122
|
-
blank_bottom(" Edit Event".
|
|
1140
|
+
blank_bottom(" Edit Event".bd)
|
|
1123
1141
|
new_title = bottom_ask(" Title: ", evt['title'] || "")
|
|
1124
1142
|
return if new_title.nil?
|
|
1125
1143
|
|
|
@@ -1153,7 +1171,7 @@ module Timely
|
|
|
1153
1171
|
evt = event_at_selected_slot
|
|
1154
1172
|
return show_feedback("No event at this time slot", 245) unless evt
|
|
1155
1173
|
|
|
1156
|
-
blank_bottom(" Delete Event".
|
|
1174
|
+
blank_bottom(" Delete Event".bd)
|
|
1157
1175
|
confirm = bottom_ask(" Delete '#{evt['title']}'? (y/n): ", "")
|
|
1158
1176
|
return unless confirm&.strip&.downcase == 'y'
|
|
1159
1177
|
|
|
@@ -1214,7 +1232,7 @@ module Timely
|
|
|
1214
1232
|
color = evt['calendar_color'] || 39
|
|
1215
1233
|
lines = []
|
|
1216
1234
|
lines << ""
|
|
1217
|
-
lines << " #{evt['title'] || '(No title)'}".fg(color).
|
|
1235
|
+
lines << " #{evt['title'] || '(No title)'}".fg(color).bd
|
|
1218
1236
|
lines << ""
|
|
1219
1237
|
|
|
1220
1238
|
if evt['all_day'].to_i == 1
|
|
@@ -1384,7 +1402,7 @@ module Timely
|
|
|
1384
1402
|
# --- ICS Import ---
|
|
1385
1403
|
|
|
1386
1404
|
def import_ics_file
|
|
1387
|
-
blank_bottom(" Import ICS File".
|
|
1405
|
+
blank_bottom(" Import ICS File".bd)
|
|
1388
1406
|
path = bottom_ask(" File path: ", "")
|
|
1389
1407
|
return cancel_create if path.nil? || path.strip.empty?
|
|
1390
1408
|
|
|
@@ -1406,7 +1424,7 @@ module Timely
|
|
|
1406
1424
|
# --- Google Calendar ---
|
|
1407
1425
|
|
|
1408
1426
|
def setup_google_calendar
|
|
1409
|
-
blank_bottom(" Google Calendar Setup".
|
|
1427
|
+
blank_bottom(" Google Calendar Setup".bd.fg(39))
|
|
1410
1428
|
email = bottom_ask(" Google email: ", "")
|
|
1411
1429
|
return cancel_create if email.nil? || email.strip.empty?
|
|
1412
1430
|
email = email.strip
|
|
@@ -1432,7 +1450,7 @@ module Timely
|
|
|
1432
1450
|
|
|
1433
1451
|
# Show calendars and let user pick
|
|
1434
1452
|
cal_list = calendars.each_with_index.map { |c, i| "#{i + 1}:#{c[:summary]}" }.join(" ")
|
|
1435
|
-
blank_bottom(" Found #{calendars.size} calendar(s)".fg(39).
|
|
1453
|
+
blank_bottom(" Found #{calendars.size} calendar(s)".fg(39).bd)
|
|
1436
1454
|
pick = bottom_ask(" Add which? (#{cal_list}, 'all', or ESC): ", "all")
|
|
1437
1455
|
return cancel_create if pick.nil?
|
|
1438
1456
|
|
|
@@ -1486,7 +1504,7 @@ module Timely
|
|
|
1486
1504
|
# --- Outlook Calendar ---
|
|
1487
1505
|
|
|
1488
1506
|
def setup_outlook_calendar
|
|
1489
|
-
blank_bottom(" Outlook/365 Calendar Setup".
|
|
1507
|
+
blank_bottom(" Outlook/365 Calendar Setup".bd.fg(33))
|
|
1490
1508
|
|
|
1491
1509
|
# Get client_id (from Azure app registration)
|
|
1492
1510
|
default_client_id = @config.get('outlook.client_id', '')
|
|
@@ -1522,11 +1540,11 @@ module Timely
|
|
|
1522
1540
|
user_code = auth['user_code']
|
|
1523
1541
|
verify_url = auth['verification_uri'] || 'https://microsoft.com/devicelogin'
|
|
1524
1542
|
|
|
1525
|
-
blank_bottom(" Outlook Device Login".
|
|
1543
|
+
blank_bottom(" Outlook Device Login".bd.fg(33))
|
|
1526
1544
|
lines = [("-" * @w).fg(238)]
|
|
1527
1545
|
lines << ""
|
|
1528
1546
|
lines << " Go to: #{verify_url}".fg(51)
|
|
1529
|
-
lines << " Enter code: #{user_code}".fg(226).
|
|
1547
|
+
lines << " Enter code: #{user_code}".fg(226).bd
|
|
1530
1548
|
lines << ""
|
|
1531
1549
|
lines << " Waiting for authorization...".fg(245)
|
|
1532
1550
|
while lines.length < @panes[:bottom].h
|
|
@@ -1561,7 +1579,7 @@ module Timely
|
|
|
1561
1579
|
|
|
1562
1580
|
# Let user pick calendars
|
|
1563
1581
|
cal_list = calendars.each_with_index.map { |c, i| "#{i + 1}:#{c[:name]}" }.join(" ")
|
|
1564
|
-
blank_bottom(" Found #{calendars.size} Outlook calendar(s)".fg(33).
|
|
1582
|
+
blank_bottom(" Found #{calendars.size} Outlook calendar(s)".fg(33).bd)
|
|
1565
1583
|
pick = bottom_ask(" Add which? (#{cal_list}, 'all', or ESC): ", "all")
|
|
1566
1584
|
return cancel_create if pick.nil?
|
|
1567
1585
|
|
|
@@ -1733,14 +1751,14 @@ module Timely
|
|
|
1733
1751
|
popup.full_refresh
|
|
1734
1752
|
lines = []
|
|
1735
1753
|
lines << ""
|
|
1736
|
-
lines << " " + "Pick Color".
|
|
1754
|
+
lines << " " + "Pick Color".bd + " current: " + "\u2588\u2588".fg(sel) + " #{sel}"
|
|
1737
1755
|
lines << ""
|
|
1738
1756
|
16.times do |row|
|
|
1739
1757
|
line = " "
|
|
1740
1758
|
16.times do |col|
|
|
1741
1759
|
c = row * 16 + col
|
|
1742
1760
|
if c == sel
|
|
1743
|
-
line += "X ".bg(c).fg(255).
|
|
1761
|
+
line += "X ".bg(c).fg(255).bd
|
|
1744
1762
|
else
|
|
1745
1763
|
line += " ".bg(c)
|
|
1746
1764
|
end
|
|
@@ -1810,7 +1828,7 @@ module Timely
|
|
|
1810
1828
|
popup.full_refresh
|
|
1811
1829
|
lines = []
|
|
1812
1830
|
lines << ""
|
|
1813
|
-
lines << " " + "Calendars".
|
|
1831
|
+
lines << " " + "Calendars".bd
|
|
1814
1832
|
lines << " " + ("-" * [pw - 6, 1].max).fg(238)
|
|
1815
1833
|
|
|
1816
1834
|
calendars.each_with_index do |cal, i|
|
|
@@ -1821,7 +1839,7 @@ module Timely
|
|
|
1821
1839
|
src = cal['source_type'] || 'local'
|
|
1822
1840
|
name = cal['name'] || '(unnamed)'
|
|
1823
1841
|
display = " #{swatch} %-22s %s [%s]" % [name[0..21], status, src]
|
|
1824
|
-
lines << (i == sel ? display.fg(39).
|
|
1842
|
+
lines << (i == sel ? display.fg(39).bd : display)
|
|
1825
1843
|
end
|
|
1826
1844
|
|
|
1827
1845
|
lines << ""
|
|
@@ -1915,7 +1933,7 @@ module Timely
|
|
|
1915
1933
|
inner_w = pw - 4
|
|
1916
1934
|
lines = []
|
|
1917
1935
|
lines << ""
|
|
1918
|
-
lines << " " + "Preferences".
|
|
1936
|
+
lines << " " + "Preferences".bd
|
|
1919
1937
|
lines << " " + ("\u2500" * [inner_w - 3, 1].max).fg(238)
|
|
1920
1938
|
|
|
1921
1939
|
pref_keys.each_with_index do |(key, label, default), i|
|
|
@@ -1932,7 +1950,7 @@ module Timely
|
|
|
1932
1950
|
display = " %-18s %s" % [label, val.to_s]
|
|
1933
1951
|
end
|
|
1934
1952
|
if i == sel
|
|
1935
|
-
lines << display.fg(39).
|
|
1953
|
+
lines << display.fg(39).bd
|
|
1936
1954
|
else
|
|
1937
1955
|
lines << display
|
|
1938
1956
|
end
|
|
@@ -2025,9 +2043,9 @@ module Timely
|
|
|
2025
2043
|
|
|
2026
2044
|
help = []
|
|
2027
2045
|
help << ""
|
|
2028
|
-
help << " " + "Timely - Terminal Calendar".
|
|
2046
|
+
help << " " + "Timely - Terminal Calendar".bd.fg(156)
|
|
2029
2047
|
help << sep
|
|
2030
|
-
help << " " + "Navigation".
|
|
2048
|
+
help << " " + "Navigation".bd.fg(156)
|
|
2031
2049
|
help << " #{k['d/RIGHT']} #{d['Next day']} #{k['D/LEFT']} #{d['Prev day']}"
|
|
2032
2050
|
help << " #{k['w']} #{d['Next week']} #{k['W']} #{d['Prev week']}"
|
|
2033
2051
|
help << " #{k['m']} #{d['Next month']} #{k['M']} #{d['Prev month']}"
|
|
@@ -2038,7 +2056,7 @@ module Timely
|
|
|
2038
2056
|
help << " #{k['e/E']} #{d['Jump to event (next/prev)']}"
|
|
2039
2057
|
help << " #{k['t']} #{d['Today']} #{k['g']} #{d['Go to (date, Mon, yyyy)']}"
|
|
2040
2058
|
help << sep
|
|
2041
|
-
help << " " + "Events".
|
|
2059
|
+
help << " " + "Events".bd.fg(156)
|
|
2042
2060
|
help << " #{k['n']} #{d['New event']} #{k['ENTER']} #{d['Edit event']}"
|
|
2043
2061
|
help << " #{k['x/DEL']} #{d['Delete event']} #{k['a']} #{d['Accept invite']}"
|
|
2044
2062
|
help << " #{k['v']} #{d['View event details (scrollable popup)']}"
|
|
@@ -17,7 +17,7 @@ module Timely
|
|
|
17
17
|
# Title: centered month and year
|
|
18
18
|
title = Date.new(year, month, 1).strftime("%B %Y")
|
|
19
19
|
pad = [(width - title.length) / 2, 1].max
|
|
20
|
-
lines << (" " * pad + title).
|
|
20
|
+
lines << (" " * pad + title).bd
|
|
21
21
|
|
|
22
22
|
# Weekday headers (with space for week number column)
|
|
23
23
|
header = " " + WEEKDAYS.each_with_index.map { |d, i|
|
|
@@ -101,9 +101,9 @@ module Timely
|
|
|
101
101
|
|
|
102
102
|
d = day.to_s.rjust(2)
|
|
103
103
|
if is_selected && is_today
|
|
104
|
-
base_color ? d.
|
|
104
|
+
base_color ? d.bd.ul.fg(base_color).bg(today_bg) : d.bd.ul.bg(today_bg)
|
|
105
105
|
elsif is_selected
|
|
106
|
-
base_color ? d.
|
|
106
|
+
base_color ? d.bd.ul.fg(base_color) : d.bd.ul
|
|
107
107
|
elsif is_today
|
|
108
108
|
base_color ? d.fg(base_color).bg(today_bg) : d.bg(today_bg)
|
|
109
109
|
elsif base_color
|
data/lib/timely/version.rb
CHANGED
data/timely.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: timely-calendar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
@@ -9,22 +9,22 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-04-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rcurses
|
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
requirements:
|
|
18
|
-
- - "
|
|
18
|
+
- - "~>"
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: '
|
|
20
|
+
version: '7.0'
|
|
21
21
|
type: :runtime
|
|
22
22
|
prerelease: false
|
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
24
|
requirements:
|
|
25
|
-
- - "
|
|
25
|
+
- - "~>"
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
|
-
version: '
|
|
27
|
+
version: '7.0'
|
|
28
28
|
- !ruby/object:Gem::Dependency
|
|
29
29
|
name: sqlite3
|
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|