Rework the marketing site with a cleaner, more restrained layout, replace emoji with Material Symbols monochrome icons, and add full SEO (Open Graph, Twitter cards, JSON-LD, canonical, robots.txt, sitemap.xml).
Rework the marketing site with a cleaner, more restrained layout, replace emoji with Material Symbols monochrome icons, and add full SEO (Open Graph, Twitter cards, JSON-LD, canonical, robots.txt, sitemap.xml).
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.
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.
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>
- 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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>