Spec
A small, FOSS office suite for the GNOME desktop, built as separate libadwaita apps that share a common scaffold. It completes the set started by Letters (word processor):
| App | Role | Status |
|---|---|---|
| Letters | Word processor | exists; migrating onto suite-common (reference consumer) |
| Tables | Spreadsheet (Excel-equivalent) | this suite |
| Decks | Presentation (PowerPoint-equivalent) | this suite |
This repo, suite-common, holds the shared code consumed by all three apps as a
meson subproject. It is extracted from Letters β Letters is both the source of the
pattern and the first consumer, so migrating Letters onto suite-common is how we dogfood
the extraction. Upstream Letters lives at
codeberg.org/eyekay/letters; the suite tracks the
fork at tuna-os/letters.
The Letters pattern (what we inherit)β
Letters gets its leverage from three layers, and so do Tables and Decks:
- Pure GTK4 / libadwaita chrome β Blueprint (
.blp) UI compiled to GtkBuilder:Adw.ApplicationWindow+Adw.ToolbarView+Adw.HeaderBar+Adw.TabView, preferences, shortcuts dialog, about dialog, menus, file dialogs, error toasts. - A
WebKit.WebViewdocument canvas β a small JS engine runs inside the webview and provides the actual editing surface. Letters uses a contenteditable HTML editor (src/editor.js); we swap in a best-of-breed JS engine per app. - In-process file-format libraries β Letters uses
pypandoc(DOCX/ODT/MD/HTML) and WeasyPrint (PDF), vendored into the Flatpak manifest. We use Python format libraries the same way; no server, fully offline/sandboxed.
Honest tradeoff: "pure libadwaita" describes the chrome. The document canvas is WebKit β exactly as in Letters. No native GTK widget does spreadsheet/slide heavy lifting, so this is the only path consistent with the existing app.
Engine choices (best-of-breed, per app)β
| App | Editing surface | Compute/render | File I/O (in-process Python) |
|---|---|---|---|
| Tables | Jspreadsheet CE (MIT) | HyperFormula (GPLv3-or-commercial) β 450+ Excel fns | python-calamine/openpyxl (xlsx), odfpy (ods), stdlib csv |
| Decks | Fabric.js (MIT) canvas | Reveal.js (MIT) present mode | python-pptx (pptx), odfpy (odp) |
Licensing note: Letters is GPLv3, so HyperFormula's GPLv3 option is compatible. All other engines are MIT. Alternative for Tables: FortuneSheet (MIT) bundles grid + formulas in one dependency at the cost of a React runtime β a fallback if CE+HyperFormula wiring is fiddly.
The WebKit β Python bridge (the core shared abstraction)β
Lifted from Letters src/window.py (new_webview() ~L287, run_js() ~L376):
- Python β JS:
webview.evaluate_javascript(code, ...)injects data/commands. - JS β Python: a
WebKit.UserContentManagerscript-message channel (ucm.connect("script-message-received::<name>", ...)) posts the document model back. - Open flow: Python reads file β converts to the engine's native JSON model β injects.
- Save flow: JS posts the model over the channel β Python writes the file.
This is the structural analogue of Letters' pypandoc.convert_file(...).
What suite-common providesβ
- App shell:
Adw.ApplicationWindow+Adw.TabViewwindow base, menus, about dialog. - WebKit bridge module:
new_webview(),run_js(), script-message registration, busy-cursor handling, offline/sandboxed webview settings. - Chrome partials (Blueprint): preferences scaffold, shortcuts dialog, error-toast helper, recent-files.
- File-I/O base class: abstract open/save (readβmodelβinject; postβwrite) with a trivial reference format, so each app implements only format adapters.
- Build glue: meson layout, gresource bundling of vendored JS, Blueprint compilation,
po/i18n skeleton, Flatpak manifest skeleton (GNOME 50 runtime), gnome-gui-spec audit.
Per-app repo layout (mirrors Letters src/)β
meson.build
io.github.hanthor.<app>.json # Flatpak: GNOME 50 runtime + vendored JS + pip libs (historical; apps ship as org.tunaos.*-rust)
data/ # icons, .desktop, gschema, appdata/metainfo
po/
src/
main.py
window.py # adapts Letters window.py: tabs + new_webview() + Python I/O bridge
window.blp
tab_page.blp
preferences.blp / preferences.py
shortcuts-dialog.blp
<app>.gresource.xml
<app>.in
engine.js # editor.js analogue: init Jspreadsheet+HyperFormula / Fabric+Reveal
styles.css
vendor/ # vendored minified UMD builds of the JS engines (gresource'd)
subprojects/suite-common/
Packagingβ
The Flatpak manifest mirrors net.codelogistics.letters.json:
- JS engines: vendored prebuilt UMD/minified bundles in
src/vendor/, listed in.gresource.xml,<script>-loaded from the HTML passed towebview.load_html(...). No Node runtime ships. - Python libs: added as
python3-*pip modules exactly like the existingpython3-pypandoc/python3-weasyprintblocks.
GNOME-GUI-spec complianceβ
Both apps target parity with Letters' audited baseline (85/92, see Letters'
AUDIT-GNOME-GUI-SPEC.md) using the gnome-gui-spec
tool. Compliance is a CI gate.
Verification (per app)β
flatpak-builder/ GNOME Builder builds the manifest (vendored engines + pip libs).- Launch; confirm libadwaita chrome (tabs, header bar, preferences, shortcuts) renders.
- App-specific round-trip tests (see each app's
SPEC.md). - Run the gnome-gui-spec audit; target Letters parity.