reoganised project

This commit is contained in:
2026-02-23 20:32:45 +00:00
parent 42bc666c11
commit 71b36dc6b5
96 changed files with 662 additions and 46 deletions
@@ -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 %}