Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
620 changes: 620 additions & 0 deletions .github/workflows/flutter-linux-native.yml

Large diffs are not rendered by default.

399 changes: 117 additions & 282 deletions .github/workflows/flutter-linux.yml

Large diffs are not rendered by default.

40 changes: 25 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Markdown, Writerside, and Nextcloud Notes editor for Linux.
* **Writerside projects** — Open and create Writerside-compatible projects; edit Markdown and XML topics; manage instances, tables of contents, reusable TOC libraries, and project structure.
* **Nextcloud Notes** — Connect to a Nextcloud server, create and edit notes offline, synchronize changes, search and filter notes, manage categories and favorites, and work with attachments.
* **References and links** — Incoming/outgoing references, document/topic/heading target chooser, reviewed local Markdown rename/move, and portable Nextcloud note URLs. See [reference workflows](docs/references.md).
* **Navigation and search** — Files, Topics, and Outline views, tabbed documents, command palette, keyboard shortcuts, document search and replace, reviewed replacement across local workspaces, and Markdown TOC generation.
* **Navigation and search** — Files, Topics, and Outline views, tabbed documents, Actions search, configurable keyboard shortcuts, document search and replace, reviewed replacement across local workspaces, and Markdown TOC generation.
* **Technical documentation** — Local rendering of Mermaid, PlantUML, D2, fenced OpenAPI specifications, and MathJax mathematical expressions.
* **PDF and HTML publishing** — Export Markdown documents and Writerside instances to configurable PDF or portable offline HTML, with controls for layout, typography, tables of contents, heading numbering, and HTML styling.
* **Git integration for local workspaces** — Review changes and diffs, stage and unstage files, commit, create and switch branches, fetch, pull, push, inspect file and project history, compare historical versions, and restore earlier file versions.
Expand Down Expand Up @@ -58,6 +58,7 @@ sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
ninja-build \
pkg-config \
python3 \
xz-utils
```

Expand All @@ -67,17 +68,16 @@ components.
Prepare and run the application:

```bash
flutter doctor
flutter pub get
flutter pub get --enforce-lockfile
flutter run -d linux
```

Pass a Markdown file or documentation directory after `--` to open it at
startup:
Pass a Markdown file or documentation directory as a Dart entrypoint argument
to open it at startup:

```bash
flutter run -d linux -- /absolute/path/to/document.md
flutter run -d linux -- /absolute/path/to/documentation
flutter run -d linux --dart-entrypoint-args=/absolute/path/to/document.md
flutter run -d linux --dart-entrypoint-args=/absolute/path/to/documentation
```

Build and run the standard checks with:
Expand All @@ -91,9 +91,16 @@ flutter test
The build assembles BusyMark's runtime components. Packaged users do not need
separate Typst, Java, Node.js, or Chromium installations.

Linux development and packaging use the official Flutter SDK version pinned in
`pubspec.yaml` (currently 3.47.5). No custom Flutter engine is built or selected.
See [Linux development](docs/development/linux-engine.md) for build modes and
[compiled application checks](docs/development/application-smoke-tests.md) for
native and document regression commands. A successful widget suite or build
does not establish native release acceptance.

The CI workflow installs additional packages for headless X11/Wayland, browser,
PDF, and sandbox verification. Those packages are not required for an ordinary
local build; see [the Linux workflow](.github/workflows/flutter-linux.yml) when
local build; see [native acceptance](.github/workflows/flutter-linux-native.yml) when
reproducing release verification.

## Snap release and security builds
Expand All @@ -113,7 +120,7 @@ sha256sum ./busymark_*.snap

Inspect and test the exact file emitted by the final `snapcraft` command. A
failed build must not fall back to an older `.snap`. The `snap` job in
[the Linux workflow](.github/workflows/flutter-linux.yml) follows this route,
[native acceptance](.github/workflows/flutter-linux-native.yml) follows this route,
installs that selected artifact, and runs the strict-confinement smoke checks.
Build information embeds the original recipe for the workflow's identity check.

Expand All @@ -122,6 +129,8 @@ the Flutter payload in an installed Snap scaffold for quick development and
may retain that scaffold's Ubuntu libraries and bundled tools. Even with
`--no-install`, its output is not a dependency refresh and must not be used for
a release, a security update, or validation of `stage-packages` changes.
It selects Flutter from PATH; `BUSYMARK_FLUTTER_BIN` can select an executable
by command name or path. The selected SDK must match the pinned version.

## Screenshots

Expand All @@ -145,9 +154,10 @@ a release, a security update, or validation of `stage-packages` changes.
<sub><b>Reading view</b> for rendered documentation.</sub>
</td>
<td width="50%">
<img src="docs/screenshots/busymark-keyboard-shortcuts.png" alt="BusyMark keyboard shortcuts dialog">
<b>Keyboard Shortcuts</b> in Settings
<br>
<sub><b>Keyboard shortcuts</b> reference.</sub>
Find actions or record a combination, assign shortcuts, resolve conflicts,
and restore defaults. <a href="docs/search-and-replace.md#keyboard-shortcut-settings">Shortcut guide</a>.
</td>
</tr>
<tr>
Expand Down Expand Up @@ -202,17 +212,17 @@ BusyMark can open a **Nextcloud Notes** workspace alongside its local Markdown a
### Connect your account

1. Ensure the **Notes** app is enabled on your Nextcloud server.
2. On BusyMark’s welcome screen, choose **Nextcloud Notes**. You can also open **Settings → Nextcloud Notes**.
2. On BusyMark’s welcome screen, choose **Browse Notes** or **New note** (both Nextcloud actions). You can also open **Settings → Nextcloud Notes**.
3. Enter the full **HTTPS** address of your Nextcloud server and click **Connect**.
4. Complete authorization in your default web browser. BusyMark verifies Notes compatibility and opens the note workspace.

Once connected, select **Nextcloud Notes** on the welcome screen to reopen the workspace. Use **Settings → Nextcloud Notes** to open it, reconnect, or disconnect the account. **One Nextcloud account can be connected at a time.**
Once connected, select **Browse Notes** on Welcome to reopen normal Notes navigation, or **New note** to create and open one note. Individual notes also reopen through Recent or Documents search. Use **Settings → Nextcloud Notes** to open it, reconnect, or disconnect the account. **One Nextcloud account can be connected at a time.**

### Work with notes

* **Create and edit:** Create notes from the Notes sidebar and edit them using the normal Markdown editor. Use each note’s menu to change its title, category, or favorite status, save a separate local copy, or delete the note.
* **Find and organize:** Use All notes, Favorites, Uncategorized, Recovery, or a category and its descendants. Sort by activity or title. Ctrl-click toggles selection, Shift-click selects a range, and Ctrl+A selects the current list; the selected-notes action reviews batch favorite/category changes and reports partial outcomes.
* **Quick Open and search:** Ctrl+P finds titles/categories, including unopened cached notes; Ctrl+Shift+P opens the command palette. Notes search combines terms with AND and supports quoted phrases, `title:` / `category:` restrictions, whole words, snippets, and navigation to the match. Unsaved open text is included without forcing a save.
* **Sidebar search:** Ctrl+Shift+D searches Documents by titles/categories, including unopened cached notes; Ctrl+Shift+P searches Actions. Ctrl+F searches only the current note’s buffer, while Ctrl+Shift+F searches the active account. Notes search combines terms with AND and supports quoted phrases, `title:` / `category:` restrictions, snippets, and navigation to the match. Unsaved open text is included without forcing a save.
* **Work offline:** Edits and new notes are saved locally before synchronization and survive restart. **Make available offline** retains a note or category/subtree's supported managed media. Its status lists available/missing bytes and offers deliberate retry/cancellation. Offline availability, freshness, and acknowledged synchronization are separate states; external dependencies remain identified.
* **Review conflicts:** If a note changes on the server while you have local edits, BusyMark preserves the competing versions for review instead of silently overwriting one. The comparison workflow supports choosing a version, merging changes, or recovering content as a separate note. Ambiguous note-creation results also require explicit review.
* **Use attachments:** Add files to notes, view supported images and media, and include managed resources when exporting Markdown or HTML. PDF export includes supported images but does not embed arbitrary file attachments. New uploads are limited to **100 MiB per file**.
Expand All @@ -231,7 +241,7 @@ See the [Notes workspace guide](docs/development/nextcloud-notes.md#everyday-not
BusyMark supports active-document replacement and reviewed workspace-wide
replacement for local workspaces. Nextcloud Notes supports replacement within
individual notes, but not workspace-wide replacement. See [Search and replace](docs/search-and-replace.md) for search
options, scope, preview behavior, and stale-result handling.
matching, scopes, preview behavior, and stale-result handling.

## Diagrams and mathematics

Expand Down
60 changes: 60 additions & 0 deletions docs/development/application-smoke-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Compiled application checks

Use the official pinned Flutter SDK and a disposable desktop with isolated
profiles. Never install test builds over an existing application.

`tools/application_close_smoke.py` requires natural process exit, durable clean
shutdown and verified descendant retirement. A Dart checkpoint, disappearing
window or forced cleanup does not pass. It identifies only the launched
application's window and records failed prerequisites and native exits.
`--expected-engine` accepts the actual official `libflutter_linux_gtk.so` and
verifies its mapped inode, device and bytes through retained descriptors.

```sh
python3 tools/application_close_smoke.py \
--output /absolute/new/shipping-close --cycles 3 --plain \
--expected-engine /path/to/official/libflutter_linux_gtk.so -- \
build/linux/x64/release/bundle/busymark

flutter build linux --release -t tools/window_close_smoke.dart
python3 tools/application_close_smoke.py \
--output /absolute/new/document-close --cycles 1 --fresh-profile \
--scenarios plain visualization pending-initialization \
dirty-save dirty-discard dirty-cancel video \
--video test/fixtures/media/close-testsrc.mp4 -- \
build/linux/x64/release/bundle/busymark
flutter build linux --release -t lib/main.dart
```

The alternate entry point exercises the real application, editors and close
listener. It verifies Save/Discard/Cancel, saved-file reopening and rendered
content before closing. Frame callbacks confirm raster activity; video checks
require changing decoded frames. The pending-initialization case requires
`visualization-shutdown initPending=1`, using a harness that withholds readiness.

The `window-controls` scenario also tests Yaru maximize, restore, minimize, close
and titlebar dragging. It requires a window manager and the explicit
`--disposable-display` option because it injects pointer input.

`--snap-instance busymark_close` tests an owned parallel instance in a disposable
machine through its ordinary strict launcher and enforced AppArmor label. Use
the normal systemd user-session bus. Standalone checks can use a private D-Bus
session. Keep WebKit sandboxing and ordinary graphics settings enabled.

`tools/owned_processes.py` and `tools/run_owned_fixture.py` are also used by
History and browser regressions. Each supervisor owns a dedicated child tree;
PID/start-time checks and held descriptors prevent signalling reused or unrelated
processes. Incomplete observation or unresolved cleanup fails verification and
prevents another case. History fixtures compile once per test file before their
operation deadlines, preserving interprocess lock and cancellation checks.

```sh
python3 -m unittest tools/test_owned_processes.py \
tools/test_application_close_smoke.py tools/test_run_owned_fixture.py \
tools/test_official_flutter_workflow.py
```

These tooling tests require Clang and PyYAML in addition to system Python.
Automatic CI runs bounded native, X11/Wayland, dictionary/offline restart and
strict-Snap product checks. Hundred-close and debug/profile campaigns remain
explicitly selected release checks; see the existing workflows.
10 changes: 7 additions & 3 deletions docs/development/export-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,11 @@ do not certify physical-printer or assistive-technology behavior.

## CI environment

The Linux workflow in `.github/workflows/flutter-linux.yml` is authoritative for
native packages, environment flags, X11 and Wayland runs, and the
strict Snap verification. Keep ordinary build prerequisites in the main README;
The automatic `.github/workflows/flutter-linux.yml` workflow runs bounded X11
host, renderer and compiled visualization/PDF checks on pull requests to `main`
and `release/v0.6.3`, and pushes to `main`. The same job reuses its release build for dictionary and Wayland checks. It
calls `.github/workflows/flutter-linux-native.yml` for genuine strict-Snap
checks; hundred-close and debug/profile campaigns remain opt-in.
Each workflow declares its native
packages and desktop prerequisites. Keep ordinary build prerequisites in the main README;
do not copy the larger CI-only dependency set into user installation steps.
39 changes: 37 additions & 2 deletions docs/development/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ the newly written or protective record.

Local History scopes ordinary browsing to the active buffer. Scope, snapshot,
and query generations prevent a delayed read or search from publishing into a
new document scope. Store-wide document search is a temporary discovery mode
for retained identities that no longer have an open file.
new document scope. Shared Local History search discovers retained identities independently of
ordinary browsing, including identities that no longer have an open file.

`SourceComparisonInput` identifies immutable source versions. Comparison uses
patience anchors, bounded local LCS, and intraline refinement in Dart UTF-16
Expand Down Expand Up @@ -214,3 +214,38 @@ JSON reports for inspection. Its optional `--application` mode opens a supplied
file in the full application and requires the exact synthetic table fixture
checked by the driver; use an isolated application profile and disposable files
for that mode.

Session and recovery JSON publication protects each staged inode before its
atomic rename. Linux performs permission changes off the UI isolate through a
held descriptor. Configured directory links are followed and their targets
pinned; final state-file links are rejected. Retargeting a pathname cannot
redirect an admitted permission change. Unreadable inodes use the same held
identity through procfs. Permission or inspection failures fail the transaction;
private modes, lock ownership, flush and recovery ordering remain mandatory.

Startup capture and pruning wait for the saved History policy. Restoration
reads at most four files concurrently, preserves session order and drains
admitted reads after a failure. Adopted recovery remains protected until
restoration and required persistence finish, including when close is requested
before an editor is published.

Opening can display validated content while Recent persistence completes. A
failed Recent update remains a warning after a successful open or save. Settings
writes retain ordered completion and retry failed publications; shutdown drains
the queue before retiring recovery ownership.

## Shared Local History search

`SearchCategory.localHistory` owns the header query and optional retained-document
ID. `findRetained()` reads the existing store and returns bounded document or
revision metadata without changing browsing, comparison, recording or buffer
ownership. Each request checks cancellation and the retained snapshot generation;
clear and account removal invalidate affected results. Revision bodies are read
sequentially and discarded after matching, rather than kept in widget state.

Activation revalidates stable document/revision IDs and checksum before the
existing comparison workflow reads a revision. Its selection callback also checks
the originating search request after the read. Ordinary History navigation keeps
the complete timeline and has no search entry points or private query controller.
The shared coordinator retains explicit removable document scopes independently
of the browsing panel.
44 changes: 44 additions & 0 deletions docs/development/linux-engine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Linux development with official Flutter

Use the official Flutter SDK pinned in `pubspec.yaml` (3.47.5) on `PATH`.
BusyMark does not build or select a custom engine or GTK runtime.

```sh
flutter pub get --enforce-lockfile
flutter run -d linux
flutter run -d linux --profile
flutter run -d linux --release
flutter build linux --debug
flutter build linux --profile
flutter build linux --release
flutter analyze
flutter test
```

CI selects the same official version. Snapcraft downloads the checksum-pinned
SDK and packages its unmodified release engine. Follow the README's clean
Snapcraft procedure in a disposable supported environment; do not replace an
installed application for acceptance. The local scaffold repack helper is not
release or confinement validation. Strict-Snap checks use the normal systemd
user-session bus for launcher cgroup tracking, not a private D-Bus session.
Standalone smoke checks use disposable D-Bus sessions to retire desktop helpers.

`Flutter Linux` automatically runs bounded GTK/WebKit, compiled visualization/PDF,
dictionary installation/offline restart, Wayland and strict-Snap build/install
checks for PRs to `main` and `release/v0.6.3`, and pushes to `main`. It reuses
`Linux native acceptance` with `run-stress: false`. Run that workflow manually
with `run-stress` enabled for hundred-close, compiled document/media and
debug/profile campaigns. See [compiled application checks](application-smoke-tests.md).
A widget-suite pass does not validate the GTK embedder.

Yaru uses the unmodified dependencies in `pubspec.lock` through Flutter's
generated plugin build. No local plugin source preparation is required.
Compiled window, document and media checks exercise the actual application;
runtime shutdown failures still fail native release acceptance. A disappearing
window or forced cleanup is not successful natural shutdown.

Startup resolves the GTK accent or its portal fallback before building the UI,
using the existing readiness deadline. A valid GTK accent does not wait for the
portal. Saved preferences are read once before `runApp()` and handed to the
settings controller; failed reads use defaults without acknowledging them as a
persisted snapshot.
Loading
Loading