yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
panel_admission.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <map>
5#include <set>
6
7#include "app/editor/editor.h"
11
12namespace yaze {
13namespace editor {
14
15namespace {
16
17// Keep entries grouped by editor, in the order of the editor's default preset.
18// A reason names the workflow the window serves and why no existing surface
19// hosts it. "Current default" entries record windows that were already visible
20// on first open when the admission policy landed; the audit document
21// (docs/internal/plans/panel-admission-audit-2026-09-29.md) tracks which of
22// them are candidates for demotion.
23const std::vector<PanelAdmissionEntry> kFirstClassAllowlist = {
24 // Overworld
25 {"overworld.canvas", "Primary map canvas; the editor's work surface."},
26 {"overworld.tile16_selector",
27 "Tile16 palette the canvas paints from; docked beside the canvas in the "
28 "Overworld Default preset."},
29 {"overworld.properties",
30 "Current default: selected-map properties in the Overworld Default "
31 "preset."},
32 {"overworld.area_graphics",
33 "Reviewed Overworld default: selected-area graphics beside the map; "
34 "shares the right-bottom dock with map properties instead of covering "
35 "the canvas with a bottom strip."},
36
37 // Dungeon
38 {"dungeon.workbench",
39 "Single default dungeon surface; hosts the room browser, canvas, and "
40 "inspector tools."},
41
42 // Graphics
43 {"graphics.sheet_browser_v2",
44 "Sheet list that selects what the pixel editor edits."},
45 {"graphics.pixel_editor", "Primary graphics sheet editing surface."},
46
47 // Palette
48 {"palette.control_panel",
49 "Single Palette workspace; hosts group navigation and color editing."},
50
51 // Sprite
52 {"sprite.vanilla_editor", "Primary sprite editing surface."},
53
54 // Screen
55 {"screen.dungeon_maps",
56 "Primary surface of the Screen editor's default preset."},
57 {"screen.title_screen",
58 "Opened by ScreenEditor::Initialize, so it is what actually shows on "
59 "first open (the preset lists dungeon_maps); resolve in the audit doc."},
60
61 // Music
62 {"music.song_browser", "Song list that selects what the tracker edits."},
63 {"music.tracker", "Playback transport for the Music Default preset."},
64 {"music.piano_roll", "Primary music editing surface."},
65
66 // Message
67 {"message.message_list",
68 "Message list that selects what the editor edits."},
69 {"message.message_editor", "Primary message text editing surface."},
70
71 // Assembly
72 {"assembly.code_editor", "Primary assembly source editing surface."},
73 {"assembly.file_browser", "Project file tree that opens sources."},
74 {"assembly.disassembly",
75 "Current default: disassembly view in the Assembly Default preset."},
76 {"assembly.toolbar",
77 "Current default: registered visible. Its Open Folder and Save buttons "
78 "are the only quick actions when no project code folder is set."},
79
80 // Planning
81 {"planning.overview",
82 "Primary planning surface: goal, milestones, route strip and decisions."},
83 {"planning.route",
84 "Route step and item editor; the Overview's route strip selects into it."},
85
86 // Shell
87 {"dashboard.main", "Editor picker shown at startup and on Ctrl+E."},
88
89 // Emulator and memory
90 {"emulator.ppu_viewer", "Primary emulator output surface."},
91 {"memory.hex_editor", "Primary surface of the Memory editor."},
92
93 // Agent
94 {"agent.chat", "Primary agent conversation surface."},
95 {"agent.configuration",
96 "Current default: agent provider setup in the Agent preset."},
97 {"agent.status", "Current default: agent status in the Agent preset."},
98};
99
100// panel-admission: deferred-begin
101// The Overworld panels are Codex-coordinated: src/app/editor/overworld/* is
102// frozen for edits until scawful approves, and Codex has in-flight branches
103// there (codex/overworld-large-selection-preview, codex/terrain-catalog).
104// Proposals for each window are in the audit document.
105constexpr char kOverworldFrozen[] =
106 "Overworld panels are Codex-coordinated; src/app/editor/overworld/* needs "
107 "scawful's approval before edits.";
108const std::vector<PanelAdmissionEntry> kDeferredDeclarations = {
109 {"overworld.canvas", kOverworldFrozen},
110 {"overworld.tile16_selector", kOverworldFrozen},
111 {"overworld.properties", kOverworldFrozen},
112 {"overworld.tile16_editor", kOverworldFrozen},
113 {"overworld.tile8_selector", kOverworldFrozen},
114 {"overworld.area_graphics", kOverworldFrozen},
115 {"overworld.scratch", kOverworldFrozen},
116 {"overworld.gfx_groups", kOverworldFrozen},
117 {"overworld.usage_stats", kOverworldFrozen},
118 {"overworld.item_list", kOverworldFrozen},
119 {"overworld.v3_settings", kOverworldFrozen},
120 {"overworld.debug", kOverworldFrozen},
121 {"overworld.entity_workbench", kOverworldFrozen},
122};
123// panel-admission: deferred-end
124
125// Fix texts carry no window id so identical failures group into one block.
126constexpr char kUndeclaredFix[] =
127 "Override GetPresentationPolicy() in the panel class (or pass a policy to "
128 "RegisterWindowContent) and return one of: "
129 "WindowPresentationPolicy::EmbeddedTool().WithHost(\"<surface that embeds "
130 "it>\") for a tool that acts on another surface's selection (inspector "
131 "section, tool tab, canvas context-menu popup); OptionalPopOut() for a "
132 "standalone window users choose to open; "
133 "Diagnostic().WithHost(\"Command palette\") for developer or validation "
134 "tooling. CoreWorkspace() is only for an editor's primary work surface and "
135 "needs a FirstClassPanelAllowlist() entry. See \"Panel admission\" in "
136 "docs/internal/architecture/editor-ui-module-pattern.md.";
137
138constexpr char kCoreFix[] =
139 "Core is reserved for an editor's primary work surface. Either add the "
140 "window id to FirstClassPanelAllowlist() in "
141 "src/app/editor/system/workspace/panel_admission.cc with the workflow it "
142 "serves and why no inspector, tool tab, or context-menu popup can host it, "
143 "or pick EmbeddedTool().WithHost(...) or OptionalPopOut().";
144
145constexpr char kDefaultVisibleFix[] =
146 "Hide it by default: remove IsVisibleByDefault()/visible_by_default and "
147 "its LayoutPresets default_visible_panels entry so it opens from its host "
148 "or the command palette. If it really is a primary surface, add the window "
149 "id to FirstClassPanelAllowlist() in "
150 "src/app/editor/system/workspace/panel_admission.cc with a reason.";
151
152constexpr char kHostFix[] =
153 "Add .WithHost(\"<surface>\") to the policy, for example "
154 "EmbeddedTool().WithHost(\"Dungeon Workbench inspector\") or "
155 "Diagnostic().WithHost(\"Command palette\"). The host is where users find "
156 "the window now that it is not in the window browser.";
157
158} // namespace
159
160const std::vector<PanelAdmissionEntry>& FirstClassPanelAllowlist() {
161 return kFirstClassAllowlist;
162}
163
165 const std::string& window_id) {
166 for (const auto& entry : kFirstClassAllowlist) {
167 if (window_id == entry.window_id) {
168 return &entry;
169 }
170 }
171 return nullptr;
172}
173
174const std::vector<PanelAdmissionEntry>& DeferredPolicyDeclarations() {
175 return kDeferredDeclarations;
176}
177
178bool IsPolicyDeclarationDeferred(const std::string& window_id) {
179 for (const auto& entry : kDeferredDeclarations) {
180 if (window_id == entry.window_id) {
181 return true;
182 }
183 }
184 return false;
185}
186
188 return "[" + category + "] " + window_id + ": " + problem +
189 "\n Fix: " + fix;
190}
191
192std::vector<std::string> CollectDefaultVisibleWindowIds(
193 const WorkspaceWindowManager& window_manager, size_t session_id) {
194 const std::vector<std::string> registered =
195 window_manager.GetWindowsInSession(session_id);
196 std::set<std::string> registered_ids(registered.begin(), registered.end());
197 std::set<std::string> visible;
198
199 for (const auto& window_id : registered) {
200 const WindowDescriptor* descriptor =
201 window_manager.GetWindowDescriptor(session_id, window_id);
202 if (descriptor != nullptr && descriptor->visibility_flag != nullptr &&
203 *descriptor->visibility_flag) {
204 visible.insert(window_id);
205 }
206 }
207
208 constexpr EditorType kPresetEditors[] = {
214 };
215 for (EditorType type : kPresetEditors) {
216 for (const auto& window_id : LayoutPresets::GetDefaultPanels(type)) {
217 if (registered_ids.count(window_id) != 0) {
218 visible.insert(window_id);
219 }
220 }
221 }
222 return std::vector<std::string>(visible.begin(), visible.end());
223}
224
225std::vector<PanelAdmissionFinding> AuditPanelAdmission(
226 const WorkspaceWindowManager& window_manager, size_t session_id) {
227 std::vector<PanelAdmissionFinding> findings;
228
229 const std::vector<std::string> default_visible =
230 CollectDefaultVisibleWindowIds(window_manager, session_id);
231 const std::set<std::string> default_visible_set(default_visible.begin(),
232 default_visible.end());
233
234 std::vector<std::string> window_ids =
235 window_manager.GetWindowsInSession(session_id);
236 std::sort(window_ids.begin(), window_ids.end());
237 window_ids.erase(std::unique(window_ids.begin(), window_ids.end()),
238 window_ids.end());
239
240 for (const auto& window_id : window_ids) {
241 const WindowDescriptor* descriptor =
242 window_manager.GetWindowDescriptor(session_id, window_id);
243 if (descriptor == nullptr) {
244 continue;
245 }
246 const WindowPresentationPolicy& policy = descriptor->presentation;
247 const bool admitted = FindFirstClassPanelEntry(window_id) != nullptr;
248 auto add = [&](std::string problem, std::string fix) {
249 findings.push_back({window_id, descriptor->category, std::move(problem),
250 std::move(fix)});
251 };
252
253 if (!policy.declared && !IsPolicyDeclarationDeferred(window_id)) {
254 add("declares no presentation policy.", kUndeclaredFix);
255 continue;
256 }
257
258 if (policy.role == WindowPresentationRole::kCoreWorkspace && !admitted) {
259 add("declares CoreWorkspace but is not in FirstClassPanelAllowlist().",
260 kCoreFix);
261 }
262
263 if (default_visible_set.count(window_id) != 0 && !admitted) {
264 add(std::string("is visible by default (IsVisibleByDefault(), "
265 "visible_by_default, or a LayoutPresets default) with "
266 "role ") +
268 " but is not in FirstClassPanelAllowlist().",
269 kDefaultVisibleFix);
270 }
271
272 const bool needs_host =
275 if (needs_host && policy.host.empty()) {
276 add(std::string(WindowPresentationRoleName(policy.role)) +
277 " names no host surface.",
278 kHostFix);
279 }
280 }
281 return findings;
282}
283
285 const std::vector<PanelAdmissionFinding>& findings) {
286 // One block per distinct fix so 60 identical failures do not repeat the
287 // instructions 60 times.
288 std::vector<std::string> fix_order;
289 std::map<std::string, std::vector<const PanelAdmissionFinding*>> groups;
290 for (const auto& finding : findings) {
291 auto [it, inserted] = groups.try_emplace(finding.fix);
292 if (inserted) {
293 fix_order.push_back(finding.fix);
294 }
295 it->second.push_back(&finding);
296 }
297
298 std::string out;
299 for (const auto& fix : fix_order) {
300 for (const PanelAdmissionFinding* finding : groups[fix]) {
301 out += " [" + finding->category + "] " + finding->window_id + ": " +
302 finding->problem + "\n";
303 }
304 out += " Fix: " + fix + "\n\n";
305 }
306 return out;
307}
308
309} // namespace editor
310} // namespace yaze
static std::vector< std::string > GetDefaultPanels(EditorType type)
Get default visible panels for an editor.
Central registry for all editor cards with session awareness and dependency injection.
const WindowDescriptor * GetWindowDescriptor(size_t session_id, const std::string &base_window_id) const
std::vector< std::string > GetWindowsInSession(size_t session_id) const
const std::vector< PanelAdmissionEntry > kFirstClassAllowlist
const std::vector< PanelAdmissionEntry > kDeferredDeclarations
const PanelAdmissionEntry * FindFirstClassPanelEntry(const std::string &window_id)
Returns the allowlist entry for a base window id, or nullptr.
std::string FormatPanelAdmissionFindings(const std::vector< PanelAdmissionFinding > &findings)
Joins findings into one message for test output.
const std::vector< PanelAdmissionEntry > & FirstClassPanelAllowlist()
bool IsPolicyDeclarationDeferred(const std::string &window_id)
std::vector< PanelAdmissionFinding > AuditPanelAdmission(const WorkspaceWindowManager &window_manager, size_t session_id)
Audits every window registered for session_id. Empty means admitted.
std::vector< std::string > CollectDefaultVisibleWindowIds(const WorkspaceWindowManager &window_manager, size_t session_id)
const char * WindowPresentationRoleName(WindowPresentationRole role)
const std::vector< PanelAdmissionEntry > & DeferredPolicyDeclarations()
Admission rules for registered workspace windows.
One audit failure. fix tells the panel author what to change.
Metadata for a dockable editor window (formerly PanelInfo)