JARVIS-UI
Update 2.160
6/20/2026
v2.160.0.0MINORJarvis-UI
Phases A–C: ModuleRegistry + Workspace Ready (§17) + flyout-free Icon Rail (§16) (Jarvis-UI)
- Delivery of Phases A–C from
docs/specs/EDITOR_UI_OVERHAUL_PLAN.md§16–§17 (Halyson decisions 2026-06-20), perdocs/ai/PROMPT_UI_GLM_REMAINING_BACKLOG_2026-06-20.md. Scope: navigation architecture + data-driven catalog + iconography — writers/save bytes/hooks/FfxLib/RT0/RT2/SGM/SPIRA FORGE untouched; the 68MenuItem_*handlers are not refactored (only called). Phase A — ModuleRegistry (single source of truth):ModuleCatalogPolicy.csevolves from a documentation stub into the authoritative catalog — newpublic static class ModuleRegistrywithIReadOnlyList<ModuleCatalogEntry> Allpopulated with 41 entries (Home + 10 Core Authoring + 5 Maps + 7 Live + 16 Extras + 2 Wave-1), one per rail-routed module.ModuleCatalogEntrygainsMode/Notes/Scope/Cluster(aModuleClusterenum) on top of the originalId/Title/Description/IconKey/RequiresProject. Copy is faithful to the literals of eachSetModule(...)inMain_Window.axaml.cs(not reinvented). Doc-comment policy: a new rail-routedMenuItem_*= append to the registry (review blocker). Phase A — Iconography:StudioIcons.axamlexpanded from 14 → 54 icons (<StreamGeometry>Lucide-like, viewBox 24×24) — one dedicated metaphor per module (sword+horns=monster, floppy=save, skull=enemy design, hex grid=sphere grid, etc.). A consistency check (script) confirms all 41 registryIconKeys resolve inStudioIcons. Phase B — §17 Workspace Ready:MainDashboard_Control.axamldrops the 6 hardcoded tiles and becomes anItemsControl ItemsSource="{Binding Modules}"with aDataTemplate(icon + Title + full Description + Mode pill).Main_DataModelgainspublic IReadOnlyList<ModuleCatalogEntry> Modules => ModuleRegistry.All. Two new converters inFFXProjectEditor/Converters/:IconKeyToGeometryConverter(resolves a string IconKey → Geometry by walkingResources+MergedDictionariesviaTryGetResource(key, ActualThemeVariant, out _)— required because{DynamicResource {Binding IconKey}}binding does not work forPathIcon.Datain Avalonia) andRequiresProjectEnabledConverter(IMultiValueConverterRequiresProject × IsProjectLoaded →IsEnabled). Hero + empty-state CTA + Workspace status card preserved (§17.4). Phase C — §16 flyout-free Icon Rail: removed the 5Button.Flyout/MenuFlyoutfromIconRailCol(XAML L156–239) — replaced by<StackPanel Name="RailStack">populated in code-behind byBuildIconRail()(onerailIconbutton per entry, grouped by cluster with a 1pxBorderas separator).Popup#RailDrawer(narrow) swaps its textualButton Classes="nav"list for a<WrapPanel Name="RailDrawerGrid">(icon grid). A single handlerButton_RailModule_Click(sender, e)readsTag=Id→Dispatch(id).RefreshRailProjectGates()re-applies theRequiresProjectgate whenProject_Service.IsProjectLoadedchanges. Phase C — generic Dispatch:OnQuickLaunchRequested(6 hardcoded keys: monster/kernel/sphere/save/extras/home) is replaced byDispatch(string moduleId)with a switch over all 41 Ids → existingMenuItem_*handler. Rail, drawer and dashboard now share the same path. Acceptance: Release build 0 errors (402 preexisting warnings); 0MenuFlyoutinIconRailCol; 0 hardcoded tile in the dashboard; 41 icons rendering in the rail + 41 cards in the home with a dedicated icon + full description; cards disabled whenRequiresProject && !IsProjectLoaded. Visual bug fixed: icons initially failed to render becauseApplication.Current.Resources.TryGetValue()in Avalonia 11 does not walkMergedDictionaries— fixed via a sharedResolveGeometry()helper that iterates the merged dicts withTryGetResource
v2.159.6.1
v2.160.1.0PATCHJarvis-UI
Phase D1 closed: `IRestorableModule` interface + `NavigationSnapshot` refactor (Jarvis-UI)
- First commit of Phase D (
docs/ai/PROMPT_GLM_PHASE_D_TO_F_2026-06-20.md§3.D1). Scope: navigation architecture (restorable back/forward) — writers/save bytes/hooks/FfxLib/RT0/RT2/SGM/SPIRA FORGE untouched. New contractFFXProjectEditor/Modules/Common/IRestorableModule.cs:CaptureState()→Dictionary<string,object?>?(null = stateless) andRestoreState(state)(idempotent, null-safe). Before D1,Main_Windowdid pattern-matching on concrete types (MonEditorSelector_Control/KernelCommands_Control) to capture state — it doesn't scale to ~38 modules. NowMain_Windowasks the control if it isIRestorableModule.NavigationSnapshotrefactor: becomesrecord struct (string ModuleId, Dictionary<string,object?>? State); theNavigationSurfaceKindenum is removed. New fieldprivate string _currentModuleId = "home"set at the start ofDispatch(string moduleId).CaptureCurrentNavigationSnapshot()is now generic viaContentFrame.Content is IRestorableModule;IsSameNavigationSurface()compares onlyModuleId;RestoreNavigationSnapshot()callsDispatch(snapshot.ModuleId)+restorable.RestoreState(snapshot.State). 3 broken call sites fixed (still referencedNavigationSurfaceKind):MenuItem_MonsterMagic1/2(L560/568) →_currentModuleId = "battle-commands-hub"+new NavigationSnapshot("battle-commands-hub")(same pattern as the already-migratedMenuItem_Commands/MenuItem_Items);NavigateToMonsterEditor(L1549) →new NavigationSnapshot("monster-editor"). Honesty: back/forward keeps working in the 3 originals (Home/Monster/KernelCommands), but Monster/KernelCommands will only be fully restorable once their controls implementIRestorableModule(D2/D4). Until then, the snapshot is created butStatecomes backnull(returns to the module, doesn't restore sub-selection). Release build 0 errors (402 preexisting warnings)
v2.160.0.0
v2.160.2.0PATCHJarvis-UI
Phase D2: `IRestorableModule` in the 7 easy editors (Jarvis-UI)
- Second commit of Phase D (
docs/ai/PROMPT_GLM_PHASE_D_TO_F_2026-06-20.md§3.D2). Scope: restorable back/forward in 7 Core Authoring editors — writers/save bytes/hooks/FfxLib/RT0/RT2/SGM/SPIRA FORGE untouched. ImplementsIRestorableModule(defined in D1) in 7 controls:TreasureEditor_Control,AutoAbilityEditor_Control,PlayerGrowthEditor_Control,CtbBaseEditor_Control,KeyItemEditor_Control,FormationEditor_Control,BukiGetTreasureCatalog_Control. EachCaptureState()capturesfilterText(string) +selectedIndex(int? derived from the selected item in theDisplayed*collection);RestoreState(state)restores the filter (which re-runsApplyFilterviaOnFilterTextChanged) and re-selects the row by index. Per-editor mapping: Treasure (SelectedTreasure/DisplayedTreasures), AutoAbility (SelectedAbility/DisplayedAbilities), PlayerGrowth (SelectedCharacter/DisplayedCharacters), CtbBase (SelectedRow/DisplayedRows), KeyItem (SelectedItem/DisplayedItems), Formation (SelectedBattle/Battles— noDisplayed*), BukiGetTreasureCatalog (SelectedRow/DisplayedRows). Honesty: Monster Editor + the 7 editors above are now fully restorable via back/forward (return to the exact filter/selection). KernelCommands + the hubs (BattleCommands/Items/etc.) come in D3 (SubTabHub). Release build 0 errors (402 preexisting warnings)
v2.160.1.0
v2.160.3.0PATCHJarvis-UI
Phase D3: `IRestorableModule` in `SubTabHub_Control` (covers 9 hubs) (Jarvis-UI)
- Third commit of Phase D (
docs/ai/PROMPT_GLM_PHASE_D_TO_F_2026-06-20.md§3.D3). Scope: restorable back/forward in everySubTabHub-based hub — writers/save bytes/hooks/FfxLib/RT0/RT2/SGM/SPIRA FORGE untouched.SubTabHub_Control.axaml.csimplementsIRestorableModule: newint _activeTabIndexfield tracked inSelectTab(index);CaptureState()returns{["activeTab"] = _activeTabIndex};RestoreState(state)callsSelectTab(idx)(which re-applies pill/active/hosted-content normally). Automatic coverage of 9 hubs: BattleCommandsHub, ItemsHub, SphereGridHub, TextHub, EnemyDesignHub, CustomizationsHub, StatsHub, EncountersHub, Blitzball — they all useSubTabHub_ControlviaAddTab(...), so they all gain active sub-tab restoration for free in a single change. Honesty: the internal content of each sub-tab (the hosted editor's filter/selection) is the hosted control's responsibility — if it also implementsIRestorableModule(D2 covered Treasure/KeyItem/etc.),Main_Windowchains the restore in sequence. Release build 0 errors (402 preexisting warnings)
v2.160.2.0
v2.160.4.0PATCHJarvis-UI
Phase D4: `IRestorableModule` in all remaining controls via default interface methods (Jarvis-UI)
- Fourth commit of Phase D (
docs/ai/PROMPT_GLM_PHASE_D_TO_F_2026-06-20.md§3.D4). Scope: declare the interface in all remaining*_Control— writers/save bytes/hooks/FfxLib/RT0/RT2/SGM/SPIRA FORGE untouched.IRestorableModule.csgains default interface methods:CaptureState() => nullandRestoreState(state) { }(stateless/no-op by default). This way, controls with no meaningful state (read-only explorers, hubs, runtime labs, debug, trackers, 16 Extras, Wave-1, sub-controls) only need to declare, IRestorableModulein their signature —Main_Windowcan now doContentFrame.Content is IRestorableModuleon any module without distinguishing type. Automated sweep viawork/_irestable_stateless_sweep_20260620.ps1: 65 controls changed (addsusing FFXProjectEditor.Modules.Common;+: UserControl, IRestorableModule), 9 skipped (the 8 already implemented in D1/D2/D3 + discontinuedMonsterAiEditor2_Control). Honesty: the 3 browsers with a filter (MagicDllBrowser/RuntimeDllManager/Ps3MagicBrowser) inherited the stateless default because theirFilterTextlives in private DataModels, not exposed on the control — real filter capture is left as a smaller backlog item. With D1+D2+D3+D4, all ~72 controls now declareIRestorableModule; those with state (8 editors + 9 hubs) do explicit override, the rest is stateless. Release build 0 errors (402 preexisting warnings)
v2.160.3.0