Eingehender Ruf: Firefox-Tab aktivieren und Fenster nativ nach vorn holen

Protokollversion 9: neue Plugin-Aktion activate und der Eintrag
IncomingCallTabTitle in websocket.json. Beginnt eine Leitung zu klingeln,
holt SwyxTray den ersten Firefox-Tab mit passendem Titel ueber das Plugin
nach vorn, ohne ihn neu zu laden.

Das Plugin allein scheitert dabei am Foreground-Lock von Windows: es kann
den Tab aktivieren, das Fenster bleibt aber hinter der gerade fokussierten
Anwendung. FirefoxWindow uebernimmt deshalb den nativen Teil - EnumWindows
auf MozillaWindowClass, minimierte Fenster wiederherstellen, dann
SetForegroundWindow mit AttachThreadInput, notfalls ALT-Tastendruck.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 14:33:51 +02:00
co-authored by Claude Fable 5
parent 6d03d31a95
commit 748687ce1c
8 changed files with 391 additions and 29 deletions
+57 -17
View File
@@ -129,10 +129,20 @@ Beim ersten Start entsteht `%LOCALAPPDATA%\SwyxTray\websocket.json`:
"Token": "E59BF4…",
"AllowedOrigins": [],
"MaxConnections": 8,
"AllowRemoteAccess": false
"AllowRemoteAccess": false,
"IncomingCallTabTitle": "SwyxWeb"
}
```
`IncomingCallTabTitle` (seit Protokollversion 9, fehlt der Eintrag, gilt
`"SwyxWeb"`): Beginnt eine Leitung zu klingeln, holt SwyxTray den ersten
Firefox-Tab, dessen Titel diesen Text enthält (Groß-/Kleinschreibung ohne
Belang, trifft also z. B. „SwyxWeb · STADTBOTE"), über das Plugin nach vorn —
ohne ihn neu zu laden, damit der Web-Client seine WebSocket-Verbindung behält.
Ist kein Plugin verbunden oder kein passender Tab offen, passiert nichts; es
wird bewusst kein neuer Tab geöffnet. Ein leerer Eintrag (`""`) schaltet das
Verhalten ab.
Solange die Prüfungen abgeschaltet sind (siehe unten), sind `Token` und
`AllowedOrigins` ohne Wirkung — für einen ersten Test ist an dieser Datei nichts
zu tun. Der Rest dieses Abschnitts gilt für den eingeschalteten Zustand.
@@ -170,7 +180,7 @@ ab; in einer bestehenden Datei ohne diesen Eintrag gilt automatisch 17655).
Auf diesem Port läuft **ausschließlich die Tab-Verwaltung** — keine
Snapshots, keine Anruf-Ereignisse, keine Telefonie-Kommandos (sie werden mit
`ok: false` abgewiesen). Nach dem `hello` ist die Richtung umgekehrt zum
Hauptport: **SwyxTray fragt, das Plugin antwortet.** Es gibt drei Aufträge,
Hauptport: **SwyxTray fragt, das Plugin antwortet.** Es gibt vier Aufträge,
jeder wird mit einem `tabresult` samt unveränderter `id` beantwortet:
**Offene Tabs auslesen:**
@@ -196,18 +206,44 @@ jeder wird mit einem `tabresult` samt unveränderter `id` beantwortet:
vom Plugin: {"cmd":"tabresult","id":3,"ok":true}
```
**Tab aktivieren** (seit Protokollversion 9 — nach vorn holen, ohne ihn neu
zu laden; das Plugin setzt das mit `browser.tabs.update(tabId,
{active: true})` samt `browser.windows.update(windowId, {focused: true})`
um):
```json
an das Plugin: {"type":"tab","id":4,"action":"activate","tabId":42}
vom Plugin: {"cmd":"tabresult","id":4,"ok":true}
```
Antwortet das Plugin binnen fünf Sekunden nicht (oder ist keines verbunden),
gilt der Auftrag als gescheitert; die darauf aufbauenden Kommandos des
Hauptports (`focus`, `tabs`, `opentab`, `closetab`) enden dann mit
`ok: false`. Vom Plugin aus sind nur `tabresult` und `ping` zulässig. Ein
„Tab aktivieren“ kennt der Kanal bewusst nicht — beim `focus`-Kommando holt
SwyxTray einen vorhandenen Tab als Komposition nach vorn: `list`, passenden
Tab per `close` schließen und die URL per `open` neu öffnen.
Hauptports (`focus`, `tabs`, `opentab`, `closetab`, `activatetab`) enden dann
mit `ok: false`. Vom Plugin aus sind nur `tabresult` und `ping` zulässig.
Ein älteres Plugin, das `activate` noch nicht kennt, antwortet darauf mit
`ok: false``focus` fällt dann auf den alten Weg zurück (Tab per `close`
schließen und die URL per `open` neu öffnen), das Aktivieren beim
eingehenden Ruf (`IncomingCallTabTitle`, siehe oben) unterbleibt und das
Protokoll vermerkt es.
Das Plugin selbst (SwyxFFPlugin) wird als **eigenes Projekt** gepflegt und ist
nicht Teil dieses Repositories. Sein `background.js` setzt genau diese drei
nicht Teil dieses Repositories. Sein `background.js` setzt genau diese
Aufträge um und verbindet sich selbsttätig neu (1 s Abstand, je Fehlversuch
verdoppelt bis 30 s), falls SwyxTray noch nicht oder nicht mehr läuft.
Für `activate` (seit Protokollversion 9) braucht das Plugin einen weiteren
Zweig in seinem `handleMessage`:
```js
} else if (m.action === "activate") {
if (m.tabId == null) {
reply({ ok: false });
} else {
const tab = await browser.tabs.update(m.tabId, { active: true });
await browser.windows.update(tab.windowId, { focused: true });
reply({ ok: true });
}
}
```
Installation zum Erproben: `about:debugging#/runtime/this-firefox`
*Temporäres Add-on laden…* → die `manifest.json` des Plugin-Projekts wählen.
@@ -229,6 +265,7 @@ wobei die UUID je Installation zufällig vergeben wird — sie steht in
| `tabs` | — | liest die offenen Firefox-Tabs über das Plugin aus |
| `opentab` | `url` | öffnet über das Plugin einen neuen Firefox-Tab |
| `closetab` | `tabId` | schließt über das Plugin den Tab mit dieser Id |
| `activatetab` | `tabId` | holt über das Plugin den Tab mit dieser Id nach vorn, ohne ihn neu zu laden |
| `addresses` | — | liefert die zwischengespeicherten Adressdaten (Telefonbücher), Antwort im Feld `addresses` |
| `contacts` | `query` | durchsucht die Adressdaten des Swyx-Clients (Telefonbücher und Kontakt-Plugins), Antwort im Feld `contacts` |
| `resolve` | `number` | löst eine Rufnummer über die Adressdaten in einen Namen auf, Antwort im Feld `name` (leer = unbekannt) |
@@ -256,21 +293,24 @@ Zustandsmeldungen.
`focus` (seit Protokollversion 3, ab Version 5 ausschließlich über das
**Firefox-Plugin**, siehe unten): war ein passender Tab schon offen — sein
Titel enthält den `title` oder seine Adresse ist die `url` —, wird er
geschlossen und die `url` neu geöffnet (`"focused": true`), sonst nur neu
geöffnet (`"focused": false`). Ist kein Plugin verbunden oder antwortet es
nicht, endet das Kommando mit `ok: false` — einen anderen Weg, eine Seite
anzuzeigen, gibt es nicht. Die `url` ist Pflicht, `title` dient nur dem
Tab-Abgleich; erlaubt sind nur absolute `http(s)`-Adressen.
aktiviert (`"focused": true`; seit Version 9 ohne Neuladen, bei einem
älteren Plugin ohne `activate` ersatzweise wie früher geschlossen und die
`url` neu geöffnet), sonst nur neu geöffnet (`"focused": false`). Ist kein
Plugin verbunden oder antwortet es nicht, endet das Kommando mit
`ok: false` — einen anderen Weg, eine Seite anzuzeigen, gibt es nicht. Die
`url` ist Pflicht, `title` dient nur dem Tab-Abgleich; erlaubt sind nur
absolute `http(s)`-Adressen.
```json
{"id":9,"cmd":"focus","title":"Kundenakte Muster GmbH","url":"https://crm.example.local/kunden/4711"}
{"type":"result","id":9,"ok":true,"focused":false}
```
`tabs`, `opentab`, `closetab` (seit Protokollversion 6): die drei Aufträge des
Tab-Verwaltungskanals stehen damit auch Webseiten offen — SwyxTray reicht sie
unverändert an das Firefox-Plugin durch. Ist kein Plugin verbunden oder
antwortet es nicht binnen fünf Sekunden, endet das Kommando mit `ok: false`.
`tabs`, `opentab`, `closetab` (seit Protokollversion 6), `activatetab` (seit
Version 9): die Aufträge des Tab-Verwaltungskanals stehen damit auch
Webseiten offen — SwyxTray reicht sie unverändert an das Firefox-Plugin
durch. Ist kein Plugin verbunden oder antwortet es nicht binnen fünf
Sekunden, endet das Kommando mit `ok: false`.
Für `opentab` gilt dieselbe Regel wie bei `focus`: nur absolute
`http(s)`-Adressen. Die `tabId` für `closetab` stammt aus einer vorherigen
`tabs`-Antwort; nach Öffnen oder Schließen verschickt die App **keine** neue