feat: Flatpak-Paket (Vorbereitung für Flathub)

- packaging/flatpak/eu.pansi.ink-notes.yml: Runtime freedesktop 26.08,
  Rust aus rust-stable, Abhängigkeiten offline aus cargo-sources.json.
  Rechte: Wayland/X11, GPU, Lesen von Bildern/Downloads/Schreibtisch,
  Schreiben in den Dokumente-Ordner (kein Zugriff auf den ganzen
  Home-Ordner).
- Im Flatpak sucht und installiert ink-notes keine Updates selbst
  (Vorgabe von Flathub); der Update-Dialog verweist auf die
  Software-Verwaltung.
- Export-Ordner aus user-dirs.dirs (z. B. ~/Dokumente) statt fest
  ~/Documents; nur dieser Ordner ist im Flatpak beschreibbar.
- Metainfo und Desktop-Datei auf Englisch mit deutscher Übersetzung,
  Release-Einträge, unterstützte Eingabegeräte.
- Test: cargo-sources.json deckt Cargo.lock ab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 16:18:37 +02:00
co-authored by Claude Opus 5.5
parent dd702524ca
commit e399c357d8
12 changed files with 5709 additions and 39 deletions
+12
View File
@@ -74,6 +74,7 @@ Die Oberfläche gibt es auf Deutsch und Englisch; ink-notes folgt der Systemspra
### Updates ### Updates
- Die App prüft auf neue Versionen auf Gitea und installiert sie auf Wunsch. Installiert wird nur, wenn die Minisign-Signatur und die SHA-256-Prüfsumme stimmen (Details unten). - Die App prüft auf neue Versionen auf Gitea und installiert sie auf Wunsch. Installiert wird nur, wenn die Minisign-Signatur und die SHA-256-Prüfsumme stimmen (Details unten).
- Als Flatpak sucht ink-notes nicht selbst nach Updates; die kommen dann über die Software-Verwaltung (`flatpak update`).
--- ---
@@ -190,6 +191,17 @@ Mehr dazu in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md), das Speicherformat
Veröffentlicht wird über Gitea Actions ([`.gitea/workflows/release.yaml`](.gitea/workflows/release.yaml)), sobald ein Tag `vX.Y.Z` gepusht wird: Tests, Pakete bauen, signieren, Release anlegen. Manuell geht es mit `GITEA_TOKEN=… ./scripts/publish-gitea-release.sh`. Veröffentlicht wird über Gitea Actions ([`.gitea/workflows/release.yaml`](.gitea/workflows/release.yaml)), sobald ein Tag `vX.Y.Z` gepusht wird: Tests, Pakete bauen, signieren, Release anlegen. Manuell geht es mit `GITEA_TOKEN=… ./scripts/publish-gitea-release.sh`.
### Flatpak
Das Manifest liegt in [`packaging/flatpak/`](packaging/flatpak/) (Runtime `org.freedesktop.Platform` 26.08, Rust aus der SDK-Erweiterung `rust-stable`). Lokal bauen und installieren:
```bash
flatpak-builder --user --install --force-clean target/flatpak-build packaging/flatpak/eu.pansi.ink-notes.yml
flatpak run eu.pansi.ink-notes
```
Nach jeder Änderung an `Cargo.lock` muss `packaging/flatpak/cargo-sources.json` mit dem [flatpak-cargo-generator](https://github.com/flatpak/flatpak-builder-tools/tree/master/cargo) neu erzeugt werden (`python3 flatpak-cargo-generator.py Cargo.lock -o packaging/flatpak/cargo-sources.json`). In der Sandbox darf ink-notes Bilder, Downloads und Schreibtisch lesen und in den Dokumente-Ordner exportieren; die Notizbücher liegen unter `~/.var/app/eu.pansi.ink-notes/`.
### Release-Signatur einrichten (einmalig) ### Release-Signatur einrichten (einmalig)
Updates werden in der App nur mit gültiger Minisign-Signatur installiert. Signiert wird mit [rsign2](https://crates.io/crates/rsign2) (`cargo install rsign2 --locked`). Schlüsselpaar ohne Passwort erzeugen (es wird nur als CI-Secret verwendet): Updates werden in der App nur mit gültiger Minisign-Signatur installiert. Signiert wird mit [rsign2](https://crates.io/crates/rsign2) (`cargo install rsign2 --locked`). Schlüsselpaar ohne Passwort erzeugen (es wird nur als CI-Secret verwendet):
+9 -5
View File
@@ -1,18 +1,22 @@
[Desktop Entry] [Desktop Entry]
Version=1.0 Version=1.0
Name=ink-notes Name=ink-notes
GenericName=Notizbuch- & Inking-App GenericName=Note-Taking & Inking App
Comment=Handschriftliche Notizen mit Stift, Touch und Maus; PDFs beschriften GenericName[de]=Notizbuch- & Inking-App
Comment=Handwritten notes with pen, touch and mouse; annotate PDFs
Comment[de]=Handschriftliche Notizen mit Stift, Touch und Maus; PDFs beschriften
Exec=ink-notes %F Exec=ink-notes %F
Icon=eu.pansi.ink-notes Icon=eu.pansi.ink-notes
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Office;Graphics;Utility; Categories=Office;Graphics;
Keywords=Note;Notiz;Pen;Stift;Ink;Inking;Stylus;Drawing;Zeichnen;PDF;Vector;Text;Annotation; Keywords=Note;Pen;Ink;Inking;Stylus;Drawing;PDF;Vector;Text;Annotation;
Keywords[de]=Notiz;Stift;Ink;Inking;Stylus;Zeichnen;PDF;Vektor;Text;Annotation;
StartupWMClass=eu.pansi.ink-notes StartupWMClass=eu.pansi.ink-notes
MimeType=application/x-inknotes; MimeType=application/x-inknotes;
Actions=NewWindow; Actions=NewWindow;
[Desktop Action NewWindow] [Desktop Action NewWindow]
Name=Neues Fenster Name=New Window
Name[de]=Neues Fenster
Exec=ink-notes Exec=ink-notes
+59 -13
View File
@@ -4,22 +4,34 @@
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license> <project_license>MIT</project_license>
<name>ink-notes</name> <name>ink-notes</name>
<summary>Notizbuch-App für Stift, Touch und Maus</summary> <summary>Handwritten notes with pen, touch and mouse</summary>
<summary xml:lang="de">Notizbuch-App für Stift, Touch und Maus</summary>
<description> <description>
<p> <p>
ink-notes ist eine Notizbuch-App für Linux, geschrieben in Rust mit wgpu. Handschrift wird als ink-notes is a notebook app for Linux tablets and convertibles. Handwriting is stored as
vectors; write with a pen, your finger, a touchpad or a mouse.
</p>
<p xml:lang="de">
ink-notes ist eine Notizbuch-App für Linux-Tablets und Convertibles. Handschrift wird als
Vektoren gespeichert; geschrieben wird mit Stift, Finger, Touchpad oder Maus. Vektoren gespeichert; geschrieben wird mit Stift, Finger, Touchpad oder Maus.
</p> </p>
<p>Funktionen:</p> <p>Features:</p>
<p xml:lang="de">Funktionen:</p>
<ul> <ul>
<li>Stift und Textmarker mit Druckstärke, Glättung beim Schreiben und Ecken-Erhaltung</li> <li>Pens and highlighter with pressure, smoothing while writing and sharp corners</li>
<li>Radierer für ganze Striche, Teilstriche oder nur Textmarker; virtuelles Lineal</li> <li xml:lang="de">Stifte und Textmarker mit Druckstärke, Glättung beim Schreiben und Ecken-Erhaltung</li>
<li>Formerkennung (Linien, Pfeile, Kreise, Ellipsen, Rechtecke, Dreiecke, Rauten, Koordinatensysteme)</li> <li>Shape recognition: hold at the end of a stroke to get clean lines, arrows, circles, rectangles, triangles and coordinate systems</li>
<li>Auswählen, Verschieben, Drehen, Skalieren und Umfärben; Rückgängig für alle Änderungen</li> <li xml:lang="de">Formerkennung: am Strichende halten für saubere Linien, Pfeile, Kreise, Rechtecke, Dreiecke und Koordinatensysteme</li>
<li>Textfelder, Bilder und PDF-Import zum Beschriften (Querformat-Folien bekommen Querformat-Seiten)</li> <li>Eraser for whole strokes, partial strokes or highlighter only; virtual ruler</li>
<li>Durchgehendes Scrollen, Zoom von 25 bis 1000 Prozent, Papier- und Musterfarben</li> <li xml:lang="de">Radierer für ganze Striche, Teilstriche oder nur Textmarker; virtuelles Lineal</li>
<li>Mehrere Notizbücher, automatisches Speichern im Hintergrund</li> <li>Select, move, rotate, scale and recolor; undo for every change</li>
<li>Export als PDF (mit durchsuchbarem Text), PNG oder SVG</li> <li xml:lang="de">Auswählen, Verschieben, Drehen, Skalieren und Umfärben; Rückgängig für alle Änderungen</li>
<li>Text boxes, images and PDF import to annotate lecture slides and worksheets</li>
<li xml:lang="de">Textfelder, Bilder und PDF-Import zum Beschriften von Vorlesungsfolien und Übungsblättern</li>
<li>Notebooks in folders, continuous scrolling, automatic saving in the background</li>
<li xml:lang="de">Notizbücher in Ordnern, durchgehendes Scrollen, automatisches Speichern im Hintergrund</li>
<li>Export as PDF (with searchable text), PNG or SVG</li>
<li xml:lang="de">Export als PDF (mit durchsuchbarem Text), PNG oder SVG</li>
</ul> </ul>
</description> </description>
<launchable type="desktop-id">eu.pansi.ink-notes.desktop</launchable> <launchable type="desktop-id">eu.pansi.ink-notes.desktop</launchable>
@@ -29,12 +41,46 @@
<categories> <categories>
<category>Office</category> <category>Office</category>
<category>Graphics</category> <category>Graphics</category>
<category>Utility</category>
</categories> </categories>
<keywords>
<keyword>notes</keyword>
<keyword>handwriting</keyword>
<keyword>stylus</keyword>
<keyword>pen</keyword>
<keyword>PDF</keyword>
<keyword>annotate</keyword>
</keywords>
<supports>
<control>pointing</control>
<control>touch</control>
<control>tablet</control>
</supports>
<developer id="eu.pansi"> <developer id="eu.pansi">
<name>Harald</name> <name>Harald Pansi</name>
</developer> </developer>
<url type="homepage">https://gitea.pansi.eu/harald/ink-notes</url> <url type="homepage">https://gitea.pansi.eu/harald/ink-notes</url>
<url type="bugtracker">https://gitea.pansi.eu/harald/ink-notes/issues</url> <url type="bugtracker">https://gitea.pansi.eu/harald/ink-notes/issues</url>
<url type="vcs-browser">https://gitea.pansi.eu/harald/ink-notes</url>
<branding>
<color type="primary" scheme_preference="light">#a8c8f0</color>
<color type="primary" scheme_preference="dark">#1f3a5f</color>
</branding>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases>
<release version="0.25.0" date="2026-10-09">
<description>
<p>English user interface; the language follows the system and can be changed in the menu. Holding to snap a shape tolerates a slightly trembling pen.</p>
</description>
</release>
<release version="0.24.0" date="2026-10-09">
<description>
<p>Axis cross from two crossing strokes.</p>
</description>
</release>
<release version="0.23.0" date="2026-10-09">
<description>
<p>The on-screen keyboard of GNOME appears when tapping a text field.</p>
</description>
</release>
</releases>
</component> </component>
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
# Flatpak-Manifest für ink-notes (Flathub).
#
# Lokal bauen und installieren (aus dem Repository-Wurzelverzeichnis):
# flatpak-builder --user --install --force-clean target/flatpak-build \
# packaging/flatpak/eu.pansi.ink-notes.yml
#
# cargo-sources.json nach jeder Änderung an Cargo.lock neu erzeugen:
# python3 flatpak-cargo-generator.py Cargo.lock -o packaging/flatpak/cargo-sources.json
# (flatpak-builder-tools, https://github.com/flatpak/flatpak-builder-tools/tree/master/cargo)
#
# Für die Einreichung bei Flathub die Quelle `type: dir` durch das Release-Tag ersetzen
# (type: git, url, tag, commit).
id: eu.pansi.ink-notes
runtime: org.freedesktop.Platform
runtime-version: '26.08'
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
command: ink-notes
finish-args:
- --socket=wayland
- --socket=fallback-x11
- --share=ipc
- --device=dri
# Die eingebaute Dateiauswahl (Bilder und PDFs einfügen) arbeitet ohne Desktop-Portal und
# bietet Bilder, Downloads und Schreibtisch an; Export (PDF, PNG, SVG) in den Dokumente-Ordner.
# Ziehen aus dem Dateimanager geht über das Dokumenten-Portal auch aus anderen Ordnern.
- --filesystem=xdg-pictures:ro
- --filesystem=xdg-download:ro
- --filesystem=xdg-desktop:ro
- --filesystem=xdg-documents
build-options:
append-path: /usr/lib/sdk/rust-stable/bin
env:
CARGO_HOME: /run/build/ink-notes/cargo
modules:
- name: ink-notes
buildsystem: simple
build-commands:
- cargo --offline build --release --locked
- install -Dm755 target/release/ink-notes /app/bin/ink-notes
- install -Dm644 packaging/eu.pansi.ink-notes.desktop /app/share/applications/eu.pansi.ink-notes.desktop
- install -Dm644 packaging/eu.pansi.ink-notes.metainfo.xml /app/share/metainfo/eu.pansi.ink-notes.metainfo.xml
- install -Dm644 assets/ink-notes.svg /app/share/icons/hicolor/scalable/apps/eu.pansi.ink-notes.svg
- install -Dm644 assets/ink-notes.png /app/share/icons/hicolor/256x256/apps/eu.pansi.ink-notes.png
sources:
- type: dir
path: ../..
skip:
- target
- .git
- .flatpak-builder
- ideas.txt
- issue.txt
- cargo-sources.json
+9 -5
View File
@@ -1,18 +1,22 @@
[Desktop Entry] [Desktop Entry]
Version=1.0 Version=1.0
Name=ink-notes Name=ink-notes
GenericName=Notizbuch- & Inking-App GenericName=Note-Taking & Inking App
Comment=Handschriftliche Notizen mit Stift, Touch und Maus; PDFs beschriften GenericName[de]=Notizbuch- & Inking-App
Comment=Handwritten notes with pen, touch and mouse; annotate PDFs
Comment[de]=Handschriftliche Notizen mit Stift, Touch und Maus; PDFs beschriften
Exec=ink-notes %F Exec=ink-notes %F
Icon=eu.pansi.ink-notes Icon=eu.pansi.ink-notes
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Office;Graphics;Utility; Categories=Office;Graphics;
Keywords=Note;Notiz;Pen;Stift;Ink;Inking;Stylus;Drawing;Zeichnen;PDF;Vector;Text;Annotation; Keywords=Note;Pen;Ink;Inking;Stylus;Drawing;PDF;Vector;Text;Annotation;
Keywords[de]=Notiz;Stift;Ink;Inking;Stylus;Zeichnen;PDF;Vektor;Text;Annotation;
StartupWMClass=eu.pansi.ink-notes StartupWMClass=eu.pansi.ink-notes
MimeType=application/x-inknotes; MimeType=application/x-inknotes;
Actions=NewWindow; Actions=NewWindow;
[Desktop Action NewWindow] [Desktop Action NewWindow]
Name=Neues Fenster Name=New Window
Name[de]=Neues Fenster
Exec=ink-notes Exec=ink-notes
+59 -13
View File
@@ -4,22 +4,34 @@
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license> <project_license>MIT</project_license>
<name>ink-notes</name> <name>ink-notes</name>
<summary>Notizbuch-App für Stift, Touch und Maus</summary> <summary>Handwritten notes with pen, touch and mouse</summary>
<summary xml:lang="de">Notizbuch-App für Stift, Touch und Maus</summary>
<description> <description>
<p> <p>
ink-notes ist eine Notizbuch-App für Linux, geschrieben in Rust mit wgpu. Handschrift wird als ink-notes is a notebook app for Linux tablets and convertibles. Handwriting is stored as
vectors; write with a pen, your finger, a touchpad or a mouse.
</p>
<p xml:lang="de">
ink-notes ist eine Notizbuch-App für Linux-Tablets und Convertibles. Handschrift wird als
Vektoren gespeichert; geschrieben wird mit Stift, Finger, Touchpad oder Maus. Vektoren gespeichert; geschrieben wird mit Stift, Finger, Touchpad oder Maus.
</p> </p>
<p>Funktionen:</p> <p>Features:</p>
<p xml:lang="de">Funktionen:</p>
<ul> <ul>
<li>Stift und Textmarker mit Druckstärke, Glättung beim Schreiben und Ecken-Erhaltung</li> <li>Pens and highlighter with pressure, smoothing while writing and sharp corners</li>
<li>Radierer für ganze Striche, Teilstriche oder nur Textmarker; virtuelles Lineal</li> <li xml:lang="de">Stifte und Textmarker mit Druckstärke, Glättung beim Schreiben und Ecken-Erhaltung</li>
<li>Formerkennung (Linien, Pfeile, Kreise, Ellipsen, Rechtecke, Dreiecke, Rauten, Koordinatensysteme)</li> <li>Shape recognition: hold at the end of a stroke to get clean lines, arrows, circles, rectangles, triangles and coordinate systems</li>
<li>Auswählen, Verschieben, Drehen, Skalieren und Umfärben; Rückgängig für alle Änderungen</li> <li xml:lang="de">Formerkennung: am Strichende halten für saubere Linien, Pfeile, Kreise, Rechtecke, Dreiecke und Koordinatensysteme</li>
<li>Textfelder, Bilder und PDF-Import zum Beschriften (Querformat-Folien bekommen Querformat-Seiten)</li> <li>Eraser for whole strokes, partial strokes or highlighter only; virtual ruler</li>
<li>Durchgehendes Scrollen, Zoom von 25 bis 1000 Prozent, Papier- und Musterfarben</li> <li xml:lang="de">Radierer für ganze Striche, Teilstriche oder nur Textmarker; virtuelles Lineal</li>
<li>Mehrere Notizbücher, automatisches Speichern im Hintergrund</li> <li>Select, move, rotate, scale and recolor; undo for every change</li>
<li>Export als PDF (mit durchsuchbarem Text), PNG oder SVG</li> <li xml:lang="de">Auswählen, Verschieben, Drehen, Skalieren und Umfärben; Rückgängig für alle Änderungen</li>
<li>Text boxes, images and PDF import to annotate lecture slides and worksheets</li>
<li xml:lang="de">Textfelder, Bilder und PDF-Import zum Beschriften von Vorlesungsfolien und Übungsblättern</li>
<li>Notebooks in folders, continuous scrolling, automatic saving in the background</li>
<li xml:lang="de">Notizbücher in Ordnern, durchgehendes Scrollen, automatisches Speichern im Hintergrund</li>
<li>Export as PDF (with searchable text), PNG or SVG</li>
<li xml:lang="de">Export als PDF (mit durchsuchbarem Text), PNG oder SVG</li>
</ul> </ul>
</description> </description>
<launchable type="desktop-id">eu.pansi.ink-notes.desktop</launchable> <launchable type="desktop-id">eu.pansi.ink-notes.desktop</launchable>
@@ -29,12 +41,46 @@
<categories> <categories>
<category>Office</category> <category>Office</category>
<category>Graphics</category> <category>Graphics</category>
<category>Utility</category>
</categories> </categories>
<keywords>
<keyword>notes</keyword>
<keyword>handwriting</keyword>
<keyword>stylus</keyword>
<keyword>pen</keyword>
<keyword>PDF</keyword>
<keyword>annotate</keyword>
</keywords>
<supports>
<control>pointing</control>
<control>touch</control>
<control>tablet</control>
</supports>
<developer id="eu.pansi"> <developer id="eu.pansi">
<name>Harald</name> <name>Harald Pansi</name>
</developer> </developer>
<url type="homepage">https://gitea.pansi.eu/harald/ink-notes</url> <url type="homepage">https://gitea.pansi.eu/harald/ink-notes</url>
<url type="bugtracker">https://gitea.pansi.eu/harald/ink-notes/issues</url> <url type="bugtracker">https://gitea.pansi.eu/harald/ink-notes/issues</url>
<url type="vcs-browser">https://gitea.pansi.eu/harald/ink-notes</url>
<branding>
<color type="primary" scheme_preference="light">#a8c8f0</color>
<color type="primary" scheme_preference="dark">#1f3a5f</color>
</branding>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases>
<release version="0.25.0" date="2026-10-09">
<description>
<p>English user interface; the language follows the system and can be changed in the menu. Holding to snap a shape tolerates a slightly trembling pen.</p>
</description>
</release>
<release version="0.24.0" date="2026-10-09">
<description>
<p>Axis cross from two crossing strokes.</p>
</description>
</release>
<release version="0.23.0" date="2026-10-09">
<description>
<p>The on-screen keyboard of GNOME appears when tapping a text field.</p>
</description>
</release>
</releases>
</component> </component>
+13 -2
View File
@@ -46,7 +46,9 @@ pub fn sanitize_filename_component(title: &str) -> String {
} }
} }
/// Ermittelt das Standard-Dokumentenverzeichnis ($XDG_DOCUMENTS_DIR mit Fallback ~/Documents). /// Ermittelt das Standard-Dokumentenverzeichnis: `$XDG_DOCUMENTS_DIR`, sonst der Eintrag in
/// `~/.config/user-dirs.dirs` (z. B. „Dokumente“), sonst `~/Documents`. Im Flatpak ist nur
/// dieser Ordner beschreibbar, der Fallback allein träfe bei deutschen Systemen daneben.
pub fn default_documents_dir() -> PathBuf { pub fn default_documents_dir() -> PathBuf {
if let Ok(dir) = std::env::var("XDG_DOCUMENTS_DIR") { if let Ok(dir) = std::env::var("XDG_DOCUMENTS_DIR") {
if !dir.trim().is_empty() { if !dir.trim().is_empty() {
@@ -56,7 +58,16 @@ pub fn default_documents_dir() -> PathBuf {
if let Ok(home) = std::env::var("HOME") { if let Ok(home) = std::env::var("HOME") {
if !home.trim().is_empty() { if !home.trim().is_empty() {
return PathBuf::from(home).join("Documents"); let home = PathBuf::from(home);
let user_dirs =
std::fs::read_to_string(home.join(".config/user-dirs.dirs")).unwrap_or_default();
if let Some(dir) =
crate::file_picker::parse_user_dir(&user_dirs, "XDG_DOCUMENTS_DIR", &home)
.filter(|dir| *dir != home)
{
return dir;
}
return home.join("Documents");
} }
} }
+2
View File
@@ -66,9 +66,11 @@ pub const EN: &[(&str, &str)] = &[
("Die beschädigte Originaldatei wurde in Quarantäne gesichert:", "The damaged original file was saved to quarantine:"), ("Die beschädigte Originaldatei wurde in Quarantäne gesichert:", "The damaged original file was saved to quarantine:"),
("Die Datei ist kein lesbares PDF", "The file is not a readable PDF"), ("Die Datei ist kein lesbares PDF", "The file is not a readable PDF"),
("Die einzige Seite eines Notizbuchs kann nicht gelöscht werden", "The only page of a notebook cannot be deleted"), ("Die einzige Seite eines Notizbuchs kann nicht gelöscht werden", "The only page of a notebook cannot be deleted"),
("Die Flatpak-Version wird über die Software-Verwaltung aktualisiert.", "The Flatpak version is updated through the software manager."),
("Die heruntergeladene Datei existiert nicht mehr.", "The downloaded file no longer exists."), ("Die heruntergeladene Datei existiert nicht mehr.", "The downloaded file no longer exists."),
("die oberste Ebene", "the top level"), ("die oberste Ebene", "the top level"),
("Die Zwischenablage ist leer", "The clipboard is empty"), ("Die Zwischenablage ist leer", "The clipboard is empty"),
("Diese Version wird über die Software-Verwaltung aktualisiert (z. B. GNOME Software oder „flatpak update“).", "This version is updated through the software manager (e.g. GNOME Software or “flatpak update”)."),
("Dieser Ordner ist leer.", "This folder is empty."), ("Dieser Ordner ist leer.", "This folder is empty."),
("Dokument '{title}' erstellt", "Document '{title}' created"), ("Dokument '{title}' erstellt", "Document '{title}' created"),
("Dokument '{title}' geöffnet", "Document '{title}' opened"), ("Dokument '{title}' geöffnet", "Document '{title}' opened"),
+14 -1
View File
@@ -4281,6 +4281,18 @@ pub fn render_ui(
ui.label(egui::RichText::new(format!("({})", runtime_name)).color(egui::Color32::from_gray(150))); ui.label(egui::RichText::new(format!("({})", runtime_name)).color(egui::Color32::from_gray(150)));
}); });
if !updater.runtime().self_update_allowed() {
ui.add_space(8.0);
ui.label(
egui::RichText::new(t!(
"Diese Version wird über die Software-Verwaltung aktualisiert \
(z. B. GNOME Software oder „flatpak update“)."
))
.color(egui::Color32::from_gray(210)),
);
return;
}
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(egui::RichText::new(t!("Server:")).color(egui::Color32::from_gray(180))); ui.label(egui::RichText::new(t!("Server:")).color(egui::Color32::from_gray(180)));
ui.label(egui::RichText::new(format!("{}/{}/{}", updater.config().server_url, updater.config().repo_owner, updater.config().repo_name)).color(egui::Color32::from_gray(210))); ui.label(egui::RichText::new(format!("{}/{}/{}", updater.config().server_url, updater.config().repo_owner, updater.config().repo_name)).color(egui::Color32::from_gray(210)));
@@ -4446,7 +4458,8 @@ pub fn render_ui(
let action_label = match updater.runtime() { let action_label = match updater.runtime() {
crate::updater::RuntimeEnvironment::AppImage { .. } => t!("🚀 AppImage aktualisieren & ausführen"), crate::updater::RuntimeEnvironment::AppImage { .. } => t!("🚀 AppImage aktualisieren & ausführen"),
crate::updater::RuntimeEnvironment::DebPackage { .. } => t!("📦 Paket mit Software-Center öffnen / installieren"), crate::updater::RuntimeEnvironment::DebPackage { .. } => t!("📦 Paket mit Software-Center öffnen / installieren"),
crate::updater::RuntimeEnvironment::PortableOrDev { .. } => t!("📂 Im Dateimanager anzeigen"), crate::updater::RuntimeEnvironment::PortableOrDev { .. }
| crate::updater::RuntimeEnvironment::Flatpak => t!("📂 Im Dateimanager anzeigen"),
}; };
ui.horizontal(|ui| { ui.horizontal(|ui| {
+41
View File
@@ -118,11 +118,20 @@ pub enum RuntimeEnvironment {
DebPackage { binary_path: PathBuf }, DebPackage { binary_path: PathBuf },
/// Aus Quellcode / Entwicklungs-Binary (z.B. target/debug oder target/release) /// Aus Quellcode / Entwicklungs-Binary (z.B. target/debug oder target/release)
PortableOrDev { binary_path: PathBuf }, PortableOrDev { binary_path: PathBuf },
/// Als Flatpak installiert: Updates kommen über die Software-Verwaltung, ink-notes lädt
/// selbst nichts herunter (Vorgabe von Flathub)
Flatpak,
} }
impl RuntimeEnvironment { impl RuntimeEnvironment {
/// Erkennt die aktuelle Laufzeitumgebung anhand der Umgebung und des Binärpfads. /// Erkennt die aktuelle Laufzeitumgebung anhand der Umgebung und des Binärpfads.
pub fn detect() -> Self { pub fn detect() -> Self {
if is_flatpak(
std::env::var("FLATPAK_ID").ok().as_deref(),
Path::new("/.flatpak-info").exists(),
) {
return RuntimeEnvironment::Flatpak;
}
let appimage_var = std::env::var("APPIMAGE").ok(); let appimage_var = std::env::var("APPIMAGE").ok();
let current_exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("ink-notes")); let current_exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("ink-notes"));
Self::detect_from(appimage_var.as_deref(), &current_exe) Self::detect_from(appimage_var.as_deref(), &current_exe)
@@ -156,8 +165,20 @@ impl RuntimeEnvironment {
RuntimeEnvironment::AppImage { .. } => t!("AppImage (Portabel)"), RuntimeEnvironment::AppImage { .. } => t!("AppImage (Portabel)"),
RuntimeEnvironment::DebPackage { .. } => t!("System-Paket (.deb)"), RuntimeEnvironment::DebPackage { .. } => t!("System-Paket (.deb)"),
RuntimeEnvironment::PortableOrDev { .. } => t!("Entwicklung / Lokal"), RuntimeEnvironment::PortableOrDev { .. } => t!("Entwicklung / Lokal"),
RuntimeEnvironment::Flatpak => "Flatpak",
} }
} }
/// Ob ink-notes selbst nach Updates suchen und sie installieren darf
pub fn self_update_allowed(&self) -> bool {
!matches!(self, RuntimeEnvironment::Flatpak)
}
}
/// Läuft ink-notes in einer Flatpak-Sandbox (`FLATPAK_ID` gesetzt oder `/.flatpak-info`
/// vorhanden)?
pub fn is_flatpak(flatpak_id: Option<&str>, info_file_exists: bool) -> bool {
flatpak_id.is_some_and(|id| !id.trim().is_empty()) || info_file_exists
} }
/// Bereinigt und validiert einen Asset-Dateinamen gegen Path Traversal Angriffe. /// Bereinigt und validiert einen Asset-Dateinamen gegen Path Traversal Angriffe.
@@ -578,6 +599,9 @@ impl UpdaterController {
/// Fragt im Hintergrund die Gitea-API ab. /// Fragt im Hintergrund die Gitea-API ab.
pub fn check_for_updates(&mut self) { pub fn check_for_updates(&mut self) {
if !self.runtime.self_update_allowed() {
return;
}
if matches!( if matches!(
self.status, self.status,
UpdaterStatus::Checking | UpdaterStatus::Downloading { .. } UpdaterStatus::Checking | UpdaterStatus::Downloading { .. }
@@ -887,6 +911,10 @@ impl UpdaterController {
} }
Ok(()) Ok(())
} }
RuntimeEnvironment::Flatpak => Err(t!(
"Die Flatpak-Version wird über die Software-Verwaltung aktualisiert."
)
.to_string()),
} }
} }
@@ -1074,6 +1102,19 @@ dcdeb7ac87d9e6cb8022e5a71bc7f2c56d779b4fce4efdd5bae5be3b0214884a ink-notes_0.1.
); );
} }
#[test]
fn test_flatpak_detection_disables_self_update() {
assert!(is_flatpak(Some("eu.pansi.ink-notes"), false));
assert!(is_flatpak(None, true));
assert!(!is_flatpak(Some(" "), false));
assert!(!is_flatpak(None, false));
assert!(!RuntimeEnvironment::Flatpak.self_update_allowed());
assert!(
RuntimeEnvironment::detect_from(None, Path::new("/usr/bin/ink-notes"))
.self_update_allowed()
);
}
#[test] #[test]
fn test_https_enforcement() { fn test_https_enforcement() {
let config = UpdaterConfig { let config = UpdaterConfig {
+33
View File
@@ -0,0 +1,33 @@
//! Das Flatpak baut ohne Netz aus `packaging/flatpak/cargo-sources.json`; jede Abhängigkeit
//! aus `Cargo.lock` muss dort stehen, sonst schlägt der Flatpak-Build fehl.
#[test]
fn cargo_sources_cover_cargo_lock() {
let lock = include_str!("../Cargo.lock");
let sources = include_str!("../packaging/flatpak/cargo-sources.json");
let mut missing = Vec::new();
for package in lock.split("[[package]]").skip(1) {
let field = |key: &str| {
package.lines().find_map(|line| {
line.strip_prefix(key)
.map(|rest| rest.trim().trim_matches('"').to_string())
})
};
// Nur Pakete aus crates.io (das eigene Paket hat keine Quelle)
if field("source = ").is_none() {
continue;
}
let (Some(name), Some(version)) = (field("name = "), field("version = ")) else {
continue;
};
let crate_file = format!("/{name}-{version}.crate\"");
if !sources.contains(&crate_file) {
missing.push(format!("{name} {version}"));
}
}
assert!(
missing.is_empty(),
"cargo-sources.json ist veraltet (flatpak-cargo-generator neu ausführen), es fehlen: {}",
missing.join(", ")
);
}