feat(desktop): Linux AppImage polish — window controls, updater UX, docs (#2144)

* feat(electron): add Linux AppImage releases

* ci: cache Linux OpenCode CLI artifacts

* fix(ci): await Linux release inventory check

* fix(electron): add frameless window controls on Linux desktop

Linux AppImages were created without native WM decorations and without
in-app controls, leaving users unable to close the window with a mouse.

Treat Linux like Windows: frameless BrowserWindow plus the existing
WindowsWindowControls header buttons and app-menu entry. macOS keeps
hidden title bar with traffic lights unchanged.

Shared usesFramelessElectronChrome() helper drives main window, mini
chat, header insets, and titlebar controls.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* feat(desktop): add configurable window controls position by OS

Add desktopWindowControlsPosition setting (auto/left/right) with OS-aware
defaults: Linux left, Windows right. Wire frameless chrome controls in
Header, TitlebarLeftControls, and MiniChatLayout, plus a Sessions settings
control for Windows and Linux desktop shells.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

* fix(desktop): address Linux AppImage release review findings

Propagate updater capability errors to the UI, treat missing
latest-linux.yml feeds as no-update, stop installed-apps IPC spam on
Linux, document FUSE/AppImage limits, add CHANGELOG entry, migrate
remaining btriapitsyn URLs, and run Electron Linux unit tests on PRs.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>

---------

Co-authored-by: jibanez-staticduo <staticduo@gmail.com>
Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
Serhii Dziupin
2026-07-13 08:59:31 +03:00
committed by GitHub
co-authored by Serhii Dziupin jibanez-staticduo
parent 7e248d4e9b
commit 502c96630e
55 changed files with 1679 additions and 112 deletions
@@ -4,12 +4,17 @@ import { Icon } from '@/components/icon/Icon';
import { useI18n } from '@/lib/i18n';
import { cn } from '@/lib/utils';
import { invokeDesktop } from '@/lib/desktop';
import type { DesktopWindowControlsSide } from '@/lib/desktop';
type WindowsWindowControlsProps = {
visible: boolean;
position?: DesktopWindowControlsSide;
};
export const WindowsWindowControls = React.memo(function WindowsWindowControls({ visible }: WindowsWindowControlsProps) {
export const WindowsWindowControls = React.memo(function WindowsWindowControls({
visible,
position = 'right',
}: WindowsWindowControlsProps) {
const { t } = useI18n();
const [isMaximized, setIsMaximized] = React.useState(false);
@@ -44,9 +49,12 @@ export const WindowsWindowControls = React.memo(function WindowsWindowControls({
}
const buttonClassName = 'app-region-no-drag inline-flex h-12 w-11 items-center justify-center text-muted-foreground transition-colors hover:bg-interactive-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary';
const containerClassName = position === 'left'
? 'app-region-no-drag -ml-3 mr-2 flex h-12 shrink-0 items-center'
: 'app-region-no-drag -mr-3 ml-2 flex h-12 shrink-0 items-center';
return (
<div className="app-region-no-drag -mr-3 ml-2 flex h-12 shrink-0 items-center" aria-label={t('header.windowControls.groupAria')}>
<div className={containerClassName} aria-label={t('header.windowControls.groupAria')}>
<button
type="button"
className={buttonClassName}