Skip to content

Commit 2ca9be9

Browse files
committed
docs(specs): v1.5.0 spec refresh — autocorrect, sanitization, clipboard, settings, setup
All file:line citations re-verified against current source with rg. - autocorrect-spec (v1.5.0): entry point WordPredictor.kt:1855; guard layer (AutocorrectContextGuard NON_PROSE_CHARS :22, morphology :1946, possessive AC-4 :1951, elongation :1889); Damerau transposition (:1831, TRANSPOSITION_PENALTY :153); same-length sub cap (MAX_SAME_LENGTH_SUBSTITUTIONS :73); rule-based tiebreak verbatim (:2163-2185) replaces the removed ALIAS_SCORE_BONUS four-tier model; FrequencyFloor slider 100-2000 -> dictionary-scaled (:40-59, use :2023); disabled-word exclusion :2062; custom-word floor exemption :2202; #151 URI-field token replacement (SuggestionHandler.kt:491) - url-sanitization-spec (v1.5.0): current URL_REGEX (quotes allowed inside, :47-49); rules compiled as anchored regex (:52-62, was literal-key matching); verbatim stripQueryParams (:108-130); system clipboard write-back (systemClipboardRewrite :34-35, service :304-308, :349-362); 207 providers; reddit_embed_host_url chaining; mid-session reload via reloadSanitizationSettings (Config.kt:965) - clipboard-history-spec (v1.4.0): new Duplicate Handling section — move-to-top on re-copy (#108, ClipboardDatabase.kt:186-199), pinned/ todo duplicates still rejected - haptics-spec (v1.5.0): rewritten from illustrative pseudo-code to the real VibratorCompat dispatch (:60-97), per-event constants, #154 one-time migration (vibrate_custom_migration_v1, Config.kt:1480), real pref keys/defaults (swipe-complete now true, Config.kt:84) - settings-system-architecture-spec (v1.5.0): decomposed ui/settings/ layer (17 section composables), settings search subsystem (SettingsSearch.kt, generated index, gate-aware scroll), corrected Defaults snippet values (height 27/40, thresholds, clipboard limit) - appearance-spec (v1.5.0): Secondary Label Size #133 (secondary_label_size_scale, Keyboard2View.kt:1275) - setup-spec (v1.5.0): Defaults table re-cited (swipe-complete true, height 27, gesture %-units); launcher Enable/Select flow UT-6 (LauncherActivity.kt:131-164); language packs are imported via SAF, never downloaded (no INTERNET permission) — replaced the fictional "download from GitHub releases" flow; packs install to files/langpacks/ - short-swipes-spec (v1.5.0): config table corrected — max distance is the 141% short/long boundary (range 50-200, was "65% / 40-80"), key short_gestures_enabled, PercentOfKey value class — Fable 5
1 parent ba9ac45 commit 2ca9be9

8 files changed

Lines changed: 535 additions & 468 deletions

File tree

docs/wiki/specs/clipboard/clipboard-history-spec.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Clipboard History - Technical Specification
33
user_guide: ../../clipboard/clipboard-history.md
44
status: implemented
5-
version: v1.3.0
5+
version: v1.4.0
66
schema_version: v4
77
---
88

@@ -172,6 +172,28 @@ private fun addCurrentClip() {
172172
}
173173
```
174174

175+
### Duplicate Handling — Move to Top (#108)
176+
177+
Copying text that is already in history does NOT create a second row and does NOT get silently ignored — the existing entry is moved to the top by refreshing its timestamps:
178+
179+
```kotlin
180+
// ClipboardDatabase.kt:186-199 (inside addClipboardEntry)
181+
db.rawQuery(duplicateQuery, arrayOf(contentHash, trimmedContent, currentTime.toString())).use { cursor ->
182+
if (cursor.moveToFirst()) {
183+
// #108: Move duplicate to top by updating its timestamp instead of ignoring
184+
val existingId = cursor.getLong(0)
185+
val updateValues = ContentValues().apply {
186+
put(COLUMN_TIMESTAMP, currentTime)
187+
put(COLUMN_EXPIRY_TIMESTAMP, expiryTimestamp)
188+
}
189+
db.update(TABLE_CLIPBOARD, updateValues, "$COLUMN_ID = ?", arrayOf(existingId.toString()))
190+
return true
191+
}
192+
}
193+
```
194+
195+
Duplicates are detected by `content_hash` + exact content match against non-expired entries. Media entries get the same treatment via their SHA-256 content hash (`ClipboardDatabase.kt:245-252`, "Duplicate media moved to top"). Pinned and todo tables behave differently: adding an already-pinned/already-todo item is rejected (`return false`) with no timestamp refresh — their ordering is user-managed (drag positions), not recency-based.
196+
175197
### Content URI Processing (IO thread)
176198

177199
```kotlin

docs/wiki/specs/clipboard/url-sanitization-spec.md

Lines changed: 118 additions & 69 deletions
Large diffs are not rendered by default.

docs/wiki/specs/gestures/short-swipes-spec.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Short Swipes - Technical Specification
33
user_guide: ../../gestures/short-swipes.md
44
status: implemented
5-
version: v1.2.7
5+
version: v1.5.0
66
---
77

88
# Short Swipes Technical Specification
@@ -152,9 +152,11 @@ fun drawSwipeTrail(canvas: Canvas, points: List<PointF>) {
152152

153153
| Setting | Key | Default | Range |
154154
|---------|-----|---------|-------|
155-
| **Min Distance** | `short_gesture_min_distance` | 28% | 15-50 |
156-
| **Max Distance** | `short_gesture_max_distance` | 65% | 40-80 |
157-
| **Enable Short Swipes** | `short_swipes_enabled` | true | boolean |
155+
| **Min Distance** | `short_gesture_min_distance` | 28 (% of key diagonal) | 10-60 (`Config.kt:119`) |
156+
| **Max Distance** (short/long boundary) | `short_gesture_max_distance` | 141 (% of key diagonal) | 50-200 (`Config.kt:120`) |
157+
| **Enable Short Swipes** | `short_gestures_enabled` | true | boolean (`Config.kt:118`) |
158+
159+
`short_gesture_max_distance` is the short/long boundary: displacement at or below it is a short swipe (subkey); beyond it the gesture continues as a word swipe. The old "200 = disabled" UI label was never implemented and has been retired (`Config.kt:571`). Both distances are stored as `PercentOfKey` — a value class that makes raw-pixel vs percent confusion uncompilable (`Config.kt:570-571`).
158160

159161
## Calibration Activity
160162

docs/wiki/specs/getting-started/setup-spec.md

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: First Time Setup - Technical Specification
33
user_guide: ../../getting-started/first-time-setup.md
44
status: implemented
5-
version: v1.2.9
5+
version: v1.5.0
66
---
77

88
# First Time Setup Technical Specification
@@ -18,7 +18,8 @@ Initial keyboard configuration flow including language pack download and prefere
1818
| Settings Activity | `SettingsActivity.kt` | Main configuration UI |
1919
| Config | `Config.kt` | Default values and preference keys |
2020
| DirectBootPrefs | `DirectBootAwarePreferences.kt` | Device-encrypted storage |
21-
| Language Manager | `LanguagePackManager.kt` | Download and install language packs |
21+
| Launcher | `LauncherActivity.kt` | First-run Enable/Select Keyboard flow |
22+
| Language Manager | `langpack/LanguagePackManager.kt` | Import and install language packs (SAF, no network) |
2223

2324
## Initialization Flow
2425

@@ -44,7 +45,7 @@ Normal keyboard operation
4445
|---------|------|----------|
4546
| **Preferences** | `shared_prefs/cleverkeys_prefs.xml` | User settings |
4647
| **Device Protected** | `shared_prefs/neural_performance_stats.xml` | Stats (encrypted) |
47-
| **Language Packs** | `files/language_packs/` | Downloaded ONNX models |
48+
| **Language Packs** | `files/langpacks/` | Imported dictionaries + unigrams (`LanguagePackManager.kt:29`) |
4849

4950
## Default Values
5051

@@ -53,34 +54,42 @@ Key defaults from `Config.kt` `object Defaults` (line 18+):
5354
| Setting | Default | Source (`Config.kt` line) |
5455
|---------|---------|--------------------------|
5556
| `THEME` | `"cleverkeysdark"` | 20 |
56-
| `KEYBOARD_HEIGHT_PORTRAIT` | 30 | 21 |
57-
| `KEYBOARD_HEIGHT_LANDSCAPE` | 40 | 22 |
58-
| `SHORT_GESTURE_MIN_DISTANCE` | 28 | 99 |
59-
| `SHORT_GESTURE_MAX_DISTANCE` | 141 | 100 |
60-
| `AUTOCORRECT_ENABLED` | true | 153 |
61-
| `LONGPRESS_TIMEOUT` | 600 | 70 |
62-
| `NEURAL_BEAM_WIDTH` | 6 | 114 |
63-
| `NEURAL_MAX_LENGTH` | 20 | 115 |
64-
| `NEURAL_CONFIDENCE_THRESHOLD` | 0.01f | 116 |
65-
| `ONNX_XNNPACK_THREADS` | 2 | 247 |
66-
| `HAPTIC_ENABLED` | true | 63 |
67-
| `HAPTIC_SWIPE_COMPLETE` | false | 69 |
68-
| `SMART_PUNCTUATION` | true | 77 |
69-
| `DOUBLE_SPACE_TO_PERIOD` | true | 86 |
70-
| `DOUBLE_SPACE_THRESHOLD` | 500 | 87 |
71-
| `LANGUAGE_DETECTION_SENSITIVITY` | 0.6f | 243 |
72-
| `CLIPBOARD_HISTORY_LIMIT` | 50 | 176-177 |
73-
74-
## Language Pack Download
75-
76-
Flow in `LanguagePackManager.kt`:
77-
78-
1. Check network connectivity
79-
2. Fetch pack list from GitHub releases
80-
3. Download ZIP containing model + dictionary
81-
4. Extract to `language_packs/{lang}/`
82-
5. Verify model loads successfully
83-
6. Update preferences with available languages
57+
| `KEYBOARD_HEIGHT_PORTRAIT` | 27 | 23 |
58+
| `KEYBOARD_HEIGHT_LANDSCAPE` | 40 | 24 |
59+
| `SHORT_GESTURE_MIN_DISTANCE` | 28 (% of key diagonal) | 119 |
60+
| `SHORT_GESTURE_MAX_DISTANCE` | 141 (% of key diagonal; short/long boundary) | 120 |
61+
| `AUTOCORRECT_ENABLED` | true | 176 |
62+
| `LONGPRESS_TIMEOUT` | 600 | 85 |
63+
| `NEURAL_BEAM_WIDTH` | 6 | 134 |
64+
| `NEURAL_MAX_LENGTH` | 20 | 135 |
65+
| `NEURAL_CONFIDENCE_THRESHOLD` | 0.01f | 136 |
66+
| `ONNX_XNNPACK_THREADS` | 2 | 299 |
67+
| `HAPTIC_ENABLED` | true | 75 |
68+
| `HAPTIC_SWIPE_COMPLETE` | true | 84 |
69+
| `SMART_PUNCTUATION` | true | 95 |
70+
| `DOUBLE_SPACE_TO_PERIOD` | true | 104 |
71+
| `DOUBLE_SPACE_THRESHOLD` | 500 | 105 |
72+
| `LANGUAGE_DETECTION_SENSITIVITY` | 0.6f | 295 |
73+
| `CLIPBOARD_HISTORY_LIMIT` | "0" (unlimited) | 215 |
74+
75+
## Enable / Select Keyboard Flow (LauncherActivity)
76+
77+
The in-app launcher offers **Enable Keyboard** and **Select Keyboard** actions (`LauncherActivity.kt:110-111`). `Select Keyboard` never calls `showInputMethodPicker()` in crash-prone states (UT-6, `LauncherActivity.kt:131-164`):
78+
79+
- CleverKeys **not yet enabled** as an input method → opens the system IME settings screen instead of the picker (`isCleverKeysEnabledCompat()` check → `launchKeyboardSettings()`).
80+
- Enabled **and window focused** → shows the system input-method picker.
81+
- Enabled but **window unfocused** at picker time → falls back to IME settings.
82+
83+
Rationale: `showInputMethodPicker()` displays a dialog owned by `system_server`; issuing it while the IME isn't enabled or the window is unfocused can crash or no-op silently.
84+
85+
## Language Pack Import
86+
87+
CleverKeys has **no INTERNET permission** — language packs are never downloaded by the app. Flow:
88+
89+
1. Obtain a prebuilt `langpack-<lang>.zip` (repo `scripts/dictionaries/`) or build one with `scripts/build_langpack.py`
90+
2. Settings → 🌐 Multi-Language → **Import Pack** (SAF file picker)
91+
3. Pack contents (manifest.json + dictionary.bin + unigrams.txt) are installed under `files/langpacks/`
92+
4. The language becomes selectable; installed packs are detected alongside bundled dictionaries
8493

8594
## Related Specifications
8695

docs/wiki/specs/settings/appearance-spec.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Appearance Settings - Technical Specification
33
user_guide: ../../settings/appearance.md
44
status: implemented
5-
version: v1.2.9
5+
version: v1.5.0
66
---
77

88
# Appearance Settings Technical Specification
@@ -244,6 +244,17 @@ class PredictionBarView : ViewGroup {
244244
| **Popup Mode** | `key_popup_mode` | ALWAYS | Always/HoldOnly |
245245
| **Prediction Height** | `prediction_bar_height` | NORMAL | Hidden/Compact/Normal/Expanded |
246246
| **Prediction Count** | `prediction_count` | 5 | 3-7 |
247+
| **Secondary Label Size** | `secondary_label_size_scale` | 1.0 (100% = unchanged) | 0.5–2.0 (UI slider 50%–200%) |
248+
249+
### Secondary Label Size (#133, v1.5.0)
250+
251+
Independent scale for the small corner (short-swipe/flick) labels, decoupled from Character Size. Default `SECONDARY_LABEL_SIZE_SCALE = 1.0f` (`Config.kt:39`); applied multiplicatively in `Keyboard2View.kt:1275`:
252+
253+
```kotlin
254+
_subLabelSize = labelBaseSize * _config.sublabelTextSize * _config.secondary_label_size_scale
255+
```
256+
257+
Turn it down when a large Character Size makes sublabels crowd the main label.
247258

248259
## Related Specifications
249260

0 commit comments

Comments
 (0)