FossilRepo

fossilrepo / templates / fossil / partials / timeline_entries.html
1
{% load fossil_filters %}
2
<style>
3
.tl-dag { position: relative; flex-shrink: 0; }
4
.tl-node {
5
position: absolute; top: 50%; z-index: 2; border-radius: 50%;
6
transform: translate(-50%, -50%); width: 10px; height: 10px;
7
}
8
.tl-node-ci { border: 2px solid; }
9
.tl-node-merge {
10
border: 2px solid; border-radius: 2px;
11
transform: translate(-50%, -50%) rotate(45deg);
12
}
13
.tl-node-leaf {
14
border: 2px solid; background: transparent !important;
15
}
16
.tl-node-w { background: #3b82f6; border: 2px solid #60a5fa; width: 8px; height: 8px; }
17
.tl-node-t { background: #eab308; border: 2px solid #facc15; width: 8px; height: 8px; }
18
.tl-node-f { background: #a855f7; border: 2px solid #c084fc; width: 8px; height: 8px; }
19
.tl-node-other { background: #6b7280; border: 2px solid #9ca3af; width: 8px; height: 8px; }
20
.tl-vline { position: absolute; width: 2px; top: 0; bottom: 0; transform: translateX(-50%); opacity: 0.4; }
21
.tl-connector {
22
position: absolute; top: 40%; z-index: 1; border-radius: 0 0 4px 4px;
23
border-bottom: 2px solid; border-left: 2px solid; border-right: 2px solid;
24
height: 30%; opacity: 0.5;
25
}
26
.tl-connector-merge {
27
top: 20%; border-radius: 4px 4px 0 0;
28
border-top: 2px solid; border-left: 2px solid; border-right: 2px solid;
29
border-bottom: none; height: 30%; opacity: 0.5;
30
}
31
.tl-date { font-size: 0.8rem; font-weight: 700; color: #d1d5db; padding: 8px 0 4px; border-bottom: 1px solid #374151; margin-bottom: 2px; }
32
.tl-row { display: flex; min-height: 28px; align-items: center; flex-wrap: wrap; transition: background-color 150ms ease; border-radius: 4px; }
33
.tl-row:hover { background: rgba(255,255,255,0.04); }
34
.tl-time { width: 42px; flex-shrink: 0; text-align: right; font-size: 0.75rem; color: #6b7280; font-variant-numeric: tabular-nums; }
35
.tl-msg { flex: 1; min-width: 0; font-size: 0.8125rem; color: #e5e5e5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
36
.tl-msg a { color: #e5e5e5; text-decoration: none; }
37
.tl-msg a:hover { color: #e8677a; }
38
.tl-meta { display: flex; align-items: center; gap: 8px; flex-shrink: 0; padding-left: 12px; flex-wrap: wrap; }
39
@media (max-width: 639px) {
40
.tl-meta { display: none; }
41
.tl-dag { display: none; }
42
.tl-row { padding: 4px 0; }
43
}
44
.tl-hash { font-family: ui-monospace, monospace; font-size: 0.7rem; color: #DC394C; text-decoration: none; }
45
.tl-hash:hover { color: #e8677a; text-decoration: underline; }
46
.tl-user { font-size: 0.7rem; color: #9ca3af; text-decoration: none; }
47
.tl-user:hover { color: #e8677a; }
48
.tl-branch { font-size: 0.65rem; padding: 1px 6px; border-radius: 9999px; background: rgba(220,57,76,0.1); border: 1px solid rgba(220,57,76,0.2); color: #e8677a; white-space: nowrap; }
49
.tl-badge { font-size: 0.6rem; padding: 0 4px; border-radius: 3px; font-weight: 600; }
50
.tl-badge-w { background: rgba(59,130,246,0.15); color: #93c5fd; }
51
.tl-badge-t { background: rgba(234,179,8,0.15); color: #fde047; }
52
.tl-badge-f { background: rgba(168,85,247,0.15); color: #d8b4fe; }
53
</style>
54
55
<div id="timeline-entries">
56
{% if entries %}
57
{% load tz %}
58
{% for item in entries %}
59
{% with e=item.entry %}
60
61
{# Date header — show when date changes #}
62
{% ifchanged e.timestamp|date:"Y-m-d" %}
63
<div class="tl-date">{{ e.timestamp|date:"Y-m-d, l" }}</div>
64
{% endifchanged %}
65
66
<div class="tl-row">
67
{# DAG column #}
68
<div class="tl-dag" style="width: {{ item.graph_width }}px;">
69
{# Vertical rail lines — colored per rail #}
70
{% for line in item.lines %}
71
<div class="tl-vline" style="left: {{ line.x }}px; background: {{ line.color }};"></div>
72
{% endfor %}
73
{# Horizontal connectors — fork (branch-out) and merge (branch-in) #}
74
{% for conn in item.connectors %}
75
<div class="{% if conn.type == 'merge' %}tl-connector-merge{% else %}tl-connector{% endif %}"
76
style="left:{{ conn.left }}px; width:{{ conn.width }}px; border-color:{{ conn.color }};"></div>
77
{% endfor %}
78
{# Node — style varies: merge diamond, leaf open circle, normal filled circle #}
79
{% if e.event_type == 'ci' %}
80
<div class="tl-node {% if item.is_merge %}tl-node-merge{% elif item.is_leaf %}tl-node-ci tl-node-leaf{% else %}tl-node-ci{% endif %}"
81
style="left: {{ item.node_x }}px; {% if item.is_merge %}background: {{ item.node_color }}; border-color: {{ item.node_color }};{% elif item.is_leaf %}border-color: {{ item.node_color }};{% else %}background: {{ item.node_color }}; border-color: {{ item.node_color }};{% endif %}"></div>
82
{% elif e.event_type == 'w' %}
83
<div class="tl-node tl-node-w" style="left: {{ item.node_x }}px;"></div>
84
{% elif e.event_type == 't' %}
85
<div class="tl-node tl-node-t" style="left: {{ item.node_x }}px;"></div>
86
{% elif e.event_type == 'f' %}
87
<div class="tl-node tl-node-f" style="left: {{ item.node_x }}px;"></div>
88
{% else %}
89
<div class="tl-node tl-node-other" style="left: {{ item.node_x }}px;"></div>
90
{% endif %}
91
</div>
92
93
{# Time #}
94
<div class="tl-time">{{ e.timestamp|date:"H:i" }}</div>
95
96
{# Message #}
97
<div class="tl-msg" style="padding-left: 10px;">
98
{% if e.event_type == "ci" %}
99
<a href="{% url 'fossil:checkin_detail' slug=project.slug checkin_uuid=e.uuid %}">{{ e.comment|default:"(no comment)"|truncatechars:90 }}</a>
100
{% elif e.event_type == "w" %}
101
<span class="tl-badge tl-badge-w">wiki</span> {{ e.comment|default:"(no comment)"|truncatechars:80 }}
102
{% elif e.event_type == "t" %}
103
<span class="tl-badge tl-badge-t">ticket</span> {{ e.comment|default:"(no comment)"|truncatechars:80 }}
104
{% elif e.event_type == "f" %}
105
<span class="tl-badge tl-badge-f">forum</span> {{ e.comment|default:"(no comment)"|truncatechars:80 }}
106
{% else %}
107
{{ e.comment|default:"(no comment)"|truncatechars:80 }}
108
{% endif %}
109
</div>
110
111
{# Meta: hash, user, branch #}
112
<div class="tl-meta">
113
{% if e.event_type == "ci" %}
114
{% include "fossil/_copy_hash.html" with hash=e.uuid slug=project.slug %}
115
{% endif %}
116
<a href="{% url 'fossil:user_activity' slug=project.slug username=e.user %}" class="tl-user">{{ e.user|display_user }}</a>
117
{% if e.branch %}<span class="tl-branch">{{ e.branch }}</span>{% endif %}
118
</div>
119
</div>
120
121
{% endwith %}
122
{% endfor %}
123
{% else %}
124
<p class="text-sm text-gray-500 py-8 text-center">No timeline entries.</p>
125
{% endif %}
126
</div>
127

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button