gabriel / musehub public
commits.ts typescript
146 lines 5.3 KB
Raw
sha256:73f24973678ceefd56628ee17c6d0535d86e33533828af6c63348f50941515ad Merge branch 'fix/smoke-test-tag-label' into dev Human 15 days ago
1 /**
2 * commits.ts — Commits list page module.
3 *
4 * Responsibilities:
5 * 1. Branch selector → navigates with ?branch= param.
6 * 2. Filter form → let HTMX handle partial updates; no manual JS submit needed.
7 * 3. Compare mode — toggle, checkbox selection, compare strip link.
8 * Uses event delegation so it survives HTMX fragment swaps.
9 */
10
11 interface CommitsCfg {
12 repoId: string;
13 base: string;
14 page: number;
15 perPage: number;
16 totalPages: number;
17 branch: string;
18 }
19
20 let _commitsCfg: CommitsCfg | undefined;
21
22 // ── URL helpers ───────────────────────────────────────────────────────────
23
24 function buildUrl(overrides: Record<string, string | number | null>): string {
25 const url = new URL(window.location.href);
26 for (const [k, v] of Object.entries(overrides)) {
27 if (v === null || v === undefined || v === "") {
28 url.searchParams.delete(k);
29 } else {
30 url.searchParams.set(k, String(v));
31 }
32 }
33 return url.toString();
34 }
35
36 // ── Branch selector ───────────────────────────────────────────────────────
37
38 function bindBranchSelector(): void {
39 const sel = document.getElementById("branch-sel") as HTMLSelectElement | null;
40 if (!sel) return;
41 sel.addEventListener("change", () => {
42 window.location.href = buildUrl({ branch: sel.value || null, page: 1 });
43 });
44 }
45
46 // ── Compare mode ──────────────────────────────────────────────────────────
47
48 let compareMode = false;
49 const selected = new Set<string>();
50
51 function updateCompareStrip(): void {
52 const strip = document.getElementById("compare-strip");
53 const countEl = document.getElementById("compare-count");
54 const link = document.getElementById("compare-link") as HTMLAnchorElement | null;
55 const cfg = _commitsCfg;
56 if (!strip) return;
57
58 const n = selected.size;
59 if (countEl) countEl.textContent = `${n} selected`;
60
61 if (n === 2 && link && cfg) {
62 const [a, b] = [...selected];
63 link.href = `${cfg.base}/compare/${a}...${b}`;
64 link.style.display = "";
65 } else if (link) {
66 link.style.display = "none";
67 }
68 strip.classList.toggle("visible", compareMode);
69 }
70
71 function toggleCompareMode(): void {
72 compareMode = !compareMode;
73 document.body.classList.toggle("compare-mode", compareMode);
74 selected.clear();
75 document.querySelectorAll<HTMLInputElement>(".compare-check").forEach(cb => { cb.checked = false; });
76 document.querySelectorAll<HTMLElement>(".commit-list-row").forEach(r => r.classList.remove("compare-selected"));
77 updateCompareStrip();
78 const btn = document.getElementById("compare-toggle-btn");
79 if (btn) btn.textContent = compareMode ? "✕ Exit Compare" : "⊞ Compare";
80 }
81
82 function onCompareCheck(cb: HTMLInputElement, commitId: string): void {
83 const row = cb.closest<HTMLElement>(".commit-list-row");
84 if (cb.checked) {
85 if (selected.size >= 2) { cb.checked = false; return; }
86 selected.add(commitId);
87 row?.classList.add("compare-selected");
88 } else {
89 selected.delete(commitId);
90 row?.classList.remove("compare-selected");
91 }
92 updateCompareStrip();
93 }
94
95 function bindCompareMode(): void {
96 // Compare toggle button
97 document.getElementById("compare-toggle-btn")
98 ?.addEventListener("click", toggleCompareMode);
99
100 // Cancel button inside strip
101 document.getElementById("compare-cancel-btn")
102 ?.addEventListener("click", toggleCompareMode);
103
104 // Checkbox event delegation — survives HTMX fragment swaps
105 document.addEventListener("change", (e: Event) => {
106 const cb = (e.target as Element).closest<HTMLInputElement>(".compare-check");
107 if (!cb) return;
108 const commitId = cb.dataset.commitId ?? cb.closest<HTMLElement>(".commit-list-row")?.dataset.commitId;
109 if (commitId) onCompareCheck(cb, commitId);
110 });
111 }
112
113 // ── Re-apply compare state after HTMX swaps ──────────────────────────────
114
115 function bindHtmxSwap(): void {
116 document.body.addEventListener("htmx:afterSwap", () => {
117 // Re-check any previously selected commits after fragment swap
118 selected.forEach(id => {
119 const row = document.querySelector<HTMLElement>(`[data-commit-id="${id}"]`);
120 const cb = row?.querySelector<HTMLInputElement>(".compare-check");
121 if (row && cb) {
122 row.classList.add("compare-selected");
123 cb.checked = true;
124 }
125 });
126 if (compareMode) {
127 document.body.classList.add("compare-mode");
128 }
129 });
130 }
131
132 // ── Entry point ───────────────────────────────────────────────────────────
133
134 export function initCommits(data: Record<string, unknown> = {}): void {
135 _commitsCfg = {
136 repoId: String(data['repoId'] ?? ''),
137 base: String(data['base'] ?? ''),
138 page: Number(data['page'] ?? 1),
139 perPage: Number(data['perPage'] ?? 30),
140 totalPages: Number(data['totalPages'] ?? 1),
141 branch: String(data['branch'] ?? ''),
142 };
143 bindBranchSelector();
144 bindCompareMode();
145 bindHtmxSwap();
146 }
File History 12 commits
sha256:cfefc25a166c3c3eed8ea3529aee19ea350bc05f2954d007420e924133b7d8ce chore: pivot to nightly channel — bump version to 0.2.0.dev… Sonnet 5 patch 12 days ago
sha256:d035733f21ccff27735fddebfbbe0ed24565a32a22db8de5885402262671ecd2 chore: bump version to 0.2.0rc15 for musehub#113 fix release Sonnet 4.6 patch 15 days ago
sha256:0032d6cfa33bc3c8367436ad768e7dd0e339b4332153160247da8266cb5fa352 Merge branch 'task/version-tags-phase3-server' into dev Human 17 days ago
sha256:4669620efda9ff41c55bdefd1f7bfe1c239d468428744c84ead9957e5a003a53 merge: rescue snapshot-recovery hardening (c00aa21d) into d… Opus 4.8 minor 30 days ago
sha256:a59da49c4611b970fc4b6ae48678ce4943261c213a07ddbd73ce9201df869b4a fix: remove false-positive proposal_comments index drop fro… Sonnet 4.6 patch 34 days ago
sha256:0a240d6dbff234f07d98a28a4a9a68db702f3f9ff9260196f24219bdb1c0b6f3 feat: render markdown mists as HTML with heading anchor links Sonnet 4.6 patch 34 days ago
sha256:24a7d47486ebc4ebd1832830580e177ec6f877b48dced8c000e198cdec4ce9d6 Merge 'task/bump-version-rc12' into 'dev' — proposal: Bump … Human 35 days ago
sha256:b9ff931d147e0114a1f17060f415b89ed551c170a91ff226c70437aa5c85f9ee Merge 'task/bump-version-rc12' into 'dev' — proposal: Bump … Human 35 days ago
sha256:d1122d21e73471879b460037b22c0b50fded7c423444a176f248428f75dac39c Merge 'task/fix-issue-pagination-cursor' into 'dev' — propo… Human 35 days ago
sha256:01e18975e73d2b3cd5b6db7929c895bef9aa6e0d4391dc5b2adfc548b41318dd Merge 'feat/adding-debug-logs-to-staging' into 'dev' — prop… Human 36 days ago
sha256:6b1949fc2797ca4c1936a637a4cbfec828ef56cf52398a2e74ca3c4f494e728f fix: use wire_bytes not mpack_bytes_raw in compute_object_b… Sonnet 4.6 patch 48 days ago
sha256:b99f2455dc346966d040133f5203297e6e3ef5803a93728a2c30568d0a0f7583 rename: delta_add → delta_upsert across wire format, models… Sonnet 4.6 patch 50 days ago