* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
The delete dialog called deleteProject from its click handler, so the
metadata read, recursive delete and sync data write all blocked the UI
thread. Run them on the IO dispatcher.
---------
Co-authored-by: Sean Li <22557931+sean0407@users.noreply.github.com>
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* New translations from Crowdin
* Give the snap libhunspell and dictionaries so spell check works
The strict snap sees core24's /usr, which has neither, so spell check never
loaded a dictionary. Stage libhunspell, mount the hunspell-dictionaries
content snap over /usr/share/hunspell, and point JNA at the staged library.
* Test that markdown editors underline misspellings in loaded text
* Point the snap's spell check at its dictionaries through DICPATH
platform-spellcheckerkt 1.4.0 searches DICPATH, so the /usr/share/hunspell
layout is no longer needed.
* Format bar: a quote that saves, toggles as the editor has them, and disabled states
The quote button toggles the editor's quote block, written as "> ", where its inline
style exported as nothing. Bold, italic and strikethrough use the editor's toggle and
show active only when the whole selection has the style. In a table cell the block
buttons are disabled and the rule button does nothing; undo and redo show disabled
when there is nothing to undo or redo.
base.css fills hovered pagination buttons with the accent color, which the
authors and feed outline styles didn't override. Disabled buttons also picked
up the filled hover.
* Keep unsaved scene buffers when the project scope closes
Scope close deleted every temp buffer, so an Android activity destroyed in the background lost unsaved edits. Only clean buffers are cleared now, Discard discards explicitly, export reads unsaved buffers, and the Android back handler observes dirty state.
* Discard the buffer of an archived or deleted scene without reloading
* Save the buffer of an archived or deleted scene without throwing
* Drop a scene's unsaved buffer and temp file when it is deleted
* Gate sync behind the unsaved-work dialogs and make Discard really discard
Sync flushed every dirty scene buffer in prepareForSync, so an automatic sync on
close committed scenes the user had just chosen to discard, and manual syncs
silently saved everything. startProjectSync now builds the same confirmation set
as requestClose (scenes, notes, encyclopedia, timeline) and ends in Sync instead
of Complete, so the existing dialogs run before the sync modal opens.
The Discard buttons previously only cleared the confirmation; they now revert the
scene buffers to disk and drop in-progress entity edits (or pop a create screen)
through a new Router.discardUnsaved hook.
* Close the remaining ungated sync paths and harden discard
Route the Home screen's Sync action through startProjectSync so it hits the
gate, ignore startProjectSync while a close request is pending, and stop the
scene synchronizer from persisting dirty buffers in prepareForSync and
finalizeSync: upload content and hashes already come from disk, and every
interactive sync is now confirmed up front, so the flush only ever committed
buffers nobody was asked about (crash-recovery buffers during the startup
account sync, for one).
ProjectRoot.discardUnsaved is now a suspend call that reverts scene buffers off
the main thread, and the entity components check and discard across their
whole stack rather than only the active destination.
* Refresh open views when their entity changes underneath them
Note, timeline event, encyclopedia entry and scene metadata views loaded
once and went stale after sync. They now follow their repository flows,
keeping an in-progress edit and only moving its baseline.
* Address review findings on open-view refresh
- An untouched open edit follows the synced change; only a dirty edit keeps its draft
- Entry reloads are serialized, survive I/O errors, and only run for the open entry
- A note deleted elsewhere closes its view unless a dirty edit is open
- Synced scene metadata is no longer marked dirty or written back by the panel
- Sync keeps the local draft name; reference use cases name their update source
* Catch specific exceptions in the entry refresh
* Offer in-app updates on the GitHub desktop builds
AppUpdater in :common with a no-op for every store build, and a Nucleus
backed NucleusAppUpdater in :desktop that checks shortly after launch and
every six hours while the new automaticUpdateChecks setting allows it. One
action, update(), downloads, verifies and hands the installer over; MSI,
EXE, DEB, RPM and AppImage installs relaunch on the new version, macOS
installs are told and sent to the release page until the signed-zip work
lands. A notice strip above the project list, a status row on the About
screen, and a settings toggle, each with previews and render tests.
* Keep an offered update on screen while it is re-checked
A check only shows Checking when nothing better is known, so the six-hourly
re-check no longer blinks the banner away. The updater's shared fields are
volatile, the banner and the About row share one state-to-text mapping,
the download test hands the installer over through a CompletableDeferred,
and the desktop platform settings component gets a test for the new toggle.
* Lay the update notice on the page and allow an environment override
The banner now renders inside the project list under the masthead, inset
to the page gutter, instead of as a strip above the whole content pane
butting against the nav rail. HAMMER_UPDATE_BASE_URL stands in for the
system property so an installed build can be pointed at a local server,
which is how the update from a 3.10.99 test build to the published 3.11.0
MSI was exercised end to end. The download test cleans up its staging
directory, as the real installer script does.
* Give the Android project selection preview fake the update members
* Fix backup restore nesting the project inside itself
Backup archives are rooted at the project's directory name, so unzipping
into the project directory left it without a project.toml at its root and
the project vanished. Restore now extracts to a staging directory and
swaps the unwrapped project into place, leaving the existing project
untouched if extraction fails.
The Backup Manager also lists backups of projects that no longer exist,
and reports a failed restore.
* Harden backup restore swap and missing-project listing
Move the existing project aside instead of deleting it so a failed swap
puts it back, serialize restores that share the staging directory, and
list a missing project once when it has both legacy and current backup
names.
* Stamp builds with their package format and publish update metadata
-Pformat=<token> is resolved in buildSrc, baked into BuildMetadata, and
printed in the startup banner next to the channel; every packaging step
passes its own. The MSI UpgradeCode is pinned to the value every installed
copy already carries. writeUpdateMetadata renders latest*.yml from the
attached installers and the release workflow uploads them to the draft
before it publishes. HammerReleaseProvider points the Nucleus updater at
releases/latest/download so partial-release tags cannot break downloads.
* Hash installers where they are built instead of re-downloading them
Each platform job now writes a <installer>.sha512 sidecar beside what it
attaches and hands the sidecars to the final job as a workflow artifact.
That job assembles latest*.yml from them and attaches the yml with the same
release action every other job uses, so nothing is downloaded back from the
draft and nothing depends on looking a draft release up by tag.
Three new options in the Export Story dialog, from user feedback on Word, EPUB and PDF exports.
- Number chapter titles: off by default for every format. On prefixes each title with its position in the headings and the Contents page. Word, RTF, PDF and Markdown used to number always; EPUB never did and now can.
- Font: Georgia, Times New Roman, Garamond, Arial, Verdana or Courier New for Word, RTF and EPUB. PDF keeps its built-in face.
- Keep blank lines between paragraphs: on by default. Off drops a lone blank line so the indent is the only separator; a run of two or more, and the boundary between scenes, still leaves one break.
* Derive the Mac App Store build mode from the distribution channel
The App Store build was detected two ways: Gradle read -PmacOsAppStoreRelease
for packaging and passed -Dhammer.app.store=true so IS_APP_STORE could gate
runtime behaviour. The channel enum already knows the same fact from
-Pchannel=mac-app-store, which build-appstore.sh passed alongside.
isAppStoreRelease now resolves from the channel token, the JVM property and
IS_APP_STORE are gone, and the three runtime readers (sandbox startup, the
bookmark-backed file access binding, the in-app changelog gate) branch on
DistributionChannel.current like every other per-vehicle rule.
* Share the channel resolver across build scripts and name the App Store check
Project.distributionChannel() in buildSrc owns the -Pchannel and F-Droid
detection, so :base and :desktop cannot disagree on the channel.
DistributionChannel.isMacAppStore replaces three spelled-out comparisons,
and supportsInAppChangelog is a pure function of the channel with a test
pinning the table. Docs corrected: section 1 is done, GitHubProvider only
uses the REST API for pre-release channels, and the App Store build also
needs -PbuildNumber.
Dark theme with a pure black background and base surface. Applies to project color themes and Android dynamic color too.
Offer the OLED theme on Android and iOS only
Read the live story as prose, one chapter at a time, from the story
editor details pane. Opens from the scene list footer, the scene editor
top bar, or the editor menu.
- Scenes in a chapter are stitched together with an ornament between
them; a toggle shows scene headings instead.
- While the reader is open the scene list works as a table of contents,
and its highlight follows the scene being read.
- Reader font size is its own setting. Text is selectable.
- Chapter and scene position survive rotation and process death.
Chapter collection is now shared with export, and the outline's chapter
pieces move into the design system. MarkdownView gains opt-in
importDuringComposition and isSelectable. The footer's outline button
uses a new short "Outline" string.
* Update ComposeTextEditor to 3.0.2
Styles move to TextEditorState.richTextStyles, headings use the block API so
they export as headings, and shortcuts match on layoutKey. Markdown pins
ParagraphSeparator.NEWLINE to keep the 2.8 on-disk format.
* Make the editor text size a render property
Rich text styles are a fixed set with em-relative headings, so the body size
lives on the editor text style and changing it rewrites no document. The
size steps through a ladder, shows its value in the format bar menu, and
answers Ctrl+Plus, Minus and 0.
* Recover from an undecryptable auth token keyset instead of crashing
Exclude the encrypted token prefs from backups, and attach the R8 mappings
to GitHub releases.
* Reset the Keystore master key along with the token prefs
2.6.0 was built against Compose 1.11 and lacks the IME members 1.12 made
abstract, so focusing an editor on iOS threw IrLinkageError on
ImeComposeStateAdapter.text and killed the app.
URL, numeric, tag, word-list, and delete-confirmation fields inherited
sentence capitalization and autocorrect, which inserted a space after
'.' in server URLs.
The desktop implementation of calculateWindowSizeClass reads an AWT window,
which does not exist under the Tao backend, and its LocalWindow reference was
removed in Compose 1.12. Size classes now derive from layout constraints.