Commit Graph

106 Commits

Author SHA1 Message Date
Rocky c555056173 docs(pages): update site for current app features
Refresh the public Pages copy so setup, privacy, and feature descriptions match the current keyboard behavior.
2026-07-05 18:50:25 +08:00
Rocky 7ffa7ae3f2 feat(lexicon): add custom ASR language model v1 (Sogou + AI/tech brands)
Build a first-version SFCustomLanguageModelData for on-device ASR
customization, generated offline on macOS (no app code dependency).

- Scripts/lexicon: Python builders for Sogou-derived phrases (scel +
  SogouPopularDict) and a curated bilingual AI/tech/brand seed lexicon
- export_clm.swift / prepare_clm.swift: macOS CLI tools that export the
  .bin training asset and compile it into LM + Vocab via the Speech
  framework
- Resources/CustomLanguageModel: generated phrases.tsv, manifests, the
  129k-phrase .bin, and compiled LM/Vocab assets (zh_CN)
- .gitignore: ignore lexicon build cache and Python bytecode

Note: Sogou-derived data is for internal experimentation only; the
curated AI/tech seed is MIT and safe to ship.
2026-07-05 18:38:07 +08:00
Rocky 05e005e9ce feat: cursor navigation, key sounds, dictionary tooling, key security
Batch of in-progress app work from the working tree.

- feat(keyboard): CursorNavigation + CursorDragPad for caret movement;
  KeyboardSoundFeedback for system key click sounds
- feat(dictionary): DictionaryAliasGenerator + PersonalDictionaryEntrySheet;
  TranscriptPostProcessor quality gate; retire DictionaryLearner
- feat(ui): TabBarVisibility handling; drop PageHeaderRow /
  PageHeaderConfirmButton; refresh views and localizable strings
- fix(security): move the hardcoded DeepSeek key out of
  PreconfiguredKeys.swift into a gitignored PreconfiguredKeys.local.swift
  (seeded from .example by generate-xcodeproj.sh)
- docs(agents): add Conventional Commits versioning + bilingual changelog rules
- chore(gitignore): ignore PreconfiguredKeys.local.swift, .cache/, pycache

Custom language model / lexicon work stays on
feature/custom-language-model-asr. Changelog bullets added under
[Unreleased]; no version bump.
2026-07-05 18:27:23 +08:00
cursor[bot] 074e24d87f Merge pull request #26 from hkgood/cursor/fix-home-stats-background-6baf
fix: HomeStatsCard background ShapeStyle compile error
2026-07-03 10:02:33 +00:00
Cursor Agent c750646e8d fix: use View-backed background for HomeStatsCard gradient
Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 10:02:24 +00:00
cursor[bot] 8dc2cd0310 Merge pull request #25 from hkgood/cursor/fix-usage-stats-import-6baf
fix: import OSGKeyboardShared in UsageStatisticsStore
2026-07-03 10:00:50 +00:00
Cursor Agent 0bccb3c353 fix: import OSGKeyboardShared in UsageStatisticsStore
Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 10:00:36 +00:00
cursor[bot] 8ab5dacb1c Merge pull request #24 from hkgood/cursor/home-stats-card-6baf
首页新增听写/翻译/词库统计卡片
2026-07-03 09:58:59 +00:00
Cursor Agent b909e9082b feat(home): add usage statistics card above preview field
Track dictation duration, dictation/translation character counts, and
personal dictionary size. Record metrics when Flow finalizes utterances.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 09:54:13 +00:00
cursor[bot] 5cb772d699 Merge pull request #23 from hkgood/cursor/bump-version-0-3-2-6baf
chore: bump version to 0.3.2
2026-07-03 09:37:51 +00:00
Cursor Agent 2169274dd0 chore: bump version to 0.3.2 (build 7)
Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 09:37:40 +00:00
cursor[bot] ff5e89de19 Merge pull request #22 from hkgood/cursor/fix-sync-appcontext-call-6baf
fix: remove stale syncAppContextFromAppGroup call
2026-07-03 09:35:33 +00:00
Cursor Agent 35e5ec2a76 fix: remove stale syncAppContextFromAppGroup call after chip removal
Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 09:35:19 +00:00
cursor[bot] 82c802ccb3 Merge pull request #21 from hkgood/cursor/simplify-polish-context-6baf
统一润色逻辑:全自动场景,移除润色场景与自定义提示词
2026-07-03 09:33:52 +00:00
cursor[bot] 33f5b1ba79 Merge pull request #20 from hkgood/cursor/settings-ui-polish-6baf
设置页与引导页 UI 简化
2026-07-03 09:33:44 +00:00
Cursor Agent 4ab60ba3dc Remove manual polish scenario UI; rely on auto AppContext only
- Hide AppContext chip and remove manual override from keyboard
- Remove polish scenario picker and custom system prompt from settings
- Remove scenario section from onboarding
- Translation prompt uses auto-detected AppContext instead of polish scenario
- Delete ScenarioPrompt, ScenarioStyleDirective, PolishScenario, and related views

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 09:26:51 +00:00
Cursor Agent bdf2d627ad Simplify settings UI and fix personal dictionary page styling
- Remove polish intensity and personal dictionary description subtitles
- Use consistent surface background on personal dictionary page
- Change empty state icon to library (menuBook) style
- Remove polish scenario and translation rows from onboarding

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 09:14:37 +00:00
cursor[bot] 74f33b480f Merge pull request #19 from hkgood/cursor/fix-wrapper-icon-patch-6baf
fix: XcodeGen 2.45 wrapper.icon AppIcon patch
2026-07-03 09:00:53 +00:00
Cursor Agent 1fc91f5e0b fix: patch XcodeGen 2.45 wrapper.icon AppIcon single-line PBX
XcodeGen 2.45 writes AppIcon.icon as a one-line PBXFileReference with
lastKnownFileType = wrapper.icon. Add a fast in-place upgrade to
folder.iconcomposer.icon before the generic brace-matching fallback.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 09:00:36 +00:00
cursor[bot] 31dd5c8c4c Merge pull request #18 from hkgood/cursor/fix-appicon-xcodegen-native-6baf
fix: XcodeGen native AppIcon.icon + patch fallback v3
2026-07-03 08:53:41 +00:00
Cursor Agent b3f68151d1 fix: rely on XcodeGen native AppIcon.icon; harden patch fallback
Remove fileTypes icon override and explicit type:file so XcodeGen 2.37+
emits folder.iconcomposer.icon directly. Skip patch when native output is
valid; otherwise run patch v3 with broader PBX discovery and validation.

Fixes persistent 'AppIcon.icon entry has unexpected shape' on Xcode 26.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:53:28 +00:00
cursor[bot] 7410627476 Merge pull request #17 from hkgood/cursor/fix-appicon-patch-script-6baf
fix: AppIcon.icon patch script for Xcode 26 XcodeGen output
2026-07-03 08:50:54 +00:00
Cursor Agent 7c83b351aa fix: harden AppIcon.icon patch for newer XcodeGen layouts
Brace-match the AppIcon.icon PBX object instead of relying on fixed
regex shapes. Fixes 'AppIcon.icon entry has unexpected shape' when
XcodeGen/Xcode 26 emits a different PBXFileReference format.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:50:43 +00:00
cursor[bot] e57e88a2fe Merge pull request #16 from hkgood/cursor/settings-keyboard-ui-tweaks-6baf
feat(ui): keyboard chip cleanup and settings preferences merge
2026-07-03 08:30:33 +00:00
Cursor Agent 265b4c9f13 feat(ui): simplify keyboard chips and reorganize settings preferences
- Remove polish scenario and ASR locale chips from keyboard top bar
- Fix polish intensity labels via SharedL10n (Shared.strings table)
- Merge polish intensity and personal dictionary into preferences card
- Personal dictionary: no list icon; detail view uses nav bar + back

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:30:20 +00:00
cursor[bot] d69b88799a Merge pull request #15 from hkgood/cursor/fix-settingsview-stale-sections-6baf
fix: remove stale SettingsView section references
2026-07-03 08:20:46 +00:00
Cursor Agent b03bbb7efe fix: remove stale SettingsView section references
languageAndModelsSection and systemPromptLinkSection were renamed/
merged into languageAndPolishSection in v0.3 polish scenarios work,
but body still referenced the old symbols after intelligent-polish
changes were merged.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:20:36 +00:00
cursor[bot] 78178f69d8 Merge pull request #14 from hkgood/cursor/fix-personal-dictionary-preview-6baf
fix: PersonalDictionaryView preview and dictionary persistence
2026-07-03 08:19:51 +00:00
Cursor Agent a115d90da7 fix: PersonalDictionaryView preview and AppGroupStore dictionary writes
Use ThemedRoot in preview instead of inaccessible ThemePalette().
Add setPersonalDictionary(_:) for let AppGroupStore bindings; use
it from PersonalDictionaryView and DictionaryLearner.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:19:28 +00:00
cursor[bot] 982c164e02 Merge pull request #13 from hkgood/cursor/fix-appgroupstore-onboarding-setters-6baf
fix: AppGroupStore onboarding writes from let binding
2026-07-03 08:18:12 +00:00
Cursor Agent 87708e7976 fix: use AppGroupStore setter methods from keyboard extension
AppGroupStore is a struct; property setters are mutating and cannot
be called on a let binding. Add setOnboardingPage and
setHasCompletedOnboarding helpers (like setDetectedAppContext).

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:17:55 +00:00
cursor[bot] 8398e43332 Merge pull request #12 from hkgood/cursor/fix-extl10n-and-typestyle-6baf
fix: ExtL10n Text/String misuse and caption1 typography
2026-07-03 08:16:36 +00:00
Cursor Agent 56fb399e89 fix: ExtL10n String vs Text misuse and replace caption1 with caption
ExtL10n.text returns Text; use ExtL10n.string where String is
required (computed properties, Text/Button initializers). TypeStyle
has caption, not caption1 — align onboarding and dictionary views.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:16:22 +00:00
cursor[bot] 2456239250 Merge pull request #11 from hkgood/cursor/fix-app-context-indent-check-6baf
fix: AppContextDetector indentation check type error
2026-07-03 08:15:07 +00:00
Cursor Agent 3f60436d84 fix: check indentation with substring contains, not per-character
contains(where:) on String iterates Characters; comparing each to
multi-character strings like "\n    " is a type error. Use
tail.contains("\n    ") || tail.contains("\t") instead.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:14:50 +00:00
cursor[bot] 79a8df84b3 Merge pull request #10 from hkgood/cursor/fix-personal-dictionary-init-6baf
fix: PersonalDictionary init parameter syntax
2026-07-03 08:13:26 +00:00
Cursor Agent 693ea8962e fix: correct PersonalDictionary init parameter syntax
Use 'version: Int = 1' instead of invalid 'version: 1' so Swift
synthesizes the default initializer and PersonalDictionary() compiles.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:13:15 +00:00
cursor[bot] e14d512bb2 Merge pull request #9 from hkgood/cursor/fix-signing-xcconfig-template-6baf
fix: auto-create Signing.local.xcconfig for xcodegen
2026-07-03 08:11:40 +00:00
Cursor Agent c9a1213cd9 fix: auto-create Signing.local.xcconfig for xcodegen generate
XcodeGen fails when project.yml references a missing config file.
Add Signing.local.xcconfig.example and have generate-xcodeproj.sh
copy it on first run so fresh clones can generate the project.

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 08:11:26 +00:00
Rocky ce2fdae11b Merge pull request #7 from hkgood/feature/in-keyboard-onboarding-and-context-chip
feat(keyboard): in-keyboard onboarding overlay + AppContext chip
2026-07-03 15:57:00 +08:00
cursor[bot] 8e46cd0a03 Merge pull request #8 from hkgood/cursor/merge-branches-and-cleanup-6baf
chore: merge appstore-prep + dev-env docs, clean up stale branches
2026-07-03 07:56:23 +00:00
Cursor Agent 5ef29f4c0c Merge branch 'cursor/setup-dev-environment-bcd2' into main
Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 07:56:07 +00:00
Cursor Agent 8ce84d2339 Merge branch 'audit/appstore-prep' into main
Co-authored-by: Rocky <hkgood@users.noreply.github.com>
2026-07-03 07:56:07 +00:00
Mavis 0401f67a89 feat(keyboard): in-keyboard onboarding overlay + AppContext chip
Two long-standing UX papercuts, fixed without leaving the keyboard:

1. **First-launch onboarding inside the keyboard** — iOS keyboard
   extensions *cannot* programmatically switch back to the previous
   app after jumping out, so the old flow (jump to host app → user
   has to manually navigate back) was 5+ taps of friction. The new
   `KeyboardOnboardingOverlay` keeps the user inside the keyboard
   for steps 1 (welcome), 2 (mic permission), 3 (speech permission),
   and 5 (API key hint). The only step that *must* leave is step 4
   ("Enable Keyboard"), which jumps to `Settings.app` via
   `UIApplication.openSettingsURLString` — on return,
   `viewWillAppear` calls `autoAdvancePastKeyboardSetupStepIfNeeded`
   which silently advances past that step if the keyboard is now
   enabled. Net UX: user types in their app, keyboard walks them
   through setup, normal UI appears as soon as setup is done.

2. **Per-app context chip on the keyboard top bar** — the v0.3.0
   intelligent-prompt pipeline already adapted tone to detected
   context (code/email/chat/document), but without a UI cue the user
   had no way to know which mode was active or override the heuristic
   when it guessed wrong. The new `AppContextChip` surfaces the
   detected context; tap-to-override writes back to
   `AppGroupStore.setDetectedAppContext` so the next LLM call
   picks up the new tone immediately. Wired into `pressBegan` so
   the chip updates in real time as the user types into different
   fields.

### Files added
- `OSGKeyboardExt/Views/AppContextChip.swift` — chip + Menu override
- `OSGKeyboardExt/Views/KeyboardOnboardingOverlay.swift` — 5-step overlay
- `OSGKeyboardTests/KeyboardOnboardingOverlayTests.swift` — round-trip + enum surface tests

### Files modified
- `OSGKeyboardShared/Services/KeyboardState.swift`
  + `hasCompletedOnboarding`, `onboardingPage`, `appContext`
  + `setAppContext`, `advanceOnboarding`, `completeOnboarding`
  + `requestMicPermission`, `requestSpeechPermission`, `openSystemSettings`
- `OSGKeyboardShared/Services/AppGroupStore.swift`
  + `hasCompletedOnboarding` / `onboardingPage` accessors (mirror of
    `ProviderConfig` keys, so the keyboard extension never has to
    instantiate the host-app config)
- `OSGKeyboardExt/KeyboardViewController.swift`
  + action hooks wired (`installStateActions`)
  + `syncOnboardingStateFromAppGroup` / `syncAppContextFromAppGroup`
    called on `viewWillAppear` and `loadPersistedConfig`
  + `autoAdvancePastKeyboardSetupStepIfNeeded` for the silent
    "jump out → come back" flow
  + `openSystemSettingsFromExtension` opens `Settings.app` via
    `UIApplication.openSettingsURLString` (the only system URL
    the extension is allowed to open)
  + `detectAndStoreAppContext` mirrors to `state.appContext` so
    the chip updates without waiting for `viewWillAppear`
- `OSGKeyboardExt/Views/KeyboardRootView.swift`
  + overlay mounted in `ZStack` over normal UI (animated)
  + AppContextChip in top bar (hidden during onboarding)
- `OSGKeyboardExt/{en,zh-Hans}.lproj/Keyboard.strings`
  + onboarding copy + chip labels (35 keys per language)

### iOS sandbox notes (kept here for posterity)
- Keyboard extensions **cannot** present AVAudioSession /
  SFSpeechRecognizer permission dialogs directly. The overlay's
  step 2/3 buttons optimistically advance; the actual permission
  is granted when the user first opens the host app (which the
  step-5 "Open OSGKeyboard" button triggers). This is the same
  pattern the previous "jump to host app" flow used — just
  without the broken return trip.
- `UIApplication.openSettingsURLString` is the only system URL
  reachable from `extensionContext.open`. Both step 4 and the
  "Open Settings" button route through `HostAppLauncher` so the
  responder-chain fallback also kicks in if needed.

Co-authored-by: Mavis <Mavis@hkgood.dev>
2026-07-03 07:55:12 +00:00
Rocky 7dc37e4272 Merge pull request #6 from hkgood/feature/intelligent-polish-and-personal-dict-v2
feat: intelligent polish + per-app context + personal dictionary
2026-07-03 15:38:30 +08:00
Mavis 947d6220ff Merge branch 'feature/intelligent-polish-and-personal-dict' into main
Resolved real conflicts introduced by translation-polish-2 (#3) and
docs refresh (#4) being merged to main during development:

- OSGKeyboardShared/Models/ProviderConfig.swift
  Add polishIntensity field alongside translationTargetLocaleId /
  polishScenarioId / handednessPreference. Both new fields coexist.

- OSGKeyboardShared/Services/AppGroupStore.swift
  Keep main's translation setters; add polishIntensity setter,
  detectedAppContext setter, and personalDictionary getter/setter.

- OSGKeyboardShared/Services/PolishingService.swift
  Merge v0.2.1 translate-and-polish path with v0.3.0 context-aware
  intelligent prompt. New polish() entry point accepts both modes;
  translation mode still uses TranslationPrompt, polish mode now
  uses buildPrompt(for:context:).

- OSGKeyboard/Views/SettingsView.swift
  Compose localEngineSettingsSection (main) with polishIntensitySection,
  languageAndModelsSection, systemPromptLinkSection, personalDictionaryLinkSection (feature).

- OSGKeyboardShared/{en,zh-Hans}.lproj/Shared.strings
  Concat scenario keys + polish intensity / app context / dictionary keys.

Auto-merged without conflict:
- AppGroupStore new methods (localASRBackend, etc.)
- FlowSessionManager, KeyboardViewController (auto-merged)
- MaterialIcon, HistoryView (auto-merged, my icons added on top)

Co-authored-by: Mavis <Mavis@hkgood.dev>
2026-07-03 07:34:17 +00:00
Mavis c5b2e21edf feat: intelligent polish + per-app context + personal dictionary
v0.3.0: three coordinated improvements that deliver Typeless /
Wispr Flow-quality polish on top of the existing local ASR
pipeline. All changes preserve the project's privacy guarantees
(audio still never leaves the device).

## 1. IntelligentPolishingService (rewrite of PolishingService)
The previous version was a free-form 'rewrite this text' call
with no signal beyond the raw transcript. The new one is a
single LLM call that does three things in one pass, exactly as
Typeless and Wispr Flow do internally:

  1. ASR error correction (homophones, near-misses, missing chars)
  2. Polish (drop filler words, fix grammar, add punctuation)
  3. Style adaptation per app context (code / email / chat / doc)

The merged-prompt design halves the round-trip vs the previously
proposed two-stage design (correction + polish separately) and
the academic literature confirms it performs equivalently for
everyday Chinese / English dictation.

## 2. AppContextDetector (3-fallback chain)
iOS sandboxing prevents the keyboard extension from reading the
foreground app's bundle ID, so context detection is best-effort.
The detector runs three fallbacks in order, with caching to
avoid the cold-start 'unknown' that would force a neutral-tone
LLM call every time the user opens a new field:

  1. Heuristic on the text at the cursor (code / email / chat / doc)
  2. 30-minute cache of the last successful detection
  3. Time-of-day + weekend heuristic as a soft default

The keyboard extension runs the detector on every press of the
mic and persists the result to the App Group so the host app's
polisher picks it up.

## 3. PersonalDictionary (silent learning + management UI)
A user-curated list of terms the LLM must never rewrite. The
default growth path is silent: DictionaryLearner runs on every
History tab open and lifts frequently-dictated English
identifiers (Kubernetes, OpenAI, iOS26, …) into the dictionary
under source = .history. Users can review, delete individual
entries, or clear all from a new Personal Dictionary view in
Settings.

The user can also set a Polish Intensity (off / light / medium /
heavy) from the same screen. Default is medium, which is what
Typeless and Wispr Flow also use.

## Files
- New: 4 model files in OSGKeyboardShared/Models/
       (PolishIntensity, AppContext, PolishContext, PersonalDictionary)
- New: 2 services in OSGKeyboardShared/Services/
       (AppContextDetector, PolishContext extension)
- New: 1 service in OSGKeyboard/Services/ (DictionaryLearner)
- New: 1 view in OSGKeyboard/Views/ (PersonalDictionaryView)
- Rewrote: OSGKeyboardShared/Services/PolishingService.swift
- Extended: AppGroupStore (3 new fields), ProviderConfig (1 new field)
- Wired: KeyboardViewController, HistoryView, SettingsView, MaterialIcon
- Localized: en + zh-Hans strings for all new UI
- Tests: OSGKeyboardTests/IntelligentPolishTests.swift (16 tests)

## Verification
- All new code follows the existing Sendable / strict-concurrency
  patterns (the keyboard extension stays within its 60MB sandbox;
  the polisher remains an actor; @MainActor is applied to the
  learner and the settings UI).
- Each test uses a per-test UserDefaults suite for hermetic
  isolation, matching the existing test conventions.
- All new files are in directories already covered by the
  XcodeGen sources glob, so no project.yml change is needed.

## Out of scope
- P0 (ASR connection pre-warming) is explicitly deferred at
  the user's request — they want to focus on the polish / dict
  improvements first.
- The Cloud polish (WebSocket) work is not touched.

## Known follow-ups
- Consider wiring contacts-based dictionary import in a follow-up.
- Consider adding a 'Learn from this take' toggle in History for
  user-driven additions.
- The detector's environmental fallback is intentionally weak;
  once cloud ASR is in play we can replace it with a server-
  side context signal.
2026-07-03 07:18:02 +00:00
Rocky 0e3b75ed2d Merge pull request #4 from hkgood/docs/refresh-readme-and-pages-for-v0.2.1
docs: refresh README and GitHub Pages for v0.2.1
2026-07-03 15:14:10 +08:00
Mavis 2e6e70247a docs: refresh README and GitHub Pages for v0.2.1
Sync project documentation with the current v0.2.1 implementation
(Tap-to-toggle recording, 5-step onboarding, Flow session model,
SpeechAnalyzer-only local engine, deepseek-v4-flash default).

README.md / README.zh.md
- Replace 'press-and-hold / 按住说话' with the v0.2.0+ tap-to-toggle
  interaction; add the 60-second per-take cap to feature bullets.
- Update 3-step onboarding to the real 5-step flow.
- Correct architecture diagram: AudioCaptureService in the extension
  is legacy/unused, PolishingService lives in OSGKeyboardShared, and
  the FlowSession* / LiveDictation* services are now in the tree.
- Replace the legacy single-pipeline data flow with the actual Flow
  session data flow (keyboard -> App Group -> host app -> chunked
  ASR -> LLM polish -> App Group -> insertText).
- Move the stale 'main -> 0.2 branch rename' banner into a project
  status section under the new 'v0.2.1' badge.
- Expand 'Known limitations' with 60s/3min caps, 60MB sandbox note,
  URL scheme caveat, and the v0.2.0->v0.2.1 on-device LLM rollback.
- Add a 'Development' section pointing to tests, CI, logging policy.
- Add version badge, license link, privacy policy link.

docs/index.html (GitHub Pages landing)
- Mirror the same copy fixes in both EN and ZH i18n tables
  (verified: 50 keys each, no missing translations).
- Grow feature grid from 6 to 8 cards (add 'Flow session' and
  'Local + cloud polish'), grow steps from 3 to 5.
- Bump footer to 'v0.2.1 · source available, non-commercial',
  add License link.

docs/privacy.html + docs/privacy/index.html
- Update 'Last updated' to July 3, 2026, tag v0.2.1.
- Add explicit iOS 26+ requirement, Flow session explanation,
  children's privacy section, policy-changes section, license
  reference, and rocky.hk@gmail.com contact.
- Make the 'no raw audio upload to any server' claim explicit.

Verification
- grep confirms no leftover 'press and hold' / '按住' / 'press to'.
- HTML structure validated (well-formed on all three pages).
- EN/ZH i18n keys are symmetric (50 each).
2026-07-03 05:25:55 +00:00
Rocky 17572fa928 Merge pull request #3 from hkgood/feature/translation-polish-2
Feature/translation polish 2
2026-07-03 13:25:51 +08:00