Compare commits

..

45 Commits

Author SHA1 Message Date
marcus hinz 9de58d43e8 Archiv als eigenes Fenster: Trilium raus, Notizbaum mit In-Place-Bearbeitung
Trilium-Integration komplett zurückgenommen (Revert von 47951a6): keine
verwaltete Instanz, kein Runtime-Download, kein ETAPI, kein Keyring-Token,
kein Setup-Wizard. Das Archiv sind wieder Markdown-Dateien unter archiveHome
samt FTS5-Suche und Wikilinks.

Notizmodell (an Trilium orientiert, ohne dessen Überbau):
- Jeder Knoten besitzt eine Textdatei — ensure_node_texts legt fehlende beim
  Seitenaufbau an (Ordner: gleichnamiges .md daneben, Wurzel: index.md);
  Ordner und Zwillingsdatei sind EINE Notiz mit Inhalt und Kindern.
- Der angezeigte Titel steht im Frontmatter der Datei, der Datei-/Ordnername
  ist technischer Verwaltungsname; move_folder nimmt den Knotentext mit.

Archiv-Ansicht (wiki-view):
- Baum links: Knoten- und Blatt-Symbole, Führungslinien pro Ebene, Dokumente
  näher an der Linie als Ordner; ganze Zeile wählt aus, nur der Pfeil klappt.
- Notiz-Ansicht rechts: großer Titel (Klick bearbeitet ihn → archive_set_title),
  Zurück-Knopf mit Auswahl-Verlauf, Details als Popup am Info-Knopf, Kindliste
  als abgesetzter Karten-Block mit Anlage-/Änderungsdatum.
- Bearbeiten in place: Split-View aus Rohtext und Live-Vorschau, Speichern per
  Knopf oder Ctrl+S (archive_read/archive_write; Frontmatter bleibt).
- Rechte Maustaste im Baum als einziger Aktionsweg (eigenes Kontextmenü,
  Standard-Menü des Webviews aus); Anlegen/Umbenennen über modale Dialoge.

Feste Flächenaufteilung statt Ablösen/Andocken:
- Angedocktes Panel = Session-Begleiter (ToDo, Befehle, flüchtiger Entwurf);
  der Dokument-Tab ist aus der Leiste raus.
- Archiv-Fenster = Archiv + Suche, Größe und Position einmalig beim Öffnen
  aus dem Monitor des rufenden Terminal-Fensters (85 % / 92 %); Öffner-Klick
  bei stehendem Fenster fokussiert und schaltet den Tab (panel-mode).
- Terminal-Fenster zu → Archiv-Fenster geht mit.

Neue Commands: archive_read, archive_write, archive_set_title.
2026-07-26 14:44:06 +02:00
marcus hinz 47951a615b Trilium als Archiv: verwaltete Instanz pro Projekt, MD-Archiv und Dokument-Tab entfernt
- domain/trilium.rs: Runtime-Download (gepinnt 0.104.0), Instanz pro Projekt auf 127.0.0.1
  mit FNV-Port, ETAPI (archive_note, search_notes), Token im Keyring
- Setup bis aufs Passwort automatisiert: trilium_connect macht new-document (skipDemoDb),
  set-password und ETAPI-Login; Modul-Flag erst nach Token-Erfolg
- write_panel legt direkt eine Trilium-Notiz an (text|path, folder/description/tags);
  noteopen-Kanal, das Terminal-Fenster öffnet die Notiz (ersetzt archive_panel)
- MD-Altbestand raus: archive*/wiki*, archiveHome samt Permissions, panel_set/panel_load/
  wiki_open, panel_archive*/archive_docs/*_archive_home, reveal_path, spellcheck
- Dokument-Tab raus: Panel = ToDo/Befehle/Suche + Archiv-Knopf (trilium_open);
  panel-view/archive-form gelöscht, Markup/CSS/Messages bereinigt
- Lösch-Stufe Archiv: Instanz stoppen, Trilium-Datadir und Keyring-Token löschen
2026-07-24 21:42:03 +02:00
marcus hinz f58b9887c7 Archiv-Navigator: Baum nur Ordner + flache Inhaltsliste (Zeilen: öffnen/umbenennen/löschen), Ordner/Dokument anlegen (Dateiname ohne Stempel, Datum im Frontmatter), Write-back geöffneter Archiv-Dokumente über Quell-Verknüpfung; Ablösen übergibt aktiven Tab 2026-07-23 21:17:41 +02:00
marcus hinz 1523856ea7 Modul-System (Registry Backend+Frontend, generierte Tabs, Settings-Sektion); ToDo-Modul: persistente Liste mit Anlegen/Bearbeiten im Panel (todos_add/todos_update, Formular mit Abbrechen); alte Hook-Todoliste (OFFENE-PUNKTE.md) komplett entfernt 2026-07-23 11:35:29 +02:00
marcus hinz 513e1a768f Dopplungen konsolidiert: settings.json-Permissions-Helfer, pool.json-Lesen über read_pool, gemeinsame Helfer für Pool-Anlage, Projekt-Config, Icon-Übernahme und Running-Guard 2026-07-22 12:58:38 +02:00
marcus hinz f7a99b1176 README: Windows-follows wieder rein, nur Installer-Erwähnungen bleiben draußen 2026-07-22 12:38:00 +02:00
marcus hinz 8e95332552 README: Windows und AppImage gestrichen — beides wird nicht ausgeliefert 2026-07-22 12:35:54 +02:00
marcus hinz 704255e471 README: Screenshots ergänzt — Linux-Tray-Popups (GNOME/Cinnamon), Panel-Entwurf, Befehlskacheln, Archiv-Suche
Aufgenommen auf der VM-Flotte (0.3.0, echte App): Popups nach Auto-
Aktivierung der paketierten Extensions; Panel-Shots über ein Demo-Projekt
mit Inhalten über die Panel-Kanaldateien.
2026-07-22 12:04:48 +02:00
marcus hinz 257b2ca2ef Cinnamon-Extension automatisch aktivieren; README: Desktop-Integration explizit
- Neu cinnamon.rs: erweitert org.cinnamon enabled-extensions idempotent um
  ai-control-popup@local; Aufruf im SNI-Zweig von init_tray (is_cinnamon).
- README: GNOME-/Cinnamon-Extension und KWin-Script mit Pfad und Aufgabe
  benannt, Auto-Aktivierung dokumentiert (inkl. GNOME-Randfall nach
  Erstinstallation); Cinnamon-X11-Aussage korrigiert (Initial-Fokus).
2026-07-22 11:22:44 +02:00
marcusH f13186e667 macos: unbenutzte Konstante MOVE_DISMISSED entfernt
Der Settings-Key steht in settings.rs, die Konstante war ein
Ueberbleibsel und hat beim Release-Build gewarnt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 10:55:12 +02:00
marcusH ea08563411 Version 0.3.0, Selbstinstallation nach ~/Applications, Gatekeeper im README
Erster Start aus DMG oder Downloads bietet einmalig an, sich nach
~/Applications zu kopieren und von dort neu zu starten — der Weg dorthin
braucht kein Administrator-Passwort, anders als /Applications. Ein DMG
kann das nicht selbst leisten: der Applications-Alias darin ist ein
absoluter Symlink auf /Applications.

Die Kopie erbt per ditto die Quarantäne des Downloads, deshalb wird sie
am Ziel gelöst — sonst käme beim Neustart erneut die Gatekeeper-Warnung
für dieselbe, gerade laufende App. Ablehnen merkt sich die App
(moveOfferDismissed), im Debug-Build fragt sie nie.

README: ~/Applications als gleichwertiger Installationsort, dazu der
Weg über System-Einstellungen -> Datenschutz & Sicherheit fuer alle, die
kein Terminal aufmachen wollen.

0.8 ist fuer den Windows-Stand reserviert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 10:47:36 +02:00
marcusH 0faa1e48fd README: Anthropic-Abschnitt auf zwei klare Aussagen gebracht
"Claude and Anthropic" führt jetzt mit den beiden Punkten, auf die es
ankommt: keine Verbindung zu Anthropic, und kein Einfluss auf Lizenz
oder Nutzung — was mit Claude Code erlaubt ist, ist es hier auch, nicht
mehr und nicht weniger; jede Anfrage läuft unter dem eigenen Login.

Die Belege bleiben darunter: keine Tokens in der App, /login macht
claude selbst, Credentials im Keychain, Warnung vor dem Datei-Fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 10:04:43 +02:00
marcusH a6dd567c0a README: Architekturübersicht, CSP-Zeile nachgezogen
Neuer Abschnitt Architecture: die drei Prozessrollen des einen Binaries
(aus Project layout hierher gezogen, dort steht jetzt ein Verweis), der
gemeinsame Look über die Plattformen und der Anchor-Kontrakt als
Tabelle — ein Codepfad show_popup, drei Herkünfte der Platzierung.

Dazu der ehrliche Linux-Absatz: Desktop x Sessiontyp x Tray-Host ist
eine breite Matrix, die kein Release ganz testet; auf einer ungetesteten
Kombination läuft das Popup, kann aber beim ersten Mal woanders landen
als am Icon.

Die CSP-Zeile im Sicherheitsmodell nennt jetzt style-src ohne
unsafe-inline und den Grund, warum die Fenster-Styles in eigenen
CSS-Dateien liegen. An tauri.conf.json gegengelesen: unsafe-inline gibt
es nur in der devCsp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:56:08 +02:00
marcusH 4bc4174d24 README: Verbrauch und Ein-Klick-Session in die Feature-Liste
Beides konnte die App längst, stand aber nur weiter unten zwischen den
Konzepten — für den Verbrauch gab es sogar einen Screenshot, aber keine
Zeile Text.

Zeitraum und Kostenangabe an der UI abgelesen: UsageList bietet 7 oder
30 Tage, die Spalte weist die Kosten als Schätzung aus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:47:55 +02:00
marcusH df828daea9 Pools können auf ein vorhandenes Config-Verzeichnis verweisen
Wer claude schon benutzt, hat einen fertigen Login in ~/.claude. Bisher
begann ai-control trotzdem bei null: jeder Pool war ein neu angelegtes
Verzeichnis, der erste Sessionstart verlangte /login. Ein Pool kann jetzt
stattdessen auf ein bestehendes Verzeichnis zeigen.

pool.json trägt dafür optional `dir` (home-kontrahiert). Der Pool-Ordner
ist dann nur die Hülle für diese Datei; pool_config_dir() löst auf und
ist die einzige Stelle, über die Sessionstart, Verbrauchsstatistik,
Panel-Provisionierung und Keychain-Name gehen.

Zeigt ein Pool auf claudes Default-Verzeichnis, bleibt CLAUDE_CONFIG_DIR
ungesetzt: claude nimmt dieses Verzeichnis von sich aus und damit den
unsuffixierten Keychain-Eintrag, an dem der bestehende Login hängt (am
Keychain gegengeprüft — "Claude Code-credentials" ohne Suffix neben den
suffixierten Pool-Einträgen). Ein Verweis auf ein anderes Verzeichnis
setzt die Variable wie gewohnt.

Das verwiesene Verzeichnis gehört der App nicht: dort entsteht nur die
Panel-Ausstattung (Skill, MCP-Eintrag, Freigabe-Ergänzung, alles
additiv), keine settings.json-Grundausstattung. Löschen trifft nur die
Hülle; Runtime-Symlinks und das Zurücksetzen des Keychain-Eintrags sind
gesperrt. Ein Verzeichnis kann nur ein Pool belegen, Ziele innerhalb von
pools/ sind ausgeschlossen.

In der UI erscheint der Knopf nur, wenn ~/.claude existiert und noch
kein Pool darauf zeigt; die Liste zeigt den Pfad unter dem Namen und
statt "Zurücksetzen" den Hinweis, dass die Anmeldung claude gehört.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:43:00 +02:00
marcusH 6b4afd2bc5 Name aICentral: README neu, sichtbare Titel umgestellt
README trägt jetzt aICentral mit der Tagline "Get Claude Code organized"
als eigene Zeile, dazu ein Hinweis auf die Unabhängigkeit von Anthropic
(Claude/Claude Code als fremde Marken, rein beschreibend genannt).

Sichtbare Stellen mitgezogen: Hauptfenster-Titel, Überschrift der
Verwaltung, Seitentitel, Linux-Tray-Titel und der AppImage-Hinweis
unter GNOME. Technische Bezeichner bleiben ai-control — Binary,
Identifier, SNI-Id und der Popup-Fenstertitel, auf den KWin-Script und
GNOME-Extension matchen.

Außerdem im README: deploy-macos.sh und deploy-windows.ps1 ergänzt, die
Installationswege pro OS beschrieben (macOS nach ~/Applications, das
Build-Bundle wandert danach nach macos.noindex), Panel-Tabs englisch
benannt und die Zweisprachigkeit der Oberfläche erwähnt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:10:47 +02:00
marcus hinz e7f98dd072 CSP: style-src ohne unsafe-inline — Fenster-Styles aus den HTML-Köpfen nach src/*-window.css
terminal/panel/popup.html laden ihre Styles jetzt als erste Imports der
jeweiligen Entries (Kaskade wie zuvor: Fenster-CSS vor xterm/fonts); devCsp
behält unsafe-inline für die Vite-HMR-Style-Injektion.
2026-07-21 19:04:36 +02:00
marcus hinz bbb06e0814 Popup auf SNI-Desktops: Activate-Koordinaten (Anchor::Click) + Cinnamon-Extension
- SNI-Activate-Koordinaten gehen als Anchor::Click durch; Cinnamon liefert
  echte Werte, bei (0,0) bleibt der Cursor-Fallback (KDE/XFCE).
- Neue Cinnamon-Extension ai-control-popup@local: platziert das Popup unter
  Wayland am Zeiger (Work-Area-geklemmt, set_position ist dort wirkungslos)
  und gibt ihm unter X11 den initialen Fokus (Muffins Focus-Stealing-
  Prevention verwirft das present() der App).
- Extension in deb/rpm nach /usr/share/cinnamon/extensions paketiert.
- README: Cinnamon-Abschnitte und Installer-Teststand aktualisiert.

Verifiziert in der VM-Flotte: Cinnamon Wayland + X11, XFCE, KDE (Wayland),
GNOME (Wayland).
2026-07-21 11:24:18 +02:00
marcusH 1fa9ffcf1b i18n bis in die Terminal- und Panel-Fenster
Uebersetzt waren bisher nur die Vue-Fenster; Terminal, Panel und Popup hatten
ihre Beschriftungen hart auf Deutsch im Markup und in den Views -- ein
englisches UI blieb zur Haelfte deutsch.

Die Tabellen ziehen nach messages.ts um, dazu ein Vue-freier t() und ein
applyI18n(), das statisches Markup ueber data-i18n-Marker beschriftet.
i18n.ts bleibt der vue-i18n-Wrapper fuer die Vue-Fenster. So kommt kein Vue
in die Terminal- und Panel-Bundles (Terminal 657,02 -> 657,06 kB).
Neue Namensraeume: panel, popup, commands, archiveForm, search, wiki.
toLocaleTimeString folgt der Sprache statt fest 'de'.

Zwei Funde nebenbei:
- window.localStorage wird ueber try/catch gelesen. Gesperrter Storage
  (privater Modus) wirft schon beim Lesen, und unter Node 22 ueberschattet
  ein undefiniertes globales localStorage das der Testumgebung.
- Die Frontend-Tests liefen unbemerkt gegen happy-doms en-US.
  test-setup.ts nagelt die Sprache fest.

49 Tests (39 bestehende, 10 neue fuer Tabellen-Paritaet, t() und applyI18n).
2026-07-20 22:36:19 +02:00
marcusH 92f34cb64d macOS: API-Key nicht mehr ueber die Kommandozeile an security
add-generic-password bekam den Key als -w-Argument; damit stand er fuer die
Dauer des Aufrufs in der Prozessliste und war per ps fuer jeden Nutzer des
Rechners lesbar. Jetzt kommt -w ohne Wert, security liest das Secret ueber
stdin (zweimal, Eingabe und Bestaetigung). Die Eingabeaufforderungen landen
auf stderr und werden aus der Fehlermeldung geschnitten.
2026-07-20 22:17:21 +02:00
marcusH e15e128fd3 deploy-macos.sh installiert nach ~/Applications
Bisher hat das Skript nur das Dev-Bundle aus Launch Services ausgetragen und
den DMG-Pfad ausgegeben; installiert wurde von Hand. Linux und Windows
installieren in ihren Deploy-Skripten, macOS war der Ausreisser.

Jetzt: alte Kopie austragen und loeschen (ditto merget sonst in ein
bestehendes Bundle hinein), neues Bundle per ditto nach ~/Applications,
lsregister -f. Guards vorweg gegen fehlendes Bundle und laufende Instanz --
eine laufende App haelt Dateien offen und ergaebe ein halb ueberschriebenes
Bundle.
2026-07-20 22:08:35 +02:00
marcus hinz e6226cfc9f README überarbeitet: aiCentral-Branding, alle sechs MCP-Tools, UUID-Registry, Sicherheitsmodell (CSP/Capabilities), dreistufiges Projekt-Löschen, Build/Deploy-Stand, Installer-Teststand mit offenen X11-Befunden 2026-07-20 20:43:09 +02:00
marcus hinz cf2c25df79 Archiv-Wechsel mit Migrations-Option
Bei gesetztem Archiv-Home fragt ein Dialog nach: Dokumente ins neue
Archiv verschieben (Checkbox, standardmäßig aus — nichts wandert
implizit) oder liegen lassen. change_archive_home_cmd setzt das neue
Home, zieht auf Wunsch die Einträge um (gleiche Platte rename, sonst
Kopie + Löschen; Kollision im Ziel bricht laut ab) und nimmt die Rechte
des alten Ordners zurück.
2026-07-20 13:48:49 +02:00
marcus hinz 347fc70982 Veraltete autogenerierte Permissions entfernt (remove_project, delete_project) 2026-07-20 13:37:34 +02:00
marcus hinz 6c765ff7b5 Projekt löschen in drei Stufen mit Artefakt-Vorschau; Listen-Scroll gefixt
Eskalationsleiter: 'integration' nimmt nur die ai-control-Spuren zurück
(Registry, .ai-control/, Archiv-Rechte + Todo-Hook in settings.json,
Panel-Dateien, .desktop — Ordner, memory/ und Claude-Code-Grundgerüst
bleiben), 'archive' löscht zusätzlich das Archiv, 'full' den Projekt-
ordner mit Arbeitsordner-Flag. delete_preview liefert die konkrete
Artefaktliste; der Dialog zeigt sie je Stufe. Ersetzt delete_project/
remove_project (Handler, App-ACL-Manifest und Capability nachgezogen).

Außerdem: main scrollt nicht mehr — Toolbars und Fehlerzeile stehen,
nur die Listen scrollen (alle drei Tabs, .list-scroll).
2026-07-20 13:37:02 +02:00
marcus hinz 6682040cc0 Command-History: stabile ID je Eintrag statt Index+Text-Abgleich
write_commands vergibt jedem Eintrag eine UUID; commands_delete löscht
darüber. Doppelklick oder ein zweites Fenster laufen ins 'bereits
entfernt' statt auf verschobene Indizes.
2026-07-20 13:37:02 +02:00
marcus hinz ab1c561d6c Robustheit: App-Settings-Lesepfad, Pool-Round-Trip, Archiv per create_new
- write_app_setting: nur eine fehlende Datei ergibt ein frisches Objekt;
  unlesbares/kaputtes JSON bricht ab, statt die übrigen App-Settings zu
  verwerfen.
- struct Pool reicht unbekannte Keys per serde(flatten) durch; Umbenennen
  schreibt den gelesenen Bestand zurück.
- Archiv-Dokumente entstehen über create_new (Kollisionsgarantie vom
  Dateisystem statt exists()-Vorabblick — TOCTOU beim Maschinen-Sync).
2026-07-20 13:37:02 +02:00
marcus hinz 446f52ca6d Settings-Dialog: feste Höhe min(60rem, 94vh) 2026-07-20 12:20:10 +02:00
marcus hinz 8068471120 Settings-Dialog: fester Kopf/Fuß mit scrollendem Mittelteil, Feinschliff
Breite min(48rem, 94vw), Schließen-Kreuz, kräftigerer Rahmen; Kopfzeile
und Aktionsleiste stehen fest, nur der Gruppen-Mittelteil scrollt, wenn
das Fenster niedriger ist als der Inhalt. Todo-Zeile über die volle
Dialogbreite; Aktions-Buttons einheitlich 10.5rem.
2026-07-20 12:01:21 +02:00
marcus hinz b743de5f34 Sicherheitsrunde II: PTY-Label, Tool-Grenzen, atomare Schreibvorgänge
- term_start nur noch für das eigene term-<projekt>-Fenster — das
  (abgelöste) Panel-Fenster kann keine Shell mehr starten und beschreiben.
  Die Tauri-ACL greift für App-Commands nachweislich nicht (kein
  __app-acl__-Manifest, Prüfung gegen tauri 2.11.5); die Label-Prüfung
  ist damit die Schranke, nicht deren Verdopplung.
- archive_panel verliert das dir-Argument: Archiv-Home samt dauerhafter
  Rechtevergabe (additionalDirectories + Edit-Allow) setzt nur noch die
  UI mit Nutzer-Dialog, kein Tool-Argument des Modells.
- write_panel(path) liest nur noch aus Projekt-, Arbeits- und
  Archiv-Ordnern (canonicalize, symlink-fest), reguläre Dateien bis 2 MB
  mit hartem Byte-Cap — der promptfreie Weg umgeht das
  Read-Permission-Modell von Claude Code nicht mehr.
- write_atomic (temp + rename) für alle JSON-Config-Schreiber: Projekt-
  Config, .claude/settings.json, Registry, App-Settings, pool.json,
  Pool-settings.json, claudes .claude.json, Todo-Hook.
- write_secret_file: create_new mit 0600 statt schreiben-und-abdichten;
  kein umask-Fenster, Symlinks werden nicht gefolgt.
- PTY-Umgebung: geerbte ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN entfernt.
- panel_file & Geschwister prüfen die Projekt-ID (zweite Linie).
- Befehlskacheln filtern Bidi-/Zero-Width-Zeichen aus Anzeige und
  Zwischenablage; der Lösch-Abgleich behält den Originaltext.
2026-07-20 12:01:21 +02:00
marcus hinz 23f6562f50 Settings-Dialog neu: drei Gruppen, festes Zeilen-Grid, Sofort-Schreiber abgesetzt
Darstellung (Titel/Theme/Dock-Icon), Ordner (Projekt/Arbeitsordner/Archiv)
und Session (Todoliste) als Gruppen mit Eyebrow-Überschriften; jede Zeile
im Grid Label | Wert | Aktionen. Pfade ~-kontrahiert mit vollem Pfad im
Hover-Pop. Die Ordner-Gruppe schreibt sofort und ist als eigene Fläche
abgesetzt (eigener Hinweis); „gilt ab nächstem Start" hängt an der
Darstellungs-Gruppe statt global. Nur ProjectList.vue + i18n-Schlüssel.
2026-07-20 11:06:40 +02:00
marcus hinz a1c95dae4a Build und Deploy getrennt: gemeinsames build.sh, Deploy-Skripte pro OS
build.sh baut nur noch (OS-Weiche für die Bundle-Liste: macOS app,dmg,
Linux deb,rpm, Git-Bash nsis); die macOS-Nachbereitung (lsregister,
.noindex) zieht nach deploy-macos.sh um. deploy-linux.sh installiert das
jüngste Bundle (dnf: rpm -Uvh --force, apt: dpkg -i),
deploy-windows.ps1 startet den NSIS-Installer.
2026-07-20 11:01:33 +02:00
marcus hinz b9d006184a Projekt-Identität und -Config neu: .ai-control/config.json mit UUID und Name
- Projekt-Config zieht von ai-control.json nach .ai-control/config.json um,
  das Icon liegt daneben und synct mit dem Projekt.
- Jedes Projekt trägt eine UUID (id) und den Anzeigenamen (name) in der
  Config; die UUID ist Registry-Schlüssel und project-Parameter aller
  Commands, der Name reine Darstellung.
- Pool-Zuordnung ist maschinenlokal und liegt in der projects.json-Registry,
  nicht mehr in der syncbaren Projekt-Config.
- open_terminal prüft die ID im Ordner gegen die Registry (verschobene oder
  ersetzte Ordner scheitern laut).
- Import übernimmt eine mitgebrachte ID, legt Arbeitsordner und Archiv-Home
  an und zieht die Archiv-Permission nach.
- Migration beim App-Start: Alt-Datei umziehen, Pool in die Registry,
  Icons aus ~/.config/ai-control/icons ins Projekt, Registry auf UUIDs.
- ProjectConfig reicht unbekannte Keys per serde(flatten) durch den
  Round-Trip (Regressionstest config_roundtrip_erhaelt_fremde_keys).
2026-07-20 11:01:33 +02:00
marcusH 6d44f77671 Sicherheitsrunde: Befehlsinjektion, Pool-Pfad, Namensprüfung, Bild-Beacon
Vier Befunde aus einer Prüfung entlang der Angriffsflächen (MCP-Tools,
Command-Grenze, Credentials, Schreibpfade und Abhängigkeiten):

- todo.rs: Der SessionStart-Hook interpolierte den Projektpfad unquotiert in
  eine Shell-Kommandozeile, die Claude Code bei jedem Sessionstart ausfuehrt.
  Ein Ordner `repo$(...)` -- etwa aus einem geklonten Fremd-Repo -- ergab
  dauerhafte Codeausfuehrung; ein Leerzeichen zerbrach den Hook still.
  Der Pfad wird jetzt gequotet, inklusive Apostroph.
- project_pool_dir: Der Pool-Name kommt aus der ai-control.json im
  Projektordner, also aus einer versionierten Datei. Ungeprueft bestimmte er
  mit ../ ein beliebiges Verzeichnis als CLAUDE_CONFIG_DIR, dessen
  settings.json wiederum apiKeyHelper traegt -- ein Kommando, das beim
  Sessionstart laeuft. Jetzt durch check_name abgesichert.
- check_name: Steuerzeichen, Backslash und fuehrender Punkt werden abgelehnt.
  Ein Newline im Projektnamen hing bisher eigene Schluessel an die .desktop-
  Datei; Exec= braucht keinen Schraegstrich, den der alte Check verbot.
- markdown.ts: Bilder duerfen nur noch lokale Ziele laden. Ein auswaertiges
  <img> laedt ohne Zutun und meldet damit IP, Zeitpunkt und im Pfad kodierte
  Daten -- der Panelinhalt stammt aus einer LLM-Session, ist also
  injizierbar. Schema-relative URLs (//host/x) waren durch ein zu laxes
  Muster erfasst. Links bleiben auswaerts erlaubt, jetzt mit
  rel="noopener noreferrer". escapeHtml escaped zusaetzlich das Apostroph.

Dazu cargo audit nachgeholt: plist 1.9.0 -> 1.10.0 hebt quick-xml auf 0.41.0
und schliesst RUSTSEC-2026-0194/0195 fuer den Laufzeitpfad. Die zweite
Instanz von quick-xml 0.39.4 bleibt ueber wayland-scanner im Lockfile --
ein proc-macro, das nur zur Compile-Zeit laeuft und ausgelieferte
Protokoll-XMLs parst, nicht im Binary landet.

77 Rust-Tests, 39 Frontend-Tests.
2026-07-19 23:47:29 +02:00
marcusH e245600235 Aufräumrunde zur Bugfix-Runde: Suche entzerrt, Flatten-Fix vervollständigt
- archive_search: match_expr in search() aufgelöst. sanitize_query lief zweimal
  pro Anfrage, und die Leer-Entscheidung stand an zwei Stellen mit leicht
  abweichender Bedingung.
- Der Tag-Filter geht jetzt durch dieselbe Quoting-Regel wie der Volltext. Ein
  leerer Tag ergab tags:"" und damit einen Syntaxfehler — erreichbar durch das
  angetippte #, den ersten Tastendruck jeder Tag-Suche.
- sanitize_query von 26 auf 12 Zeilen: split('"') und split_whitespace statt
  handgeschriebenem Tokenizer. Gleiches Ergebnis fuer alle Testeingaben.
- TerminalConfig bekommt ebenfalls serde flatten. Der Block wird als Ganzes
  mitgeschrieben, ein terminal.fontSize waere also weiterhin verloren gegangen;
  set_terminal_config_in traegt die unbekannten Keys jetzt herueber.
- markdown.test.ts enthielt ein literales NUL-Byte, wodurch git die Datei als
  binaer behandelte. Der Test erzeugt das Zeichen jetzt selbst.
- Kleineres: commands_delete-Bedingung als direkter Vergleich, eine
  Verschachtelungsebene in pool.rs, zwei redundante Kommentarbloecke.
  commands_delete benennt sich in der Doku als Provisorium.
2026-07-19 23:22:25 +02:00
marcusH b127269e89 Bugfix-Runde Panel/Archiv: Datenverlust, Suche, Löschen, Markdown
- ProjectConfig reicht unbekannte Keys durch (serde flatten). Bisher verlor
  jeder read-modify-write die Felder neuerer Versionen; so ging archiveHome
  bei der Pool-Zuweisung durch einen älteren Build verloren.
- Archiv-Suche: Nutzereingabe wird in einen FTS5-Ausdruck übersetzt, der nicht
  scheitern kann. ai-control, C++ oder eine halbe Phrase warfen bisher einen
  Syntaxfehler — bei der Live-Suche in jedem Tippzwischenstand.
- commands_delete: Befehlstext als Identität, Indizes werden geprüft. Bisher
  Panik oder Löschen der falschen Kachel, wenn die Panel-Indizes der Datei
  hinterherhingen.
- Archivieren überschreibt keine Datei mehr: Der Stempel hat Minutenauflösung,
  bei Kollision wird -2, -3, ... angehängt.
- archive_panel prüft erst auf leeres Panel, dann wird das Archiv-Home gesetzt;
  bisher blieb ein umkonfiguriertes Home trotz Meldung "nicht archiviert".
- Kaputte .claude.json eines Pools wird nicht mehr durch eine frische ersetzt;
  nur eine fehlende Datei rechtfertigt das.
- Archiv-Formular leert beim Schließen — sonst galten Ordner und Schlagwörter
  des vorigen Dokuments unbemerkt fürs nächste.
- Markdown fürs Panel entschärft (src/markdown.ts): rohes HTML wird als Text
  ausgegeben, Link- und Bildziele auf harmlose Schemata beschränkt. Das Panel
  teilt sich den Webview mit dem Terminal, dessen term_write in die PTY
  schreibt; ohne CSP war das ein Weg von Fremdinhalt zu Befehlsausführung.

Regressionstests je Fix: 73 Rust-Tests, 35 Frontend-Tests.
2026-07-19 23:04:17 +02:00
marcus hinz 1345ef799c panel-update: erst auf Dokument schalten, dann Inhalt setzen
Der Modus-Umschalter restauriert beim Wechsel den gemerkten Dokument-Titel;
kam ein Dokument aus der Suche, überschrieb das den frisch gesetzten Titel —
der Wiki-Sprung lief damit ins Leere.
2026-07-19 17:21:26 +02:00
marcus hinz 286513def8 Refinement-Runde Panel/Wiki: Bedienung, Themes, Archiv-Wahl, Frontend-Tests
- Slug transliteriert Umlaute/ß (fuer statt f-r); aktiver Tab mit Akzent +
  Unterstrich; kein aktiver Tab bei zugeklapptem Panel (mode.clear)
- Suche: Live-Update auch beim Löschen (<3 Zeichen: Hinweis), #tag-Filter in
  der Query, Treffer öffnen im Dokument-Tab (panel_load, ohne Frontmatter);
  Wiki-Sprung-Button im Dokument; Wiki-Fehler sichtbar als Toast
- Archiv-Formular am Archiv-Button (Ordner/Beschreibung/Schlagwörter);
  panel_archive_cmd nimmt Meta entgegen
- Wiki-Übersicht: Zuletzt-Sektion, Backlink-Zähler, Wurzel-Eyebrow
- Themes: Panel-Farben über CSS-Variablen, applyTheme leitet sie aus dem
  jeweiligen Theme ab (auch helle); gemeinsames themes.ts
- Fenster: Doppelklick maximiert; gelöstes Fenster merkt Größe/Position
  (Window-ACL: set-position/outer-position/inner-size ergänzt);
  open_panel_window async (GTK-Deadlock-Vorgabe); Panel-Fenster spiegelt
  JS-Fehler ins Dev-Log
- Archiv pro Projekt wähl-/abwählbar (Einstellungsdialog, setzt/entfernt
  Permissions); require_archive_home legt den Ordner an; ohne Archiv nur
  Befehle+Dokument im Panel
- Frontend-Tests: vitest + happy-dom, 27 Tests über Modus-, Such-, Wiki-,
  Editor- und Formular-Logik (npm test)
2026-07-19 17:08:05 +02:00
marcus hinz 81a663e020 Panel-Feinschliff: Fensterrahmen, Live-Suche, Min/Max im gelösten Fenster
- 1-px-Fensterrahmen in Theme-Rahmenfarbe für die rahmenlosen Fenster (Linux)
- Suchfeld: Live-Suche ab 3 Zeichen mit 300-ms-Debounce, letztes Wort als
  Präfix (arch → arch*); Enter sucht wörtlich (exakte FTS-Syntax)
- Gelöstes Fenster: Minimieren/Maximieren neben Andocken/Schließen
2026-07-19 15:32:05 +02:00
marcus hinz 994d5070f0 Archiv-Wiki im Panel: vier Tabs, FTS5-Suche, strukturierbares Archiv
- Archiv-Home pro Projekt (archiveHome in ai-control.json); archive_panel
  mit Ordner, Beschreibung und Schlagwörtern (Frontmatter + Zeitstempel-Stem)
- Archiv-Index (archive_index.rs): Scan mit Frontmatter, Wikilinks,
  Backlinks; Wiki-Seiten als strukturierte Daten (Übersicht/Tag-Seiten)
- FTS5-Suche (archive_search.rs, rusqlite bundled): in-memory-Index pro
  Anfrage; MCP search_archive und Panel-Suchfeld (search_run) teilen die Engine
- Panel: vier Puffer/Ansichten (Dokument, Befehle, Suche, Wiki) mit Tabs im
  Fenster-Header; gemeinsame Verdrahtung panel-wiring.ts, Kachel-CSS,
  Wiki-View mit Tag-Chips, Ordner-Sektionen und Backlinks
- Archivieren speichert offene Bearbeitung (flush); Tab-Wechsel speichert
  statt zu blockieren; Fehler sichtbar als Panel-Toast
- Abgelöstes Fenster: rahmenlos (Linux), eigene Kopfleiste mit Tabs und
  Fensterknöpfen, Theme-Kopplung über gemeinsames themes.ts
- MCP-Tools show_commands, show_archive; Befehls-History mit Kachel-Löschen
2026-07-19 15:25:40 +02:00
marcus hinz d4d455fa08 Popup-Fokus auf SNI-X11-Desktops: present() nach jedem Map
Muffin (Cinnamon) mappt das Popup ohne Fokus — das set_focus aus show_popup
läuft vor dem Map und fällt der Focus-Stealing-Prevention zum Opfer; ohne
initialen Fokus greift das Blur-Schließen nie. Ein map-event-Handler ruft
present(), GTK besorgt sich unter X11 dabei den echten Server-Timestamp.

Registriert nur unter X11 außerhalb von GNOME: auf GNOME/Wayland zeigt die
Extension das Popup, dort brach das Fokus-Grab den Klick-Weg (Ubuntu/Kubuntu).
Bestätigt auf Mint/Cinnamon, Ubuntu und Kubuntu.
2026-07-17 19:37:05 +02:00
marcus hinz be1e4aad3f Popup-Blur-Guard ohne Timer: Sperre fällt mit dem ersten Focused(true)
Bisher löste ein Thread die Sperre nach 350 ms (geraten gegen das
spuriose Focused(false), das KDE direkt nach show/set_focus feuert).
Jetzt steht die Sperre ab dem Anzeigen und fällt deterministisch mit
dem ersten echten Fokus-Event des Popups.
2026-07-17 00:17:17 +02:00
marcus hinz 3fe911040d KWin-Script lauffähig unter Plasma 6: KPackageStructure + frameGeometry-Objekt
- metadata.json: ohne KPackageStructure=KWin/Script verwirft KWin 6 das
  Paket schon beim Einsammeln (isScriptLoaded blieb false)
- main.js: KWin-Scripts laufen in reiner QJSEngine ohne QML-Globals,
  Qt.rect warf 'Qt is not defined' — die Engine konvertiert ein
  {x,y,width,height}-Objekt selbst nach QRectF
2026-07-16 20:47:23 +02:00
marcus hinz 8aba0eb315 GNOME-Extension: Popup schließt bei Klick auf Desktop/Shell-Chrome
Klick auf leere Fläche wechselt den Fensterfokus nicht, der Blur-Pfad
der App greift dort nicht. Solange das Popup offen ist, fängt ein
captured-event-Handler am Stage solche Klicks ab und relayt Hide per
D-Bus; Klicks auf den Panel-Button toggeln weiter selbst.
2026-07-15 20:10:33 +02:00
marcus hinz 9a99bca466 Modul-Split: Fachlogik nach domain/, OS-Code nach platform/, Tauri-Verdrahtung in app.rs/commands.rs
- lib.rs nur noch Prozessrollen-Dispatch + Panic-Tracer
- KWin-Script als KDE-Pendant zur GNOME-Extension (Popup ans Zeiger-
  Rechteck), wird von deb/rpm mit ausgeliefert
- SNI-Tray auf ksni (pure Rust), appindicator-Depends entfallen
2026-07-15 20:10:33 +02:00
177 changed files with 17742 additions and 5241 deletions
+170 -42
View File
@@ -1,21 +1,59 @@
# ai-control # aICentral
Pool and session management for [Claude Code](https://claude.com/claude-code) on macOS — a tray app with a built-in terminal. Linux and Windows integration to follow. **Get Claude Code organized**
Pool, project, and session management for [Claude Code](https://claude.com/claude-code) — a tray app with a built-in terminal, a draft panel, and a per-project document archive. macOS and Linux; Windows support follows. (Repository and binary name: `ai-control`.)
> All code in this project was generated by Claude (Claude Code). > All code in this project was generated by Claude (Claude Code).
<img src="docs/projects.png" width="640" alt="Projects"> <img src="docs/projects.png" width="640" alt="Projects">
## Purpose ## What it does
Running Claude Code with multiple accounts or credential sets means hitting the right `CLAUDE_CONFIG_DIR` (plus keychain entry) for every session. ai-control turns that into clicks: - **Several logins, one click apart** — keep separate Claude Code logins side by side (subscription accounts or API keys) and give every project the one it should run under. Each project starts with its own login; no re-login, no juggling environment variables. An existing login in `~/.claude` is picked up as a pool of its own, so nothing has to be set up twice.
- **Command history** — every shell command the assistant hands you lands beside the terminal as a copyable tile, with the session's full command history one click away.
- **Text panel** — Markdown drafts appear next to the terminal instead of scrolling past in the chat: readable, selectable, editable, and copyable in one go.
- **Archive with full-text search** — keep drafts as Markdown files per project, curated with folder, description, and tags; browse them as a wiki and search their full text.
- **What it costs** — tokens and estimated cost per login and project over the last 7 or 30 days, read straight from the session transcripts (retries deduplicated).
- **One click, one session** — the tray popup lists every project with its icon and a green dot when it runs; a click starts it or brings the running terminal to the front.
- **Pools** — named credential sets (OAuth login or API key) under `~/.config/ai-control/pools/<pool>`. Each pool is a self-contained Claude config directory with its own keychain entry. ## How it works
- **Projects** — arbitrary directories, mapped through a registry (`~/.config/ai-control/projects.json`, name → path; paths under home are stored as `~/…` and therefore stable across machines). Each project is assigned a pool, plus terminal settings (theme, icon, window title).
- **Sessions** — a built-in terminal per project (xterm.js + PTY) that launches Claude Code with the pool's config directory as `CLAUDE_CONFIG_DIR`. Every terminal runs as its own process with its own Dock icon and Cmd-Tab entry. Running Claude Code with multiple accounts or credential sets means hitting the right `CLAUDE_CONFIG_DIR` (plus keychain entry) for every session. aICentral turns that into clicks:
- **Tray** — the app itself is a pure menu-bar app without a Dock entry. The tray menu lists all projects with icon and status dot (green = running); clicking starts the project or brings the running terminal to the front.
- **Pools** — named credential sets (OAuth login or API key) under `~/.config/ai-control/pools/<pool>`. Each pool is a self-contained Claude config directory with its own keychain entry. A pool can also *point at* an existing config directory instead: if `~/.claude` is there, it is offered as a pool, keeps its login, and is left otherwise untouched — the app adds panel access there and never deletes anything in it.
- **Projects** — arbitrary directories carrying their own identity in `<project>/.ai-control/config.json` (UUID, display name, terminal settings, archive home — this file syncs with the project). The machine-local registry `~/.config/ai-control/projects.json` maps that UUID to the directory and the assigned pool; paths under home are stored as `~/…` and therefore stable across machines.
- **Sessions** — a built-in terminal per project (xterm.js + PTY) that launches Claude Code with the pool's config directory as `CLAUDE_CONFIG_DIR`. Every terminal runs as its own process — on macOS with its own Dock icon and Cmd-Tab entry, on Linux as its own window with its own app id.
- **Tray** — the app itself is a pure tray app. Clicking the tray icon opens a popup listing all projects with icon and status dot (green = running); clicking a project starts it or brings the running terminal to the front.
- **Text panel** — a workspace beside the terminal with four tabs: command tiles, the current draft, an archive wiki, and archive search. Claude writes into it through a bundled MCP server instead of flooding the chat.
- **Archive** — drafts can be archived as Markdown files into a per-project archive home, curated with folder, description, and tags at archiving time; browsable as a wiki and searchable via full-text search.
- **Session watcher** — detects the end of a session by the terminal process disappearing and then (opt-in) syncs the Git repository containing the project: add → commit → pull --rebase → push. - **Session watcher** — detects the end of a session by the terminal process disappearing and then (opt-in) syncs the Git repository containing the project: add → commit → pull --rebase → push.
## Text panel and MCP tools
The app ships an MCP stdio server (`ai-control --mcp-panel`, server key `text-panel`) and provisions it into every pool, including the tool permission — no per-pool setup. Six tools:
| Tool | Purpose |
|---|---|
| `write_panel` | Place a Markdown draft in the panel instead of printing it as chat prose; accepts inline text or a file path (the server reads the file itself). |
| `write_commands` | List shell commands for the user as copyable tiles (command + optional note), appended to the session's command history. |
| `show_commands` | Show the command history (tile view). |
| `archive_panel` | Save the current draft as a Markdown file into the project's archive home; takes `folder`, `description`, `tags` for the frontmatter. |
| `show_archive` | Render the archive overview as a wiki page — documents grouped by folder, with descriptions and clickable tag links; with `tag`, that tag's page. |
| `search_archive` | FTS5 full-text search over the archive (words, "phrases", prefix*); hits appear as tiles in the panel. |
The panel docks into the terminal window and can be detached into its own window. The header tab bar switches between **Commands** (command tiles), the current **document**, **Wiki**, and **Search** (live search, `#tag` filtering); without a configured archive home the archive tabs stay hidden. The draft's title is taken from its first heading and is editable, as is the content. Spell checking follows `spellcheckLang` (per-text override in the panel).
The archive lives in the project's `archiveHome` (set in `.ai-control/config.json`); documents are plain Markdown files with frontmatter — no database, the search index is built in-memory per query.
![Draft document in the panel beside the terminal](docs/panel-draft.png)
| Command tiles | Archive search |
|---|---|
| ![Command tiles](docs/panel-commands.png) | ![Archive search](docs/panel-search.png) |
The interface speaks English and German across all windows; the language follows the browser locale and can be switched in the app.
## Screenshots ## Screenshots
| Pools | Usage | | Pools | Usage |
@@ -26,87 +64,177 @@ Running Claude Code with multiple accounts or credential sets means hitting the
|---|---| |---|---|
| ![Tray menu](docs/tray.png) | ![Session terminal](docs/terminal.png) | | ![Tray menu](docs/tray.png) | ![Session terminal](docs/terminal.png) |
## Installation
### macOS
Prebuilt DMGs are attached to the releases. Open the DMG and drag `ai-control.app` onto the `Applications` shortcut — or drop it into your personal `~/Applications` instead, which works just as well and asks for no administrator password. On first launch from anywhere else (the DMG, your Downloads folder), the app offers to move itself to `~/Applications` and restarts from there.
The bundles are **not signed or notarized** — an Apple Developer ID costs €99/year, which this project doesn't have. If you'd like to change that: [Buy me a coffee](https://buymeacoffee.com/marcusH). macOS quarantines the unsigned download; clear it with whichever path you installed to:
```sh
xattr -d com.apple.quarantine /Applications/ai-control.app # or
xattr -d com.apple.quarantine ~/Applications/ai-control.app
```
Without the terminal: double-click the app, dismiss the warning, then open **System Settings → Privacy & Security**. Near the bottom of that page macOS now offers *Open Anyway* for the app it just blocked — click it and confirm. The warning is gone for good afterwards.
Or avoid the topic entirely and build from source (below) — self-built apps aren't quarantined.
### Linux
Releases carry a `.deb` and an `.rpm`. Both install three desktop-integration pieces alongside the app: the **GNOME Shell extension** `ai-control-popup@local` (tray button + popup on GNOME, `/usr/share/gnome-shell/extensions/`), the **Cinnamon extension** `ai-control-popup@local` (popup placement on Wayland, initial focus on X11, `/usr/share/cinnamon/extensions/`), and the **KWin script** `ai-control-popup` (popup placement on KDE Wayland, `/usr/share/kwin/scripts/`).
Tray integration by desktop: GNOME via the bundled shell extension, KDE Wayland via StatusNotifierItem plus the bundled KWin script for popup placement, KDE X11/XFCE/Cinnamon via StatusNotifierItem directly. On Cinnamon the bundled extension additionally places the popup at the tray icon on **Wayland** (a Wayland client cannot position its own window) and gives it initial focus on **X11** (Muffin's focus-stealing prevention would otherwise drop it).
The app **enables the matching piece automatically** on each start, per user and without prompting: GNOME via `gnome-extensions enable`, Cinnamon via the `org.cinnamon enabled-extensions` gsettings key, KDE via `kwriteconfig6` + KWin reconfigure. Cinnamon and KDE pick the change up immediately. GNOME Shell only scans for newly installed extensions at session start, so right after the very first package install the enable call can come too early — the tray button then appears after the next login; from then on it is always active.
| Tray popup on GNOME | Tray popup on Cinnamon |
|---|---|
| ![Tray popup on GNOME](docs/linux-gnome-popup.png) | ![Tray popup on Cinnamon](docs/linux-cinnamon-popup.png) |
### Installer tests
The installers are hand-tested per release. Current state: Fedora 44 via rpm on GNOME, KDE Plasma, Cinnamon, and XFCE (VM test fleet); Ubuntu 26.04 (GNOME), Kubuntu (KDE Plasma, Wayland) and Linux Mint (Cinnamon) via deb in earlier rounds. The two open findings from that round are resolved: popup placement on Cinnamon Wayland now comes from the bundled Cinnamon extension, and click-outside-to-close verified fine on XFCE and Cinnamon. macOS re-test of the current build is in progress.
### Windows
Follows.
## Architecture
One binary, three process roles:
- **Main app** (`ai-control`) — tray, main window, management, session watcher.
- **Terminal process** (`ai-control --terminal <project>`) — one window with its own PTY; launches Claude Code in the project directory with the assigned pool's config directory. On macOS it carries its own Dock icon and Cmd-Tab entry, on Linux its own app id and `.desktop` file.
- **MCP server** (`ai-control --mcp-panel`) — stdio server started by Claude Code itself; no GUI.
Running projects are recognised by their terminal processes (`pgrep` for `--terminal <project>`) — there is no state file that could go stale.
**One look, every platform.** Windows are frameless and draw their own header as the title bar, styled from theme variables: macOS keeps the traffic lights as an overlay, Linux gets its own window buttons. The tray popup is the same HTML window everywhere — what differs is only how the click arrives and who places the window.
**Placement follows a contract.** `show_popup` is a single code path; where the popup lands is decided by the anchor the tray integration hands it:
| Anchor | Where it comes from | Placement |
|---|---|---|
| `IconRect` | native tray (macOS, Windows) | popup's right edge at the tray icon — below the menu bar, above the taskbar |
| `Click` | StatusNotifierItem (KDE, XFCE, Cinnamon) | at the activate coordinates when the host supplies usable ones, otherwise at the pointer, clamped into the monitor |
| `Managed` | GNOME shell extension, KWin script, Cinnamon extension | the compositor places the window itself |
GNOME has no tray at all, so the bundled shell extension provides the icon and relays the click over D-Bus. On Wayland a window cannot position itself, which is why KDE and Cinnamon get a small script/extension for that job.
**Linux, honestly:** desktop × session type (X11/Wayland) × tray host is a wide matrix, and no release can test all of it. What was tested is listed under [Installer tests](#installer-tests). On an untested combination the popup works, but its first placement may sit somewhere other than at the tray icon.
## Security model
- **CSP** — the WebViews run under `default-src 'self'`; scripts, styles, and connections are limited to the app itself and the Tauri IPC endpoint (dev mode additionally allows the Vite dev server and inline styles for hot reload). `style-src` carries no `unsafe-inline`: the window styles live in their own CSS files rather than in the HTML heads, so the policy holds without an exception.
- **Per-window capabilities, deny-by-default** — four capability files, one per window group: `main` (management, broadest command set), `popup` (list/start projects only), `term-*` (PTY plus docked panel), `panel-*` (panel/archive channels only — no PTY, no management). A backend command is callable only from windows whose capability lists it.
- **Pools separate configuration, not access** — keychain entries are deliberately readable without a prompt; any process running as your user can read any pool key. No isolation promise is made or implied.
## Project deletion
Deleting a project is scoped in three stages, each preceded by a preview of the affected artifacts (integration files, archive permission, todo hook, panel files, archive documents, working directories):
1. **Integration only** — removes ai-control's traces: `.ai-control/`, registry entry, panel/session files, archive permission, todo hook, desktop file. The project itself stays untouched.
2. **+ Archive** — additionally deletes the archive home with its documents.
3. **Full** — additionally deletes the project directory (working directories only with an explicit extra flag).
## Project layout ## Project layout
``` ```
├── index.html main UI (project/pool management) ├── index.html main UI (project/pool management)
├── terminal.html terminal window (xterm.js) ├── terminal.html terminal window (xterm.js) + docked text panel
├── panel.html detached text panel window
├── popup.html tray popup
├── src/ frontend: Vue 3 + TypeScript ├── src/ frontend: Vue 3 + TypeScript
│ (incl. wiki-view, search-view, archive-form,
│ commands-view + vitest tests)
├── src-tauri/ ├── src-tauri/
│ ├── src/lib.rs Tauri commands: projects, pools, keychain, │ ├── src/lib.rs entry point, process roles
│ OAuth login, tray menu, session watcher ├── src/app.rs Tauri wiring: tray, main/popup windows, watcher
│ ├── src/terminal.rs PTY sessions (portable-pty), terminal windows, │ ├── src/mcp.rs MCP stdio server (6 tools, see above)
│ Dock icon, focusing running terminals ├── src/terminal.rs PTY sessions (portable-pty), terminal windows
│ ├── src/domain/ registry, project config, settings, watcher,
│ │ deletion scopes
│ ├── src/platform/ macOS / Linux / Windows specifics
│ ├── capabilities/ per-window ACL (main/popup/terminal/panel)
│ └── icons/ app and tray icons │ └── icons/ app and tray icons
├── gnome-shell-extension/ tray/popup integration for GNOME
├── cinnamon-extension/ popup placement for Cinnamon Wayland
├── kwin-script/ popup placement for KDE Wayland
├── dev.sh development mode (tauri dev) ├── dev.sh development mode (tauri dev)
── build.sh release build (.app bundle) ── build.sh release build for the current OS (see below)
├── deploy-linux.sh install the freshest local bundle (rpm/deb)
└── deploy-macos.sh install the fresh .app into ~/Applications
``` ```
Two process roles from one binary: The three process roles that binary takes are described under [Architecture](#architecture).
- **Main app** (`ai-control`) — tray, main window, management, watcher.
- **Terminal process** (`ai-control --terminal <project>`) — one window with its own PTY; launches Claude Code in the project directory with the assigned pool's `CLAUDE_CONFIG_DIR`.
Running projects are detected through their terminal processes (`pgrep` for `--terminal <project>`), without a state file.
## Tooling ## Tooling
| Area | Stack | | Area | Stack |
|-----------|-------| |-----------|-------|
| Shell | Tauri 2 (Rust) | | Shell | Tauri 2 (Rust) |
| Frontend | Vue 3, TypeScript, Vite | | Frontend | Vue 3, TypeScript, Vite; vitest (happy-dom) for the panel views |
| Terminal | xterm.js, portable-pty | | Terminal | xterm.js, portable-pty |
| macOS | objc2 / objc2-app-kit (Dock icon, window focus, tray) | | macOS | objc2 / objc2-app-kit (Dock icon, window focus, tray) |
| Secrets | keyring (macOS Keychain) | | Linux | GTK 3 / WebKitGTK, ksni (StatusNotifierItem), zbus (D-Bus) |
| Secrets | keyring (macOS Keychain / Secret Service) |
## Installation
Prebuilt DMGs are attached to the releases. The bundles are **not signed or notarized** — an Apple Developer ID costs €99/year, which this project doesn't have. If you'd like to change that: [Buy me a coffee](https://buymeacoffee.com/marcusH). macOS quarantines the unsigned download; after copying `ai-control.app` to `~/Applications`, clear it with:
```sh
xattr -d com.apple.quarantine ~/Applications/ai-control.app
```
Or avoid the topic entirely and build from source (below) — self-built apps aren't quarantined.
## Development ## Development
```sh ```sh
./dev.sh # tauri dev with hot reload ./dev.sh # tauri dev with hot reload
./build.sh # release build; bundle lands in src-tauri/target/release/bundle/macos.noindex/ ./build.sh # release build for the current OS
./deploy-macos.sh # …then install it (pick the script for your OS)
``` ```
`build.sh` moves the bundle into a `.noindex` directory and unregisters it from Launch Services so Spotlight only finds the installed copy. Install by copying `ai-control.app` to `~/Applications`. `build.sh` picks the bundles by platform: macOS `.app` + DMG, Linux deb + rpm. Installing is the job of the matching deploy script:
Prerequisites: Rust (stable), Node.js/npm, macOS. `dev.sh`/`build.sh` expect `CARGO_HOME`/`RUSTUP_HOME` under `~/tools/` — adjust the two exports in the scripts for a standard installation. - **`deploy-macos.sh`** — installs the fresh bundle into `~/Applications` (replacing the previous copy) and registers it with Launch Services. The build output is then moved into `bundle/macos.noindex/` and unregistered, so Spotlight and the launcher only ever find the installed copy. It refuses to run while `ai-control` is still running.
- **`deploy-linux.sh`** — installs the freshest local bundle via `rpm`/`dpkg`.
Build dependencies on Linux: `webkit2gtk-4.1`, `libgtk-3`, `libayatana-appindicator3`, `librsvg2`, `patchelf`.
Prerequisites: Rust (stable), Node.js/npm. `dev.sh`/`build.sh` expect `CARGO_HOME`/`RUSTUP_HOME` under `~/tools/` — adjust the two exports in the scripts for a standard installation.
## Configuration layout ## Configuration layout
``` ```
~/.config/ai-control/ ~/.config/ai-control/
├── settings.json app settings (see below) ├── settings.json app settings (see below)
├── projects.json project registry: name → directory ├── projects.json registry: project UUID → directory
│ + pool assignment (machine-local)
└── pools/<pool>/ one Claude config directory per pool └── pools/<pool>/ one Claude config directory per pool
└── pool.json name + credential type └── pool.json name + credential type
<project directory>/ any path, mapped via the registry <project directory>/ any path, mapped via the registry
├── .claude/ Claude Code project configuration ├── .claude/ Claude Code project configuration
└── ai-control.json pool assignment + terminal settings └── .ai-control/ syncs with the project
├── config.json project id (UUID), display name,
│ terminal settings (theme, icon, title),
│ archive home (archiveHome)
└── icon.png project icon
``` ```
### settings.json ### settings.json
| Key | Default | Meaning | | Key | Default | Meaning |
|--------------------|----------|---------| |--------------------|----------|---------|
| `claudeCommand` | `claude` | Command launched in the project terminal (via `zsh -ic`, so your `.zshrc` PATH applies). | | `claudeCommand` | `claude` | Command launched in the project terminal (via your login shell, `$SHELL -lc`, so your profile PATH applies). |
| `syncOnSessionEnd` | `false` | After a session ends, commit and push the Git repository containing the project. | | `syncOnSessionEnd` | `false` | After a session ends, commit and push the Git repository containing the project. |
| `poolSyncDir` | unset | Directory to sync pool runtime data into (session transcripts, todos, prompt history — what `/resume` needs). When set, a pool's runtime files are replaced by symlinks into `<poolSyncDir>/<pool>`, so sessions travel with however that directory is synced (git, Syncthing, …). Unset: everything stays local in the pool directory. | | `poolSyncDir` | unset | Directory to sync pool runtime data into (session transcripts, todos, prompt history — what `/resume` needs). When set, a pool's runtime files are replaced by symlinks into `<poolSyncDir>/<pool>`, so sessions travel with however that directory is synced (git, Syncthing, …). Unset: everything stays local in the pool directory. |
| `terminalFontSize` | `13` | Font size of the built-in terminal (also adjustable from the terminal window). |
| `spellcheckLang` | `de` | Spell-check language of the text panel (per-text override in the panel). |
## Claude and Anthropic licensing ## Claude and Anthropic
ai-control is pool management only — it does not change, extend, or circumvent Anthropic's licensing in any way. Using Claude Code through ai-control is subject to the same Anthropic terms as running it directly. Two things to be clear about:
For that reason the app stores no OAuth tokens itself: with an OAuth pool, the first session of a project on that pool goes through the regular Anthropic login (`/login`, browser, against your subscription), run by Claude Code itself. Credentials live where Claude Code puts them — in the macOS keychain, one entry per pool. - **No connection to Anthropic.** aICentral is an independent open-source project — not affiliated with, sponsored by, endorsed by, or in any way associated with Anthropic. Claude and Claude Code are trademarks of Anthropic and are named here solely to describe what this app works with.
- **No effect on your licence or your usage.** aICentral organises config directories; it does not touch, extend, replace, or circumvent Anthropic's terms, and it grants no access of its own. Whatever you may do with Claude Code, you may do through aICentral — no more, no less. Every request runs under your own login against your own subscription or API key, exactly as it would without this app.
The app stores no OAuth tokens itself: with an OAuth pool, the first session of a project on that pool goes through the regular Anthropic login (`/login`, browser, against your subscription), run by Claude Code itself. Credentials live where Claude Code puts them — in the macOS Keychain or the freedesktop Secret Service, one entry per pool.
API keys go into the keychain as well (one entry per pool). Only when no keychain/keyring is available does the app offer — after an explicit confirmation — to store the key as a file in the pool directory (mode 0600). We strongly advise against that fallback: an API key on disk is readable by every process running as your user. Use the keychain. API keys go into the keychain as well (one entry per pool). Only when no keychain/keyring is available does the app offer — after an explicit confirmation — to store the key as a file in the pool directory (mode 0600). We strongly advise against that fallback: an API key on disk is readable by every process running as your user. Use the keychain.
+20 -19
View File
@@ -1,4 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Gemeinsamer Release-Build für macOS/Linux (Windows: unter Git-Bash läuft
# auch dieses Script). Baut nur — ausrollen macht das jeweilige
# deploy-<os>-Script.
set -euo pipefail set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -8,22 +11,20 @@ export RUSTUP_HOME="$HOME/tools/.rustup"
export PATH="$CARGO_HOME/bin:$PATH" export PATH="$CARGO_HOME/bin:$PATH"
cd "$DIR" cd "$DIR"
# CI=true: DMG ohne Finder-AppleScript (Fenster-Layout) bauen — das Script case "$(uname -s)" in
# wäre aus Nicht-GUI-Shells TCC-blockiert. Darwin)
CI=true npm run tauri build -- --bundles app,dmg "$@" # CI=true: DMG ohne Finder-AppleScript (Fenster-Layout) bauen — das Script
# wäre aus Nicht-GUI-Shells TCC-blockiert.
# Dev-Bundle in ein .noindex-Verzeichnis schieben und aus Launch Services CI=true npm run tauri build -- --bundles app,dmg "$@"
# austragen — Spotlight indiziert .noindex-Ordner nicht, damit erscheint im ;;
# Starter/Spotlight nur die installierte Kopie unter ~/Applications. Linux)
BUNDLE="$DIR/src-tauri/target/release/bundle/macos" npm run tauri build -- --bundles deb,rpm "$@"
NOINDEX="$DIR/src-tauri/target/release/bundle/macos.noindex" ;;
LSREGISTER=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister MINGW* | MSYS*)
# lsregister -u meldet -10814, wenn das Bundle nicht registriert ist — npm run tauri build -- --bundles nsis "$@"
# erwartetes Ergebnis, kein Fehler. ;;
"$LSREGISTER" -u "$BUNDLE/ai-control.app" || true *)
rm -rf "$NOINDEX/ai-control.app" echo "unbekanntes OS: $(uname -s)" >&2
mkdir -p "$NOINDEX" exit 1
mv "$BUNDLE/ai-control.app" "$NOINDEX/" ;;
"$LSREGISTER" -u "$NOINDEX/ai-control.app" || true esac
echo "DMG: $(ls "$DIR"/src-tauri/target/release/bundle/dmg/*.dmg)"
@@ -0,0 +1,69 @@
/* Cinnamon-Extension (CJS, Muffin/Meta-0)
* ai-control-popup@local
*
* Das Tray-Icon kommt unter Cinnamon über SNI (xapp), die App zeigt das
* Popup selbst. Die Extension ergänzt, was der Client nicht kann:
* - Wayland: set_position aus dem Client ist wirkungslos — die Extension
* schiebt das frisch gemappte Popup an den Mauszeiger (der beim Klick auf
* dem Icon steht), mit derselben Klemm-Logik wie position_popup in app.rs.
* - X11: Muffins Focus-Stealing-Prevention verwirft das present() der App —
* ohne initialen Fokus schließt das Blur-Verstecken nie. Die Extension
* fokussiert das Popup beim Map aus dem Compositor heraus (platziert wird
* client-seitig über die SNI-Activate-Koordinaten).
*/
const Meta = imports.gi.Meta;
const WIN_TITLE = 'ai-control-popup';
let mapId = 0;
function _place(win) {
const [px, py] = global.get_pointer();
const rect = win.get_frame_rect();
// Work-Area statt Monitor: die Panel-Struts bleiben frei, das Popup sitzt
// über der Leiste statt darauf.
const wa = win.get_work_area_current_monitor();
const maxX = wa.x + wa.width - rect.width;
const maxY = wa.y + wa.height - rect.height;
const x = Math.min(Math.max(px - rect.width, wa.x), Math.max(maxX, wa.x));
const y = Math.min(Math.max(py, wa.y), Math.max(maxY, wa.y));
win.move_frame(true, x, y);
}
// Beim Mappen unsichtbar schalten und vor dem nächsten Redraw positionieren —
// der erste sichtbare Frame sitzt schon am Zielort (Muster wie in der
// GNOME-Extension, dort über global.compositor.get_laters()).
function _placeBeforeRedraw(actor, win) {
actor.remove_all_transitions();
actor.opacity = 0;
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
const rect = win.get_frame_rect();
// Noch keine echte Größe -> nächster Redraw, bis positionierbar.
if (!rect || rect.width <= 1) return true;
_place(win);
actor.opacity = 255;
return false;
});
}
function init(_metadata) {}
function enable() {
mapId = global.window_manager.connect('map', (_wm, actor) => {
const win = actor.meta_window;
if (!win || win.get_title() !== WIN_TITLE) return;
if (Meta.is_wayland_compositor()) {
_placeBeforeRedraw(actor, win);
} else {
win.activate(global.get_current_time());
}
});
}
function disable() {
if (mapId) {
global.window_manager.disconnect(mapId);
mapId = 0;
}
}
@@ -0,0 +1,7 @@
{
"uuid": "ai-control-popup@local",
"name": "ai-control popup placement",
"description": "ai-control-Tray-Popup: platziert es unter Wayland am Mauszeiger und gibt ihm unter X11 den initialen Fokus (Focus-Stealing-Prevention).",
"cinnamon-version": ["6.0", "6.2", "6.4", "6.6"],
"version": 1
}
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Linux-Deploy nach build.sh: jüngstes Paket-Bundle installieren.
# dnf-Systeme: rpm -Uvh --force (installiert auch dieselbe Version neu),
# apt-Systeme: dpkg -i. Braucht sudo.
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUNDLE="$DIR/src-tauri/target/release/bundle"
if command -v dnf >/dev/null; then
PKG="$(ls -t "$BUNDLE"/rpm/ai-control-*.rpm | head -1)"
sudo rpm -Uvh --force "$PKG"
elif command -v dpkg >/dev/null; then
PKG="$(ls -t "$BUNDLE"/deb/ai-control_*.deb | head -1)"
sudo dpkg -i "$PKG"
else
echo "weder dnf noch dpkg gefunden" >&2
exit 1
fi
echo "installiert: $PKG"
echo "Haupt-App übers Tray beenden und neu starten (Migration läuft beim Start)."
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# macOS-Deploy nach build.sh: das frisch gebaute Bundle nach ~/Applications
# installieren. Das Dev-Bundle wandert danach aus Launch Services heraus in ein
# .noindex-Verzeichnis — Spotlight indiziert .noindex nicht, damit erscheint im
# Starter/Spotlight nur die installierte Kopie.
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUNDLE="$DIR/src-tauri/target/release/bundle/macos"
NOINDEX="$DIR/src-tauri/target/release/bundle/macos.noindex"
TARGET="$HOME/Applications"
LSREGISTER=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister
if [[ ! -d "$BUNDLE/ai-control.app" ]]; then
echo "kein frisches Bundle unter $BUNDLE — erst ./build.sh laufen lassen" >&2
exit 1
fi
# Eine laufende Instanz hält Dateien im Bundle offen; das Ersetzen ergäbe eine
# halb überschriebene App.
if pgrep -qx ai-control; then
echo "ai-control läuft — erst beenden, dann erneut deployen" >&2
exit 1
fi
# Installieren: alte Kopie ganz weg (ditto führt sonst Reste alter Builds
# fort), dann das neue Bundle hin und in Launch Services eintragen.
mkdir -p "$TARGET"
"$LSREGISTER" -u "$TARGET/ai-control.app" || true
rm -rf "$TARGET/ai-control.app"
ditto "$BUNDLE/ai-control.app" "$TARGET/ai-control.app"
"$LSREGISTER" -f "$TARGET/ai-control.app"
# lsregister -u meldet -10814, wenn das Bundle nicht registriert ist —
# erwartetes Ergebnis, kein Fehler.
"$LSREGISTER" -u "$BUNDLE/ai-control.app" || true
rm -rf "$NOINDEX/ai-control.app"
mkdir -p "$NOINDEX"
mv "$BUNDLE/ai-control.app" "$NOINDEX/"
"$LSREGISTER" -u "$NOINDEX/ai-control.app" || true
echo "installiert: $TARGET/ai-control.app"
echo "DMG: $(ls "$DIR"/src-tauri/target/release/bundle/dmg/*.dmg)"
+13
View File
@@ -0,0 +1,13 @@
# Windows-Deploy nach dem Build (--bundles nsis): jüngsten Installer starten.
$ErrorActionPreference = "Stop"
$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
$installer = Get-ChildItem "$dir\src-tauri\target\release\bundle\nsis\ai-control_*-setup.exe" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $installer) {
Write-Error "kein Installer gefunden — erst build.sh (Git-Bash) mit nsis-Bundle laufen lassen"
}
Start-Process -Wait $installer.FullName
Write-Output "installiert: $($installer.Name)"
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -79,6 +79,7 @@ export default class AiControlPopupExtension extends Extension {
const win = this._findWindow(); const win = this._findWindow();
if (win && win.showing_on_its_workspace()) { if (win && win.showing_on_its_workspace()) {
this._call('Hide'); this._call('Hide');
this._disconnectOutsideClick();
return; return;
} }
// Fenster unsichtbar halten, bis es am Platz sitzt (kein Sprung Mitte->Tray). // Fenster unsichtbar halten, bis es am Platz sitzt (kein Sprung Mitte->Tray).
@@ -86,6 +87,37 @@ export default class AiControlPopupExtension extends Extension {
if (actor) actor.opacity = 0; if (actor) actor.opacity = 0;
this._call('Show'); this._call('Show');
this._positionWhenReady(); this._positionWhenReady();
this._connectOutsideClick();
}
// Klick auf leere Desktop-Fläche oder Shell-Chrome wechselt den Fensterfokus
// nicht — der Blur-Pfad der App greift dort nicht. Solche Klicks landen im
// Shell-Stage: solange das Popup offen ist, hier abgreifen und Hide relayen.
_connectOutsideClick() {
if (this._clickId) return;
this._clickId = global.stage.connect('captured-event', (_stage, event) => {
if (event.type() !== Clutter.EventType.BUTTON_PRESS) return Clutter.EVENT_PROPAGATE;
const win = this._findWindow();
if (!win || !win.showing_on_its_workspace()) {
this._disconnectOutsideClick();
return Clutter.EVENT_PROPAGATE;
}
const [x, y] = event.get_coords();
// Panel-Button toggelt selbst; Klicks ins Popup erreichen den Stage nicht.
const [bx, by] = this._button.get_transformed_position();
if (x >= bx && x < bx + this._button.width && y >= by && y < by + this._button.height) {
return Clutter.EVENT_PROPAGATE;
}
this._call('Hide');
this._disconnectOutsideClick();
return Clutter.EVENT_PROPAGATE;
});
}
_disconnectOutsideClick() {
if (!this._clickId) return;
global.stage.disconnect(this._clickId);
this._clickId = 0;
} }
_call(method) { _call(method) {
@@ -186,6 +218,7 @@ export default class AiControlPopupExtension extends Extension {
global.window_manager.disconnect(this._mapId); global.window_manager.disconnect(this._mapId);
this._mapId = 0; this._mapId = 0;
} }
this._disconnectOutsideClick();
this._removeButton(); this._removeButton();
this._proxy = null; this._proxy = null;
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ai-control</title> <title>aICentral</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
@@ -0,0 +1,39 @@
// KWin-Script (Plasma 6): das Pendant zur GNOME-Shell-Extension.
// Unter Wayland darf nur der Compositor ein Fenster positionieren — die App
// (Client) kann das nicht. Dieses Script schiebt unser rahmenloses Popup an
// den Mauszeiger, der beim Tray-Klick auf dem Icon sitzt. Sonst nichts.
//
// Kein Drift: Fenster, UI und Verhalten kommen unverändert aus der App; hier
// wird ausschließlich platziert.
const WIN_TITLE = "ai-control-popup";
function place(w) {
if (!w || w.caption !== WIN_TITLE) {
return;
}
const cursor = workspace.cursorPos;
// MaximizeArea = Arbeitsfläche ohne Panels; so sitzt das Popup neben der
// Leiste, nicht darunter.
const area = workspace.clientArea(KWin.MaximizeArea, w);
const width = w.frameGeometry.width;
const height = w.frameGeometry.height;
// Rechte Fensterkante an den Zeiger; oben/unten aus dem Klemmen an die
// Arbeitsflächenkante (Panel oben -> unter dem Icon, Panel unten -> darüber).
let x = cursor.x - width;
let y = cursor.y;
if (x < area.x) x = area.x;
if (x + width > area.x + area.width) x = area.x + area.width - width;
if (y < area.y) y = area.y;
if (y + height > area.y + area.height) y = area.y + area.height - height;
// Reine QJSEngine, kein QML: `Qt.rect` gibt es hier nicht; die Engine
// konvertiert ein {x,y,width,height}-Objekt selbst nach QRectF.
w.frameGeometry = { x: x, y: y, width: width, height: height };
}
// windowAdded feuert beim (erneuten) Mappen des Popups, windowActivated fängt
// das Wiederanzeigen ab, falls der Compositor das Fenster nur reaktiviert.
workspace.windowAdded.connect(place);
workspace.windowActivated.connect(place);
@@ -0,0 +1,14 @@
{
"KPlugin": {
"Id": "ai-control-popup",
"Name": "ai-control Popup Positioner",
"Description": "Schiebt das rahmenlose ai-control-Popup an den Mauszeiger — das Pendant zur GNOME-Shell-Extension, nur für KWin (Wayland erlaubt Positionieren nur im Compositor).",
"Authors": [{ "Name": "marcus.hinz" }],
"Version": "1.0",
"License": "MIT",
"EnabledByDefault": false
},
"KPackageStructure": "KWin/Script",
"X-Plasma-API": "javascript",
"X-Plasma-MainScript": "code/main.js"
}
+468
View File
@@ -12,6 +12,7 @@
"@fontsource/jetbrains-mono": "^5.2.8", "@fontsource/jetbrains-mono": "^5.2.8",
"@tauri-apps/api": "^2.11.1", "@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-autostart": "^2.5.1", "@tauri-apps/plugin-autostart": "^2.5.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-opener": "^2.5.4", "@tauri-apps/plugin-opener": "^2.5.4",
"@xterm/addon-fit": "^0.11.0", "@xterm/addon-fit": "^0.11.0",
@@ -27,8 +28,10 @@
"@types/node": "^24.13.2", "@types/node": "^24.13.2",
"@vitejs/plugin-vue": "^6.0.7", "@vitejs/plugin-vue": "^6.0.7",
"@vue/tsconfig": "^0.9.1", "@vue/tsconfig": "^0.9.1",
"happy-dom": "^20.11.0",
"typescript": "~6.0.2", "typescript": "~6.0.2",
"vite": "^8.1.1", "vite": "^8.1.1",
"vitest": "^4.1.10",
"vue-tsc": "^3.3.5" "vue-tsc": "^3.3.5"
} }
}, },
@@ -481,6 +484,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"dev": true,
"license": "MIT"
},
"node_modules/@tauri-apps/api": { "node_modules/@tauri-apps/api": {
"version": "2.11.1", "version": "2.11.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz",
@@ -717,6 +727,15 @@
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.8.0"
} }
}, },
"node_modules/@tauri-apps/plugin-clipboard-manager": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.3.2.tgz",
"integrity": "sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.8.0"
}
},
"node_modules/@tauri-apps/plugin-dialog": { "node_modules/@tauri-apps/plugin-dialog": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz",
@@ -746,6 +765,31 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@types/chai": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*",
"assertion-error": "^2.0.1"
}
},
"node_modules/@types/deep-eql": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.13.2", "version": "24.13.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
@@ -756,6 +800,23 @@
"undici-types": "~7.18.0" "undici-types": "~7.18.0"
} }
}, },
"node_modules/@types/whatwg-mimetype": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
"integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "6.0.7", "version": "6.0.7",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz",
@@ -773,6 +834,129 @@
"vue": "^3.2.25" "vue": "^3.2.25"
} }
}, },
"node_modules/@vitest/expect": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
"integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.1.10",
"@vitest/utils": "4.1.10",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/mocker": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
"integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.1.10",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"msw": "^2.4.9",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"msw": {
"optional": true
},
"vite": {
"optional": true
}
}
},
"node_modules/@vitest/mocker/node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
"integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/runner": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
"integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.1.10",
"pathe": "^2.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/snapshot": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
"integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.10",
"@vitest/utils": "4.1.10",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/spy": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
"integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/utils": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
"integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.10",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@volar/language-core": { "node_modules/@volar/language-core": {
"version": "2.4.28", "version": "2.4.28",
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz",
@@ -984,6 +1168,46 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/buffer-image-size": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz",
"integrity": "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
},
"engines": {
"node": ">=4.0"
}
},
"node_modules/chai": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -1012,12 +1236,29 @@
"url": "https://github.com/fb55/entities?sponsor=1" "url": "https://github.com/fb55/entities?sponsor=1"
} }
}, },
"node_modules/es-module-lexer": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz",
"integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==",
"dev": true,
"license": "MIT"
},
"node_modules/estree-walker": { "node_modules/estree-walker": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/expect-type": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/fdir": { "node_modules/fdir": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -1087,6 +1328,25 @@
"node": ">8.0.0" "node": ">8.0.0"
} }
}, },
"node_modules/happy-dom": {
"version": "20.11.0",
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.11.0.tgz",
"integrity": "sha512-XogN4asPd1a56di9prVG6bZxteNcXsZxxKmAvcEfc5Px5Ca2hMyMgk8wvqK2K1V8zXg40j9VANXsDaJYh9DeNA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": ">=20.0.0",
"@types/whatwg-mimetype": "^3.0.2",
"@types/ws": "^8.18.1",
"buffer-image-size": "^0.6.4",
"entities": "^7.0.1",
"whatwg-mimetype": "^3.0.0",
"ws": "^8.21.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/lightningcss": { "node_modules/lightningcss": {
"version": "1.32.0", "version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
@@ -1406,6 +1666,20 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/obug": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz",
"integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==",
"dev": true,
"funding": [
"https://github.com/sponsors/sxzz",
"https://opencollective.com/debug"
],
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/opentype.js": { "node_modules/opentype.js": {
"version": "0.8.0", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.8.0.tgz", "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.8.0.tgz",
@@ -1425,6 +1699,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1515,6 +1796,13 @@
"@rolldown/binding-win32-x64-msvc": "1.1.4" "@rolldown/binding-win32-x64-msvc": "1.1.4"
} }
}, },
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
"dev": true,
"license": "ISC"
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1524,12 +1812,43 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
"dev": true,
"license": "MIT"
},
"node_modules/std-env": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
"integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
"dev": true,
"license": "MIT"
},
"node_modules/tiny-inflate": { "node_modules/tiny-inflate": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
"integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.17", "version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
@@ -1547,6 +1866,16 @@
"url": "https://github.com/sponsors/SuperchupuDev" "url": "https://github.com/sponsors/SuperchupuDev"
} }
}, },
"node_modules/tinyrainbow": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -1654,6 +1983,96 @@
} }
} }
}, },
"node_modules/vitest": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
"integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "4.1.10",
"@vitest/mocker": "4.1.10",
"@vitest/pretty-format": "4.1.10",
"@vitest/runner": "4.1.10",
"@vitest/snapshot": "4.1.10",
"@vitest/spy": "4.1.10",
"@vitest/utils": "4.1.10",
"es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0",
"magic-string": "^0.30.21",
"obug": "^2.1.1",
"pathe": "^2.0.3",
"picomatch": "^4.0.3",
"std-env": "^4.0.0-rc.1",
"tinybench": "^2.9.0",
"tinyexec": "^1.0.2",
"tinyglobby": "^0.2.15",
"tinyrainbow": "^3.1.0",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
"why-is-node-running": "^2.3.0"
},
"bin": {
"vitest": "vitest.mjs"
},
"engines": {
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.1.10",
"@vitest/browser-preview": "4.1.10",
"@vitest/browser-webdriverio": "4.1.10",
"@vitest/coverage-istanbul": "4.1.10",
"@vitest/coverage-v8": "4.1.10",
"@vitest/ui": "4.1.10",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@edge-runtime/vm": {
"optional": true
},
"@opentelemetry/api": {
"optional": true
},
"@types/node": {
"optional": true
},
"@vitest/browser-playwright": {
"optional": true
},
"@vitest/browser-preview": {
"optional": true
},
"@vitest/browser-webdriverio": {
"optional": true
},
"@vitest/coverage-istanbul": {
"optional": true
},
"@vitest/coverage-v8": {
"optional": true
},
"@vitest/ui": {
"optional": true
},
"happy-dom": {
"optional": true
},
"jsdom": {
"optional": true
},
"vite": {
"optional": false
}
}
},
"node_modules/vscode-uri": { "node_modules/vscode-uri": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
@@ -1720,6 +2139,55 @@
"typescript": ">=5.0.0" "typescript": ">=5.0.0"
} }
}, },
"node_modules/whatwg-mimetype": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"siginfo": "^2.0.0",
"stackback": "0.0.2"
},
"bin": {
"why-is-node-running": "cli.js"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ws": {
"version": "8.21.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+5 -1
View File
@@ -1,12 +1,13 @@
{ {
"name": "ai-control", "name": "ai-control",
"private": true, "private": true,
"version": "0.2.0", "version": "0.3.0",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc -b && vite build", "build": "vue-tsc -b && vite build",
"test": "vitest run",
"preview": "vite preview", "preview": "vite preview",
"tauri": "tauri" "tauri": "tauri"
}, },
@@ -14,6 +15,7 @@
"@fontsource/jetbrains-mono": "^5.2.8", "@fontsource/jetbrains-mono": "^5.2.8",
"@tauri-apps/api": "^2.11.1", "@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-autostart": "^2.5.1", "@tauri-apps/plugin-autostart": "^2.5.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-opener": "^2.5.4", "@tauri-apps/plugin-opener": "^2.5.4",
"@xterm/addon-fit": "^0.11.0", "@xterm/addon-fit": "^0.11.0",
@@ -29,8 +31,10 @@
"@types/node": "^24.13.2", "@types/node": "^24.13.2",
"@vitejs/plugin-vue": "^6.0.7", "@vitejs/plugin-vue": "^6.0.7",
"@vue/tsconfig": "^0.9.1", "@vue/tsconfig": "^0.9.1",
"happy-dom": "^20.11.0",
"typescript": "~6.0.2", "typescript": "~6.0.2",
"vite": "^8.1.1", "vite": "^8.1.1",
"vitest": "^4.1.10",
"vue-tsc": "^3.3.5" "vue-tsc": "^3.3.5"
} }
} }
+38 -207
View File
@@ -2,209 +2,32 @@
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Entwurf</title> <title data-i18n="panel.windowDocument">Dokument</title>
<style>
html,
body {
height: 100%;
margin: 0;
background: #1e1e2e;
color: #cdd6f4;
overflow: hidden;
}
body {
display: flex;
flex-direction: column;
}
.panel-head {
flex: none;
display: flex;
flex-direction: column;
gap: 3px;
padding: 7px 8px 9px 14px;
background: #181825;
border-bottom: 1px solid #313244;
font:
500 12px/1 -apple-system,
system-ui,
sans-serif;
user-select: none;
-webkit-user-select: none;
}
.panel-titlerow {
display: flex;
align-items: center;
gap: 2px;
min-width: 0;
}
.panel-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 2px;
}
.panel-title {
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #a6adc8;
}
.panel-title[contenteditable="true"] {
overflow: visible;
text-overflow: clip;
outline: none;
color: inherit;
cursor: text;
}
.panel-btn {
border: none;
background: transparent;
color: #a6adc8;
cursor: default;
border-radius: 6px;
padding: 6px 9px;
font: inherit;
display: flex;
align-items: center;
}
.panel-btn:hover {
background: #313244;
color: #cdd6f4;
}
.panel-btn.copied {
color: #a6e3a1;
}
.panel-btn.error {
color: #f38ba8;
}
.panel-btn svg {
display: block;
stroke: currentColor;
stroke-width: 1.3;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.panel-btn.active {
background: #313244;
color: #cdd6f4;
}
.panel-btn.changed {
color: #f9e2af;
}
.panel-editor {
flex: 1;
min-height: 0;
resize: none;
border: none;
outline: none;
padding: 16px 20px;
background: transparent;
color: inherit;
font-family: "JetBrains Mono", Menlo, monospace;
font-size: 12px;
line-height: 1.55;
}
.panel-editor[hidden] {
display: none;
}
.panel-lang {
margin-right: auto;
background: #313244;
color: #a6adc8;
border: none;
border-radius: 6px;
padding: 3px 4px;
font: inherit;
font-size: 11px;
cursor: default;
}
#panel-content {
flex: 1;
min-height: 0;
overflow: auto;
padding: 16px 20px;
user-select: text;
-webkit-user-select: text;
cursor: text;
font-size: 13px;
line-height: 1.55;
}
#panel-content::-webkit-scrollbar {
width: 8px;
}
#panel-content::-webkit-scrollbar-thumb {
background: #313244;
border-radius: 4px;
}
#panel-content.raw {
white-space: pre-wrap;
word-break: break-word;
font-family: "JetBrains Mono", Menlo, monospace;
font-size: 12px;
}
#panel-content.md h1,
#panel-content.md h2,
#panel-content.md h3 {
line-height: 1.25;
margin: 1em 0 0.4em;
}
#panel-content.md h1 {
font-size: 1.4em;
}
#panel-content.md h2 {
font-size: 1.2em;
}
#panel-content.md h3 {
font-size: 1.05em;
}
#panel-content.md :first-child {
margin-top: 0;
}
#panel-content.md p,
#panel-content.md ul,
#panel-content.md ol,
#panel-content.md pre,
#panel-content.md blockquote {
margin: 0 0 0.7em;
}
#panel-content.md code {
font-family: "JetBrains Mono", Menlo, monospace;
font-size: 0.9em;
background: #313244;
border-radius: 4px;
padding: 1px 5px;
}
#panel-content.md pre {
background: #11111b;
border-radius: 8px;
padding: 12px 14px;
overflow: auto;
}
#panel-content.md pre code {
background: none;
padding: 0;
}
#panel-content.md blockquote {
border-left: 3px solid #313244;
padding-left: 12px;
color: #a6adc8;
}
#panel-content.md a {
color: #89b4fa;
}
</style>
</head> </head>
<body> <body>
<div class="panel-topbar" data-tauri-drag-region>
<!-- Tabs kommen aus der Modul-Registry (panel-wiring). -->
<div class="panel-tabs" id="panel-tabs"></div>
<button class="winbtn" id="panel-dock" data-i18n-title="panel.dock">
<svg width="14" height="14" viewBox="0 0 16 16"><rect x="1.5" y="2.5" width="13" height="11" rx="1.5" /><path d="M9.5 2.5v11" /><path d="M4 8h3.4M6 6.2 7.8 8 6 9.8" /></svg>
</button>
<button class="winbtn osctl" id="win-min" data-i18n-aria="panel.minimize">
<svg width="12" height="12" viewBox="0 0 12 12"><line x1="2" y1="6.5" x2="10" y2="6.5" /></svg>
</button>
<button class="winbtn osctl" id="win-max" data-i18n-aria="panel.maximize">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><rect x="2.5" y="2.5" width="7" height="7" rx="1" /></svg>
</button>
<button class="winbtn close" id="panel-close" data-i18n-title="panel.closePanel">
<svg width="12" height="12" viewBox="0 0 12 12"><path d="M3 3l6 6M9 3l-6 6" /></svg>
</button>
</div>
<div class="panel-head"> <div class="panel-head">
<div class="panel-titlerow" data-tauri-drag-region> <div class="panel-titlerow" data-tauri-drag-region>
<span class="panel-title">Entwurf</span> <span class="panel-title" data-i18n="panel.tabDraft">Dokument</span>
<button class="panel-btn panel-edit" id="panel-title-edit" title="Titel bearbeiten"></button> <button class="panel-btn panel-edit draft-only" id="panel-title-edit" data-i18n-title="panel.editTitle"></button>
</div> </div>
<div class="panel-actions"> <div class="panel-actions">
<select class="panel-lang" id="panel-lang" title="Sprache der Rechtschreibprüfung"> <select class="panel-lang draft-only" id="panel-lang" data-i18n-title="panel.spellcheckLang">
<option value="de">DE</option> <option value="de">DE</option>
<option value="en">EN</option> <option value="en">EN</option>
<option value="en-GB">EN-GB</option> <option value="en-GB">EN-GB</option>
@@ -213,25 +36,33 @@
<option value="it">IT</option> <option value="it">IT</option>
<option value="nl">NL</option> <option value="nl">NL</option>
</select> </select>
<button class="panel-btn" id="panel-mode" title="Rohtext / gerendert">MD</button> <button class="panel-btn draft-only" id="panel-wiki-jump" data-i18n-title="panel.openInWiki">
<button class="panel-btn" id="panel-content-edit" title="Entwurf bearbeiten (Cmd/Ctrl+Enter speichert, Esc verwirft)"> <svg width="14" height="14" viewBox="0 0 16 16"><path d="M8 3.5C6.6 2.4 4.6 2 2.5 2v11c2.1 0 4.1.4 5.5 1.5 1.4-1.1 3.4-1.5 5.5-1.5V2c-2.1 0-4.1.4-5.5 1.5z" /><path d="M8 3.5v11" /></svg>
</button>
<button class="panel-btn draft-only" id="panel-mode" data-i18n-title="panel.toggleRaw" data-i18n="panel.rendered">MD</button>
<button class="panel-btn draft-only" id="panel-content-edit" data-i18n-title="panel.editDraft">
<svg width="14" height="14" viewBox="0 0 16 16"><path d="M10.8 2.6 13.4 5.2 6 12.6l-3.1.5.5-3.1z" /><path d="M9.7 3.7 12.3 6.3" /></svg> <svg width="14" height="14" viewBox="0 0 16 16"><path d="M10.8 2.6 13.4 5.2 6 12.6l-3.1.5.5-3.1z" /><path d="M9.7 3.7 12.3 6.3" /></svg>
</button> </button>
<button class="panel-btn" id="panel-copy" title="In die Zwischenablage kopieren"> <button class="panel-btn draft-only" id="panel-copy" data-i18n-title="panel.copy">
<svg width="14" height="14" viewBox="0 0 16 16"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5" /><path d="M10.5 3.2V3A1.5 1.5 0 0 0 9 1.5H3A1.5 1.5 0 0 0 1.5 3v6A1.5 1.5 0 0 0 3 10.5h.2" /></svg> <svg width="14" height="14" viewBox="0 0 16 16"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5" /><path d="M10.5 3.2V3A1.5 1.5 0 0 0 9 1.5H3A1.5 1.5 0 0 0 1.5 3v6A1.5 1.5 0 0 0 3 10.5h.2" /></svg>
</button> </button>
<button class="panel-btn" id="panel-archive" title="In den Archiv-Ordner speichern"> <button class="panel-btn draft-only" id="panel-archive" data-i18n-title="panel.archive">
<svg width="14" height="14" viewBox="0 0 16 16"><rect x="1.5" y="2.5" width="13" height="3.5" rx="1" /><path d="M2.8 6v6.5a1 1 0 0 0 1 1h8.4a1 1 0 0 0 1-1V6" /><path d="M6.3 9h3.4" /></svg> <svg width="14" height="14" viewBox="0 0 16 16"><rect x="1.5" y="2.5" width="13" height="3.5" rx="1" /><path d="M2.8 6v6.5a1 1 0 0 0 1 1h8.4a1 1 0 0 0 1-1V6" /><path d="M6.3 9h3.4" /></svg>
</button> </button>
<button class="panel-btn" id="panel-dock" title="Wieder andocken">
<svg width="14" height="14" viewBox="0 0 16 16"><rect x="1.5" y="2.5" width="13" height="11" rx="1.5" /><path d="M9.5 2.5v11" /><path d="M4 8h3.4M6 6.2 7.8 8 6 9.8" /></svg>
</button>
<button class="panel-btn" id="panel-close" title="Panel schließen">
<svg width="12" height="12" viewBox="0 0 12 12"><path d="M3 3l6 6M9 3l-6 6" /></svg>
</button>
</div> </div>
</div> </div>
<!-- Modul-Container (<mode>-content) reihen sich hier ein. -->
<div id="panel-content" class="md"></div> <div id="panel-content" class="md"></div>
<div id="resize-grips" aria-hidden="true">
<div class="grip n" data-dir="North"></div>
<div class="grip s" data-dir="South"></div>
<div class="grip w" data-dir="West"></div>
<div class="grip e" data-dir="East"></div>
<div class="grip nw" data-dir="NorthWest"></div>
<div class="grip ne" data-dir="NorthEast"></div>
<div class="grip sw" data-dir="SouthWest"></div>
<div class="grip se" data-dir="SouthEast"></div>
</div>
<script type="module" src="/src/panel.ts"></script> <script type="module" src="/src/panel.ts"></script>
</body> </body>
</html> </html>
+1 -8
View File
@@ -2,14 +2,7 @@
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Projekte</title> <title data-i18n="panel.windowProjects">Projekte</title>
<style>
html,
body {
margin: 0;
background: transparent;
}
</style>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
+496 -8
View File
@@ -19,6 +19,18 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.4" version = "1.1.4"
@@ -30,23 +42,27 @@ dependencies = [
[[package]] [[package]]
name = "ai-control" name = "ai-control"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"glib", "glib",
"gtk",
"keyring", "keyring",
"ksni",
"log", "log",
"objc2", "objc2",
"objc2-app-kit", "objc2-app-kit",
"objc2-foundation", "objc2-foundation",
"portable-pty", "portable-pty",
"rfd 0.15.4", "rfd 0.15.4",
"rusqlite",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-autostart", "tauri-plugin-autostart",
"tauri-plugin-clipboard-manager",
"tauri-plugin-dialog", "tauri-plugin-dialog",
"tauri-plugin-log", "tauri-plugin-log",
"uuid", "uuid",
@@ -94,12 +110,42 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.103" version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "arboard"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
dependencies = [
"clipboard-win",
"image",
"log",
"objc2",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-foundation",
"parking_lot",
"percent-encoding",
"windows-sys 0.60.2",
"wl-clipboard-rs",
"x11rb",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.8" version = "0.7.8"
@@ -266,6 +312,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]] [[package]]
name = "auto-launch" name = "auto-launch"
version = "0.5.0" version = "0.5.0"
@@ -623,6 +680,30 @@ dependencies = [
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clipboard-win"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
dependencies = [
"error-code",
]
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@@ -735,6 +816,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.7" version = "0.1.7"
@@ -803,7 +890,7 @@ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim 0.11.1",
"syn 2.0.118", "syn 2.0.118",
] ]
@@ -829,6 +916,17 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "dbus-codegen"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a49da9fdfbe872d4841d56605dc42efa5e6ca3291299b87f44e1cde91a28617c"
dependencies = [
"clap",
"dbus",
"xml-rs",
]
[[package]] [[package]]
name = "dbus-secret-service" name = "dbus-secret-service"
version = "4.1.0" version = "4.1.0"
@@ -839,6 +937,15 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "dbus-tree"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f456e698ae8e54575e19ddb1f9b7bce2298568524f215496b248eb9498b4f508"
dependencies = [
"dbus",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.5.8" version = "0.5.8"
@@ -974,7 +1081,7 @@ checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"cssparser", "cssparser",
"foldhash", "foldhash 0.2.0",
"html5ever", "html5ever",
"precomputed-hash", "precomputed-hash",
"selectors", "selectors",
@@ -1122,6 +1229,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "error-code"
version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "5.4.1" version = "5.4.1"
@@ -1143,12 +1256,30 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.4.1" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "fax"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a"
[[package]] [[package]]
name = "fdeflate" name = "fdeflate"
version = "0.3.7" version = "0.3.7"
@@ -1194,6 +1325,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.9" version = "1.1.9"
@@ -1210,6 +1347,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.2.0" version = "0.2.0"
@@ -1451,6 +1594,16 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix",
"windows-link 0.2.1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.17" version = "0.2.17"
@@ -1633,13 +1786,42 @@ dependencies = [
"syn 2.0.118", "syn 2.0.118",
] ]
[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [ dependencies = [
"ahash", "ahash 0.7.8",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash 0.8.12",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
] ]
[[package]] [[package]]
@@ -1648,6 +1830,15 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown 0.14.5",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@@ -1660,6 +1851,15 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.5.2" version = "0.5.2"
@@ -1918,6 +2118,7 @@ dependencies = [
"moxcms", "moxcms",
"num-traits", "num-traits",
"png 0.18.1", "png 0.18.1",
"tiff",
] ]
[[package]] [[package]]
@@ -2090,6 +2291,18 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "ksni"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c7d627125aac4ea80802d51d9f8c3e4ceb948792414b0e345903ff78337b3a7"
dependencies = [
"dbus",
"dbus-codegen",
"dbus-tree",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@@ -2154,6 +2367,17 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.12.1" version = "0.12.1"
@@ -2310,6 +2534,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.2.2" version = "0.2.2"
@@ -2596,6 +2829,16 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "os_pipe"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "pango" name = "pango"
version = "0.18.3" version = "0.18.3"
@@ -2656,6 +2899,17 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
"fixedbitset",
"hashbrown 0.15.5",
"indexmap 2.14.0",
]
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.13.1" version = "0.13.1"
@@ -2734,13 +2988,13 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]] [[package]]
name = "plist" name = "plist"
version = "1.9.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"indexmap 2.14.0", "indexmap 2.14.0",
"quick-xml", "quick-xml 0.41.0",
"serde", "serde",
"time", "time",
] ]
@@ -2779,7 +3033,7 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"concurrent-queue", "concurrent-queue",
"hermit-abi", "hermit-abi 0.5.2",
"pin-project-lite", "pin-project-lite",
"rustix", "rustix",
"windows-sys 0.61.2", "windows-sys 0.61.2",
@@ -2924,6 +3178,12 @@ version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.39.4" version = "0.39.4"
@@ -2933,6 +3193,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "quick-xml"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.46" version = "1.0.46"
@@ -3196,6 +3465,20 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "rusqlite"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
dependencies = [
"bitflags 2.13.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]] [[package]]
name = "rust_decimal" name = "rust_decimal"
version = "1.42.1" version = "1.42.1"
@@ -3714,6 +3997,12 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@@ -3993,6 +4282,21 @@ dependencies = [
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
[[package]]
name = "tauri-plugin-clipboard-manager"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf"
dependencies = [
"arboard",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.18",
]
[[package]] [[package]]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.7.1" version = "2.7.1"
@@ -4180,6 +4484,15 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -4220,6 +4533,20 @@ dependencies = [
"syn 2.0.118", "syn 2.0.118",
] ]
[[package]]
name = "tiff"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.53" version = "0.3.53"
@@ -4522,6 +4849,17 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "tree_magic_mini"
version = "3.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
dependencies = [
"memchr",
"nom",
"petgraph",
]
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.5" version = "0.2.5"
@@ -4604,6 +4942,12 @@ version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.8" version = "2.5.8"
@@ -4665,6 +5009,18 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.2.1" version = "0.2.1"
@@ -4799,6 +5155,76 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "wayland-backend"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
dependencies = [
"cc",
"downcast-rs",
"rustix",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
dependencies = [
"bitflags 2.13.0",
"rustix",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
dependencies = [
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
dependencies = [
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
dependencies = [
"proc-macro2",
"quick-xml 0.39.4",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
dependencies = [
"pkg-config",
]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.103" version = "0.3.103"
@@ -4901,6 +5327,12 @@ dependencies = [
"windows-core 0.61.2", "windows-core 0.61.2",
] ]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@@ -5375,6 +5807,24 @@ version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wl-clipboard-rs"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
dependencies = [
"libc",
"log",
"os_pipe",
"rustix",
"thiserror 2.0.18",
"tree_magic_mini",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-protocols-wlr",
]
[[package]] [[package]]
name = "writeable" name = "writeable"
version = "0.6.3" version = "0.6.3"
@@ -5455,6 +5905,29 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "x11rb"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
dependencies = [
"gethostname",
"rustix",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xml-rs"
version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.3" version = "0.8.3"
@@ -5639,6 +6112,21 @@ version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zune-core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
[[package]] [[package]]
name = "zvariant" name = "zvariant"
version = "5.12.0" version = "5.12.0"
+9 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "ai-control" name = "ai-control"
version = "0.2.0" version = "0.3.0"
description = "Pool- und Session-Verwaltung für Claude Code" description = "Pool- und Session-Verwaltung für Claude Code"
authors = ["marcus.hinz"] authors = ["marcus.hinz"]
license = "MIT" license = "MIT"
@@ -29,18 +29,26 @@ tauri = { version = "2.11.3", features = ["macos-private-api", "tray-icon", "ima
tauri-plugin-log = "2" tauri-plugin-log = "2"
tauri-plugin-autostart = "2" tauri-plugin-autostart = "2"
tauri-plugin-dialog = "2" tauri-plugin-dialog = "2"
tauri-plugin-clipboard-manager = "2"
base64 = "0.22" base64 = "0.22"
sha2 = "0.10" sha2 = "0.10"
portable-pty = "0.9" portable-pty = "0.9"
uuid = { version = "1", features = ["v4"] } uuid = { version = "1", features = ["v4"] }
# Volltext-Suche übers Panel-Archiv (SQLite FTS5, in-memory aus dem Baum gebaut).
rusqlite = { version = "0.32", features = ["bundled"] }
keyring = { version = "3", features = ["apple-native", "sync-secret-service", "windows-native"] } keyring = { version = "3", features = ["apple-native", "sync-secret-service", "windows-native"] }
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
zbus = "5" zbus = "5"
glib = "0.18" glib = "0.18"
gtk = "0.18"
rfd = { version = "0.15", default-features = false, features = ["gtk3"] } rfd = { version = "0.15", default-features = false, features = ["gtk3"] }
ksni = "0.1"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
# Der Installations-Dialog läuft vor dem Tauri-Setup, also ohne AppHandle und
# damit ohne das Dialog-Plugin. Unter macOS braucht rfd kein Backend-Feature.
rfd = { version = "0.15", default-features = false }
objc2 = "0.6" objc2 = "0.6"
objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSImage", "NSResponder", "NSRunningApplication"] } objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSImage", "NSResponder", "NSRunningApplication"] }
objc2-foundation = { version = "0.3", features = ["NSString"] } objc2-foundation = { version = "0.3", features = ["NSString"] }
+80 -1
View File
@@ -1,3 +1,82 @@
fn main() { fn main() {
tauri_build::build() // App-ACL-Manifest: erzeugt pro Command allow-/deny-Permissions. Sobald das
// Manifest existiert, gilt deny-by-default — jedes Command muss in einer
// Capability einem Fenster zugeteilt sein (capabilities/*.json), sonst
// lehnt Tauri den Aufruf ab. Neue Commands hier UND in der passenden
// Capability eintragen.
tauri_build::try_build(
tauri_build::Attributes::new().app_manifest(tauri_build::AppManifest::new().commands(&[
"list_projects",
"create_project_full",
"add_project",
"delete_preview",
"delete_project_scoped",
"project_work_dirs",
"set_project_dir",
"add_work_dir",
"remove_work_dir",
"list_pools",
"create_oauth_pool",
"create_apikey_pool",
"create_reference_pool",
"default_config_dir",
"rename_pool",
"delete_pool",
"assign_pool",
"unassign_pool",
"set_terminal_config",
"project_icon",
"pool_label",
"usage_stats",
"stop_project",
"restart_project",
"start_or_focus_cmd",
"open_main_window",
"quit_app",
"sync_setting",
"set_sync_setting",
"terminal_font_size",
"set_terminal_font_size",
"link_pool_runtime",
"oauth_login",
"keychain_status",
"set_apikey",
"panel_archive_dir_cmd",
"panel_archive_cmd",
"set_archive_home_cmd",
"change_archive_home_cmd",
"clear_archive_home_cmd",
"archive_docs_cmd",
"reveal_path_cmd",
"spellcheck_lang",
"open_terminal",
"term_start",
"term_log",
"term_write",
"term_resize",
"buffer_read",
"enabled_modules",
"module_registry",
"set_module",
"commands_delete",
"todos_delete",
"todos_add",
"todos_update",
"panel_set",
"search_run",
"panel_load",
"wiki_open",
"archive_move",
"archive_rename",
"archive_read",
"archive_write",
"archive_set_title",
"archive_delete",
"archive_move_folder",
"archive_create_folder",
"archive_create_doc",
"open_panel_window",
])),
)
.expect("tauri-build");
} }
-23
View File
@@ -1,23 +0,0 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "enables the default permissions",
"windows": [
"main",
"popup",
"term-*",
"panel-*"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-hide",
"core:window:allow-start-resize-dragging",
"autostart:default",
"dialog:default"
]
}
+61
View File
@@ -0,0 +1,61 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main",
"description": "Hauptfenster: komplette Projekt-/Pool-Verwaltung.",
"windows": [
"main"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"core:window:allow-set-position",
"core:window:allow-outer-position",
"core:window:allow-inner-size",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-hide",
"core:window:allow-start-resize-dragging",
"autostart:default",
"dialog:default",
"clipboard-manager:allow-write-text",
"allow-list-projects",
"allow-create-project-full",
"allow-add-project",
"allow-project-work-dirs",
"allow-set-project-dir",
"allow-add-work-dir",
"allow-remove-work-dir",
"allow-list-pools",
"allow-create-oauth-pool",
"allow-create-apikey-pool",
"allow-create-reference-pool",
"allow-default-config-dir",
"allow-rename-pool",
"allow-delete-pool",
"allow-assign-pool",
"allow-unassign-pool",
"allow-set-terminal-config",
"allow-project-icon",
"allow-usage-stats",
"allow-stop-project",
"allow-restart-project",
"allow-open-terminal",
"allow-sync-setting",
"allow-set-sync-setting",
"allow-link-pool-runtime",
"allow-oauth-login",
"allow-keychain-status",
"allow-set-apikey",
"allow-panel-archive-dir-cmd",
"allow-set-archive-home-cmd",
"allow-clear-archive-home-cmd",
"allow-archive-docs-cmd",
"allow-delete-preview",
"allow-delete-project-scoped",
"allow-change-archive-home-cmd",
"allow-module-registry",
"allow-set-module"
]
}
+50
View File
@@ -0,0 +1,50 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "panel",
"description": "Abgelöstes Panel-Fenster: nur Panel-/Archiv-Kanäle, keine PTY, keine Verwaltung.",
"windows": [
"panel-*"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"core:window:allow-center",
"core:window:allow-set-position",
"core:window:allow-outer-position",
"core:window:allow-inner-size",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-hide",
"core:window:allow-start-resize-dragging",
"autostart:default",
"dialog:default",
"clipboard-manager:allow-write-text",
"allow-archive-create-doc",
"allow-archive-create-folder",
"allow-archive-delete",
"allow-archive-move",
"allow-archive-move-folder",
"allow-archive-rename",
"allow-archive-read",
"allow-archive-write",
"allow-archive-set-title",
"allow-buffer-read",
"allow-commands-delete",
"allow-enabled-modules",
"allow-list-projects",
"allow-panel-archive-cmd",
"allow-panel-archive-dir-cmd",
"allow-panel-load",
"allow-panel-set",
"allow-reveal-path-cmd",
"allow-search-run",
"allow-spellcheck-lang",
"allow-term-log",
"allow-todos-add",
"allow-todos-delete",
"allow-todos-update",
"allow-wiki-open"
]
}
+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "popup",
"description": "Tray-Popup: Projektliste anzeigen und starten.",
"windows": [
"popup"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"core:window:allow-set-position",
"core:window:allow-outer-position",
"core:window:allow-inner-size",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-hide",
"core:window:allow-start-resize-dragging",
"autostart:default",
"dialog:default",
"clipboard-manager:allow-write-text",
"allow-list-projects",
"allow-project-icon",
"allow-start-or-focus-cmd",
"allow-open-main-window",
"allow-quit-app"
]
}
+57
View File
@@ -0,0 +1,57 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "terminal",
"description": "Terminal-Fenster: PTY und angedocktes Panel.",
"windows": [
"term-*"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"core:window:allow-set-position",
"core:window:allow-outer-position",
"core:window:allow-inner-size",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-hide",
"core:window:allow-start-resize-dragging",
"autostart:default",
"dialog:default",
"clipboard-manager:allow-write-text",
"allow-archive-create-doc",
"allow-archive-create-folder",
"allow-archive-delete",
"allow-archive-move",
"allow-archive-move-folder",
"allow-archive-rename",
"allow-archive-read",
"allow-archive-write",
"allow-archive-set-title",
"allow-buffer-read",
"allow-commands-delete",
"allow-enabled-modules",
"allow-list-projects",
"allow-open-panel-window",
"allow-panel-archive-cmd",
"allow-panel-archive-dir-cmd",
"allow-panel-load",
"allow-panel-set",
"allow-pool-label",
"allow-project-icon",
"allow-reveal-path-cmd",
"allow-search-run",
"allow-set-terminal-font-size",
"allow-spellcheck-lang",
"allow-term-log",
"allow-term-resize",
"allow-term-start",
"allow-term-write",
"allow-terminal-font-size",
"allow-todos-add",
"allow-todos-delete",
"allow-todos-update",
"allow-wiki-open"
]
}
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-add-project"
description = "Enables the add_project command without any pre-configured scope."
commands.allow = ["add_project"]
[[permission]]
identifier = "deny-add-project"
description = "Denies the add_project command without any pre-configured scope."
commands.deny = ["add_project"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-add-work-dir"
description = "Enables the add_work_dir command without any pre-configured scope."
commands.allow = ["add_work_dir"]
[[permission]]
identifier = "deny-add-work-dir"
description = "Denies the add_work_dir command without any pre-configured scope."
commands.deny = ["add_work_dir"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-create-doc"
description = "Enables the archive_create_doc command without any pre-configured scope."
commands.allow = ["archive_create_doc"]
[[permission]]
identifier = "deny-archive-create-doc"
description = "Denies the archive_create_doc command without any pre-configured scope."
commands.deny = ["archive_create_doc"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-create-folder"
description = "Enables the archive_create_folder command without any pre-configured scope."
commands.allow = ["archive_create_folder"]
[[permission]]
identifier = "deny-archive-create-folder"
description = "Denies the archive_create_folder command without any pre-configured scope."
commands.deny = ["archive_create_folder"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-delete"
description = "Enables the archive_delete command without any pre-configured scope."
commands.allow = ["archive_delete"]
[[permission]]
identifier = "deny-archive-delete"
description = "Denies the archive_delete command without any pre-configured scope."
commands.deny = ["archive_delete"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-docs-cmd"
description = "Enables the archive_docs_cmd command without any pre-configured scope."
commands.allow = ["archive_docs_cmd"]
[[permission]]
identifier = "deny-archive-docs-cmd"
description = "Denies the archive_docs_cmd command without any pre-configured scope."
commands.deny = ["archive_docs_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-move"
description = "Enables the archive_move command without any pre-configured scope."
commands.allow = ["archive_move"]
[[permission]]
identifier = "deny-archive-move"
description = "Denies the archive_move command without any pre-configured scope."
commands.deny = ["archive_move"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-move-folder"
description = "Enables the archive_move_folder command without any pre-configured scope."
commands.allow = ["archive_move_folder"]
[[permission]]
identifier = "deny-archive-move-folder"
description = "Denies the archive_move_folder command without any pre-configured scope."
commands.deny = ["archive_move_folder"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-read"
description = "Enables the archive_read command without any pre-configured scope."
commands.allow = ["archive_read"]
[[permission]]
identifier = "deny-archive-read"
description = "Denies the archive_read command without any pre-configured scope."
commands.deny = ["archive_read"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-rename"
description = "Enables the archive_rename command without any pre-configured scope."
commands.allow = ["archive_rename"]
[[permission]]
identifier = "deny-archive-rename"
description = "Denies the archive_rename command without any pre-configured scope."
commands.deny = ["archive_rename"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-set-title"
description = "Enables the archive_set_title command without any pre-configured scope."
commands.allow = ["archive_set_title"]
[[permission]]
identifier = "deny-archive-set-title"
description = "Denies the archive_set_title command without any pre-configured scope."
commands.deny = ["archive_set_title"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-archive-write"
description = "Enables the archive_write command without any pre-configured scope."
commands.allow = ["archive_write"]
[[permission]]
identifier = "deny-archive-write"
description = "Denies the archive_write command without any pre-configured scope."
commands.deny = ["archive_write"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-assign-pool"
description = "Enables the assign_pool command without any pre-configured scope."
commands.allow = ["assign_pool"]
[[permission]]
identifier = "deny-assign-pool"
description = "Denies the assign_pool command without any pre-configured scope."
commands.deny = ["assign_pool"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-buffer-read"
description = "Enables the buffer_read command without any pre-configured scope."
commands.allow = ["buffer_read"]
[[permission]]
identifier = "deny-buffer-read"
description = "Denies the buffer_read command without any pre-configured scope."
commands.deny = ["buffer_read"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-change-archive-home-cmd"
description = "Enables the change_archive_home_cmd command without any pre-configured scope."
commands.allow = ["change_archive_home_cmd"]
[[permission]]
identifier = "deny-change-archive-home-cmd"
description = "Denies the change_archive_home_cmd command without any pre-configured scope."
commands.deny = ["change_archive_home_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-clear-archive-home-cmd"
description = "Enables the clear_archive_home_cmd command without any pre-configured scope."
commands.allow = ["clear_archive_home_cmd"]
[[permission]]
identifier = "deny-clear-archive-home-cmd"
description = "Denies the clear_archive_home_cmd command without any pre-configured scope."
commands.deny = ["clear_archive_home_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-commands-delete"
description = "Enables the commands_delete command without any pre-configured scope."
commands.allow = ["commands_delete"]
[[permission]]
identifier = "deny-commands-delete"
description = "Denies the commands_delete command without any pre-configured scope."
commands.deny = ["commands_delete"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-commands-read"
description = "Enables the commands_read command without any pre-configured scope."
commands.allow = ["commands_read"]
[[permission]]
identifier = "deny-commands-read"
description = "Denies the commands_read command without any pre-configured scope."
commands.deny = ["commands_read"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-create-apikey-pool"
description = "Enables the create_apikey_pool command without any pre-configured scope."
commands.allow = ["create_apikey_pool"]
[[permission]]
identifier = "deny-create-apikey-pool"
description = "Denies the create_apikey_pool command without any pre-configured scope."
commands.deny = ["create_apikey_pool"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-create-oauth-pool"
description = "Enables the create_oauth_pool command without any pre-configured scope."
commands.allow = ["create_oauth_pool"]
[[permission]]
identifier = "deny-create-oauth-pool"
description = "Denies the create_oauth_pool command without any pre-configured scope."
commands.deny = ["create_oauth_pool"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-create-project-full"
description = "Enables the create_project_full command without any pre-configured scope."
commands.allow = ["create_project_full"]
[[permission]]
identifier = "deny-create-project-full"
description = "Denies the create_project_full command without any pre-configured scope."
commands.deny = ["create_project_full"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-create-reference-pool"
description = "Enables the create_reference_pool command without any pre-configured scope."
commands.allow = ["create_reference_pool"]
[[permission]]
identifier = "deny-create-reference-pool"
description = "Denies the create_reference_pool command without any pre-configured scope."
commands.deny = ["create_reference_pool"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-default-config-dir"
description = "Enables the default_config_dir command without any pre-configured scope."
commands.allow = ["default_config_dir"]
[[permission]]
identifier = "deny-default-config-dir"
description = "Denies the default_config_dir command without any pre-configured scope."
commands.deny = ["default_config_dir"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-delete-pool"
description = "Enables the delete_pool command without any pre-configured scope."
commands.allow = ["delete_pool"]
[[permission]]
identifier = "deny-delete-pool"
description = "Denies the delete_pool command without any pre-configured scope."
commands.deny = ["delete_pool"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-delete-preview"
description = "Enables the delete_preview command without any pre-configured scope."
commands.allow = ["delete_preview"]
[[permission]]
identifier = "deny-delete-preview"
description = "Denies the delete_preview command without any pre-configured scope."
commands.deny = ["delete_preview"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-delete-project-scoped"
description = "Enables the delete_project_scoped command without any pre-configured scope."
commands.allow = ["delete_project_scoped"]
[[permission]]
identifier = "deny-delete-project-scoped"
description = "Denies the delete_project_scoped command without any pre-configured scope."
commands.deny = ["delete_project_scoped"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-enabled-modules"
description = "Enables the enabled_modules command without any pre-configured scope."
commands.allow = ["enabled_modules"]
[[permission]]
identifier = "deny-enabled-modules"
description = "Denies the enabled_modules command without any pre-configured scope."
commands.deny = ["enabled_modules"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-keychain-status"
description = "Enables the keychain_status command without any pre-configured scope."
commands.allow = ["keychain_status"]
[[permission]]
identifier = "deny-keychain-status"
description = "Denies the keychain_status command without any pre-configured scope."
commands.deny = ["keychain_status"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-link-pool-runtime"
description = "Enables the link_pool_runtime command without any pre-configured scope."
commands.allow = ["link_pool_runtime"]
[[permission]]
identifier = "deny-link-pool-runtime"
description = "Denies the link_pool_runtime command without any pre-configured scope."
commands.deny = ["link_pool_runtime"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-list-pools"
description = "Enables the list_pools command without any pre-configured scope."
commands.allow = ["list_pools"]
[[permission]]
identifier = "deny-list-pools"
description = "Denies the list_pools command without any pre-configured scope."
commands.deny = ["list_pools"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-list-projects"
description = "Enables the list_projects command without any pre-configured scope."
commands.allow = ["list_projects"]
[[permission]]
identifier = "deny-list-projects"
description = "Denies the list_projects command without any pre-configured scope."
commands.deny = ["list_projects"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-module-registry"
description = "Enables the module_registry command without any pre-configured scope."
commands.allow = ["module_registry"]
[[permission]]
identifier = "deny-module-registry"
description = "Denies the module_registry command without any pre-configured scope."
commands.deny = ["module_registry"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-oauth-login"
description = "Enables the oauth_login command without any pre-configured scope."
commands.allow = ["oauth_login"]
[[permission]]
identifier = "deny-oauth-login"
description = "Denies the oauth_login command without any pre-configured scope."
commands.deny = ["oauth_login"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-open-main-window"
description = "Enables the open_main_window command without any pre-configured scope."
commands.allow = ["open_main_window"]
[[permission]]
identifier = "deny-open-main-window"
description = "Denies the open_main_window command without any pre-configured scope."
commands.deny = ["open_main_window"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-open-panel-window"
description = "Enables the open_panel_window command without any pre-configured scope."
commands.allow = ["open_panel_window"]
[[permission]]
identifier = "deny-open-panel-window"
description = "Denies the open_panel_window command without any pre-configured scope."
commands.deny = ["open_panel_window"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-open-terminal"
description = "Enables the open_terminal command without any pre-configured scope."
commands.allow = ["open_terminal"]
[[permission]]
identifier = "deny-open-terminal"
description = "Denies the open_terminal command without any pre-configured scope."
commands.deny = ["open_terminal"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-panel-archive-cmd"
description = "Enables the panel_archive_cmd command without any pre-configured scope."
commands.allow = ["panel_archive_cmd"]
[[permission]]
identifier = "deny-panel-archive-cmd"
description = "Denies the panel_archive_cmd command without any pre-configured scope."
commands.deny = ["panel_archive_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-panel-archive-dir-cmd"
description = "Enables the panel_archive_dir_cmd command without any pre-configured scope."
commands.allow = ["panel_archive_dir_cmd"]
[[permission]]
identifier = "deny-panel-archive-dir-cmd"
description = "Denies the panel_archive_dir_cmd command without any pre-configured scope."
commands.deny = ["panel_archive_dir_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-panel-load"
description = "Enables the panel_load command without any pre-configured scope."
commands.allow = ["panel_load"]
[[permission]]
identifier = "deny-panel-load"
description = "Denies the panel_load command without any pre-configured scope."
commands.deny = ["panel_load"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-panel-read"
description = "Enables the panel_read command without any pre-configured scope."
commands.allow = ["panel_read"]
[[permission]]
identifier = "deny-panel-read"
description = "Denies the panel_read command without any pre-configured scope."
commands.deny = ["panel_read"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-panel-set"
description = "Enables the panel_set command without any pre-configured scope."
commands.allow = ["panel_set"]
[[permission]]
identifier = "deny-panel-set"
description = "Denies the panel_set command without any pre-configured scope."
commands.deny = ["panel_set"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-pool-label"
description = "Enables the pool_label command without any pre-configured scope."
commands.allow = ["pool_label"]
[[permission]]
identifier = "deny-pool-label"
description = "Denies the pool_label command without any pre-configured scope."
commands.deny = ["pool_label"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-project-icon"
description = "Enables the project_icon command without any pre-configured scope."
commands.allow = ["project_icon"]
[[permission]]
identifier = "deny-project-icon"
description = "Denies the project_icon command without any pre-configured scope."
commands.deny = ["project_icon"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-project-work-dirs"
description = "Enables the project_work_dirs command without any pre-configured scope."
commands.allow = ["project_work_dirs"]
[[permission]]
identifier = "deny-project-work-dirs"
description = "Denies the project_work_dirs command without any pre-configured scope."
commands.deny = ["project_work_dirs"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-quit-app"
description = "Enables the quit_app command without any pre-configured scope."
commands.allow = ["quit_app"]
[[permission]]
identifier = "deny-quit-app"
description = "Denies the quit_app command without any pre-configured scope."
commands.deny = ["quit_app"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-remove-work-dir"
description = "Enables the remove_work_dir command without any pre-configured scope."
commands.allow = ["remove_work_dir"]
[[permission]]
identifier = "deny-remove-work-dir"
description = "Denies the remove_work_dir command without any pre-configured scope."
commands.deny = ["remove_work_dir"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-rename-pool"
description = "Enables the rename_pool command without any pre-configured scope."
commands.allow = ["rename_pool"]
[[permission]]
identifier = "deny-rename-pool"
description = "Denies the rename_pool command without any pre-configured scope."
commands.deny = ["rename_pool"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-restart-project"
description = "Enables the restart_project command without any pre-configured scope."
commands.allow = ["restart_project"]
[[permission]]
identifier = "deny-restart-project"
description = "Denies the restart_project command without any pre-configured scope."
commands.deny = ["restart_project"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-reveal-path-cmd"
description = "Enables the reveal_path_cmd command without any pre-configured scope."
commands.allow = ["reveal_path_cmd"]
[[permission]]
identifier = "deny-reveal-path-cmd"
description = "Denies the reveal_path_cmd command without any pre-configured scope."
commands.deny = ["reveal_path_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-search-read"
description = "Enables the search_read command without any pre-configured scope."
commands.allow = ["search_read"]
[[permission]]
identifier = "deny-search-read"
description = "Denies the search_read command without any pre-configured scope."
commands.deny = ["search_read"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-search-run"
description = "Enables the search_run command without any pre-configured scope."
commands.allow = ["search_run"]
[[permission]]
identifier = "deny-search-run"
description = "Denies the search_run command without any pre-configured scope."
commands.deny = ["search_run"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-apikey"
description = "Enables the set_apikey command without any pre-configured scope."
commands.allow = ["set_apikey"]
[[permission]]
identifier = "deny-set-apikey"
description = "Denies the set_apikey command without any pre-configured scope."
commands.deny = ["set_apikey"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-archive-home-cmd"
description = "Enables the set_archive_home_cmd command without any pre-configured scope."
commands.allow = ["set_archive_home_cmd"]
[[permission]]
identifier = "deny-set-archive-home-cmd"
description = "Denies the set_archive_home_cmd command without any pre-configured scope."
commands.deny = ["set_archive_home_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-module"
description = "Enables the set_module command without any pre-configured scope."
commands.allow = ["set_module"]
[[permission]]
identifier = "deny-set-module"
description = "Denies the set_module command without any pre-configured scope."
commands.deny = ["set_module"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-project-dir"
description = "Enables the set_project_dir command without any pre-configured scope."
commands.allow = ["set_project_dir"]
[[permission]]
identifier = "deny-set-project-dir"
description = "Denies the set_project_dir command without any pre-configured scope."
commands.deny = ["set_project_dir"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-sync-setting"
description = "Enables the set_sync_setting command without any pre-configured scope."
commands.allow = ["set_sync_setting"]
[[permission]]
identifier = "deny-set-sync-setting"
description = "Denies the set_sync_setting command without any pre-configured scope."
commands.deny = ["set_sync_setting"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-terminal-config"
description = "Enables the set_terminal_config command without any pre-configured scope."
commands.allow = ["set_terminal_config"]
[[permission]]
identifier = "deny-set-terminal-config"
description = "Denies the set_terminal_config command without any pre-configured scope."
commands.deny = ["set_terminal_config"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-terminal-font-size"
description = "Enables the set_terminal_font_size command without any pre-configured scope."
commands.allow = ["set_terminal_font_size"]
[[permission]]
identifier = "deny-set-terminal-font-size"
description = "Denies the set_terminal_font_size command without any pre-configured scope."
commands.deny = ["set_terminal_font_size"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-set-todo"
description = "Enables the set_todo command without any pre-configured scope."
commands.allow = ["set_todo"]
[[permission]]
identifier = "deny-set-todo"
description = "Denies the set_todo command without any pre-configured scope."
commands.deny = ["set_todo"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-spellcheck-lang"
description = "Enables the spellcheck_lang command without any pre-configured scope."
commands.allow = ["spellcheck_lang"]
[[permission]]
identifier = "deny-spellcheck-lang"
description = "Denies the spellcheck_lang command without any pre-configured scope."
commands.deny = ["spellcheck_lang"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-start-or-focus-cmd"
description = "Enables the start_or_focus_cmd command without any pre-configured scope."
commands.allow = ["start_or_focus_cmd"]
[[permission]]
identifier = "deny-start-or-focus-cmd"
description = "Denies the start_or_focus_cmd command without any pre-configured scope."
commands.deny = ["start_or_focus_cmd"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-stop-project"
description = "Enables the stop_project command without any pre-configured scope."
commands.allow = ["stop_project"]
[[permission]]
identifier = "deny-stop-project"
description = "Denies the stop_project command without any pre-configured scope."
commands.deny = ["stop_project"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-sync-setting"
description = "Enables the sync_setting command without any pre-configured scope."
commands.allow = ["sync_setting"]
[[permission]]
identifier = "deny-sync-setting"
description = "Denies the sync_setting command without any pre-configured scope."
commands.deny = ["sync_setting"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-term-log"
description = "Enables the term_log command without any pre-configured scope."
commands.allow = ["term_log"]
[[permission]]
identifier = "deny-term-log"
description = "Denies the term_log command without any pre-configured scope."
commands.deny = ["term_log"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-term-resize"
description = "Enables the term_resize command without any pre-configured scope."
commands.allow = ["term_resize"]
[[permission]]
identifier = "deny-term-resize"
description = "Denies the term_resize command without any pre-configured scope."
commands.deny = ["term_resize"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-term-start"
description = "Enables the term_start command without any pre-configured scope."
commands.allow = ["term_start"]
[[permission]]
identifier = "deny-term-start"
description = "Denies the term_start command without any pre-configured scope."
commands.deny = ["term_start"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-term-write"
description = "Enables the term_write command without any pre-configured scope."
commands.allow = ["term_write"]
[[permission]]
identifier = "deny-term-write"
description = "Denies the term_write command without any pre-configured scope."
commands.deny = ["term_write"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-terminal-font-size"
description = "Enables the terminal_font_size command without any pre-configured scope."
commands.allow = ["terminal_font_size"]
[[permission]]
identifier = "deny-terminal-font-size"
description = "Denies the terminal_font_size command without any pre-configured scope."
commands.deny = ["terminal_font_size"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-todo-state"
description = "Enables the todo_state command without any pre-configured scope."
commands.allow = ["todo_state"]
[[permission]]
identifier = "deny-todo-state"
description = "Denies the todo_state command without any pre-configured scope."
commands.deny = ["todo_state"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-todos-add"
description = "Enables the todos_add command without any pre-configured scope."
commands.allow = ["todos_add"]
[[permission]]
identifier = "deny-todos-add"
description = "Denies the todos_add command without any pre-configured scope."
commands.deny = ["todos_add"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-todos-delete"
description = "Enables the todos_delete command without any pre-configured scope."
commands.allow = ["todos_delete"]
[[permission]]
identifier = "deny-todos-delete"
description = "Denies the todos_delete command without any pre-configured scope."
commands.deny = ["todos_delete"]
@@ -0,0 +1,11 @@
# Automatically generated - DO NOT EDIT!
[[permission]]
identifier = "allow-todos-update"
description = "Enables the todos_update command without any pre-configured scope."
commands.allow = ["todos_update"]
[[permission]]
identifier = "deny-todos-update"
description = "Denies the todos_update command without any pre-configured scope."
commands.deny = ["todos_update"]

Some files were not shown because too many files have changed in this diff Show More