Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
d41c926
v3.0.0 Polaris — anti-injection polish layer + deterministic cleanup
AmirK-S May 26, 2026
5f27ea8
fix: Whisper hallucination filter now strips accents + handles loops
AmirK-S May 26, 2026
cd672e1
fix: hallucination filter — anti-overprotection pass
AmirK-S May 26, 2026
52ec0b2
feat: embed git SHA at build time + show in Settings → Updates
AmirK-S May 26, 2026
bca6a11
v3.0.1 — first stable Polaris release
AmirK-S May 26, 2026
98e1e62
fix: Fn key — suppress macOS emoji picker + single tap stops hands-free
AmirK-S May 29, 2026
ae7824e
v3.0.2 — Fn emoji-picker fix + single-tap hands-free stop
AmirK-S May 29, 2026
6287219
feat: guided nudge to disable Globe key emoji picker for Fn hotkey
AmirK-S May 29, 2026
417be7f
v3.0.3 — guided nudge for Globe key emoji picker
AmirK-S May 29, 2026
555a358
fix: replace mic-recorder plugin with in-house cpal capture + harden …
AmirK-S Jun 1, 2026
873693a
v3.0.4 — replace mic-recorder plugin with in-house cpal capture
AmirK-S Jun 1, 2026
c1a6dec
v3.1 polish: audit response + VAD + audio device picker + frontend tests
AmirK-S Jun 10, 2026
1cbf47c
CI: bump Rust toolchain pin to 1.90.0 (1.85.0 too old for time>=1.88)
AmirK-S Jun 10, 2026
022793b
audio_capture: fix Result vs Option mismatch in device name match
AmirK-S Jun 10, 2026
2332180
fix Rust test assertions: polish 403 form + low_pass realistic attenu…
AmirK-S Jun 10, 2026
521bb42
CI build.yml: skip updater bundles so signing key isn't required
AmirK-S Jun 10, 2026
9b076e0
rm Finder dupes + .gitignore them (build 2.yml shadowed 1.90.0 pin)
AmirK-S Jun 10, 2026
d1b854d
gitignore: block Finder/editor file duplicates from re-sneaking into CI
AmirK-S Jun 10, 2026
9c17bab
CI build.yml: restore TAURI_SIGNING_PRIVATE_KEY (Tauri demands it eve…
AmirK-S Jun 10, 2026
80ce071
v3.1.0 — VAD, mic picker, history re-paste, hands-free indicator, aud…
AmirK-S Jun 11, 2026
3e61c64
release.yml: pass --latest=false on beta publish (gh 422 on prereleas…
AmirK-S Jun 11, 2026
8edb7bc
release.yml: sync publish-job beta regex w/ build job (numeric-only p…
AmirK-S Jun 11, 2026
533a02f
fix Ctrl+Space double-tap stuck state + triple-tap leak
AmirK-S Jun 11, 2026
1312b19
v3.1.1 — fix Ctrl+Space double-tap stuck state
AmirK-S Jun 11, 2026
b76cf49
v3.1.2 — fix 4 v3.1.1-1 field bugs (spam-click stuck + hallucinations…
AmirK-S Jun 13, 2026
a4d45f1
v3.1.3 — fix Whisper glossary hallucination ("Glossary, c'est une phr…
AmirK-S Jun 13, 2026
c9f6f91
v3.1.4 — fix trailing-word cutoff (WASAPI drain) + dictionary words l…
AmirK-S Jun 14, 2026
2ea6ffa
fix Tauri command Send: move tokio::sleep to top of stop_recording
AmirK-S Jun 14, 2026
c1c86b8
v3.1.5 — explicit transcription-language picker + bigger WASAPI drain
AmirK-S Jun 15, 2026
21965d1
v3.1.6 — drop Whisper prompt entirely (every prompt leaks on silence)
AmirK-S Jun 15, 2026
ccf52b7
install.sh: use releases list instead of /releases/latest
Aug 20, 2026
5a73825
fix silent dictation loss: pin injection flags, recover the event tap…
Aug 26, 2026
ececb46
trace: detect process stalls, bracket the pipeline tail, rate-limit t…
Aug 26, 2026
d09ecbd
fix paste verification reading back before the target consumed the ev…
Aug 26, 2026
6caa99e
distinguish a dead microphone from a quiet room, and name the device
Aug 26, 2026
950455e
surface polish outages instead of swallowing them
Aug 27, 2026
6683c10
hold an activity assertion during dictations, so macOS cannot nap the…
Aug 27, 2026
b0622d5
migrate polish off the model Groq shut down on 16 August
Aug 27, 2026
f9bc75c
close the blind spots that could still eat a dictation silently
Aug 27, 2026
0071364
rebuild the event tap when re-arming stops working
Aug 28, 2026
aecff1b
take the keychain off the dictation critical path
Aug 28, 2026
eb93889
remove the paywall: every feature free and uncapped
Aug 28, 2026
c24b1f1
docs: research on why people pay for useless things
Aug 28, 2026
47872df
time each polish attempt: 12s is either a slow model or a silent retry
Aug 28, 2026
f12381e
cache the keychain secret: 9.5s of a 10s dictation was one flag write
Aug 28, 2026
237f751
warm the keychain at launch instead of during the first dictation
Aug 28, 2026
0df8c84
stop the hallucination filter from eating anaphora
Aug 28, 2026
14cac67
TTP Pro: the Companion — sound packs, and the seam that keeps it cosm…
Aug 28, 2026
bf53532
the pill gets a face
Aug 28, 2026
bf31b8c
stop rebuilding a tap that will never come back
Aug 28, 2026
5b645c5
measure the audio that arrived, not the dead air in front of it
Aug 30, 2026
799a929
tell the user the microphone is dead while they can still do somethin…
Aug 31, 2026
ce09c4a
let the maintainer see his own product
Aug 31, 2026
4aa576e
fix the preview button: it was a button inside a button
Aug 31, 2026
663da8d
warmer sound packs: bowl, marimba, felt
Aug 31, 2026
818a9c8
stop bending RadioOption, and get the pack names out of Rust
Aug 31, 2026
3f3a6f8
docs: standing brief for the overhaul programme
Aug 31, 2026
0a32f20
ship a signed build without shipping a release
Aug 31, 2026
1e914f1
tests: stop the licensing suite reaching the real keychain
Aug 31, 2026
e9449e0
trace: get the writer off the hot path, and record the negative cases
Aug 31, 2026
2c4a056
companion: the face was metronomic, and blank when you were looking
Aug 31, 2026
a1f2ba0
landing: stop claiming nothing leaves your machine
Aug 31, 2026
1a22d70
docs, tooling: the standing record, and a trace analyser that runs
Aug 31, 2026
7a6c4db
fix the three defects the trace proved, and get the keychain off the …
Aug 31, 2026
7a4c357
companion: a face worth looking at, and a coat for the animal to wear
Aug 31, 2026
265be48
manual: a second edition, for the specimens that turned out to have eyes
Aug 31, 2026
acc4fb6
docs: wave 4 results and the open debt, named
Aug 31, 2026
7e5d200
settings: stop the store eating fields, and get the changelog out of …
Sep 1, 2026
8a23a0c
audit: measure the constants that caused the incident, and run the te…
Sep 1, 2026
109a580
trace: reconcile the vocabulary, and prove the fixes rather than assu…
Sep 1, 2026
f36b500
landing: rebuild the site around what is actually true
Sep 1, 2026
675b5de
paste: "pasted" now means observed, and the swallowed pastes were in …
Sep 6, 2026
86a2c57
retries: stop ignoring the delay the server asked for
Sep 6, 2026
17c6de0
panic: a default user's crash left no trace at all
Sep 6, 2026
6f8042f
analyser: 17 errors were 1, and the messages were reassuring about th…
Sep 6, 2026
892e2ae
hallucination filter: only delete a loop the known list corroborates
Sep 6, 2026
11aad48
pill: say when the text was sent but nobody saw it arrive
Sep 6, 2026
d0fe8a9
polish: rule 5 was half-working, and the prompt contradicted itself t…
Sep 6, 2026
9543c53
hallucination list: six rules could never fire
Sep 6, 2026
ead6a28
trace: every line now names the process that wrote it
Sep 6, 2026
4a043e5
analyser: split the invariant that let 46 test 429s hide a real 403
Sep 6, 2026
598abf6
regression: two minutes of real speech, dropped as a hallucination
Sep 7, 2026
98eb6e8
hallucination filter: cut the signature, keep the sentence
Sep 7, 2026
a6670b3
recordings: a capture can no longer fill the disk, or stay on it forever
Sep 11, 2026
1819bd8
tray: copy the last transcription from the menu-bar icon
Sep 11, 2026
cdd42d2
onboarding: stop announcing a trial, caps and a paywall that do not e…
Sep 11, 2026
e64c56e
licence: no trial — the Companion is a thank-you, and only a licence …
Sep 11, 2026
ae9828f
companion: remove the face
Sep 14, 2026
befde4d
appearance: remove the coats
Sep 14, 2026
61f20e1
settings: language and appearance follow macOS
Sep 14, 2026
adddd96
pill: hide at rest, and hands-free is a double-tap, not a setting
Sep 14, 2026
4bfc2d6
onboarding: four steps, one key form
Sep 14, 2026
aabc000
chore: remove dead code
Sep 14, 2026
620f84f
settings: fold the rarely-used settings under Advanced
Sep 14, 2026
6bd3f21
docs: architecture matches the simplified app
Sep 14, 2026
48cfd38
docs: research for drag-to-authorize permissions in onboarding
Sep 14, 2026
10fe0e5
dictation: a finishing dictation no longer kills the next one
Sep 14, 2026
82aa6e9
trigger: dictate with any key, modifier or mouse button
Sep 14, 2026
e76a946
pill: one sober indicator, hidden at rest
Sep 14, 2026
630656e
onboarding: three steps, ending with a real dictation
Sep 14, 2026
3215144
paste: wait for the next dictation's key before typing
Sep 14, 2026
c61a651
onboarding: grant a permission by dragging TTP into the list
Sep 14, 2026
5abb0e3
docs: record the simplification wave in the programme status
Sep 14, 2026
81a9868
permission helper: read the frontmost app off the main thread
Sep 14, 2026
0e953cc
copy: the permission and the release note describe what shipped
Sep 14, 2026
adf26ae
trigger: pick the swallow tap back up when Accessibility arrives late
Sep 14, 2026
9e6f5ef
first launch: fix what the first signed build showed
Sep 14, 2026
3a0598e
test: WhatsNew reads the corrected 3.1.7 note
Sep 14, 2026
9d78275
settings: one compact column instead of cards and a sidebar
Sep 14, 2026
134576d
trigger: an optional second shortcut, for an external keyboard
Sep 14, 2026
dbce619
settings: short pages in a sidebar, each with a few titled groups
Sep 14, 2026
3815c6a
screen context: polish reads the text around the cursor, like Superwh…
Sep 14, 2026
5ce2985
screen context: give a just-woken Electron app time to build its tree
Sep 14, 2026
50602a7
screen context: a lone quote mark no longer hides a sentence start
Sep 14, 2026
2b4f1af
screen context: send only terms that sound like a dictated word
Sep 14, 2026
da076f3
screen context: the dictionary's spellings join the terms polish may use
Sep 14, 2026
dbd2e5d
paste verify: a field that grew then emptied received the text
Sep 14, 2026
247fbaf
dock: TTP gets a Dock tile and a ⌘-Tab entry while Settings is open
Sep 15, 2026
d4ffb43
pill: an unverified paste gets the same double tick as an observed one
Sep 15, 2026
29e77c1
pill: a paste that did not land points calmly to History
Sep 15, 2026
a502aea
first launch: no prompt, freeze or white pill before the onboarding asks
Sep 15, 2026
5f10356
v3.2.0 — version bump and What's New note
Sep 15, 2026
f2aab80
v3.2.1 — report a problem, and a crash-report question at setup
Sep 15, 2026
b230742
screen context: say at setup and in Settings what goes to Groq
Sep 15, 2026
8e7521e
speed: stop waits only for the audio that was spoken; shorter paste s…
Sep 15, 2026
a4c373a
polish: a model choice in Settings — Accurate (gpt-oss-120b) or Fast …
Sep 15, 2026
0b5a60f
ci: maintainer builds on polaris/** are macOS only
Sep 15, 2026
c6456a1
screen context: off until chosen, on its own setup page; more headroo…
Sep 15, 2026
fcb7c79
onboarding: say where the voice goes before asking for the Groq key
Sep 15, 2026
118984e
polish: remove the Fast model choice
Sep 15, 2026
777e04c
trace: the frontend and setting changes write to ttp-trace.log
Sep 15, 2026
bafc22b
landing: rebuild the site in the app's own look
Sep 15, 2026
dc21077
pill: say the previous text is waiting while the next one records
Sep 16, 2026
47e222a
v3.2.2 — version bump and What's New note
Sep 16, 2026
eaaed78
cleanup: stop eating numbers, the word "point" and interjections
Sep 16, 2026
78757b5
chore: ignorer builds/ et .dev-certs/
Sep 16, 2026
cb69419
settings: stop freezing on focus; screen context: ignore TTP's own pa…
Sep 21, 2026
dcc8c98
whisper: retry with the app's language when auto-detect answers in an…
Sep 21, 2026
6331df5
whisper: "Auto" means French or English, never a third language
Sep 21, 2026
bc3a66a
settings: say what Auto does within one dictation
Sep 21, 2026
1ddc983
silence gate: count speech windows, keep the audio it drops
Sep 23, 2026
a20c4ea
silence gate: speech window floor 0.008 -> 0.004, trace window spread
Sep 23, 2026
04040a2
updater: stage the download, install and relaunch after two quiet min…
Sep 23, 2026
f922676
v3.2.3 — version bump and What's New note
Sep 24, 2026
21f62cc
updater: the saved beta setting decides the channel
Sep 24, 2026
6e5ba2f
updater: apply the staged update from Rust, awake, after two quiet mi…
Sep 24, 2026
6d82450
updater: after an idle self-update, notify instead of opening What's New
Sep 24, 2026
d44616b
docs: keep internal research and planning notes out of the public repo
Sep 24, 2026
1d3fec7
repo: a README that says what TTP is today, real release notes, issue…
Sep 24, 2026
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
44 changes: 44 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Bug report
description: Something did not work as expected
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time. The fastest route is inside the app: **Settings → Advanced → Report a problem** prepares an e-mail with the technical details and nothing you dictated. You can send that, or describe the problem below.
- type: textarea
id: what
attributes:
label: What happened
description: What you did, what you expected, and what you got instead.
validations:
required: true
- type: input
id: version
attributes:
label: TTP version
description: Shown in the Settings window.
placeholder: "3.2.3"
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
options:
- macOS (Apple Silicon)
- macOS (Intel)
- Windows
validations:
required: true
- type: input
id: mic
attributes:
label: Microphone
description: Built-in, AirPods, USB headset…
- type: textarea
id: report
attributes:
label: Report or logs
description: Anything from the report that helps, if you have it.
render: text
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Security issue
url: https://github.com/AmirK-S/TTP/security/policy
about: Please report vulnerabilities privately, not as a public issue.
- name: Website and setup guide
url: https://ttp.amirks.eu
about: Download, setup and frequently asked questions.
15 changes: 15 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Feature request
description: An idea that would make TTP more useful to you
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What are you trying to do
description: The situation and what gets in the way today.
validations:
required: true
- type: textarea
id: idea
attributes:
label: What would help
374 changes: 351 additions & 23 deletions .github/workflows/build.yml

Large diffs are not rendered by default.

176 changes: 148 additions & 28 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,67 @@ jobs:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

# Detect whether this tag targets the beta channel (e.g. v2.0.0-beta.1).
# Detect whether this tag targets the beta channel.
# Convention (since v3.0.1): numeric-only pre-release suffix
# like `v3.1.0-1`, `v3.1.0-2`. MSI bundler accepts numeric
# pre-releases (since tauri-bundler v1.2.0) but rejects alphanumeric
# ones like `-beta.N` — see ARCHITECTURE.md for the long history.
# The legacy `-beta.N` pattern is still matched for backward
# compatibility with the v3.0.0-beta.1..4 tags already in the wild.
# Outputs:
# is_beta = "true" | "false"
# channel = "beta" | "stable"
# Used downstream to flip the GitHub Release to prerelease and to
# publish a `latest-beta.json` manifest pointing at this build.
- name: Detect beta tag
id: beta-check
shell: bash
run: |
TAG="${{ github.ref_name }}"
if [[ "$TAG" =~ -beta\.[0-9]+$ ]]; then
# Accept only:
# vX.Y.Z — stable
# vX.Y.Z-N — numeric beta (current convention)
# vX.Y.Z-beta.N — legacy beta (preserved for v3.0.0-beta.1..4)
# Reject ANY other pre-release suffix (rc, hotfix, canary, dash with
# nothing after) so a malformed tag can't ship to /releases/latest
# as a stable build. Without this guard, `v3.0.5-rc.1` would have
# been promoted to `--latest` and silently overwritten the stable
# auto-updater pointer.
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "is_beta=false" >> "$GITHUB_OUTPUT"
echo "channel=stable" >> "$GITHUB_OUTPUT"
elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-[0-9]+$ ]] || [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$ ]]; then
echo "is_beta=true" >> "$GITHUB_OUTPUT"
echo "channel=beta" >> "$GITHUB_OUTPUT"
else
echo "is_beta=false" >> "$GITHUB_OUTPUT"
echo "channel=stable" >> "$GITHUB_OUTPUT"
echo "::error::Tag $TAG has an unrecognised format. Use vX.Y.Z (stable) or vX.Y.Z-N (numeric beta)."
exit 1
fi

# Cross-check the tag against the version baked into the source. Without
# this, you can push tag v3.0.5 against a commit where Cargo.toml +
# package.json still say 3.0.4 — the build produces a v3.0.4 binary and
# uploads it to release v3.0.5. The updater manifest takes its version
# from Cargo.toml/tauri.conf.json, so the manifest would mismatch the
# tag and users would re-install the same build on every check.
- name: Verify tag matches source version
shell: bash
run: |
TAG="${{ github.ref_name }}"
# Strip the leading 'v' and any pre-release suffix to get the bare
# vX.Y.Z core for comparison.
TAG_VERSION="${TAG#v}"
TAG_CORE="${TAG_VERSION%%-*}"
PKG_VERSION=$(node -p "require('./package.json').version")
CARGO_VERSION=$(grep -E '^version = ' src-tauri/Cargo.toml | head -1 | sed -E 's/version = "(.*)"/\1/')
if [ "$TAG_CORE" != "$PKG_VERSION" ]; then
echo "::error::Tag $TAG core $TAG_CORE does not match package.json version $PKG_VERSION"
exit 1
fi
if [ "$TAG_CORE" != "$CARGO_VERSION" ]; then
echo "::error::Tag $TAG core $TAG_CORE does not match Cargo.toml version $CARGO_VERSION"
exit 1
fi
echo "Tag $TAG matches source version $TAG_CORE (package.json + Cargo.toml)."

# On Windows + beta tags, skip the MSI bundler. MSI rejects non-numeric
# SemVer pre-release identifiers (e.g. -beta.1) — see tauri-apps/tauri
# issue. NSIS .exe still ships and is sufficient for beta-channel users.
Expand All @@ -74,24 +116,29 @@ jobs:
- name: Setup Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @ 2025-12-16
with:
# Pin the toolchain explicitly. Without a `toolchain:` input, the
# action installs whatever is the latest stable rustc at run time,
# so two release builds of the same git SHA can produce different
# binaries depending on when CI ran. Bump this deliberately via PR.
#
# 2026-06-10: bumped from 1.85.0 to 1.90.0 — transitive deps (time,
# time-core, time-macros) require ≥1.88.0. 1.90.0 is the latest
# stable at pin time and gives margin for future minor dep bumps.
toolchain: '1.90.0'
targets: ${{ matrix.target }}
components: rustfmt, clippy

- name: Rust cache
uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2
with:
workspaces: src-tauri -> target

- name: Install opus dependencies (macOS)
if: matrix.platform == 'macos-latest'
run: brew install cmake opus pkg-config
env:
HOMEBREW_NO_AUTO_UPDATE: 1

- name: Set cross-compile env for x86_64 macOS
if: matrix.target == 'x86_64-apple-darwin'
run: |
echo "OPUS_NO_PKG_CONFIG=1" >> $GITHUB_ENV
echo "CMAKE_POLICY_VERSION_MINIMUM=3.5" >> $GITHUB_ENV
# `cmake`, `opus`, `pkg-config`, and the `OPUS_NO_PKG_CONFIG` /
# `CMAKE_POLICY_VERSION_MINIMUM` env vars used to be installed here to
# support an opus cross-compile that no longer exists. Cargo.toml only
# enables `rodio` with `features = ["wav"]`, cpal needs neither cmake
# nor opus, and `grep opus Cargo.lock` returns zero matches. The
# ~30-60s of brew time per macOS run that this saves is non-trivial.

- name: Install frontend dependencies
run: npm ci
Expand All @@ -104,7 +151,19 @@ jobs:
working-directory: src-tauri
run: cargo test --lib

# Windows certificate import
# Windows certificate import + thumbprint wire-through.
#
# Before this step, the certificate was imported into the runner's
# CurrentUser cert store but tauri.conf.json carried
# `"certificateThumbprint": null`, so the Tauri Windows bundler never
# invoked signtool. The NSIS .exe and MSI shipped UNSIGNED — every
# SmartScreen prompt showed "Unknown publisher" and the certificate
# was effectively unused.
#
# Now: extract the thumbprint from the imported .pfx and rewrite the
# null field in tauri.conf.json before the build runs. We rewrite the
# file (rather than passing via env) because tauri-action does not
# forward the env var into the bundler config at build time.
- name: Import Windows Certificate
if: matrix.platform == 'windows-latest' && env.WINDOWS_CERTIFICATE != ''
env:
Expand All @@ -114,7 +173,15 @@ jobs:
New-Item -ItemType directory -Path certificate
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
$pwd = ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password $pwd
# Read the thumbprint back from the .pfx and inject it into the
# Tauri config so the bundler actually invokes signtool.
$thumb = (Get-PfxCertificate -FilePath certificate/certificate.pfx).Thumbprint
Write-Host "Windows certificate thumbprint: $thumb"
$conf = Get-Content src-tauri/tauri.conf.json -Raw
$conf = $conf -replace '"certificateThumbprint":\s*null', "`"certificateThumbprint`": `"$thumb`""
Set-Content -Path src-tauri/tauri.conf.json -Value $conf -NoNewline

# Apple Developer ID certificate import — MANDATORY for release tags.
# Releases must be signed and notarized; we fail loudly if the secret is
Expand Down Expand Up @@ -288,10 +355,23 @@ jobs:
|| sentry-cli debug-files upload \
--include-sources \
"src-tauri/target/release/"
# TODO: JS source map upload to be added later. With @sentry/react
# now wired on the frontend, JS stack traces from the Vite bundle
# will arrive minified until we run `sentry-cli sourcemaps upload`
# against `dist/` on the same release tag (package.json version).

# JS sourcemaps: vite.config.ts emits .map sidecars alongside the
# bundled JS via `build.sourcemap: "hidden"` (no sourceMappingURL
# comment, so end-user devtools don't see them). We upload the maps
# to Sentry keyed by the package.json version so server-side
# symbolication resolves minified frames in @sentry/react events.
#
# Only run once per release (macOS arm64) to avoid uploading the
# same artifacts three times across the matrix.
if [[ "${{ matrix.target }}" == "aarch64-apple-darwin" ]] && [ -d dist ]; then
REL_VERSION=$(node -p "require('./package.json').version")
echo "Uploading JS sourcemaps for release $REL_VERSION"
sentry-cli sourcemaps inject ./dist || true
sentry-cli sourcemaps upload \
--release "$REL_VERSION" \
./dist || true
fi

# Upload a stable-named alias so /releases/latest/download/<stable> works
# without the consumer having to know the version. Versioned originals
Expand Down Expand Up @@ -374,11 +454,19 @@ jobs:
id: beta-check
shell: bash
run: |
# MUST stay in sync with the regex in the build job at the top of
# this file. Without that, a numeric-pre-release tag like
# `v3.1.0-1` would be classified differently between the two jobs
# — the build job builds a beta but the publish job tries to mark
# the release as latest, hitting HTTP 422 from GitHub.
TAG="${{ github.ref_name }}"
if [[ "$TAG" =~ -beta\.[0-9]+$ ]]; then
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "is_beta=false" >> "$GITHUB_OUTPUT"
elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-[0-9]+$ ]] || [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$ ]]; then
echo "is_beta=true" >> "$GITHUB_OUTPUT"
else
echo "is_beta=false" >> "$GITHUB_OUTPUT"
echo "::error::Tag $TAG has an unrecognised format. Use vX.Y.Z (stable) or vX.Y.Z-N (numeric beta)."
exit 1
fi

- name: Publish latest-beta.json (stable tag)
Expand Down Expand Up @@ -434,6 +522,33 @@ jobs:
gh release upload "$STABLE_TAG" _channel/latest-beta.json \
--clobber --repo "${{ github.repository }}"

# Release notes. Every release used to say only "See the assets below
# for downloadable installers." The app already carries a What's New
# note per version (whatsNew.notes in en.json); the release page now
# says the same thing. Stable tags only, and never fatal: a version
# without a note keeps the default body.
- name: Checkout release-notes source
if: steps.beta-check.outputs.is_beta == 'false'
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
sparse-checkout: |
scripts/release_notes.py
src/i18n/locales/en.json
sparse-checkout-cone-mode: false

- name: Write release notes from the What's New note
if: steps.beta-check.outputs.is_beta == 'false'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
TAG="${{ github.ref_name }}"
if python3 scripts/release_notes.py "${TAG#v}" > notes.md; then
gh release edit "$TAG" --notes-file notes.md --repo "${{ github.repository }}"
else
echo "::warning::No What's New note for ${TAG#v}; keeping the default release body."
fi

# Flip the GitHub Release from draft to published. The release-matrix job
# creates it as a draft on purpose (so tauri-action's per-platform retry
# attempts are idempotent against the same draft), and historically there
Expand All @@ -449,10 +564,15 @@ jobs:
run: |
set -e
if [ "${{ steps.beta-check.outputs.is_beta }}" = "true" ]; then
# Beta tag: publish but stay prerelease and do NOT mark as latest,
# so stable-channel users keep pointing at the most recent stable.
# Beta tag: publish but stay prerelease and EXPLICITLY do NOT mark
# as latest, so stable-channel users keep pointing at the most
# recent stable. We pass `--latest=false` explicitly because
# without it gh inherits whatever flag the release was created
# with (tauri-action's default leaves it ambiguous), and GitHub
# then rejects the prerelease flag with HTTP 422
# ("Latest release cannot be draft or prerelease.").
gh release edit "${{ github.ref_name }}" \
--draft=false --prerelease \
--draft=false --prerelease --latest=false \
--repo "${{ github.repository }}"
else
# Stable tag: publish and explicitly mark as latest so
Expand Down
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,32 @@ dist-ssr
.planning/
.claude/
assets-export/

# Finder / editor auto-duplicates. macOS Finder creates "<name> 2.<ext>"
# when paste-copying into a directory that already has the same name;
# editor crashes can leave "<name>.<ext>.tmp.<pid>.<hex>" files behind.
# These have shadowed a real workflow file in CI before (a stale
# "build 2.yml" with rustc 1.85.0 ran instead of build.yml's 1.90.0 pin).
**/* 2.yml
**/* 2.rs
**/* 2.ts
**/* 2.tsx
**/*.yml.tmp.*
**/*.rs.tmp.*

# Certificats de signature (jamais versionnes)
.dev-certs/

# Builds telecharges (dmg, exe)
builds/

# Local secrets (fal.ai key for the promo video, etc.)
.env
.env.*

# Promo video working files (Remotion project, voice takes, renders)
promo/

# Python bytecode
__pycache__/
*.pyc
Loading
Loading