Skip to content

Commit bf2bce5

Browse files
committed
feat: support discord timestamps
next step: automatically fetch latest sar version a-la sar.portal2.sr
1 parent e8cd97f commit bf2bce5

File tree

5 files changed

+112
-2
lines changed

5 files changed

+112
-2
lines changed

generate.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import csv
5+
import re
56
import markdown
67

78
def mdspan(md, classname=None):
@@ -114,6 +115,17 @@ def generate_command_table():
114115
if not ent_path.endswith(".md"): continue
115116
md_str += read_section(ent_path[:-3])
116117

118+
# Replace Discord-style UNIX epoch markdown like <t:1609459200:R>
119+
# with HTML <time> elements that client-side JS will render.
120+
def _replace_timestamp_tokens(s):
121+
def _repl(m):
122+
epoch = m.group(1)
123+
fmt = m.group(2)
124+
return f'<time class="discord-timestamp" data-epoch="{epoch}" data-format="{fmt}">{epoch}</time>'
125+
return re.sub(r'<t:(\d+):([tTdDfFR])>', _repl, s)
126+
127+
md_str = _replace_timestamp_tokens(md_str)
128+
117129
md = markdown.Markdown(extensions=['toc'])
118130
content = md.convert(md_str)
119131

out/scroll.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const calcNavAmounts = (root, root_top, root_bottom, root_min, root_max) => {
1010
const min = root_min + (root_max - root_min) * nest_min;
1111
const max = root_min + (root_max - root_min) * nest_max;
1212
if (child.tagName === "A") {
13-
id = child.href.substring(child.href.lastIndexOf("#") + 1)
13+
if (!child.href.includes("#")) continue;
14+
const id = child.href.substring(child.href.lastIndexOf("#") + 1)
1415
nav_amounts.push({ id: id, min: min, max: max });
1516
} else {
1617
nav_amounts = nav_amounts.concat(calcNavAmounts(child, rect.top, rect.bottom, min, max));

out/style.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ nav .navindent {
9898
font-size: 0.9em;
9999
}
100100

101-
:not(td) > code {
101+
:not(td) > code, time {
102102
padding: 1px 2px;
103103

104104
background: #3a3a3a;
@@ -197,6 +197,7 @@ table.commands tbody {
197197
display: block;
198198

199199
max-width: calc(100vw - 2 * 20px); /* 20px is the horizontal padding on "main" */
200+
min-height: 30em;
200201
max-height: 30em;
201202
overflow: auto;
202203
}

out/timestamp.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
(function(){
2+
function formatDate(epoch, fmt){
3+
const d = new Date(Number(epoch) * 1000);
4+
switch(fmt){
5+
case 't': return d.toLocaleTimeString(undefined, {hour: 'numeric', minute: '2-digit'});
6+
case 'T': return d.toLocaleTimeString(undefined, {hour: 'numeric', minute: '2-digit', second: '2-digit'});
7+
case 'd': return d.toLocaleDateString();
8+
case 'D': return d.toLocaleDateString(undefined, {year:'numeric', month:'long', day:'numeric'});
9+
case 'f': return d.toLocaleString(undefined, {year:'numeric', month:'short', day:'numeric', hour:'numeric', minute:'2-digit'});
10+
case 'F': return d.toLocaleString(undefined, {weekday:'long', year:'numeric', month:'long', day:'numeric', hour:'numeric', minute:'2-digit'});
11+
case 'R': return relativeTime(new Date(Number(epoch) * 1000));
12+
default: return d.toString();
13+
}
14+
}
15+
16+
function relativeTime(date){
17+
const now = Date.now();
18+
let diff = Math.floor((now - date.getTime()) / 1000);
19+
if (Math.abs(diff) < 5) return 'just now';
20+
const intervals = [
21+
[60, 'second', 1],
22+
[3600, 'minute', 60],
23+
[86400, 'hour', 3600],
24+
[604800, 'day', 86400],
25+
[2629800, 'week', 604800],
26+
[31557600, 'month', 2629800],
27+
[Number.MAX_SAFE_INTEGER, 'year', 31557600]
28+
];
29+
const past = diff > 0;
30+
diff = Math.abs(diff);
31+
for (let i = 0; i < intervals.length; i++){
32+
if (diff < intervals[i][0]){
33+
const val = Math.floor(diff / intervals[i][2]) || 0;
34+
const unit = intervals[i][1] + (val === 1 ? '' : 's');
35+
return past ? `${val} ${unit} ago` : `in ${val} ${unit}`;
36+
}
37+
}
38+
return '';
39+
}
40+
41+
function renderElement(el){
42+
const epoch = el.getAttribute('data-epoch');
43+
const fmt = el.getAttribute('data-format') || 'f';
44+
if (!epoch) return;
45+
el.textContent = formatDate(epoch, fmt);
46+
el.title = new Date(Number(epoch) * 1000).toLocaleString();
47+
}
48+
49+
// Schedule updates per-element for relative timestamps
50+
function scheduleRelative(el){
51+
// Render now
52+
renderElement(el);
53+
54+
// Compute next update delay based on current distance
55+
const epochMs = Number(el.getAttribute('data-epoch')) * 1000;
56+
const now = Date.now();
57+
let diff = Math.floor((now - epochMs) / 1000);
58+
const absDiff = Math.abs(diff);
59+
60+
let unitSeconds;
61+
if (absDiff < 60) unitSeconds = 1; // seconds
62+
else if (absDiff < 3600) unitSeconds = 60; // minutes
63+
else if (absDiff < 86400) unitSeconds = 3600; // hours
64+
else if (absDiff < 604800) unitSeconds = 86400; // days
65+
else if (absDiff < 2629800) unitSeconds = 604800; // weeks
66+
else if (absDiff < 31557600) unitSeconds = 2629800; // months
67+
else unitSeconds = 31557600; // years
68+
69+
const remainder = absDiff % unitSeconds;
70+
let nextSeconds = remainder === 0 ? unitSeconds : (unitSeconds - remainder);
71+
if (nextSeconds < 1) nextSeconds = 1;
72+
73+
// Clear previous timer if any
74+
if (el._timeoutId) clearTimeout(el._timeoutId);
75+
76+
el._timeoutId = setTimeout(function(){
77+
// On timeout, re-schedule (this will re-render and compute next delay)
78+
scheduleRelative(el);
79+
}, nextSeconds * 1000);
80+
}
81+
82+
function init(){
83+
// Render non-relative timestamps once
84+
document.querySelectorAll('time.discord-timestamp').forEach(function(el){
85+
const fmt = el.getAttribute('data-format') || 'f';
86+
if (fmt === 'R'){
87+
scheduleRelative(el);
88+
} else {
89+
renderElement(el);
90+
}
91+
});
92+
}
93+
94+
document.addEventListener('DOMContentLoaded', init);
95+
})();

template.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ <h1>Portal 2 Rules</h1>
2525
<script src="scroll.js"></script>
2626
<script src="mobile-menu.js"></script>
2727
<script src="command-table.js"></script>
28+
<script src="timestamp.js"></script>
2829
</html>

0 commit comments

Comments
 (0)