timely-calendar 1.0.2 → 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 +67 -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
|
|
@@ -345,6 +363,7 @@ module Timely
|
|
|
345
363
|
.gsub(/-::~:~::~:~.*$/m, '') # Google Meet block
|
|
346
364
|
.gsub(/_{3,}/, '') # Long underscore lines (Teams HR)
|
|
347
365
|
.gsub(/-{5,}/, '') # Long dash lines
|
|
366
|
+
.gsub(/[━═─]{3,}/, '') # Heavy/double/light box-drawing lines
|
|
348
367
|
.gsub(/\n{3,}/, "\n\n") # Collapse excessive blank lines
|
|
349
368
|
.strip
|
|
350
369
|
end
|
|
@@ -544,7 +563,7 @@ module Timely
|
|
|
544
563
|
|
|
545
564
|
# Info bar: top row with bg color
|
|
546
565
|
def render_info_bar
|
|
547
|
-
title = " Timely".
|
|
566
|
+
title = " Timely".bd
|
|
548
567
|
date_str = @selected_date.strftime(" %A, %B %d, %Y")
|
|
549
568
|
phase = Astronomy.moon_phase(@selected_date)
|
|
550
569
|
moon = " #{phase[:symbol]} #{phase[:phase_name]} (#{(phase[:illumination] * 100).round}%)"
|
|
@@ -687,11 +706,11 @@ module Timely
|
|
|
687
706
|
today_bg = @config.get('colors.today_bg', 246)
|
|
688
707
|
today_fg = @config.get('colors.today_fg', 232)
|
|
689
708
|
header = if is_sel && is_today
|
|
690
|
-
header.
|
|
709
|
+
header.bd.ul.fg(today_fg).bg(today_bg)
|
|
691
710
|
elsif is_sel
|
|
692
|
-
header.
|
|
711
|
+
header.bd.ul.fg(base_color).bg(sel_alt_a)
|
|
693
712
|
elsif is_today
|
|
694
|
-
header.
|
|
713
|
+
header.bd.ul.fg(today_fg).bg(today_bg)
|
|
695
714
|
else
|
|
696
715
|
header.fg(base_color)
|
|
697
716
|
end
|
|
@@ -728,7 +747,7 @@ module Timely
|
|
|
728
747
|
max_allday.times do |row|
|
|
729
748
|
allday_slot = -(max_allday - row) # top row = most negative, bottom = -1
|
|
730
749
|
is_row_selected = (@selected_slot == allday_slot)
|
|
731
|
-
parts = [is_row_selected ? " All".fg(255).
|
|
750
|
+
parts = [is_row_selected ? " All".fg(255).bd + " " : " " * time_col]
|
|
732
751
|
7.times do |col|
|
|
733
752
|
evt = week_allday[col][row]
|
|
734
753
|
day = week_start + col
|
|
@@ -740,7 +759,7 @@ module Timely
|
|
|
740
759
|
color = evt['calendar_color'] || 39
|
|
741
760
|
marker = is_at ? ">" : " "
|
|
742
761
|
entry = "#{marker}#{title}"[0, day_col - 1]
|
|
743
|
-
cell = cell_bg ? entry.fg(color).
|
|
762
|
+
cell = cell_bg ? entry.fg(color).bd.bg(cell_bg) : entry.fg(color)
|
|
744
763
|
else
|
|
745
764
|
cell = cell_bg ? " ".bg(cell_bg) : " "
|
|
746
765
|
end
|
|
@@ -776,7 +795,7 @@ module Timely
|
|
|
776
795
|
|
|
777
796
|
# Time label: highlight if selected
|
|
778
797
|
time_label = format("%02d:%02d ", hour, minute)
|
|
779
|
-
time_label = is_slot_selected ? time_label.fg(255).
|
|
798
|
+
time_label = is_slot_selected ? time_label.fg(255).bd : time_label.fg(238)
|
|
780
799
|
|
|
781
800
|
parts = [time_label]
|
|
782
801
|
7.times do |col|
|
|
@@ -808,7 +827,7 @@ module Timely
|
|
|
808
827
|
color = evt['calendar_color'] || 39
|
|
809
828
|
entry = "#{marker}#{title}"
|
|
810
829
|
entry = entry[0, day_col - 1] + "." if entry.length > day_col
|
|
811
|
-
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)
|
|
812
831
|
else
|
|
813
832
|
cell = " ".bg(cell_bg)
|
|
814
833
|
end
|
|
@@ -850,7 +869,7 @@ module Timely
|
|
|
850
869
|
time_info = st.strftime('%a %Y-%m-%d %H:%M')
|
|
851
870
|
time_info += " - #{Time.at(evt['end_time'].to_i).strftime('%H:%M')}" if evt['end_time']
|
|
852
871
|
end
|
|
853
|
-
lines << " #{title}".fg(color).
|
|
872
|
+
lines << " #{title}".fg(color).bd + " #{time_info}".fg(252)
|
|
854
873
|
|
|
855
874
|
# Line 2: Location + Organizer + Calendar on same line
|
|
856
875
|
details = []
|
|
@@ -897,7 +916,7 @@ module Timely
|
|
|
897
916
|
|
|
898
917
|
else
|
|
899
918
|
# No events: show day summary
|
|
900
|
-
lines << " #{@selected_date.strftime('%A, %B %d, %Y')}".
|
|
919
|
+
lines << " #{@selected_date.strftime('%A, %B %d, %Y')}".bd
|
|
901
920
|
|
|
902
921
|
# Astronomical events (solstices, meteor showers, etc.)
|
|
903
922
|
lat = @config.get('location.lat', 59.9139)
|
|
@@ -985,7 +1004,7 @@ module Timely
|
|
|
985
1004
|
if calendars.size > 1
|
|
986
1005
|
cal_list = calendars.each_with_index.map { |c, i| "#{i + 1}:#{c['name']}" }.join(" ")
|
|
987
1006
|
default_idx = calendars.index(cal) || 0
|
|
988
|
-
blank_bottom(" New Event".fg(cal_color).
|
|
1007
|
+
blank_bottom(" New Event".fg(cal_color).bd)
|
|
989
1008
|
pick = bottom_ask(" Calendar (#{cal_list}): ", (default_idx + 1).to_s)
|
|
990
1009
|
return cancel_create if pick.nil?
|
|
991
1010
|
idx = pick.strip.to_i - 1
|
|
@@ -993,11 +1012,11 @@ module Timely
|
|
|
993
1012
|
cal_color = cal['color'] || 39
|
|
994
1013
|
end
|
|
995
1014
|
|
|
996
|
-
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)
|
|
997
1016
|
title = bottom_ask(" Title: ", "")
|
|
998
1017
|
return cancel_create if title.nil? || title.strip.empty?
|
|
999
1018
|
|
|
1000
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1019
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1001
1020
|
time_str = bottom_ask(" Start time (HH:MM or 'all day'): ", default_time)
|
|
1002
1021
|
return cancel_create if time_str.nil?
|
|
1003
1022
|
|
|
@@ -1012,7 +1031,7 @@ module Timely
|
|
|
1012
1031
|
minute = (parts[1] || 0).to_i
|
|
1013
1032
|
start_ts = Time.new(@selected_date.year, @selected_date.month, @selected_date.day, hour, minute, 0).to_i
|
|
1014
1033
|
|
|
1015
|
-
blank_bottom(" #{title.strip} at #{time_str.strip}".fg(cal_color).
|
|
1034
|
+
blank_bottom(" #{title.strip} at #{time_str.strip}".fg(cal_color).bd)
|
|
1016
1035
|
dur_str = bottom_ask(" Duration in minutes: ", "60")
|
|
1017
1036
|
return cancel_create if dur_str.nil?
|
|
1018
1037
|
duration = dur_str.strip.to_i
|
|
@@ -1021,12 +1040,12 @@ module Timely
|
|
|
1021
1040
|
end
|
|
1022
1041
|
|
|
1023
1042
|
# Location
|
|
1024
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1043
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1025
1044
|
location = bottom_ask(" Location (Enter to skip): ", "")
|
|
1026
1045
|
location = nil if location.nil? || location.strip.empty?
|
|
1027
1046
|
|
|
1028
1047
|
# Invitees
|
|
1029
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1048
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1030
1049
|
invitees_str = bottom_ask(" Invite (comma-separated emails, Enter to skip): ", "")
|
|
1031
1050
|
attendees = nil
|
|
1032
1051
|
if invitees_str && !invitees_str.strip.empty?
|
|
@@ -1034,7 +1053,7 @@ module Timely
|
|
|
1034
1053
|
end
|
|
1035
1054
|
|
|
1036
1055
|
# Attachments via rtfm --pick
|
|
1037
|
-
blank_bottom(" #{title.strip}".fg(cal_color).
|
|
1056
|
+
blank_bottom(" #{title.strip}".fg(cal_color).bd)
|
|
1038
1057
|
attach_str = bottom_ask(" Add attachments? (y/N): ", "")
|
|
1039
1058
|
attachments = nil
|
|
1040
1059
|
if attach_str&.strip&.downcase == 'y'
|
|
@@ -1118,7 +1137,7 @@ module Timely
|
|
|
1118
1137
|
evt = event_at_selected_slot
|
|
1119
1138
|
return show_feedback("No event at this time slot", 245) unless evt
|
|
1120
1139
|
|
|
1121
|
-
blank_bottom(" Edit Event".
|
|
1140
|
+
blank_bottom(" Edit Event".bd)
|
|
1122
1141
|
new_title = bottom_ask(" Title: ", evt['title'] || "")
|
|
1123
1142
|
return if new_title.nil?
|
|
1124
1143
|
|
|
@@ -1152,7 +1171,7 @@ module Timely
|
|
|
1152
1171
|
evt = event_at_selected_slot
|
|
1153
1172
|
return show_feedback("No event at this time slot", 245) unless evt
|
|
1154
1173
|
|
|
1155
|
-
blank_bottom(" Delete Event".
|
|
1174
|
+
blank_bottom(" Delete Event".bd)
|
|
1156
1175
|
confirm = bottom_ask(" Delete '#{evt['title']}'? (y/n): ", "")
|
|
1157
1176
|
return unless confirm&.strip&.downcase == 'y'
|
|
1158
1177
|
|
|
@@ -1213,7 +1232,7 @@ module Timely
|
|
|
1213
1232
|
color = evt['calendar_color'] || 39
|
|
1214
1233
|
lines = []
|
|
1215
1234
|
lines << ""
|
|
1216
|
-
lines << " #{evt['title'] || '(No title)'}".fg(color).
|
|
1235
|
+
lines << " #{evt['title'] || '(No title)'}".fg(color).bd
|
|
1217
1236
|
lines << ""
|
|
1218
1237
|
|
|
1219
1238
|
if evt['all_day'].to_i == 1
|
|
@@ -1383,7 +1402,7 @@ module Timely
|
|
|
1383
1402
|
# --- ICS Import ---
|
|
1384
1403
|
|
|
1385
1404
|
def import_ics_file
|
|
1386
|
-
blank_bottom(" Import ICS File".
|
|
1405
|
+
blank_bottom(" Import ICS File".bd)
|
|
1387
1406
|
path = bottom_ask(" File path: ", "")
|
|
1388
1407
|
return cancel_create if path.nil? || path.strip.empty?
|
|
1389
1408
|
|
|
@@ -1405,7 +1424,7 @@ module Timely
|
|
|
1405
1424
|
# --- Google Calendar ---
|
|
1406
1425
|
|
|
1407
1426
|
def setup_google_calendar
|
|
1408
|
-
blank_bottom(" Google Calendar Setup".
|
|
1427
|
+
blank_bottom(" Google Calendar Setup".bd.fg(39))
|
|
1409
1428
|
email = bottom_ask(" Google email: ", "")
|
|
1410
1429
|
return cancel_create if email.nil? || email.strip.empty?
|
|
1411
1430
|
email = email.strip
|
|
@@ -1431,7 +1450,7 @@ module Timely
|
|
|
1431
1450
|
|
|
1432
1451
|
# Show calendars and let user pick
|
|
1433
1452
|
cal_list = calendars.each_with_index.map { |c, i| "#{i + 1}:#{c[:summary]}" }.join(" ")
|
|
1434
|
-
blank_bottom(" Found #{calendars.size} calendar(s)".fg(39).
|
|
1453
|
+
blank_bottom(" Found #{calendars.size} calendar(s)".fg(39).bd)
|
|
1435
1454
|
pick = bottom_ask(" Add which? (#{cal_list}, 'all', or ESC): ", "all")
|
|
1436
1455
|
return cancel_create if pick.nil?
|
|
1437
1456
|
|
|
@@ -1485,7 +1504,7 @@ module Timely
|
|
|
1485
1504
|
# --- Outlook Calendar ---
|
|
1486
1505
|
|
|
1487
1506
|
def setup_outlook_calendar
|
|
1488
|
-
blank_bottom(" Outlook/365 Calendar Setup".
|
|
1507
|
+
blank_bottom(" Outlook/365 Calendar Setup".bd.fg(33))
|
|
1489
1508
|
|
|
1490
1509
|
# Get client_id (from Azure app registration)
|
|
1491
1510
|
default_client_id = @config.get('outlook.client_id', '')
|
|
@@ -1521,11 +1540,11 @@ module Timely
|
|
|
1521
1540
|
user_code = auth['user_code']
|
|
1522
1541
|
verify_url = auth['verification_uri'] || 'https://microsoft.com/devicelogin'
|
|
1523
1542
|
|
|
1524
|
-
blank_bottom(" Outlook Device Login".
|
|
1543
|
+
blank_bottom(" Outlook Device Login".bd.fg(33))
|
|
1525
1544
|
lines = [("-" * @w).fg(238)]
|
|
1526
1545
|
lines << ""
|
|
1527
1546
|
lines << " Go to: #{verify_url}".fg(51)
|
|
1528
|
-
lines << " Enter code: #{user_code}".fg(226).
|
|
1547
|
+
lines << " Enter code: #{user_code}".fg(226).bd
|
|
1529
1548
|
lines << ""
|
|
1530
1549
|
lines << " Waiting for authorization...".fg(245)
|
|
1531
1550
|
while lines.length < @panes[:bottom].h
|
|
@@ -1560,7 +1579,7 @@ module Timely
|
|
|
1560
1579
|
|
|
1561
1580
|
# Let user pick calendars
|
|
1562
1581
|
cal_list = calendars.each_with_index.map { |c, i| "#{i + 1}:#{c[:name]}" }.join(" ")
|
|
1563
|
-
blank_bottom(" Found #{calendars.size} Outlook calendar(s)".fg(33).
|
|
1582
|
+
blank_bottom(" Found #{calendars.size} Outlook calendar(s)".fg(33).bd)
|
|
1564
1583
|
pick = bottom_ask(" Add which? (#{cal_list}, 'all', or ESC): ", "all")
|
|
1565
1584
|
return cancel_create if pick.nil?
|
|
1566
1585
|
|
|
@@ -1732,14 +1751,14 @@ module Timely
|
|
|
1732
1751
|
popup.full_refresh
|
|
1733
1752
|
lines = []
|
|
1734
1753
|
lines << ""
|
|
1735
|
-
lines << " " + "Pick Color".
|
|
1754
|
+
lines << " " + "Pick Color".bd + " current: " + "\u2588\u2588".fg(sel) + " #{sel}"
|
|
1736
1755
|
lines << ""
|
|
1737
1756
|
16.times do |row|
|
|
1738
1757
|
line = " "
|
|
1739
1758
|
16.times do |col|
|
|
1740
1759
|
c = row * 16 + col
|
|
1741
1760
|
if c == sel
|
|
1742
|
-
line += "X ".bg(c).fg(255).
|
|
1761
|
+
line += "X ".bg(c).fg(255).bd
|
|
1743
1762
|
else
|
|
1744
1763
|
line += " ".bg(c)
|
|
1745
1764
|
end
|
|
@@ -1809,7 +1828,7 @@ module Timely
|
|
|
1809
1828
|
popup.full_refresh
|
|
1810
1829
|
lines = []
|
|
1811
1830
|
lines << ""
|
|
1812
|
-
lines << " " + "Calendars".
|
|
1831
|
+
lines << " " + "Calendars".bd
|
|
1813
1832
|
lines << " " + ("-" * [pw - 6, 1].max).fg(238)
|
|
1814
1833
|
|
|
1815
1834
|
calendars.each_with_index do |cal, i|
|
|
@@ -1820,7 +1839,7 @@ module Timely
|
|
|
1820
1839
|
src = cal['source_type'] || 'local'
|
|
1821
1840
|
name = cal['name'] || '(unnamed)'
|
|
1822
1841
|
display = " #{swatch} %-22s %s [%s]" % [name[0..21], status, src]
|
|
1823
|
-
lines << (i == sel ? display.fg(39).
|
|
1842
|
+
lines << (i == sel ? display.fg(39).bd : display)
|
|
1824
1843
|
end
|
|
1825
1844
|
|
|
1826
1845
|
lines << ""
|
|
@@ -1914,7 +1933,7 @@ module Timely
|
|
|
1914
1933
|
inner_w = pw - 4
|
|
1915
1934
|
lines = []
|
|
1916
1935
|
lines << ""
|
|
1917
|
-
lines << " " + "Preferences".
|
|
1936
|
+
lines << " " + "Preferences".bd
|
|
1918
1937
|
lines << " " + ("\u2500" * [inner_w - 3, 1].max).fg(238)
|
|
1919
1938
|
|
|
1920
1939
|
pref_keys.each_with_index do |(key, label, default), i|
|
|
@@ -1931,7 +1950,7 @@ module Timely
|
|
|
1931
1950
|
display = " %-18s %s" % [label, val.to_s]
|
|
1932
1951
|
end
|
|
1933
1952
|
if i == sel
|
|
1934
|
-
lines << display.fg(39).
|
|
1953
|
+
lines << display.fg(39).bd
|
|
1935
1954
|
else
|
|
1936
1955
|
lines << display
|
|
1937
1956
|
end
|
|
@@ -2024,9 +2043,9 @@ module Timely
|
|
|
2024
2043
|
|
|
2025
2044
|
help = []
|
|
2026
2045
|
help << ""
|
|
2027
|
-
help << " " + "Timely - Terminal Calendar".
|
|
2046
|
+
help << " " + "Timely - Terminal Calendar".bd.fg(156)
|
|
2028
2047
|
help << sep
|
|
2029
|
-
help << " " + "Navigation".
|
|
2048
|
+
help << " " + "Navigation".bd.fg(156)
|
|
2030
2049
|
help << " #{k['d/RIGHT']} #{d['Next day']} #{k['D/LEFT']} #{d['Prev day']}"
|
|
2031
2050
|
help << " #{k['w']} #{d['Next week']} #{k['W']} #{d['Prev week']}"
|
|
2032
2051
|
help << " #{k['m']} #{d['Next month']} #{k['M']} #{d['Prev month']}"
|
|
@@ -2037,7 +2056,7 @@ module Timely
|
|
|
2037
2056
|
help << " #{k['e/E']} #{d['Jump to event (next/prev)']}"
|
|
2038
2057
|
help << " #{k['t']} #{d['Today']} #{k['g']} #{d['Go to (date, Mon, yyyy)']}"
|
|
2039
2058
|
help << sep
|
|
2040
|
-
help << " " + "Events".
|
|
2059
|
+
help << " " + "Events".bd.fg(156)
|
|
2041
2060
|
help << " #{k['n']} #{d['New event']} #{k['ENTER']} #{d['Edit event']}"
|
|
2042
2061
|
help << " #{k['x/DEL']} #{d['Delete event']} #{k['a']} #{d['Accept invite']}"
|
|
2043
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
|