reoganised project
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
{% extends "base" %}
|
||||
|
||||
{% block title %}{{ file.name }} · {{ package.name }}{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<style>
|
||||
/* ── Path breadcrumb ────────────────────────── */
|
||||
.file-path-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
background: var(--surface2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius) var(--radius) 0 0;
|
||||
padding: 8px 14px;
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
}
|
||||
.file-path-bar a { color: var(--accent); }
|
||||
.file-path-bar a:hover { text-decoration: underline; }
|
||||
.file-path-sep { color: var(--border); margin: 0 3px; }
|
||||
.file-path-current { color: var(--text); }
|
||||
|
||||
/* ── File metadata row ──────────────────────── */
|
||||
.file-meta-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 7px 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
background: var(--surface);
|
||||
font-size: 12px;
|
||||
color: var(--text-dim);
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.file-meta-bar .mono { font-family: var(--mono); }
|
||||
|
||||
/* ── Directory listing (same as package home but nested) */
|
||||
.file-tree-body {
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
.file-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 7px 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
transition: background .1s;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
}
|
||||
.file-row:last-child { border-bottom: none; }
|
||||
.file-row:hover { background: var(--surface2); }
|
||||
.file-icon { color: var(--muted); flex-shrink: 0; }
|
||||
.file-name { flex: 1; }
|
||||
.file-name a { color: var(--text); }
|
||||
.file-name a:hover { color: var(--accent); text-decoration: none; }
|
||||
.file-commit { font-size: 11px; color: var(--text-dim); flex: 2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.file-age { font-size: 11px; color: var(--muted); flex-shrink: 0; }
|
||||
|
||||
/* ── File content / code viewer ─────────────── */
|
||||
.code-viewer {
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
.code-viewer pre {
|
||||
margin: 0;
|
||||
padding: 16px 20px;
|
||||
overflow-x: auto;
|
||||
background: #0a0a0a;
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #c8ccd4;
|
||||
}
|
||||
/* Line numbers */
|
||||
.code-table { width: 100%; border-collapse: collapse; }
|
||||
.code-table tr:hover .code-line { background: rgba(255,255,255,.03); }
|
||||
.code-table tr:hover .line-num { background: rgba(255,255,255,.03); }
|
||||
.line-num {
|
||||
user-select: none;
|
||||
text-align: right;
|
||||
padding: 0 16px 0 16px;
|
||||
color: var(--muted);
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
width: 1%;
|
||||
white-space: nowrap;
|
||||
vertical-align: top;
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.code-line {
|
||||
padding: 0 16px;
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
white-space: pre;
|
||||
color: #c8ccd4;
|
||||
}
|
||||
|
||||
/* ── Binary/image preview ───────────────────── */
|
||||
.file-preview-img {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32px;
|
||||
background: #0a0a0a;
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
}
|
||||
.file-preview-img img { max-width: 100%; max-height: 600px; border-radius: 4px; }
|
||||
|
||||
.file-binary-msg {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: var(--text-dim);
|
||||
font-size: 13px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
}
|
||||
|
||||
.toolbar-search {
|
||||
position: relative;
|
||||
max-width: 260px;
|
||||
}
|
||||
.toolbar-search input {
|
||||
width: 100%;
|
||||
padding: 5px 10px 5px 28px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 12px;
|
||||
font-family: var(--sans);
|
||||
outline: none;
|
||||
}
|
||||
.toolbar-search input:focus { border-color: var(--accent); }
|
||||
.toolbar-search input::placeholder { color: var(--muted); }
|
||||
.toolbar-search-icon {
|
||||
position: absolute;
|
||||
left: 8px; top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--muted);
|
||||
pointer-events: none;
|
||||
}
|
||||
.dir-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
background: var(--surface2);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
|
||||
<div class="breadcrumb">
|
||||
<a href="/packages/">packages</a>
|
||||
<span class="breadcrumb-sep">/</span>
|
||||
<a href="/packages/{{ package.name }}">{{ package.name }}</a>
|
||||
<span class="breadcrumb-sep">/</span>
|
||||
<span>files</span>
|
||||
</div>
|
||||
|
||||
<!-- Path bar -->
|
||||
<div class="file-path-bar">
|
||||
<a href="/packages/{{ package.name }}/~repo">{{ package.name }}</a>
|
||||
{% for segment in path_segments %}
|
||||
<span class="file-path-sep">/</span>
|
||||
{% if loop.last %}
|
||||
<span class="file-path-current">{{ segment.name }}</span>
|
||||
{% else %}
|
||||
<a href="/packages/{{ package.name }}/~repo/{{ segment.path }}">{{ segment.name }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if file.is_dir %}
|
||||
<!-- Directory: optional search toolbar + listing -->
|
||||
<div class="dir-toolbar">
|
||||
<div class="toolbar-search">
|
||||
<svg class="toolbar-search-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<form method="get" action="/packages/{{ package.name }}/~repo">
|
||||
<input type="text" name="q" placeholder="Filter files…" value="{{ query | default(value='') }}">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-tree-body">
|
||||
{% if file.parent_path is defined %}
|
||||
<a class="file-row" href="/packages/{{ package.name }}/~repo/{{ file.parent_path }}">
|
||||
<span class="file-icon">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>
|
||||
</span>
|
||||
<span class="file-name" style="color:var(--text-dim)">..</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% for entry in file.children %}
|
||||
<div class="file-row">
|
||||
<span class="file-icon">
|
||||
{% if entry.is_dir %}
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="color:#4f8ef7"><path d="M3 7a2 2 0 012-2h4l2 2h8a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7z"/></svg>
|
||||
{% else %}
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="file-name">
|
||||
<a href="/packages/{{ package.name }}/~repo/{{ entry.path }}">{{ entry.name }}</a>
|
||||
</span>
|
||||
<span class="file-commit">{{ entry.last_commit_message | default(value="") }}</span>
|
||||
<span class="file-age">{{ entry.last_modified | default(value="") }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% elif file.is_image %}
|
||||
<!-- Image preview -->
|
||||
<div class="file-meta-bar">
|
||||
<span>{{ file.size | default(value="unknown size") }}</span>
|
||||
<a href="{{ file.raw_url }}" class="mono" style="font-size:11px">raw ↗</a>
|
||||
</div>
|
||||
<div class="file-preview-img">
|
||||
<img src="{{ file.raw_url }}" alt="{{ file.name }}">
|
||||
</div>
|
||||
|
||||
{% elif file.is_binary %}
|
||||
<!-- Binary file -->
|
||||
<div class="file-meta-bar">
|
||||
<span>{{ file.size | default(value="unknown size") }} · binary file</span>
|
||||
{% if file.raw_url %}<a href="{{ file.raw_url }}" class="mono" style="font-size:11px">download ↗</a>{% endif %}
|
||||
</div>
|
||||
<div class="file-binary-msg">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="color:var(--border);margin-bottom:8px"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
<p>Binary file — cannot be displayed.</p>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<!-- Text / code file -->
|
||||
<div class="file-meta-bar">
|
||||
<span class="mono">{{ file.name }}</span>
|
||||
<span>
|
||||
<span class="mono">{{ file.line_count | default(value="?") }} lines</span>
|
||||
<span style="margin: 0 8px">·</span>
|
||||
<span>{{ file.size | default(value="") }}</span>
|
||||
{% if file.raw_url %}<span style="margin: 0 8px">·</span><a href="{{ file.raw_url }}" style="font-size:11px" class="mono">raw ↗</a>{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="code-viewer">
|
||||
<pre><table class="code-table">
|
||||
{% if file.lines %}
|
||||
{% for line in file.lines %}
|
||||
<tr>
|
||||
<td class="line-num">{{ loop.index }}</td>
|
||||
<td class="code-line">{{ line | escape }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td class="code-line" style="padding:16px 20px">{{ file.content | default(value="") | escape }}</td></tr>
|
||||
{% endif %}
|
||||
</table></pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user