diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4e3a0e..d7d622e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -450,6 +450,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +### Settings — Linux text-input freeze workaround + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#884](https://github.com/Psychotoxical/psysonic/pull/884)** + +* **Settings → System → Behavior** (Linux only): optional toggle for users on WebKitGTK 2.50.x where text fields freeze when clicked (issues #342, #782) — turning it on forces the input to repaint on focus. Default off; enabling it adds a brief flicker on search icons. + + + ## [1.46.0] - 2026-05-18 > **🙏 Special thanks to [@zz5zz](https://github.com/zz5zz)** for his tireless quirk-spotting and bug reports on the [Psysonic Discord](https://discord.gg/AMnDRErm4u) — several of the polish fixes in this release landed directly off the back of his messages. diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx index 42d48c59..23d050e7 100644 --- a/src/app/AppShell.tsx +++ b/src/app/AppShell.tsx @@ -164,6 +164,32 @@ export function AppShell() { return () => window.removeEventListener('psy:toggle-sidebar', onToggleSidebar); }, [isSidebarCollapsed, setSidebarCollapsed]); + // Workaround for WebKitGTK 2.50.x text-input repaint hang on + // Linux Mint / Ubuntu 24.04 (issues #342, #782). When opted in, + // nudge WebKit awake on every input/textarea focus via a sync + // reflow read plus a one-frame translateZ(0) toggle on the input's + // parent so the rendering pipeline re-evaluates the layer tree. + // Side-effect: search magnifier flickers briefly on focus. + const linuxWebkitInputForceRepaint = useAuthStore(s => s.linuxWebkitInputForceRepaint); + useEffect(() => { + if (!linuxWebkitInputForceRepaint) return; + const handler = (e: FocusEvent) => { + const target = e.target; + if (!(target instanceof HTMLElement)) return; + const tag = target.tagName; + if (tag !== 'INPUT' && tag !== 'TEXTAREA') return; + const layerHost = (target.parentElement as HTMLElement | null) ?? target; + void layerHost.offsetHeight; + const previous = layerHost.style.transform; + layerHost.style.transform = 'translateZ(0)'; + requestAnimationFrame(() => { + layerHost.style.transform = previous; + }); + }; + document.addEventListener('focusin', handler, true); + return () => document.removeEventListener('focusin', handler, true); + }, [linuxWebkitInputForceRepaint]); + const { queueWidth, isDraggingQueue, diff --git a/src/components/settings/SystemTab.tsx b/src/components/settings/SystemTab.tsx index b07b0dea..6c622bdb 100644 --- a/src/components/settings/SystemTab.tsx +++ b/src/components/settings/SystemTab.tsx @@ -119,6 +119,21 @@ export function SystemTab() { +
+
+
+
{t('settings.linuxWebkitInputForceRepaint')}
+
{t('settings.linuxWebkitInputForceRepaintDesc')}
+
+ +
{waylandTextRenderAvailable && ( <>
diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index b8fd66ee..00088ebb 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -336,6 +336,7 @@ const CONTRIBUTOR_ENTRIES = [ 'Interface Scale: scales the entire window — sidebar, queue, player bar, modals and the fullscreen player follow the main content (PR #781)', 'Local library index (preview): SQLite per-server track store, background initial and delta sync, live and Advanced Search against the local index, integrity verify and auto-reconcile on count drop (PR #846)', 'Server index-key rebuild: safe dual-DB migration flow, per-server analysis strategy controls, and playback/index scope hardening (PR #864)', + 'Settings: opt-in Linux input-focus repaint — workaround for the WebKitGTK 2.50.x text-field freeze (PR #884)', ], }, ] as const; diff --git a/src/locales/de/settings.ts b/src/locales/de/settings.ts index b67db267..48fea658 100644 --- a/src/locales/de/settings.ts +++ b/src/locales/de/settings.ts @@ -205,6 +205,8 @@ export const settings = { useCustomTitlebarDesc: 'Ersetzt die System-Titelleiste durch eine eingebaute, die zum App-Theme passt. Deaktivieren, um die native GNOME/GTK-Titelleiste zu verwenden.', linuxWebkitSmoothScroll: 'Sanftes Mausrad (Linux)', linuxWebkitSmoothScrollDesc: 'An: mit Nachlauf. Aus: zeilenweise wie in GTK-Apps.', + linuxWebkitInputForceRepaint: 'Eingabefelder beim Fokus neu zeichnen (Linux)', + linuxWebkitInputForceRepaintDesc: 'Behelf für WebKitGTK 2.50.x, wo Textfelder beim Klick einfrieren. Such-Icons flackern beim Fokus kurz.', linuxWaylandTextRender: 'Wayland-Textdarstellung (Linux)', linuxWaylandTextRenderDesc: 'Kantenglättung in der Oberfläche wirkt sofort. Die WebKit-Beschleunigungsrichtlinie (scharf/GPU) wird gespeichert und beim nächsten App-Start angewendet — ein Live-Umschalten kann WebKitGTK einfrieren.', diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index 440efee8..e8428ee6 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -228,6 +228,8 @@ export const settings = { useCustomTitlebarDesc: 'Replace the system title bar with a built-in one that matches the app theme. Disable to use the native GNOME/GTK title bar.', linuxWebkitSmoothScroll: 'Smooth wheel (Linux)', linuxWebkitSmoothScrollDesc: 'On: inertial scroll. Off: line-by-line, GTK-style.', + linuxWebkitInputForceRepaint: 'Repaint inputs on focus (Linux)', + linuxWebkitInputForceRepaintDesc: 'Workaround for WebKitGTK 2.50.x where text fields freeze when clicked. Search icons flicker briefly on focus.', linuxWaylandTextRender: 'Wayland text rendering (Linux)', linuxWaylandTextRenderDesc: 'Font smoothing in the UI updates immediately. WebKit hardware acceleration (sharp / GPU presets) is saved and applied on the next app start — changing it live can freeze WebKitGTK on some setups.', diff --git a/src/locales/es/settings.ts b/src/locales/es/settings.ts index 87371d2a..8638c6b7 100644 --- a/src/locales/es/settings.ts +++ b/src/locales/es/settings.ts @@ -204,6 +204,8 @@ export const settings = { useCustomTitlebarDesc: 'Reemplaza la barra de título del sistema con una integrada que coincide con el tema de la app. Desactiva para usar la barra nativa de GNOME/GTK.', linuxWebkitSmoothScroll: 'Rueda suave (Linux)', linuxWebkitSmoothScrollDesc: 'Activado: inercia. Desactivado: pasos por línea (estilo GTK).', + linuxWebkitInputForceRepaint: 'Repintar los campos al enfocar (Linux)', + linuxWebkitInputForceRepaintDesc: 'Solución para WebKitGTK 2.50.x donde los campos de texto se congelan al pulsar. Las lupas parpadean brevemente al enfocar.', linuxWaylandTextRender: 'Renderizado de texto Wayland (Linux)', linuxWaylandTextRenderDesc: 'El suavizado en la interfaz se aplica al instante. La aceleración de hardware de WebKit (nítido/GPU) se guarda y aplica al reiniciar la app; cambiarla en vivo puede congelar WebKitGTK.', diff --git a/src/locales/fr/settings.ts b/src/locales/fr/settings.ts index eafaf63b..29f8aaf7 100644 --- a/src/locales/fr/settings.ts +++ b/src/locales/fr/settings.ts @@ -200,6 +200,8 @@ export const settings = { preloadMiniPlayerDesc: 'Construit la fenêtre du mini-lecteur en arrière-plan au démarrage de l\'application afin qu\'elle affiche son contenu instantanément à la première ouverture. Utilise un peu plus de mémoire.', linuxWebkitSmoothScroll: 'Molette fluide (Linux)', linuxWebkitSmoothScrollDesc: 'Activé : inertie. Désactivé : pas à la ligne, style GTK.', + linuxWebkitInputForceRepaint: 'Repeindre les champs au focus (Linux)', + linuxWebkitInputForceRepaintDesc: 'Contournement pour WebKitGTK 2.50.x où les champs texte gèlent au clic. Les loupes scintillent brièvement au focus.', linuxWaylandTextRender: 'Rendu du texte Wayland (Linux)', linuxWaylandTextRenderDesc: 'Le lissage dans l’interface est immédiat. L’accélération matérielle WebKit (net / GPU) est enregistrée et appliquée au prochain lancement — la modifier en direct peut figer WebKitGTK.', diff --git a/src/locales/nb/settings.ts b/src/locales/nb/settings.ts index aa6ffa82..eafbab5a 100644 --- a/src/locales/nb/settings.ts +++ b/src/locales/nb/settings.ts @@ -199,6 +199,8 @@ export const settings = { preloadMiniPlayerDesc: 'Bygger miniavspiller-vinduet i bakgrunnen ved appstart slik at det viser innhold umiddelbart ved første åpning. Bruker litt mer minne.', linuxWebkitSmoothScroll: 'Mykt musehjul (Linux)', linuxWebkitSmoothScrollDesc: 'På: treg rull med etterslep. Av: trinnvis som i GTK.', + linuxWebkitInputForceRepaint: 'Tegne om tekstfelt ved fokus (Linux)', + linuxWebkitInputForceRepaintDesc: 'Omgår WebKitGTK 2.50.x der tekstfelt fryser ved klikk. Søkeikoner blinker kort ved fokus.', linuxWaylandTextRender: 'Wayland-tekstgjengivelse (Linux)', linuxWaylandTextRenderDesc: 'Utjevning i grensesnittet skjer med én gang. WebKit-maskinvareakselerasjon (skarp/GPU) lagres og brukes ved neste appstart — live bytte kan fryse WebKitGTK.', diff --git a/src/locales/nl/settings.ts b/src/locales/nl/settings.ts index 1de9b202..da981566 100644 --- a/src/locales/nl/settings.ts +++ b/src/locales/nl/settings.ts @@ -200,6 +200,8 @@ export const settings = { preloadMiniPlayerDesc: 'Bouwt het venster van de mini-speler op de achtergrond bij het opstarten van de app, zodat het bij de eerste opening direct inhoud toont. Gebruikt iets meer geheugen.', linuxWebkitSmoothScroll: 'Vloeiend muiswiel (Linux)', linuxWebkitSmoothScrollDesc: 'Aan: traag naloop. Uit: regel voor regel, GTK-stijl.', + linuxWebkitInputForceRepaint: 'Tekstvelden bij focus opnieuw tekenen (Linux)', + linuxWebkitInputForceRepaintDesc: 'Tijdelijke oplossing voor WebKitGTK 2.50.x waar tekstvelden bij klikken bevriezen. Zoekicoontjes flikkeren kort bij focus.', linuxWaylandTextRender: 'Wayland-tekstweergave (Linux)', linuxWaylandTextRenderDesc: 'Vloeiendheid in de UI werkt meteen. WebKit-hardwareversnelling (scherp/GPU) wordt bewaard en toegepast bij de volgende app-start — live schakelen kan WebKitGTK laten vastlopen.', diff --git a/src/locales/ro/settings.ts b/src/locales/ro/settings.ts index 7706d887..3b010cbe 100644 --- a/src/locales/ro/settings.ts +++ b/src/locales/ro/settings.ts @@ -207,6 +207,8 @@ export const settings = { useCustomTitlebarDesc: 'Înlocuiește bara de titlu a sistemului cu una care corespunde cu tema aplicației. Dezactivează pentru a folosi bara de titlu nativ GNOME/GTK.', linuxWebkitSmoothScroll: 'Rotiță lină (Linux)', linuxWebkitSmoothScrollDesc: 'Pornit: scroll inert. Oprit: linie cu linie, în stil GTK.', + linuxWebkitInputForceRepaint: 'Redesenare câmpuri la focus (Linux)', + linuxWebkitInputForceRepaintDesc: 'Soluție pentru WebKitGTK 2.50.x unde câmpurile text îngheață la clic. Pictogramele lupă pâlpâie scurt la focus.', linuxWaylandTextRender: 'Randare text Wayland (Linux)', linuxWaylandTextRenderDesc: 'Netezirea în interfață se aplică imediat. Accelerația hardware WebKit (ascuțit/GPU) este salvată și aplicată la următoarea pornire a aplicației — comutarea în timpul rulării poate îngheța WebKitGTK.', diff --git a/src/locales/ru/settings.ts b/src/locales/ru/settings.ts index 91494eb9..6c1c2537 100644 --- a/src/locales/ru/settings.ts +++ b/src/locales/ru/settings.ts @@ -229,6 +229,8 @@ export const settings = { 'Заменить системную строку заголовка встроенной, в стиле темы приложения. Отключите, чтобы использовать родную строку GNOME/GTK и т.д.', linuxWebkitSmoothScroll: 'Плавное колесо (Linux)', linuxWebkitSmoothScrollDesc: 'Вкл — инерция. Выкл — по шагам, как в GTK.', + linuxWebkitInputForceRepaint: 'Перерисовывать поля ввода при фокусе (Linux)', + linuxWebkitInputForceRepaintDesc: 'Обход проблемы WebKitGTK 2.50.x, когда текстовые поля зависают при клике. Иконки поиска кратко мигают при фокусе.', linuxWaylandTextRender: 'Рендеринг текста Wayland (Linux)', linuxWaylandTextRenderDesc: 'Сглаживание в интерфейсе меняется сразу. Политику ускорения WebKit (чёткий / GPU) сохраняем и применяем при следующем запуске — переключение на лету на части сборок зависает в WebKitGTK.', diff --git a/src/locales/zh/settings.ts b/src/locales/zh/settings.ts index 704b73ee..706d1208 100644 --- a/src/locales/zh/settings.ts +++ b/src/locales/zh/settings.ts @@ -200,6 +200,8 @@ export const settings = { preloadMiniPlayerDesc: '在应用启动时于后台构建迷你播放器窗口,使其首次打开即可立即显示内容。会占用少量额外内存。', linuxWebkitSmoothScroll: '滚轮平滑(Linux)', linuxWebkitSmoothScrollDesc: '开:惯性滚动。关:逐行,类似 GTK。', + linuxWebkitInputForceRepaint: '获得焦点时重绘输入框(Linux)', + linuxWebkitInputForceRepaintDesc: '针对 WebKitGTK 2.50.x 文本框点击后冻结的临时方案。聚焦时搜索图标会短暂闪烁。', linuxWaylandTextRender: 'Wayland 文本渲染(Linux)', linuxWaylandTextRenderDesc: '界面字体平滑立即生效。WebKit 硬件加速策略(锐利 / GPU)会保存并在下次启动应用时应用;运行中反复切换可能导致 WebKitGTK 卡死。', linuxWaylandTextRenderBalanced: '平衡', diff --git a/src/store/authStore.ts b/src/store/authStore.ts index 16d52e2a..5dd9e17f 100644 --- a/src/store/authStore.ts +++ b/src/store/authStore.ts @@ -79,6 +79,7 @@ export const useAuthStore = create()( preloadMiniPlayer: false, linuxWebkitKineticScroll: true, linuxWaylandTextRenderProfile: 'sharp', + linuxWebkitInputForceRepaint: false, loggingMode: 'normal', nowPlayingEnabled: false, lyricsServerFirst: true, diff --git a/src/store/authStoreTypes.ts b/src/store/authStoreTypes.ts index 6d840a26..48587a34 100644 --- a/src/store/authStoreTypes.ts +++ b/src/store/authStoreTypes.ts @@ -136,6 +136,11 @@ export interface AuthState { linuxWebkitKineticScroll: boolean; /** Linux Wayland + GPU compositing: WebKit text rasterisation profile (live, no restart). */ linuxWaylandTextRenderProfile: LinuxWaylandTextRenderProfile; + /** Linux WebKitGTK 2.50.x text-input repaint hang workaround (issues #342, #782). + * When true, toggles a one-frame transform on the focused input's parent so WebKit + * re-evaluates the layer tree. Off by default — the side-effect is a brief flicker + * on focus, accepted trade-off for the affected users. */ + linuxWebkitInputForceRepaint: boolean; /** Runtime backend logging level. */ loggingMode: LoggingMode; nowPlayingEnabled: boolean; @@ -310,6 +315,7 @@ export interface AuthState { setPreloadMiniPlayer: (v: boolean) => void; setLinuxWebkitKineticScroll: (v: boolean) => void; setLinuxWaylandTextRenderProfile: (v: LinuxWaylandTextRenderProfile) => void; + setLinuxWebkitInputForceRepaint: (v: boolean) => void; setLoggingMode: (v: LoggingMode) => void; setNowPlayingEnabled: (v: boolean) => void; setLyricsServerFirst: (v: boolean) => void; diff --git a/src/store/authUiAppearanceActions.ts b/src/store/authUiAppearanceActions.ts index 0b72c3a5..2f59eebf 100644 --- a/src/store/authUiAppearanceActions.ts +++ b/src/store/authUiAppearanceActions.ts @@ -22,6 +22,7 @@ export function createUiAppearanceActions(set: SetState): Pick< | 'setPreloadMiniPlayer' | 'setLinuxWebkitKineticScroll' | 'setLinuxWaylandTextRenderProfile' + | 'setLinuxWebkitInputForceRepaint' | 'setSeekbarStyle' | 'setQueueNowPlayingCollapsed' | 'setQueueDurationDisplayMode' @@ -45,6 +46,7 @@ export function createUiAppearanceActions(set: SetState): Pick< setPreloadMiniPlayer: (v) => set({ preloadMiniPlayer: v }), setLinuxWebkitKineticScroll: (v) => set({ linuxWebkitKineticScroll: v }), setLinuxWaylandTextRenderProfile: (v) => set({ linuxWaylandTextRenderProfile: v }), + setLinuxWebkitInputForceRepaint: (v) => set({ linuxWebkitInputForceRepaint: v }), setSeekbarStyle: (v) => set({ seekbarStyle: v }), setQueueNowPlayingCollapsed: (v) => set({ queueNowPlayingCollapsed: v }), setQueueDurationDisplayMode: (v) => set({ queueDurationDisplayMode: v }),