timely-calendar 1.0.1 → 1.0.2
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 +98 -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: a336a4e6f30f78ab53e5da5005c8a95114d0dc6657d2c5323101615d3cfa4015
|
|
4
|
+
data.tar.gz: fc5783786223c1d421832cb0cf46599192fb0f06386fae3c6a7cfc0050aeedf6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a62d684e33d3f597748b142e18e0d0c5dc579d4bdb11c3d143efe4fec76129d982d5b47a4944b89e7466a72be92a680a8d10feac54539b1220a23048cc34cf4
|
|
7
|
+
data.tar.gz: f2613ecb9a86d8febc5cd8de611c42b371b54440ee6a6c5b7c2c4bffabaf29854be4795d7ba9aa601f982fee6179962956096e567e0a27ba200d67254e3be1cf
|
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,13 @@ 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(/\n{3,}/, "\n\n") # Collapse excessive blank lines
|
|
349
|
+
.strip
|
|
318
350
|
end
|
|
319
351
|
|
|
320
352
|
# Find the event at the currently selected time slot
|
|
@@ -323,11 +355,18 @@ module Timely
|
|
|
323
355
|
events = events_on_selected_day
|
|
324
356
|
|
|
325
357
|
if @selected_slot < 0
|
|
326
|
-
# Negative slot
|
|
358
|
+
# Negative slot uses max_allday across the visible week (same as render_mid_pane)
|
|
359
|
+
week_start = @selected_date - (@selected_date.cwday - 1)
|
|
360
|
+
max_allday = 0
|
|
361
|
+
7.times do |i|
|
|
362
|
+
day = week_start + i
|
|
363
|
+
n = (@events_by_date[day] || []).count { |e| e['all_day'].to_i == 1 }
|
|
364
|
+
max_allday = n if n > max_allday
|
|
365
|
+
end
|
|
327
366
|
allday = events.select { |e| e['all_day'].to_i == 1 }
|
|
328
|
-
|
|
329
|
-
idx
|
|
330
|
-
return
|
|
367
|
+
idx = max_allday - @selected_slot.abs # -max -> 0 (first), -1 -> max-1 (last)
|
|
368
|
+
return allday[idx] if idx >= 0 && idx < allday.size
|
|
369
|
+
return nil
|
|
331
370
|
end
|
|
332
371
|
|
|
333
372
|
hour = @selected_slot / 2
|
|
@@ -363,6 +402,7 @@ module Timely
|
|
|
363
402
|
# If there are more events on the current day after the selected one, go to next
|
|
364
403
|
if events.length > 0 && @selected_event_index < events.length - 1
|
|
365
404
|
@selected_event_index += 1
|
|
405
|
+
move_slot_to_event(events[@selected_event_index], events)
|
|
366
406
|
render_mid_pane
|
|
367
407
|
render_bottom_pane
|
|
368
408
|
return
|
|
@@ -375,7 +415,10 @@ module Timely
|
|
|
375
415
|
if day_events && !day_events.empty?
|
|
376
416
|
@selected_date = check_date
|
|
377
417
|
@selected_event_index = 0
|
|
378
|
-
|
|
418
|
+
load_events_for_range
|
|
419
|
+
events = events_on_selected_day
|
|
420
|
+
move_slot_to_event(events.first, events) if events.any?
|
|
421
|
+
render_all
|
|
379
422
|
return
|
|
380
423
|
end
|
|
381
424
|
end
|
|
@@ -388,6 +431,7 @@ module Timely
|
|
|
388
431
|
# If there are more events on the current day before the selected one, go to prev
|
|
389
432
|
if events.length > 0 && @selected_event_index > 0
|
|
390
433
|
@selected_event_index -= 1
|
|
434
|
+
move_slot_to_event(events[@selected_event_index], events)
|
|
391
435
|
render_mid_pane
|
|
392
436
|
render_bottom_pane
|
|
393
437
|
return
|
|
@@ -399,8 +443,11 @@ module Timely
|
|
|
399
443
|
day_events = @db.get_events_for_date(check_date)
|
|
400
444
|
if day_events && !day_events.empty?
|
|
401
445
|
@selected_date = check_date
|
|
402
|
-
|
|
403
|
-
|
|
446
|
+
load_events_for_range
|
|
447
|
+
events = events_on_selected_day
|
|
448
|
+
@selected_event_index = [events.length - 1, 0].max
|
|
449
|
+
move_slot_to_event(events.last, events) if events.any?
|
|
450
|
+
render_all
|
|
404
451
|
return
|
|
405
452
|
end
|
|
406
453
|
end
|
|
@@ -408,6 +455,27 @@ module Timely
|
|
|
408
455
|
show_feedback("No earlier events found within the past year", 245)
|
|
409
456
|
end
|
|
410
457
|
|
|
458
|
+
def move_slot_to_event(evt, day_events)
|
|
459
|
+
return unless evt
|
|
460
|
+
if evt['all_day'].to_i == 1
|
|
461
|
+
# Compute max_allday across the visible week (same as render_mid_pane)
|
|
462
|
+
week_start = @selected_date - (@selected_date.cwday - 1)
|
|
463
|
+
max_allday = 0
|
|
464
|
+
7.times do |i|
|
|
465
|
+
day = week_start + i
|
|
466
|
+
n = (@events_by_date[day] || []).count { |e| e['all_day'].to_i == 1 }
|
|
467
|
+
max_allday = n if n > max_allday
|
|
468
|
+
end
|
|
469
|
+
allday = day_events.select { |e| e['all_day'].to_i == 1 }
|
|
470
|
+
idx = allday.index { |e| e['id'] == evt['id'] } || 0
|
|
471
|
+
@selected_slot = -(max_allday - idx)
|
|
472
|
+
else
|
|
473
|
+
t = Time.at(evt['start_time'].to_i)
|
|
474
|
+
@selected_slot = t.hour * 2 + (t.min >= 30 ? 1 : 0)
|
|
475
|
+
@slot_offset = [@selected_slot - 5, 0].max
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
411
479
|
# --- Data loading ---
|
|
412
480
|
|
|
413
481
|
def load_events_for_range
|
|
@@ -839,7 +907,16 @@ module Timely
|
|
|
839
907
|
astro.each { |evt| lines << " #{evt}".fg(180) } if astro.any?
|
|
840
908
|
|
|
841
909
|
lines << ""
|
|
842
|
-
|
|
910
|
+
if events.any?
|
|
911
|
+
allday = events.count { |e| e['all_day'].to_i == 1 }
|
|
912
|
+
timed = events.size - allday
|
|
913
|
+
parts = []
|
|
914
|
+
parts << "#{timed} timed" if timed > 0
|
|
915
|
+
parts << "#{allday} all-day" if allday > 0
|
|
916
|
+
lines << " #{parts.join(', ')} event#{'s' if events.size > 1} today".fg(240)
|
|
917
|
+
else
|
|
918
|
+
lines << " No events scheduled".fg(240)
|
|
919
|
+
end
|
|
843
920
|
end
|
|
844
921
|
|
|
845
922
|
# Pad to fill pane
|
|
@@ -854,6 +931,7 @@ module Timely
|
|
|
854
931
|
# --- Actions ---
|
|
855
932
|
|
|
856
933
|
def go_to_date
|
|
934
|
+
blank_bottom("")
|
|
857
935
|
input = bottom_ask("Go to: ", "")
|
|
858
936
|
return if input.nil? || input.strip.empty?
|
|
859
937
|
|
|
@@ -1112,8 +1190,8 @@ module Timely
|
|
|
1112
1190
|
end
|
|
1113
1191
|
|
|
1114
1192
|
text = lines.join("\n")
|
|
1115
|
-
IO.popen('xclip -selection clipboard', 'w') { |io| io.write(text) } rescue
|
|
1116
|
-
|
|
1193
|
+
IO.popen('xclip -selection clipboard', 'w') { |io| io.write(text) } rescue nil
|
|
1194
|
+
IO.popen('xclip -selection primary', 'w') { |io| io.write(text) } rescue nil
|
|
1117
1195
|
show_feedback("Event copied to clipboard", 156)
|
|
1118
1196
|
end
|
|
1119
1197
|
|
|
@@ -1192,7 +1270,7 @@ module Timely
|
|
|
1192
1270
|
end
|
|
1193
1271
|
|
|
1194
1272
|
lines << ""
|
|
1195
|
-
lines << " " + "UP/DOWN:scroll ESC/q:close".fg(245)
|
|
1273
|
+
lines << " " + "UP/DOWN:scroll C-Y:copy ESC/q:close".fg(245)
|
|
1196
1274
|
|
|
1197
1275
|
popup.text = lines.join("\n")
|
|
1198
1276
|
popup.refresh
|
|
@@ -1210,6 +1288,13 @@ module Timely
|
|
|
1210
1288
|
popup.pagedown
|
|
1211
1289
|
when 'PgUP'
|
|
1212
1290
|
popup.pageup
|
|
1291
|
+
when 'C-Y'
|
|
1292
|
+
clean = lines.map { |l| l.respond_to?(:pure) ? l.pure : l.gsub(/\e\[[0-9;]*m/, '') }.join("\n")
|
|
1293
|
+
IO.popen('xclip -selection clipboard', 'w') { |io| io.write(clean) } rescue nil
|
|
1294
|
+
IO.popen('xclip -selection primary', 'w') { |io| io.write(clean) } rescue nil
|
|
1295
|
+
lines[-1] = " " + "Copied to clipboard".fg(156)
|
|
1296
|
+
popup.text = lines.join("\n")
|
|
1297
|
+
popup.refresh
|
|
1213
1298
|
end
|
|
1214
1299
|
end
|
|
1215
1300
|
|
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.2
|
|
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-25 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
|