Linux-Fassung (Ubuntu) ergaenzen
Die Fachlogik war bereits plattformneutral — paths.js loeste ~/.config schon auf, jsonl.js liest ueber os.homedir(). Zu tun waren Autostart, Build-Targets und drei Linux-Eigenheiten der Oberflaeche. XWayland erzwingen: Ab Electron 36 waehlt Chromium in einer Wayland-Sitzung von sich aus Wayland. Dort darf sich ein Fenster weder selbst positionieren noch dauerhaft in den Vordergrund legen — beides ist der Kern dieses Widgets. Der Schalter muss auf der Kommandozeile stehen, weil Ozone die Plattform vor dem Main-Skript festlegt; appendSwitch() und ELECTRON_OZONE_PLATFORM_HINT kommen beide zu spaet. Bleibt ein einmaliger Neustart mit --ozone-platform=x11, der sich am Argument selbst als Abbruchbedingung erkennt. Im AppImage muss dabei execPath auf APPIMAGE zeigen, sonst laeuft der Nachfolger in den /tmp-Einhaengepunkt, den der Vorgaenger beim Beenden wegraeumt. Autostart: app.setLoginItemSettings() gibt es unter Linux nicht. Das neue Modul autostart.js kapselt die Weiche und schreibt bzw. loescht dort ~/.config/autostart/claude-live-dashboard.desktop. Der Startbefehl kommt aus APPIMAGE, falls gesetzt. Ausserdem: enable-transparent-visuals fuer echte Transparenz, das 32-px-Tray-Symbol in der GNOME-Leiste, plattformabhaengige Beschriftungen in Tray-Menue und Einstellungsdialog, AppImage- und deb-Targets, icon.png auf 512 px (electron-builder rechnet daraus die Linux-Groessen herunter) und start.sh als Gegenstueck zu start.cmd. Getestet unter GNOME/Wayland: Fenster auf X11 an erwarteter Position, gemerkte Position wird wiederhergestellt, Tray registriert sich auf DBus, dump liest die Transkripte, beide Pakete bauen und das AppImage startet ohne Schalter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+62
-13
@@ -12,6 +12,9 @@ const { app, BrowserWindow, Tray, Menu, ipcMain, screen, nativeImage, shell } =
|
||||
const { Store, loadConfig } = require('./store');
|
||||
const { Collector } = require('./collector');
|
||||
const { CONFIG_FILE, STATE_FILE, USER_DATA_DIR, ensureUserConfig } = require('./paths');
|
||||
const { getAutostart, setAutostart: applyAutostart } = require('./autostart');
|
||||
|
||||
const IS_LINUX = process.platform === 'linux';
|
||||
|
||||
const COMPACT_HEIGHT = 352;
|
||||
const EXPANDED_HEIGHT = 640;
|
||||
@@ -30,11 +33,59 @@ let collector = null;
|
||||
let store = null;
|
||||
let config = {};
|
||||
|
||||
/**
|
||||
* Erzwingt XWayland, sobald das Widget in einer Wayland-Sitzung startet.
|
||||
*
|
||||
* Electron wählt seit Version 36 von sich aus Wayland. Dort darf sich ein
|
||||
* Fenster aber weder selbst positionieren noch dauerhaft in den Vordergrund
|
||||
* legen — beides ist der Kern dieses Widgets. Über XWayland funktioniert es.
|
||||
*
|
||||
* Der Schalter muss auf der Kommandozeile stehen: Ozone wählt die Plattform,
|
||||
* bevor dieses Skript läuft, deshalb kommen `appendSwitch()` und
|
||||
* `ELECTRON_OZONE_PLATFORM_HINT` beide zu spät. Bleibt ein Neustart mit
|
||||
* ergänztem Argument. Das Argument ist zugleich die Abbruchbedingung — im
|
||||
* zweiten Durchlauf steht es in `argv` und der Zweig greift nicht mehr.
|
||||
*
|
||||
* Muss vor `requestSingleInstanceLock()` stehen, damit die verworfene Instanz
|
||||
* die Sperre gar nicht erst nimmt.
|
||||
*/
|
||||
function forceX11IfWayland() {
|
||||
if (!IS_LINUX || !process.env.WAYLAND_DISPLAY) return false;
|
||||
if (process.env.ELECTRON_OZONE_PLATFORM_HINT) return false; // ausdrücklich übersteuert
|
||||
if (process.argv.some((a) => a.startsWith('--ozone-platform'))) return false;
|
||||
|
||||
const args = process.argv.slice(1).concat('--ozone-platform=x11');
|
||||
|
||||
// Ein AppImage hängt sich beim Start unter /tmp ein und wirft die Einhängung
|
||||
// beim Beenden wieder weg. `process.execPath` zeigt dorthin und wäre für den
|
||||
// Nachfolger schon verschwunden — neu gestartet werden muss das Abbild.
|
||||
if (process.env.APPIMAGE) app.relaunch({ execPath: process.env.APPIMAGE, args });
|
||||
else app.relaunch({ args });
|
||||
|
||||
app.exit(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!forceX11IfWayland()) {
|
||||
// Ohne diesen Schalter zeichnet Chromium unter Linux hinter das rahmenlose
|
||||
// Fenster einen schwarzen Kasten statt echter Transparenz.
|
||||
if (IS_LINUX) app.commandLine.appendSwitch('enable-transparent-visuals');
|
||||
|
||||
bootstrap();
|
||||
}
|
||||
|
||||
/** Verhindert, dass mehrere Instanzen dieselben Zustandsdateien beschreiben. */
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit();
|
||||
} else {
|
||||
function bootstrap() {
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
app.on('second-instance', () => showWindow());
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,13 +193,13 @@ function setClickThrough(enabled) {
|
||||
}
|
||||
|
||||
function setAutostart(enabled) {
|
||||
app.setLoginItemSettings({ openAtLogin: enabled, args: [] });
|
||||
applyAutostart(enabled);
|
||||
buildTrayMenu();
|
||||
}
|
||||
|
||||
function buildTrayMenu() {
|
||||
if (!tray) return;
|
||||
const autostart = app.getLoginItemSettings().openAtLogin;
|
||||
const autostart = getAutostart();
|
||||
|
||||
tray.setContextMenu(
|
||||
Menu.buildFromTemplate([
|
||||
@@ -171,7 +222,7 @@ function buildTrayMenu() {
|
||||
click: (item) => setClickThrough(item.checked),
|
||||
},
|
||||
{
|
||||
label: 'Mit Windows starten',
|
||||
label: IS_LINUX ? 'Mit dem System starten' : 'Mit Windows starten',
|
||||
type: 'checkbox',
|
||||
checked: autostart,
|
||||
click: (item) => setAutostart(item.checked),
|
||||
@@ -186,7 +237,9 @@ function buildTrayMenu() {
|
||||
}
|
||||
|
||||
function createTray() {
|
||||
const icon = nativeImage.createFromPath(path.join(ROOT, 'assets', 'tray.png'));
|
||||
// In der GNOME-Leiste wirkt das 16-px-Symbol ausgefranst — dort das größere.
|
||||
const file = IS_LINUX ? 'tray@32.png' : 'tray.png';
|
||||
const icon = nativeImage.createFromPath(path.join(ROOT, 'assets', file));
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip('Claude Live Dashboard');
|
||||
tray.on('click', toggleWindow);
|
||||
@@ -297,7 +350,8 @@ function registerIpc() {
|
||||
config,
|
||||
meta: {
|
||||
configPath: CONFIG_FILE,
|
||||
autostart: app.getLoginItemSettings().openAtLogin,
|
||||
autostart: getAutostart(),
|
||||
platform: process.platform,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -366,8 +420,3 @@ app.on('window-all-closed', () => {});
|
||||
app.on('before-quit', () => {
|
||||
if (collector) collector.stop();
|
||||
});
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
app.quit();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user