Skip to content

Releases: developerakshat12/FluidCorePDF

v1.1.8

Choose a tag to compare

@github-actions github-actions released this 30 Sep 10:38

FluidCore v1.1.8 — Aspect-Correct Crop Tiles & Stable Excerpt Caching

FluidCore v1.1.8 is an excerpt-card rendering correctness release. Diagram crops dropped onto the infinite synthesis workspace are no longer distorted, and cards no longer flash the "Visual Diagram Crop" placeholder while their real content is already cached. It also cancels raster work for zoom tiers the user has already zoomed past, and collapses four disagreeing copies of the card geometry constants into one definition.

Both defects had a single origin: the crop-tile pipeline derived tile pixel dimensions from card geometry and then constrained them with per-axis, independent clamps, while the production path never enforced the per-tile byte ceiling that the test path did. The aspect skew was baked into the cached raster at creation time and was therefore irreversible; the oversized tiles made those skewed surfaces big enough that the cache could only hold two, so the rest of the screen had nothing to draw.

  1. Crops Are No Longer Squashed: A single uniform scale is derived from the authoritative PDF crop box, and every ceiling is applied as a uniform reduction of that one scale. No clamp can stretch one axis independently.
  2. The 4 MiB Tile Ceiling Now Applies in Production: It was previously enforced only by renderCropSync, the synchronous path used by tests and benchmarks. A 200%-zoom card produced an 8.14 MiB tile against a 24 MiB slice.
  3. Cards Stop Flashing the Placeholder: The cache pins every crop visible in the viewport and retains one lightweight lower-resolution tier, so there is always a real image to blit while a sharper tile renders.
  4. Obsolete Zoom Tiers Are Cancelled Before Rendering: Crossing a resolution boundary queued a render per visible card at the intermediate tier. Those rasters ran under the global Poppler lock and were then immediately superseded.
  5. One Definition of Card Geometry: Layout, rendering, and tile requests now share a single body-rect definition instead of four literals that disagreed.
  6. Fixed: The Windows executable's version resource was not kept in step with the release; v1.1.7 shipped reporting 1.1.6.0 in Windows file properties.

Aspect Distortion

The reported bug

With the canvas at 200% zoom, crops of 4:3 slide pages (CS F301-L2-3(Introduction).pdf, 720×540 pt) rendered visibly narrower than the corresponding figure on the source page, with boxes inside the diagram condensed and text appearing vertically stretched. The skew reached ≈17% horizontally.

Root cause

Three independent per-axis decisions stacked:

  1. The requested box came from the card, not the crop. drawExcerptCard requested bounds().w - 20.0 by bounds().h - 36.0. Substituting the layout engine's own formula (cardW = imgW + 28, cardH = imgH + 46) gives imgW + 8 by imgH + 10 — already a skewed aspect, at every zoom including 100%. For the 4:3 card in the report session (imgW = 450, imgH = 337.5) that is a −1.15% baseline error. Small enough to go unnoticed; the same error that the next step amplified.
  2. Each axis was clamped to 1536 px independently. requestCropAsync did clamp(cardW * lodScale, 16, 1536) and clamp(cardH * lodScale, 16, 1536) separately.
  3. The raster transform was per-axis. renderBackgroundCrop called cairo_scale(cr, targetW / cropW, targetH / cropH).

For the reported card (cardW = 478, cardH = 383.5, so a base request of 458×347.5), the Retina tier asked for 1832×1390. The width saturated at 1536; the height did not. The result was a 1536×1390 tile carrying an aspect of 1.1050 instead of 1.3333 — −17.12% — with the squeeze already rasterized into the surface.

This was unrecoverable. The blit in drawExcerptCard is deliberately uniform (scale = min(bodyW/surfW, bodyH/surfH)), which is correct, and it faithfully reproduces whatever aspect the tile already had. Re-blitting at a different zoom, tier, or card size could not undo it, so the skewed surface was served for the lifetime of the cache entry.

The fix

renderBackgroundCrop is the only place that calls poppler_page_get_size, so it is the only place that can know the crop's true point dimensions. All sizing now happens there, and the caller's two numbers are treated as a bounding box constraint rather than a pixel size:

// src/app/services/TileSizing.h
double s = std::min(static_cast<double>(targetW) / cropW,
                    static_cast<double>(targetH) / cropH);
s = std::min(s, static_cast<double>(kMaxTileDimension) / cropW);
s = std::min(s, static_cast<double>(kMaxTileDimension) / cropH);

There is deliberately no min(1.0, …) on that expression. Capping the scale at 1 px/pt would pin every tile to 72 DPI and destroy Retina supersampling on high-DPI displays; the whole point of the tier ladder is to render above 1 px/pt. The byte ceiling is then a further uniform reduction:

const double maxPixelArea = static_cast<double>(kMaxTileBytes) / 4.0;
const double pixelArea = (cropW * s) * (cropH * s);
if (pixelArea > maxPixelArea) {
    s *= std::sqrt(maxPixelArea / pixelArea);
}

Two details that are easy to get wrong and were both handled:

  • The floor is 1 px, not 16 px. A per-axis minimum re-introduces exactly the defect this change removes. On a 5×700 pt sliver crop, a 16 px floor yields a 16×338 tile — a 6.6× distortion — where a 1 px floor preserves the true ratio.
  • The byte ceiling is re-checked after rounding. Rounding cropW * s and cropH * s to integers can push the product back over the limit (a 1449×724 tile is 4,196,304 B, over the 4,194,304 B ceiling). A guard decrements the longer axis until the tile fits.

For the reported card, the Retina tile is now 1155×866 = 3.81 MiB, down from 8.14 MiB, at an aspect of 1.3333.

renderCropSync was routed through the same policy, so the test and benchmark paths can no longer diverge from production.


Placeholder Flicker

The reported bug

Cards repeatedly flashed the dashed "Visual Diagram Crop" placeholder while their content was already cached, on every zoom gesture, and worst immediately after the 20-second idle trim.

Root cause

The 4 MiB per-tile ceiling lived in renderCropSync only. The asynchronous path — the one the renderer actually uses — had no byte ceiling at all, so a Retina tile was 8,540,160 B against a Slice::ExcerptTiles budget of 24 MiB working / 6 MiB idle. Three such tiles did not fit; at idle, a single one exceeded the entire budget. With three or four cards on screen, at least one was guaranteed to be missing at any instant.

That alone would make the placeholder likely. Three mechanisms made it unavoidable:

  1. evict() was blind. It popped the LRU tail with no knowledge of what was on screen, so it took visible cards first.
  2. evictSiblingTiers() removed the safety net. On inserting a higher tier it purged every other tier of the same crop. getBestAvailableSurface() falls back to another resident tier — and the eviction policy had just guaranteed there wasn't one. A card could reach zero resident surfaces, at which point the placeholder is the only correct output.
  3. The idle trim cleared everything. main.cpp calls both setMaxBytes(6 MiB) and trimToBytes(6 MiB). trimToBytes had no pinning either, so the working set was dropped wholesale and the next repaint began blank for every visible card.

The result was a closed loop: card 1's high tier completes → its fallback is purged → card 2's render completes → card 1 is evicted → card 1 shows the placeholder → card 1 redraws and re-dispatches. Repeating at the render-completion rate.

The fix

Viewport pinning. WorkspaceRenderer::drawExcerptCard records a CropIdentity — (docId, pageNo, xNorm, yNorm, wNorm, hNorm), deliberately tier-independent — for every visible crop, and commits the set at frame end. evict() and trimToBytes() search for an unpinned entry and halt if every resident tile belongs to an on-screen card.

Pinning by crop identity rather than by exact cache key is load-bearing. A CropCacheKey includes the tier, so pinning it would pin nothing at exactly the moment of a tier boundary — the instant the flicker happens — while leaving evictable the one lower tier that was preventing the placeholder.

The pin set is collected inside drawExcerptCard rather than at the top of the paint pass, because stacked excerpt cards are drawn via drawCardStack → drawExcerptCard and their children are not returned by the model's visibleIn() query. Collecting inside the function covers both paths.

Fallback retention. evictSiblingTiers() now keeps one Overview or Standard tile per crop, so getBestAvailableSurface() always resolves during a zoom transition. This is what converts the hard oscillation into a brief softening.

With the byte ceiling now enforced in production, the Retina tile dropped from 8.14 MiB to 3.81 MiB, which alone moves working-budget residency from two cards to six.


Obsolete Zoom Tiers Were Rasterized Before Being Discarded

Crossing a resolution boundary (computeLodTierFromZoom has hard edges at 0.35 / 0.85 / 1.75) invalidates every visible card's key at once. Each card then queues a render at the tier it is leaving. Those tasks sat in a two-worker GThreadPool and were picked up regardless of whether they were still wanted.

Cancellation is performed at dispatch, on the main thread, inside requestCropAsync: any in-flight request for the same crop at a different tier has its per-task cancelToken set, and its in-flight bookkeeping unmapped synchronously. The worker checks that token immediately before renderBackgroundCrop and returns without touching Poppler.

Two placement details decide whether this is correct or a memory leak:

  • The token is std::shared_ptr<std::atomic<bool>>, mirroring the existing aliveToken. `o...
Read more

v1.1.7

Choose a tag to compare

@github-actions github-actions released this 29 Sep 06:56

FluidCore v1.1.7 — Stylus Tool Honesty & Interaction State Recovery

FluidCore v1.1.7 is a stylus-input correctness release. It fixes a digitizer rewriting the user's selected tool, recovers gestures that were being stranded mid-flight, and repairs the pen barrel button's width bookkeeping. It also removes one feature that was only reachable through the behaviour being fixed.

This release reverses a documented v1.1.6 behaviour (see Breaking Change below). Upgrading users who annotate with a pen will notice the difference immediately.

  1. Stylus Selects Instead of Inking: A pen now honours the selected tool exactly as a mouse does. Select and Crop work with a digitizer instead of laying down ink.
  2. Stuck Interaction States: Crop, text-selection, and inking gestures are torn down correctly when a release event is lost, instead of silently swallowing all later input.
  3. Barrel Button Restores Pen Width: Holding a pen's barrel button no longer pins the ink width at the eraser radius for the rest of the session.
  4. Removed: Shift-for-highlighter on stylus input.

Breaking Change: Stylus No Longer Rewrites the Selected Tool

The reported bug

With the Crop tool selected, dragging in the document pane intermittently drew a pen stroke instead of selecting a region to crop. The same gesture sometimes worked perfectly. The Select tool was worse: it consistently drew ink whenever a pen was used.

Root cause

StylusButtonRouter::resolveInkTool() existed for exactly one purpose — to rewrite the selected tool to "pen" whenever a stylus was the input device. v1.1.6 shipped it covering select, text, crop, and rect_select, on the reasoning that a pen press in a selection mode starts a selection and consumes the press, so annotating would otherwise require first switching tools with a mouse.

Two independent mechanisms then combined badly:

  • The crop gate also excluded styluses. InkOverlay::onButtonPress tested if (!isStylus && (effectiveTool == "crop" ...)). Even with the tool rewrite removed, a pen could not enter the crop branch at all; it fell through to the inking path and committed a real AddStrokeCommand with stroke.tool == "pen" to the undo stack and the annotation store.
  • The outcome depended on driver-level device reporting. The rewrite was keyed on gdk_device_get_source(). A Wintab digitizer that GDK reported as GDK_SOURCE_PEN inked; the same tablet reported as GDK_SOURCE_MOUSE (mouse mode, hover-state transitions, driver quirks) cropped. Identical gesture, two behaviours — which is exactly the intermittency that was reported.

The fix

The tool-rewriting layer is deleted rather than extended with another exclusion. The rule is now stated once, in the StylusButtonRouter class comment:

A stylus is a pointing device, not an inking device: it selects text, drags crop rectangles, and moves cards, exactly as a mouse does.

  • StylusButtonRouter::resolveInkTool() and its isMouseOnlyTool() helper are removed. The class now owns only barrel buttons, device classification, and stylus attributes.
  • InkOverlay::resolveToolForDevice() and the write-only m_stylusHighlighter latch are removed. onButtonPress reads m_currentTool directly.
  • The !isStylus guard is removed from both the crop and text-selection branches in onButtonPress, so a pen genuinely enters those paths.
  • WorkspaceView's per-stroke stylus tool override and its ToolOverrideGuard unwind are removed. Leaving them would have kept the pen inking over card selection on the infinite canvas, contradicting the rule on the other pane.

The accepted trade-off

To ink with a pen you must now select the Pen, Highlighter, or Eraser tool first. v1.1.6 deliberately avoided that by making the pen always ink.

This is a considered reversal, not an oversight. A digitizer is the most precise pointing device available, which makes it the best tool for selecting a region and the worst tool for a gesture that a mouse already does adequately. The v1.1.6 trade-off optimised for "never reach for the mouse" and paid for it by making two tools unusable with the hardware they suit most.

Removed: Shift-for-highlighter

v1.1.6 supported holding Shift with a stylus press to get the highlighter nib while a selection tool was active. That path existed only inside resolveInkTool and had nowhere to live once the rewrite was deleted. There is no replacement behaviour — select the Highlighter tool instead.

StylusButtonRouterTest's testPenOverridesMouseOrientedTools was deleted rather than rewritten. It tested a function that no longer exists, and the protection is now structural: there is no code left that can rewrite a tool. A vacuous replacement asserting the new behaviour would have been worse than no test.


Stuck Interaction States

InkOverlay tracks four independent interaction flags — m_isDrawing, m_isSelectingCrop, m_isSelectingText, and m_isPotentialExcerptDrag — and both the motion and release handlers test them in a fixed priority order (crop → text → drawing). Nothing guaranteed they were mutually exclusive, and several teardown paths were missing.

How a gesture got stranded

GDK_LEAVE_NOTIFY_MASK was requested in the constructor but no leave-notify-event handler was ever connected. When a release was not delivered to the overlay, onButtonRelease never ran and the flag stayed true for the rest of the session:

  • A stranded m_isSelectingCrop swallowed every subsequent release at the crop branch, which returns early and never reaches the line clearing m_isDrawing. The half-finished pen stroke stayed live, kept rendering, and was committed against a later release.
  • InkOverlay::setTool() cancelled nothing, so switching tools mid-gesture preserved the stale flag: crop → pen mid-drag meant the pen received zero samples; pen → crop mid-stroke meant the pen kept drawing while the Crop tool was highlighted, and the release committed a stroke the user believed was a crop.
  • Esc cleared the crop selection but not the crop drag flag.
  • onButtonPress had no "interaction already in flight" guard, so a second device or a duplicate press could set one flag while another was still set.

The fix

InkOverlay::cancelCurrentInteraction() is a single teardown routine, mirroring the existing WorkspaceView::cancelCurrentInteraction. It clears all four flags, discards any in-progress stroke, and unwinds a barrel borrow. It is now called from:

  • setTool() — so switching tools always ends the previous gesture
  • onButtonPress() — a press implies the previous gesture is over
  • leave-notify-event — a genuine pointer exit
  • focus-out-event (new) — how a broken pointer grab usually surfaces
  • proximity-out-event — the pen leaving digitizer range, which sends no release and was the commoner stranding case
  • palm-rejection cancellation — cancelActiveTouches() now delegates instead of duplicating the teardown, which had already drifted and missed the barrel borrow

Grab-mode leaves are deliberately skipped. GDK holds an implicit grab for the duration of a button press, so a release outside the widget still reaches onButtonRelease; cancelling on a GDK_CROSSING_GRAB crossing would discard a live stroke every time the pointer merely drifted over the scrollbar. That assumption is now documented at the call site.

Related: crop motion could fall through into inking

onMotionNotify's crop branch tested m_dragStartPageIndex < pages.size() and returned TRUE only inside that guard. A relayout between press and motion — zoom, reload, or any of the ~20 updateLayoutDimensions() call sites — invalidated the index and let the event fall through into the text and then the ink branch, feeding a crop drag into a pen stroke. The branch now returns unconditionally.


Barrel Button Restores Pen Width

A barrel press wrote m_currentTool = "eraser" and m_currentWidth = kEraserWidth directly, bypassing setTool() and therefore never setting m_widthForcedByTool. The release path restored only the tool. After any barrel press/release the pen stayed pinned at 20.0 pt for the rest of the session.

Two teardown paths also disagreed: the new cancelCurrentInteraction() restored tool and width, while the ordinary release restored only the tool — so behaviour depended on which path happened to run.

The fix

  • InkOverlay::restoreBorrowedBarrelTool() is one implementation shared by the release path, the tool-change teardown, and the pointer-leave teardown.
  • The barrel press now sets m_widthForcedByTool = true, so it owns the width it forces.
  • The width restore is guarded by m_widthForcedByTool, preserving the existing invariant that a width the user chose is never clobbered. (An earlier draft of this fix reset the width unconditionally and would have silently discarded a slider-chosen width on any barrel press.)

Related: a latched eraser blocked all later borrows

StylusButtonRouter::m_barrelLatched was never cleared anywhere in the app. One long barrel press made endBarrel() return false permanently, so the borrow could never unwind again. This became load-bearing with the shared restore above, which would have been a permanent no-op. clearBarrelLatch() is now called when the user explicitly selects a non-eraser tool — an explicit tool choice is the user saying the eraser is no longer wanted. Covered by testClearBarrelLatchAllowsAFurtherBorrow.


Files Changed

Area Change
src/app/services/StylusButtonRouter.{h,cpp} Removed resolveInkTool() and isMouseOnlyTool(). Added clearBarrelLatch(). Class comment now records the stylus-is-a-pointing-device rule and the reversal.
src/app/document/InkOverlay.{h,cpp} Added cancelCurrentInteraction(),...
Read more

v1.1.6

Choose a tag to compare

@github-actions github-actions released this 28 Sep 20:15

FluidCore v1.1.6 — Constant-Width Ink, Unified Stroke Renderer, Memory Budget, & Stylus Barrel Buttons

FluidCore v1.1.6 is an ink-fidelity, memory-governance, and stylus-input release. It also hardens the Linux release pipeline, which had been structurally unable to produce a complete release.

  1. Constant-Width Ink: Stroke geometry no longer tapers with pressure. A stroke is drawn at exactly the width the user selected, along its entire length.
  2. Unified Stroke Renderer: All four ink renderers (document pane, infinite canvas, PDF export, excerpt crop tiles) now share a single Cairo rasterizer instead of four disagreeing copies.
  3. Memory Budget: A single authority partitions the cache budget across page tiles, excerpt tiles, and undo history, sized from physical RAM and trimmed when idle.
  4. Stylus Barrel Buttons: Barrel presses on a pen now erase instead of panning the canvas or opening a context menu.
  5. Release Pipeline Repair: The Linux AppImage step could not succeed — its required tools were never installed — and two jobs raced to publish half-releases. Both are fixed.

🌟 What's New & Fixed in v1.1.6

1. Constant-Width Ink — StrokeWidthModel

New canonical header src/libfluidcore/geometry/StrokeWidthModel.h is now the single source of truth for ink width, derived by every renderer and by the eraser hit-test.

  • Removed the pressure taper. Width was previously baseWidth * (0.25 + 0.75 * p), so the selected width meant something different at every point along the same stroke.
  • Fixes a detached blob at pen lift. On lift, a stroke's tail collapsed toward the 0.25x floor while the round end cap kept its full radius, leaving what looked like a rendering fault at the end of otherwise clean lines.
  • Fixes save/reload weight drift. Pressure is still captured and persisted with each stroke but no longer modulates geometry, so a reloaded stroke re-renders at the same apparent weight the user drew.
  • Fixes eraser hit-radius mismatch. The eraser sized its hit radius from a pressure the ink no longer used.
  • Shared highlighter opacity. kHighlighterAlpha = 0.5 is defined once, so a highlight reads identically in the pane, on the canvas, and in exported PDF.
  • NaN-safe clamping. renderedWidth() and clampPressure() reject NaN, and a floor of kMinStrokeWidthPx = 0.5 keeps antialiasing from degenerating into a dotted line.

2. Unified Stroke Renderer — StrokeRenderer

New src/app/services/StrokeRenderer.{h,cpp} is the single Cairo entry point for ink geometry. The document pane overlay, the infinite canvas, PDF export, and excerpt crop tiles all call it.

  • Fixes polygonal ink at high zoom. The old code flattened every span into a fixed 3–4 chords and stroked each chord separately. A fixed chord count is resolution dependent, so ink looked smooth at 50% zoom and visibly faceted at 200%.
  • Fixes beaded outlines. Chained round caps at stepping widths produced a dashed-looking edge. The Centripetal Catmull-Rom spline is now emitted as cairo_curve_to spans and stroked once, so a single stroke is a single composite and Cairo's stroker subdivides adaptively at any zoom.
  • Fixes export/display drift. The four renderers had independent copies of the rasterizer that disagreed on both flattening density and the pressure-to-width equation, so exported ink was narrower than what was on screen.
  • Reflection end conditions. Phantom points reflect past each end of the polyline, so the spline is defined across the whole stroke rather than only between interior samples.

3. Memory Budget — MemoryBudget

New src/app/services/MemoryBudget.{h,cpp} arbitrates every byte-budgeted consumer in the app.

  • Fixes a 2x budget overrun. Four independent caches each declared kDefaultMaxBytes = 64 MB and each enforced it only against its own surfaces, so a session could legitimately authorize 256 MB against a documented idle target of 120 MB (TRD.md §4). Nothing arbitrated between them and nothing ever called setMaxBytes().
  • RAM-derived total. recommendedTotalBytes() derives the pool from physical RAM, clamped to [16 MB, 512 MB], falling back to 64 MB when RAM cannot be determined.
  • Named slices. PageTiles, ExcerptTiles, and UndoStack each receive an explicit share. The partition is deliberately page-tile heavy, because page tiles back the reading experience the user is looking at right now, excerpt tiles are card thumbnails that re-render cheaply, and undo history is depth-bounded in practice.
  • The two UndoStacks split their slice. There is an independent stack for the document pane and the workspace canvas, so undoBytesPerStack() divides evenly. UndoStack lives in libfluidcore and must not depend on the GUI (ADR-0001), so the frontend sets each stack's budget explicitly at construction rather than the engine reading the pool.
  • Idle trim and restore. trimToIdle() lowers every slice to 25% of its configured size, releasing cache surfaces back to the allocator instead of holding them until exit; restoreFromIdle() puts them back. Both are safe to call repeatedly.

4. Stylus Barrel Buttons — StylusButtonRouter

New src/app/services/StylusButtonRouter.{h,cpp} maps GDK stylus buttons and device events onto ink intents.

  • Fixes barrel press panning the canvas. GDK's Win32 backend already surfaces Wintab hardware as ordinary GDK input — a pen arrives as GDK_SOURCE_PEN with a GDK_AXIS_PRESSURE axis, the eraser end as GDK_SOURCE_ERASER, and barrel buttons as plain GdkEventButton presses on that pen device. Left alone, a barrel press was indistinguishable from a middle click, so it panned the canvas or opened a context menu instead of erasing.
  • Stylus overrides mouse-oriented tools. select, text, crop, and rect_select are mouse modes that start a selection and consume the press. A stylus now overrides them; otherwise touching the page with a pen while the select tool was active selected text instead of drawing, forcing a mouse tool switch before every annotation.
  • Eraser is honoured as selected. An earlier revision overrode unconditionally, which meant a stylus press with the eraser selected drew a stroke at the eraser's forced width instead of erasing.
  • Barrel-button state machine. isBarrelButton() lets the eraser be held for the duration of a press.
  • No Windows Ink required. This is plain GDK input routing, the layer the rest of the app is already built on.

5. Release Pipeline Repair

The Linux release could not have succeeded, and this is the direct cause of repeated re-release attempts.

  • AppImage step was guaranteed to fail. appimagetool hard-die()s with "mksquashfs command is missing but required" and "desktop-file-validate command is missing but required" before doing any work. Neither squashfs-tools nor desktop-file-utils was in the runner's package list, so no AppImage was ever produced. Both are now installed in ci.yml and release.yml.
  • Two jobs raced to publish. release-windows and release-linux each called softprops/action-gh-release for the same tag with no ordering. The first to finish published a live release containing only 3 of 6 assets, and a simultaneous create could return 422 already_exists. Release is now a single publish job gated on needs:, with fail_on_unmatched_files: true so a missing asset fails loudly instead of shipping a partial release.
  • Node 20 actions can no longer run. GitHub removed Node 20 from hosted runners on 2026-09-23 with no opt-out, which stranded actions/checkout@v4, actions/upload-artifact@v4, and softprops/action-gh-release@v2. All are on Node 24 majors now.
  • ubuntu-22.04 is deprecated. The image entered deprecation on 2026-09-17 with hard brownouts scheduled before its 2027-04-17 retirement. Both workflows are pinned to ubuntu-24.04; ubuntu-latest is also pinned because it migrates to ubuntu-26.04 between 2026-10-19 and 2026-11-19.
  • Portable zip used Windows path separators. Compress-Archive under Windows PowerShell 5.1 writes share\glib-2.0\schemas\gschemas.compiled into the archive, which violates the ZIP specification. Non-Windows extractors materialised those as single flat files instead of a directory tree. The packager now writes entries by hand and then verifies that no entry contains a backslash and that the entry count matches.
  • Windows checksums were a truncated table. Get-FileHash | Out-File emits a formatted table whose Path column is ellipsis-truncated at console width, and it is not parseable by sha256sum -c. Both platforms now emit a standard <hash> <bare-filename> file.
  • Inno Setup could not accept a path with spaces. /DSourceDistDir= and /DOutputDir= were passed unquoted through a space-joining argument list, and fluidcore.iss used an unquoted OutputDir=. Both are now quoted.
  • Cairo pin now fails loudly. curl -sSL had no --fail, so a 404 body was written to disk with exit 0 and pacman then failed on a non-zstd file. The pin is -fsSL and short-circuits when the installed Cairo is already the pinned version.
  • Added the missing MSYS2 dependency. mingw-w64-ucrt-x86_64-gtk-update-icon-cache was required by CLAUDE.md and ops/CONTEXT.md but absent from both workflows, leaving the bundled share\icons\hicolor tree without a cache.
  • Added .gitattributes. Without it, Windows checkouts silently rewrite LF to CRLF, which breaks ops/scripts/*.sh (bad interpreter: bash^M) and makes ops/patches/*.patch unapplyable because its context lines no longer match. Line endings are now pinned per file type.
  • Added concurrency, timeout-minutes, and least-privilege permissions to both workflows, and workflow_dispatch to release.yml so a release can be re-run without deleting and re-pushing a tag.

📦 Platform Binaries & Pac...

Read more

v1.1.5

Choose a tag to compare

@github-actions github-actions released this 23 Sep 11:21

FluidCore v1.1.5 — Linux Touchpad Smooth Scrolling, Scrollbar Drag Performance, & Persistence Hardening

FluidCore v1.1.5 is a dedicated performance and bug fix release focused on reading fluidity, input responsiveness, and storage reliability across Linux and Windows:

  1. Linux Touchpad Two-Finger Scrolling: Full support for continuous GDK_SCROLL_SMOOTH touchpad gestures under X11/libinput and Wayland. Two-finger scrolling now uses native GTK kinetic acceleration ($\Delta y \cdot \text{pageSize}^{2/3}$), while discrete mouse wheel scrolling continues to operate with native physical precision.
  2. Buttery-Smooth Scrollbar Dragging: Completely eliminated scrollbar stutter and UI lag when rapidly browsing documents. Active viewport pinning preserves on-screen pages in memory without eviction thrashing, while interactive drag throttling keeps the GTK main loop executing at a silky 60 FPS.
  3. Viewport Pinning Cache Eviction Fix: Separated partial damage clip rectangles from the active reading viewport in DocumentPane::draw(). Visible pages are now protected from premature cache eviction during small scroll increments.
  4. Concurrency & Crop Tile Cache Stability: Hardened background crop rasterization in PdfDocumentService and excerpt card rendering in ExcerptTileCache.
  5. Project Store Legacy Schema Migration Hardening: Verified and hardened automated SQLite migrations for legacy projects containing external PDF paths (external_path) and last viewed pages (last_viewed_page).
  6. Air-Gapped Zero-Telemetry Verification: Verified that all 39 CTest test suites pass with zero network calls and strict memory limits ($\le 1.2\text{ GB}$).

🌟 What's New & Fixed in v1.1.5

1. Linux Touchpad Two-Finger Scrolling & Smooth Deltas

  • Smooth Scroll Event Mask Registration:
    • Registered GDK_SMOOTH_SCROLL_MASK | GDK_SCROLL_MASK on all viewport and overlay widgets: m_scroller (GtkScrolledWindow), m_overlay (GtkOverlay), m_area (GtkDrawingArea), and m_inkOverlay->widget().
    • Connected scroll-event on m_inkOverlay->widget() to ensure gestures over active inking canvases are properly intercepted and processed.
  • Continuous Kinetic Scrolling:
    • In DocumentPane::onScroll(), handled GDK_SCROLL_SMOOTH via gdk_event_get_scroll_deltas.
    • Updated vertical and horizontal adjustments (vadj / hadj) using standard GTK smooth kinetics:
      $$\Delta = \text{deltaY} \cdot \text{pageSize}^{2/3}$$
    • Preserved discrete mouse wheel fallback (return FALSE;) so GtkScrolledWindow continues handling physical scroll wheels natively.
  • Clamped Trackpad Zoom & Squeeze Gestures:
    • Clamped sub-pixel trackpad deltas during Ctrl+scroll zoom and Shift+scroll squeeze to $[-2.5, 2.5]$, preventing erratic zoom jumps when swiping on high-precision trackpads.

2. Elimination of Scrollbar Dragging Lag & Cache Thrashing

  • True Active Viewport Pinning:
    • In DocumentPane::draw(), separated the Cairo damage rectangle (clip) from the active viewport ([viewYStart, viewYEnd]).
    • All pages currently overlapping the on-screen viewport (plus prefetch margin) are pinned in PageTileCache.
    • Eliminates the severe cache thrashing loop where partial-clip redraws unpinned visible pages, forcing repeated synchronous Poppler rasterization on every scroll tick.
  • Interactive Scrollbar Drag Throttling:
    • Connected button-press-event and button-release-event to gtk_scrolled_window_get_vscrollbar(), tracking m_isScrollbarDragging.
    • Added a 100ms debounce timer on vadj's value-changed signal.
    • During rapid scrollbar dragging across uncached pages, draw() renders clean white page placeholder rectangles at 60 FPS without synchronously locking globalPopplerMutex() or blocking the UI thread.
    • As soon as dragging settles or the mouse button is released, the settled viewport page is immediately rasterized and cached.

3. Storage & Migration Hardening

  • Schema Migration Conformity in ProjectStoreTest:
    • Fixed legacy DDL migration test in ProjectStoreTest.cpp to use the canonical projects table and file_path_relative column name.
    • Ensured seamless rehydration of external PDF document paths (external_path) and last reading position across legacy .ltproj bundles.

📦 Platform Binaries & Packaging

  • Windows 11 (Native):
    • Inno Setup installer: FluidCore-Setup-x64.exe (v1.1.5)
    • Portable zip: fluidcore-windows-x64.zip (v1.1.5)
    • Resources: src/app/fluidcore.rc version set to 1.1.5.0
  • Linux (Ubuntu / Debian / Fedora / Arch):
    • AppImage: FluidCore-1.1.5-x86_64.AppImage
    • Debian package: fluidcore_1.1.5_amd64.deb
    • AppStream metainfo: resources/linux/org.fluidcore.platform.metainfo.xml updated with v1.1.5 release metadata

🧪 Verification & Test Results

  • WSL Linux Build: Clean build with make -j4 (0 warnings, 0 errors).
  • Windows Ninja Build: Clean build with ops\scripts\build-win.ps1 (0 warnings, 0 errors).
  • CTest Suite: 39/39 tests passed (100% pass rate).
  • Memory & Scalability: Verified with ScalabilityBenchmarkTest ($\le 1.2\text{ GB}$ ceiling preserved).

Full Changelog: v1.1.4...v1.1.5

v1.1.4

Choose a tag to compare

@github-actions github-actions released this 22 Sep 11:21

FluidCore v1.1.4 — Spacebar Minimap Peeking, Clipboard Image Pasting, R-Tree Robustness, & Persistence Hardening

FluidCore v1.1.4 is a major stability, usability, and bug fix release focused on canvas workflow speed, multimodal content import, spatial query correctness, and project persistence guarantees:

  1. Temporary Spacebar Minimap Peeking: Hold down Spacebar to temporarily reveal and inspect the minimap radar overlay at any time; releasing Spacebar automatically restores your canvas view without disrupting your active tool or workflow.
  2. Native Clipboard Image Pasting (Ctrl+V) & Drag-and-Drop Raster Images: Paste screenshots and images directly from your system clipboard or drag-and-drop image files (PNG, JPG, WEBP, etc.) straight onto the canvas. Images are saved into project assets, automatically scaled with aspect ratio preservation, and registered as excerpt cards with full Undo/Redo support.
  3. External PDF Path Retention & Robust Document Resolution: Projects now persist and restore absolute external paths (external_path) alongside bundle-relative paths for referenced documents. Multi-strategy document resolution ensures annotations and excerpt crops never detach even across directory renames or different mount points.
  4. R-Tree Spatial Index Geometric Correctness & Node Lifecycle Hardening*: Upgraded intersection tests to inclusive bounds ($\le$), added handling for zero-area degenerate boxes (point strokes), tightened parent bounds correctly during deep recursive splits, and implemented clean root collapsing when elements are deleted and re-inserted.
  5. Atomic SQLite Transaction Rollback Protection: Project saves now enforce strict transactional rollback (ROLLBACK;) if any statement fails during differential upserts, preventing partial or corrupted database commits.
  6. Zero-Length Click Stroke Clutter Prevention: Accidental single clicks or micro-displacement taps (< 1.0 px) on both the PDF document reader and the canvas inking surface are filtered out, preventing stray dot strokes or unintentional connector line conversions.
  7. Refined Keyboard Shortcuts & Accelerators: Unified Delete and Backspace handling for selected cards and connector lines, Ctrl+M persistent minimap toggle, Alt+1..Alt+6 and F1..F6 tool accelerators, and Esc tool reset.

🌟 What's New & Fixed in v1.1.4

1. Spacebar Minimap Peeking & Synchronized Viewport Navigation

  • Temporary Radar Peeking via Spacebar:
    • In addition to canvas panning (Space + Drag), pressing and holding Spacebar now activates a live peek of the minimap navigation widget (isSpacePeekingMinimap = true).
    • Upon releasing Spacebar, the minimap gracefully dismisses if persistent minimap mode was turned off.
  • Bi-Directional State Synchronization:
    • The top toolbar minimap button and Ctrl+M keyboard shortcut stay strictly in sync with WorkspaceView state via setOnMinimapVisibilityChanged.
    • Added re-entrancy protection (m_updatingMinimapState) preventing cascading toggle signal loops.
  • High-Performance Minimap Rendering:
    • Optimized minimap rendering loop to filter out micro canvas ink strokes (CanvasStrokeNode), drastically reducing Cairo drawing overhead when hundreds of handwriting strokes are present while maintaining full visibility for cards, decks, and structural groups.

2. Clipboard Image Pasting (Ctrl+V) & Raster Drag-and-Drop

  • Direct Clipboard Paste (Ctrl+V):
    • Paste screenshots or copied images directly from the OS clipboard into the infinite canvas.
    • Automatically discriminates between text and image clipboard payloads, preserving normal text entry if a text entry box is focused.
    • Generates unique timestamped PNG assets in assets/images/ inside the project bundle (or session assets for unsaved workspaces).
  • Drag-and-Drop Image Files:
    • Dragging PNG, JPEG, JPG, BMP, WEBP, GIF, TIFF, or ICO files into the workspace window seamlessly ingests and embeds them into the workspace.
  • Aspect-Ratio-Preserving Geometry:
    • Image cards calculate optimal viewport dimensions based on native image aspect ratio:
      $$W = 320\text{ px}, \quad H = \text{clamp}\left(\text{header} + \text{padding} + (W - 32) \cdot \frac{\text{imgH}}{\text{imgW}},, 120,, 800\right)$$
    • Placed directly at current viewport center or drop coordinates, and pushed onto the undo stack via InsertNodeCommand.
  • High-Fidelity Raster Rendering:
    • WorkspaceRenderer includes dedicated raster image caching (s_imgSurfaceCache) and header title formatting, cleanly rendering image crops alongside PDF page excerpts.

3. External Document Persistence & Resolution Fallback Engine

  • Persistent External PDF Paths:
    • Added external_path TEXT column to documents SQLite table schema in ProjectStore.
    • When referencing external documents without copying them into the bundle, FluidCore permanently remembers the source filesystem location.
  • Companion Path Tracking in DocumentPane:
    • Decoupled document viewing path from annotation storage path via m_companionPath, allowing annotations to be loaded and saved to dedicated companion files or directly to external PDFs.
  • Multi-Tiered Document Resolution:
    • Enhanced PdfDocumentService::resolveEntryLocked with multi-strategy resolution: exact docId match, normalized canonical path matching, filename-only matching, filesystem existence fallback, and single-document project fallback.
    • Robust URI encoding and file path normalization prevent malformed file URI errors on Windows drive letters and Linux paths.

4. R*-Tree Spatial Index Geometric Robustness

  • Inclusive Boundary Overlaps:
    • Changed box intersection from strict inequality (<) to inclusive inequality (<=):
      $$\text{intersects}(A, B) \iff A.x \le B.x + B.w ;\land; B.x \le A.x + A.w ;\land; A.y \le B.y + B.h ;\land; B.y \le A.y + A.h$$
    • Resolves edge-touching query misses where cards or strokes sharing boundary coordinates were erroneously excluded from viewport queries.
  • Degenerate Point Rectangles:
    • Fixed unionOf to allow zero-width and zero-height bounding boxes (a.w < 0 || a.h < 0), properly supporting point hits and zero-size ink stubs.
  • Deep Split Bounds Propagation:
    • Ensured tightenUp(nodeIdx) is systematically invoked after multi-level node splits, guaranteeing that parent bounding boxes strictly enclose all child nodes up to the root.
  • Root Collapse & Re-Insertion Safety:
    • Implemented collapseRootIfNeeded(), properly recycling empty roots and collapsing single-child interior roots when nodes are deleted or moved, preventing tree corruption when clearing a canvas and inserting new nodes.

5. Project Persistence & Transaction Integrity

  • Atomic Rollback on SQLite Statement Failure:
    • Introduced executeOrRollback in ProjectStore::saveProject.
    • Any error encountered during metadata update, document registration, node upsert, edge upsert, or deletion immediately executes ROLLBACK;, preventing partial database writes.
  • Project Dirty Tracking:
    • UndoStack change listeners automatically mark the project dirty (viewCtx->isProjectDirty = true) when uncommitted actions exist, preventing accidental lost work.

6. Micro-Tap Ink Clutter Prevention & Usability Polish

  • Accidental Click / Micro-Tap Filtering:
    • Ink strokes with $\le 1$ sample or displacement $|\Delta \vec{p}| &lt; 1.0\text{ px}$ are filtered out in both InkOverlay and WorkspaceView.
    • Eliminates accidental dot marks and false-positive connector stroke conversions when clicking to focus.
  • Unified Deletion:
    • Pressing Delete or Backspace cleanly removes whichever entity is selected (card, stack, or relational link connector) via deleteSelected().
  • Keyboard Accelerators:
    • Ctrl + M: Toggle minimap
    • Ctrl + V: Paste image from clipboard
    • Alt + 1 .. Alt + 6 & F1 .. F6: Direct tool selection (Pen, Highlighter, Eraser, Select, Crop, Connector)
    • Esc: Cancel active interaction, clear text/crop selections, and return tool to Select

📦 Downloads & Binaries

Platform Package Format File Name Description
Windows Native Installer .exe FluidCore-Setup-x64.exe Recommended for Windows. Inno Setup 64-bit installer with Start Menu integration, desktop shortcut, uninstaller, and .ltproj project associations.
Windows Portable Zip .zip fluidcore-windows-x64.zip Standalone portable archive containing all required UCRT64 runtime DLLs, GLib schemas, GDK-Pixbuf loaders, and Adwaita icons. Run anywhere without admin privileges.
Linux AppImage Bundle .AppImage FluidCore-1.1.4-x86_64.AppImage Recommended for Linux. Standalone portable executable with bundled dependencies and Wayland/X11 support. Run anywhere (chmod +x).
Linux Debian Package .deb fluidcore_1.1.4_amd64.deb Native Debian/Ubuntu package (apt install ./fluidcore_1.1.4_amd64.deb) with system dependency management and FreeDesktop menu entry.
Linux Flatpak Manifest .yml ops/flatpak/org.fluidcore.platform.yml Sandboxed Flathub distribution manifest with zero-network isolation (--unshare=network).

🧪 Verification & Test Suite Summary

  • 100% CTest Suite Passing: All 39/39 test suites passing with zero regressions:
    • RTreeIndexTest: Validates inclusive boundary intersection, degenerate point queries, deep-split recursive bounds tightening, and empty root collapse/re-insertion.
    • ProjectStoreTest: Validates external_path round-trip persistence, idempotent schema upgrades, and transactional rollback on failures.
    • WorkspaceInteractionTest: Validates `isS...
Read more

FluidCore v1.1.3 — Excerpt Card Resizing, Smooth Bézier Inking, Layer Stacking, & Memory Remediation

Choose a tag to compare

@github-actions github-actions released this 12 Sep 15:34

FluidCore v1.1.3 — Excerpt Card Resizing, Smooth Bézier Pen Inking, Layer Stacking, & Comprehensive Memory Remediation

FluidCore v1.1.3 delivers high-impact slice-of-life usability enhancements, inking rendering fidelity upgrades, and deep architectural memory leak remediations:

  1. Interactive Excerpt Card Resizing: Resize text and visual crop excerpt cards with live high-resolution rasterization, strict aspect-ratio locking for visual crops, and full Undo/Redo integration.
  2. Silky-Smooth Canvas Pen Inking: Eliminates low-pixel faceting, jagged lines, and polygonal clipping on tight circles and loops via curvature-adaptive deadzone scaling and centripetal Catmull–Rom Bézier spline interpolation.
  3. Highlighter Layer Stacking: Highlighter strokes always render cleanly beneath solid pen strokes across both the PDF Document Reader and the Infinite Spatial Workspace canvas.
  4. PDF Page Position Persistence: Projects now remember and automatically restore the active PDF reading page across saves and reloads, backed by an idempotent SQLite schema migration.
  5. Comprehensive Memory Leak Remediation: Root-cause resolution of MinGW winpthread mutex retention via native Win32 CRITICAL_SECTION patching, ephemeral background search workers, and mimalloc heap slack reclamation.
  6. Linux Distribution Packaging & ABI Portability Fix: Re-targeted Linux CI and release packagers to Ubuntu 22.04 LTS (GLIBC 2.35), statically linked C++ runtime (-static-libgcc -static-libstdc++), and refined library bundling in AppImage/.deb to eliminate GLIBC_2.38 and GLIBCXX_3.4.31 launch errors.

🌟 What's New in v1.1.3

1. Interactive Excerpt Card Resizing

  • Intuitive Visual Grab Handles:
    • Hovering over or selecting any excerpt card reveals a 14 px circular grab handle at the bottom-right corner.
    • Contextual cursor updates to se-resize (GDK_BOTTOM_RIGHT_CORNER) upon hover.
  • Strict Aspect-Ratio Locking for Visual Crop Cards:
    • Image crop cards automatically lock and preserve their original $(W:H)$ aspect ratio:
      $$\text{scale} = \max\left(\frac{W_0 + \Delta x}{W_0},, \frac{H_0 + \Delta y}{H_0}\right), \quad W = \text{clamp}(W_0 \cdot \text{scale}, 160, 1600), \quad H = \frac{W}{AR}$$
    • As visual cards enlarge or shrink, the excerpt image dynamically scales to fit the card geometry without any distortion or letterboxing.
  • Full Undo / Redo Support (Ctrl+Z / Ctrl+Y):
    • Backed by the newly implemented ResizeNodeCommand in libfluidcore.
    • Seamlessly pushes commands to the undo stack upon mouse release, with automatic R-Tree spatial index recalculation.
  • Dynamic Tile Cache Invalidation:
    • Upon completing a card resize, the excerpt tile cache automatically invalidates and re-renders the underlying PDF region at the new scale, ensuring vector-sharp clarity even at large expansions.

2. Silky-Smooth Canvas Pen Inking & Anti-Faceting Engine

  • Curvature-Adaptive Deadzone Filtering:
    • Previous fixed-distance stabilization deadzones ($1.5$–$2.0$ px) truncated sharp directional changes, turning small loops and circles into faceted polygons at low pixel widths (1–2 px).
    • The new StrokeStabilizer measures the turning angle between successive stroke vectors:
      $$\cos \theta = \frac{\Delta \vec{v}{i} \cdot \Delta \vec{v}{i-1}}{|\Delta \vec{v}{i}| , |\Delta \vec{v}{i-1}|}$$
    • When a sharp turn or circle is detected ($\cos \theta &lt; 0.95$), the deadzone automatically scales down to $0.15$ px, preserving tight curves and circular handwriting with zero polygonal flattening.
  • Centripetal Catmull–Rom Bézier Splines:
    • Replaced linear segment chaining with centripetal Catmull–Rom cubic Bézier splining across both committed canvas strokes and live in-flight wet ink.
    • Live wet ink and committed nodes evaluate 4-step sub-segment Bézier curves with smooth pressure interpolation via evalCubicBezier, eliminating visual "popping" or snapping upon pen-up.

3. True Layer Stacking: Highlighter Sits Beneath Pen

  • PDF Document Overlay (InkOverlay):
    • Split rendering into a two-pass compositor: Pass 1 renders all highlighter strokes in semi-transparent mode (CAIRO_OPERATOR_OVER); Pass 2 renders solid ink pens directly on top.
  • Infinite Spatial Workspace Canvas (WorkspaceRenderer):
    • Re-architected canvas layer stacking order:
      1. Excerpt Cards, Stacks, and Structural Elements
      2. Committed Highlighter Strokes
      3. Active Wet Inking Highlighter Stroke
      4. Committed Pen Strokes
      5. Active Wet Inking Pen Stroke
      6. Selection Rings, Grab Handles, and Connectors
    • Highlighting over existing writing or sketching never dims, occludes, or muddies pen strokes.

4. PDF Reading Position Persistence Across Project Reloads

  • Zero-Friction Context Restoration:
    • Saving a project (Ctrl+S) records the exact PDF page currently visible in the document reader viewport.
    • Reopening the .ltproj project bundle automatically scrolls the document reader directly to that page once dimensions and layout are computed.
  • Idempotent SQLite Schema Migration:
    • Added last_viewed_page INTEGER NOT NULL DEFAULT 0 to the documents table in ProjectStore.
    • Uses PRAGMA table_info(documents) inspection prior to running ALTER TABLE, guaranteeing seamless, idempotent upgrades of existing project databases without throwing duplicate column errors.

5. Deep Memory Leak Remediation & Architecture Hardening

  • Native Win32 CriticalSection Remediations:
    • Diagnosed and resolved a subtle memory retention bug in MinGW-w64's winpthread library, where std::mutex allocations bypassed standard CRT cleanup upon thread termination.
    • Applied native Win32 CRITICAL_SECTION fallbacks in ops/patches/poppler_win32_critical_section.patch for rock-solid concurrency stability.
  • Ephemeral Background Search Document Lifecycle:
    • Document search workers now utilize an ephemeral PopplerDocument lifecycle and scheduled worker thread recycling (every 8 searches), releasing page descriptor caches immediately upon search completion.
  • mimalloc Hardened Heap Management:
    • Linked mimalloc across fluidcore_app with PurgeDelay=0 and active decommit flags to aggressively return unmapped virtual memory slack directly to Windows.
    • Added internal live memory telemetry (MemoryTelemetry.h) and telemetry monitoring scripts in debug/scripts/.

6. Linux Distribution Packaging & ABI Portability Hardening

  • Universal GLIBC 2.35+ Compatibility:
    • Re-targeted GitHub Actions release and packaging runners from ubuntu-24.04 to ubuntu-22.04.
    • Releases are now built against GLIBC 2.35, resolving runtime failures (version GLIBC_2.38 not found) on Ubuntu 22.04 LTS, Debian 12, Linux Mint 21, and other enterprise distributions, while maintaining forward compatibility with Ubuntu 24.04+.
  • Static C++ Runtime Linking (-static-libgcc -static-libstdc++):
    • Statically linked libstdc++ and libgcc into fluidcore_app on Linux, eliminating GLIBCXX_3.4.xx runtime version mismatches.
  • AppImage & Debian Package Refinements:
    • Updated package-appimage.sh excludelist to prevent bundling libstdc++/libgcc_s which could shadow newer host GPU display drivers.
    • Enhanced package-deb.sh dependencies with libpoppler-glib8 (>= 20.0) | libpoppler-glib8t64 for seamless installation across both Ubuntu 22.04 and Ubuntu 24.04 (t64 transition).

📦 Downloads & Binaries

Platform Package Format File Name Description
Windows Native Installer .exe FluidCore-Setup-x64.exe Recommended for Windows. Inno Setup 64-bit installer with Start Menu integration, desktop shortcut, uninstaller, and .ltproj project associations.
Windows Portable Zip .zip fluidcore-windows-x64.zip Standalone portable archive containing all required UCRT64 runtime DLLs, GLib schemas, GDK-Pixbuf loaders, and Adwaita icons. Run anywhere without admin privileges.
Linux Debian Package .deb fluidcore_1.1.3_amd64.deb Native Debian/Ubuntu package (apt install ./fluidcore_1.1.3_amd64.deb) with system dependency management and FreeDesktop menu entry.
Linux AppImage Bundle .AppImage FluidCore-1.1.3-x86_64.AppImage Recommended for Linux. Standalone portable executable with bundled dependencies and Wayland/X11 support. Run anywhere (chmod +x).

🧪 Verification & Test Suite Summary

  • 100% CTest Suite Passing: All 39/39 unit and integration test suites pass with zero failures:
    • ProjectStoreTest: Validates lastViewedPage persistence round-trip and idempotent schema migration.
    • UndoStackTest: Validates ResizeNodeCommand execution, undo, redo, and spatial R-Tree index bounding box updates.
    • StrokeStabilizerTest: Validates adaptive curvature deadzone scaling on high-frequency directional turns.
    • PopplerMalformedRegressionTest & PopplerConcurrencyTest: Validates thread safety and memory bounds.
  • Invariants Gate: python ops/scripts/check_invariants.py passed (zero runtime network calls, strict libfluidcore engine boundary).
  • Code Style Compliance: 100% compliant with clang-format --dry-run --Werror.

v1.0.2 — Custom Pen & Highlighter Controls, Smooth Inking Ribbon, & CI Hardening

Choose a tag to compare

@developerakshat12 developerakshat12 released this 07 Sep 08:37

FluidCore v1.0.2 introduces user-configurable stroke weights and colors for both the Pen and Highlighter tools, an interactive contextual toolbar UI with per-tool memory, and a complete rendering overhaul of the infinite canvas highlighter engine for silky-smooth, bead-free translucent highlighting.


🌟 What's New in v1.0.2

1. Custom Inking Properties with Independent Tool Memory

  • Per-Tool Memory: Pen and Highlighter now independently remember their custom color and thickness. Switching tools or toggling the Eraser (E) preserves your exact settings.
  • Architectural Grounding: ToolManager serves as the single source of truth, synchronizing properties in real time across the PDF Reader (DocumentPane / InkOverlay) and Infinite Spatial Workspace (WorkspaceView / WorkspaceRenderer).

2. Modern Contextual Toolbar UI (TopToolbarWidget)

  • Interactive Color Pill (fc-color-pill):
    • Displays the active tool's color swatch.
    • Clicking opens the Color Popover with curated high-contrast palettes tailored for Pen (8 rich tones) and Highlighter (6 vibrant translucent tones).
    • Includes a direct Hex code input (#RRGGBB) with real-time validation and a native system color dialog launcher.
  • Interactive Weight Pill (fc-weight-pill):
    • Live thickness dot and numeric label (e.g., 2.0 pt / 14.0 pt).
    • Clicking opens the Weight Popover featuring:
      • Live Anti-Aliased Preview Canvas: Visualizes stroke thickness, round caps, and exact alpha compositing before you draw.
      • Quick Preset Pills: One-click thickness options (1px, 2px, 4px, 8px for Pen; 8px, 14px, 22px, 32px for Highlighter).
      • Continuous Slider & Stepper Buttons: Smooth GtkScale adjustments with [-] and [+] nudge controls.
  • Context-Aware Sensitivity: Inking pills automatically enter a subtle dimmed state when non-drawing tools (Select, Eraser, Crop, Connector) are active.

3. Keyboard Shortcuts for Inking

  • Press [ to decrement active tool stroke thickness (by 0.5 pt for Pen, 2.0 pt for Highlighter).
  • Press ] to increment active tool stroke thickness (by 0.5 pt for Pen, 2.0 pt for Highlighter).

4. Silky-Smooth Translucent Highlighter Rendering

  • Eliminated Overlapping "Beads" / "Caterpillar" Artifact: Replaced per-segment direct canvas drawing with isolated offscreen group compositing (cairo_push_group / cairo_pop_group_to_source). Overlapping segment round caps now fuse at 100% opacity into a unified mask before being stamped onto the canvas in a single pass at uniform 45% alpha.
  • Centripetal Catmull–Rom Bézier Splines: Raw, faceted polygonal line segments are now interpolated with smooth cubic Bézier curves (cairo_curve_to), matching the PDF document overlay standard.
  • Consistent In-Flight vs. Committed Rendering: Live in-flight strokes and committed workspace nodes use identical Bézier mathematics, eliminating visual popping or shape warping upon stylus/mouse release.
  • Preserved Pen Dynamics: Fountain pen pressure variation and calligraphic stroke dynamics remain 100% untouched.

5. Build System & CI Hardening

  • Added libsqlite3-dev, zlib1g-dev, and dpkg-dev packages to Ubuntu CI jobs to ensure zero packaging failures on GitHub Actions runners.
  • Hardened CMake SQLite target resolution in src/libfluidcore with seamless fallback between SQLite::SQLite3 and SQLite3::SQLite3.
  • Expanded unit test coverage with ToolManagerTest (38/38 CTest suites passing with 100% success on both Windows and Linux).

📦 Downloads & Binaries

Platform Package Format File Name Size SHA-256 Checksum
Windows Native Installer .exe FluidCore-Setup-x64.exe 30.0 MB 8BA8767A3BC66AC4F9A9E115DF1EB01F227A00FB93D59F5B105FCD66FBA60BA5
Windows Portable Zip .zip fluidcore-windows-x64.zip 41.8 MB DDD1B73015E5A36677BE99D92B354FFDCF17A852AAACE6246EE615C7257F17A5
Linux Debian Package .deb fluidcore_1.0.2_amd64.deb 6.9 MB D94F26F06F3B389259BCA876B275A05131B69279809E210B6F5C65D334D15AB8
Linux AppImage Bundle .AppImage FluidCore-1.0.2-x86_64.AppImage 34.0 MB 9D087337161848A5B34AF5D386F1B6342C781F4DC4F7C7CC65FC43AB7A694938

🚀 Verification & Integrity

To verify package integrity after download:

Windows (PowerShell):

Get-FileHash FluidCore-Setup-x64.exe -Algorithm SHA256
Get-FileHash fluidcore-windows-x64.zip -Algorithm SHA256


**Full Changelog**: https://github.com/developerakshat12/FluidCorePDF/compare/v1.0.1...v1.0.2

**Full Changelog**: https://github.com/developerakshat12/FluidCorePDF/compare/v1.0.1...v1.0.2

# FluidCore v1.0.1 — Bug Fixes & Stability Release

Choose a tag to compare

@developerakshat12 developerakshat12 released this 06 Sep 11:28

FluidCore v1.0.1 — Bug Fixes & Stability Release

FluidCore is an offline-first, high-performance fluid document synthesis platform built with C++20, Cairo, and GTK3. This release resolves coordinate-space mapping issues during excerpt cropping, stabilizes cursor-anchored focal zoom, adds bi-directional card navigation, and updates official distribution packages for Windows and Linux.


🚀 Key Highlights & Fixes

1. Authoritative Annotation Capture on Excerpt Crops

  • Fixed Missing Annotations on Arbitrary Page Sizes: Fixed an issue where pen and highlighter annotations rendered on non-standard PDF page geometries (e.g. 459×666 vs. standard 612×792) failed bounding-box intersection tests during cropping.
  • Normalized Spatial Contract ($[0.0 \dots 1.0]$): ExcerptTileCache now strictly operates on normalized crop coordinates. Guessing or falling back to default page sizes has been eliminated.
  • Authoritative Geometry Querying: DocumentPane queries the actual vector page dimensions (getPageDimensions()) and uses a strict 4-tier document ID match to eliminate cross-document annotation leakage.

2. Focal Zoom Invariance (ViewportZoomAnchor)

  • Cursor-Anchored Zooming: Introduced ViewportZoomAnchor, a dedicated mathematical solver that preserves the exact document point under the mouse cursor or viewport center during zoom transitions.
  • Deep Document Navigation: Eliminated visual jumps and drift when zooming in deep documents (e.g., page 87+).
  • Invariance Test Suite: Verified via ViewportZoomAnchorTest, covering zoom-in, zoom-out, centered toolbar zoom, and scroll boundary clamping.

3. Enhanced Card Navigation & Anchor Return

  • Interactive Return Anchor: Added a clickable left-edge anchor pill (◀) on excerpt cards. Clicking the pill jumps the document reader back to the exact source page and excerpt location.
  • Refined Hit-Testing: Updated card selection and bounding-box hit testing in CardLayoutEngine for smooth dragging and resizing.

📦 Downloads & Official Distribution Assets

Platform Format Asset Name Description
Windows Native Installer FluidCore-Setup-x64.exe Recommended for Windows. Inno Setup 64-bit installer with Start Menu integration, desktop shortcut, uninstaller, and .ltproj file associations.
Windows Portable Zip fluidcore-windows-x64.zip Standalone portable archive with bundled UCRT64 runtime DLLs, GLib schemas, GDK-Pixbuf loaders, and Adwaita icons. Run without admin privileges.
Linux AppImage Bundle FluidCore-1.0.1-x86_64.AppImage Recommended for Linux. Standalone portable executable with bundled dependencies, desktop integration, and Wayland/X11 support. (chmod +x && ./FluidCore-1.0.1-x86_64.AppImage)
Linux Debian Package fluidcore_1.0.1_amd64.deb Native Debian/Ubuntu package with system dependency management and FreeDesktop menu integration. (sudo apt install ./fluidcore_1.0.1_amd64.deb)
Linux Flatpak Manifest ops/flatpak/org.fluidcore.platform.yml Sandboxed distribution manifest for Flathub with zero-network isolation (--unshare=network).

⚡ Verification & Invariants

  • Unit & Integration Tests: 37/37 test suites passing (100%) on Windows (MSYS2 UCRT64) and Linux (Ubuntu 24.04).
  • Scalability: Cold load duration 0.02s across 50 PDFs (5,000 vector pages) with bounded peak working set (62.6 MB vs. 1.2 GB budget).
  • Code Quality: Formatted according to repository .clang-format rules.

Full Changelog: v1.0.0...v1.0.1

Full Changelog: v1.0.0...v1.0.1

FluidCore v1.0.0 — Official Full Release

Choose a tag to compare

@developerakshat12 developerakshat12 released this 05 Sep 13:46

FluidCore v1.0.0 — Official Full Release 🚀

FluidCore v1.0.0 is officially released! FluidCore is an open-source, offline-first alternative to LiquidText designed for active reading, deep comprehension, and complex knowledge synthesis. It fuses a malleable PDF reader with an infinite 2D thinking canvas.

Available natively for Windows 11 and Linux (Ubuntu, Debian, Fedora, Arch).

What's New in v1.0.0

  • Accordion Squeeze: Dynamically collapse non-adjacent pages with pinch or Ctrl+Shift+Scroll to view distant paragraphs, formulas, or figures side-by-side.
  • Search-Driven Squeeze: Press Ctrl+F or Ctrl+Shift+S to automatically fold intermediate material and bring all search hits into view with fold crease markers.
  • Direct PDF Vector Inking: Annotate PDFs with pressure-sensitive stylus strokes, translucent highlighter overlays, Catmull-Rom curve stabilization ($\le 8.05\text{ ms}$ latency), and two-phase whole-stroke eraser.
  • Infinite 2D Thinking Canvas: Drag excerpts and image crops onto an unbounded canvas indexed with an $O(\log N)$ R*-tree spatial index.
  • Freehand Stylus Notes & Palm Rejection: Sketch freehand diagrams and handwriting notes directly on the canvas with dynamic hardware profiles (Wacom EMR/AES, Microsoft Surface, HP MPP).
  • Magnetic Card Stacking: Snapping cards together (16pt threshold) merges them into reorderable decks with accordion cascade previews and inline rename popovers.
  • Relational Bezier Ink Connectors: Draw lines between thoughts with the connector tool (Alt+6 / F6 / A / L) to create persistent Bezier edges (GraphEdge) that route around obstacles.
  • Bi-Directional Anchors: Click ↗ Anchor on any excerpt to glide smoothly to the source passage in the PDF; click ↶ Return to glide back to your canvas thought.
  • Synthesis Export: Export flattened PDFs with burnt annotations or clean Markdown outlines complete with Mermaid flowcharts.
  • 100% Offline & Private: Self-contained .ltproj directory bundles backed by SQLite WAL with atomic temp-swap commits. Zero telemetry, zero cloud calls, completely air-gapped.

Integrity & Checksums

SHA256 checksums are attached in checksums-windows-sha256.txt and checksums-linux-sha256.txt. Verify via:

# Windows
Get-FileHash <filename> -Algorithm SHA256

# Linux
sha256sum <filename>


**Full Changelog**: https://github.com/developerakshat12/FluidCorePDF/compare/v0.9.0...v1.0.0

**Full Changelog**: https://github.com/developerakshat12/FluidCorePDF/compare/v0.9.0...v1.0.0

FluidCore v0.9.0 — Windows Native Pre-Release

Choose a tag to compare

@developerakshat12 developerakshat12 released this 05 Sep 11:18

FluidCore Platform v0.9.0 (Windows Native Pre-Release)

An open-source, offline-first alternative to LiquidText designed for active reading and synthesis: a fluid, malleable PDF reader fused with an infinite 2D thinking canvas.

This pre-release introduces native Windows 11 support, standalone installation packages, and the core active-reading toolset.


✨ What's in this Pre-Release

  • Accordion Squeeze: Pinch or Ctrl+Shift+Scroll to collapse non-adjacent pages and compare distant sections side-by-side.
  • Infinite 2D Canvas & Excerpts: Drag text or visual image crops from your documents onto an unbounded workspace with R*-tree spatial indexing.
  • Bi-Directional Anchors: Click ↗ Anchor on any card to glide to the original source location in the document; click ↶ Return to jump back to your canvas thought.
  • Relational Ink Connectors: Draw lines between cards with the connector tool (Alt+6 / F6) to create persistent Bezier relational links (GraphEdge).
  • Magnetic Card Stacking: Drop cards within 16pt proximity to snap them into collapsible, reorderable topic decks.
  • Full-Text & Tag Search: Fast in-memory search across document text, canvas snippets, and tags (#tag, tag:xyz) with camera glide navigation.
  • Synthesis Export: Flatten annotated vector PDFs or export Markdown outlines with embedded Mermaid flowcharts.
  • 100% Offline & Private: Stored in .ltproj bundles (SQLite WAL format) with atomic temp-swap commits. Zero telemetry, zero cloud calls, air-gapped security.

📦 Downloads for Windows (x64)

Asset Description
FluidCore-Setup-x64.exe Recommended. Native 64-bit installer with Start Menu integration, desktop shortcut, uninstaller, and .ltproj file associations.
fluidcore-windows-x64.zip Portable standalone zip containing all bundled UCRT64 runtime DLLs, GLib schemas, and icons. Run anywhere without administrator rights.

Checksums (SHA-256)

  • FluidCore-Setup-x64.exe: 3470501C5159393E3AA572335E44815C95017909A880B043B2F3C4F5716582DD
  • fluidcore-windows-x64.zip: EC9731B54B25F4DB60DECD751260009ECBE910EC71E897D4790EE93DBA8E034C

Note: Hardening tasks (keyboard accessibility and i18n) and Linux package bundles remain in active progress toward the v1.0 release.

Full Changelog: https://github.com/developerakshat12/FluidCorePDF/commits/v0.9.0