Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ debug = "line-tables-only"

[package]
name = "pickscribe"
version = "0.2.0"
version = "0.2.1"
edition = "2024"
description = "PickScribe by Pickforge Studio: local Linux dictation with AI cleanup and paste"
license = "MIT"
Expand Down
230 changes: 6 additions & 224 deletions docs/releases/UNRELEASED.md
Original file line number Diff line number Diff line change
@@ -1,245 +1,27 @@
# Unreleased
# Unreleased (v0.2.2 candidate)

Working draft for the next PickScribe release. Keep this current while PRs
land. At release time, copy and polish it into the GitHub release description,
then reset this file.

## User-facing changes

- macOS dictation now ships for Apple silicon through the same no-sudo install
script as Linux, with native cue playback and appearance-aware tray icons.
Distribution is intentionally unsigned because Pickforge has no Apple
Developer ID; script installs avoid Gatekeeper quarantine, while browser
downloads require right-click Open or clearing the quarantine xattr. The
local whisper.cpp installer supports Homebrew and portable build tooling.
- On macOS, PickScribe can now start and stop dictation with a configurable
in-app global shortcut (Cmd+Shift+Space by default). Linux continues to use
the existing desktop-environment keybinding path.
- Transcribe audio or video files by dropping them onto the app or browsing
from the dashboard. PickScribe runs local whisper.cpp transcription with
progress and cancellation, stores results in History, and exports TXT, SRT,
or VTT. AI cleanup remains off by default. File transcription requires
`ffmpeg`.
- File transcriptions are excluded from dictation session and time-saved
metrics.
- Slow cleanup endpoints, doctor checks, recording cancellation, and recorder
warm-up no longer freeze the UI. Stopping incremental transcription now
preserves completed segments and only re-transcribes the unfinished tail.
- PickScribe now finds `whisper-cli` in `~/.local/bin` when desktop sessions omit
that directory from `PATH`.
- Whisper model auto-detection now recognizes English-only `.en` models and
other `ggml-*.bin` variants in the standard local model directory.
- Double-clicking empty titlebar space now maximizes or restores the window.
- On Linux, the float capsule now keeps its glow visible without capturing
clicks in the transparent margin. Other platforms keep a snug window to
avoid intercepting clicks. Its idle waveform also re-measures correctly while
the window settles.
- Tray state changes no longer repeatedly spawn desktop-theme probes.
- Settings now change runtime behavior only after persistence succeeds.
Float-button changes from the tray or capsule stay synchronized with an open Settings form
without overwriting unrelated unsaved edits.
- Fixed the float capsule staying out of KDE's Alt+Tab switcher when running
through the `PICKSCRIBE_X11=1` XWayland fallback. The KWin `skipswitcher`
rule now installs for any KDE Wayland session — native Wayland or its
XWayland fallback — instead of only native Wayland.
- Fixed unsaved Settings actions disappearing at compact window sizes. The
save/discard controls now render at the app overlay layer instead of
inside the scrolling Settings surface (whose entrance animation made it a
`position: fixed` containing block), so they stay visible and unclipped at
every supported window size. The header Save button is now visible and
disabled while clean, and hidden without shifting layout while dirty;
exactly one primary Save action is presented at a time. The dirty-state
overlay shows the full unsaved-indicator/Discard/Save dock at wide widths
and a labeled Save pill (discard remains reachable via the existing
navigation-away guard) at compact widths, matching the app's existing
700px sidebar breakpoint.
- None yet.

## Internal/release changes

- Added unsigned Apple silicon `.app.tar.gz` packaging and updater artifacts to
the release workflow, with a combined Linux/macOS `latest.json`. Extended the
installer with idempotent `~/Applications/PickScribe.app` updates and fixed
issue #57 by removing the Bash 3.2 parser-incompatible case statement nested
in command substitution.
- Integrated `@pickforge/tauri-updater` behind the default-off
`studioUpdateDialog` release flag. The shared dialog preserves packaged-only,
visible-main-window startup checks while excluding hidden login starts and the
floating capsule; the existing confirm flow remains active while the flag is
off.
- Added deterministic development fixtures for the shared update dialog at
`?update-fixture=available` and `?update-fixture=downloading`. PickScribe has
no visual-regression harness, so this keeps visual review reproducible without
introducing a new browser-test stack in this PR.
- Added file-transcription conversion, segmented whisper output, progress,
TXT/SRT/VTT formatting, and additive history fields for source files and
segments.
- The release workflow strips bundled `libwayland` libraries from AppImage
builds, verifies the rebuilt image, and re-signs it before asset collection.
- Tagged builds create a draft GitHub release. Publishing remains a manual gate
after the draft AppImage artifact passes a desktop smoke test.
- CLI and desktop cleanup now share provider resolution, local-only enforcement,
prompts, response handling, raw fallback, and conservative segment safety.
- CLI and desktop dictation now share the Linux clipboard, paste-chord, and
typing-backend delivery strategy.
- CI now blocks complexity regressions, coverage regressions, leaked secrets,
and high or critical dependency advisories.
- Added macOS compile support for the transparent float window and a blocking
macOS CI job that checks and tests the app on macOS 15.
- Added native macOS microphone capture via `ffmpeg`'s `avfoundation` input
device (16kHz mono s16 WAV, matching the Linux PipeWire pipeline), reusing
the existing recorder process/stop/log machinery instead of a separate
CoreAudio backend. `SttConfig::recorder` now defaults to `ffmpeg` on macOS
and `pw-record` on Linux; explicit user config is unaffected. Removed the
"Native audio capture" release blocker for macOS (dictation as a whole
remains blocked on paste automation, global shortcuts, and signing). The
doctor page now probes the platform-appropriate recorder (`ffmpeg` on
macOS, `pw-record` on Linux) before the dictation-support early return.
Added `src-tauri/Info.plist` with `NSMicrophoneUsageDescription`, which
Tauri 2 merges into the generated macOS bundle `Info.plist`.
- Added macOS text delivery through OS-shipped subprocesses: clipboard copy
via `pbcopy`, and hotkey paste / typing via `osascript` (System Events),
reusing the existing Linux `ProcessRuntime` seam with no new engine-crate
dependencies. Auto delivery resolves to the paste-chord hotkey on macOS.
Failures caused by a missing Accessibility grant now surface a clear
message pointing at System Settings → Privacy & Security → Accessibility
instead of the raw System Events error. Doctor's "Clipboard" and "Paste
backend" checks are now platform-aware (`pbcopy` / Accessibility-trust on
macOS, `wl-copy`/`xclip`/`xsel` / `ydotool`+socket on Linux, unchanged) and
now run for macOS too. Removed the "Paste automation" release blocker for
macOS; dictation still isn't a supported release target there pending the
other blockers (native audio capture, global shortcuts, tray/window
validation, signing, smoke tests).
- None yet.

## Validation

### Tested

- Issue #66 PR 6 (unsigned macOS packaging): `/bin/sh -n scripts/install.sh`,
`bun run test` (installer smoke plus 42 frontend tests), `cargo test` (123 +
10 + 7 passing, 1 ignored), `cargo check` in `src-tauri`, `actionlint`, and
ShellCheck warning-level checks pass on Apple silicon macOS. The signed
`bun run tauri build --bundles app` reached the updater packaging step and
failed only because the local environment has no `TAURI_SIGNING_PRIVATE_KEY`;
retrying with updater artifacts disabled produced `PickScribe.app`, ad-hoc
signed by Tauri. A synthetic release set verified that
`pickforge-tauri-release` generates one manifest containing both
`linux-x86_64` and `darwin-aarch64`.
- Whisper model auto-detection fix: `cargo test` and `cargo clippy --workspace
--all-targets -- -D warnings`. Live-verified the installed
`~/.local/share/whisper.cpp/models/ggml-base.en.bin` model is selected.
- pickscribe#66 PR 5 (macOS platform polish and support flip): `cargo test`
(115 + 10 + 7 passing, 1 ignored), `cargo test --manifest-path
src-tauri/Cargo.toml` (38 passing), `cargo clippy --workspace --all-targets
-- -D warnings`, `bun install --frozen-lockfile`, `bunx vitest run` (42
passing), `bun run check`, and `bash -n scripts/install-whisper-cpp-local`
pass on macOS. Live checks played the generated start cue once through
`/usr/bin/afplay` at low volume and confirmed the `defaults read -g
AppleInterfaceStyle` result (`Dark`) matches the current System Events dark
appearance. `node tests/install-script-smoke.mjs` does not exercise the
whisper.cpp installer and remains blocked by the pre-existing
`scripts/install.sh` line 154 syntax error.
- pickscribe#66 PR 2 (macOS audio capture via ffmpeg/avfoundation): `cargo
test` (workspace root, 122 tests including new `recorder_args` and
`platform` coverage), `cargo test --manifest-path src-tauri/Cargo.toml` (37
tests), `cargo clippy --workspace --all-targets -- -D warnings` (matches CI).
Verified device syntax on this machine with `ffmpeg -f avfoundation
-list_devices true -i ""` and confirmed `:default` is accepted as the input
target with this ffmpeg build (8.1). Live smoke: spawned the app's exact
`ffmpeg -nostdin -hide_banner -f avfoundation -i :default -ar 16000 -ac 1
-c:a pcm_s16le -y <path>` args, stopped with `SIGINT` after ~2s matching
`Recording::stop`'s escalation path — ffmpeg exited normally ("received
signal 2") and produced a valid 37,966-byte WAV, well above the 8KB
too-short threshold. No macOS TCC microphone permission prompt appeared;
Terminal already had mic authorization on this machine from an earlier
`-list_devices` probe.
- pickscribe#66 PR 3 (macOS text delivery): `cargo test` (root, 108/108 +
10/10 + 7/7 passing), `cargo test --manifest-path src-tauri/Cargo.toml`
(37/37 passing), and `cargo clippy --workspace --all-targets -- -D
warnings` (clean), all on macOS. Live smoke on this macOS machine: the
real `ProcessRuntime::copy_to_clipboard` macOS path piped a marker string
through `pbcopy` and read it back with `pbpaste` — round-tripped
correctly (kept as an `#[ignore]`d regression test, run manually with
`--ignored`). Did not exercise `osascript` keystroke synthesis (paste
chord / typing) live, to avoid sending real keystrokes into the active
session; also skipped the proposed harmless `osascript -e '... count
processes'` probe because it would trigger a first-run System Events
automation-permission dialog, which is itself a disturbance on a live
machine — both are covered instead by unit tests against the
`DeliveryRuntime` seam and the pure script-building/error-mapping
functions.
- Issue #66 PR 4 (macOS in-app dictation shortcut): `cargo test` (119 tests),
`cargo test --manifest-path src-tauri/Cargo.toml` (38 tests), `cargo clippy
--workspace --all-targets -- -D warnings`, `bun install --frozen-lockfile`,
`bun run check`, `bunx vitest run` (42 tests), and `bun run build` on macOS.
Live global-shortcut registration was intentionally skipped to avoid changing
the active desktop session.
- Issue #59 (macOS compile support and CI): `cargo check --manifest-path
src-tauri/Cargo.toml`, `cargo test --manifest-path src-tauri/Cargo.toml` (37
tests), `bun install --frozen-lockfile && bun run check`, `bun run lint`, and
`bun run build` on macOS.
- pickforge-platform#36 PR 4 (default-off shared update dialog): `bun run
check`, `bunx vitest run` (39 tests, including flag selection,
visible/focused deferral, and hidden-autostart single-check coverage), `bun
run test:coverage`, and `bun run build`. The full `bun run test` command is
blocked before Vitest by the pre-existing `scripts/install.sh` line 154 shell
syntax error, unchanged from `origin/main`.
- Issue #46 (float capsule KDE Alt+Tab exclusion, porting the accepted
PickGauge #49 fix merged as pickgauge#66): extracted the gating logic in
`kwin.rs` into a pure `is_kde_wayland_session(xdg_session_type,
xdg_current_desktop)` predicate and removed the `GDK_BACKEND == "x11"`
early return that skipped the KWin rule for the `PICKSCRIBE_X11=1`
XWayland fallback (renderer backend is orthogonal to session type — the
KDE-desktop filter that protects non-KDE desktops from kwinrulesrc writes
is unchanged). Ported 7 unit tests from pickgauge's `kwin.rs` covering the
predicate and the existing `group_has_key` helper (verified with an
isolated `rustc` compile — 7/7 pass — because the full
`cargo test --workspace --locked --all-targets` run fails to build on this
macOS dev machine identically on unmodified `origin/main`: `.transparent()`
is not available without the tauri `"transparent"` Cargo feature here, a
pre-existing environment gap unrelated to this change). `bun run check`,
`bunx vitest run` (33 tests), and `bun run build` all pass. Updated the
`ensure_float_window` doc comment and README's task-switcher table to
match PickScribe's actual platform support (Linux only for now; Windows/
macOS rows omitted rather than claiming untested support that doesn't
exist yet). Real KDE Alt+Tab validation defers to Elberte-PC.
- Issue #45 (unsaved Settings actions at compact sizes, porting the accepted
PickGauge #47 pattern): `bun run check`, `vitest run` (33 frontend tests,
including a new `settingsSaveDisplayState` characterization suite covering
clean/dirty header-visibility and single-action rules), `vitest run
--coverage` (ratchet holds), and `bun run build`. PickScribe has no
browser-preview/Playwright harness (unlike PickGauge's
`scripts/validate-browser-preview.mjs`), so no headless-browser regression
at the compact repro sizes was added — flagged as a follow-up decision
rather than introducing new test tooling unrequested. Confirmed the same
`.content.fade-up` containing-block hazard exists in PickScribe's
`App.svelte` as in PickGauge, which the fix addresses the same way.
- Feature and fix PRs ran their focused frontend and Rust checks before merge.
- Shared cleanup policy: `cargo test --workspace --locked --all-targets`,
`bun run build`, and CLI smoke checks for raw output, conservative segments,
local-only fallback, and strict non-zero exit behavior.
- Shared Linux delivery: `cargo test --workspace --locked --all-targets`,
`bun run build`, and CLI smoke checks for stdout-only, auto-to-type fallback,
`~/.local/bin` backends, clipboard-required hotkeys, terminal paste chords,
failed-copy suppression, and non-fatal paste failure.
- v0.2.0 release prep: `cargo check --workspace`, `bun run check`,
`bun run test`, `bun run test:coverage`,
`cargo test --workspace --locked --all-targets`, and
`bun run pickforge-tauri-release validate-config`.
- CI gate installation: `bun run check`, `bun run lint`, `bun run
test:coverage`, `bun run build`, root-package Rust tests, clippy, and
llvm-cov, gitleaks, OSV-Scanner, and actionlint.
- Nothing yet.

### Not tested yet

- Shared update dialog visual fixture review at 1020×720 and 780×560, and the
owner-gated signed packaged update smoke.
- v0.2.0 tagged release build and signed artifacts.
- Draft AppImage desktop smoke test, including interactive file drag/drop and
dialog transcription.
- Installer and updater flow against the v0.2.0 draft artifacts.
- Workflow lint with `actionlint` (not installed locally).
- Nothing yet.

### Release blockers

- Do not publish the draft GitHub release until its AppImage artifact passes a
desktop smoke test.
- None known.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pickscribe-app",
"private": true,
"version": "0.2.0",
"version": "0.2.1",
"type": "module",
"scripts": {
"dev": "vite --host 127.0.0.1 --port 1420",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pickscribe-app"
version = "0.2.0"
version = "0.2.1"
edition = "2024"
description = "PickScribe desktop app"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "PickScribe",
"version": "0.2.0",
"version": "0.2.1",
"identifier": "com.pickforge.pickscribe",
"build": {
"beforeDevCommand": "bun run dev",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/updateFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export async function mountUpdateFixture(state: "available" | "downloading"): Pr
const mounted = mountStudioUpdater({
adapter,
eligibility: { packaged: true, mainWindow: true, visible: true, focused: true },
metadata: { productName: "PickScribe", productMark: "PS", currentVersion: "0.2.0" },
metadata: { productName: "PickScribe", productMark: "PS", currentVersion: "0.2.1" },
});

const controller = mounted.controller;
Expand Down