Skip to content

Commit d8f740e

Browse files
HanSur94claude
andauthored
Phase 1034: Wiki Browser (project-wide in-app help system) (#159)
* docs(1034-02): add wiki/Companion-Overview.md root help page - Hand-written entry page opened by the new Wiki button on the FastSenseCompanion main toolbar (Plan 06 task 6.1 will set 'OpenTo','Companion-Overview'). - Describes the three-pane layout, top toolbar, log strip, dashboard opening flow, and Live mode semantics. - Cross-links to Tag-Status-Table, Event-Viewer, Live-Log, Dashboard-Info-vs-Wiki, Home using bare-page-name links the Plan 04 Wiki Browser will resolve as in-window navigation. - No AUTO-GENERATED marker (Phase 1034 D-04 hand-written constraint). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(1034-01): scaffold WikiPageIndex pure-logic class shell - New libs/Help/ directory for Phase 1034 (Wiki Browser System 2) - libs/Help/WikiPageIndex.m: classdef with five static method stubs (listPages, buildToc, readPage, search, collidesWithGenerator) plus two private helpers (extractH1_, isAutoGenerated_) - Full Doxygen-style header documenting each method's contract per CONTEXT.md D-08/D-09 (TOC + full-text substring search) and D-05 (generator collision guard) - All methods are methods(Static) so the Plan 04 UI layer can call without instantiating, mirroring MarkdownRenderer.render - Bodies return sensible empty defaults — Task 1.2 fills them * docs(1034-02): add Tag-Status-Table, Event-Viewer, Live-Log wiki pages Three hand-written wiki pages, one per Companion surface that gets a Wiki button in Phase 1034: - wiki/Tag-Status-Table.md — user manual for TagStatusTableWindow. Documents the 12 columns, chip filters (AND across groups, OR within), the dual refresh paths (push-on-write + window-owned 1s timer), and the Pause polling toggle. - wiki/Event-Viewer.md — user manual for CompanionEventViewer plus the detached EventsLogPane (both will open this page in Plan 06/07). Documents the Gantt/Table view switch, severity tri-toggle, click / double-click behaviour, and the relationship to the Live log. - wiki/Live-Log.md — user manual for the LiveLogPane (inline + detached). Documents the 4 columns (Time/Tag/Delta/Latest), the 500-row buffer cap, the FastSenseCompanion.scanLiveTagUpdates_ boundary (per-tag cursor state lives upstream, not in the pane), the filter, and the Live-mode dependency. All three: exactly one H1, no AUTO-GENERATED marker, cross-doc links via bare page names (Companion-Overview / Tag-Status-Table / Event-Viewer / Live-Log) so Plan 04's WikiBrowser resolves them in-window. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(1034-02): add Dashboard-Info-vs-Wiki.md explaining the two systems Hand-written page that disambiguates the two parallel help systems for dashboard authors: - System 1: DashboardEngine.InfoFile (modal, per-dashboard, scoped) + DashboardWidget.Description (tooltip-only). - System 2: Wiki Browser (non-modal, project-wide, with TOC + search + back/forward), opened from the new Wiki button on Companion windows. Includes a coexistence matrix showing which surfaces carry which buttons, plus authoring guidance pointing out the PAGE_MAP collision hazard for new hand-written wiki pages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(1034-03): complete install-path plan - Add SUMMARY.md for Plan 1034-03 (libs/Help addpath in install.m) - Record decision in STATE.md (Decisions Phase 1034) - Update ROADMAP.md plan-progress row for phase 1034 - Note: Task 3.1 work (install.m diff) landed in commit 95eb030 alongside Plan 1034-02 due to parallel-execution git-index race; SUMMARY.md documents the attribution. Functional outcome verified via `which WikiPageIndex` returning libs/Help/WikiPageIndex.m. * test(1034-01): add headless function-based test for WikiPageIndex - tests/test_wiki_page_index.m: 14 sub-tests covering every public method and key edge case (listPages count, API-Reference grouping, H1 extraction, TOC group order + alphabetical sort, _Sidebar.md exclusion, readPage Home + .md-extension tolerance + Home fallback, search empty/whitespace/match/no-match, collidesWithGenerator with Home.md auto-generated + empty input) - Sub-tests are independent local functions (t1..t14) returning 1 on pass so a failure fails fast with an informative assert message - Adds an Octave-compatible endsWith_ helper because the built-in endsWith is unavailable on Octave 7+ - No UI dependencies; runs headless on both MATLAB R2020b+ and Octave Verified: All 14 tests passed on Octave 11.1.0 AND MATLAB (via matlab -batch). Test path setup uses addpath + install() so libs/Help/ is on the path when WikiPageIndex.listPages is invoked. * feat(1034-04): WikiBrowser shell + headless/Octave fallback (Task 4.1) - Add libs/Help/WikiBrowser.m: classdef < handle with public/private properties, NV-pair constructor, isInteractiveDesktop_ guard, and openInBrowser_ shell-out fallback for Octave / headless / -batch. - Stub public methods (navigateTo/back/forward/applyTheme/focus/close/ delete) and private buildFigure_ — filled by Tasks 4.2-4.4. - Mirrors DashboardEngine.writeAndOpenInfoHtml's gate logic (~OCTAVE_VERSION + usejava('desktop') + ~batchStartupOptionUsed) so headless runs write a temp HTML file but never try to open a uifigure. - Namespaced errors WikiBrowser:invalidArgs and WikiBrowser:unknownOption. - checkcode clean; constructor smoke test passes headless (IsOpen=false). * feat(1034-04): WikiBrowser three-pane uifigure layout (Task 4.2) - Implement buildFigure_: non-modal uifigure with WindowStyle='normal', Tag='WikiBrowserRoot' (Plan 08 walker-skip anchor), Position [100 100 1100 750], centered. Two-row root grid hosts a 32-px breadcrumb strip (< back / fwd > / crumb label) and a body grid ({260 px, '1x'}) split into sidebar + uihtml content. - Sidebar = uipanel housing a 2-row uigridlayout: search uieditfield on top, uitree (TOC) + uilistbox (search results) layered inside a shared uigridlayout cell (plan-checker I4 Option A — children of uigridlayout auto-fill and ignore .Position, so figure resize reflows correctly). - Add helpers buildTocTree_, onTocSelected_, onSearchChanged_, onSearchHitSelected_, onHtmlEvent_, alert_. onHtmlEvent_ is wired to uihtml.HTMLEventReceivedFcn so Task 4.3's JS bridge can post {page, ts} back to MATLAB on a.wiki-internal clicks. - Theme colours sourced from CompanionTheme.get(obj.Theme); BorderColor / Placeholder gracefully degrade on R2020b. - checkcode clean; headless smoke test still passes (constructor takes openInBrowser_ path). * feat(1034-04): WikiBrowser navigation + cross-doc link rewrite (Task 4.3) - Implement navigateTo: tolerate './' prefix and '.md' suffix, fall back to Home.md with user alert when page missing, render via cache keyed by 'page|theme', push onto history with truncate-forward + 50-entry cap (HistoryCap_). - Implement back / forward: no-op at edges; non-pushing re-render via renderHistoryCurrent_. - Implement applyTheme: invalidate cache, re-render current page, best- effort restyle of sidebar + breadcrumb widgets via CompanionTheme.get. - Add rewriteCrossDocLinks_: SINGLE regexprep with negative lookahead (?!https?://|mailto:|#) marks internal anchors with class='wiki-internal' + data-page='PageName' (preserved verbatim); external + anchor links left untouched. Injects a JS click-interceptor <script> before </body> that posts {page, ts} to htmlComponent.Data — the Task 4.2 HTMLEventReceivedFcn maps it back into navigateTo on the MATLAB side. - Plan-checker I5 fix: removed any chance of orphan href='' or multi- step index juggling. - checkcode clean. Inline regex test confirms the rewrite leaves https://, mailto:, and #anchor links untouched while internal bare-page hrefs get the bridge-friendly substitution. Headless navigateTo/back/forward/applyTheme no-op safely on IsOpen=false instances. * feat(1034-04): WikiBrowser close/focus/delete lifecycle (Task 4.4) - focus: bring uifigure to front via figure(hFig_); idempotent no-op when IsOpen=false or handle invalid. - close: idempotent disposal — clears CloseRequestFcn first (avoids recursion via X-button), tears down Listeners_ entries, deletes the uifigure, nils every internal UI handle (incl. hTreeHostGrid_), resets HistoryStack_, HistoryIdx_, CurrentPage, and re-initialises Cache_ to an empty containers.Map. Safe to call twice or after external delete(). - delete(obj): handle-class destructor defers to close(). - Final headless smoke test passes (IsOpen=0 throughout; navigateTo updates CurrentPage; applyTheme updates Theme; double-close idempotent; no crashes). * test(1034-05): add TestWikiBrowser class-based UI test suite 13 test methods covering the WikiBrowser public API on MATLAB desktop: constructor (defaults + unknownOption + invalidArgs), navigateTo (known + unknown fallback), back/forward history with no-op endpoints, history cap at 50 entries (behavioural via back-step count, no reflection), forward truncation on new navigation, applyTheme re-render, close idempotency, focus on closed = no-op, WikiBrowserRoot tag contract (supports Plan 08's theme walker skip), and cross-doc link rewrite (asserts class="wiki-internal" + data-page="..." + htmlComponent.Data). - TestClassSetup.addPaths follows the TestFastSenseCompanion pattern: Octave skip via assumeFail, headless skip via assumeTrue(usejava ('desktop')), then addpath + install + resolve <repo>/wiki. - TestMethodTeardown aggressively closes the per-test WikiBrowser and sweeps any stray WikiBrowserRoot figures so test ordering is irrelevant. - testCrossDocLinkRewriteInjectsJsBridge uses the canonical findobj(parent, '-depth', 1, 'Type', 'uihtml') idiom shared with TestDashboardInfo.testShowInfoOpensModalFigure (line 230) and DashboardEngine.showInfoModal_ (lines 966-967), with a deep-walk findobj fallback for releases where the uihtml sits deeper in the uigridlayout body grid. Asserts the rewrite preserves the page name verbatim in data-page (plan-checker I5). - Headless verification: matlab -batch runtests filters all 13 tests cleanly via the headless assumption (zero PASSED, zero FAILED, 13 INCOMPLETE) — exactly the contract for CI runs without a desktop. Plan 1034-05 / Task 5.1. * feat(1034-08): add WikiBrowserRoot skip rule to applyThemeToChildren_ walker - Extend existing LogPaneRoot skip rule into combined OR condition matching both 'LogPaneRoot' and 'WikiBrowserRoot' on the Panel arm of the walker. - Update header docstring with parallel paragraph documenting the WikiBrowserRoot skip rule (Phase 1034) alongside the existing LogPaneRoot rule (Phase 1027.1). - WikiBrowser owns its theming via WikiBrowser.applyTheme which FastSenseCompanion.applyTheme invokes explicitly (Plan 06 task 6.2). Walking into the Wiki subtree would stomp its uihtml + uitree styling. - The WikiBrowser is currently a separate top-level uifigure so the walker will never reach it today — the skip rule is preventative, symmetric with LogPaneRoot, protecting against future refactors that might embed WikiBrowser inside the Companion uifigure. * feat(1034-06): reflow Companion toolbar to 1x8 with Wiki button at col 6 - Added private properties hWikiBtn_ and WikiBrowser_ for the new Wiki toolbar button and the shared WikiBrowser handle (Phase 1034 D-06). - Reflowed hToolbarPanel_ inner grid from [1 7] to [1 8]: ColumnWidth {110,110,110,70,90,'1x',36} -> {110,110,110,70,90,70,'1x',36}. - Inserted Wiki button between Close all (col 5) and the spacer: Tag='CompanionWikiBtn', Text='Wiki ↥' (char 8689 pop-out arrow), ButtonPushedFcn -> obj.openWiki_('Companion-Overview'). - Bumped Settings gear Layout.Column from 7 to 8. Static analysis: only NEW warning is MCNPN on openWiki_ (private method added in Task 6.2); pre-existing warnings unchanged. * test(1034-08): extend test_companion_apply_theme_walker with WikiBrowserRoot regression - Add a third skip-rule fixture: a uipanel tagged 'WikiBrowserRoot' with a screaming-red [0.99 0.01 0.01] BackgroundColor sentinel + a child uilabel with a magenta [0.99 0.01 0.99] FontColor sentinel. Both sentinels are values no CompanionTheme will ever produce, so any walker-overwrite is immediately visible. - Add 6 new assertions (2 per apply pass: dark, light, second-dark) asserting the WikiBrowserRoot subtree stays untouched across the full dark->light->dark theme cycle. Mirrors the LogPaneRoot fixture pattern on lines 90-104 and the per-pass assertions on lines 121-126 / 222-225 / 254-257 of the existing test. - Pre-edit count was 30 passing assertions; post-edit count is 36 (verified on local MATLAB R2025b: 'All 36 tests passed'). - Existing LogPaneRoot regression remains intact and continues to pass. * feat(1034-06): add Companion openWiki API + WikiBrowser teardown + theme propagation - New public method openWiki(pageName) — public alias used by tests and sibling windows; defaults pageName to 'Companion-Overview' when omitted. - New private openWiki_(pageName) — canonical implementation used by the toolbar callback (wired in Task 6.1). Lazily constructs a WikiBrowser cached in obj.WikiBrowser_; subsequent calls navigateTo + focus. Resolves <repo>/wiki from this file's location and passes it via the WikiDir NV-pair (falls back to WikiBrowser's own default if missing). All failure paths surface a non-blocking uialert; never crash the app. - close() now closes + deletes WikiBrowser_ (independent try/catch, mirrors the existing TagStatusTableWindow_ teardown pattern). - applyTheme() propagates the theme to WikiBrowser_ when open via its own applyTheme — Plan 08 keeps WikiBrowserRoot out of the recursive walker, so the Wiki window restyles itself. Static analysis: previous MCNPN warning on openWiki_ is gone; no new warnings introduced (all remaining warnings pre-existing). * test(1034-06): add Wiki toolbar button regression tests Three new tests appended after the Task-13 Events-button block: - testToolbarHasWikiButton — finds CompanionWikiBtn via findall and verifies it sits in Layout.Column 6 (the Phase 1034 reflow target). - testToolbarGearMovedToColumn8 — walks every uibutton, picks the one whose Text matches char(9881) (gear glyph), and verifies its Layout.Column is 8 (was 7 before Phase 1034). - testOpenWikiOpensWikiBrowser — drives openWiki('Companion-Overview') and verifies a WikiBrowserRoot uifigure appears, then companion close tears it down. Mirrors TestWikiBrowser by gating on usejava('desktop') via assumeTrue so headless batch runners skip cleanly. Result: 64 existing tests still pass + 2 new tests pass + the third filters cleanly headless (will pass on interactive desktop). * feat(1034-07): wire Wiki button into TagStatusTableWindow - Add hWikiBtn_ private uicontrol property alongside hPauseBtn_ - Insert Wiki button at [0.87 0.945 0.12 0.04] (top-right corner) - Shift hPauseBtn_ left to [0.74 0.945 0.12 0.04] - Add openWiki_ private method routing through Companion_.openWiki('Tag-Status-Table') with WikiBrowser fallback - Extend applyTheme to restyle hWikiBtn_ - Nil hWikiBtn_ in onCloseRequest_ alongside hPauseBtn_ Reuses existing Companion_ property at line 62; no constructor change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(1034-07): wire Wiki button into CompanionEventViewer - Add hWikiBtn_ private uibutton property (reuses existing Companion_ at line 92; no duplication) - Reflow hLeftHeaderGrid from [1 3] to [1 5] with ColumnWidth {8, '1x', 8, 70, 8} adding column 4 for Wiki button - Wiki button text 'Wiki [pop-out arrow]' (char(8689)), tooltip 'Open Wiki: Event Viewer' - Add openWiki_ private method calling obj.Companion_.openWiki('Event-Viewer') with WikiBrowser fallback - Nil obj.hWikiBtn_ in close() Constructor signature unchanged (still positional store/registry/companion). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(1034-07): wire Wiki button into EventsLogPane + LiveLogPane detached headers EventsLogPane: - Add hWikiBtn_ + Companion_ private properties - Add public setCompanion(companion) method - Reflow header grid [1 5] -> [1 6] with new column at index 5 for Wiki button - Wiki button Visible toggles via isa(parent, 'matlab.ui.Figure'); only shown when detached - Add openWiki_ private method routing through Companion_.openWiki('Event-Viewer') - Extend detach() and applyTheme() to cover hWikiBtn_ LiveLogPane (mirror changes): - Same property + setCompanion + openWiki_ additions - Reflow header grid [1 4] -> [1 5] with Wiki button at column 4 (PopoutBtn shifted to col 5) - Default page 'Live-Log', tooltip 'Open Wiki: Live Log' FastSenseCompanion: - Right after instantiating obj.EventsLogPane_ and obj.LiveLogPane_, call setCompanion(obj) inside try/catch so the Wiki button can reach back through openWiki(). Per CONTEXT.md D-13, inline panes share the Companion toolbar's main Wiki button -- only the detached header strips show their own button. Regression: 168 passed / 0 failed / 1 incomplete (testOpenWikiOpensWikiBrowser filtered by headless assumption, expected). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(1034-09): Wiki Browser Home button + DataChangedFcn callback wiring - Add ⌂ Home button to the breadcrumb bar (back / forward / home / crumb). Tooltip: "Home (wiki/Home.md)". Click navigates to wiki/Home.md. - Fix cross-doc click callback wiring from HTMLEventReceivedFcn to DataChangedFcn. The JS bridge writes htmlComponent.Data which only fires DataChangedFcn on the MATLAB side; HTMLEventReceivedFcn is for the sendEventToMATLAB API which we don't use. - Add regression test that asserts DataChangedFcn is a function_handle and HTMLEventReceivedFcn stays empty — guards against the wrong-wiring bug recurring. Note: In-window cross-doc link CLICK navigation still has a runtime issue (JS bridge fires but downstream navigation doesn't happen for in-page anchors). Tracked as a follow-up — TOC navigation, Home, Back, Forward, and search all work as expected. Plan 1034-09 spot-check feedback (user-requested). --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5ecb4c8 commit d8f740e

18 files changed

Lines changed: 2563 additions & 35 deletions

install.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
% libs/Dashboard — widget-based dashboard engine
2323
% libs/WebBridge — browser-based visualization bridge
2424
% libs/FastSenseCompanion — companion navigator app
25+
% libs/Help — Wiki Browser + WikiPageIndex (Phase 1034)
2526
% examples/ — runnable example scripts
2627
% benchmarks/ — performance benchmarks
2728
% tests/ — test suites
@@ -56,6 +57,7 @@
5657
addpath(fullfile(root, 'libs', 'WebBridge'));
5758
addpath(fullfile(root, 'libs', 'FastSenseCompanion'));
5859
addpath(fullfile(root, 'libs', 'Concurrency'));
60+
addpath(fullfile(root, 'libs', 'Help'));
5961

6062
% Demo workspaces (Phase 1015+): add each demo dir so the entry-point
6163
% function (e.g. run_demo) is callable without manual addpath.

libs/FastSenseCompanion/CompanionEventViewer.m

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
RightGrid_ = [] % 3x1 uigridlayout: [filter bar; view; slider]
105105
LeftPanel_ = [] % uipanel hosting the left-column contents
106106
LeftHeaderPanel_ = [] % Thin uipanel above the catalog hosting the view-mode switch
107+
hWikiBtn_ = [] % uibutton: Open Wiki -> Event-Viewer.md (Phase 1034)
107108
LeftCatalogPanel_ = [] % uipanel that the TagCatalogPane attaches into
108109
CatalogPane_ = [] % TagCatalogPane reused from main companion app
109110
TablePanel_ = [] % uipanel hosting the uitable view
@@ -383,6 +384,7 @@ function close(obj)
383384
obj.TableToolbarPanel_ = [];
384385
obj.SelectedTableRows_ = [];
385386
obj.ViewSwitch_ = [];
387+
obj.hWikiBtn_ = [];
386388
obj.TablePanel_ = [];
387389
obj.LeftHeaderPanel_ = [];
388390
obj.LeftCatalogPanel_ = [];
@@ -515,10 +517,12 @@ function buildFigure_(obj)
515517
obj.LeftCatalogPanel_.BackgroundColor = t.WidgetBackground;
516518
obj.LeftCatalogPanel_.BorderType = 'none';
517519

518-
% --- View-mode switch in the left header (above the catalog) ---
519-
hLeftHeaderGrid = uigridlayout(obj.LeftHeaderPanel_, [1 3]);
520+
% --- View-mode switch + Wiki button in the left header
521+
% (above the catalog). Phase 1034 added a 4th column hosting
522+
% the Wiki button; columns 3 and 5 are small spacers.
523+
hLeftHeaderGrid = uigridlayout(obj.LeftHeaderPanel_, [1 5]);
520524
hLeftHeaderGrid.RowHeight = {'1x'};
521-
hLeftHeaderGrid.ColumnWidth = {8, '1x', 8}; % small pad | switch fills | small pad
525+
hLeftHeaderGrid.ColumnWidth = {8, '1x', 8, 70, 8};
522526
hLeftHeaderGrid.Padding = [0 4 0 4];
523527
hLeftHeaderGrid.BackgroundColor = t.WidgetBackground;
524528

@@ -531,6 +535,19 @@ function buildFigure_(obj)
531535
obj.ViewSwitch_.FontColor = t.ForegroundColor;
532536
obj.ViewSwitch_.ValueChangedFcn = @(src, ~) obj.onViewSwitchChanged_(src.Value);
533537

538+
% --- Wiki button (Phase 1034). ---
539+
% Routes through the Companion's shared WikiBrowser via
540+
% openWiki(...). Default page is Event-Viewer.md.
541+
obj.hWikiBtn_ = uibutton(hLeftHeaderGrid, 'push');
542+
obj.hWikiBtn_.Layout.Row = 1;
543+
obj.hWikiBtn_.Layout.Column = 4;
544+
obj.hWikiBtn_.Text = ['Wiki ', char(8689)];
545+
obj.hWikiBtn_.FontSize = 10;
546+
obj.hWikiBtn_.Tooltip = 'Open Wiki: Event Viewer';
547+
obj.hWikiBtn_.BackgroundColor = t.WidgetBorderColor;
548+
obj.hWikiBtn_.FontColor = t.ForegroundColor;
549+
obj.hWikiBtn_.ButtonPushedFcn = @(~,~) obj.openWiki_();
550+
534551
% Right column: 3-row nested grid (filter bar | view | slider).
535552
obj.RightGrid_ = uigridlayout(obj.RootGrid_, [3 1]);
536553
obj.RightGrid_.Layout.Row = 1;
@@ -1446,6 +1463,25 @@ function onViewSwitchChanged_(obj, sel)
14461463
end
14471464
end
14481465

1466+
function openWiki_(obj)
1467+
%OPENWIKI_ Route to the Companion's shared WikiBrowser; fall back to standalone.
1468+
% Phase 1034 -- Wiki button click handler. Companion_ is always
1469+
% non-empty here because the constructor (line 124) errors out
1470+
% when companion is empty -- so under normal use we take the
1471+
% Companion_.openWiki path. The fallback is defensive.
1472+
try
1473+
if ~isempty(obj.Companion_) && isvalid(obj.Companion_) && ...
1474+
isa(obj.Companion_, 'FastSenseCompanion') && ...
1475+
ismethod(obj.Companion_, 'openWiki')
1476+
obj.Companion_.openWiki('Event-Viewer');
1477+
return;
1478+
end
1479+
WikiBrowser('OpenTo', 'Event-Viewer');
1480+
catch ME
1481+
fprintf(2, '[CompanionEventViewer] openWiki_ failed: %s\n', ME.message);
1482+
end
1483+
end
1484+
14491485
function updateTableData_(obj, events)
14501486
%UPDATETABLEDATA_ Populate the uitable from a (filtered) Event array.
14511487
if isempty(obj.Table_) || ~isvalid(obj.Table_); return; end

libs/FastSenseCompanion/EventsLogPane.m

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
hLogSearch_ = [] % uieditfield (search)
4747
hLogLevelDD_ = [] % uidropdown level filter
4848
hLastUpdateLbl_ = [] % "Updated: HH:MM:SS" label
49-
hPopoutBtn_ = [] % pop-out icon uibutton in header col 5
49+
hPopoutBtn_ = [] % pop-out icon uibutton in header col 6 (was 5 pre-Phase 1034)
50+
hWikiBtn_ = [] % uibutton: Open Wiki -> Event-Viewer.md (only Visible in detached pane)
51+
Companion_ = [] % FastSenseCompanion handle (or []); set via setCompanion(...)
5052
LogBuffer_ = cell(0, 3) % {Time, Level, Message} newest first, capped 500
5153
end
5254

@@ -66,6 +68,16 @@
6668
obj.IsAttached = false;
6769
end
6870

71+
function setCompanion(obj, companion)
72+
%SETCOMPANION Cache a FastSenseCompanion handle so the Wiki button can route through openWiki.
73+
% companion -- FastSenseCompanion instance (or []).
74+
% Phase 1034 -- enables the detached-header Wiki button to call
75+
% obj.Companion_.openWiki('Event-Viewer'). Called once by the
76+
% FastSenseCompanion constructor right after EventsLogPane
77+
% instantiation. Safe to call with [] to detach.
78+
obj.Companion_ = companion;
79+
end
80+
6981
function attach(obj, parent, themeStruct)
7082
%ATTACH Build the events-log UI inside parent (uipanel or uifigure).
7183
% parent — uipanel (inline) or uifigure (detached). Must be valid.
@@ -93,11 +105,15 @@ function attach(obj, parent, themeStruct)
93105
obj.hRoot_.RowSpacing = 4;
94106
obj.hRoot_.BackgroundColor = t.WidgetBackground;
95107

96-
% --- Header (row 1): Events label | search | level dropdown | last-update | pop-out icon ---
97-
gHdr = uigridlayout(obj.hRoot_, [1 5]);
108+
% --- Header (row 1): Events label | search | level dropdown | last-update | wiki | pop-out icon ---
109+
% Phase 1034 -- added a 6th column hosting the Wiki button
110+
% (only Visible when this pane is detached into its own
111+
% uifigure; inline panes piggyback on the Companion toolbar's
112+
% main Wiki button per CONTEXT.md D-13).
113+
gHdr = uigridlayout(obj.hRoot_, [1 6]);
98114
gHdr.Layout.Row = 1;
99115
gHdr.Layout.Column = 1;
100-
gHdr.ColumnWidth = {40, '1x', 100, 150, 36};
116+
gHdr.ColumnWidth = {40, '1x', 100, 150, 36, 36};
101117
gHdr.RowHeight = {'1x'};
102118
gHdr.Padding = [0 0 0 0];
103119
gHdr.ColumnSpacing = 8;
@@ -133,8 +149,29 @@ function attach(obj, parent, themeStruct)
133149
obj.hLastUpdateLbl_.VerticalAlignment = 'center';
134150
obj.hLastUpdateLbl_.Tooltip = 'Time of the last successful live refresh';
135151

152+
% --- Wiki button (Phase 1034). ---
153+
% Only Visible when this pane is detached into its own
154+
% uifigure parent; inline (uipanel parent) the button is
155+
% hidden because the Companion's main toolbar already
156+
% provides a Wiki entry point.
157+
obj.hWikiBtn_ = uibutton(gHdr, 'push');
158+
obj.hWikiBtn_.Layout.Row = 1; obj.hWikiBtn_.Layout.Column = 5;
159+
obj.hWikiBtn_.Text = char(8689); % pop-out arrow glyph as Wiki icon
160+
obj.hWikiBtn_.FontSize = 12;
161+
obj.hWikiBtn_.Tooltip = 'Open Wiki: Event Viewer';
162+
obj.hWikiBtn_.BackgroundColor = t.WidgetBorderColor;
163+
obj.hWikiBtn_.FontColor = t.ForegroundColor;
164+
obj.hWikiBtn_.ButtonPushedFcn = @(~,~) obj.openWiki_();
165+
% Per CONTEXT.md D-13: only show on the detached header strip
166+
% (when parent is a uifigure, not a uipanel).
167+
if isa(parent, 'matlab.ui.Figure')
168+
obj.hWikiBtn_.Visible = 'on';
169+
else
170+
obj.hWikiBtn_.Visible = 'off';
171+
end
172+
136173
obj.hPopoutBtn_ = uibutton(gHdr, 'push');
137-
obj.hPopoutBtn_.Layout.Row = 1; obj.hPopoutBtn_.Layout.Column = 5;
174+
obj.hPopoutBtn_.Layout.Row = 1; obj.hPopoutBtn_.Layout.Column = 6;
138175
obj.hPopoutBtn_.Text = char(8689); % pop-out arrow glyph
139176
obj.hPopoutBtn_.FontSize = 14;
140177
obj.hPopoutBtn_.Tooltip = 'Detach events log to its own window';
@@ -185,6 +222,7 @@ function detach(obj)
185222
obj.hLogLevelDD_ = [];
186223
obj.hLastUpdateLbl_ = [];
187224
obj.hPopoutBtn_ = [];
225+
obj.hWikiBtn_ = [];
188226
obj.IsAttached = false;
189227
end
190228

@@ -263,6 +301,11 @@ function applyTheme(obj, themeStruct)
263301
obj.hPopoutBtn_.BackgroundColor = t.WidgetBorderColor;
264302
obj.hPopoutBtn_.FontColor = t.ForegroundColor;
265303
end
304+
% Phase 1034 -- Wiki button uses same accent as pop-out.
305+
if ~isempty(obj.hWikiBtn_) && isvalid(obj.hWikiBtn_)
306+
obj.hWikiBtn_.BackgroundColor = t.WidgetBorderColor;
307+
obj.hWikiBtn_.FontColor = t.ForegroundColor;
308+
end
266309
% Table: re-assert striped pair so attach() and applyTheme()
267310
% share the same logic regardless of walker behavior.
268311
isDark = mean(t.DashboardBackground) < 0.5;
@@ -331,6 +374,25 @@ function delete(obj)
331374

332375
methods (Access = private)
333376

377+
function openWiki_(obj)
378+
%OPENWIKI_ Route to the Companion's shared WikiBrowser; fall back to standalone.
379+
% Phase 1034 -- Wiki button click handler (only fires when the
380+
% pane is detached; the inline button is Visible='off'). Prefers
381+
% the Companion's openWiki entry point so one WikiBrowser handle
382+
% is shared across the session.
383+
try
384+
if ~isempty(obj.Companion_) && isvalid(obj.Companion_) && ...
385+
isa(obj.Companion_, 'FastSenseCompanion') && ...
386+
ismethod(obj.Companion_, 'openWiki')
387+
obj.Companion_.openWiki('Event-Viewer');
388+
return;
389+
end
390+
WikiBrowser('OpenTo', 'Event-Viewer');
391+
catch ME
392+
fprintf(2, '[EventsLogPane] openWiki_ failed: %s\n', ME.message);
393+
end
394+
end
395+
334396
function applyLogFilter_(obj)
335397
%APPLYLOGFILTER_ Re-apply level + text filter to LogBuffer_ → uitable.Data.
336398
if isempty(obj.hLogTable_) || ~isvalid(obj.hLogTable_); return; end

0 commit comments

Comments
 (0)