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
24 changes: 23 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ This file is updated as part of the change, not reconstructed when a version is

## [Unreleased]

## [1.1.0] - 2026-09-26

### Added

- **Tab organization**: reorder open document tabs by dragging inside the tab strip, or with `Alt+Left` / `Alt+Right` while a tab has keyboard focus. Middle-click closes a tab, and the strip scrolls the active tab back into view when tabs overflow. Tab order is session chrome - it does not move files or change the Folder.
- **Link hover source peek**: hovering a document link shows where it resolves (path, heading status, other matching documents) and a short excerpt of the target's Markdown source. The excerpt is shown as source, not as rendered HTML, and uses the document text that hover already reads. Not Preview and not a second renderer.

### Changed

- Tab navigation follows visible tab order: `Ctrl/Cmd+Tab` and `Ctrl/Cmd+Shift+Tab` activate the next or previous tab in the strip and wrap at the ends, instead of walking most-recently-used order. `Ctrl/Cmd+PageDown` / `Ctrl/Cmd+PageUp` keep doing the same and now appear as accelerators in Navigate and in Settings keyboard help.
- Language defaults to **System**: Fulvid follows the operating system language when it has that language, and English otherwise. A language chosen in Settings -> Language still wins and is kept, and regional system tags (`de-DE`, `pt_BR`) match by primary language. Guide: [docs/I18N.md](docs/I18N.md).

### Fixed

- Saving a document keeps that file's own permissions. A save used to replace them with the process default, so a document kept private (`0600`) came back readable by other accounts on the machine.
- Document text shown in editor hover tooltips (link labels, headings, paths, candidate matches) stays literal text. It could previously be read as Markdown, so a crafted heading or filename could turn a tooltip into a clickable external link or an image request.
- A Folder scan stops at a ceiling on total document text as well as on document count and depth, so a very large tree degrades to a partial Folder - still reported as partial - instead of loading document bodies without bound.
- Typing with Preview open no longer walks the whole Folder scan on every keystroke, which was noticeable in Folders with thousands of documents.
- A Graph background worker that fails to start is terminated instead of being left running. The layout is still computed in-process in that case, as before.
- `Ctrl/Cmd+Shift+Tab` and the Tab trap in dialogs register on WebKit builds that report the chord as `ISO_Left_Tab` (Linux).

## [1.0.0] - 2026-09-19

First stable release of Fulvid.
Expand Down Expand Up @@ -190,7 +211,8 @@ First release of Fulvid, a standalone desktop editor for Markdown and MDX.
- Inert Preview and Export HTML from the same renderer. Export writes a `.html` file and cannot overwrite a Markdown or MDX note.
- English and Spanish application chrome. Document text, filenames, and link targets are not translated.

[Unreleased]: https://github.com/ManuelGil/fulvid/compare/v1.0.0...HEAD
[Unreleased]: https://github.com/ManuelGil/fulvid/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/ManuelGil/fulvid/releases/tag/v1.1.0
[1.0.0]: https://github.com/ManuelGil/fulvid/releases/tag/v1.0.0
[0.12.0]: https://github.com/ManuelGil/fulvid/releases/tag/v0.12.0
[0.11.0]: https://github.com/ManuelGil/fulvid/releases/tag/v0.11.0
Expand Down
15 changes: 9 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ Keep pull requests focused.
- UI selection goes through `selectDocument`. `activateDocument` is session-internal. Surfaces open a document through `openOrActivate`. Do not wire editor buffers to Focus changes.
- Folder I/O uses `assertWithinWorkspace` (lexical) plus `assertCanonicallyContained` (symlink/realpath). Standalone Open and Save As use host dialogs and grants. No generic absolute-path read/write RPC.
- Preview and Export HTML share `renderMarkdownPreview`. Do not add a second Markdown renderer.
- Dispose canvas, workers, and observers with their owner ([docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#resources)).
- Dispose canvas, workers, and observers with their owner ([docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#resources)). Packaged Linux memory baseline and when to re-measure: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#memory-footprint-and-future-considerations).
- Presentation tokens: [`src/mainview/styles/`](src/mainview/styles/). Chrome icons: [`AppIcon.vue`](src/mainview/shell/AppIcon.vue). Quick Actions rules (groups, overflow tiers, a11y): [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#quick-actions-toolbar). Do not fork Monaco or edit `node_modules` for icons; widget Codicons are remapped in `monacoLucideIcons.ts`. Launcher icon: [`assets/README.md`](assets/README.md).
- A bound the reader can see belongs to one owner: the store that clamps a value also exports the range its control offers (`EDITOR_FONT_SIZE_LIMITS`, `layoutStore`'s `*_LIMITS`, `LAYOUT_RESIZE_STEP_PX`). Do not restate a min/max on an input.
- Name timer delays and size caps where they are enforced (`*_MS`, `MAX_*`) rather than inlining the number at the call. A number a reader cannot explain is the thing the next contributor changes by accident.
- UI wording: [docs/I18N.md](docs/I18N.md). Settings hints should say what changes, when it applies, and give a concrete example.
- Releases: [docs/DISTRIBUTION.md](docs/DISTRIBUTION.md). Actions is the main path. The Linux Makefile is a local helper.
- User-facing changes: add an entry under `Unreleased` in [CHANGELOG.md](CHANGELOG.md) in the same change. When a version is released, move those entries under that version, open a new empty `Unreleased` section, bump `package.json` / `electrobun.config.ts`, and add `docs/releases/vX.Y.Z.md`. Do not reconstruct a version from git history at the last minute, log every commit, or rewrite a published version except to fix a factual error.
Expand Down Expand Up @@ -66,18 +68,19 @@ Controlled mitigation matrix (about 55s each, same machine/WebKitGTK 2.52.6/Vite
| minimal HTTP wait (`/`, `/@vite/client`, `/main.ts`) | readiness gate only | keep small |
| `forwardConsole: false` | no change in reconnect rate | keep for Cursor-agent console hygiene (Vite auto-enables forwardConsole when an agent is detected) |
| `__electrobun` stub | not an HMR metric | keep for rare Vite-HTTP preload race |
| `WEBKIT_DISABLE_COMPOSITING_MODE=1` | separate from HMR; targets `GLXBadWindow` | Linux HMR only |
| `WEBKIT_DISABLE_COMPOSITING_MODE=1` | helps `views://` / GLXBadWindow; **breaks Vite HMR HTTP** (no `[vite] connected`, white window) | `bun run start` / `bun run dev` and Linux compatibility CI only - **not** `dev:hmr` |
| `GDK_BACKEND=x11` | avoids Wayland GDK blank paint | Linux local runners (`electrobunDev.ts`, `devHmr.ts`) unless `FULVID_KEEP_GDK_BACKEND=1` |

What Fulvid does:

- `vite.config.ts` `server.warmup.clientFiles` pre-transforms the first-paint graph (measured reduction in HMR reconnect rate; does not silence WebKit).
- `scripts/devHmr.ts` waits only for `/`, `/@vite/client`, and `/main.ts` before `electrobun dev` (do not expand this list without new measurements).
- `vite.config.ts` sets `server.forwardConsole: false` so Cursor-agent sessions do not pipe console over the HMR socket (Vite's default is agent-detected `true`, otherwise `false`). This is not a proven reconnect fix.
- `electrobunClient.ts` installs a minimal `window.__electrobun` bridge if preload has not yet, so Electroview.init does not throw under Vite HTTP.
- `scripts/devHmr.ts` defaults `WEBKIT_DISABLE_COMPOSITING_MODE=1` on Linux when unset (same profile as Linux compatibility CI) for `GLXBadWindow`, not for `WebLoaderStrategy` failures.
- Shared `scripts/linuxWebViewEnv.ts`: both runners use `electrobunDevProcessEnv`. On Linux, force `GDK_BACKEND=x11` unless `FULVID_KEEP_GDK_BACKEND=1`. Only the `views://` profile defaults `WEBKIT_DISABLE_COMPOSITING_MODE=1` when unset. The HMR profile clears an inherited compositing disable on Linux only. Windows and macOS runners do not set or clear these variables.
- Does **not** filter or hide WebKit/GLX messages.
- Does **not** switch to CEF, add a WebView watchdog, or auto-restart the renderer.
- Does **not** set compositing env in packaged production code; override locally if needed: `WEBKIT_DISABLE_COMPOSITING_MODE=1`.
- Does **not** set compositing env in packaged production code; override locally if needed: `WEBKIT_DISABLE_COMPOSITING_MODE=1` (avoid on `dev:hmr`).

Upstream direction: Electrobun native Wayland support (remove forced `GDK_BACKEND=x11`) plus WebKitGTK NetworkProcess stability under heavy ESM load. Re-test HMR after Electrobun/WebKitGTK upgrades. No exact upstream bug matching this Vite+Electrobun HMR scenario was identified; related WebKit work exists around NetworkProcess kills under load (RealtimeKit).

Expand Down Expand Up @@ -105,13 +108,13 @@ Static checks and integration carry most of the signal. A unit test is an except
| Format / lint / types | `bun run format:check`, `bun run lint`, `bun run typecheck` | Prettier, ESLint, `vue-tsc` |
| Unit | `bun run test:unit` | `*.unit.test.ts` |
| Integration | `bun run test:integration` | `*.integration.test.ts` |
| Smoke | `bun run smoke` | `*.smoke.test.ts` plus built shell; optional desktop launch |
| Smoke | `bun run smoke` | Built shell; also re-runs the lifecycle integration test; optional desktop launch |
| Compatibility smoke | `bun run smoke:compatibility` | Packaged-app CI check (`FULVID_SMOKE_LAUNCH=1` to start the binary) |
| Aggregate | `bun run test` | Unit plus integration (what CI validate runs) |

Write a unit test when the property is deterministic, lives in one module, and integration would bury it. Do not add a unit test to raise coverage, restate TypeScript, freeze private structure, wrap a trivial helper, or duplicate an integration test.

Use integration when the behavior crosses modules, the filesystem, document lifecycle, or the RPC trust boundary. `documentLifecycle.smoke.test.ts` is the reference for a real editing loop. Graph canvas behavior is smoke or manual.
Use integration when the behavior crosses modules, the filesystem, document lifecycle, or the RPC trust boundary. `documentLifecycle.integration.test.ts` is the reference for a real editing loop and runs in `bun run test` / `validate`. Graph canvas behavior is smoke or manual.

### Cross-platform contract

Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ It is open source (MIT). The source in this repository is the product.

Open a Markdown or MDX file and edit it. Untitled tabs work when you are still deciding where the file lives. Save and Save As write source, not a converted note format.

Keep several documents open in the tab strip. `Ctrl/Cmd+Tab` and `Ctrl/Cmd+PageDown` activate the next tab in tab order, `Ctrl/Cmd+Shift+Tab` and `Ctrl/Cmd+PageUp` the previous one, and the strip scrolls the active tab back into view when the tabs overflow. Change the order by dragging a tab, or with `Alt+Left` / `Alt+Right` while a tab has keyboard focus. Middle-click closes a tab.

Open a folder when you want Explorer, Quick Open, Global Search, Graph, and Document Context. Those views read the same files you are editing. They are optional. Writing still comes first.

Follow links that are actually in the source. You choose one link mode for the session: Markdown (default) or Wikilink. Broken links stay visible. Fulvid does not create a file because a link points at a missing path.
Follow links that are actually in the source. You choose one link mode for the session: Markdown (default) or Wikilink. Hovering a document link shows where it resolves: the path, the heading when the link points at one, and a short peek of its Markdown source, as source rather than a rendered page. Broken links stay visible, and the hover says the link matches no document and lists the documents that came close. Fulvid does not create a file because a link points at a missing path.

Search in two places. Local find is the editor's own find (`Ctrl/Cmd+F`). Global Search (`Ctrl/Cmd+Shift+F`) looks through document content in the open folder. Quick Open (`Ctrl/Cmd+P`) jumps to a document by title, filename, or path in that folder - it does not search content.

Expand All @@ -57,7 +59,7 @@ Look at an Outline of headings, or at Document Context for references and facts

Install **local Extensions** when you want small add-ons - a menu command, a note template, or a bounded selection transform - without giving them the filesystem, network, or the live editor. Packs live under your user data folder as ordinary files you can inspect. Details: [docs/EXTENSIONS.md](docs/EXTENSIONS.md). Catalog: sibling [`fulvid-extensions`](../fulvid-extensions/).

The chrome is English or Spanish. Document text, filenames, and link targets are never translated.
The chrome is translated for the locales listed in [docs/I18N.md](docs/I18N.md). It follows your operating system language when Fulvid has that language and English when it does not, or you can choose one in Settings. Document text, filenames, and link targets are never translated.

![MDX opened as source in Fulvid](assets/screenshots/editor-mdx.png)

Expand Down Expand Up @@ -114,7 +116,7 @@ A document on disk is a `.md`, `.markdown`, or `.mdx` file. Untitled buffers liv

Folder reads and writes stay inside the folder you opened. Opening a single file, or using Save As, goes through the operating system's file dialog. Later saves of that standalone file use the grant issued at dialog time.

A save that did not reach disk does not pretend it did. If the file's modification time changed since Fulvid last read or saved it, you get a conflict instead of a silent overwrite.
A save that did not reach disk does not pretend it did. If the file's modification time changed since Fulvid last read or saved it, you get a conflict instead of a silent overwrite. A save replaces the text in the file and leaves the file's own permissions alone, so a document you keep private stays private.

You can keep using git, another editor, or a static generator on the same tree. Fulvid is a guest on the filesystem, not the owner of it.

Expand Down Expand Up @@ -154,15 +156,15 @@ Fulvid is a good fit if you:

## Who Fulvid is not for

Skip Fulvid if you need a personal knowledge manager, a cloud workspace, live collaboration, or a place that executes MDX as an application. It is not a generic IDE, not a plugin platform, and not a publishing pipeline.
Skip Fulvid if you need a personal knowledge manager, a cloud workspace, live collaboration, or a place that executes MDX as an application. It is not a generic IDE, not a plugin marketplace, and not a publishing pipeline.

Those are other products. Fulvid stays a desktop editor for local Markdown and MDX files.

## Platforms

Fulvid is built as a desktop app for Linux, Windows, and macOS. There is no 32-bit build.

**Linux.** Packaging produces a Debian package (`fulvid_<version>_linux-x64.deb`) and a `.tar.gz` archive. Those files are meant for GitHub Releases. There is no Flathub, Snap, or AppImage package today.
**Linux.** Packaging produces a Debian package (`fulvid_<version>_linux-x64.deb`) and a `.tar.gz` archive for GitHub Releases. There is no Flathub, Snap, or AppImage package.

**Windows.** Packaging produces a zip (`fulvid_<version>_win-x64-Setup.zip`) for 64-bit Windows.

Expand All @@ -172,9 +174,9 @@ Unsigned local builds may need an OS security approval the first time they run.

## Installation

GitHub Releases is the public download channel. There is not a published release on that page yet, so the way to run Fulvid today is from source.
Download a published build from [GitHub Releases](https://github.com/ManuelGil/fulvid/releases) and pick the artifact for your platform (see [Platforms](#platforms)). That is the public install channel.

When a release is published, download it from [GitHub Releases](https://github.com/ManuelGil/fulvid/releases) and pick the file for your platform. Until then, use the steps below.
To run from source for development or contribution:

You need [Bun](https://bun.sh) **1.4.2** or newer on the host (what `bun run doctor` checks). Electrobun's and Vite's CLIs also need [Node](https://nodejs.org/) 18 or newer on `PATH` (`#!/usr/bin/env node`). The packaged app embeds Electrobun 2.0.1 with Hutch's Bun **1.4.0** runtime - that packaged Bun version is independent of the host Bun you use to develop.

Expand Down
2 changes: 1 addition & 1 deletion assets/screenshots/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Screenshots

Window captures from Fulvid (product UI). Older product shots are from 0.1.0; annotation shots match the current Add/Edit Quick Action UX.
Window captures from Fulvid (product UI). Some early product shots predate 1.0.0 chrome; annotation shots match the Add/Edit Quick Action UX. Treat this set as illustrative, not a full feature catalog.

| File | Shows |
| --- | --- |
Expand Down
Loading