arnaucube
arnaucube/gitviewPublic Self-hosted repository
gitview/static/app.js
static/app.jsRaw
 1'use strict';
 2// Search and sorting operate on the server-rendered repository list.
 3const search = document.querySelector('#repo-search');
 4const globalSearch = document.querySelector('#global-search');
 5const list = document.querySelector('#repo-list');
 6const cards = [...document.querySelectorAll('[data-repo]')];
 7function filterRepositories(value) {
 8  const query = value.trim().toLowerCase();
 9  let count = 0;
10  for (const card of cards) {
11    card.hidden = !card.textContent.toLowerCase().includes(query);
12    if (!card.hidden) count++;
13  }
14  const counter = document.querySelector('#result-count');
15  if (counter) counter.textContent = count;
16  const empty = document.querySelector('#no-results');
17  if (empty) empty.hidden = count !== 0 || !query;
18  if (list) list.hidden = count === 0 && !!query;
19}
20search?.addEventListener('input', () => { if (globalSearch) globalSearch.value = search.value; filterRepositories(search.value); });
21globalSearch?.addEventListener('input', () => {
22  if (search) { search.value = globalSearch.value; filterRepositories(globalSearch.value); }
23});
24// On repository pages, Enter carries the search back to the workspace.
25globalSearch?.addEventListener('keydown', event => {
26  if (event.key === 'Enter' && !search) location.href = '/?q=' + encodeURIComponent(globalSearch.value);
27});
28const initialQuery = new URLSearchParams(location.search).get('q');
29if (initialQuery && search) { search.value = initialQuery; globalSearch.value = initialQuery; filterRepositories(initialQuery); }
30document.querySelector('#repo-sort')?.addEventListener('change', event => {
31  const byName = event.target.value === 'name';
32  cards.sort((a, b) => byName ? a.dataset.repo.localeCompare(b.dataset.repo) : Number(a.dataset.index) - Number(b.dataset.index));
33  for (const card of cards) list.append(card);
34  document.querySelector('#sort-description').textContent = byName ? 'name' : event.target.selectedOptions[0].dataset.description;
35});
36document.querySelector('#view-toggle')?.addEventListener('click', event => {
37  const compact = list.classList.toggle('compact');
38  event.currentTarget.setAttribute('aria-pressed', String(compact));
39});
40document.querySelector('#theme-toggle')?.addEventListener('click', () => {
41  const theme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';
42  document.documentElement.dataset.theme = theme;
43  try { localStorage.setItem('gitview-theme', theme); } catch (_) { /* Theme still works without storage. */ }
44});
45document.addEventListener('keydown', event => {
46  if (event.key === '/' && !/INPUT|TEXTAREA|SELECT/.test(document.activeElement.tagName)) {
47    event.preventDefault(); (search || globalSearch)?.focus();
48  }
49  if (event.key === 'Escape') document.querySelectorAll('details[open]').forEach(item => item.open = false);
50});
51document.addEventListener('click', event => {
52  document.querySelectorAll('details[open]').forEach(item => { if (!item.contains(event.target)) item.open = false; });
53});
54for (const input of document.querySelectorAll('[data-clone]')) input.value = location.origin + input.dataset.clone;
55let toastTimer;
56function toast(message) {
57  const element = document.querySelector('#toast');
58  element.textContent = message;
59  element.classList.add('visible');
60  clearTimeout(toastTimer);
61  toastTimer = setTimeout(() => element.classList.remove('visible'), 2500);
62}
63for (const button of document.querySelectorAll('[data-copy]')) button.addEventListener('click', async () => {
64  const input = document.getElementById(button.dataset.copy);
65  try { await navigator.clipboard.writeText(input.value); toast('Clone URL copied'); }
66  catch (_) { input.focus(); input.select(); toast('Select and copy the clone URL'); }
67});
68document.querySelector('[data-scroll-readme]')?.addEventListener('click', event => {
69  event.preventDefault();
70  const readme = document.querySelector('.readme-panel');
71  if (readme) readme.scrollIntoView({ behavior: 'smooth' }); else toast('No README in this repository');
72});