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});