Release v0.6.60: allow apostrophes in custom corrections
This commit is contained in:
1 parent
4d8d8d47b9
commit
011664cc53
12 files changed
+49
-18
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(KSwitcher VERSION 0.6.59 LANGUAGES CXX RC)
|
||||
project(KSwitcher VERSION 0.6.60 LANGUAGES CXX RC)
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||
endif()
|
||||
|
||||
@@ -723,9 +723,11 @@ default and require the main **Automatic correction (experimental)** checkbox to
|
||||
`првиіт=привіт` or `helo=hello`. Entries are normalized to lowercase; duplicate source words
|
||||
are rejected. The typo must contain 1–32 letters. The correction may contain several words
|
||||
separated by single spaces, for example `мамамила=мама мила`, up to 32 characters including
|
||||
spaces. Both sides must use the same supported language, without punctuation; leading,
|
||||
spaces. Corrections also allow apostrophes (`'`, `’`, `ʼ`) between letters, for example
|
||||
`ревю=рев'ю`. Both sides must use the same supported language, without other punctuation; leading,
|
||||
trailing and repeated spaces are not allowed. Explicit pairs take priority over dictionary recognition, including
|
||||
already valid words, and do not require dictionary membership or satisfy the frequency limit.
|
||||
Update every synchronizing PC to v0.6.60 or newer before sharing apostrophe pairs; older clients reject them.
|
||||
- **Correct spelling typos using dictionaries**: checks unknown words in the current language
|
||||
after no layout correction is found. It accepts exactly one dictionary candidate reachable
|
||||
by inserting, deleting or replacing one letter, or swapping two adjacent letters. Both input
|
||||
|
||||
Binary file not shown.
+8
-6
@@ -1,15 +1,15 @@
|
||||
# Latest Windows release
|
||||
|
||||
## v0.6.59 — 2026-11-09
|
||||
## v0.6.60 — 2026-11-09
|
||||
|
||||
## Changes
|
||||
- Show clear download/upload progress and results, including the server version, applied changes or unchanged local settings. Preserve action errors instead of replacing them with an older status.
|
||||
- Identify OpenBao HTTP 400 failures as authentication, download or write failures instead of labeling every failure as a rejected write. Failed downloads do not apply remote settings.
|
||||
- Refresh the already-open Settings window after a successful Download remote, preserving its selected tab. This also works if the Sync dialog was closed while the request was running. Confirmation explains that unsaved Settings edits will be discarded.
|
||||
- Retain all v0.6.58 LAN, clipboard, screenshot, snippet and chat-profile behavior. No sync schema or compatibility change.
|
||||
- Fix custom auto-correction pairs such as `ревю=рев'ю`: correction text now accepts straight (`'`), typographic (`’`) and modifier-letter (`ʼ`) apostrophes between letters, both when saving settings and applying replacements.
|
||||
- Preserve the exact apostrophe, initial capitalization, surrounding punctuation and current layout. Multi-word corrections remain supported, with a maximum of 32 characters including spaces and apostrophes.
|
||||
- Use the same correction validator for settings and runtime. Source words still contain only letters; leading/trailing or adjacent apostrophes, apostrophes next to spaces, other punctuation and mixed-language replacements remain invalid. Update the validation message and user documentation.
|
||||
- Retain all v0.6.59 sync, LAN, clipboard, screenshot, snippet and chat-profile behavior. No new setting, shortcut or default.
|
||||
|
||||
## Validation
|
||||
Passed: 132 sync, 571 input/UI, 341 native, 829 chat-profile/UI, 205 LAN input, 289 LAN clipboard, 43 LAN pairing and 5 settings-save merge checks; dictionary equivalence for 140,939 words; wheel, StayHere, Utilities, screenshot UI/save/registration, 54 updater checks and updater progress. Regression tests use an isolated mock OpenBao transport, not a live user account.
|
||||
Passed: 349 native checks (including apostrophe validation, correction, capitalization/punctuation and settings roundtrips), 571 input/UI, 829 chat-profile/UI, 132 sync, 205 LAN input, 289 LAN clipboard, 43 LAN pairing and 5 settings-save merge checks; dictionary equivalence for 140,939 words; wheel, StayHere, Utilities, screenshot UI/save/registration, 54 updater checks and updater progress. Release packaging verifies the manifest, all file hashes and the ZIP checksum.
|
||||
|
||||
## Upgrade and compatibility
|
||||
Choose **Update** in the tray menu. Legacy v0.3.0–v0.4.1 clients first receive the v0.4.2 compatibility bridge, then the current full release. v0.2.3 and earlier require manual installation.
|
||||
@@ -17,6 +17,8 @@ For manual installation, exit KSwitcher, close its configurator and screenshot e
|
||||
|
||||
**Update every synchronizing PC to v0.6.51 or newer before sharing application-specific snippets. Older versions do not understand application restrictions and may treat scoped snippets as global.** Keep a backup before downgrading. Settings with Telegram Web profiles require v0.6.51 or newer. Chat profiles now synchronize from v0.6.53. Update every synchronizing PC to v0.6.53 or newer before syncing: older clients reject the chat-profile field. Synchronization stores application names, visible chat names and language IDs on your configured OpenBao server. No message contents are included.
|
||||
|
||||
**Update every synchronizing PC to v0.6.60 or newer before sharing replacement pairs containing apostrophes. Older clients reject these settings.** Existing letter-only and phrase pairs require no migration.
|
||||
|
||||
Download **KSwitcher-win-x64.zip** with **SHA256SUMS.txt**. The package contains no personal settings, credentials or clipboard history.
|
||||
|
||||
Publish stable `release-vX.Y.Z` tags matching the executable version. Keep `v0.4.2` as the last legacy tag.
|
||||
@@ -1 +1 @@
|
||||
f0d7d15534db21c17b113a25cc07d59a68fa151ecf995ac360ac476751171a9a KSwitcher-win-x64.zip
|
||||
31e6d5571db106154e3b72391c4f13b856523e602c84a1f8a2935f05c9508854 KSwitcher-win-x64.zip
|
||||
+3
-3
@@ -9,8 +9,8 @@ IDC_SCREENSHOT_PENCIL CURSOR "icons/lucide/pencil.cur"
|
||||
1 RT_MANIFEST "app.manifest"
|
||||
201 RCDATA "update.ps1"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,6,59,0
|
||||
PRODUCTVERSION 0,6,59,0
|
||||
FILEVERSION 0,6,60,0
|
||||
PRODUCTVERSION 0,6,60,0
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
BEGIN
|
||||
@@ -20,7 +20,7 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||
VALUE "ProductName", "KSwitcher"
|
||||
VALUE "FileVersion", "0.6.59"
|
||||
VALUE "FileVersion", "0.6.60"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
+1
-1
@@ -257,7 +257,7 @@ std::optional<AutoSuggestion> AutoCorrector::suggest(const std::wstring &typed,
|
||||
{
|
||||
auto replacement = settings.autoReplacements.find(small);
|
||||
if (replacement != settings.autoReplacements.end() &&
|
||||
replacement->second.find_first_not_of(alphabet + L" ") == small.npos)
|
||||
validAutoCorrection(replacement->second, alphabet))
|
||||
return sameLayout(replacement->second);
|
||||
}
|
||||
auto personal = [&](const std::wstring &language, const std::wstring &candidate)
|
||||
|
||||
@@ -21,6 +21,20 @@ int snippetAppPriority(const Snippet &snippet, const std::wstring &process)
|
||||
for (const auto &app : snippet.apps) if (snippetAppName(app) == name && !name.empty()) return 2;
|
||||
return 0;
|
||||
}
|
||||
bool validAutoCorrection(const std::wstring &text, const std::wstring &alphabet)
|
||||
{
|
||||
if (text.empty() || text.size() > 32) return false;
|
||||
auto isLetter = [&](wchar_t c) { return alphabet.find(c) != alphabet.npos; };
|
||||
for (size_t i = 0; i < text.size(); ++i)
|
||||
{
|
||||
if (isLetter(text[i])) continue;
|
||||
if (text[i] != L' ' && text[i] != L'\'' && text[i] != L'\u2019' && text[i] != L'\u02bc')
|
||||
return false;
|
||||
if (i == 0 || i + 1 == text.size() || !isLetter(text[i - 1]) || !isLetter(text[i + 1]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int HotkeyHook::update(UINT key, bool down, UINT modifiers,
|
||||
const std::vector<std::pair<int, Hotkey>> &bindings)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ struct Layout
|
||||
bool hasLetter(wchar_t ch) const;
|
||||
};
|
||||
bool letter(wchar_t ch);
|
||||
bool validAutoCorrection(const std::wstring &text, const std::wstring &alphabet);
|
||||
std::wstring lower(std::wstring text);
|
||||
std::wstring upper(std::wstring text);
|
||||
std::wstring convert(const std::wstring &, const Layout &, const Layout &, bool force = false);
|
||||
|
||||
+2
-3
@@ -138,10 +138,9 @@ void validateSettings(const Settings &s)
|
||||
valid |= !from.empty() && !to.empty() && from.size() <= 32 && to.size() <= 32 &&
|
||||
from == lower(from) && to == lower(to) && from != to &&
|
||||
from.find_first_not_of(alphabet) == from.npos &&
|
||||
to.find_first_not_of(std::wstring(alphabet) + L" ") == to.npos &&
|
||||
to.front() != L' ' && to.back() != L' ' && to.find(L" ") == to.npos;
|
||||
validAutoCorrection(to, alphabet);
|
||||
if (!valid)
|
||||
throw std::runtime_error("Use a lowercase typo of 1-32 letters and a different correction of 1-32 characters in the same language. Single spaces between correction words are allowed.");
|
||||
throw std::runtime_error("Use a lowercase typo of 1-32 letters and a different correction of 1-32 characters in the same language. Single spaces between words and apostrophes between letters are allowed.");
|
||||
}
|
||||
for (const auto &[language, words] : s.autoWords)
|
||||
{
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include "platform.h"
|
||||
namespace ks
|
||||
{
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.59";
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.60";
|
||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
||||
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
||||
|
||||
+14
-1
@@ -592,6 +592,16 @@ int main(int argc, char **argv)
|
||||
require(!edited.suggest(L"hlelo ", 0, layouts, options), "never-correct overrides explicit pairs");
|
||||
options.autoNever.clear();
|
||||
options.autoReplacements[L"мамамила"] = L"мама мила";
|
||||
for (const auto *text : {L"рев'ю", L"рев’ю", L"ревʼю", L"моє рев'ю"})
|
||||
{
|
||||
options.autoReplacements[L"ревю"] = text;
|
||||
ks::validateSettings(options);
|
||||
correction = edited.suggest(L"(Ревю)! ", 2, layouts, options);
|
||||
auto expected = std::wstring(text);
|
||||
expected[0] = ks::upper(expected.substr(0, 1))[0];
|
||||
require(correction && correction->text == L"(" + expected + L")! " && correction->target == 2,
|
||||
"apostrophe corrections preserve punctuation, capitalization and layout");
|
||||
}
|
||||
correction = edited.suggest(L"(Мамамила)! ", 2, layouts, options);
|
||||
require(correction && correction->text == L"(Мама мила)! " && correction->target == 2,
|
||||
"joined words split while preserving capitalization, punctuation and layout");
|
||||
@@ -718,7 +728,8 @@ int main(int argc, char **argv)
|
||||
settings.elevatedAutostart = true;
|
||||
settings.autoCorrect = true;
|
||||
settings.autoReplace = settings.autoTypos = true;
|
||||
settings.autoReplacements = {{L"првиіт", L"привіт"}, {L"helo", L"hello"}, {L"мамамила", L"мама мила"}};
|
||||
settings.autoReplacements = {{L"првиіт", L"привіт"}, {L"helo", L"hello"}, {L"мамамила", L"мама мила"},
|
||||
{L"ревю", L"рев'ю"}, {L"пять", L"п’ять"}, {L"імя", L"імʼя"}};
|
||||
settings.autoMaxRank = 10000;
|
||||
settings.autoMinLength = 5;
|
||||
settings.autoPrefixChars = L"(#";
|
||||
@@ -739,6 +750,8 @@ int main(int argc, char **argv)
|
||||
{L"helloworld", L"hello\nworld"}, {L"hello world", L"helloworld"},
|
||||
{L"helloworld", L"hello світ"}, {L"helloworld", L"hello world!"},
|
||||
{L"helloworld", L"hello Hello"}, {L"helloworld", L""},
|
||||
{L"review", L"'review"}, {L"review", L"review’"},
|
||||
{L"review", L"re''view"}, {L"review", L"reʼ view"},
|
||||
{L"helloworld", L"abcdefghijklmnopqrstuvwxyz abcdef"}})
|
||||
{
|
||||
auto invalid = settings;
|
||||
|
||||
Reference in new issue
Block a user