9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_format.h"
19EM_JS(
void, localStorage_setItem, (
const char* key,
const char* value), {
21 localStorage.setItem(UTF8ToString(key), UTF8ToString(value));
23 console.error(
'Failed to save to localStorage:', e);
27EM_JS(
char*, localStorage_getItem, (
const char* key), {
29 const value = localStorage.getItem(UTF8ToString(key));
30 if (value === null)
return null;
31 const len = lengthBytesUTF8(value) + 1;
32 const ptr = _malloc(len);
33 stringToUTF8(value, ptr, len);
36 console.error(
'Failed to read from localStorage:', e);
41EM_JS(
void, localStorage_removeItem, (
const char* key), {
43 localStorage.removeItem(UTF8ToString(key));
45 console.error(
'Failed to remove from localStorage:', e);
49EM_JS(
int, localStorage_hasItem, (
const char* key), {
51 return localStorage.getItem(UTF8ToString(key)) !== null ? 1 : 0;
53 console.error(
'Failed to check localStorage:', e);
58EM_JS(
void, localStorage_clear, (), {
62 for (let i = 0; i < localStorage.length; i++) {
63 const key = localStorage.key(i);
64 if (key &&
key.startsWith(
'yaze_')) {
68 keys.forEach(key => localStorage.removeItem(key));
70 console.error(
'Failed to clear localStorage:', e);
76absl::Status WasmSettings::SaveTheme(
const std::string& theme) {
77 localStorage_setItem(kThemeKey, theme.c_str());
78 return absl::OkStatus();
81std::string WasmSettings::LoadTheme() {
82 char* theme = localStorage_getItem(kThemeKey);
86 std::string result(theme);
91std::string WasmSettings::GetCurrentThemeData() {
95absl::Status WasmSettings::LoadUserFont(
const std::string& name,
96 const std::string& data,
float size) {
102nlohmann::json WasmSettings::RecentFilesToJson(
103 const std::vector<RecentFile>& files) {
104 nlohmann::json json_array = nlohmann::json::array();
105 for (
const auto& file : files) {
106 nlohmann::json entry;
107 entry[
"filename"] = file.filename;
109 std::chrono::duration_cast<std::chrono::milliseconds>(
110 file.timestamp.time_since_epoch())
112 json_array.push_back(entry);
117std::vector<WasmSettings::RecentFile> WasmSettings::JsonToRecentFiles(
118 const nlohmann::json& json) {
119 std::vector<RecentFile> files;
120 if (!
json.is_array())
return files;
122 for (
const auto& entry :
json) {
123 if (entry.contains(
"filename") && entry.contains(
"timestamp")) {
125 file.filename = entry[
"filename"].get<std::string>();
126 auto ms = std::chrono::milliseconds(entry[
"timestamp"].get<int64_t>());
127 file.timestamp = std::chrono::system_clock::time_point(ms);
128 files.push_back(file);
134absl::Status WasmSettings::AddRecentFile(
135 const std::string& filename,
136 std::chrono::system_clock::time_point timestamp) {
138 char* json_str = localStorage_getItem(kRecentFilesKey);
139 std::vector<RecentFile> files;
143 nlohmann::json
json = nlohmann::json::parse(json_str);
144 files = JsonToRecentFiles(json);
145 }
catch (
const std::exception& e) {
147 emscripten_log(EM_LOG_WARN,
"Failed to parse recent files: %s", e.what());
154 std::remove_if(files.begin(), files.end(),
155 [&filename](
const RecentFile& f) {
156 return f.filename == filename;
161 files.insert(files.begin(), {filename, timestamp});
164 if (files.size() > 20) {
169 nlohmann::json
json = RecentFilesToJson(files);
170 localStorage_setItem(kRecentFilesKey,
json.dump().c_str());
172 return absl::OkStatus();
175std::vector<std::string> WasmSettings::GetRecentFiles(
size_t max_count) {
176 std::vector<std::string> result;
178 char* json_str = localStorage_getItem(kRecentFilesKey);
184 nlohmann::json
json = nlohmann::json::parse(json_str);
185 std::vector<RecentFile> files = JsonToRecentFiles(json);
187 size_t count = std::min(max_count, files.size());
188 for (
size_t i = 0; i < count; ++i) {
189 result.push_back(files[i].filename);
191 }
catch (
const std::exception& e) {
192 emscripten_log(EM_LOG_WARN,
"Failed to parse recent files: %s", e.what());
199absl::Status WasmSettings::ClearRecentFiles() {
200 localStorage_removeItem(kRecentFilesKey);
201 return absl::OkStatus();
204absl::Status WasmSettings::RemoveRecentFile(
const std::string& filename) {
205 char* json_str = localStorage_getItem(kRecentFilesKey);
207 return absl::OkStatus();
211 nlohmann::json
json = nlohmann::json::parse(json_str);
212 std::vector<RecentFile> files = JsonToRecentFiles(json);
215 std::remove_if(files.begin(), files.end(),
216 [&filename](
const RecentFile& f) {
217 return f.filename == filename;
221 nlohmann::json new_json = RecentFilesToJson(files);
222 localStorage_setItem(kRecentFilesKey, new_json.dump().c_str());
223 }
catch (
const std::exception& e) {
225 return absl::InternalError(
226 absl::StrFormat(
"Failed to remove recent file: %s", e.what()));
230 return absl::OkStatus();
235absl::Status WasmSettings::SaveWorkspace(
const std::string& name,
236 const std::string& layout_json) {
237 std::string
key = absl::StrCat(kWorkspacePrefix, name);
238 return WasmStorage::SaveProject(key, layout_json);
241absl::StatusOr<std::string> WasmSettings::LoadWorkspace(
const std::string& name) {
242 std::string
key = absl::StrCat(kWorkspacePrefix, name);
243 return WasmStorage::LoadProject(key);
246std::vector<std::string> WasmSettings::ListWorkspaces() {
247 std::vector<std::string> all_projects = WasmStorage::ListProjects();
248 std::vector<std::string> workspaces;
250 const std::string prefix(kWorkspacePrefix);
251 for (
const auto& project : all_projects) {
252 if (project.find(prefix) == 0) {
253 workspaces.push_back(project.substr(prefix.length()));
260absl::Status WasmSettings::DeleteWorkspace(
const std::string& name) {
261 std::string
key = absl::StrCat(kWorkspacePrefix, name);
262 return WasmStorage::DeleteProject(key);
265absl::Status WasmSettings::SetActiveWorkspace(
const std::string& name) {
266 localStorage_setItem(kActiveWorkspaceKey,
name.c_str());
267 return absl::OkStatus();
270std::string WasmSettings::GetActiveWorkspace() {
271 char* workspace = localStorage_getItem(kActiveWorkspaceKey);
275 std::string result(workspace);
282absl::Status WasmSettings::SaveUndoHistory(
const std::string& editor_id,
283 const std::vector<uint8_t>& history) {
284 std::string
key = absl::StrCat(kUndoHistoryPrefix, editor_id);
285 return WasmStorage::SaveRom(key, history);
288absl::StatusOr<std::vector<uint8_t>> WasmSettings::LoadUndoHistory(
289 const std::string& editor_id) {
290 std::string
key = absl::StrCat(kUndoHistoryPrefix, editor_id);
291 return WasmStorage::LoadRom(key);
294absl::Status WasmSettings::ClearUndoHistory(
const std::string& editor_id) {
295 std::string
key = absl::StrCat(kUndoHistoryPrefix, editor_id);
296 return WasmStorage::DeleteRom(key);
299absl::Status WasmSettings::ClearAllUndoHistory() {
300 std::vector<std::string> all_roms = WasmStorage::ListRoms();
301 const std::string prefix(kUndoHistoryPrefix);
303 for (
const auto& rom : all_roms) {
304 if (rom.find(prefix) == 0) {
305 auto status = WasmStorage::DeleteRom(rom);
312 return absl::OkStatus();
317absl::Status WasmSettings::SaveSetting(
const std::string& key,
318 const nlohmann::json& value) {
319 std::string storage_key = absl::StrCat(kSettingsPrefix, key);
320 localStorage_setItem(storage_key.c_str(), value.dump().c_str());
323 auto now = std::chrono::system_clock::now();
324 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
325 now.time_since_epoch()).count();
326 localStorage_setItem(kLastSaveTimeKey, std::to_string(ms).c_str());
328 return absl::OkStatus();
331absl::StatusOr<nlohmann::json> WasmSettings::LoadSetting(
const std::string& key) {
332 std::string storage_key = absl::StrCat(kSettingsPrefix, key);
333 char* value = localStorage_getItem(storage_key.c_str());
336 return absl::NotFoundError(absl::StrFormat(
"Setting '%s' not found", key));
340 nlohmann::json
json = nlohmann::json::parse(value);
343 }
catch (
const std::exception& e) {
345 return absl::InvalidArgumentError(
346 absl::StrFormat(
"Failed to parse setting '%s': %s", key, e.what()));
350bool WasmSettings::HasSetting(
const std::string& key) {
351 std::string storage_key = absl::StrCat(kSettingsPrefix, key);
352 return localStorage_hasItem(storage_key.c_str()) == 1;
355absl::Status WasmSettings::SaveAllSettings(
const nlohmann::json& settings) {
356 if (!settings.is_object()) {
357 return absl::InvalidArgumentError(
"Settings must be a JSON object");
360 for (
auto it = settings.begin(); it != settings.end(); ++it) {
361 auto status = SaveSetting(it.key(), it.value());
367 return absl::OkStatus();
370absl::StatusOr<nlohmann::json> WasmSettings::LoadAllSettings() {
371 nlohmann::json settings = nlohmann::json::object();
377 std::vector<std::string> common_keys = {
378 "show_grid",
"grid_size",
"auto_save",
"auto_save_interval",
379 "show_tooltips",
"confirm_on_delete",
"default_editor",
380 "animation_speed",
"zoom_level",
"show_minimap"
383 for (
const auto& key : common_keys) {
384 if (HasSetting(key)) {
385 auto result = LoadSetting(key);
387 settings[
key] = *result;
395absl::Status WasmSettings::ClearAllSettings() {
396 localStorage_clear();
397 return absl::OkStatus();
402absl::StatusOr<std::string> WasmSettings::ExportSettings() {
403 nlohmann::json export_data = nlohmann::json::object();
406 export_data[
"theme"] = LoadTheme();
409 char* recent_json = localStorage_getItem(kRecentFilesKey);
412 export_data[
"recent_files"] = nlohmann::json::parse(recent_json);
420 export_data[
"active_workspace"] = GetActiveWorkspace();
423 nlohmann::json workspaces = nlohmann::json::object();
424 for (
const auto& name : ListWorkspaces()) {
425 auto workspace_data = LoadWorkspace(name);
426 if (workspace_data.ok()) {
427 workspaces[
name] = nlohmann::json::parse(*workspace_data);
430 export_data[
"workspaces"] = workspaces;
433 auto all_settings = LoadAllSettings();
434 if (all_settings.ok()) {
435 export_data[
"settings"] = *all_settings;
438 return export_data.dump(2);
441absl::Status WasmSettings::ImportSettings(
const std::string& json_str) {
443 nlohmann::json import_data = nlohmann::json::parse(json_str);
446 if (import_data.contains(
"theme")) {
447 SaveTheme(import_data[
"theme"].get<std::string>());
451 if (import_data.contains(
"recent_files")) {
452 localStorage_setItem(kRecentFilesKey,
453 import_data[
"recent_files"].dump().c_str());
457 if (import_data.contains(
"active_workspace")) {
458 SetActiveWorkspace(import_data[
"active_workspace"].get<std::string>());
462 if (import_data.contains(
"workspaces") && import_data[
"workspaces"].is_object()) {
463 for (
auto it = import_data[
"workspaces"].begin();
464 it != import_data[
"workspaces"].end(); ++it) {
465 SaveWorkspace(it.key(), it.value().dump());
470 if (import_data.contains(
"settings") && import_data[
"settings"].is_object()) {
471 SaveAllSettings(import_data[
"settings"]);
474 return absl::OkStatus();
475 }
catch (
const std::exception& e) {
476 return absl::InvalidArgumentError(
477 absl::StrFormat(
"Failed to import settings: %s", e.what()));
481absl::StatusOr<std::chrono::system_clock::time_point> WasmSettings::GetLastSaveTime() {
482 char* time_str = localStorage_getItem(kLastSaveTimeKey);
484 return absl::NotFoundError(
"No save time recorded");
488 int64_t ms = std::stoll(time_str);
490 return std::chrono::system_clock::time_point(std::chrono::milliseconds(ms));
491 }
catch (
const std::exception& e) {
493 return absl::InvalidArgumentError(
494 absl::StrFormat(
"Failed to parse save time: %s", e.what()));
static ThemeManager & Get()
std::string ExportCurrentThemeJson() const
EM_JS(void, CallJsAiDriver,(const char *history_json), { if(window.yaze &&window.yaze.ai &&window.yaze.ai.processAgentRequest) { window.yaze.ai.processAgentRequest(UTF8ToString(history_json));} else { console.error("AI Driver not found in window.yaze.ai.processAgentRequest");} })
absl::Status LoadFontFromMemory(const std::string &name, const std::string &data, float size_pixels)