(() => { 'use strict'; const exactLabels = new Map([ ['Code', 'Files'], ['Issues', 'Tasks'], ['Pull Requests', 'Proposals'], ['Pull requests', 'Proposals'], ['Actions', 'Evidence'], ['Commits', 'Checkpoints'], ['Branches', 'Attempts'], ['New Issue', 'New Task'], ['Create Issue', 'Create Task'], ['New Pull Request', 'New Proposal'], ['Create Pull Request', 'Create Proposal'], ['Merge Pull Request', 'Accept Proposal'], ['Compare & pull request', 'Review as Proposal'], ]); const phraseLabels = [ [/\bPull Requests\b/g, 'Proposals'], [/\bPull requests\b/g, 'Proposals'], [/\bpull requests\b/g, 'proposals'], [/\bNew Issue\b/g, 'New Task'], [/\bnew issue\b/g, 'new task'], [/\bIssues\b/g, 'Tasks'], [/\bissues\b/g, 'tasks'], [/\bActions\b/g, 'Evidence'], [/\bCommits\b/g, 'Checkpoints'], [/\bBranches\b/g, 'Attempts'], ]; const semanticRoots = [ '#navbar', '.secondary-nav', '[role="main"] .ui.header', '[role="main"] h1', '[role="main"] h2', '[role="main"] h3', '[role="main"] .ui.button', '[role="main"] .menu', '[role="main"] .breadcrumb', '[role="main"] label', ].join(','); function replaceExactText(root) { const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); const nodes = []; while (walker.nextNode()) nodes.push(walker.currentNode); for (const node of nodes) { const original = node.nodeValue; const trimmed = original.trim(); const replacement = exactLabels.get(trimmed); if (!replacement) continue; node.nodeValue = original.replace(trimmed, replacement); } } function replacePhrases(value) { let result = value; for (const [pattern, replacement] of phraseLabels) { result = result.replace(pattern, replacement); } return result; } function replaceAccessibleLabels(root) { const attributes = ['aria-label', 'data-text', 'data-tooltip-content', 'placeholder', 'title']; for (const element of root.querySelectorAll('[aria-label], [data-text], [data-tooltip-content], [placeholder], [title]')) { for (const attribute of attributes) { const value = element.getAttribute(attribute); if (!value) continue; const replacement = replacePhrases(value); if (replacement !== value) element.setAttribute(attribute, replacement); } } } function repoNavigation() { return document.querySelector('.secondary-nav .overflow-menu-items'); } function navigationLink(suffix) { const navigation = repoNavigation(); if (!navigation) return null; return [...navigation.querySelectorAll(':scope > a.item')].find((link) => { const path = new URL(link.href, window.location.origin).pathname.replace(/\/$/, ''); return path.endsWith(suffix); }) || null; } function workflowLink(source, label) { if (!source) return null; const link = document.createElement('a'); link.className = 'flex-text-block muted'; link.href = source.href; link.setAttribute('aria-label', label); const icon = source.querySelector('svg'); if (icon) link.append(icon.cloneNode(true)); link.append(document.createTextNode(` ${label}`)); const count = source.querySelector('.ui.label'); if (count) link.append(count.cloneNode(true)); return link; } function addWorkflowSummary() { const sidebar = document.querySelector('.repo-home-sidebar-top'); if (!sidebar || sidebar.querySelector('.hop-native-workflow')) return; const links = [ workflowLink(navigationLink('/issues'), 'Tasks'), workflowLink(navigationLink('/pulls'), 'Proposals'), workflowLink(navigationLink('/actions'), 'Evidence'), ].filter(Boolean); if (!links.length) return; const list = document.createElement('div'); list.className = 'flex-list hop-native-workflow'; const item = document.createElement('div'); item.className = 'flex-item'; const main = document.createElement('div'); main.className = 'flex-item-main'; const title = document.createElement('div'); title.className = 'flex-item-title'; title.textContent = 'Hop workflow'; const body = document.createElement('div'); body.className = 'flex-item-body tw-text-15'; const linkList = document.createElement('div'); linkList.className = 'tw-flex tw-flex-col tw-gap-2 tw-mt-2'; for (const link of links) linkList.append(link); body.append(linkList); main.append(title, body); item.append(main); list.append(item); sidebar.prepend(list); } function replaceBrandImages() { const upstreamBrandAsset = /\/img\/(?:avatar_default\.png|logo\.png|logo\.svg)(?:\?.*)?$/; for (const image of document.querySelectorAll('img[src]')) { if (!upstreamBrandAsset.test(image.src)) continue; image.src = image.src.replace(upstreamBrandAsset, '/img/hop.svg?v=4'); } } function applyHopSemantics() { const brandLogo = document.querySelector('#navbar-logo img'); if (brandLogo) brandLogo.src = brandLogo.src.replace(/\/img\/(?:logo|hop)\.svg(?:\?.*)?$/, '/img/hop.svg?v=4'); replaceBrandImages(); for (const root of document.querySelectorAll(semanticRoots)) replaceExactText(root); replaceAccessibleLabels(document); addWorkflowSummary(); const title = replacePhrases(document.title); if (title !== document.title) document.title = title; document.documentElement.dataset.hopNative = 'true'; } let queued = false; function queueApply() { if (queued) return; queued = true; window.requestAnimationFrame(() => { queued = false; applyHopSemantics(); }); } document.addEventListener('DOMContentLoaded', queueApply); document.addEventListener('htmx:afterSwap', queueApply); new MutationObserver(queueApply).observe(document.documentElement, {childList: true, subtree: true}); })();