This guide describes the workflows LinSync currently documents for users (release 1.1.1). The CLI ships the full subcommand surface; the Rust core covers text, folder, binary, table, image, document, webpage, merge, filter, plugin, storage, paths, trash, sandbox, and logging behavior; and the GUI exposes a nine-section QML / Kirigami shell (Compare, Image Compare, Webpage Compare, Document Compare, Sessions, Filters, Plugins, Settings, About — plus Credits and Licenses reached from the About page).
For the things LinSync intentionally does not do yet, see
docs/known-limitations-1.0.md.
Build and test the workspace:
cargo fmt --all
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warningsRun the CLI:
cargo run -p linsync-cli -- compare left.txt right.txtCLI exit codes:
0: no differences1: differences found2: command or runtime error
Launch the GUI:
cargo run -p linsync
cargo run -p linsync -- left.txt right.txtThe shell uses a StackLayout for nine sidebar entries plus the
Credits and Licenses pages reached from About. The Merge workspace is
opened from the Compare toolbar rather than the sidebar.
- Compare — text and folder pairs with browse buttons, difference
navigation, find, and read-only summary cards. The page is backed by
either the local JSON bridge or the feature-gated
cxx-qthost. A toolbar entry opens the three-way Merge workspace. - Image Compare — dedicated workspace for image diff (exact, tolerance, perceptual modes).
- Webpage Compare — dedicated workspace for source HTML, extracted text, and resource-tree modes.
- Document Compare — dedicated workspace for PDF / DOCX / ODT comparison via plugin extractors.
- Sessions — open tabs and recent paths surfaced from the active bridge. Switch and close tabs from the list; copy recent paths to the clipboard.
- Filters — include/exclude glob editors with chip removal,
quick-add presets (
.git/**,target/**,node_modules/**,*.lock,*.tmp),.gitignoretoggle, follow-symlinks toggle, maximum-depth spin box, named filter save/load/delete, and a legacy.fltmigrator. Filters persist via/filters/*bridge endpoints and apply to folder compares run from the same tab. - Plugins — populated from
discover_plugins()via/plugins/list. Per-plugin enable/disable persists via/plugins/toggle. The XDG discovery paths ($XDG_DATA_HOME/linsync/plugins/<id>/,/usr/share/linsync/plugins/<id>/,/usr/local/share/linsync/plugins/<id>/) are documented inline. A per-plugin options dialog drives/plugins/options/{get,set}. - Settings — four
Kirigami.FormLayoutcards covering appearance (color schemes, pane font, tab width, line numbers, whitespace, word wrap), comparison behavior (default mode, ignore case / whitespace / blank lines / EOL, EOL on save), session (restore last, confirm on close, persist recent paths, max recent), and storage (open config folder, reset to defaults). Settings persist to$XDG_CONFIG_HOME/linsync/settings.json; storage shape is described indocs/settings-storage-decision.md. - About — version, license, and platform pills, a 2×2 feature grid, a LinSync information card, and a "Licenses & Credits" card that deep-links into Credits and Licenses.
- Credits — searchable table of every Cargo crate from
docs/third-party-notices.mdwith license-family tinting and per-rowcrates.ionavigation, plus a runtime-component list (Qt 6, KDE Frameworks 6, FreeDesktop). Reached from About → Credits. - Licenses — tabbed full-text reader: a LinSync License tab with the GPL v3 text (and a "Dialog" button to pop it out), a Third-party tab bundling the crate manifest plus the MIT, Apache-2.0, BSD-2-Clause, Unlicense, and Unicode-3.0 license texts, and an Acknowledgements tab. Each tab supports line-number filtering, a wrap toggle, copy, and clear. Reached from About → Licenses.
Pre-existing bridge-URL parsing warnings from
qml6 -- --linsync-bridge <url> are harmless and unrelated to the
sidebar pages.
The Compare toolbar's Stop button cancels an in-flight compare from the
GUI: it flips the bridge-side cancel flag via /cancel?id=X, and the
compare reports "Compare cancelled" while preserving any partial result.
Compare two text files:
cargo run -p linsync-cli -- compare left.txt right.txtUseful output modes:
cargo run -p linsync-cli -- compare --json left.txt right.txt
cargo run -p linsync-cli -- compare --count left.txt right.txt
cargo run -p linsync-cli -- compare --quiet left.txt right.txtText ignore options:
cargo run -p linsync-cli -- compare --ignore-case --ignore-whitespace left.txt right.txt
cargo run -p linsync-cli -- compare --ignore-blank-lines --ignore-line-regex '^Generated:' left.txt right.txt
cargo run -p linsync-cli -- compare --substitute-regex 'id=\d+' 'id=<id>' left.txt right.txtExplicit compare type overrides:
cargo run -p linsync-cli -- compare --type auto left.csv right.csv
cargo run -p linsync-cli -- compare --type text left.csv right.csv
cargo run -p linsync-cli -- compare --type binary left.bin right.bin
cargo run -p linsync-cli -- compare --type folder left-dir right-dir
cargo run -p linsync-cli -- compare --type table left.csv right.csv--type auto is the default. It routes directories to folder compare,
likely-binary files to binary compare, and .csv/.tsv files to table
compare.
Compare two folders:
cargo run -p linsync-cli -- folders left-dir right-dirMachine-readable output:
cargo run -p linsync-cli -- folders --json left-dir right-dir
cargo run -p linsync-cli -- folders --csv left-dir right-dirCompare method examples:
cargo run -p linsync-cli -- folders --method binary left-dir right-dir
cargo run -p linsync-cli -- folders --method size left-dir right-dir
cargo run -p linsync-cli -- folders --method hash-blake3 left-dir right-dir
cargo run -p linsync-cli -- folders --method normalized-text left-dir right-dir
cargo run -p linsync-cli -- folders --method date-size --timestamp-tolerance-ms 2000 left-dir right-dir
cargo run -p linsync-cli -- folders --method full --large-file-threshold-bytes 10485760 --large-file-method binary left-dir right-dir
cargo run -p linsync-cli -- folders --symlinks target left-dir right-dir
cargo run -p linsync-cli -- folders --symlinks follow left-dir right-dir
cargo run -p linsync-cli -- folders --symlinks special left-dir right-dirFilter examples:
cargo run -p linsync-cli -- folders --exclude-generated left-dir right-dir
cargo run -p linsync-cli -- folders --filter 'f!:generated' --state skipped left-dir right-dir
cargo run -p linsync-cli -- folders --filter-name Generated --state skipped left-dir right-dir
cargo run -p linsync-cli -- folders --filter 'd!:target' --hide-skipped left-dir right-dirThe folder compare engine supports core entry states, skipped filter rows, error rows, content/metadata methods, symlink target/follow/ special policies, recursive symlink loop detection, special-file guards, and large-file method downgrades with per-entry notes in JSON/CSV output. Rows expose name, extension, type, per-side size, per-side modified time, compare result, and error/status metadata. The engine exposes progress events and a cancellable entry point that preserves partial results and marks unvisited rows as aborted. The folder operation planner can stage copy, delete, rename, create-missing-folder, and refresh operations with overwrite, permission, conflict, and invalid-selection warnings. Delete uses the FreeDesktop Trash when available and can produce restore guidance for trashed items while making permanent-delete results visibly non-restorable.
Note: Folder operation re-comparisons currently invoke
FolderCompareOptions::default()in the HTTP bridge, ignoring active filters / walk options. CLI runs honour all options. Seedocs/known-limitations-1.0.md.
LinSync 1.1 added three specialized compare engines.
cargo run -p linsync-cli -- compare --type image left.png right.png
cargo run -p linsync-cli -- compare --type image --image-mode tolerance --image-tolerance 0.02 left.png right.png
cargo run -p linsync-cli -- compare --type image --image-mode perceptual --image-delta-e 2.0 left.png right.png
cargo run -p linsync-cli -- compare --type image --image-frames all left.gif right.gif
cargo run -p linsync-cli -- compare --type image --json left.png right.png--type image is required to invoke image compare from the CLI;
--type auto (the default) routes image files to binary instead.
The engine offers three modes: exact, tolerance (per-channel
threshold), and perceptual (CIEDE2000). Reports pixel deltas and a
bounding box of the diff region. Dimension mismatches are padded to a
common transparent canvas and reported as unequal. The GUI Image
Compare page surfaces the same modes, loads the supported image format
list from the running build, renders a red diff overlay with region
navigation, and can save the generated overlay PNG to a user-selected
path.
Supported formats now include GIF, Radiance HDR (.hdr), and OpenEXR
(.exr) alongside PNG, JPEG, WebP, and TIFF. HDR/EXR inputs are
tone-mapped to 8-bit RGBA before comparison, so they are suitable for
release-artifact checks rather than HDR-mastering fidelity. For animated
GIF / APNG / WebP, add --image-frames all (default first) to compare
every frame pairwise; the result reports the total frame count and which
frames differ. Animation timing and disposal modes are not modelled —
frames are compared by index.
cargo run -p linsync-cli -- compare --type document left.pdf right.pdf
cargo run -p linsync-cli -- compare --type document --document-mode ocr_text --ocr-language eng left.pdf right.pdf
cargo run -p linsync-cli -- compare --type document --document-mode rendered --document-pages 2-4 left.pdf right.pdfRoutes through helper plugins (Tesseract, Poppler, LibreOffice). Text,
OCR-text, and rendered (per-page image diff, optionally narrowed with
--document-pages FIRST-LAST) modes are all functional. In OCR mode the
engine also asks the OCR engine for per-word bounding boxes (when the
plugin supports it) and surfaces them as positional data on the result
(left_word_positions / right_word_positions); these are exposed as
data and word counts, not as a visual overlay drawn on a rendered page.
cargo run -p linsync-cli -- webpage --sub-mode html --accept-network-fetch https://example.com/a https://example.com/b
cargo run -p linsync-cli -- webpage --sub-mode text --accept-network-fetch https://example.com/a https://example.com/b
cargo run -p linsync-cli -- webpage --sub-mode tree --depth 2 --accept-network-fetch https://example.com/a https://example.com/bwebpage requires the explicit --accept-network-fetch flag because
it performs outbound HTTP requests. Source HTML, extracted visible
text, and resource-tree sub-modes work end-to-end. Rendered DOM diff
and screenshot diff are produced by an out-of-process Qt WebEngine
renderer; they require a build with the web-engine feature enabled.
Compare three files against a base:
cargo run -p linsync-cli -- compare3 left.txt base.txt right.txtEmit conflict markers:
cargo run -p linsync-cli -- compare3 --markers left.txt base.txt right.txt
cargo run -p linsync-cli -- compare3 --json left.txt base.txt right.txtInspect an already-conflicted Git worktree file:
cargo run -p linsync-cli -- conflict src/file-with-conflicts.txt
cargo run -p linsync-cli -- conflict --json src/file-with-conflicts.txtThe GUI Merge page presents a base/left/right/result layout with choose-side controls and per-conflict navigation. Conflict highlight/scroll uses each side's own line ranges, so next/previous both cycles through conflicts and scrolls each pane to the matching lines.
The filter grammar supports wildcard rules, Rust regex rules
(f:, f!:, d:, d!:), file-expression rules (fe:, fe!:,
de:, de!:, e:, e!:), and diagnostics for unsupported
Windows-specific prefixes. File expressions cover type == text|binary,
byte-size comparisons (e.g. size >= 10KB), and Unix epoch millisecond
modified-time comparisons (e.g. modified_ms >= 0). The Filters page
includes a legacy .flt migrator with a preview of converted rules.
Named filters persist under $XDG_CONFIG_HOME/linsync/filters.json and
are referenced from the CLI with --filter-name <name>.
Create a diff patch:
cargo run -p linsync-cli -- patch left.txt right.txt --format unified
cargo run -p linsync-cli -- patch left.txt right.txt --format context --context 5
cargo run -p linsync-cli -- patch left.txt right.txt --format normal
cargo run -p linsync-cli -- patch left-dir right-dir --format unified
cargo run -p linsync-cli -- patch left.txt right.txt --preview
cargo run -p linsync-cli -- patch left.txt right.txt --output changes.patchWrite an HTML report:
cargo run -p linsync-cli -- report left.txt right.txt --output report.html
cargo run -p linsync-cli -- report left-dir right-dir --output folder-report.html
cargo run -p linsync-cli -- report left-dir right-dir --output folder-report.html --columns path,state --tree-state collapsed --nested-file-reportsPatch export targets text file output and folder-level patch sets where
changed members are representable UTF-8 text. Use --preview to print
the patch without writing a file. HTML reports support text and folder
comparisons, selected folder columns, expanded/collapsed tree state,
and optional nested text file reports.
You can also save a comparison result and re-render it later without
recomparing. compare … --save-result FILE writes a small JSON envelope,
and report --from-json FILE --output report.html re-renders it to HTML:
cargo run -p linsync-cli -- compare --type image --save-result result.json left.png right.png
cargo run -p linsync-cli -- report --from-json result.json --output report.htmlThe round-trip works for text, folder, table, binary, image, and document results.
Binary/hex compare:
cargo run -p linsync-cli -- hex --width 16 left.bin right.bin
cargo run -p linsync-cli -- hex --json left.bin right.bin
cargo run -p linsync-cli -- hex --metadata-only --json left.bin right.binCSV/table compare:
cargo run -p linsync-cli -- table --header left.csv right.csv
cargo run -p linsync-cli -- table --json left.csv right.csvArchive-as-folder compare:
cargo run -p linsync-cli -- archive left.zip right.zip
cargo run -p linsync-cli -- archive left.tar.zst right.tar.zstThe archive command can route each archive through a folder-virtualizer
/ unpacker plugin (--unpacker PLUGIN_ID), which handles nested-archive
recursion and member extraction into a virtual folder before the folder
compare. Without a plugin it falls back to extracting via unzip/tar
subprocesses for the built-in archive formats.
Self-compare:
cargo run -p linsync-cli -- self-compare file.txt
cargo run -p linsync-cli -- self-compare --json file.txtTemporary self-compare copies are created below
$XDG_CACHE_HOME/linsync/comparisons and cleaned up on exit.
GUI handoff from scripts:
cargo run -p linsync-cli -- launch -- left.txt right.txt
cargo run -p linsync-cli -- launch --wait -- left-dir right-dirExternal viewer fallback for unsupported files:
cargo run -p linsync-cli -- open-external unsupported.custom
cargo run -p linsync-cli -- open-external --wait unsupported.custom
cargo run -p linsync-cli -- open-external --preset kate source.rs
cargo run -p linsync-cli -- open-external --preset nvim-terminal notes.txt
cargo run -p linsync-cli -- reveal path/to/file.txt
cargo run -p linsync-cli -- reveal --wait path/to/file.txtopen-external uses xdg-open by default. Set LINSYNC_OPEN to use a
specific viewer command from scripts, or pass --preset for kate,
kwrite, vscode, vscodium, gnome-text-editor, sublime,
nvim-terminal, xdg-open, or JetBrains launcher presets such as
jetbrains-idea and jetbrains-pycharm. nvim-terminal uses
x-terminal-emulator by default; set LINSYNC_TERMINAL to choose
another terminal wrapper.
reveal first asks org.freedesktop.FileManager1.ShowItems to reveal
the selected path. If that desktop DBus API is unavailable, it opens
the containing folder with xdg-open. Set LINSYNC_REVEAL to use a
file-manager-specific reveal command from scripts.
When open-external or reveal runs the external command
synchronously (--wait), a non-zero exit code from the helper is
propagated as exit code 2 ("error"), not 1 ("differences"), so
wrapper scripts can distinguish a tool failure from a diff result.
LinSync does not support Windows-only in-process or scriptlet plugins.
Linux plugins are external helper processes using JSON over
stdin/stdout. The plugin host runs helpers under the
linsync-sandbox policy (Landlock + seccompiler with a bubblewrap
fallback).
Plugin classes today (PluginClass, serialized as snake_case):
unpackerprediffereditor_complementexternal_viewerfolder_virtualizerdocument_text_extractorocr_enginepdf_renderer
Plugins that fetch web resources (e.g. the bundled web-fetch plugin
under packaging/plugins/) are not a distinct class — they are
helpers invoked by core engines under the same linsync-sandbox
policy.
The Plugins page lists discovered plugins with class chips, license
expression, source badge, and an enable/disable toggle. Per-plugin
options are edited through a dialog wired to
/plugins/options/{get,set}.
In addition to the global enable/disable toggle, a plugin can be enabled or disabled per compare profile: select a user profile and set the per-profile override, which is stored on the profile and takes precedence over the global setting. (Built-in profiles are read-only — copy one to a user profile first.)
When more than one prediffer runs in a chain, two prediffers can
normalize the same thing. A prediffer's manifest can declare
normalization_categories; when two prediffers share a category, the
--prediffer-conflict-policy flag on compare (or the profile's
equivalent setting) decides what happens:
cargo run -p linsync-cli -- compare --prediffer-conflict-policy first-wins left.txt right.txtchain (the default) runs every prediffer; first-wins keeps the first
of two overlapping prediffers and drops the later one(s); last-wins
keeps the last. Prediffers that declare no categories never conflict.
See docs/plugin-protocol.md for the helper protocol.
Generate shell completion or a man page:
cargo run -p linsync-cli -- completions bash
cargo run -p linsync-cli -- man --output linsync-cli.1Use LinSync as a Git difftool or mergetool through the CLI commands
documented in docs/git-integration.md. The difftool path is usable
for text comparisons. The mergetool path supports --auto-resolve and
can launch the GUI Merge workspace interactively for hands-on conflict
resolution, validating the saved output before returning.
If a command exits with 1, differences were found. This is expected
and should not be treated as a runtime failure.
If a command exits with 2, check stderr for command usage, missing
files, invalid output-mode combinations, or unsupported options.
If plugin execution fails, inspect the helper stderr and verify:
- the manifest entry is relative to the plugin directory
- the helper is executable
- the helper writes one JSON response to stdout
- diagnostics go to stderr, not stdout
- output stays within configured size limits
If trashing is unavailable, LinSync's core delete planner requires explicit permanent-delete confirmation. The GUI for this workflow is not implemented yet.
If Flatpak packaging is used later, filesystem access, external editors, and helper/plugin execution may require portals or extra permissions.
For current project status, see docs/feature-matrix.md and
docs/known-limitations-1.0.md.