GameOver1337 / GitClone Public
К репозиторию
7.1 KB
templates/layout.html
7.1 KB
1{{define "layout"}}<!DOCTYPE html>
2<html lang="ru">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<meta name="color-scheme" content="light dark">
7<title>{{template "title" .}} · Clancore Repo</title>
8<link rel="stylesheet" href="/static/css/main.css">
9</head>
10<body>
11<a class="skip-link" href="#main">Перейти к содержимому</a>
12
13<header class="header">
14 <div class="container header-inner">
15 <a href="/" class="header-logo" aria-label="Clancore Repo — на главную">
16 <svg height="32" width="32" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
17 <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
18 </svg>
19 </a>
20
21 <nav class="header-nav" aria-label="Основная навигация">
22 <a href="/">Репозитории</a>
23 {{if .UserID}}
24 <a href="/dashboard">Панель</a>
25 <a href="/builds">Сборки</a>
26 {{end}}
27 </nav>
28
29 <div class="header-search">
30 <form action="/" method="get" role="search">
31 <label class="sr-only" for="header-search-input">Поиск репозиториев</label>
32 <svg class="octicon header-search-icon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"/></svg>
33 <input id="header-search-input" type="search" name="q" placeholder="Поиск репозиториев…" value="{{.Query}}" autocomplete="off">
34 <kbd class="search-kbd">/</kbd>
35 </form>
36 </div>
37
38 <div class="header-user">
39 {{if .UserID}}
40 <a href="/profile" class="avatar avatar-sm header-avatar" title="{{.User.Username}}">{{slice .User.Username 0 1}}</a>
41 <form method="POST" action="/logout" class="inline-form">
42 <input type="hidden" name="csrf_token" value="{{.CSRF}}">
43 <button type="submit" class="header-link-button">Выйти</button>
44 </form>
45 {{else}}
46 <a href="/login" class="header-link">Войти</a>
47 <a href="/register" class="btn btn-sm btn-inverted">Регистрация</a>
48 {{end}}
49 </div>
50 </div>
51</header>
52
53<main class="container" id="main">
54{{template "content" .}}
55</main>
56
57<footer class="footer">
58 <div class="container footer-inner">
59 <div class="footer-left">
60 <a href="/" class="footer-mark" aria-label="Clancore Repo">
61 <svg height="24" width="24" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
62 <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
63 </svg>
64 </a>
65 <span>© Clancore Repo</span>
66 </div>
67 <nav class="footer-links" aria-label="Навигация в подвале">
68 <a href="/">Репозитории</a>
69 <a href="/builds">Сборки</a>
70 {{if not .UserID}}<a href="/register">Регистрация</a>{{end}}
71 </nav>
72 </div>
73</footer>
74
75<script>
76document.addEventListener("click", function (event) {
77 var button = event.target.closest("[data-copy]");
78 if (!button || !navigator.clipboard) {
79 return;
80 }
81 event.preventDefault();
82 var value = button.getAttribute("data-copy");
83 navigator.clipboard.writeText(value).then(function () {
84 var originalHTML = button.innerHTML;
85 var hasText = button.textContent.trim() !== "";
86 if (hasText) {
87 button.textContent = "Скопировано";
88 setTimeout(function () { button.innerHTML = originalHTML; }, 1500);
89 } else {
90 button.classList.add("copied");
91 button.setAttribute("title", "Скопировано");
92 setTimeout(function () {
93 button.classList.remove("copied");
94 button.setAttribute("title", "Скопировать URL");
95 }, 1500);
96 }
97 });
98});
99
100// Выпадающие панели (например, кнопка Code)
101document.addEventListener("click", function (event) {
102 var trigger = event.target.closest("[data-dropdown]");
103 if (trigger) {
104 var panel = document.getElementById(trigger.getAttribute("data-dropdown"));
105 if (panel) {
106 var willOpen = !panel.classList.contains("open");
107 panel.classList.toggle("open", willOpen);
108 trigger.setAttribute("aria-expanded", willOpen ? "true" : "false");
109 event.preventDefault();
110 return;
111 }
112 }
113 if (event.target.closest(".popover")) {
114 return;
115 }
116 document.querySelectorAll(".popover.open").forEach(function (panel) {
117 panel.classList.remove("open");
118 });
119 document.querySelectorAll("[data-dropdown]").forEach(function (btn) {
120 btn.setAttribute("aria-expanded", "false");
121 });
122});
123
124// Горячие клавиши как в GitHub: "/" — поиск, "t" — фильтр файлов, Esc — закрыть панель
125document.addEventListener("keydown", function (event) {
126 if (event.key === "Escape") {
127 document.querySelectorAll(".popover.open").forEach(function (panel) {
128 panel.classList.remove("open");
129 });
130 document.querySelectorAll("[data-dropdown]").forEach(function (btn) {
131 btn.setAttribute("aria-expanded", "false");
132 });
133 return;
134 }
135 if (event.key !== "/" && event.key !== "t") {
136 return;
137 }
138 var tag = (document.activeElement && document.activeElement.tagName) || "";
139 if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || event.metaKey || event.ctrlKey) {
140 return;
141 }
142 var target = document.getElementById(event.key === "/" ? "header-search-input" : "file-filter");
143 if (target) {
144 event.preventDefault();
145 target.focus();
146 }
147});
148
149// "Go to file" — фильтр строк дерева файлов
150(function () {
151 var filter = document.getElementById("file-filter");
152 if (!filter) {
153 return;
154 }
155 filter.addEventListener("input", function () {
156 var query = filter.value.trim().toLowerCase();
157 document.querySelectorAll(".file-table tbody tr").forEach(function (row) {
158 var name = row.querySelector(".file-name");
159 var match = query === "" || (name && name.textContent.toLowerCase().indexOf(query) !== -1);
160 row.hidden = !match;
161 });
162 });
163})();
164</script>
165</body>
166</html>
167{{end}}