yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
project_management_panel.cc
Go to the documentation of this file.
2
3#include "absl/strings/str_format.h"
9#include "imgui/imgui.h"
10#include "rom/rom.h"
11#include "util/platform_paths.h"
12#include "yaze_config.h"
13
14namespace yaze {
15namespace editor {
16
18 if (!project_) {
19 ImGui::TextDisabled("No project loaded");
20 ImGui::Spacing();
21 ImGui::TextWrapped(
22 "Open a .yaze project file or create a new project to access "
23 "project management features.");
24 return;
25 }
26
28 ImGui::Separator();
30 ImGui::Separator();
32 ImGui::Separator();
34 ImGui::Separator();
36}
37
39 // Section header
41 ImGui::Spacing();
42
43 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Project Format:");
44 ImGui::SameLine();
46 ? ".yaze"
47 : ".zsproj");
48
49 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Project YAZE Version:");
50 ImGui::SameLine();
51 const std::string& project_version = project_->metadata.yaze_version;
52 if (project_version.empty()) {
53 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
54 ImGui::TextColored(gui::ConvertColorToImVec4(theme.warning), "Unknown");
55 } else if (project_version != YAZE_VERSION_STRING) {
56 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
57 ImGui::TextColored(gui::ConvertColorToImVec4(theme.warning), "%s",
58 project_version.c_str());
59 if (ImGui::IsItemHovered()) {
60 ImGui::SetTooltip("Project saved with v%s; running v%s",
61 project_version.c_str(), YAZE_VERSION_STRING);
62 }
63 } else {
64 ImGui::Text("%s", project_version.c_str());
65 }
66
67 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Running YAZE:");
68 ImGui::SameLine();
69 ImGui::Text("%s", YAZE_VERSION_STRING);
70
71 // Project file path (read-only, click to copy)
72 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Path:");
73 ImGui::SameLine();
74 if (ImGui::Selectable(project_->filepath.c_str(), false,
75 ImGuiSelectableFlags_None,
76 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
77 ImGui::SetClipboardText(project_->filepath.c_str());
78 if (toast_manager_) {
79 toast_manager_->Show("Path copied to clipboard", ToastType::kInfo);
80 }
81 }
82 if (ImGui::IsItemHovered()) {
83 ImGui::SetTooltip("Click to copy path");
84 }
85
86 ImGui::Spacing();
87
88 // Editable Project Name
89 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Project Name:");
90 static char name_buffer[256] = {};
91 if (name_buffer[0] == '\0' && !project_->name.empty()) {
92 strncpy(name_buffer, project_->name.c_str(), sizeof(name_buffer) - 1);
93 }
94 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
95 if (ImGui::InputText("##project_name", name_buffer, sizeof(name_buffer))) {
96 project_->name = name_buffer;
97 project_dirty_ = true;
98 }
99
100 // Editable Author
101 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Author:");
102 static char author_buffer[256] = {};
103 if (author_buffer[0] == '\0' && !project_->metadata.author.empty()) {
104 strncpy(author_buffer, project_->metadata.author.c_str(),
105 sizeof(author_buffer) - 1);
106 }
107 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
108 if (ImGui::InputText("##author", author_buffer, sizeof(author_buffer))) {
109 project_->metadata.author = author_buffer;
110 project_dirty_ = true;
111 }
112
113 // Editable Description
114 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Description:");
115 static char desc_buffer[1024] = {};
116 if (desc_buffer[0] == '\0' && !project_->metadata.description.empty()) {
117 strncpy(desc_buffer, project_->metadata.description.c_str(),
118 sizeof(desc_buffer) - 1);
119 }
120 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
121 if (ImGui::InputTextMultiline("##description", desc_buffer,
122 sizeof(desc_buffer), ImVec2(0, 60))) {
123 project_->metadata.description = desc_buffer;
124 project_dirty_ = true;
125 }
126
127 ImGui::Spacing();
128}
129
132 ImGui::Spacing();
133
134 ImGui::TextWrapped(
135 "Primary data lives under the .yaze root. Click any path to copy it.");
136 ImGui::Spacing();
137
139 if (!app_root.ok()) {
140 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
141 ImGui::TextColored(gui::ConvertColorToImVec4(theme.error),
142 "Storage unavailable: %s",
143 std::string(app_root.status().message()).c_str());
144 return;
145 }
146
147 std::vector<std::pair<const char*, std::filesystem::path>> locations = {
148 {"Root", *app_root},
149 {"Projects", *app_root / "projects"},
150 {"Layouts", *app_root / "layouts"},
151 {"Workspaces", *app_root / "workspaces"},
152 {"Logs", *app_root / "logs"},
153 {"Agent", *app_root / "agent"}};
154
155 auto temp_root = util::PlatformPaths::GetTempDirectory();
156 if (temp_root.ok()) {
157 locations.emplace_back("Temp", *temp_root);
158 }
159
160 if (ImGui::BeginTable("##storage_locations", 2,
161 ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV |
162 ImGuiTableFlags_SizingStretchProp)) {
163 ImGui::TableSetupColumn("Location", ImGuiTableColumnFlags_WidthFixed,
164 110.0f);
165 ImGui::TableSetupColumn("Path", ImGuiTableColumnFlags_WidthStretch);
166 for (const auto& entry : locations) {
167 ImGui::TableNextRow();
168 ImGui::TableNextColumn();
169 ImGui::TextColored(gui::GetTextSecondaryVec4(), "%s", entry.first);
170 ImGui::TableNextColumn();
171 const std::string display_path =
173 ImGui::PushID(entry.first);
174 if (ImGui::Selectable(display_path.c_str(), false,
175 ImGuiSelectableFlags_SpanAllColumns)) {
176 ImGui::SetClipboardText(display_path.c_str());
177 if (toast_manager_) {
178 toast_manager_->Show("Path copied to clipboard", ToastType::kInfo);
179 }
180 }
181 if (ImGui::IsItemHovered()) {
182 ImGui::SetTooltip("Click to copy");
183 }
184 ImGui::PopID();
185 }
186 ImGui::EndTable();
187 }
188
189 ImGui::Spacing();
190}
191
194 ImGui::Spacing();
195
196 // Current ROM
197 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Current ROM:");
198 if (project_->rom_filename.empty()) {
199 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
200 ImGui::TextColored(gui::ConvertColorToImVec4(theme.warning),
201 "Not configured");
202 } else {
203 // Show just the filename, full path on hover
204 std::string filename = project_->rom_filename;
205 size_t pos = filename.find_last_of("/\\");
206 if (pos != std::string::npos) {
207 filename = filename.substr(pos + 1);
208 }
209 ImGui::Text("%s", filename.c_str());
210 if (ImGui::IsItemHovered()) {
211 ImGui::SetTooltip("%s", project_->rom_filename.c_str());
212 }
213 }
214
215 // ROM status
216 if (rom_ && rom_->is_loaded()) {
217 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Title:");
218 ImGui::SameLine();
219 ImGui::Text("%s", rom_->title().c_str());
220
221 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Size:");
222 ImGui::SameLine();
223 ImGui::Text("%.2f MB", static_cast<float>(rom_->size()) / (1024 * 1024));
224
225 if (rom_->dirty()) {
226 const auto& theme2 = gui::ThemeManager::Get().GetCurrentTheme();
227 ImGui::TextColored(gui::ConvertColorToImVec4(theme2.warning),
228 "%s Unsaved changes", ICON_MD_WARNING);
229 }
230 }
231
232 ImGui::Spacing();
233
234 // Action buttons
235 float button_width = (ImGui::GetContentRegionAvail().x - 8) / 2;
236
237 if (ImGui::Button(ICON_MD_SWAP_HORIZ " Swap ROM", ImVec2(button_width, 0))) {
238 if (swap_rom_callback_) {
240 }
241 }
242 if (ImGui::IsItemHovered()) {
243 ImGui::SetTooltip("Replace the ROM file for this project");
244 }
245
246 ImGui::SameLine();
247
248 if (ImGui::Button(ICON_MD_REFRESH " Reload", ImVec2(button_width, 0))) {
251 }
252 }
253 if (ImGui::IsItemHovered()) {
254 ImGui::SetTooltip("Reload ROM from disk");
255 }
256
257 ImGui::Spacing();
258}
259
261 gui::ColoredTextF(gui::GetPrimaryVec4(), "%s Version Control",
263 ImGui::Spacing();
264
265 if (!version_manager_) {
266 ImGui::TextDisabled("Version manager not available");
267 return;
268 }
269
270 bool git_initialized = version_manager_->IsGitInitialized();
271
272 if (!git_initialized) {
273 ImGui::TextWrapped(
274 "Git is not initialized for this project. Initialize Git to enable "
275 "version control and snapshots.");
276 ImGui::Spacing();
277
278 if (ImGui::Button(ICON_MD_ADD " Initialize Git",
279 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
280 auto status = version_manager_->InitializeGit();
281 if (status.ok()) {
282 if (toast_manager_) {
283 toast_manager_->Show("Git repository initialized",
285 }
286 } else {
287 if (toast_manager_) {
289 absl::StrFormat("Failed to initialize Git: %s", status.message()),
291 }
292 }
293 }
294 return;
295 }
296
297 // Show current commit
298 std::string current_hash = version_manager_->GetCurrentHash();
299 if (!current_hash.empty()) {
300 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Current:");
301 ImGui::SameLine();
302 ImGui::Text("%s", current_hash.substr(0, 7).c_str());
303 }
304
305 ImGui::Spacing();
306
307 // Create snapshot section
308 ImGui::Text("Create Snapshot:");
309 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
310 ImGui::InputTextWithHint("##snapshot_msg", "Snapshot message...",
312
313 if (ImGui::Button(ICON_MD_CAMERA_ALT " Create Snapshot",
314 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
315 std::string msg =
316 snapshot_message_[0] ? snapshot_message_ : "Manual snapshot";
317 auto result = version_manager_->CreateSnapshot(msg);
318 if (result.ok()) {
319 if (toast_manager_) {
321 absl::StrFormat("Snapshot created: %s", result->commit_hash),
323 }
324 snapshot_message_[0] = '\0';
325 history_dirty_ = true;
326 } else {
327 if (toast_manager_) {
329 absl::StrFormat("Snapshot failed: %s", result.status().message()),
331 }
332 }
333 }
334 if (ImGui::IsItemHovered()) {
335 ImGui::SetTooltip(
336 "Create a snapshot of your project (Git commit + ROM backup)");
337 }
338
339 // Show recent history
341}
342
345 return;
346 }
347
348 ImGui::Spacing();
349 if (ImGui::CollapsingHeader(ICON_MD_LIST " Recent Snapshots",
350 ImGuiTreeNodeFlags_DefaultOpen)) {
351 // Refresh history if needed
352 if (history_dirty_) {
354 history_dirty_ = false;
355 }
356
357 if (history_cache_.empty()) {
358 ImGui::TextDisabled("No snapshots yet");
359 } else {
360 for (const auto& entry : history_cache_) {
361 // Format: "hash message"
362 size_t space_pos = entry.find(' ');
363 std::string hash =
364 space_pos != std::string::npos ? entry.substr(0, 7) : entry;
365 std::string message =
366 space_pos != std::string::npos ? entry.substr(space_pos + 1) : "";
367
369 ImGui::SameLine();
370 ImGui::TextWrapped("%s", message.c_str());
371 }
372 }
373 }
374}
375
377 gui::ColoredTextF(gui::GetPrimaryVec4(), "%s Quick Actions", ICON_MD_BOLT);
378 ImGui::Spacing();
379
380 float button_width = ImGui::GetContentRegionAvail().x;
381
382 // Show unsaved indicator
383 if (project_dirty_) {
384 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
385 ImGui::TextColored(gui::ConvertColorToImVec4(theme.warning),
386 "%s Project has unsaved changes", ICON_MD_EDIT);
387 ImGui::Spacing();
388 }
389
390 if (ImGui::Button(ICON_MD_SAVE " Save Project", ImVec2(button_width, 0))) {
393 project_dirty_ = false;
394 }
395 }
396
397 ImGui::Spacing();
398
399 // Editable Code folder
400 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Code Folder:");
401 static char code_buffer[512] = {};
402 if (code_buffer[0] == '\0' && !project_->code_folder.empty()) {
403 strncpy(code_buffer, project_->code_folder.c_str(),
404 sizeof(code_buffer) - 1);
405 }
406 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 32);
407 if (ImGui::InputText("##code_folder", code_buffer, sizeof(code_buffer))) {
408 project_->code_folder = code_buffer;
409 project_dirty_ = true;
410 }
411 ImGui::SameLine();
412 if (ImGui::Button(ICON_MD_FOLDER_OPEN "##browse_code")) {
415 }
416 }
417 if (ImGui::IsItemHovered()) {
418 ImGui::SetTooltip("Browse for code folder");
419 }
420
421 // Editable Assets folder
422 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Assets Folder:");
423 static char assets_buffer[512] = {};
424 if (assets_buffer[0] == '\0' && !project_->assets_folder.empty()) {
425 strncpy(assets_buffer, project_->assets_folder.c_str(),
426 sizeof(assets_buffer) - 1);
427 }
428 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 32);
429 if (ImGui::InputText("##assets_folder", assets_buffer,
430 sizeof(assets_buffer))) {
431 project_->assets_folder = assets_buffer;
432 project_dirty_ = true;
433 }
434 ImGui::SameLine();
435 if (ImGui::Button(ICON_MD_FOLDER_OPEN "##browse_assets")) {
437 browse_folder_callback_("assets");
438 }
439 }
440 if (ImGui::IsItemHovered()) {
441 ImGui::SetTooltip("Browse for assets folder");
442 }
443
444 // Editable Build target
445 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Build Target:");
446 static char build_buffer[256] = {};
447 if (build_buffer[0] == '\0' && !project_->build_target.empty()) {
448 strncpy(build_buffer, project_->build_target.c_str(),
449 sizeof(build_buffer) - 1);
450 }
451 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
452 if (ImGui::InputText("##build_target", build_buffer, sizeof(build_buffer))) {
453 project_->build_target = build_buffer;
454 project_dirty_ = true;
455 }
456
457 // Build script
458 ImGui::TextColored(gui::GetTextSecondaryVec4(), "Build Script:");
459 static char script_buffer[512] = {};
460 if (script_buffer[0] == '\0' && !project_->build_script.empty()) {
461 strncpy(script_buffer, project_->build_script.c_str(),
462 sizeof(script_buffer) - 1);
463 }
464 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
465 if (ImGui::InputText("##build_script", script_buffer,
466 sizeof(script_buffer))) {
467 project_->build_script = script_buffer;
468 project_dirty_ = true;
469 }
470}
471
472} // namespace editor
473} // namespace yaze
auto size() const
Definition rom.h:138
bool dirty() const
Definition rom.h:133
bool is_loaded() const
Definition rom.h:132
auto title() const
Definition rom.h:137
std::vector< std::string > GetHistory(int limit=10) const
std::string GetCurrentHash() const
absl::StatusOr< SnapshotResult > CreateSnapshot(const std::string &message)
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static absl::StatusOr< std::filesystem::path > GetTempDirectory()
Get a temporary directory for the application.
static absl::StatusOr< std::filesystem::path > GetAppDataDirectory()
Get the user-specific application data directory for YAZE.
static std::string NormalizePathForDisplay(const std::filesystem::path &path)
Normalize path separators for display.
#define YAZE_VERSION_STRING
#define ICON_MD_FOLDER_OPEN
Definition icons.h:813
#define ICON_MD_MEMORY
Definition icons.h:1195
#define ICON_MD_STORAGE
Definition icons.h:1865
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_FOLDER_SPECIAL
Definition icons.h:815
#define ICON_MD_CAMERA_ALT
Definition icons.h:355
#define ICON_MD_SWAP_HORIZ
Definition icons.h:1896
#define ICON_MD_REFRESH
Definition icons.h:1572
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_ADD
Definition icons.h:86
#define ICON_MD_BOLT
Definition icons.h:282
#define ICON_MD_SAVE
Definition icons.h:1644
#define ICON_MD_HISTORY
Definition icons.h:946
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
void ColoredText(const char *text, const ImVec4 &color)
ImVec4 GetPrimaryVec4()
ImVec4 GetTextSecondaryVec4()
void ColoredTextF(const ImVec4 &color, const char *fmt,...)
ProjectMetadata metadata
Definition project.h:122
std::string assets_folder
Definition project.h:135