uttrflow / uttrflow/uttrflow-swift
Clipboard search ignores character width, and alias matching disagrees with search about nukta: "जिंदगी" finds a clip aliased "ज़िंदगी" but doesn't rank it as the alias
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
Every list search goes through one rule (`Sources/UttrflowUX/SearchMatching.swift:6-11`):
```swift
range(of: needle, options: [.caseInsensitive, .diacriticInsensitive], range: nil, locale: locale)
```
Alias matching reduces both sides with `folding` instead (`Sources/UttrflowUX/PanelAlias.swift:27-31`, used by `PanelResults.isAlias` at `Sources/UttrflowUX/PanelResults.swift:135-140`). Checked with a throwaway script calling the same Foundation APIs with the same options:
| clip text or alias | query | search (`range`) | alias equal (`folding`) |
|---|---|---|---|
| `ABC123` (full-width) | `abc123` | no match | — |
| `カタカナ` (half-width katakana) | `カタカナ` | no match | — |
| alias `ज़िंदगी` | `जिंदगी` | match | **not equal** |
| alias `Café` | `cafe` | match | equal |
- **Width.** Full-width Latin letters and digits are common in text copied from Japanese, Chinese and Korean pages and forms. Half-width katakana is common in older Japanese systems and receipts. Neither can be found by typing the usual form. Adding `.widthInsensitive` makes the first row match.
- **Nukta.** `range(of:options:.diacriticInsensitive)` ignores the Devanagari nukta (़, U+093C), but `folding(options:.diacriticInsensitive)` keeps it. So a Hindi alias typed without the nukta shows the clip, but as a content match. It loses the exact-alias ranking that `isAlias` exists to give, and the comment at `PanelResults.swift:137` says the two must "spell one name". Latin accents agree, which is why this hasn't shown up.
## Why it matters
Hindi is often typed without a nukta (ज़ → ज), and CJK text is full of full-width characters. Search should find a clip whichever way it was typed, and alias ranking should agree with search.
## Acceptance criteria
- `SearchMatching` adds `.widthInsensitive`.
- `PanelAlias.handle` gives the same answer as the search rule for the rows above. The simplest way is to also fold width, and to compare with `compare(_:options:range:locale:) == .orderedSame` using the same options instead of `folding` plus `==`. Or fold with the same options through a shared helper.
- Tests: in `Tests/UttrflowUXTests/PanelResultsTests.swift`, next to the existing "case and accents" test (around line 130), cover full-width and half-width katakana matches. In `Tests/UttrflowUXTests/PanelAliasTests.swift`, cover `ज़िंदगी`/`जिंदगी` being the same alias, and keep `Café`/`cafe`.
Not in scope: Arabic hamza forms (`أحمد` vs `احمد`) don't match either. Folding them is a normalisation choice rather than a bug, so leave it for a separate discussion.
## Where to start
- `Sources/UttrflowUX/SearchMatching.swift:6-11`
- `Sources/UttrflowUX/PanelAlias.swift:27-31`, `Sources/UttrflowUX/PanelResults.swift:135-140`
- Tests to extend: `Tests/UttrflowUXTests/PanelResultsTests.swift`, `Tests/UttrflowUXTests/PanelAliasTests.swift`
- Before pushing, run `make verify` (export `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer` first). It is the same command CI runs, and it enforces the 95% coverage floor per module.
- Read [CONTRIBUTING.md](https://github.com/uttrflow/uttrflow-swift/blob/main/CONTRIBUTING.md) first, and say on this issue that you are taking it.
**Size:** S, about 2 hours.
Contributor guide
Research direction
Read CONTRIBUTING.md, then inspect Sources/UttrflowUX/SearchMatching.swift, PanelAlias.swift, and PanelResults.swift. Extend PanelResultsTests.swift and PanelAliasTests.swift with the specified width and nukta cases while preserving the Café test. Run make verify with DEVELOPER_DIR set; done means search and alias ranking agree for all listed cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- desktop, search, testing
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 86/100