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));
32 const len = lengthBytesUTF8(value) + 1;
33 const ptr = _malloc(len);
34 stringToUTF8(value, ptr, len);
37 console.error(
'Failed to read from localStorage:', e);
42EM_JS(
void, localStorage_removeItem, (
const char* key), {
44 localStorage.removeItem(UTF8ToString(key));
46 console.error(
'Failed to remove from localStorage:', e);
50EM_JS(
int, localStorage_hasItem, (
const char* key), {
52 return localStorage.getItem(UTF8ToString(key)) != null ? 1 : 0;
54 console.error(
'Failed to check localStorage:', e);
59EM_JS(
void, localStorage_clear, (), {
63 for (let i = 0; i < localStorage.length; i++) {
64 const key = localStorage.key(i);
65 if (key &&
key.startsWith(
'yaze_')) {
69 keys.forEach(function(key) { localStorage.removeItem(key); });
71 console.error(
'Failed to clear localStorage:', e);
77absl::Status WasmSettings::SaveTheme(
const std::string& theme) {
78 localStorage_setItem(kThemeKey, theme.c_str());
79 return absl::OkStatus();
82std::string WasmSettings::LoadTheme() {
83 char* theme = localStorage_getItem(kThemeKey);
87 std::string result(theme);
92std::string WasmSettings::GetCurrentThemeData() {
96absl::Status WasmSettings::LoadUserFont(
const std::string& name,
97 const std::string& data,
float size) {
103nlohmann::json WasmSettings::RecentFilesToJson(
104 const std::vector<RecentFile>& files) {
105 nlohmann::json json_array = nlohmann::json::array();
106 for (
const auto& file : files) {
107 nlohmann::json entry;
108 entry[
"filename"] = file.filename;
109 entry[
"timestamp"] = 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())
123 for (
const auto& entry :
json) {
124 if (entry.contains(
"filename") && entry.contains(
"timestamp")) {
126 file.filename = entry[
"filename"].get<std::string>();
127 auto ms = std::chrono::milliseconds(entry[
"timestamp"].get<int64_t>());
128 file.timestamp = std::chrono::system_clock::time_point(ms);
129 files.push_back(file);
135absl::Status WasmSettings::AddRecentFile(
136 const std::string& filename,
137 std::chrono::system_clock::time_point timestamp) {
139 char* json_str = localStorage_getItem(kRecentFilesKey);
140 std::vector<RecentFile> files;
144 nlohmann::json
json = nlohmann::json::parse(json_str);
145 files = JsonToRecentFiles(json);
146 }
catch (
const std::exception& e) {
148 emscripten_log(EM_LOG_WARN,
"Failed to parse recent files: %s", e.what());
154 files.erase(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);
214 files.erase(std::remove_if(files.begin(), files.end(),
215 [&filename](
const RecentFile& f) {
216 return f.filename == filename;
220 nlohmann::json new_json = RecentFilesToJson(files);
221 localStorage_setItem(kRecentFilesKey, new_json.dump().c_str());
222 }
catch (
const std::exception& e) {
224 return absl::InternalError(
225 absl::StrFormat(
"Failed to remove recent file: %s", e.what()));
229 return absl::OkStatus();
234absl::Status WasmSettings::SaveWorkspace(
const std::string& name,
235 const std::string& layout_json) {
236 std::string
key = absl::StrCat(kWorkspacePrefix, name);
237 return WasmStorage::SaveProject(key, layout_json);
240absl::StatusOr<std::string> WasmSettings::LoadWorkspace(
241 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(
283 const std::string& editor_id,
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())
327 localStorage_setItem(kLastSaveTimeKey, std::to_string(ms).c_str());
329 return absl::OkStatus();
332absl::StatusOr<nlohmann::json> WasmSettings::LoadSetting(
333 const std::string& key) {
334 std::string storage_key = absl::StrCat(kSettingsPrefix, key);
335 char* value = localStorage_getItem(storage_key.c_str());
338 return absl::NotFoundError(absl::StrFormat(
"Setting '%s' not found", key));
342 nlohmann::json
json = nlohmann::json::parse(value);
345 }
catch (
const std::exception& e) {
347 return absl::InvalidArgumentError(
348 absl::StrFormat(
"Failed to parse setting '%s': %s", key, e.what()));
352bool WasmSettings::HasSetting(
const std::string& key) {
353 std::string storage_key = absl::StrCat(kSettingsPrefix, key);
354 return localStorage_hasItem(storage_key.c_str()) == 1;
357absl::Status WasmSettings::SaveAllSettings(
const nlohmann::json& settings) {
358 if (!settings.is_object()) {
359 return absl::InvalidArgumentError(
"Settings must be a JSON object");
362 for (
auto it = settings.begin(); it != settings.end(); ++it) {
363 auto status = SaveSetting(it.key(), it.value());
369 return absl::OkStatus();
372absl::StatusOr<nlohmann::json> WasmSettings::LoadAllSettings() {
373 nlohmann::json settings = nlohmann::json::object();
379 std::vector<std::string> common_keys = {
380 "show_grid",
"grid_size",
"auto_save",
381 "auto_save_interval",
"show_tooltips",
"confirm_on_delete",
382 "default_editor",
"animation_speed",
"zoom_level",
385 for (
const auto& key : common_keys) {
386 if (HasSetting(key)) {
387 auto result = LoadSetting(key);
389 settings[
key] = *result;
397absl::Status WasmSettings::ClearAllSettings() {
398 localStorage_clear();
399 return absl::OkStatus();
404absl::StatusOr<std::string> WasmSettings::ExportSettings() {
405 nlohmann::json export_data = nlohmann::json::object();
408 export_data[
"theme"] = LoadTheme();
411 char* recent_json = localStorage_getItem(kRecentFilesKey);
414 export_data[
"recent_files"] = nlohmann::json::parse(recent_json);
422 export_data[
"active_workspace"] = GetActiveWorkspace();
425 nlohmann::json workspaces = nlohmann::json::object();
426 for (
const auto& name : ListWorkspaces()) {
427 auto workspace_data = LoadWorkspace(name);
428 if (workspace_data.ok()) {
429 workspaces[
name] = nlohmann::json::parse(*workspace_data);
432 export_data[
"workspaces"] = workspaces;
435 auto all_settings = LoadAllSettings();
436 if (all_settings.ok()) {
437 export_data[
"settings"] = *all_settings;
440 return export_data.dump(2);
443absl::Status WasmSettings::ImportSettings(
const std::string& json_str) {
445 nlohmann::json import_data = nlohmann::json::parse(json_str);
448 if (import_data.contains(
"theme")) {
449 SaveTheme(import_data[
"theme"].get<std::string>());
453 if (import_data.contains(
"recent_files")) {
454 localStorage_setItem(kRecentFilesKey,
455 import_data[
"recent_files"].dump().c_str());
459 if (import_data.contains(
"active_workspace")) {
460 SetActiveWorkspace(import_data[
"active_workspace"].get<std::string>());
464 if (import_data.contains(
"workspaces") &&
465 import_data[
"workspaces"].is_object()) {
466 for (
auto it = import_data[
"workspaces"].begin();
467 it != import_data[
"workspaces"].end(); ++it) {
468 SaveWorkspace(it.key(), it.value().dump());
473 if (import_data.contains(
"settings") &&
474 import_data[
"settings"].is_object()) {
475 SaveAllSettings(import_data[
"settings"]);
478 return absl::OkStatus();
479 }
catch (
const std::exception& e) {
480 return absl::InvalidArgumentError(
481 absl::StrFormat(
"Failed to import settings: %s", e.what()));
485absl::StatusOr<std::chrono::system_clock::time_point>
486WasmSettings::GetLastSaveTime() {
487 char* time_str = localStorage_getItem(kLastSaveTimeKey);
489 return absl::NotFoundError(
"No save time recorded");
493 int64_t ms = std::stoll(time_str);
495 return std::chrono::system_clock::time_point(std::chrono::milliseconds(ms));
496 }
catch (
const std::exception& e) {
498 return absl::InvalidArgumentError(
499 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)