|
1
|
{% extends "base.html" %} |
|
2
|
{% block title %}{{ filepath }} — {{ project.name }} — Fossilrepo{% endblock %} |
|
3
|
|
|
4
|
{% block extra_head %} |
|
5
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"> |
|
6
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> |
|
7
|
<style> |
|
8
|
.code-table { border-collapse: collapse; width: 100%; } |
|
9
|
.code-table td.line-num { |
|
10
|
width: 1%; white-space: nowrap; padding: 0 16px !important; |
|
11
|
text-align: right; user-select: none; cursor: pointer; |
|
12
|
color: #4b5563; font-size: 0.75rem; line-height: 1.5rem; |
|
13
|
border-right: 1px solid #374151; vertical-align: top; |
|
14
|
} |
|
15
|
.code-table td.line-num:hover { color: #DC394C; } |
|
16
|
.code-table td.line-num a { color: inherit; text-decoration: none; display: block; } |
|
17
|
.code-table td.line-code { |
|
18
|
white-space: pre; padding: 0 16px !important; |
|
19
|
font-size: 0.8125rem; line-height: 1.5rem; vertical-align: top; |
|
20
|
} |
|
21
|
.line-row:hover { background: rgba(220, 57, 76, 0.05); } |
|
22
|
.line-row:target { background: rgba(220, 57, 76, 0.12); } |
|
23
|
.line-row:target .line-num { color: #DC394C; font-weight: 600; } |
|
24
|
.line-row { scroll-margin-top: 40vh; } |
|
25
|
.line-popover { |
|
26
|
position: absolute; left: 100%; top: 50%; transform: translateY(-50%); |
|
27
|
margin-left: 4px; z-index: 20; white-space: nowrap; |
|
28
|
background: #1f2937; border: 1px solid #374151; border-radius: 6px; |
|
29
|
box-shadow: 0 4px 12px rgba(0,0,0,0.4); padding: 2px; |
|
30
|
display: flex; gap: 2px; |
|
31
|
} |
|
32
|
.line-popover button { |
|
33
|
display: flex; align-items: center; gap: 4px; padding: 4px 8px; |
|
34
|
font-size: 0.7rem; color: #d1d5db; background: transparent; |
|
35
|
border: none; border-radius: 4px; cursor: pointer; |
|
36
|
} |
|
37
|
.line-popover button:hover { background: #374151; color: #fff; } |
|
38
|
.line-popover button.copied { color: #22c55e; } |
|
39
|
</style> |
|
40
|
{% endblock %} |
|
41
|
|
|
42
|
{% block content %} |
|
43
|
<h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1> |
|
44
|
{% include "fossil/_project_nav.html" %} |
|
45
|
|
|
46
|
<div class="overflow-hidden rounded-lg bg-gray-800 shadow-sm border border-gray-700"> |
|
47
|
<!-- File path breadcrumb + stats --> |
|
48
|
<div class="px-4 py-3 border-b border-gray-700 flex flex-wrap items-center justify-between gap-x-2 gap-y-2"> |
|
49
|
<div class="flex flex-wrap items-center gap-1 text-sm font-mono min-w-0"> |
|
50
|
<a href="{% url 'fossil:code' slug=project.slug %}" class="text-brand-light hover:text-brand">{{ project.slug }}</a> |
|
51
|
{% for crumb in file_breadcrumbs %} |
|
52
|
<span class="text-gray-600">/</span> |
|
53
|
{% if forloop.last %} |
|
54
|
<span class="text-gray-200">{{ crumb.name }}</span> |
|
55
|
{% else %} |
|
56
|
<a href="{% url 'fossil:code_dir' slug=project.slug dirpath=crumb.path %}" class="text-brand-light hover:text-brand">{{ crumb.name }}</a> |
|
57
|
{% endif %} |
|
58
|
{% endfor %} |
|
59
|
</div> |
|
60
|
<div class="flex flex-wrap items-center gap-2"> |
|
61
|
{% if can_render %} |
|
62
|
<div class="flex items-center gap-1 text-xs"> |
|
63
|
<a href="?mode=source" class="px-2 py-1 rounded {% if view_mode == 'source' %}bg-brand text-white{% else %}text-gray-500 hover:text-white{% endif %}">Source</a> |
|
64
|
<a href="?mode=rendered" class="px-2 py-1 rounded {% if view_mode == 'rendered' %}bg-brand text-white{% else %}text-gray-500 hover:text-white{% endif %}">Rendered</a> |
|
65
|
</div> |
|
66
|
{% endif %} |
|
67
|
<a href="{% url 'fossil:code_blame' slug=project.slug filepath=filepath %}" class="px-2 py-1 text-xs text-gray-500 hover:text-brand-light rounded hover:bg-gray-700">Blame</a> |
|
68
|
<a href="{% url 'fossil:file_history' slug=project.slug filepath=filepath %}" class="px-2 py-1 text-xs text-gray-500 hover:text-brand-light rounded hover:bg-gray-700">History</a> |
|
69
|
<a href="{% url 'fossil:code_raw' slug=project.slug filepath=filepath %}" class="px-2 py-1 text-xs text-gray-500 hover:text-brand-light rounded hover:bg-gray-700">Raw</a> |
|
70
|
{% if not is_binary %} |
|
71
|
<span class="text-xs text-gray-500">{{ line_count }} line{{ line_count|pluralize }}</span> |
|
72
|
{% endif %} |
|
73
|
</div> |
|
74
|
</div> |
|
75
|
<div class="overflow-x-auto"> |
|
76
|
{% if is_binary %} |
|
77
|
<p class="p-4 text-sm text-gray-500">{{ content }}</p> |
|
78
|
{% elif view_mode == "rendered" and rendered_html %} |
|
79
|
<div class="px-6 py-6"> |
|
80
|
<div class="prose prose-invert prose-gray max-w-none"> |
|
81
|
{{ rendered_html }} |
|
82
|
</div> |
|
83
|
</div> |
|
84
|
{% else %} |
|
85
|
<table class="code-table"> |
|
86
|
<tbody> |
|
87
|
{% for line in lines %} |
|
88
|
<tr class="line-row" id="L{{ line.num }}"> |
|
89
|
<td class="line-num" style="position:relative" |
|
90
|
x-data="{ pop: false, copied: false }" |
|
91
|
@click.stop="pop = !pop; window.location.hash = 'L{{ line.num }}'" |
|
92
|
@click.outside="pop = false"> |
|
93
|
{{ line.num }} |
|
94
|
<div class="line-popover" x-show="pop" x-transition @click.stop> |
|
95
|
<button @click="navigator.clipboard.writeText(window.location.origin + window.location.pathname + '#L{{ line.num }}'); copied = true; setTimeout(() => { copied = false; pop = false }, 1000)" :class="copied && 'copied'"> |
|
96
|
<span x-show="!copied">Copy link</span><span x-show="copied">Copied!</span> |
|
97
|
</button> |
|
98
|
</div> |
|
99
|
</td> |
|
100
|
<td class="line-code"><code class="language-{{ language }}">{{ line.text }}</code></td> |
|
101
|
</tr> |
|
102
|
{% endfor %} |
|
103
|
</tbody> |
|
104
|
</table> |
|
105
|
{% endif %} |
|
106
|
</div> |
|
107
|
</div> |
|
108
|
|
|
109
|
{% if not is_binary %} |
|
110
|
<script> |
|
111
|
document.querySelectorAll('.line-code code').forEach(el => hljs.highlightElement(el)); |
|
112
|
</script> |
|
113
|
{% endif %} |
|
114
|
{% endblock %} |
|
115
|
|