rin
Public Open-source lightweight git hosting.

refactor(ui): simplify custom context menu

7e469ac Browse files
R rin · 2026-10-18 23:51 UTC
5 files changed +4 -182
assets/input.css
+1 -26
... ... @@ -1176,9 +1176,6 @@ kbd, .kbd {
1176 1176 transform: translateY(0) scale(1);
1177 1177 animation: contextMenuIn 130ms cubic-bezier(0.16, 1, 0.3, 1);
1178 1178 }
1179 -body[data-repo-path] .context-menu {
1180 - max-height: min(80vh, 52rem);
1181 -}
1182 1179 .context-menu-section[hidden],
1183 1180 .context-menu-item[hidden] {
1184 1181 display: none;
... ... @@ -1199,18 +1196,6 @@ body[data-repo-path] .context-menu {
1199 1196 text-transform: uppercase;
1200 1197 color: hsl(var(--muted-foreground));
1201 1198 }
1202 -.context-menu-meta {
1203 - margin-left: auto;
1204 - max-width: 9.5rem;
1205 - overflow: hidden;
1206 - text-overflow: ellipsis;
1207 - white-space: nowrap;
1208 - font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1209 - font-size: 0.6rem;
1210 - letter-spacing: 0;
1211 - text-transform: none;
1212 - opacity: 0.85;
1213 -}
1214 1199 .context-menu-item {
1215 1200 display: flex;
1216 1201 width: 100%;
... ... @@ -1236,7 +1221,7 @@ body[data-repo-path] .context-menu {
1236 1221 color: hsl(var(--muted-foreground));
1237 1222 transition: color 90ms ease;
1238 1223 }
1239 -.context-menu-item > span:not(.context-menu-check):not(.context-menu-kbd) {
1224 +.context-menu-item > span:not(.context-menu-kbd) {
1240 1225 min-width: 0;
1241 1226 overflow: hidden;
1242 1227 text-overflow: ellipsis;
... ... @@ -1266,16 +1251,6 @@ body[data-repo-path] .context-menu {
1266 1251 border: 1px solid hsl(var(--border));
1267 1252 border-radius: 0.22rem;
1268 1253 }
1269 -.context-menu-check {
1270 - display: inline-flex;
1271 - margin-left: auto;
1272 - flex: none;
1273 - opacity: 0;
1274 - color: hsl(var(--foreground));
1275 -}
1276 -.context-menu-item.is-current .context-menu-check {
1277 - opacity: 1;
1278 -}
1279 1254
1280 1255 /* Shortcuts dialog */
1281 1256 .shortcuts-list {
internal/i18n/i18n.go
+0 -8
... ... @@ -364,15 +364,11 @@ var translations = map[Lang]map[string]string{
364 364 "contextmenu.open_image": "Открыть изображение",
365 365 "contextmenu.copy_image": "Копировать адрес изображения",
366 366 "contextmenu.save_image": "Сохранить изображение",
367 - "contextmenu.repository": "Репозиторий",
368 - "contextmenu.repo_actions": "Действия",
369 367 "contextmenu.page": "Страница",
370 368 "contextmenu.back": "Назад",
371 369 "contextmenu.forward": "Вперёд",
372 370 "contextmenu.reload": "Обновить",
373 371 "contextmenu.copy_url": "Копировать адрес страницы",
374 - "contextmenu.appearance": "Оформление",
375 - "contextmenu.app": "Приложение",
376 372
377 373 // Флеш-сообщения
378 374 "flash.welcome": "Добро пожаловать в Polar, @%s!",
... ... @@ -982,15 +978,11 @@ var translations = map[Lang]map[string]string{
982 978 "contextmenu.open_image": "Open image in new tab",
983 979 "contextmenu.copy_image": "Copy image address",
984 980 "contextmenu.save_image": "Save image",
985 - "contextmenu.repository": "Repository",
986 - "contextmenu.repo_actions": "Actions",
987 981 "contextmenu.page": "Page",
988 982 "contextmenu.back": "Back",
989 983 "contextmenu.forward": "Forward",
990 984 "contextmenu.reload": "Reload",
991 985 "contextmenu.copy_url": "Copy page URL",
992 - "contextmenu.appearance": "Appearance",
993 - "contextmenu.app": "Application",
994 986
995 987 // Flash messages
996 988 "flash.welcome": "Welcome to Polar, @%s!",
internal/web/static/css/app.css
+1 -26
... ... @@ -1176,9 +1176,6 @@ kbd, .kbd {
1176 1176 transform: translateY(0) scale(1);
1177 1177 animation: contextMenuIn 130ms cubic-bezier(0.16, 1, 0.3, 1);
1178 1178 }
1179 -body[data-repo-path] .context-menu {
1180 - max-height: min(80vh, 52rem);
1181 -}
1182 1179 .context-menu-section[hidden],
1183 1180 .context-menu-item[hidden] {
1184 1181 display: none;
... ... @@ -1199,18 +1196,6 @@ body[data-repo-path] .context-menu {
1199 1196 text-transform: uppercase;
1200 1197 color: hsl(var(--muted-foreground));
1201 1198 }
1202 -.context-menu-meta {
1203 - margin-left: auto;
1204 - max-width: 9.5rem;
1205 - overflow: hidden;
1206 - text-overflow: ellipsis;
1207 - white-space: nowrap;
1208 - font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1209 - font-size: 0.6rem;
1210 - letter-spacing: 0;
1211 - text-transform: none;
1212 - opacity: 0.85;
1213 -}
1214 1199 .context-menu-item {
1215 1200 display: flex;
1216 1201 width: 100%;
... ... @@ -1236,7 +1221,7 @@ body[data-repo-path] .context-menu {
1236 1221 color: hsl(var(--muted-foreground));
1237 1222 transition: color 90ms ease;
1238 1223 }
1239 -.context-menu-item > span:not(.context-menu-check):not(.context-menu-kbd) {
1224 +.context-menu-item > span:not(.context-menu-kbd) {
1240 1225 min-width: 0;
1241 1226 overflow: hidden;
1242 1227 text-overflow: ellipsis;
... ... @@ -1266,16 +1251,6 @@ body[data-repo-path] .context-menu {
1266 1251 border: 1px solid hsl(var(--border));
1267 1252 border-radius: 0.22rem;
1268 1253 }
1269 -.context-menu-check {
1270 - display: inline-flex;
1271 - margin-left: auto;
1272 - flex: none;
1273 - opacity: 0;
1274 - color: hsl(var(--foreground));
1275 -}
1276 -.context-menu-item.is-current .context-menu-check {
1277 - opacity: 1;
1278 -}
1279 1254
1280 1255 /* Shortcuts dialog */
1281 1256 .shortcuts-list {
internal/web/static/js/app.js
+2 -26
... ... @@ -69,7 +69,6 @@
69 69 el.classList.toggle('active', isCurrent);
70 70 el.setAttribute('aria-checked', isCurrent ? 'true' : 'false');
71 71 });
72 - updateContextMenuThemeState();
73 72 }
74 73 window.PolarSetTheme = setTheme;
75 74
... ... @@ -146,17 +145,6 @@
146 145 }
147 146 }
148 147
149 - function updateContextMenuThemeState() {
150 - var menu = document.getElementById('context-menu');
151 - if (!menu) return;
152 - var theme = document.documentElement.getAttribute('data-theme') || 'system';
153 - menu.querySelectorAll('[data-theme-value]').forEach(function (item) {
154 - var isCurrent = item.getAttribute('data-theme-value') === theme;
155 - item.classList.toggle('is-current', isCurrent);
156 - item.setAttribute('aria-checked', isCurrent ? 'true' : 'false');
157 - });
158 - }
159 -
160 148 function closeContextMenu() {
161 149 var menu = document.getElementById('context-menu');
162 150 if (!menu || !menu.classList.contains('open')) return;
... ... @@ -229,7 +217,6 @@
229 217 var imageCopyItem = menu.querySelector('[data-context-action="image-copy"]');
230 218 if (imageCopyItem) imageCopyItem.hidden = !imageURL || imageIsTemporary;
231 219
232 - updateContextMenuThemeState();
233 220 menu.classList.add('open');
234 221 menu.setAttribute('aria-hidden', 'false');
235 222 menu.scrollTop = 0;
... ... @@ -271,12 +258,6 @@
271 258 var action = item.getAttribute('data-context-action');
272 259 var state = contextMenuState || {};
273 260
274 - if (action === 'theme') {
275 - setTheme(item.getAttribute('data-theme-value'), false);
276 - closeContextMenu();
277 - return;
278 - }
279 -
280 261 closeContextMenu();
281 262
282 263 if (action === 'copy-selection') {
... ... @@ -320,10 +301,6 @@
320 301 window.history.back();
321 302 } else if (action === 'page-forward') {
322 303 window.history.forward();
323 - } else if (action === 'palette') {
324 - openPalette('');
325 - } else if (action === 'shortcuts') {
326 - openDialog('shortcuts-dialog');
327 304 }
328 305 }
329 306
... ... @@ -332,9 +309,8 @@
332 309 var order = ['light', 'dark', 'system'];
333 310 var next = order[(order.indexOf(current) + 1) % order.length];
334 311 setTheme(next, false);
335 - var item = document.querySelector('#context-menu [data-theme-value="' + next + '"]');
336 - var label = item ? item.querySelector('span:not(.context-menu-check)') : null;
337 - showToast(label ? label.textContent.trim() : next);
312 + var item = document.querySelector('#command-palette [data-palette-theme="' + next + '"]');
313 + showToast(item ? item.textContent.trim() : next);
338 314 }
339 315
340 316 function shortcutUnavailable() {
internal/web/views/layouts/base.html
+0 -96
... ... @@ -401,73 +401,6 @@
401 401 </button>
402 402 </div>
403 403
404 - {{if .Repo}}
405 - <div class="context-menu-section" data-context-section="repo">
406 - <div class="context-menu-label">
407 - <span>{{t .Lang "contextmenu.repository"}}</span>
408 - <span class="context-menu-meta">{{.Repo.OwnerUsername}}/{{.Repo.Name}}</span>
409 - </div>
410 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}" role="menuitem">
411 - {{icon "code" "size-3.5"}}<span>{{t .Lang "repo.tab.code"}}</span>
412 - </a>
413 - {{if .Repo.EnableIssues}}
414 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/issues" role="menuitem">
415 - {{icon "issue" "size-3.5"}}<span>{{t .Lang "repo.tab.issues"}}</span>
416 - </a>
417 - {{end}}
418 - {{if .Repo.EnablePulls}}
419 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls" role="menuitem">
420 - {{icon "git-pull-request" "size-3.5"}}<span>{{t .Lang "repo.tab.pulls"}}</span>
421 - </a>
422 - {{end}}
423 - {{if .Repo.EnableReleases}}
424 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/releases" role="menuitem">
425 - {{icon "tag" "size-3.5"}}<span>{{t .Lang "repo.tab.releases"}}</span>
426 - </a>
427 - {{end}}
428 - {{if .Repo.EnableActivity}}
429 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/activity" role="menuitem">
430 - {{icon "activity" "size-3.5"}}<span>{{t .Lang "repo.tab.activity"}}</span>
431 - </a>
432 - {{end}}
433 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/branches" role="menuitem">
434 - {{icon "git-branch" "size-3.5"}}<span>{{t .Lang "repo.tab.branches"}}</span>
435 - </a>
436 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/commits/{{.Repo.DefaultBranch}}" role="menuitem">
437 - {{icon "history" "size-3.5"}}<span>{{t .Lang "repo.tab.commits"}}</span>
438 - </a>
439 - </div>
440 -
441 - {{if or .CanWrite (and .CurrentUser (or .Repo.EnableIssues .Repo.EnablePulls))}}
442 - <div class="context-menu-section" data-context-section="repo-actions">
443 - <div class="context-menu-label">{{t .Lang "contextmenu.repo_actions"}}</div>
444 - {{if .CurrentUser}}{{if .Repo.EnableIssues}}
445 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/issues/new" role="menuitem">
446 - {{icon "issue" "size-3.5"}}<span>{{t .Lang "issues.new"}}</span>
447 - </a>
448 - {{end}}{{if .Repo.EnablePulls}}
449 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/new" role="menuitem">
450 - {{icon "git-pull-request" "size-3.5"}}<span>{{t .Lang "pulls.new"}}</span>
451 - </a>
452 - {{end}}{{end}}
453 - {{if .CanWrite}}
454 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/new-file" role="menuitem">
455 - {{icon "plus" "size-3.5"}}<span>{{t .Lang "file.new_file"}}</span>
456 - </a>
457 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/new-folder" role="menuitem">
458 - {{icon "folder" "size-3.5"}}<span>{{t .Lang "file.new_folder"}}</span>
459 - </a>
460 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/upload" role="menuitem">
461 - {{icon "upload" "size-3.5"}}<span>{{t .Lang "file.upload_file"}}</span>
462 - </a>
463 - <a class="context-menu-item" href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/settings" role="menuitem">
464 - {{icon "settings" "size-3.5"}}<span>{{t .Lang "repo.tab.settings"}}</span>
465 - </a>
466 - {{end}}
467 - </div>
468 - {{end}}
469 - {{end}}
470 -
471 404 <div class="context-menu-section" data-context-section="page">
472 405 <div class="context-menu-label">{{t .Lang "contextmenu.page"}}</div>
473 406 <button type="button" class="context-menu-item" data-context-action="page-back" role="menuitem">
... ... @@ -484,36 +417,7 @@
484 417 </button>
485 418 </div>
486 419
487 - <div class="context-menu-section" data-context-section="appearance">
488 - <div class="context-menu-label">{{t .Lang "contextmenu.appearance"}}</div>
489 - <button type="button" class="context-menu-item" data-context-action="theme" data-theme-value="light" role="menuitemradio" aria-checked="false">
490 - {{icon "sun" "size-3.5"}}<span>{{t .Lang "theme.light"}}</span><span class="context-menu-check">{{icon "check" "size-3.5"}}</span>
491 - </button>
492 - <button type="button" class="context-menu-item" data-context-action="theme" data-theme-value="dark" role="menuitemradio" aria-checked="false">
493 - {{icon "moon" "size-3.5"}}<span>{{t .Lang "theme.dark"}}</span><span class="context-menu-check">{{icon "check" "size-3.5"}}</span>
494 - </button>
495 - <button type="button" class="context-menu-item" data-context-action="theme" data-theme-value="system" role="menuitemradio" aria-checked="false">
496 - {{icon "laptop" "size-3.5"}}<span>{{t .Lang "theme.system"}}</span><span class="context-menu-check">{{icon "check" "size-3.5"}}</span>
497 - </button>
498 - </div>
499 420
500 - <div class="context-menu-section" data-context-section="app">
501 - <div class="context-menu-label">{{t .Lang "contextmenu.app"}}</div>
502 - <button type="button" class="context-menu-item" data-context-action="palette" role="menuitem">
503 - {{icon "command" "size-3.5"}}<span>{{t .Lang "shortcuts.palette"}}</span><kbd class="context-menu-kbd">Ctrl K</kbd>
504 - </button>
505 - <button type="button" class="context-menu-item" data-context-action="shortcuts" role="menuitem">
506 - {{icon "keyboard" "size-3.5"}}<span>{{t .Lang "shortcuts.title"}}</span><kbd class="context-menu-kbd">?</kbd>
507 - </button>
508 - {{if .CurrentUser}}
509 - <a class="context-menu-item" href="/new" role="menuitem">
510 - {{icon "plus" "size-3.5"}}<span>{{t .Lang "repo.new.title"}}</span>
511 - </a>
512 - <a class="context-menu-item" href="/settings" role="menuitem">
513 - {{icon "settings" "size-3.5"}}<span>{{t .Lang "nav.settings"}}</span>
514 - </a>
515 - {{end}}
516 - </div>
517 421 </div>
518 422
519 423 <div id="toast-container" class="toast-container" aria-live="polite"></div>