Files
502c96630e 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>
2026-07-13 08:59:31 +03:00

75 lines
2.4 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
PRODUCTION_UPDATER_FEED,
parseLoopbackUpdaterUrl,
resolveUpdaterFeed,
} from './updater-feed.mjs';
const overrideEnvironment = {
OPENCHAMBER_E2E: '1',
OPENCHAMBER_UPDATER_E2E_URL: 'http://127.0.0.1:49152/updates/',
};
test('production updater feed is immutable GitHub configuration', () => {
assert.equal(Object.isFrozen(PRODUCTION_UPDATER_FEED), true);
assert.deepEqual(PRODUCTION_UPDATER_FEED, {
provider: 'github',
owner: 'openchamber',
repo: 'openchamber',
});
});
test('requires the complete E2E environment and embedded build-marker conjunction', () => {
const cases = [
{},
{ environment: overrideEnvironment },
{ environment: { OPENCHAMBER_E2E: '1' }, testBuild: true },
{
environment: { OPENCHAMBER_UPDATER_E2E_URL: overrideEnvironment.OPENCHAMBER_UPDATER_E2E_URL },
testBuild: true,
},
{ environment: overrideEnvironment, testBuild: false },
];
for (const input of cases) assert.equal(resolveUpdaterFeed(input), PRODUCTION_UPDATER_FEED);
});
test('accepts only credential-free loopback HTTP(S) URLs', () => {
assert.equal(parseLoopbackUpdaterUrl('http://127.0.0.1:8080/feed'), 'http://127.0.0.1:8080/feed');
assert.equal(parseLoopbackUpdaterUrl('https://127.255.0.1/feed/'), 'https://127.255.0.1/feed/');
assert.equal(parseLoopbackUpdaterUrl('http://[::1]:8080/feed'), 'http://[::1]:8080/feed');
for (const value of [
'http://localhost:8080/feed',
'http://0.0.0.0:8080/feed',
'http://192.168.1.5:8080/feed',
'https://example.com/feed',
'file:///tmp/feed',
'ftp://127.0.0.1/feed',
'http://user:secret@127.0.0.1/feed',
'http://127.0.0.1/feed?token=secret',
'http://127.0.0.1/feed#fragment',
'not-a-url',
]) assert.equal(parseLoopbackUpdaterUrl(value), null, value);
});
test('uses a generic feed only when every test-only gate is valid', () => {
assert.deepEqual(resolveUpdaterFeed({
environment: overrideEnvironment,
testBuild: true,
}), {
provider: 'generic',
url: 'http://127.0.0.1:49152/updates/',
});
});
test('invalid URLs fall back to the production feed even with both test gates', () => {
for (const url of ['https://example.com/feed', 'http://localhost/feed', '']) {
assert.equal(resolveUpdaterFeed({
environment: { ...overrideEnvironment, OPENCHAMBER_UPDATER_E2E_URL: url },
testBuild: true,
}), PRODUCTION_UPDATER_FEED);
}
});