Release v0.6.26: OpenBao settings synchronization with per-setting conflicts
This commit is contained in:
+32
-2
@@ -8,6 +8,22 @@
|
||||
namespace ks
|
||||
{
|
||||
using Json = nlohmann::json;
|
||||
std::wstring snippetId(const Snippet &snippet)
|
||||
{
|
||||
if (!snippet.id.empty()) return snippet.id;
|
||||
// Deterministic migration of pre-ID profiles on every existing device.
|
||||
std::wstring id = L"legacy-";
|
||||
const wchar_t *hex = L"0123456789abcdef";
|
||||
for (unsigned char c : utf8(lower(snippet.trigger))) { id += hex[c >> 4]; id += hex[c & 15]; }
|
||||
return id;
|
||||
}
|
||||
std::wstring newSnippetId()
|
||||
{
|
||||
GUID guid{}; wchar_t text[40]{};
|
||||
if (FAILED(CoCreateGuid(&guid))) throw std::runtime_error("Cannot create snippet ID.");
|
||||
StringFromGUID2(guid, text, 40);
|
||||
return text;
|
||||
}
|
||||
namespace
|
||||
{
|
||||
Bytes jsonBytes(const Json &j)
|
||||
@@ -107,8 +123,14 @@ void validateSettings(const Settings &s)
|
||||
s.maxImageMiB > 64)
|
||||
throw std::runtime_error("History limits: 10–10000 items, 1–2048 MiB total, 1–64 MiB per image");
|
||||
std::set<std::wstring> seen;
|
||||
std::set<std::wstring> ids;
|
||||
for (const auto &id : s.snippetTombstones)
|
||||
if (id.empty() || id.size() > 600 || !ids.insert(id).second)
|
||||
throw std::runtime_error("Invalid snippet deletion record.");
|
||||
for (const auto &snippet : s.snippets)
|
||||
{
|
||||
auto id = snippetId(snippet);
|
||||
if (id.size() > 600 || !ids.insert(id).second) throw std::runtime_error("Duplicate snippet ID or deleted snippet.");
|
||||
if (snippet.trigger.size() < 2 || snippet.trigger.size() > 64 ||
|
||||
snippet.trigger.find_first_of(L"\r\n\t ") != snippet.trigger.npos)
|
||||
throw std::runtime_error("Snippet triggers must contain 2–64 characters without whitespace");
|
||||
@@ -203,7 +225,13 @@ Settings decodeSettings(const Bytes &bytes, const std::vector<Layout> &layouts)
|
||||
}
|
||||
if (j.contains("snippets"))
|
||||
for (auto &sn : j.at("snippets"))
|
||||
s.snippets.push_back({str(sn, "trigger"), str(sn, "text")});
|
||||
{
|
||||
Snippet item{str(sn, "trigger"), str(sn, "text"), str(sn, "id")};
|
||||
item.id = snippetId(item);
|
||||
s.snippets.push_back(std::move(item));
|
||||
}
|
||||
if (j.contains("snippetTombstones"))
|
||||
for (const auto &id : j.at("snippetTombstones")) s.snippetTombstones.push_back(wide(id.get<std::string>()));
|
||||
validateSettings(s);
|
||||
return s;
|
||||
}
|
||||
@@ -264,7 +292,9 @@ Bytes encodeSettings(const Settings &s)
|
||||
j["layoutHotkeys"].push_back({{"layoutId", utf8(s.directIds[i])}, {"hotkey", utf8(s.direct[i])}});
|
||||
j["snippets"] = Json::array();
|
||||
for (auto &sn : s.snippets)
|
||||
j["snippets"].push_back({{"trigger", utf8(sn.trigger)}, {"text", utf8(sn.text)}});
|
||||
j["snippets"].push_back({{"trigger", utf8(sn.trigger)}, {"text", utf8(sn.text)}, {"id", utf8(snippetId(sn))}});
|
||||
j["snippetTombstones"] = Json::array();
|
||||
for (const auto &id : s.snippetTombstones) j["snippetTombstones"].push_back(utf8(id));
|
||||
j["excludedApps"] = Json::array();
|
||||
for (auto &name : s.excluded)
|
||||
j["excludedApps"].push_back(utf8(name));
|
||||
|
||||
Reference in New Issue
Block a user