yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
annotation_overlay_panel.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
2#define YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
3
4#include <algorithm>
5#include <cstdio>
6#include <filesystem>
7#include <fstream>
8#include <string>
9#include <vector>
10
13#include "app/gui/core/icons.h"
14#include "core/project.h"
15#include "imgui/imgui.h"
16#include "nlohmann/json.hpp"
17
18namespace yaze::editor {
19
23enum class AnnotationPriority : int {
24 kNote = 0,
25 kBug = 1,
26 kBlocker = 2,
27};
28
32struct AnnotationEntry {
33 int room_id = 0;
34 std::string text;
36 std::string category; // "design", "test", "bug", etc.
37 std::string created_at;
38};
39
53class AnnotationOverlayPanel : public WindowContent {
54 public:
55 std::string GetId() const override { return "oracle.annotations"; }
56 std::string GetDisplayName() const override { return "Annotations"; }
57 std::string GetIcon() const override { return ICON_MD_NOTES; }
58 std::string GetEditorCategory() const override { return "Agent"; }
59 std::string GetWorkflowGroup() const override { return "Planning"; }
60 std::string GetWorkflowDescription() const override {
61 return "Review and edit room-level project annotations";
62 }
66
67 void SetAnnotationsPath(const std::string& path) {
68 annotations_path_ = path;
70 }
71
72 void Draw(bool* /*p_open*/) override {
73 // Lazily discover annotations path from project context
74 if (annotations_path_.empty()) {
75 if (auto* project = ContentRegistry::Context::current_project()) {
76 if (!project->code_folder.empty()) {
77 namespace fs = std::filesystem;
78 fs::path candidate =
79 fs::path(project->GetAbsolutePath(project->code_folder)) /
80 "Docs" / "Dev" / "Planning" / "annotations.json";
81 SetAnnotationsPath(candidate.string());
82 }
83 }
84 }
85
86 // Filter controls
87 ImGui::Text("Room filter:");
88 ImGui::SameLine();
89 ImGui::InputInt("##room_filter", &filter_room_id_);
90 ImGui::SameLine();
91 if (ImGui::Button("All")) filter_room_id_ = -1;
92
93 ImGui::SameLine();
94 const char* priorities[] = {"All", "Note", "Bug", "Blocker"};
95 ImGui::Combo("Priority", &filter_priority_, priorities, 4);
96
97 ImGui::Separator();
98
99 // Annotation list
100 ImGui::BeginChild("annotation_list", ImVec2(0, -120),
101 ImGuiChildFlags_Borders);
102
103 for (size_t i = 0; i < annotations_.size(); ++i) {
104 const auto& ann = annotations_[i];
105
106 // Apply filters
107 if (filter_room_id_ >= 0 && ann.room_id != filter_room_id_) continue;
108 if (filter_priority_ > 0 &&
109 static_cast<int>(ann.priority) != (filter_priority_ - 1))
110 continue;
111
112 ImU32 dot_color = GetPriorityColor(ann.priority);
113 ImVec2 cursor = ImGui::GetCursorScreenPos();
114 ImGui::GetWindowDrawList()->AddCircleFilled(
115 ImVec2(cursor.x + 6, cursor.y + 10), 5.0f, dot_color);
116 ImGui::Dummy(ImVec2(16, 0));
117 ImGui::SameLine();
118
119 char label[128];
120 snprintf(label, sizeof(label), "Room 0x%02X: %s##ann_%zu", ann.room_id,
121 ann.text.c_str(), i);
122
123 if (ImGui::Selectable(label, selected_index_ == static_cast<int>(i))) {
124 selected_index_ = static_cast<int>(i);
125 // Copy to edit buffer
126 edit_room_ = ann.room_id;
127 snprintf(edit_text_, sizeof(edit_text_), "%s", ann.text.c_str());
128 edit_priority_ = static_cast<int>(ann.priority);
129 snprintf(edit_category_, sizeof(edit_category_), "%s",
130 ann.category.c_str());
131 }
132 }
133
134 ImGui::EndChild();
135
136 ImGui::Separator();
137
138 // Edit / Add form
139 ImGui::Text("Room:");
140 ImGui::SameLine();
141 ImGui::SetNextItemWidth(80);
142 ImGui::InputInt("##edit_room", &edit_room_);
143
144 ImGui::SameLine();
145 ImGui::Text("Priority:");
146 ImGui::SameLine();
147 ImGui::SetNextItemWidth(80);
148 const char* pri_names[] = {"Note", "Bug", "Blocker"};
149 ImGui::Combo("##edit_priority", &edit_priority_, pri_names, 3);
150
151 ImGui::InputText("Text", edit_text_, sizeof(edit_text_));
152 ImGui::InputText("Category", edit_category_, sizeof(edit_category_));
153
154 if (ImGui::Button("Add")) {
155 AnnotationEntry entry;
156 entry.room_id = edit_room_;
157 entry.text = edit_text_;
158 entry.priority = static_cast<AnnotationPriority>(edit_priority_);
159 entry.category = edit_category_;
160 annotations_.push_back(entry);
162 }
163
164 ImGui::SameLine();
165 if (selected_index_ >= 0 &&
166 selected_index_ < static_cast<int>(annotations_.size())) {
167 if (ImGui::Button("Update")) {
168 auto& ann = annotations_[selected_index_];
169 ann.room_id = edit_room_;
170 ann.text = edit_text_;
171 ann.priority = static_cast<AnnotationPriority>(edit_priority_);
172 ann.category = edit_category_;
174 }
175 ImGui::SameLine();
176 if (ImGui::Button("Delete")) {
178 selected_index_ = -1;
180 }
181 }
182 }
183
184 // ─── Public API for DungeonMapPanel integration ───────────────
185
189 std::vector<const AnnotationEntry*> GetAnnotationsForRoom(
190 int room_id) const {
191 std::vector<const AnnotationEntry*> result;
192 for (const auto& ann : annotations_) {
193 if (ann.room_id == room_id) {
194 result.push_back(&ann);
195 }
196 }
197 return result;
198 }
199
204 int GetMaxPriorityForRoom(int room_id) const {
205 int max_pri = -1;
206 for (const auto& ann : annotations_) {
207 if (ann.room_id == room_id) {
208 max_pri = std::max(max_pri, static_cast<int>(ann.priority));
209 }
210 }
211 return max_pri;
212 }
213
214 static ImU32 GetPriorityColor(AnnotationPriority priority) {
215 switch (priority) {
217 return IM_COL32(220, 50, 50, 255); // Red
219 return IM_COL32(220, 150, 30, 255); // Orange
221 default:
222 return IM_COL32(60, 120, 220, 255); // Blue
223 }
224 }
225
226 private:
228 annotations_.clear();
229 if (annotations_path_.empty()) return;
230
231 std::ifstream file(annotations_path_);
232 if (!file.is_open()) return;
233
234 try {
235 nlohmann::json root;
236 file >> root;
237
238 if (root.contains("annotations") && root["annotations"].is_array()) {
239 for (const auto& item : root["annotations"]) {
240 AnnotationEntry entry;
241 entry.room_id = item.value("room_id", 0);
242 entry.text = item.value("text", "");
243 entry.priority =
244 static_cast<AnnotationPriority>(item.value("priority", 0));
245 entry.category = item.value("category", "");
246 entry.created_at = item.value("created_at", "");
247 annotations_.push_back(std::move(entry));
248 }
249 }
250 } catch (...) {
251 // Silently ignore parse errors
252 }
253 }
254
256 if (annotations_path_.empty()) return;
257
258 nlohmann::json root;
259 nlohmann::json arr = nlohmann::json::array();
260
261 for (const auto& ann : annotations_) {
262 nlohmann::json item;
263 item["room_id"] = ann.room_id;
264 item["text"] = ann.text;
265 item["priority"] = static_cast<int>(ann.priority);
266 item["category"] = ann.category;
267 item["created_at"] = ann.created_at;
268 arr.push_back(item);
269 }
270
271 root["annotations"] = arr;
272
273 std::ofstream file(annotations_path_);
274 if (file.is_open()) {
275 file << root.dump(2) << std::endl;
276 }
277 }
278
279 std::string annotations_path_;
280 std::vector<AnnotationEntry> annotations_;
281
282 // Filter state
283 int filter_room_id_ = -1;
284 int filter_priority_ = 0;
285
286 // Edit state
287 int selected_index_ = -1;
288 int edit_room_ = 0;
289 char edit_text_[256] = {0};
290 int edit_priority_ = 0;
291 char edit_category_[64] = {0};
292};
293
294} // namespace yaze::editor
295
296#endif // YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
void SetAnnotationsPath(const std::string &path)
void Draw(bool *) override
Draw the panel content.
WindowLifecycle GetWindowLifecycle() const override
Get the lifecycle category for this window.
std::string GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
std::vector< const AnnotationEntry * > GetAnnotationsForRoom(int room_id) const
Get annotations for a specific room.
int GetMaxPriorityForRoom(int room_id) const
Get the highest priority for a room (for dot color). Returns -1 if no annotations.
std::string GetWorkflowDescription() const override
Optional workflow description for menus/command palette.
std::string GetEditorCategory() const override
Editor category this panel belongs to.
std::vector< AnnotationEntry > annotations_
static ImU32 GetPriorityColor(AnnotationPriority priority)
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
std::string GetId() const override
Unique identifier for this panel.
std::string GetIcon() const override
Material Design icon for this panel.
#define ICON_MD_NOTES
Definition icons.h:1332
::yaze::project::YazeProject * current_project()
Get the current project instance.
Editors are the view controllers for the application.
AnnotationPriority
Annotation priority levels.
WindowLifecycle
Defines lifecycle behavior for editor windows.
@ CrossEditor
User can pin to persist across editors.
A room-level annotation entry.