timely-calendar 1.0.1 → 1.0.3
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/img/timely-kb.svg +282 -0
- data/lib/timely/ui/application.rb +99 -13
- data/lib/timely/ui/panes.rb +3 -3
- data/lib/timely/version.rb +1 -1
- data/lib/timely/weather.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: afd143174ee0c9300fbce699d4400b3bd2dabb7761571dcbe1196857c81c46cc
|
|
4
|
+
data.tar.gz: '0938df470ceedda38b7ce563df8b9ece0c296bc11ba52d4e0a3fef16a19bb0f7'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7f46d4646e632f9a9770556252eb83d377cd7b4b4b5dbc89f3bf6b0bba6ee3cf2f44bd915ef1562b8daaeb7acef139c9f9dd874bff22e5f84eac5a0f117a2f4
|
|
7
|
+
data.tar.gz: 6db50a84acd413e8bda0d6cdbdaff8752d643b43c1bfdeba7d6b233116a759b2f7ca82061e39ec691e09598ca5e0177e89d5cdb6276a1bf7555eca566d9970b6
|
data/img/timely-kb.svg
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1500 560" width="1500" height="560" font-family="monospace, Courier New, Courier">
|
|
2
|
+
<defs>
|
|
3
|
+
<style>
|
|
4
|
+
.key { fill: #333; stroke: #555; stroke-width: 1; rx: 5; ry: 5; }
|
|
5
|
+
.kw { fill: #2a2a2a; stroke: #555; stroke-width: 1; rx: 5; ry: 5; }
|
|
6
|
+
.kl { font-size: 12px; fill: #bbb; font-weight: bold; text-anchor: middle; }
|
|
7
|
+
.lo { font-size: 8.5px; fill: #FF8888; text-anchor: middle; }
|
|
8
|
+
.up { font-size: 8.5px; fill: #88BBFF; text-anchor: middle; }
|
|
9
|
+
.ct { font-size: 10px; fill: #88FF88; }
|
|
10
|
+
.sp { font-size: 8.5px; fill: #DDAAFF; text-anchor: middle; }
|
|
11
|
+
.nv { font-size: 8.5px; fill: #bbb; text-anchor: middle; }
|
|
12
|
+
</style>
|
|
13
|
+
</defs>
|
|
14
|
+
<rect width="1500" height="560" fill="#1a1a1a" rx="8"/>
|
|
15
|
+
<text x="470" y="22" text-anchor="middle" font-size="15" font-weight="bold" fill="#fff">Timely Keyboard Reference</text>
|
|
16
|
+
|
|
17
|
+
<!-- Row 0: Esc -->
|
|
18
|
+
<rect x="10" y="34" width="58" height="55" class="key"/>
|
|
19
|
+
<text x="39" y="54" class="kl">Esc</text>
|
|
20
|
+
<text x="39" y="80" class="sp">close popup</text>
|
|
21
|
+
|
|
22
|
+
<!-- Row 1: Number row kw=62 gap=4 -->
|
|
23
|
+
<rect x="10" y="97" width="58" height="55" class="key"/>
|
|
24
|
+
<text x="39" y="127" class="kl">` ~</text>
|
|
25
|
+
|
|
26
|
+
<rect x="72" y="97" width="62" height="55" class="key"/>
|
|
27
|
+
<text x="103" y="127" class="kl">1</text>
|
|
28
|
+
|
|
29
|
+
<rect x="138" y="97" width="62" height="55" class="key"/>
|
|
30
|
+
<text x="169" y="127" class="kl">2</text>
|
|
31
|
+
|
|
32
|
+
<rect x="204" y="97" width="62" height="55" class="key"/>
|
|
33
|
+
<text x="235" y="127" class="kl">3</text>
|
|
34
|
+
|
|
35
|
+
<rect x="270" y="97" width="62" height="55" class="key"/>
|
|
36
|
+
<text x="301" y="127" class="kl">4</text>
|
|
37
|
+
|
|
38
|
+
<rect x="336" y="97" width="62" height="55" class="key"/>
|
|
39
|
+
<text x="367" y="127" class="kl">5</text>
|
|
40
|
+
|
|
41
|
+
<rect x="402" y="97" width="62" height="55" class="key"/>
|
|
42
|
+
<text x="433" y="127" class="kl">6</text>
|
|
43
|
+
|
|
44
|
+
<rect x="468" y="97" width="62" height="55" class="key"/>
|
|
45
|
+
<text x="499" y="127" class="kl">7</text>
|
|
46
|
+
|
|
47
|
+
<rect x="534" y="97" width="62" height="55" class="key"/>
|
|
48
|
+
<text x="565" y="127" class="kl">8</text>
|
|
49
|
+
|
|
50
|
+
<rect x="600" y="97" width="62" height="55" class="key"/>
|
|
51
|
+
<text x="631" y="127" class="kl">9</text>
|
|
52
|
+
|
|
53
|
+
<rect x="666" y="97" width="62" height="55" class="key"/>
|
|
54
|
+
<text x="697" y="127" class="kl">0</text>
|
|
55
|
+
|
|
56
|
+
<rect x="732" y="97" width="62" height="55" class="key"/>
|
|
57
|
+
<text x="763" y="127" class="kl">- _</text>
|
|
58
|
+
|
|
59
|
+
<rect x="798" y="97" width="62" height="55" class="key"/>
|
|
60
|
+
<text x="829" y="127" class="kl">= +</text>
|
|
61
|
+
|
|
62
|
+
<rect x="864" y="97" width="76" height="55" class="kw"/>
|
|
63
|
+
<text x="902" y="130" class="nv">Bksp</text>
|
|
64
|
+
|
|
65
|
+
<!-- Row 2: QWERTY start x=10+80=90 offset, kw=62 -->
|
|
66
|
+
<rect x="10" y="160" width="76" height="55" class="kw"/>
|
|
67
|
+
<text x="48" y="193" class="nv">Tab</text>
|
|
68
|
+
|
|
69
|
+
<rect x="90" y="160" width="62" height="55" class="key"/>
|
|
70
|
+
<text x="121" y="176" class="kl">Q</text>
|
|
71
|
+
<text x="121" y="207" class="lo">q quit</text>
|
|
72
|
+
|
|
73
|
+
<rect x="156" y="160" width="62" height="55" class="key"/>
|
|
74
|
+
<text x="187" y="176" class="kl">W</text>
|
|
75
|
+
<text x="187" y="194" class="up">W Week-1</text>
|
|
76
|
+
<text x="187" y="207" class="lo">w Week+1</text>
|
|
77
|
+
|
|
78
|
+
<rect x="222" y="160" width="62" height="55" class="key"/>
|
|
79
|
+
<text x="253" y="176" class="kl">E</text>
|
|
80
|
+
<text x="253" y="194" class="up">E Prev evt</text>
|
|
81
|
+
<text x="253" y="207" class="lo">e Next evt</text>
|
|
82
|
+
|
|
83
|
+
<rect x="288" y="160" width="62" height="55" class="key"/>
|
|
84
|
+
<text x="319" y="176" class="kl">R</text>
|
|
85
|
+
<text x="319" y="207" class="lo">r reply</text>
|
|
86
|
+
|
|
87
|
+
<rect x="354" y="160" width="62" height="55" class="key"/>
|
|
88
|
+
<text x="385" y="176" class="kl">T</text>
|
|
89
|
+
<text x="385" y="207" class="lo">t today</text>
|
|
90
|
+
|
|
91
|
+
<rect x="420" y="160" width="62" height="55" class="key"/>
|
|
92
|
+
<text x="451" y="176" class="kl">Y</text>
|
|
93
|
+
<text x="451" y="194" class="up">Y Year-1</text>
|
|
94
|
+
<text x="451" y="207" class="lo">y Year+1</text>
|
|
95
|
+
|
|
96
|
+
<rect x="486" y="160" width="62" height="55" class="key"/>
|
|
97
|
+
<text x="517" y="193" class="kl">U</text>
|
|
98
|
+
|
|
99
|
+
<rect x="552" y="160" width="62" height="55" class="key"/>
|
|
100
|
+
<text x="583" y="176" class="kl">I</text>
|
|
101
|
+
<text x="583" y="207" class="lo">i import ICS</text>
|
|
102
|
+
|
|
103
|
+
<rect x="618" y="160" width="62" height="55" class="key"/>
|
|
104
|
+
<text x="649" y="176" class="kl">O</text>
|
|
105
|
+
<text x="649" y="194" class="up">O Outlook</text>
|
|
106
|
+
|
|
107
|
+
<rect x="684" y="160" width="62" height="55" class="key"/>
|
|
108
|
+
<text x="715" y="176" class="kl">P</text>
|
|
109
|
+
<text x="715" y="194" class="up">P Prefs</text>
|
|
110
|
+
|
|
111
|
+
<rect x="750" y="160" width="62" height="55" class="key"/>
|
|
112
|
+
<text x="781" y="193" class="kl">Å</text>
|
|
113
|
+
|
|
114
|
+
<rect x="816" y="160" width="62" height="55" class="key"/>
|
|
115
|
+
<text x="847" y="193" class="kl">^</text>
|
|
116
|
+
|
|
117
|
+
<rect x="882" y="160" width="58" height="55" class="key"/>
|
|
118
|
+
<text x="911" y="193" class="kl">\</text>
|
|
119
|
+
|
|
120
|
+
<!-- Row 3: ASDF start x=10+90=100 offset -->
|
|
121
|
+
<rect x="10" y="223" width="86" height="55" class="kw"/>
|
|
122
|
+
<text x="53" y="255" class="nv">Ctrl</text>
|
|
123
|
+
|
|
124
|
+
<rect x="100" y="223" width="62" height="55" class="key"/>
|
|
125
|
+
<text x="131" y="239" class="kl">A</text>
|
|
126
|
+
<text x="131" y="270" class="lo">a accept</text>
|
|
127
|
+
|
|
128
|
+
<rect x="166" y="223" width="62" height="55" class="key"/>
|
|
129
|
+
<text x="197" y="239" class="kl">S</text>
|
|
130
|
+
<text x="197" y="257" class="up">S Sync</text>
|
|
131
|
+
|
|
132
|
+
<rect x="232" y="223" width="62" height="55" class="key"/>
|
|
133
|
+
<text x="263" y="239" class="kl">D</text>
|
|
134
|
+
<text x="263" y="257" class="up">D Day-1</text>
|
|
135
|
+
<text x="263" y="270" class="lo">d Day+1</text>
|
|
136
|
+
|
|
137
|
+
<rect x="298" y="223" width="62" height="55" class="key"/>
|
|
138
|
+
<text x="329" y="255" class="kl">F</text>
|
|
139
|
+
|
|
140
|
+
<rect x="364" y="223" width="62" height="55" class="key"/>
|
|
141
|
+
<text x="395" y="239" class="kl">G</text>
|
|
142
|
+
<text x="395" y="257" class="up">G Google</text>
|
|
143
|
+
<text x="395" y="270" class="lo">g go to date</text>
|
|
144
|
+
|
|
145
|
+
<rect x="430" y="223" width="62" height="55" class="key"/>
|
|
146
|
+
<text x="461" y="239" class="kl">H</text>
|
|
147
|
+
<text x="461" y="270" class="nv">h = Day-1</text>
|
|
148
|
+
|
|
149
|
+
<rect x="496" y="223" width="62" height="55" class="key"/>
|
|
150
|
+
<text x="527" y="239" class="kl">J</text>
|
|
151
|
+
<text x="527" y="270" class="lo">j next e/d</text>
|
|
152
|
+
|
|
153
|
+
<rect x="562" y="223" width="62" height="55" class="key"/>
|
|
154
|
+
<text x="593" y="239" class="kl">K</text>
|
|
155
|
+
<text x="593" y="270" class="lo">k prev e/d</text>
|
|
156
|
+
|
|
157
|
+
<rect x="628" y="223" width="62" height="55" class="key"/>
|
|
158
|
+
<text x="659" y="239" class="kl">L</text>
|
|
159
|
+
<text x="659" y="270" class="nv">l = Day+1</text>
|
|
160
|
+
|
|
161
|
+
<rect x="694" y="223" width="62" height="55" class="key"/>
|
|
162
|
+
<text x="725" y="255" class="kl">Ø</text>
|
|
163
|
+
|
|
164
|
+
<rect x="760" y="223" width="62" height="55" class="key"/>
|
|
165
|
+
<text x="791" y="255" class="kl">Æ</text>
|
|
166
|
+
|
|
167
|
+
<rect x="826" y="223" width="114" height="55" class="kw"/>
|
|
168
|
+
<text x="883" y="248" class="nv">Enter</text>
|
|
169
|
+
<text x="883" y="268" class="sp">edit event</text>
|
|
170
|
+
|
|
171
|
+
<!-- Row 4: ZXCV start x=10+100=110 offset -->
|
|
172
|
+
<rect x="10" y="286" width="96" height="55" class="kw"/>
|
|
173
|
+
<text x="58" y="318" class="nv">Shift</text>
|
|
174
|
+
|
|
175
|
+
<rect x="110" y="286" width="62" height="55" class="key"/>
|
|
176
|
+
<text x="141" y="315" class="kl">Z</text>
|
|
177
|
+
|
|
178
|
+
<rect x="176" y="286" width="62" height="55" class="key"/>
|
|
179
|
+
<text x="207" y="302" class="kl">X</text>
|
|
180
|
+
<text x="207" y="333" class="sp">x del event</text>
|
|
181
|
+
|
|
182
|
+
<rect x="242" y="286" width="62" height="55" class="key"/>
|
|
183
|
+
<text x="273" y="302" class="kl">C</text>
|
|
184
|
+
<text x="273" y="320" class="up">C Calendars</text>
|
|
185
|
+
|
|
186
|
+
<rect x="308" y="286" width="62" height="55" class="key"/>
|
|
187
|
+
<text x="339" y="302" class="kl">V</text>
|
|
188
|
+
<text x="339" y="333" class="lo">v view popup</text>
|
|
189
|
+
|
|
190
|
+
<rect x="374" y="286" width="62" height="55" class="key"/>
|
|
191
|
+
<text x="405" y="315" class="kl">B</text>
|
|
192
|
+
|
|
193
|
+
<rect x="440" y="286" width="62" height="55" class="key"/>
|
|
194
|
+
<text x="471" y="302" class="kl">N</text>
|
|
195
|
+
<text x="471" y="333" class="lo">n new event</text>
|
|
196
|
+
|
|
197
|
+
<rect x="506" y="286" width="62" height="55" class="key"/>
|
|
198
|
+
<text x="537" y="302" class="kl">M</text>
|
|
199
|
+
<text x="537" y="320" class="up">M Month-1</text>
|
|
200
|
+
<text x="537" y="333" class="lo">m Month+1</text>
|
|
201
|
+
|
|
202
|
+
<rect x="572" y="286" width="62" height="55" class="key"/>
|
|
203
|
+
<text x="603" y="315" class="kl">, <</text>
|
|
204
|
+
|
|
205
|
+
<rect x="638" y="286" width="62" height="55" class="key"/>
|
|
206
|
+
<text x="669" y="315" class="kl">. ></text>
|
|
207
|
+
|
|
208
|
+
<rect x="704" y="286" width="62" height="55" class="key"/>
|
|
209
|
+
<text x="735" y="302" class="kl">/ ?</text>
|
|
210
|
+
<text x="735" y="333" class="sp">? help</text>
|
|
211
|
+
|
|
212
|
+
<rect x="770" y="286" width="170" height="55" class="kw"/>
|
|
213
|
+
<text x="855" y="318" class="nv">Shift</text>
|
|
214
|
+
|
|
215
|
+
<!-- Row 5: Bottom -->
|
|
216
|
+
<rect x="10" y="349" width="70" height="50" class="kw"/>
|
|
217
|
+
<text x="45" y="378" class="nv">Ctrl</text>
|
|
218
|
+
|
|
219
|
+
<rect x="84" y="349" width="58" height="50" class="kw"/>
|
|
220
|
+
<text x="113" y="378" class="nv">Alt</text>
|
|
221
|
+
|
|
222
|
+
<rect x="146" y="349" width="330" height="50" class="key"/>
|
|
223
|
+
<text x="311" y="378" class="nv">Space</text>
|
|
224
|
+
|
|
225
|
+
<rect x="480" y="349" width="62" height="50" class="key"/>
|
|
226
|
+
<text x="511" y="378" class="kl"># @</text>
|
|
227
|
+
|
|
228
|
+
<!-- Nav cluster -->
|
|
229
|
+
<rect x="990" y="97" width="64" height="55" class="key"/>
|
|
230
|
+
<text x="1022" y="120" class="nv">Home</text>
|
|
231
|
+
<text x="1022" y="143" class="nv">top slot</text>
|
|
232
|
+
|
|
233
|
+
<rect x="1058" y="97" width="64" height="55" class="key"/>
|
|
234
|
+
<text x="1090" y="120" class="nv">End</text>
|
|
235
|
+
<text x="1090" y="143" class="nv">btm slot</text>
|
|
236
|
+
|
|
237
|
+
<rect x="990" y="160" width="64" height="55" class="key"/>
|
|
238
|
+
<text x="1022" y="183" class="nv">PgUp</text>
|
|
239
|
+
<text x="1022" y="206" class="nv">page slots</text>
|
|
240
|
+
|
|
241
|
+
<rect x="1058" y="160" width="64" height="55" class="key"/>
|
|
242
|
+
<text x="1090" y="183" class="nv">PgDn</text>
|
|
243
|
+
<text x="1090" y="206" class="nv">page slots</text>
|
|
244
|
+
|
|
245
|
+
<rect x="990" y="223" width="64" height="55" class="key"/>
|
|
246
|
+
<text x="1022" y="248" class="nv">Del</text>
|
|
247
|
+
<text x="1022" y="268" class="sp">del event</text>
|
|
248
|
+
|
|
249
|
+
<rect x="1022" y="286" width="60" height="55" class="key"/>
|
|
250
|
+
<text x="1052" y="310" class="nv">UP</text>
|
|
251
|
+
<text x="1052" y="333" class="nv">prev slot</text>
|
|
252
|
+
|
|
253
|
+
<rect x="958" y="349" width="60" height="50" class="key"/>
|
|
254
|
+
<text x="988" y="370" class="nv">LEFT</text>
|
|
255
|
+
<text x="988" y="390" class="nv">prev day</text>
|
|
256
|
+
|
|
257
|
+
<rect x="1022" y="349" width="60" height="50" class="key"/>
|
|
258
|
+
<text x="1052" y="370" class="nv">DOWN</text>
|
|
259
|
+
<text x="1052" y="390" class="nv">next slot</text>
|
|
260
|
+
|
|
261
|
+
<rect x="1086" y="349" width="60" height="50" class="key"/>
|
|
262
|
+
<text x="1116" y="370" class="nv">RIGHT</text>
|
|
263
|
+
<text x="1116" y="390" class="nv">next day</text>
|
|
264
|
+
|
|
265
|
+
<!-- Right panel: Ctrl combos -->
|
|
266
|
+
<rect x="1180" y="34" width="300" height="200" rx="6" fill="#1a2a1a" stroke="#4a4" stroke-width="1"/>
|
|
267
|
+
<text x="1330" y="56" text-anchor="middle" font-size="13" font-weight="bold" fill="#66FF66">Ctrl Combos</text>
|
|
268
|
+
<text x="1194" y="80" class="ct">C-R Refresh all calendars</text>
|
|
269
|
+
<text x="1194" y="105" class="ct">C-L Redraw screen</text>
|
|
270
|
+
<text x="1194" y="130" class="ct">C-Y Copy event to clipboard</text>
|
|
271
|
+
|
|
272
|
+
<!-- Legend -->
|
|
273
|
+
<rect x="20" y="470" width="14" height="14" fill="#3a2222" rx="2" stroke="#FF6666" stroke-width="0.5"/>
|
|
274
|
+
<text x="40" y="482" font-size="10" fill="#FF6666">lowercase</text>
|
|
275
|
+
<rect x="120" y="470" width="14" height="14" fill="#22223a" rx="2" stroke="#6699FF" stroke-width="0.5"/>
|
|
276
|
+
<text x="140" y="482" font-size="10" fill="#6699FF">UPPERCASE</text>
|
|
277
|
+
<rect x="240" y="470" width="14" height="14" fill="#223a22" rx="2" stroke="#66FF66" stroke-width="0.5"/>
|
|
278
|
+
<text x="260" y="482" font-size="10" fill="#66FF66">Ctrl+key</text>
|
|
279
|
+
<rect x="350" y="470" width="14" height="14" fill="#2a223a" rx="2" stroke="#CC99FF" stroke-width="0.5"/>
|
|
280
|
+
<text x="370" y="482" font-size="10" fill="#CC99FF">special</text>
|
|
281
|
+
<text x="460" y="482" font-size="10" fill="#888">gray = navigation</text>
|
|
282
|
+
</svg>
|
|
@@ -123,6 +123,9 @@ module Timely
|
|
|
123
123
|
when 't'
|
|
124
124
|
@selected_date = Date.today
|
|
125
125
|
@selected_event_index = 0
|
|
126
|
+
now = Time.now
|
|
127
|
+
@selected_slot = now.hour * 2 + (now.min >= 30 ? 1 : 0)
|
|
128
|
+
@slot_offset = [@selected_slot - 5, 0].max
|
|
126
129
|
date_changed
|
|
127
130
|
when 'g'
|
|
128
131
|
go_to_date
|
|
@@ -150,6 +153,17 @@ module Timely
|
|
|
150
153
|
manual_sync
|
|
151
154
|
when 'C'
|
|
152
155
|
show_calendars
|
|
156
|
+
when 'C-R'
|
|
157
|
+
@_cached_planets_date = nil # Force planet recalculation
|
|
158
|
+
@weather_forecast = nil # Force weather refresh
|
|
159
|
+
@_weather_fetched_at = nil
|
|
160
|
+
@db.execute("DELETE FROM weather_cache") rescue nil # Clear DB cache too
|
|
161
|
+
load_events_for_range
|
|
162
|
+
render_all
|
|
163
|
+
when 'C-L'
|
|
164
|
+
Rcurses.clear_screen
|
|
165
|
+
create_panes
|
|
166
|
+
render_all
|
|
153
167
|
when 'P'
|
|
154
168
|
show_preferences
|
|
155
169
|
when '?'
|
|
@@ -267,6 +281,19 @@ module Timely
|
|
|
267
281
|
def date_changed
|
|
268
282
|
@selected_event_index = 0
|
|
269
283
|
load_events_for_range
|
|
284
|
+
# If slot is in all-day area but no event there, jump to first timed event or current time
|
|
285
|
+
if @selected_slot && @selected_slot < 0 && event_at_selected_slot.nil?
|
|
286
|
+
events = events_on_selected_day
|
|
287
|
+
first_timed = events.find { |e| e['all_day'].to_i != 1 }
|
|
288
|
+
if first_timed
|
|
289
|
+
t = Time.at(first_timed['start_time'].to_i)
|
|
290
|
+
@selected_slot = t.hour * 2 + (t.min >= 30 ? 1 : 0)
|
|
291
|
+
else
|
|
292
|
+
now = Time.now
|
|
293
|
+
@selected_slot = now.hour * 2 + (now.min >= 30 ? 1 : 0)
|
|
294
|
+
end
|
|
295
|
+
@slot_offset = [@selected_slot - 5, 0].max
|
|
296
|
+
end
|
|
270
297
|
render_all
|
|
271
298
|
end
|
|
272
299
|
|
|
@@ -313,8 +340,14 @@ module Timely
|
|
|
313
340
|
desc = desc.gsub(/<[^>]+>/, ' ').gsub(/ /i, ' ').gsub(/&/i, '&').gsub(/</i, '<').gsub(/>/i, '>')
|
|
314
341
|
end
|
|
315
342
|
end
|
|
316
|
-
# Remove common garbage from Google/Outlook descriptions
|
|
317
|
-
desc.gsub(/BC\d+-Color:\s*-?\d+\s*/, '')
|
|
343
|
+
# Remove common garbage from Google/Outlook/Teams descriptions
|
|
344
|
+
desc.gsub(/BC\d+-Color:\s*-?\d+\s*/, '') # Google color metadata
|
|
345
|
+
.gsub(/-::~:~::~:~.*$/m, '') # Google Meet block
|
|
346
|
+
.gsub(/_{3,}/, '') # Long underscore lines (Teams HR)
|
|
347
|
+
.gsub(/-{5,}/, '') # Long dash lines
|
|
348
|
+
.gsub(/[━═─]{3,}/, '') # Heavy/double/light box-drawing lines
|
|
349
|
+
.gsub(/\n{3,}/, "\n\n") # Collapse excessive blank lines
|
|
350
|
+
.strip
|
|
318
351
|
end
|
|
319
352
|
|
|
320
353
|
# Find the event at the currently selected time slot
|
|
@@ -323,11 +356,18 @@ module Timely
|
|
|
323
356
|
events = events_on_selected_day
|
|
324
357
|
|
|
325
358
|
if @selected_slot < 0
|
|
326
|
-
# Negative slot
|
|
359
|
+
# Negative slot uses max_allday across the visible week (same as render_mid_pane)
|
|
360
|
+
week_start = @selected_date - (@selected_date.cwday - 1)
|
|
361
|
+
max_allday = 0
|
|
362
|
+
7.times do |i|
|
|
363
|
+
day = week_start + i
|
|
364
|
+
n = (@events_by_date[day] || []).count { |e| e['all_day'].to_i == 1 }
|
|
365
|
+
max_allday = n if n > max_allday
|
|
366
|
+
end
|
|
327
367
|
allday = events.select { |e| e['all_day'].to_i == 1 }
|
|
328
|
-
|
|
329
|
-
idx
|
|
330
|
-
return
|
|
368
|
+
idx = max_allday - @selected_slot.abs # -max -> 0 (first), -1 -> max-1 (last)
|
|
369
|
+
return allday[idx] if idx >= 0 && idx < allday.size
|
|
370
|
+
return nil
|
|
331
371
|
end
|
|
332
372
|
|
|
333
373
|
hour = @selected_slot / 2
|
|
@@ -363,6 +403,7 @@ module Timely
|
|
|
363
403
|
# If there are more events on the current day after the selected one, go to next
|
|
364
404
|
if events.length > 0 && @selected_event_index < events.length - 1
|
|
365
405
|
@selected_event_index += 1
|
|
406
|
+
move_slot_to_event(events[@selected_event_index], events)
|
|
366
407
|
render_mid_pane
|
|
367
408
|
render_bottom_pane
|
|
368
409
|
return
|
|
@@ -375,7 +416,10 @@ module Timely
|
|
|
375
416
|
if day_events && !day_events.empty?
|
|
376
417
|
@selected_date = check_date
|
|
377
418
|
@selected_event_index = 0
|
|
378
|
-
|
|
419
|
+
load_events_for_range
|
|
420
|
+
events = events_on_selected_day
|
|
421
|
+
move_slot_to_event(events.first, events) if events.any?
|
|
422
|
+
render_all
|
|
379
423
|
return
|
|
380
424
|
end
|
|
381
425
|
end
|
|
@@ -388,6 +432,7 @@ module Timely
|
|
|
388
432
|
# If there are more events on the current day before the selected one, go to prev
|
|
389
433
|
if events.length > 0 && @selected_event_index > 0
|
|
390
434
|
@selected_event_index -= 1
|
|
435
|
+
move_slot_to_event(events[@selected_event_index], events)
|
|
391
436
|
render_mid_pane
|
|
392
437
|
render_bottom_pane
|
|
393
438
|
return
|
|
@@ -399,8 +444,11 @@ module Timely
|
|
|
399
444
|
day_events = @db.get_events_for_date(check_date)
|
|
400
445
|
if day_events && !day_events.empty?
|
|
401
446
|
@selected_date = check_date
|
|
402
|
-
|
|
403
|
-
|
|
447
|
+
load_events_for_range
|
|
448
|
+
events = events_on_selected_day
|
|
449
|
+
@selected_event_index = [events.length - 1, 0].max
|
|
450
|
+
move_slot_to_event(events.last, events) if events.any?
|
|
451
|
+
render_all
|
|
404
452
|
return
|
|
405
453
|
end
|
|
406
454
|
end
|
|
@@ -408,6 +456,27 @@ module Timely
|
|
|
408
456
|
show_feedback("No earlier events found within the past year", 245)
|
|
409
457
|
end
|
|
410
458
|
|
|
459
|
+
def move_slot_to_event(evt, day_events)
|
|
460
|
+
return unless evt
|
|
461
|
+
if evt['all_day'].to_i == 1
|
|
462
|
+
# Compute max_allday across the visible week (same as render_mid_pane)
|
|
463
|
+
week_start = @selected_date - (@selected_date.cwday - 1)
|
|
464
|
+
max_allday = 0
|
|
465
|
+
7.times do |i|
|
|
466
|
+
day = week_start + i
|
|
467
|
+
n = (@events_by_date[day] || []).count { |e| e['all_day'].to_i == 1 }
|
|
468
|
+
max_allday = n if n > max_allday
|
|
469
|
+
end
|
|
470
|
+
allday = day_events.select { |e| e['all_day'].to_i == 1 }
|
|
471
|
+
idx = allday.index { |e| e['id'] == evt['id'] } || 0
|
|
472
|
+
@selected_slot = -(max_allday - idx)
|
|
473
|
+
else
|
|
474
|
+
t = Time.at(evt['start_time'].to_i)
|
|
475
|
+
@selected_slot = t.hour * 2 + (t.min >= 30 ? 1 : 0)
|
|
476
|
+
@slot_offset = [@selected_slot - 5, 0].max
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
411
480
|
# --- Data loading ---
|
|
412
481
|
|
|
413
482
|
def load_events_for_range
|
|
@@ -839,7 +908,16 @@ module Timely
|
|
|
839
908
|
astro.each { |evt| lines << " #{evt}".fg(180) } if astro.any?
|
|
840
909
|
|
|
841
910
|
lines << ""
|
|
842
|
-
|
|
911
|
+
if events.any?
|
|
912
|
+
allday = events.count { |e| e['all_day'].to_i == 1 }
|
|
913
|
+
timed = events.size - allday
|
|
914
|
+
parts = []
|
|
915
|
+
parts << "#{timed} timed" if timed > 0
|
|
916
|
+
parts << "#{allday} all-day" if allday > 0
|
|
917
|
+
lines << " #{parts.join(', ')} event#{'s' if events.size > 1} today".fg(240)
|
|
918
|
+
else
|
|
919
|
+
lines << " No events scheduled".fg(240)
|
|
920
|
+
end
|
|
843
921
|
end
|
|
844
922
|
|
|
845
923
|
# Pad to fill pane
|
|
@@ -854,6 +932,7 @@ module Timely
|
|
|
854
932
|
# --- Actions ---
|
|
855
933
|
|
|
856
934
|
def go_to_date
|
|
935
|
+
blank_bottom("")
|
|
857
936
|
input = bottom_ask("Go to: ", "")
|
|
858
937
|
return if input.nil? || input.strip.empty?
|
|
859
938
|
|
|
@@ -1112,8 +1191,8 @@ module Timely
|
|
|
1112
1191
|
end
|
|
1113
1192
|
|
|
1114
1193
|
text = lines.join("\n")
|
|
1115
|
-
IO.popen('xclip -selection clipboard', 'w') { |io| io.write(text) } rescue
|
|
1116
|
-
|
|
1194
|
+
IO.popen('xclip -selection clipboard', 'w') { |io| io.write(text) } rescue nil
|
|
1195
|
+
IO.popen('xclip -selection primary', 'w') { |io| io.write(text) } rescue nil
|
|
1117
1196
|
show_feedback("Event copied to clipboard", 156)
|
|
1118
1197
|
end
|
|
1119
1198
|
|
|
@@ -1192,7 +1271,7 @@ module Timely
|
|
|
1192
1271
|
end
|
|
1193
1272
|
|
|
1194
1273
|
lines << ""
|
|
1195
|
-
lines << " " + "UP/DOWN:scroll ESC/q:close".fg(245)
|
|
1274
|
+
lines << " " + "UP/DOWN:scroll C-Y:copy ESC/q:close".fg(245)
|
|
1196
1275
|
|
|
1197
1276
|
popup.text = lines.join("\n")
|
|
1198
1277
|
popup.refresh
|
|
@@ -1210,6 +1289,13 @@ module Timely
|
|
|
1210
1289
|
popup.pagedown
|
|
1211
1290
|
when 'PgUP'
|
|
1212
1291
|
popup.pageup
|
|
1292
|
+
when 'C-Y'
|
|
1293
|
+
clean = lines.map { |l| l.respond_to?(:pure) ? l.pure : l.gsub(/\e\[[0-9;]*m/, '') }.join("\n")
|
|
1294
|
+
IO.popen('xclip -selection clipboard', 'w') { |io| io.write(clean) } rescue nil
|
|
1295
|
+
IO.popen('xclip -selection primary', 'w') { |io| io.write(clean) } rescue nil
|
|
1296
|
+
lines[-1] = " " + "Copied to clipboard".fg(156)
|
|
1297
|
+
popup.text = lines.join("\n")
|
|
1298
|
+
popup.refresh
|
|
1213
1299
|
end
|
|
1214
1300
|
end
|
|
1215
1301
|
|
data/lib/timely/ui/panes.rb
CHANGED
|
@@ -14,9 +14,9 @@ module Timely
|
|
|
14
14
|
def create_panes
|
|
15
15
|
@panes = {}
|
|
16
16
|
|
|
17
|
-
# Layout: info(1) + top(months, fixed
|
|
18
|
-
# Top pane: 1 blank row + 8 month rows =
|
|
19
|
-
top_h =
|
|
17
|
+
# Layout: info(1) + top(months, fixed 10) + mid(week, flexible) + bottom(details) + status(1)
|
|
18
|
+
# Top pane: 1 blank row + 8 month rows + 1 padding row = 10
|
|
19
|
+
top_h = 10
|
|
20
20
|
bottom_h = (@h * 0.2).to_i
|
|
21
21
|
bottom_h = [bottom_h, 5].max
|
|
22
22
|
mid_h = @h - 2 - top_h - bottom_h # 2 = info + status
|
data/lib/timely/version.rb
CHANGED
data/lib/timely/weather.rb
CHANGED
|
@@ -31,6 +31,7 @@ module Timely
|
|
|
31
31
|
uri = URI("https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=#{lat}&lon=#{lon}")
|
|
32
32
|
req = Net::HTTP::Get.new(uri)
|
|
33
33
|
req['User-Agent'] = 'timely-calendar/0.1 g@isene.com'
|
|
34
|
+
req['Accept-Encoding'] = 'identity'
|
|
34
35
|
|
|
35
36
|
res = Net::HTTP.start(uri.hostname, uri.port,
|
|
36
37
|
use_ssl: true,
|
|
@@ -91,7 +92,7 @@ module Timely
|
|
|
91
92
|
if db
|
|
92
93
|
db.execute(
|
|
93
94
|
"INSERT OR REPLACE INTO weather_cache (date, hour, data, fetched_at) VALUES (?, ?, ?, ?)",
|
|
94
|
-
'forecast', '00', JSON.generate(forecast), Time.now.to_i
|
|
95
|
+
['forecast', '00', JSON.generate(forecast), Time.now.to_i]
|
|
95
96
|
)
|
|
96
97
|
end
|
|
97
98
|
|
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.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-03-
|
|
12
|
+
date: 2026-03-27 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rcurses
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- CLAUDE.md
|
|
54
54
|
- README.md
|
|
55
55
|
- bin/timely
|
|
56
|
+
- img/timely-kb.svg
|
|
56
57
|
- img/timely.svg
|
|
57
58
|
- lib/timely.rb
|
|
58
59
|
- lib/timely/astronomy.rb
|