detached 8610746

find: Enter focuses selected match (centered) and closes overlay; clicking a match also closes; right-side context stops at nearest punctuation (, . -) to avoid awkward truncation

commit
86107469f15ca1235e19b8cc7ba677fa803f789f
author
bamen · · 2025-09-09 16:02 UTC
committer
bamen · · 2025-09-09 19:47 UTC
parent
6f48527
signature
unsigned
2 files · +20 -19
D pkg/webkit/ucm_js_access.go +0 -8
@@ -1,8 +0,0 @@
1-package webkit
2-
3-// getOmniboxScript exposes the injected omnibox/find component script as a function
4-// to avoid any build-tag edge cases when referenced from cgo files.
5-func getOmniboxScript() string {
6- return ucmOmniboxScript
7-}
8-
@@ -1,8 +0,0 @@
1-package webkit
2-
3-// getOmniboxScript exposes the injected omnibox/find component script as a function
4-// to avoid any build-tag edge cases when referenced from cgo files.
5-func getOmniboxScript() string {
6- return ucmOmniboxScript
7-}
8-
R pkg/webkit/ucm_js.go → pkg/webkit/ucm_js_cgo.go +20 -11 renamed, 91% similar
@@ -1,10 +1,12 @@
1+//go:build webkit_cgo
2+
13 package webkit
24
3-// ucmOmniboxScript is injected at document-start via WebKit UserContentManager.
4-// It provides a reusable overlay component with two modes:
5-// - "omnibox": navigation/search suggestions (Ctrl/Cmd+L)
6-// - "find": in-page find with yellow highlights and match list (Ctrl/Cmd+F)
7-const ucmOmniboxScript = `(() => {
5+// getOmniboxScript returns the injected omnibox/find component script.
6+// Kept in a cgo-tagged file to ensure availability when referenced
7+// from other cgo files in this package.
8+func getOmniboxScript() string {
9+ return `(() => {
810 try {
911 if (window.__dumber_omnibox_loaded) return; // idempotent
1012 window.__dumber_omnibox_loaded = true;
@@ -38,7 +40,7 @@ const ucmOmniboxScript = `(() => {
3840 root.id = 'dumber-omnibox-root';
3941 root.style.cssText = 'position:fixed;inset:0;z-index:2147483647;display:none;';
4042 const box = document.createElement('div');
41- box.style.cssText = 'max-width:720px;margin:8vh auto;padding:8px 10px;background:#1b1b1b;color:#eee;border:1px solid #444;border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.6);font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,\"Helvetica Neue\",Arial,sans-serif;';
43+ box.style.cssText = 'max-width:720px;margin:8vh auto;padding:8px 10px;background:#1b1b1b;color:#eee;border:1px solid #444;border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.6);font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,"Helvetica Neue",Arial,sans-serif;';
4244 const input = document.createElement('input');
4345 input.type = 'text';
4446 input.placeholder = 'Type URL or search…';
@@ -57,9 +59,9 @@ const ucmOmniboxScript = `(() => {
5759 if (v) H.post({type:'navigate', url:v});
5860 H.toggle(false);
5961 } else if (H.mode==='find' && e.key === 'Enter'){
60- e.preventDefault(); H.jump(1);
61- } else if (H.mode==='find' && e.key === 'Enter' && e.shiftKey){
62- e.preventDefault(); H.jump(-1);
62+ e.preventDefault();
63+ if (e.shiftKey) { H.jump(-1); }
64+ else { H.revealSelection(); H.close(); }
6365 } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp'){
6466 e.preventDefault();
6567 const n = (H.mode==='find'? H.matches.length : H.suggestions.length);
@@ -106,7 +108,7 @@ const ucmOmniboxScript = `(() => {
106108 item.style.cssText = 'padding:8px 10px;cursor:pointer;border-bottom:1px solid #2a2a2a;'+(i===H.selectedIndex?'background:#0a0a0a;':'');
107109 const ctx = document.createElement('div'); ctx.textContent = m.context || ''; ctx.style.cssText = 'color:#ddd;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
108110 item.appendChild(ctx);
109- item.addEventListener('click',()=>{ H.selectedIndex=i; H.revealSelection(); H.paintList(); });
111+ item.addEventListener('click',()=>{ H.selectedIndex=i; H.revealSelection(); H.paintList(); H.close(); });
110112 list.appendChild(item);
111113 });
112114 }
@@ -153,7 +155,13 @@ const ucmOmniboxScript = `(() => {
153155 parent.insertBefore(span, text);
154156 text.nodeValue = afterVal;
155157 H.highlightNodes.push({span, text: match});
156- const context = (before.nodeValue.slice(-30) + match.nodeValue + afterVal.slice(0, 30)).replace(/\s+/g,' ').trim();
158+ const leftCtx = before.nodeValue.slice(-30);
159+ let rightRaw = afterVal.slice(0, 30);
160+ const boundaryIdx = rightRaw.search(/[.,-]/);
161+ if (boundaryIdx !== -1) {
162+ rightRaw = rightRaw.slice(0, boundaryIdx+1);
163+ }
164+ const context = (leftCtx + match.nodeValue + rightRaw).replace(/\s+/g,' ').trim();
157165 H.matches.push({el: span, context});
158166 if (H.matches.length >= MAX_MATCHES) break;
159167 s = afterVal;
@@ -190,3 +198,4 @@ const ucmOmniboxScript = `(() => {
190198 window.__dumber_find_query = (q)=> { if (H.mode!=='find') H.setMode('find'); if (!H.visible) H.toggle(true); H.input.value = q||''; H.find(q||''); };
191199 } catch (e) { /* no-op */ }
192200 })();`
201+}
@@ -1,10 +1,12 @@
1+//go:build webkit_cgo
2+
1 package webkit3 package webkit
2 4
3-// ucmOmniboxScript is injected at document-start via WebKit UserContentManager.5+// getOmniboxScript returns the injected omnibox/find component script.
4-// It provides a reusable overlay component with two modes:6+// Kept in a cgo-tagged file to ensure availability when referenced
5-// - "omnibox": navigation/search suggestions (Ctrl/Cmd+L)7+// from other cgo files in this package.
6-// - "find": in-page find with yellow highlights and match list (Ctrl/Cmd+F)8+func getOmniboxScript() string {
7-const ucmOmniboxScript = `(() => {9+ return `(() => {
8 try {10 try {
9 if (window.__dumber_omnibox_loaded) return; // idempotent11 if (window.__dumber_omnibox_loaded) return; // idempotent
10 window.__dumber_omnibox_loaded = true;12 window.__dumber_omnibox_loaded = true;
@@ -38,7 +40,7 @@ const ucmOmniboxScript = `(() => {
38 root.id = 'dumber-omnibox-root';40 root.id = 'dumber-omnibox-root';
39 root.style.cssText = 'position:fixed;inset:0;z-index:2147483647;display:none;';41 root.style.cssText = 'position:fixed;inset:0;z-index:2147483647;display:none;';
40 const box = document.createElement('div');42 const box = document.createElement('div');
41- box.style.cssText = 'max-width:720px;margin:8vh auto;padding:8px 10px;background:#1b1b1b;color:#eee;border:1px solid #444;border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.6);font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,\"Helvetica Neue\",Arial,sans-serif;';43+ box.style.cssText = 'max-width:720px;margin:8vh auto;padding:8px 10px;background:#1b1b1b;color:#eee;border:1px solid #444;border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.6);font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,"Helvetica Neue",Arial,sans-serif;';
42 const input = document.createElement('input');44 const input = document.createElement('input');
43 input.type = 'text';45 input.type = 'text';
44 input.placeholder = 'Type URL or search…';46 input.placeholder = 'Type URL or search…';
@@ -57,9 +59,9 @@ const ucmOmniboxScript = `(() => {
57 if (v) H.post({type:'navigate', url:v});59 if (v) H.post({type:'navigate', url:v});
58 H.toggle(false);60 H.toggle(false);
59 } else if (H.mode==='find' && e.key === 'Enter'){61 } else if (H.mode==='find' && e.key === 'Enter'){
60- e.preventDefault(); H.jump(1);62+ e.preventDefault();
61- } else if (H.mode==='find' && e.key === 'Enter' && e.shiftKey){63+ if (e.shiftKey) { H.jump(-1); }
62- e.preventDefault(); H.jump(-1);64+ else { H.revealSelection(); H.close(); }
63 } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp'){65 } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp'){
64 e.preventDefault();66 e.preventDefault();
65 const n = (H.mode==='find'? H.matches.length : H.suggestions.length);67 const n = (H.mode==='find'? H.matches.length : H.suggestions.length);
@@ -106,7 +108,7 @@ const ucmOmniboxScript = `(() => {
106 item.style.cssText = 'padding:8px 10px;cursor:pointer;border-bottom:1px solid #2a2a2a;'+(i===H.selectedIndex?'background:#0a0a0a;':'');108 item.style.cssText = 'padding:8px 10px;cursor:pointer;border-bottom:1px solid #2a2a2a;'+(i===H.selectedIndex?'background:#0a0a0a;':'');
107 const ctx = document.createElement('div'); ctx.textContent = m.context || ''; ctx.style.cssText = 'color:#ddd;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';109 const ctx = document.createElement('div'); ctx.textContent = m.context || ''; ctx.style.cssText = 'color:#ddd;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
108 item.appendChild(ctx);110 item.appendChild(ctx);
109- item.addEventListener('click',()=>{ H.selectedIndex=i; H.revealSelection(); H.paintList(); });111+ item.addEventListener('click',()=>{ H.selectedIndex=i; H.revealSelection(); H.paintList(); H.close(); });
110 list.appendChild(item);112 list.appendChild(item);
111 });113 });
112 }114 }
@@ -153,7 +155,13 @@ const ucmOmniboxScript = `(() => {
153 parent.insertBefore(span, text);155 parent.insertBefore(span, text);
154 text.nodeValue = afterVal;156 text.nodeValue = afterVal;
155 H.highlightNodes.push({span, text: match});157 H.highlightNodes.push({span, text: match});
156- const context = (before.nodeValue.slice(-30) + match.nodeValue + afterVal.slice(0, 30)).replace(/\s+/g,' ').trim();158+ const leftCtx = before.nodeValue.slice(-30);
159+ let rightRaw = afterVal.slice(0, 30);
160+ const boundaryIdx = rightRaw.search(/[.,-]/);
161+ if (boundaryIdx !== -1) {
162+ rightRaw = rightRaw.slice(0, boundaryIdx+1);
163+ }
164+ const context = (leftCtx + match.nodeValue + rightRaw).replace(/\s+/g,' ').trim();
157 H.matches.push({el: span, context});165 H.matches.push({el: span, context});
158 if (H.matches.length >= MAX_MATCHES) break;166 if (H.matches.length >= MAX_MATCHES) break;
159 s = afterVal;167 s = afterVal;
@@ -190,3 +198,4 @@ const ucmOmniboxScript = `(() => {
190 window.__dumber_find_query = (q)=> { if (H.mode!=='find') H.setMode('find'); if (!H.visible) H.toggle(true); H.input.value = q||''; H.find(q||''); };198 window.__dumber_find_query = (q)=> { if (H.mode!=='find') H.setMode('find'); if (!H.visible) H.toggle(true); H.input.value = q||''; H.find(q||''); };
191 } catch (e) { /* no-op */ }199 } catch (e) { /* no-op */ }
192 })();`200 })();`
201+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close