Added
-
Commands can have no keyboard shortcut without becoming unavailable in
the command palette. Shortcut recording can be cancelled, and help reflects
cleared bindings (GH #523). -
Reset interface zoom is available in the command palette (GH #522).
-
Changes from a long-absent device now merge automatically. If an older
incoming change falls behind this device's compact local history, Tine
rebuilds that history, waits for any still-arriving prerequisite, and resumes
editing by itself without asking you to reopen the app or merge manually. -
All of a query's display settings, in one place. A query block carries six
presentation facts — the view, what the results are grouped by, the sort order,
which columns show, what the footer totals, and a row limit — and until now the
controls beside a query could state one and a half of them:+ sortheld one
sort,+ summarizeheld one total and one grouping, and the column list, the
view and the limit had no inline control at all. A Display button in the
open sheet now holds all six. The sort, column and total lists are real lists:
add to them, move an entry up or down, remove one. A second sort or a second
total is editable rather than invisible, and a setting Tine's controls do not
cover is left exactly as your file spells it. Every change is one undo step and
one ordinary property line.A query table now saves what you do to it, too. Clicking a header sorts the
results and keeps that sort in the note — ascending, descending, then off.
Where the engine cannot sort by a column (the title, the task marker, tags, a
computed column) the table still sorts what is on screen and says Table-only
sort, so the difference between sorted and saved as sorted is visible
rather than found after a reload. Dragging a header saves the column order, and
a column's footer cycles count / sum / average. -
Picking a field now shows you your own graph. Adding a condition used to
ask which KIND of thing you wanted first, and then — for a property — offer an
alphabetical list of every key in the graph, with no counts and no types, in
which a key you use four hundred times sat below one you used once. Changing an
existing row's field asked the same question again through a different control.
Both are one list now, and it is your graph: the built-in fields first, then
every property key you actually use, commonest first, each row saying what type
Tine has observed for it (and what you declared, when you have), how many
blocks or pages carry it, and a few of the values it holds. Type to narrow it.
A key the list does not already offer where you need it is offered by name —
Use "…" as a block property, and on a block query Use "…" as a page
property for the same key read off the block's own page — each with its
honest0 blocks today. That is how a key your graph has not got yet, and a
key it has only on pages, both stay writable. A graph with thousands of keys
opens as quickly as a small one — the list draws a screenful at a time — and
the arrow keys reach any of them, including on a phone-width window, where the
sheet scrolls to bring your own properties into view.While Tine is still reading your graph's properties, the list says so and
keeps the built-in fields usable; it does not show an empty graph. A property
condition waits for that answer rather than guessing that the key holds text —
which matters most right after you declare a type, when the old answer is the
wrong one. Your chosen key and anything you have typed are kept while it waits.Built-in fields — Task marker, Scheduled, Page / tag reference — show no
count. Tine keeps no statistics for those, and showing a borrowed number would
be telling you something it never measured. -
The query text is always there, and it answers back. The box holding the
query's text used to be a collapsed Query text twisty at the foot of the
sheet — the one control that can say everything the rows cannot was the one
control you had to know to look for. It is now simply part of an open sheet,
and Tine reads what you type as you type it. When it cannot read something it
says which word, offers Show me to select that word in the text, lists the
spellings it does know and the property keys your graph has that look like it,
and points at the Guide's query-language reference. Your results stay on
screen, greyed, while the text is broken — a typo is not an empty graph — and
Save query text only ever saves the text that is in front of you. The
⟨advanced⟩row is a button now: press it and the cursor lands in the text at
the part the rows could not draw. -
A query Tine understood only part of says so plainly. It used to list what
it could not read under a bare No results, which read as "here are your
results". It now says: Tine didn't understand part of this query, so it
returned no results — which is what actually happened. -
A query block now reads as a sentence, and edits as a sheet. The row of
chips over every{{query}}is gone. At rest a query is one line of plain
English — Blocks where task: TODO and page: Project/Roadmap — with the number
of results beside it and a ⚙ to open it. Click either and a sheet opens over
the blocks below: Find blocks ▾ where … on top, then one row per condition,
each of themfield ▾ | operator ▾ | valuewith a ⋮ for the rest and an × to
remove it. + Add condition adds one. Conditions can be grouped as all of,
any of, none of or not all of when a query needs a branch. Escape, Back,
or a click anywhere else closes the sheet and leaves the sentence. On a
phone-width screen the sheet docks to the bottom of the window instead of
hanging off the side of the block.A condition Tine cannot draw as a row is still shown — in red, with the reason,
and removable — rather than hidden behind raw text, and a query nested far
deeper than anyone means to read folds into one⟨advanced⟩row that is still
saved, still run, and still removable. Nothing about the file changes: the
block stays an ordinary{{query}}bullet, and the query text is still there
under the sheet to read or edit directly. -
Changing what a query selects tells you what it will cost. Switching a
query between blocks and pages used to silently drop the conditions that
stopped applying —taskmeans nothing for a page — so a click could delete
half a query without saying so. Tine now re-reads the whole query first and
says exactly what happened: Switching to pages — 1 of your 3 conditions don't
apply to pages (task = 'TODO'), with Remove them, Keep anyway and
Cancel. Kept conditions stay visible as red rows with their reason, so
nothing disappears behind your back. -
A typed condition reopens as the comparison that wrote it.
cost > 100,
due before 2026-01-01andname starts with projcould be built but not read
back: reopening one showed a bare equality, and editing it destroyed the
comparison. Every comparison the builder offers now survives the round trip, so
a query you built last month opens with the operator you chose. -
One
/querycommand. The slash menu had Query and Query (visual
builder) as separate commands that made the same block. There is one now, and
it opens the new sheet with the field chooser already up. -
{{query (all-page-tags)}}works. Logseq has this filter — every page
that carries at least onetags::value — and Tine did not know it, so a
graph written in Logseq that used it showed nothing. It now selects the same
pages Logseq selects. -
A query that finds nothing can say which condition emptied it. Under an
empty result there is now a why empty? disclosure: for each condition it
shows how many blocks that condition matches on its own, and how many the
query would match without it. A query with several conditions used to say only
"No results", which is the one moment you most need to know which one to
loosen. Nothing extra is asked of the engine until you open it. -
Ctrl+Y also redoes, on Windows and Linux. Ctrl+Z undid and Ctrl+Y did
nothing, because Logseq binds redo to Ctrl/Cmd+Shift+Z and leaves Ctrl+Y
unbound — so the key most editors on those platforms use for redo reached no
command at all. Ctrl+Shift+Z is still the binding the shortcuts list shows and
remaps; Ctrl+Y rides along as a second default, keeps one Redo row, and is
dropped if you bind Redo to something of your own. Not on macOS, where Ctrl+Y
is the system's yank inside every text field (GH #491). -
You can tell Tine what a property is, and then compare by it. Open a
property in the visual query builder and it now names the property's type —
text,number,date,checkboxor a page link — and whether that type was
observed from your own values or declared. Choose declare type… to state it
yourself, with a list of box for keys that hold several values; Tine writes
an ordinarytine.type::line on the page named after the key, and
remove declaration takes it away again. Declaring one is what makes real
comparisons available:before/after/betweenfor dates,<≤>≥
for numbers,containsandstarts withfor text. Once a type is declared,
Tine also tells you how many blocks disagree with it, which is usually a
typo you wanted to find. -
Search and the page switcher tell you when they are still catching up.
Whole-graph search, the Ctrl-K page switcher and the((block picker now read
their answers from the same index the rest of Tine queries, instead of
re-reading and re-parsing your pages for every keystroke. On a large graph that
is the difference between a search that stalls and one that returns. Two things
change on screen. While the index is still being built — right after opening a
graph, or in the moment after a save — the switcher says it is searching rather
than showing an empty list, and if it genuinely cannot answer it says so and
offers to retry, instead of silently reporting that nothing matched. An empty
result now means "nothing matches", and only that. -
A search that returns both pages and blocks is now two results, each with
its own controls. A mixed query used to render one undifferentiated run of
hits and offer one set of display settings for all of them, so choosing a
table because the pages suited one meant the blocks got a table too, and a row
limit meant to trim a long block list also trimmed the pages. Pages and
Blocks are now separate sections, pages first, each with its own Display
button, its own view, its own columns and sort, and its own row limit. Setting
one says nothing about the other: emptying the Blocks section leaves every
page in place, and neither section can borrow the room the other did not use.
A section that matches nothing says so and keeps its controls, so you can
change the setting that emptied it.A section that leaves its display unset still follows the query's own
settings, exactly as before. Saying nothing and saying nothing, on purpose
are different states and Tine keeps them apart: clearing a section's settings
is recorded, survives a save and reopen in both Markdown and Org, and does not
quietly revert to the inherited ones the next time the file is read. -
Choose what makes a page a match. A search that returns pages matched them
on their name and aliases only. A query can now ask for pages matched on their
content — a page whose own blocks satisfy the search — or on both.
A page admitted by its content is returned once, showing its best-matching
block as the evidence, and the Blocks section is unaffected by the choice.
Leaving it unset keeps the existing name-and-alias behaviour byte for byte.
Changed
-
Page rename scans less text in Direct Files graphs. A controlled
10,005-file graph completed the reference scan about 34.5% faster; the later
whole-graph cache refresh remains a separate cost (GH #406). -
Blocked rename/save messages identify the actual pending work, conflicts
or reopen requirement. This improves diagnosis of GH #535; the underlying
reported false-conflict state is not yet resolved. -
Default task styling uses consistent checkbox colors, strikes through
completed task text without its label, and applies the approved hover,
opacity, title-spacing and primary-text theme adjustments (GH #394). -
A query with two conditions no longer reads the whole graph to answer about
three blocks. Every condition in a query was asked of the index as its own
complete list — "all the DONE tasks", "all the blocks tagged x" — and SQLite
builds each list in full before it compares them. So a query scoped to one
page, answering three rows, still enumerated every DONE task you have, and got
slower as the graph grew even though its answer did not. Tine now picks the
one condition that names a single thing (a page, a reference, a tag, a property
value), asks the index for that, and checks the remaining conditions against
each candidate directly — including when your query groups its conditions,
which used to hide the useful one a level down. On a 1,045-file graph the
three slowest queries in it went from 1.0 ms to 0.07 ms; on a thirty-times-
larger copy of the same graph one of them went from 53 ms to 0.4 ms. Results, their order and their
grouping are unchanged, and both spellings are checked against the old
whole-graph walk on every test shape. -
Linked References looks only at the blocks that link to the page. The
index has always known which blocks refer to a page, but the panel asked it
only which PAGES did, and then re-examined every block on each of them. On a
1,045-page graph the busiest page narrowed to 184 pages holding 3,434 blocks,
of which 412 actually referred to it. The panel now asks for the blocks, so it
examines about one block for every ten it examined before. The rows, their
order, their breadcrumbs and their highlighted matches are unchanged, and a
page the index cannot narrow is still read the old way rather than answered
wrongly. Unlinked References is unaffected: its index narrows to pages. -
Opening a page no longer reads the whole graph while the index is catching
up. Linked and Unlinked References used to answer a question the index was
still preparing by parsing every page in the graph instead. Right after a
graph opens, that is every file you have, once for each of the two panels, to
produce rows the index serves a moment later anyway. The panels now wait the
same fraction of a second a query block already waits, and the Unlinked
References header says what it is waiting for. Printing, publishing and the
reference diagnostics are unchanged: they have nothing to wait on, so they
still read the pages directly. -
The query sheet's
+ sortand+ summarizebuttons are gone. Everything
they did — and the three settings they never reached — is in the Display
panel that now sits beside every query, on the sheet and inline alike. Notes
written with the old controls are read and rendered unchanged; only the place
you go to edit them has moved. -
Queries order all matches before sampling and display limits. Without an
explicit sort, a sample is taken from the complete default page/document
order, so an existing sampled query may show different rows than it did
before. Query statistics describe that complete sample even when a display
limit hides rows. -
Export to PDF answers a page's queries from the same index the rest of Tine
reads. A{{query …}}or{{tine-query …}}inside a page you export is
answered from the current search index on both Direct Files and Managed
graphs, instead of walking the graph a second time just for the export. Two
differences you will notice. While the index is still being built — the first
open, or the moment after a save — preparation now waits and retries rather
than exporting a page with a gap where the results belong. And when a query
exceeds a Print limit, whether in matches, source length or nesting depth,
Tine tells you which limit it hit and prepares nothing: no print dialog opens
on a page whose results are incomplete, where before the PDF was produced with
a "narrow it before publishing" note printed in place of the results. Starting
a second export, or switching graphs mid-preparation, cancels the first
cleanly. Page-anchored queries remain unsupported in a printed page. -
A
{{query (property …)}}on a Direct Files graph no longer re-reads every
page to work out what its values mean. Deciding whetherrank:: 007is a
number or a word is a whole-graph question, and Tine answered it by walking
every loaded document each time it needed an answer, even though the same
facts already sit in the projection it keeps beside the graph. Once that
projection is ready the answer is read straight out of it; while it is still
being built — the first open, or the moment after a save — the old walk still
answers, so nothing waits on it. The values a query matches are unchanged, and
proved so: all three sources now build a byte-identical table on the same
graph. -
Tine now says when a query stops being readable by Logseq. Some things
you can build — grouping, column totals, typed comparisons likecost > 100—
have no{{query}}spelling at all, and saving one has always rewritten the
block into Tine's own{{tine-query}}form, which Logseq shows as plain text.
It did that silently. A notice now appears under the query the moment it
happens, with Undo that change — the ordinary undo, so Ctrl+Z does the same
thing — and Keep it. Don't show this again is remembered per graph on
that device only; the choice is never written into your graph and never syncs. -
A view directive you remove now stays removed. Column totals and grouping
were written both into the query text and into the block'stine.*properties,
and the properties win — so deleting a total or a grouping in the builder left
the old copy in the text, and the next time the block was read it came back.
Those two directives now live in the block's properties alone;sort-byand
samplekeep their place in the query text, because Logseq reads those too. A
block you open and close without editing is still not written at all. -
Reopening an unchanged Direct Files graph no longer re-parses it. Tine
now checks the graph's files against the query projection it keeps beside
the graph, page by page from the file bytes, and parses only the pages that
actually changed since the last session; an unchanged graph is ready with
nothing parsed and nothing retained in memory. A missing or damaged
projection is rebuilt by streaming pages through a bounded queue instead of
holding the whole parsed graph. Saves and deletions reach the projection
whether or not a parsed copy of the graph exists. -
A query about pages now answers with pages.
{{query (page-property …)}},
(page-tags …)and(namespace …)ask a question about pages, but Tine
answered them by listing every block on each matching page — so one matching
page could fill the result with its whole outline. They now list the pages
themselves. A query about blocks is unaffected. -
The query block's
⚙ advanced/← Simpleswitch is gone. It converted
between the chip builder and a datalog query, in both directions, and neither
direction was safe: going out dropped any sort, grouping or summary the query
carried, and coming back could only read the exact shape it had itself
written, so an advanced query written by hand came back wrong or not at all.
An advanced query now stays advanced — editable as text, with its own note
saying which parts Tine ran and which it ignored — and is no longer offered a
chip builder that would rewrite it. -
{{query (property …)}}now matches the way you read the page, not the way
the bytes happen to be written. A property value matches whatever its
letter case —status:: Doneanswers(property status done); a value
written as a list matches any of its items, sotopics:: rust, queries
answers(property topics rust); and a key whose values are all numbers or
all dates is compared as a number or a date instead of as text, so
rank:: 007answers(property rank 7)and a date property compares in
calendar order. Logseq compares the raw text, case-sensitively, and splits
only a handful of built-in keys — so a property query that found nothing in
Logseq may well find what you meant here. Page names ([[…]],tags::,
alias::) were already matched case-insensitively and still are. -
The Concord base ledger now reclaims entries whose stored text has been
removed from underneath it. Its prune already dropped unreferenced blobs and
unreadable records; an index or conflict pin naming a blob that an antivirus
quarantine, a disk cleaner or a partial restore had deleted was kept forever
as dead metadata. Conflict diffs were never affected — a missing blob has
always degraded to the ordinary two-column diff — and the ledger still never
warns about one.
Fixed
-
Privacy-safe diagnostic reports now include the bounded reason code for
failed Tine-managed storage page saves, making prolonged unsaved-draft
failures diagnosable without debug mode or a special build. A large report's
selectable preview is shortened so Settings remains responsive; Copy and
Save still export the complete report (GH #540). -
Closing with failed saves identifies the affected pages. Choosing not to
discard opens a recovery panel with page navigation, save retry, and draft
copying, including a complete recovery copy (GH #540). -
A conflict whose original physical page is unavailable exposes its retained
draft and guarded resolution instead of hiding recovery behind the page-load
error (GH #541). -
Recovered drafts can be resolved after files change while Tine is closed. Concord now applies choices to the same current file version shown in its review. A further change after review still stops the write and asks for a fresh review.
-
Externally replaced images refresh reliably in linked asset folders. File notifications through an approved assets symlink now reach every graph window sharing that folder, including after files are deleted.
-
Quick Capture works on a cold launch. Starting Tine with
--capturewaits for the selected graph to open before showing the capture window, so page suggestions and the saved completion policy are ready for the first input. -
Compact PDF toolbars keep More and Close reachable. The document title
uses its own row in narrow panes, and settings and outline panels stay below
the controls (UI-PDF-COMPACT-TOOLBAR-CLIPPED-INTERNAL). -
Dragging down through multiline text keeps selection under the pointer.
Starting a drag in rendered text no longer accumulates a character offset on
each wrapped line; code blocks and Unicode text follow the same native caret. -
Windows network graphs open correctly through UNC paths and mapped SMB
drives. Directory enumeration preserves the network root while retaining
filesystem path-confinement checks (GH #533). -
Android formatting-toolbar taps perform each command once. The toolbar
retains its horizontal position and editing focus, stays above the keyboard,
and keeps the keyboard open during sibling moves (GH #495, #496). -
Touch long-press page menus stay open. Touch does not arm desktop hover
previews, and duplicate native context-menu or compatibility-click events no
longer dismiss the menu just opened (GH #207). -
Initial native Android text selection updates the formatting toolbar
without requiring a later selection-handle movement (GH #375). -
Indent and outdent preserve the caret and selection instead of moving
them to the end of the block (GH #519). -
Code-block horizontal scrollbars can be dragged without the click
entering edit mode (GH #520). -
Embed gestures keep the intended host or source. Clicking a gap does not
edit the host block; dragging an embedded root moves that occurrence, while
nested source controls retain their own actions (GH #514, #516). -
Editing below an expanded embed preserves the viewport, including Undo
and Redo when the source text changes the embed's height (GH #515). -
Journals adds the new day after midnight without replacing the active
editor, preserving its draft, focus and selection (GH #532). -
Block references render live task states while preserving literal task
words and aliases (GH #518). -
Editor emoji font selection follows the platform display font, reducing
the font switch between reading and editing (GH #458). -
Quitting while a change arrives from another device now shuts down cleanly.
If a synced change landed in the moment you closed Tine, the shutdown reported
an error instead of finishing normally. Nothing was lost either way — the
change was already saved — but the error was wrong and alarming. Ordinary
progress during shutdown is now treated as ordinary progress. -
Concurrent page titles after a checkpoint are reported as an editing conflict. If two offline devices gave one page different titles and compacted their history, reopening could incorrectly report a damaged page-name index. Tine now identifies the conflicting titles correctly; synchronization may remain blocked until that conflict is resolved.
-
Queries survive a settings change. On a Direct Files graph, dismissing
the Guide toast — or changing the default home, time tracking, bracket
display, doc-mode Enter, logical outdenting, preferred format or journal
title format, restoring a backup, or havingconfig.ednrewritten outside
Tine — turned every query block into "Query results unavailable" until the
graph was reopened. The refresh that follows a configuration write rebuilt
the graph without the query index the ordinary open attaches. Both paths now
attach it through one function, and the refresh retires the previous index
worker before starting its replacement. -
Creating a block no longer freezes every query on "Rebuilding the query
index…". A block created in the editor is saved under the editor's own id,
which is not a UUID; the index refused the whole page for it, and every
automatic rebuild refused the same page again, so after one such save no
query in the app answered until restart. Such a block now gets a stable index
key and answers still name it by the id the editor knows. -
"why empty?" describes an answer, not the absence of one. While the query
index was still rebuilding, a query block said "No results" and offered
"why empty?", which opened onto an empty panel. The block now shows
"Rebuilding the query index…" until its query has actually run, and the
affordance appears only for a query that ran and matched nothing. -
A query and a board no longer disagree about what
group by statemeans.
One line,tine.group-by:: state, meant two different things depending on
which face was reading it: the task marker to a board, and an ordinary property
namedstateto a list — so changing a query from a list to a board could
change what it grouped by, and a board asked to group by a property called
statussilently grouped by the task marker instead. A query now names its
grouping field outright, in a line of its own:tine.group-field:: prop:status
for the property,tine.group-field:: statefor the task marker. There is one
reading of that line, and one piece of code produces it — the app, the saved
file and a published page cannot drift apart, which they could before.Notes written the old way keep working with no migration and are not rewritten
until you change the grouping yourself; when you do, the old line is replaced
rather than left beside the new one. Turning grouping off on a board now stays
off: switching the view used to bring the task marker back, because "you said
no" and "nobody said anything" looked the same in the file. Tables and boards
built from a block's own children are unchanged. -
A query summary shows every total you asked for. A query can ask for more
than one —count; cost=sum— and only the first was ever shown, whichever the
line happened to spell first; asking for a second one looked like it did
nothing. Every requested total now renders, in order, repeats included, both
overall and per group. The grouped breakdown, the board's columns and the
table's footer are now computed by one piece of code over one set of rows, so
they cannot disagree about which group a row belongs to. Grouping by tags puts
a row in every one of its tags' groups, exactly as the board does, and the
summary says so instead of presenting counts that do not add up to the result
as though they did. -
Which columns a query table shows, and what its columns ARE, are two
different lines now — and neither erases the other. A query block used to
keep both intine.fields::, so the two halves of Tine that write it took
turns destroying each other's work: editing the query's conditions replaced a
table's declared column types with a bare list of names, and declaring a column
type deleted the list of columns you had chosen to see.tine.fields::is now
only the column types; a newtine.columns::line holds the columns a query
table shows, and their order. Editing a query leaves your types, widths,
filter, formulas and any other property on the block exactly as they were, and
declaring a type on a query table carries your existing column choice across
in one step you can undo in one go.Notes written before this keep working with no migration and without being
rewritten: atine.fields::line holding only plain names is still read as a
column list until something replaces it. Published pages now show the same
columns, in the same order, with the same types as the app — they used to
ignore the choice entirely. A column you asked for that no result carries
still gets its place, empty, instead of shifting the rest. -
Editing a query's conditions no longer costs it its grouping or its
totals. Saving a change to what a query selects rewrites the query's text,
and that text has no room for grouping or aggregates — so a(group-by …)or
(aggregate …)written in the query itself simply vanished on the next save,
along with everything else the rewritten text could not carry. Tine now writes
down whatever the block does not already record before rewriting it, so a
filter edit keeps the view you had. Column totals a query does not recognize
— the sheet's ownmedian,stddevand the rest — survive the save untouched
instead of being dropped, and renaming a field carries a query's totals across
intact, repeated columns and all. -
Managed Storage page deletion retains original block state for Restore, and moving a subtree within its page preserves concurrent edits to its children.
-
Managed Storage accepts independent page-path and content edits from offline
devices without requiring their whole path indexes to match at delivery time.
Exact path ownership, release ancestry, and local journal checks remain in place. -
Managed Storage reuses durable writer identities across edits and restarts.
If its writer record is lost or damaged, new edits use a fresh causal identity
so an older offline branch can still arrive with its original edits intact. -
Managed Storage validates the exact peer-counter ranges in incoming CRDT
updates, rejecting replayed ranges even when their starting frontier matches. -
A query written in the middle of a line now shows its title. A
{{query …}}with a title or other display options — anything in the trailing
{…}— was read back from a truncated copy of your text whenever it sat
inside a sentence rather than alone on its own line: the closing brace was
missing, so the title was ignored and a stray}appeared after the results.
A query with a comma inside a quoted condition was cut at the comma the same
way. Tine now reads the query out of the file exactly as you wrote it, so what
it shows is what is there. -
A published page no longer prints a stray
}after a query. Publishing
read query macros from the same truncated copy, so a query carrying a title
emitted its options' closing brace as visible text next to the results, and
the results themselves were computed from a query missing its last character. -
Renaming a query can no longer rewrite the query. Editing a query's title
used to re-assemble the whole macro from Tine's own reading of it. Now the
title is the only thing that changes: your conditions go back to the file
exactly as you typed them, even for a query Tine only partly understands. If
the new title would produce something Tine could not read back, nothing is
written at all and the query says why. -
A saved query can no longer be written as text Tine will not read back.
A query whose conditions produced a comma next to a[[page]], or that
started with a page reference and carried a title or a sort order, was written
to the file as bytes the document reader does not see as a query at all: the
block turned into literal text and the query was gone, with no way to get it
back by reopening. Saving now proves the result is readable — with the same
parser that renders your page, in both Markdown and Org. A query that starts
with a page reference is written in an equivalent spelling Logseq reads
identically; anything still unreadable is refused with a message instead of
being written, so the query you had stays exactly as it was. -
Turning a row off no longer changes what the rest of the query answers.
Disabling one condition in a group could make a query that matched nothing
suddenly match everything, because a disabled row and a leftovertrue
cancelled each other out in a way the query never asked for. A disabled row
now removes exactly itself. -
A
<% current page %>query no longer runs twice when you navigate.
Moving to another page re-ran such a query immediately against the page you
had just left, then ran it again once the substituted text caught up — so one
navigation cost two whole-graph passes and could briefly show the previous
page's answer. It now runs once, for the page you are actually on. -
A broken or unusual condition survives editing another row. A commented
out condition with an unfinished quote used to be replaced withfalse— the
text you wrote was gone after one save, and the unfinished quote could swallow
the working row underneath it. Oldcontent-regexconditions had no way to be
written down at all, so editing any other row in the same query erased them.
Both are now preserved exactly, shown with their original text and error, and
left alone when you edit a neighbour. -
A query with a filter Tine does not understand now says so instead of
quietly answering a shorter question.{{query (and (task TODO) (frobnicate x))}}used to drop everything from the unknown filter onwards
and run as(task TODO)— a list that looked right and was not. The unknown
filter is now reported, the rest of the query is still shown, and no results
are returned until it is fixed. -
A property line with no value no longer crashes the reference reader. A
block whose last line is a baresize::— a property you have written the
name of but not the value — could panic while Tine gathered the evidence
behind a linked reference. -
Two pages whose names differ only by punctuation or case no longer wedge a
Managed Storage graph. Creating a page and then, before the background
save-settling caught up, creating a second page whose name folds to the same
key (Alphaand/Alpha, orFooandfooat a different file path)
reported both saves as successful — and then left the graph unopenable, with
every later open refusing. The second save is now refused up front, while
nothing has been written, the same way it already is once the first save has
settled. -
A Managed Storage sync operation no longer gets stuck behind its own
leftover crash evidence. If Tine lost power midway through tidying up after
publishing a file to the shared sync folder, it left behind a small record
named for that exact operation — and the next time the same operation ran, it
refused to start because that name was taken. The refusal could only clear
when an unrelated periodic sweep happened to run, which on a quiet graph might
be never. An operation now clears its own leftover record and carries on. -
Managed Storage keeps cleaning up sync conflict copies for the life of the
graph. When a file-sync service delivered a conflict copy of a shared
provider file, Tine cleaned it up and kept one small record of what it had
removed — and never retired those records. After 512 of them, the cleanup
itself started refusing, so a busy multi-device graph accumulated conflict
copies it could no longer clear from inside the app. Those records are now
retired as soon as the operation they belong to is finished and forgotten, so
the directory tracks the graph rather than its history. Checking there is
room for one more also no longer opens and inspects every record already
there, which took up to 511 file opens on a path a save waits on. -
On Android, dark mode no longer blanks the notification bar. Since 0.6.981
the strip behind the status bar has been painted by the app window rather than
the page, and it followed the phone's light/dark setting while the clock and
icons followed Tine's own — so running Tine in dark mode on a phone still in
light mode put white icons on a white strip. One setting now decides both
(GH #467). -
A conflict Tine cannot read no longer freezes the page. When the
comparison failed, the panel stayed on "Reading both versions…" forever and
the page body went blank, so the page could not be used at all. The failure is
now shown, with the reason, and the rest of the page keeps working (GH #490). -
A page with an unresolved save conflict can be opened on disk again.
"Open with default app" and "Show in folder" both refused while a conflict was
pending, which left a page whose conflict view would not load with no way to
read it at all. Both now open the file as it stands on disk and say so; your
unsaved changes stay in Tine until you resolve the conflict (GH #490). -
Clicking inside a code block now puts the cursor where you clicked. In a
code block of any size, clicking anywhere in it opened an editor that looked
blank: the cursor jumped to the very end of the block, and the editor was
scrolled to the far right of its longest line, so none of the code you clicked
was on screen. Both halves are fixed — a click maps to the character under it,
and the editor opens showing that character (GH #489). -
Clicking a link to the PDF you are already reading keeps your place.
Opening a PDF link with no page or highlight attached — a plain
asset link to the document already open in that pane — jumped the reader back
to page 1 and cleared the highlight you were looking at. Reopening the
resource you are already on is now a no-op; a link that does name a page or a
highlight still navigates. -
Zooming a PDF no longer throws you back to page 1, and your place in a PDF
survives quitting and reopening. A zoom briefly collapses the reader's
scroll position, and the reader was recording that momentary position as
"where you are" — so zooming jumped to page 1, and a reopened PDF often
started at the beginning instead of where you left off. -
The PDF reader's Fit width, Fit height, Area highlight, Notes and Outline
are reachable again in a narrow reader pane. Below 520px — a split pane, a
companion pane, a phone — the toolbar correctly moved those five tools into
the More-settings menu, but a CSS ordering mistake left the menu copies hidden
as well, so there was nowhere left to click them. They now appear in the menu
exactly when the toolbar is too narrow to show them inline. -
The whole left-sidebar row opens its page again, and the right sidebar's
spare width is now the drag handle it was meant to be. v0.6.981 narrowed the
wrong pane: it made only the page title clickable in the left sidebar, which
turned a page namedtestinto a target a few characters wide, and it left
untouched the right sidebar, where the reported problem actually was. Both are
corrected. In the left sidebar every pixel of a row navigates, including the
blank space beside a short name — a deliberate divergence from Logseq, since
reordering a favourite is protected by the drag threshold, not by keeping part
of the row inert. In the right sidebar the title anchor no longer stretches
across the row, so the empty space beside it belongs to the reorder drag and
the hand cursor stops following the pointer out over nothing (GH #468,
GH #464).
Download an installer for your platform below. Windows and Linux ship both x64 and ARM64 builds (match your CPU). Windows users who prefer no installer can grab the portable Tine_*-portable.zip for their architecture. Windows builds are currently unsigned, so Windows may show a warning on first launch. The macOS build is signed and notarized. On macOS, if Tine repeatedly asks to access Documents, see the workaround in the README.