Delete the in-tree de.jonasbroeckmann.kzip ZIP format implementation
(~700 lines across commonMain/jvmMain/iosMain) and the kompress.core
dependency, in favor of the published no.synth:kmp-zip 0.11.3 library
plus its okio adapter.
ZipUtils now wraps zipTo/unzipFrom for directory operations and uses
the synchronous ZipInputStream(ByteArray) constructor for the in-memory
extract path, removing the previous write-temp-file workaround. The
skipHiddenFiles parameter is dropped (production callers already passed
false) and the corresponding tests are removed.
Co-authored-by: Claude <noreply@anthropic.com>
- Switch to FlatLaf (Darcula) for an IntelliJ-grade look and feel.
Lives in buildSrc only — never ships in the released app.
- Restructure the dialog into TitledBorder sections: Version,
Publish scope, Will push tag, Changelog.
- Replace the always-on checkbox grid with an All / Targeted scope
toggle. Full release (the common case) is now one click and shows
the bare vX.Y.Z tag; Targeted enables the per-store checkboxes.
- Make the computed tag the dialog's focal point — large monospaced
bold, italic + dimmed when no platforms are picked.
- Right-align Commit button, live-update window title to show the
version transition, bump frame size for more changelog room.
Migrate the sync server's storage from SQLite to PostgreSQL with two
drivers: an in-process Zonky embedded server (the personal-install
experience) and a remote-Postgres mode (for multi-tenant deployments).
Single dialect, single set of .sq files, single set of DAOs.
The v1 schema bakes in Tier 1+2 cleanup: CITEXT for case-insensitive
columns, UUID for project ids, TIMESTAMPTZ everywhere, real BOOLEANs,
BIGSERIAL keys, partial indexes for sparse columns, and explicit ON
DELETE behavior plus CHECK constraints on every table.
A one-shot SQLite-to-Postgres migrator runs at startup against an
existing ~/hammer_data/server.db, copies it row-by-row in a single
transaction, verifies row counts in-transaction, then renames the
source to server.db.migrated-<ts>.bak. --migrate-dry-run lets operators
rehearse against a copy of production.
Verified:
- :server:test 409/0/0/0 (~2 min, embedded Postgres shared across the
JVM with truncate-between-tests)
- :integrationTests:jvmTest 9/0/0/0 (~54s)
- :desktopTest green via pre-commit hook
- Embedded-mode boot smoke against a fresh ~/hammer_data
- Remote-mode boot smoke against a Postgres 16 container, schema landed
cleanly with CITEXT/UUID/TIMESTAMPTZ promotions intact.
Notable carry-overs documented for ops:
- auth_token PK changed from `token` to (user_id, install_id); the
migrator collapses any historical duplicates via ON CONFLICT.
- One-shot migration is irreversible; an off-box backup of server.db
is the operator's job, not a replacement for the in-process .bak.
- 120MB of embedded Postgres binaries are now bundled in the server
jar (one of five platform artifacts is extracted at boot).
- Pre-commit hook now skips :server:test; CI should still run it.
The server now persists its sync state to PostgreSQL, with two interchangeable
drivers selected by `storage.type` in the TOML config:
* EMBEDDED (default): in-process Zonky Postgres on a pinned port, data dir
`~/hammer_data/pgdata`. Preserves the "single JAR, no external services"
feel of the old SQLite setup for personal installs.
* REMOTE: HikariCP-pooled JDBC to an externally-managed Postgres for
multi-tenant deployments.
Schema is laid in at v1 with Tier 1 + Tier 2 features baked in:
* TIMESTAMPTZ AS Instant for every timestamp column (via
`InstantColumnAdapter`).
* BOOLEAN columns (was `INTEGER AS kotlin.Boolean`).
* BIGSERIAL primary keys.
* CITEXT extension promoted onto `account.email`, `account.pen_name`, and
`white_list.email` after `Schema.create()` for case-insensitive UNIQUE
indexes.
* Explicit `ON DELETE` per FK; partial UNIQUE index on `account.pen_name`;
partial index for community-author lookup; CHECK constraints for length
and `expires > created` invariants.
* `INSERT ... ON CONFLICT DO UPDATE` for upserts; insert-then-lookup
pattern replaces `last_insert_rowid()` triple-queries.
UUID promotion for `project.uuid` and `deleted_project.uuid` is deferred to a
follow-up release; existing tests pass placeholder strings that fail UUID
validation.
The legacy SQLite SqlDelight setup moves to
`src/main/sqldelight-legacy-sqlite/.../legacy/` and stays compiled as a
read-only `LegacySqliteDatabase` so the one-time migrator can stream rows
from it.
`SqliteToPostgresMigrator` runs at startup when a legacy `server.db` is
present alongside a Postgres-configured server: copies row-for-row inside a
single Postgres transaction with `session_replication_role = replica` to
suppress FK checks during the bulk insert, runs a row-count parity check on
the same connection, commits, and renames the source file to
`server.db.migrated-<timestamp>.bak`. A `--migrate-dry-run` CLI flag runs
the same flow but rolls back, for staging rehearsal.
Downstream consumers (DAOs, repositories, frontend pages) updated to use
`kotlin.time.Instant` directly instead of SQLite-flavored date strings.
When -Pfdroid=true (or FDROID_BUILD env var) is set:
- skip the foojay-resolver-convention plugin
- exclude :desktop from the build (its eager jbrLauncher.get()
needs JBR, which F-Droid can't provide)
Reverts the Nucleus packaging port and its follow-ups:
- 93c7973c Trying nucleus for packaging Snap stays bespoke
- 7c14bcd5 Broaden artifact staging to handle Nucleus sandboxed pipeline paths
- d24f6183 Add homepage and debMaintainer for electron-builder
- 8831e9c5 Update release workflows for Nucleus packaging
- 4af31163 Port :desktop packaging to Nucleus framework
Compose Desktop is back as the packaging backend (jpackage targets,
custom packageMsix task, bespoke buildDistSnap/AppImage/Flatpak via
registerLinuxDistributionTasks). The Nucleus runtime libraries —
nucleus.darkmode-detector and nucleus.decorated-window-jbr — are
kept for window decoration and dark-mode detection in code, along
with the JBR jvmToolchain vendor pin and the libs.versions.toml
'nucleus' version entry that they depend on.
The Nucleus-pipeline state is preserved on branch
'nucleus-build-pipeline' for future reference.
Detect the native Linux desktop scale factor and feed it to AWT via
sun.java2d.uiScale before Swing initializes. No-op on macOS/Windows and
on JetBrains Runtime (which detects scale natively); needed for any
non-JBR JVM and for future GraalVM Native Image builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap desktop windows in JewelDecoratedWindow + JewelTitleBar so the app
draws its own title bar (works on any JVM, no JBR needed). Adds nucleus
decorated-window-jni + decorated-window-jewel artifacts, plus the Jewel
int-ui-standalone dep for the IntUiTheme wrapper that JewelDecoratedWindow
reads its colors from. Jewel artifacts live in the IntelliJ snapshots
repo (cache-redirector path resolves to cloudfront 404, the direct
www.jetbrains.com URL works).
DecoratedWindowScope implements FrameWindowScope, so the existing
EditorMenuBar (which uses FrameWindowScope.MenuBar) keeps working — the
JMenuBar will render below the custom title bar on Windows/Linux, and on
macOS it stays in the system menu bar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Swaps the bespoke OsThemeDetector for Nucleus's dark-mode detector.
The non-composable initial LAF install uses getPlatformDarkModeDetector(),
and the live-update path inside application{} uses the isSystemInDarkMode()
composable so theme changes recompose without a manual listener.
Drops the explicit jna / jna-platform deps that only existed to back-fill
jSystemThemeDetector's transitive graph.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces Compose Desktop's bespoke nativeDistributions + the hand-rolled
packageMsix, snapcraft, flatpak-builder, and appimagetool tasks with
the Nucleus plugin's unified DSL. Nucleus is a fork of compose-desktop-
application that adds first-class AppX/Portable/Snap/Flatpak/AppImage
targets, so org.jetbrains.compose can no longer be applied alongside
it (task name collisions); compose.desktop.currentOs and
compose.components.uiToolingPreview are now resolved via explicit
Maven coordinates instead.
The hand-edited msix/AppxManifest.xml, snap/snapcraft.yaml, and
flatpak/*.yaml are now generated from the DSL; the source files
remain in-tree for now and will be deleted once CI validates the
nucleus output across all targets.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resizable single-project stats widget (4x1 / 2x2 / 3x2 / 3x3+) showing
total word count, cadence-goal progress, today/streak, a 7-day sparkline,
and Open/Note actions. Adds a config activity for project selection.
Bumps Glance to 1.2.0-rc01 and wires up providePreview + setWidgetPreviews
so all three widgets render previews on Android 15+ pickers. Adds the
project name to the Add Note widget header. Extracts parseDailyWordTotals
and formatWidgetWords as shared helpers.
Notes, Timeline events, and Encyclopedia entries now share a markdown-aware
edit field (with format bar + spell-check) and a lightweight read-only
renderer that uses the editor's draw pipeline without its chrome.
Bumps composetexteditor to 1.8.2-SNAPSHOT (mavenLocal) for the new
RichTextView API; adds mavenLocal() to allprojects.repositories.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Renames ProjectTheme.color1/color2 to primary/secondary and pipes them
into a Material 3 ColorScheme via material-kolor's dynamicColorScheme.
The user's secondary seeds both the Material 3 secondary and tertiary
palettes, the Rainbow palette style keeps them in distinct hue lanes
instead of collapsing toward the primary, and surfaceTint is redirected
to secondary so elevated surfaces (cards, dialogs, top bars) pick up
the accent.
The override is hoisted to the platform entry points
(ProjectEditorWindow.AppContent on desktop, ProjectRootActivity.Content
on Android) so a single ProjectThemeOverride wraps the NavigationRail,
ProjectRootUi, FAB stack, and confirm-close dialogs. ProjectRootUi and
ProjectRootFab no longer self-wrap.
All five project FABs (notes, encyclopedia, timeline, scene group,
scene) explicitly draw containerColor/contentColor from
secondaryContainer so the second color is the obvious accent on the
heaviest visible chrome.
New project-data sync phase runs before entity transfer. The blob is a
single per-project row on the server, hash-checked for conflicts; the
client surfaces a per-field pick-a-side resolver UI when both sides
edited concurrently. Author name is a text field, custom theme uses a
clickable color swatch backed by skydoves/colorpicker-compose, and the
word-count goal is cadence + count. Each opt-in section nulls out its
data when unchecked so the wire signal is unambiguous.