add simulator mode to desktop designer for testing apps

- Add --simulator flag to launch home screen showing discovered apps
- Create app discovery system to scan test-apps/ directory
- Build simulator home screen with dark phone-like UI
- Add Lua API: simulator.launchApp, simulator.goHome, simulator.getApps
- ESC key returns to home when inside an app
- Apps displayed with icons in grid layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 11:16:01 +01:00
parent 02db0d849c
commit f41eda6f62
7 changed files with 795 additions and 3 deletions

View File

@@ -0,0 +1,91 @@
// D:\Dev\Mosis\MosisService\designer\src\app_discovery.cpp
#include "app_discovery.h"
#include <nlohmann/json.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
using json = nlohmann::json;
namespace mosis {
std::vector<AppInfo> AppDiscovery::DiscoverApps(const std::string& apps_directory) {
std::vector<AppInfo> apps;
if (!fs::exists(apps_directory) || !fs::is_directory(apps_directory)) {
std::cerr << "AppDiscovery: Directory not found: " << apps_directory << std::endl;
return apps;
}
std::cout << "AppDiscovery: Scanning " << apps_directory << std::endl;
for (const auto& entry : fs::directory_iterator(apps_directory)) {
if (!entry.is_directory()) continue;
std::string app_dir = entry.path().string();
AppInfo info;
if (LoadAppManifest(app_dir, info)) {
apps.push_back(std::move(info));
std::cout << "AppDiscovery: Found app '" << apps.back().name
<< "' (" << apps.back().id << ")" << std::endl;
}
}
std::cout << "AppDiscovery: Found " << apps.size() << " apps" << std::endl;
return apps;
}
bool AppDiscovery::LoadAppManifest(const std::string& app_directory, AppInfo& info) {
fs::path manifest_path = fs::path(app_directory) / "manifest.json";
if (!fs::exists(manifest_path)) {
return false;
}
try {
std::ifstream file(manifest_path);
if (!file.is_open()) {
std::cerr << "AppDiscovery: Cannot open " << manifest_path << std::endl;
return false;
}
json j;
file >> j;
// Required fields
if (!j.contains("id") || !j.contains("name") || !j.contains("entry")) {
std::cerr << "AppDiscovery: Missing required fields in " << manifest_path << std::endl;
return false;
}
info.id = j["id"].get<std::string>();
info.name = j["name"].get<std::string>();
info.entry = j["entry"].get<std::string>();
// Optional fields
info.version = j.value("version", "1.0.0");
info.icon = j.value("icon", "icon.tga");
info.description = j.value("description", "");
// Normalize path separators
info.app_path = app_directory;
std::replace(info.app_path.begin(), info.app_path.end(), '\\', '/');
// Verify entry file exists
fs::path entry_path = fs::path(app_directory) / info.entry;
if (!fs::exists(entry_path)) {
std::cerr << "AppDiscovery: Entry file not found: " << entry_path << std::endl;
return false;
}
return true;
}
catch (const std::exception& e) {
std::cerr << "AppDiscovery: Error parsing " << manifest_path << ": " << e.what() << std::endl;
return false;
}
}
} // namespace mosis

View File

@@ -0,0 +1,32 @@
// D:\Dev\Mosis\MosisService\designer\src\app_discovery.h
#pragma once
#include <string>
#include <vector>
namespace mosis {
struct AppInfo {
std::string id; // e.g., "com.mosis.sandbox-test"
std::string name; // Display name
std::string version; // Version string
std::string entry; // Entry point (e.g., "main.rml")
std::string icon; // Icon filename (e.g., "icon.tga")
std::string description; // App description
std::string app_path; // Full path to app directory
// Computed paths
std::string GetEntryPath() const { return app_path + "/" + entry; }
std::string GetIconPath() const { return app_path + "/" + icon; }
};
class AppDiscovery {
public:
// Scan a directory for apps (folders with manifest.json)
static std::vector<AppInfo> DiscoverApps(const std::string& apps_directory);
// Load a single app's manifest
static bool LoadAppManifest(const std::string& app_directory, AppInfo& info);
};
} // namespace mosis