Omarchy-native file OS: a Qt Quick browser, an opt-in OS file picker, and a
handler contract that works like Omarchy / Quickshell plugins — without living
inside omarchy-shell.
Nautilus stays the packaged folder app. This repo does not edit
/usr/share/omarchy/.
Status: work in progress. The window is daily-driveable on Omarchy 4.0.x (Hyprland + Quickshell chrome + Qt 6.11). Peek, the do-layer, first-party handlers, trash, thumbs, and an opt-in FileChooser portal are in-tree. Folder MIME, Super+Shift+F, and session-default picker are not stolen yet.
Omarchy's desktop chrome is one long-running Quickshell process
(omarchy-shell). Plugins for the bar, panels, and overlays install with
omarchy plugin add <git-url> — a manifest.json, reserved omarchy.* ids,
and an unsandboxed-code warning.
Synchro copies that ritual, not the process:
- Same instinct: a git repo, a manifest, kinds + entry points,
synchro handler add. - Different address space: handlers load in Synchro (in-process QML) or as a
detached peer (
omawrite %f,omacut %f). They never joinomarchy-shell. - A file-manager crash must not take the bar, lock screen, or polkit with it.
If you already write Omarchy plugins or Qt Quick apps, you already know how to extend Synchro.
The synchro-git AUR recipe is ready, but the initial AUR listing is pending
account registration availability. Until it is listed, build the same package
directly from the public repository:
git clone https://github.com/ryrobes/synchro.git
cd synchro/packaging/aur
makepkg -si
synchro-omarchy-setup
synchroOnce the listing is live, installation will simply be yay -S synchro-git.
synchro-omarchy-setup is an idempotent per-user finish step: it reloads and
enables synchro-indexd.service, starts the singleton catalog owner, and
installs the Synchro skill for Omarchy's configured system agent. Add --menu
to also install the optional Super+Space search provider. Package hooks cannot
safely operate on the invoking user's session bus, so the AUR install message
points to this explicit command instead of attempting user-service setup as
root.
The package installs the application, built-in handlers, agent skill source, desktop entry, icon, Omarchy launchers, the opt-in Super+Space provider assets, the catalog-maintenance user service, and the opt-in FileChooser portal descriptor. It does not change your Hyprland bindings, folder MIME default, Omarchy menu, or portal preference. Those remain explicit user choices described below.
Synchro currently uses Qt's private RHI API for its accelerated spatial views. That is acceptable for the deliberately narrow Omarchy 4.0.x target, but it means the package must be rebuilt after a Qt upgrade. A future package in the Omarchy repository can move in lockstep with the distro's Qt packages.
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/synchro
./build/synchro ~/Pictures--new-window is accepted and is the default (new process).
--select /path/to/file opens the containing folder with that file selected.
Optional develop install (binary + launchers on ~/.local/bin, portal/D-Bus
files under ~/.local/share):
cmake -S . -B build -G Ninja -DCMAKE_INSTALL_PREFIX="$HOME/.local"
cmake --build build
cmake --install build
synchro-omarchy-setupRun the complete test suite with:
cmake -S . -B build-test -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
cmake --build build-test
ctest --test-dir build-test --output-on-failurepackaging/omarchy/ is a user overlay. CMake never writes /usr/share/omarchy/.
The list is focused on launch. The command field is always visible; it is not an always-focused omnibar.
| Key | What it does |
|---|---|
| Arrow keys | Move through the rendered rows or tiles |
Enter |
Open the file, or enter the folder |
| Backspace | Up a directory |
Alt+Left / Alt+Right |
Back / forward |
| Type | Filter the current folder; Up/Down select, Enter opens, Escape clears and restores position |
Space |
Toggle the lightweight Look panel. |
Shift+Space |
Open full Peek. Esc / Q / Space leave. |
Ctrl+Enter / right-click |
Do-layer (sticky actions + params) |
/ · Ctrl+K |
Filter the listing |
: |
Command palette (:trash :home :volumes :sql :flow :help) |
Ctrl+L |
Jump (current path selected) |
?name |
Name search via fd |
Ctrl+1 / Ctrl+2 |
Switch to list / grid |
Ctrl+M |
Enter/leave StrataV; M switches StrataV / MapV while there. |
| Middle-drag | Pan the StrataV / MapV camera; left-drag orbits and the wheel zooms. |
Ctrl+C / Ctrl+X / Ctrl+V |
Copy / cut / paste |
F2 / Ctrl+Shift+N |
Rename / new folder |
Delete / Ctrl+Z |
Trash / undo |
Ctrl+H |
Toggle hidden files |
Ctrl+B / Ctrl+D |
Toggle bookmark for this folder (Ctrl+D remains compatible) |
Ctrl+G |
Reveal a result in its folder |
| Ctrl+backtick | Terminal panel |
F1 / :? |
Key reference |
Look is the ambient preview. Peek is deliberate deep inspection. Enter is commit. The do-layer is do.
StrataV / MapV. :fsv, or Ctrl+M, turns the browser canvas into a spatial
3D view without giving up selection, Look, Miller browsing, or actions. An
indexed scene is assembled breadth-first on a separate cancellable worker.
StrataV omits the current-directory monument: its folders become bounded
MapV buildings in a deterministic road-connected neighborhood, and its files
occupy the open lots as smaller log-height structures. Indexed folder
buildings use exact recursive byte totals; footprint, height, and a restrained
brightness shift use a sibling-relative logarithmic p10–p90 scale, while the
exact total remains beneath the label. This makes meaningful differences
visible without allowing one backup tree to flatten the neighborhood. Each
district roof contains a MapV miniature of up to 96 immediate children plus an
explicit folded-remainder block. Rooftop geometry is explanatory rather than a
nested selection surface: every click, action, Look update, and camera flight
resolves to the owning folder. E / Right lifts the 96-cell fold for the
selected district; C / Left restores the bounded miniature. Rooftop color
uses the active Omarchy image, video, audio, code, data, and archive hues, with
a quiet HUD key; brightness still encodes recency. Every immediate root child
remains represented, and an uncataloged folder falls back to the same
structural live-filesystem renderer.
Volumes. :volumes or the volumes chip is a listing of user-facing mounts — not a sidebar, not GVFS. / is always a row (the system disk, even when home lives on it). Each row shows free / total / filesystem. / and extra disks also get a compact chip with free space (/ 180G, KINGSTON 18G); USB chips vanish when you unplug. Enter an extra disk and it is a root tree: crumbs start at volumes / KINGSTON, and Q / h at the mount root returns to the volumes listing instead of /run/media. Status line shows KINGSTON 18G free / 64G while you are inside. Do-layer Eject unmounts a removable volume (udisksctl). /proc, snaps, and portal mounts stay hidden.
Look and Peek. Space shows or hides the lightweight Look panel without
changing its saved startup preference. Shift+Space promotes the selection into
the full preview overlay. A/D hop the index ↔ the file; W/S then scroll
the preview (sqlite / duckdb / text / archives are interactive). Enter /
double-click in Peek is the same commit as the root listing.
Do-layer. Ctrl+Enter and right-click open the same sticky surface. Actions
on the left, look box on the right (file preview, or a large folder mosaic).
If the action has QML params, they mount under the look box — format picks,
rename editor, whatever the handler shipped. Compatible XDG applications are
listed directly as actions, with the configured default first; images also get
an Omarchy-native Open in Aether action. W/S change verbs. A/D hop
verbs ↔ params (or the preview). Enter runs. Esc / Q leave.
This is the product. Almost everything that interprets a file is a handler. The core owns listing, navigation, selection, search, thumbnails, trash, and the registry.
| Kind | When it runs | Typical runtime |
|---|---|---|
preview |
Peek (Space) and the do-layer look box | In-process QML |
open |
Enter / double-click | exec of a peer (omawrite %f) |
action |
Do-layer, : palette, or a key |
exec, core verb, or QML params |
folder |
Entering a matching directory | Banner QML only (v1) |
location |
Jump chip / :name |
Path, core adapter, or chrome QML |
thumbnail |
Listing / mosaic tiles | Core verb or system .thumbnailer |
panel |
Dockable sidecar surface | In-process QML |
A pack may declare more than one kind (preview + action is common).
SQL files. :sql opens a read-only DuckDB workbench over Synchro's
metadata catalog. SQLite remains the live source of truth; queries use the
latest complete native DuckDB shadow when one is available. here follows the
current folder, selection
is an execution-time snapshot of selected rows, and the recursive tree
relation indexes itself the first time a query needs it. :sql scan forces a
refresh without adding a permanent toolbar control. Tree scans are recursive,
uncapped, and incremental: committed batches are immediately queryable while
the dedicated scan worker continues, and active tree results refresh at a
controlled cadence. Completed scan roots and row counts are persisted alongside
the catalog, so relaunching reuses the existing tree instead of rebuilding it;
an indexed parent also covers queries rooted in its subfolders. Real
directories including .git and node_modules are indexed; directory
symlinks are listed but not followed to avoid loops.
Progress appears in the browser's bottom status rail. All
three expose raw byte
size, 1024-based floating-point kb,
mb, and gb rounded to two decimal places, plus boolean hidden (with
is_hidden retained for compatibility). Cheap navigation fields require no
content crawl or catalog rebuild: kind maps extensions into practical file
families, stem removes the final extension, depth counts path levels,
age_days drives age_bucket, size_bucket bands bytes, and
modified_date / modified_month expose calendar groupings. root identifies
the persisted scan root. Deterministic derived fields are materialized in each
DuckDB generation while time-relative age fields remain live. New or touched
rows also receive a stable local file_id from
device/inode identity, allowing deterministic facts to follow a rename without
turning the path into identity.
facts exposes the versioned key/value evidence table, while image_facts
pivots facts already harvested from thumbnail pixels: dimensions, orientation,
aspect ratio, average color, color family, brightness, saturation, blue share,
and a deterministic visual hash. The blue and wide lenses run against
that relation. They show analyzed/total coverage in the lens rail and request a
bounded, low-priority batch when opened; ordinary thumbnail generation fills
the same cache at essentially no extra decode cost. Facts are accepted only
when analyzer version, source size, and source mtime match the current file.
projects is another relation made entirely from catalog marker names and
returns real folders with a deterministic project_type such as python,
node, rust, or go. Existing catalogs migrate in place—none of these
features requires rebuilding the recursive tree.
Results containing path are live:
click to reveal and
double-click to enter/open. Query rows always replace the main listing while
keeping the real folder as their context. Aggregate rows
without a path become temporary, drillable relation folders; a simple
GROUP BY extension therefore browses like a folder of file types instead of
leaving you in a detached result grid. Those folders use stable representative
content for formats with useful previews (images, video, text, archives, and
Parquet); opaque formats keep a clear extension identity card instead of a
misleading generic-file mosaic. Browser Back returns from a drilled group to
its parent aggregate query. SQL and Terminal declare the same
panel.group and appear as modes of one workspace dock; third-party panels can
opt into another group without hard-coded UI changes. The disposable source
catalog lives at ~/.local/share/synchro/catalog.sqlite; browsing never depends
on it. A background user service periodically checks whether a new DuckDB
generation is eligible: indexing must be finished, catalog writes must have
been quiet for ten minutes, and the active generation must be at least an hour
old. Eligible builds use two low-priority worker threads, build
catalog.duckdb beside the active generation, validate it, and atomically
promote it. Queries already in flight retain the previous inode; new queries
receive the promoted generation. A cross-process lock keeps multiple Synchro
windows from duplicating the build, and query failures fall back to the SQLite
bridge. Inspect or force this cache with
synchro catalog shadow status and synchro catalog shadow rebuild --force.
Filename quick-open uses a separate contentless SQLite FTS5 trigram index. Catalog inserts, renames, and deletes update it in the same transaction; an existing catalog is backfilled once by a low-priority worker with a throttled duty cycle, so migration does not become another full-speed foreground scan. Queries of three or more characters support true basename substrings. Saved SQL locations and pinned folders that match are ranked ahead of ordinary files, while hidden/cache/build-tree matches are demoted. The headless JSON contract intended for launcher integrations is:
synchro launcher search --query "test pdf" --cwd "$PWD" --limit 8 --compact
synchro launcher search --content --query "import re" --cwd "$HOME" --limit 8 --compactThe launcher now consumes Synchro's versioned
org.omarchy.Synchro.Search1 user service rather than
opening the catalog itself. Apps and Quickshell plugins can use the same
asynchronous, cancellable API directly; synchro search is its stable CLI:
synchro search "test pdf" --kind file --extension pdf --limit 20 --compact
synchro search status --cwd "$HOME" --compact
synchro search describe --compactThe response includes catalog coverage and generation metadata, pagination, cached thumbnail URLs, and launch operations for open/reveal/SQL result handoffs. Content search currently preserves the app's bounded live-ripgrep behavior and identifies itself honestly as non-indexed. See the API document for the D-Bus methods and options.
On Omarchy 4.0, enable that index in the normal Super+Space menu with:
synchro-omarchy-menu-installThe installer uses Omarchy's supported local-clone mechanism rather than
editing /usr/share/omarchy. Normal applications and commands stay first;
after a 110 ms debounce, matching saved SQL locations, pinned folders, files,
and directories appear in a separate FILES & FOLDERS section. Prefix the
query with / for an explicit file-only search. Filename terms are combined
with AND semantics. Prefix with // for bounded literal in-file search using
the same ripgrep pathway as Synchro's content search. Existing warm thumbnails
appear as result icons without generating cold previews. The clone is reversible:
disable <username>.menu and re-enable omarchy.menu from Omarchy's plugin
manager to return to the stock menu.
Omaflow. :flow opens the optional Omaflow companion as another workspace
panel. Synchro discovers the installed Omarchy plugin from its manifest, then
watches Omaflow's structured index, rule files, staging state, and activity log.
The selected rule is rendered as an adaptive flow graph with branching routes
and inspectable steps. New or Revise sends a plain-language request to
Omarchy's default agent; its staged rule, warnings, and full graph remain a
draft until explicitly installed or discarded. Dry runs are one step; a real
manual run and draft installation must each be armed and confirmed. Every
operation invokes the discovered Omaflow executable directly with fixed
arguments—never through a shell. Rules with an Omaflow accepts contract are
also exposed in Synchro's do layer only while the explicit selection
matches their MIME, suffix, path, project-marker, kind, and count constraints.
The mounted flow picker defaults to a dry run; a real run must be armed, then
receives the selection through a private bounded JSON context file.
Use save in the SQL panel to name a query (for example, “big webp files”); it becomes a persistent location beside pinned folders and reopens against the folder context it was saved from. The compact lens rail supplies useful starting queries without hiding their SQL: kind, size, and age become drillable pseudo-folders; largest is a direct ranked file view; projects finds real directories from common repository/build markers; blue and wide use locally derived image evidence. Selecting a lens writes the complete query into the editor, where it can be changed or saved normally.
Location handlers may also ship listing chrome — entryPoints.row and
entryPoints.thumb — QML that paints on each visible row or grid thumb.
The core still owns the model (used / total / percent / detail roles). If no
chrome is mounted, FileList/FileGrid draw a first-party bar from percent.
synchro.location.volumes is the first client. Do not use this to restyle
every file in $HOME.
synchro handler add <git-url> # lands disabled
synchro handler add <git-url> --enable
synchro handler list
synchro handler enable <id>
synchro handler disable <id>
synchro handler update [id]
synchro handler remove <id>
synchro handler validate <dir>--yes skips the warning and the confirm prompts.
Third-party trees land in ~/.config/synchro/handlers/<id>/. First-party
handlers ship in this repo under handlers/ and load by default.
Reserved namespaces: synchro.* and omarchy.* are first-party only.
synchro handler add will reject them.
Threat model: handlers are unsandboxed user code — in-process QML or a detached exec — same as Omarchy plugins. Review the repo before you enable it.
A handler is a directory:
my.preview.notes/
manifest.json
Preview.qml # only if the kind needs QML
manifest.json is the same idea as an Omarchy plugin manifest:
{
"schemaVersion": 1,
"id": "acme.preview.notes",
"name": "Notes peek",
"version": "1.0.0",
"author": "you",
"license": "MIT",
"description": "Peek a .note file as text.",
"kinds": ["preview"],
"priority": 70,
"match": {
"suffix": [".note"],
"minItems": 1,
"maxItems": 1,
"host": "posix-local"
},
"entryPoints": {
"preview": "Preview.qml"
},
"preview": {
"runtime": "inprocess"
}
}Rules that match omarchy-plugin-validate:
- Entry points are relative, no leading
/, no.., and must exist. - No symlinks that escape the handler folder.
folder.replaceListingis rejected (the host owns the listing model).preview/ QMLactionneed an entry point.openand execactionmay be manifest-only.
Validate before you push:
synchro handler validate ./my.preview.notesPreview and action QML root on HandlerSurface:
import QtQuick
import Synchro.Handler 1.0
import Synchro.Theme 1.0
HandlerSurface {
id: root
// injected: file, selection, host, manifest
Text {
anchors.fill: parent
anchors.margins: Theme.space(8)
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontBody
wrapMode: Text.Wrap
text: {
var p = root.host.readPreview(root.file, 65536)
return p.text || p.error || ""
}
}
}The list keeps Qt focus. Esc / Space / Q stay with Synchro.
- Peek: implement
peekKey(key, modifiers)(return true if consumed) and/or setpeekFlickable.A/Dhop index ↔ file;W/Sthen callpeekKey. - Do-layer params: implement
commit()(Enter runs it) and optionalactionKey(key, modifiers). The QML mounts on the right, under the look box. Seehandlers/synchro.action.copy-as/.
Useful host calls (read-only first-party policy — do not execute the file):
| Call | Use |
|---|---|
host.readPreview(file, maxBytes) |
First N bytes as text |
host.readParquet(file, maxRows) |
Footer schema + sample |
host.readDatabase(file, "sqlite"|"duckdb", table, offset, limit) |
Table browser |
host.requestParquet(...) / parquetReady |
Non-blocking tagged Parquet read |
host.requestDatabase(...) / databaseReady |
Non-blocking tagged database read |
host.readArchive(file, maxEntries) |
Zip / tar / gzip members |
host.rasterUrl(file) |
Image URL Qt can paint (WebP rasterized) |
host.copyText(text) |
Clipboard |
host.runOpen(handlerId) |
Run a registered open handler |
host.closeAction() |
Leave the do-layer |
host.navigate(url) / host.reveal(url) / host.openExternal(url) |
Leave this file |
Theme tokens (Theme.foreground, Theme.accent, Theme.space(8), …) follow
the active Omarchy theme. Do not import qs.Commons — that module belongs to
the shell.
Preview handlers can opt into the ambient Look panel by declaring
"preview": { "runtime": "inprocess", "inline": "quick-app" }. Synchro
mounts the handler's HandlerSurface directly in Look without panel chrome.
Use host.requestParquet(...) or host.requestDatabase(...) and their tagged
ready signals for expensive reads so stale work is ignored when selection
changes. Quick apps are opt-in because they instantiate while browsing.
Omawrite does not need QML. The first-party wrapper is a manifest:
{
"kinds": ["open"],
"entryPoints": {},
"open": {
"runtime": "exec",
"exec": "omawrite %f",
"tryExec": "omawrite"
}
}tryExec hides the handler when the binary is missing. Field codes follow the
usual desktop-entry shape (%f %F %d). This is how a future Quickshell
photo surface, or any Qt Quick peer, attaches: ship a .desktop-style exec
and a Synchro manifest, not a Nautilus Python extension.
| Id | Kind | Role |
|---|---|---|
synchro.preview.image |
preview | Raster peek |
synchro.preview.text |
preview | Highlighted text; unknown text-ish files fall through here |
synchro.preview.markdown |
preview | Rendered markdown (Enter still opens Omawrite) |
synchro.preview.pdf |
preview | PDF peek |
synchro.preview.video |
preview | Video peek |
synchro.preview.parquet |
preview | Schema + sample rows |
synchro.preview.sqlite |
preview | Interactive table / schema browser |
synchro.preview.duckdb |
preview | Same browser; needs duckdb on PATH |
synchro.preview.archive |
preview | Zip / tar / gzip member list (no extract) |
synchro.preview.folder |
preview | Folder peek listing |
synchro.open.xdg |
open | xdg-open fallback |
synchro.open.omawrite |
open | Markdown → Omawrite |
synchro.open.omacut |
open | Video → Omacut |
| XDG desktop applications | action | Matching apps are enumerated directly in the Action Deck |
synchro.action.rename |
action | Inline, extension-aware rename editor |
synchro.action.copy-as |
action | Copy path / URI / name (QML params) |
synchro.action.trash |
action | Core trash verb |
synchro.action.terminal |
action | xdg-terminal-exec --dir=%d |
synchro.action.agent |
action | Prompt Omarchy's default agent with this folder/selection + catalog access |
synchro.location.home |
location | $HOME |
synchro.location.recent |
location | Recents |
synchro.location.trash |
location | XDG trash |
synchro.location.volumes |
location | Disks / USB as a listing + root trees |
synchro.action.eject |
action | Unmount / power-off a removable volume |
synchro.panel.sql |
panel | :sql read-only DuckDB over here, tree, and selection |
synchro.panel.omaflow |
panel | :flow Omaflow rules, dry runs, and recent activity |
Office docs, audio, 7z, fonts, and a few more previews are still on the preview backlog.
Synchro works with Omarchy's configured system agent without requiring MCP.
The normal browser startup installs one shared synchro skill using Omarchy's
cross-harness convention (~/.agents/skills, Codex, Claude, and Pi). Existing
user-owned skill paths are preserved and reported rather than overwritten:
synchro agent doctor
synchro agent installAn Action Deck handoff inherits SYNCHRO_SELECTION, SYNCHRO_CWD, and the exact
running executable in SYNCHRO_BIN (so uninstalled dogfood builds work too).
Its first command is "$SYNCHRO_BIN" agent context --compact, which returns one
self-describing JSON document containing the complete selection, working
folder, catalog relations and fields, examples, result-safety rules, and
available hand-back commands. The same command is useful to agent scripts
outside the UI when those environment variables are present.
Synchro's persisted catalog is available without starting the QML UI. Agents
use synchro agent query, which runs the validated read-only query surface
through a narrow transient Omarchy user service; this lets SQLite use its WAL
files without granting the agent broad access outside its sandbox. Ordinary
shell scripts can continue to use synchro query directly. Output is always
JSON and includes the query engine (duckdb-shadow or duckdb-sqlite), shadow
revision gap, query scope, complete/incomplete index coverage, scan/catalog
timestamps, elapsed time, and truncation state:
synchro query --cwd "$PWD" --sql \
"select name, kind, mb, modified_date, path from tree where kind = 'image' order by size desc" \
--limit 100
# SQL can come from stdin; selection can be repeated.
printf '%s\n' 'select * from selection' | \
synchro query --sql - --selection ./one.txt --selection ./two.txt
# Hand a result back as a navigable Synchro pseudo-folder.
synchro agent show --cwd "$PWD" --label "large images" --sql \
"select name,path,kind,mb from tree where kind = 'image' order by size desc"For extended integrations, synchro mcp --stdio exposes the same read-only
engine to MCP clients. MCP is optional: ordinary system-agent handoffs and
catalog work never depend on its registration. It
provides typed tools for name/path search, SQL, project discovery, file facts,
saved queries, and opening a result as a navigable SQL pseudo-folder in
Synchro. A local Codex registration looks like:
[mcp_servers.synchro]
command = "/usr/bin/synchro"
args = ["mcp", "--stdio"]Use the actual installed path when dogfooding an uninstalled build. Raw SQL is
restricted to SELECT/WITH; the catalog database is attached read-only and
extension autoload/install are disabled. Agents should check
catalog.coverageComplete and truncated before treating a result as
exhaustive.
Browser windows only publish the folders and facts they actually touch. One
lightweight synchro index serve process owns FTS migration, recursive
reconciliation, and DuckDB freshness for the whole session; opening a normal
Synchro window starts it if needed, and a lock makes duplicate launches exit.
The installed synchro-indexd.service can also be enabled at login:
systemctl --user enable --now synchro-indexd.service
synchro index statusRoutine shadow refreshes clone the active DuckDB generation and apply the persistent SQLite file/fact change ledger. Existing queries keep reading the previous inode until the patched generation is atomically promoted. A bounded full compaction is scheduled no more than weekly and can be requested explicitly; it runs with low CPU/I/O priority, a 1 GiB DuckDB memory limit, and the user-service resource ceiling:
synchro catalog shadow refresh
synchro catalog shadow compactThe background recursive reconciliation starts after 15 minutes and then at
six-hour intervals. Between those passes, a bounded inotify hot set follows
recently browsed folders, pins, and saved-query roots. Events are coalesced for
650 ms and reconcile only the affected directories; newly created folders join
the watched neighborhood automatically. The default ceiling is 2,048 watches,
so the durable sweep remains the correctness backstop rather than attempting an
inotify watch for every directory in a multi-million-row catalog. Useful tuning
and diagnostics are available from Indexer settings (Ctrl+, or
:settings). Saving writes ~/.config/synchro/config.json; the user daemon
watches that directory and retunes itself without sudo or a service restart.
The command-line seams remain available:
synchro index watch /path/to/a/project
synchro index unwatch /path/to/a/project
synchro index status
SYNCHRO_INDEX_MAX_WATCHES=4096 synchro index serveSYNCHRO_INDEX_WATCH_DEBOUNCE_MS and
SYNCHRO_INDEX_WATCH_NEIGHBORHOOD tune batching and the number of immediate
child directories retained around each hot root. Ordinary installs should keep
the defaults.
The same settings surface contains an opt-in semantic-image experiment. It
stores normalized 512-dimensional CLIP vectors in a separate
~/.local/share/synchro/semantic.sqlite, never in the operational catalog or
DuckDB shadow. Work is incremental, low-priority, and source-versioned by file
identity, size, and mtime. Each pass considers changed files first, then images
directly inside recently browsed, pinned, and saved-query folders before
continuing the durable full-tree sweep. When visual facts are enabled, the same
decoded pixels also feed palette, aspect, and perceptual-hash metadata instead
of reading the image a second time. The first run downloads and SHA-256 verifies
a 336 MiB MIT-licensed Qdrant/clip-ViT-B-32-vision ONNX model. :see blue images runs the matching local text encoder, compares only vectors beneath the
current folder, and opens the ranked images as a normal navigable pseudo-folder.
The same seam is scriptable:
synchro semantic search "blue images" --cwd "$HOME/Pictures" --limit 60
synchro semantic status --compactThe first semantic query downloads and verifies the companion 242 MiB text
encoder and its fixed 77-token tokenizer. Arch users need the optional
python-numpy, python-pillow, python-onnxruntime-cpu, and
python-tokenizers packages. The feature remains off when they are absent and
background image indexing is disabled by default.
Semantic search is deliberately allowed to return fewer rows than requested. Weak cosine matches are withheld instead of filling the browser with unrelated files, and near-identical resized or recompressed copies are collapsed to their best-ranked representative. Results report scoped eligible/indexed coverage, the confidence floor, and duplicate/weak-candidate counts so an incomplete or empty result is distinguishable from a failed search.
The built-in Agent action runs omarchy agent prompt, so it always follows
Omarchy's current default agent. It starts in the selected folder (or a selected
file's parent), passes the complete selection as a short-lived private JSON
manifest, and points the agent at the stable context contract above. This keeps
the handoff useful for Codex, Claude, Gemini, or whichever agent Omarchy selects,
without hard-coding an agent-specific launcher or MCP configuration.
Synchro can implement org.freedesktop.impl.portal.FileChooser as
synchro --portal. This is opt-in. gtk stays the fallback. Do not set
default=synchro (we do not implement Screenshot / ScreenCast).
- Install the
.portal+ D-Bus service (cmake --installabove), or dogfood from the build dir:
mkdir -p ~/.local/share/xdg-desktop-portal/portals ~/.local/share/dbus-1/services
cp packaging/portals/synchro.portal ~/.local/share/xdg-desktop-portal/portals/
printf '%s\n' \
'[D-BUS Service]' \
'Name=org.freedesktop.impl.portal.desktop.synchro' \
"Exec=$PWD/build/synchro --portal" \
> ~/.local/share/dbus-1/services/org.freedesktop.impl.portal.desktop.synchro.service- Write a replace-not-merge user portals.conf (must restate
default=). Copypackaging/xdg-desktop-portal/hyprland-portals.conf.exampleto~/.config/xdg-desktop-portal/hyprland-portals.conf:
[preferred]
default=hyprland;gtk
org.freedesktop.impl.portal.FileChooser=synchro;gtk-
systemctl --user restart xdg-desktop-portal.service. -
Optional: keep
--portalup as a user service. Copypackaging/systemd/synchro-portal.serviceto~/.config/systemd/user/, thensystemctl --user daemon-reload && systemctl --user enable --now synchro-portal. After a rebuild:systemctl --user restart synchro-portal. Logs:journalctl --user -u synchro-portal -f.
omarchy file select already talks to the frontend portal; it is not forked.
Chooser windows float via packaging/omarchy/synchro.lua.
Omarchy still binds Super+Shift+F to Nautilus. To try Synchro locally, append
packaging/omarchy/bindings-overlay.lua to ~/.config/hypr/bindings.lua and
reload Hyprland:
hl.unbind("SUPER + SHIFT + F")
hl.unbind("SUPER + ALT + SHIFT + F")
o.bind("SUPER + SHIFT + F", "Synchro", { launch = "synchro --new-window" })
o.bind("SUPER + ALT + SHIFT + F", "Synchro (cwd)",
"synchro --new-window \"$(omarchy-cmd-terminal-cwd)\"")synchro must be on PATH. After cmake --install, you can instead bind
{ omarchy = "synchro" } / { omarchy = "synchro-cwd" }.
Window rules: copy packaging/omarchy/synchro.lua to
~/.config/hypr/apps/synchro.lua and require("hypr.apps.synchro") from
~/.config/hypr/hyprland.lua. Browser windows stay tiled; Open/Save/Select
titles float.
- Folder MIME. Nautilus remains
xdg-mime query defaultfor folders. Super+Shift+F stays Nautilus until you overlay it. /usr/share/omarchy/. Launchers, window rules, and the keybind live underpackaging/omarchy/as a user overlay.- Session-default FileChooser. User
hyprland-portals.confonly. org.freedesktop.FileManager1. Still Nautilus.omarchy-shell. Synchro is not a Quickshell plugin and will not become one.
| In this stack | Later |
|---|---|
| Tiled browser, path bar, chips, command field | Tabs / split |
Enter → handler open (Omawrite, xdg-open) |
FileManager1 |
Peek, do-layer, trash, recents, ? name search |
?? / rg content search |
Opt-in FileChooser (synchro --portal) |
Session-default picker |
synchro handler add |
folder.replaceListing |
Optional synchro index pull |
Vendored GGUF, LLM chat, Snapper UI |
The long-form product spec is DESIGN.md. Preview gaps: PREVIEW-BACKLOG.md.
MIT. See LICENSE.