' =================================================================== ' File: debug-panel.puml ' Purpose: Single source of truth for module-level activity + per-method sequences. ' Module: debug-panel.js — Draggable in-page panel: logs tail, tools/settings, queue, pause/stop. ' Edit rules: Follow the legend at bottom; preserve VIEW/METHOD anchors for automation. ' =================================================================== ' Neutral defaults — typography/layout only (keeps partition colors intact) skinparam Shadowing false skinparam SequenceMessageAlign center skinparam SequenceLifeLineBorderColor #666666 skinparam SequenceLifeLineBorderThickness 1 ' ==== VIEW: Branch Flow (debug-panel.js) =================================== @startuml title debug-panel.js — Branch Flow (full module) start :debug-panel; fork ' -------- DebugPanel.constructor() -------- partition "DebugPanel.constructor()" #E7FAE3 { :constructor; :panel/bodyLogs/bodyTools = null;\ncollapsed=false; drag={active:false,dx:0,dy:0}; :panelState = _loadPanelState(); kill } fork again ' -------- _loadPanelState() -------- partition "_loadPanelState()" #FFF6D1 { :_loadPanelState; :JSON.parse(localStorage.getItem(STORAGE_KEYS.panel)||'{}');\ncatch -> {}; :return state; kill } fork again ' -------- _savePanelState(partial) -------- partition "_savePanelState(partial)" #FFE1DB { :_savePanelState; :merged = {...panelState, ...partial};\npanelState=merged; localStorage.setItem(..., JSON.stringify(merged)); kill } fork again ' -------- flashBtn(btn, label, ms) -------- partition "flashBtn(btn, label, ms)" #DCF9EE { :flashBtn; :guard !btn; disable; temp label ✓; fade; setTimeout restore; kill } fork again ' -------- toast(msg, ms) -------- partition "toast(msg, ms)" #FFE6F0 { :toast; :guard !panel; create floating div; append; auto-remove; kill } fork again ' -------- copyLast(n=50) -------- partition "copyLast(n=50)" #E6F3FF { :copyLast; :lines = logger.getRecentLogs(n);\ntry navigator.clipboard.writeText -> info + toast;\ncatch -> _fallbackCopy(lines); kill } fork again ' -------- _fallbackCopy(text, err?) -------- partition "_fallbackCopy(text, err?)" #F0E6FA { :_fallbackCopy; :overlay+textarea UI; select+focus; Close button;\nwarn on missing Clipboard API; kill } fork again ' -------- mount() -------- partition "mount()" #E7FAF7 { :mount; :guard document.body else defer;\nif !cfg.debug.showPanel return; :build root DOM (header/tabs/controls/bodies);\nappend to body;\nset refs; _wireControls(); _startLogRefresh();\npost-mount log info; kill } fork again ' -------- _wireControls() -------- partition "_wireControls()" #FFF2E7 { :_wireControls; :bind level select; copy buttons; pause/resume; queue clear;\nSTOP; tabs; collapse; dragging; clear history;\nfeature toggles; number inputs; bridge key;\nconfig JSON save/reset; kill } fork again ' -------- _loadToolsPanel() -------- partition "_loadToolsPanel()" #E7F7FF { :_loadToolsPanel; :sync toggles/nums from cfg;\nmask bridge key; sanitize config JSON (mask bridgeKey); kill } fork again ' -------- _startLogRefresh() -------- partition "_startLogRefresh()" #FFF7E7 { :_startLogRefresh; :renderLogs() each second: show tail of buffer or hints;\nautoscroll; kill } fork again ' -------- bootstrap -------- partition "bootstrap (new + mount)" #F7E7FF { :panel = new DebugPanel();\nif DOMContentLoaded pending -> add listener -> mount();\nelse mount();\nwindow.AI_REPO_DEBUG_PANEL = panel; kill } end fork @enduml ' ==== METHOD: constructor() ================================================ @startuml title debug-panel:constructor(): \n Initialize DOM refs, drag state, and persisted panel state participant "Caller" as CL participant "constructor()" as CTOR participant "_loadPanelState()" as LPS activate CL CL -> CTOR : new DebugPanel() activate CTOR CTOR -> CTOR : panel=null; bodyLogs=null; bodyTools=null; collapsed=false CTOR -> CTOR : drag={active:false,dx:0,dy:0} CTOR -> LPS : _loadPanelState() LPS --> CTOR : state | {} CTOR -> CTOR : panelState = state CTOR --> CL : instance deactivate CTOR deactivate CL @enduml ' ==== METHOD: _loadPanelState() ============================================ @startuml title debug-panel:_loadPanelState(): \n Read panel position/collapsed state from localStorage participant "Caller" as CL participant "_loadPanelState()" as LPS participant "localStorage" as LS participant "STORAGE_KEYS" as SK activate CL CL -> LPS : initial request activate LPS LPS -> SK : read STORAGE_KEYS.panel SK --> LPS : key LPS -> LS : getItem(key) LS --> LPS : json | null alt parse ok LPS --> CL : JSON.parse(json) || {} else parse error LPS --> CL : {} end deactivate LPS deactivate CL @enduml ' ==== METHOD: _savePanelState(partial) ====================================== @startuml title debug-panel:_savePanelState(partial): \n Merge and persist panel state participant "Caller" as CL participant "_savePanelState(partial)" as SPS participant "localStorage" as LS participant "STORAGE_KEYS" as SK activate CL CL -> SPS : partial activate SPS SPS -> SPS : merged = {...panelState, ...partial}; panelState=merged SPS -> SK : STORAGE_KEYS.panel SK --> SPS : key SPS -> LS : setItem(key, JSON.stringify(merged)) LS --> SPS : ok SPS --> CL : (void) deactivate SPS deactivate CL @enduml ' ==== METHOD: flashBtn(btn, label='Done', ms=900) =========================== @startuml title debug-panel:flashBtn(btn, label, ms): \n Temporarily disable and relabel a button participant "Caller" as CL participant "flashBtn(btn,label,ms)" as FSH participant "DOM" as DOM participant "Timer" as TMR activate CL CL -> FSH : btn, label, ms activate FSH alt !btn FSH --> CL : (void) deactivate FSH deactivate CL return end FSH -> DOM : btn.disabled=true; store old text; set `${label} ✓`; opacity=.7 FSH -> TMR : setTimeout(ms) TMR --> FSH : wake FSH -> DOM : restore text; disabled=false; opacity='' DOM --> FSH : ok FSH --> CL : (void) deactivate FSH deactivate CL @enduml ' ==== METHOD: toast(msg, ms=1200) ========================================== @startuml title debug-panel:toast(msg, ms): \n Show a transient floating toast in panel participant "Caller" as CL participant "toast(msg, ms)" as TST participant "DOM" as DOM participant "Timer" as TMR activate CL CL -> TST : msg, ms activate TST alt !panel TST --> CL : (void) deactivate TST deactivate CL return end TST -> DOM : createElement('div') with styles; appendChild(panel) DOM --> TST : elem TST -> TMR : setTimeout(ms) TMR --> TST : wake TST -> DOM : elem.remove() DOM --> TST : removed TST --> CL : (void) deactivate TST deactivate CL @enduml ' ==== METHOD: copyLast(n=50) =============================================== @startuml title debug-panel:copyLast(n): \n Copy recent logs to clipboard or show manual copy overlay participant "Caller" as CL participant "copyLast(n)" as CPL participant "Logger" as LOG participant "Navigator.clipboard" as NCB participant "toast(msg)" as TST participant "_fallbackCopy(text,err?)" as FBC activate CL CL -> CPL : n activate CPL CPL -> LOG : getRecentLogs(n) LOG --> CPL : lines (string) CPL -> NCB : writeText(lines) alt clipboard ok NCB --> CPL : resolved CPL -> LOG : info("Copied last n lines") CPL -> TST : toast(`Copied last ${n} logs`) TST --> CPL : shown else clipboard error or unsupported NCB --> CPL : rejected | (no API) CPL -> FBC : _fallbackCopy(lines, error?) FBC --> CPL : overlay shown end CPL --> CL : (void) deactivate CPL deactivate CL @enduml ' ==== METHOD: _fallbackCopy(text, originalError) ============================ @startuml title debug-panel:_fallbackCopy(text, originalError): \n Fullscreen overlay with textarea for manual copy participant "Caller" as CL participant "_fallbackCopy(text,err?)" as FBC participant "DOM" as DOM participant "Logger" as LOG activate CL CL -> FBC : text, originalError? activate FBC FBC -> DOM : create overlay + panel + textarea + Close button; append to body DOM --> FBC : mounted FBC -> DOM : textarea.focus(); textarea.select() DOM --> FBC : selected FBC -> LOG : warn("Clipboard API unavailable", {error}) LOG --> FBC : ok FBC --> CL : (void) deactivate FBC deactivate CL @enduml ' ==== METHOD: mount() ======================================================= @startuml title debug-panel:mount(): \n Create panel DOM, wire controls, start log refresh, log ready participant "Caller" as CL participant "mount()" as MNT participant "Config" as CFG participant "DOM" as DOM participant "_wireControls()" as WRC participant "_startLogRefresh()" as LRF participant "Logger" as LOG participant "Timer" as TMR activate CL CL -> MNT : initial request activate MNT alt !document.body MNT -> TMR : setTimeout(100) TMR --> MNT : retry MNT --> CL : (returns after scheduling) else body present MNT -> CFG : get('debug.showPanel') CFG --> MNT : bool alt disabled MNT --> CL : (void) else enabled MNT -> DOM : build root HTML (header/tabs/controls/bodies) DOM --> MNT : root MNT -> DOM : appendChild(document.body, root) DOM --> MNT : ok MNT -> MNT : this.panel/root refs; bodyLogs/bodyTools MNT -> WRC : _wireControls() WRC --> MNT : wired MNT -> LRF : _startLogRefresh() LRF --> MNT : started MNT -> TMR : setTimeout(100) TMR --> MNT : tick MNT -> LOG : info("Debug panel mounted...") LOG --> MNT : ok MNT -> LOG : info("Panel visible at: (...)") end MNT --> CL : (void) end deactivate MNT deactivate CL @enduml ' ==== METHOD: _wireControls() ============================================== @startuml title debug-panel:_wireControls(): \n Bind all UI controls to config/logger/queue/history/actions participant "Caller" as CL participant "_wireControls()" as WRC participant "Config" as CFG participant "Logger" as LOG participant "DOM" as DOM participant "Queue" as QUE participant "History" as HIS participant "Paste" as PST activate CL CL -> WRC : initial request activate WRC ' Log level selector WRC -> DOM : select('.rc-level') DOM --> WRC : sel WRC -> CFG : get('debug.level') CFG --> WRC : currentLevel WRC -> LOG : trace(`[Debug Panel] Current log level: ${currentLevel}`) WRC -> DOM : sel.onchange -> LOG.setLevel(newLevel) + LOG.info(...) ' Copy buttons WRC -> DOM : .rc-copy / .rc-copy-200 (onclick -> copyLast(n) + flashBtn) DOM --> WRC : wired ' Pause/Resume runtime WRC -> DOM : .rc-pause (onclick -> toggle cfg.runtime.paused; flashBtn; toast; log) DOM --> WRC : wired ' Queue clear WRC -> DOM : .rc-queue-clear (onclick -> AI_REPO_QUEUE.clear(); flashBtn; toast; log.warn) DOM --> WRC : wired ' Emergency STOP WRC -> DOM : .rc-stop (onclick -> AI_REPO_STOP(); flashBtn; toast; log.warn) DOM --> WRC : wired ' Tabs + Tools load WRC -> DOM : tabs ('Logs'/'Tools & Settings'); switch bodies; call _loadToolsPanel() DOM --> WRC : wired ' Collapse toggle WRC -> DOM : .rc-collapse (onclick -> toggle collapsed; save state) DOM --> WRC : wired ' Drag header WRC -> DOM : .rc-header (mousedown -> track; mousemove -> set left/top; mouseup -> _savePanelState) DOM --> WRC : wired ' Clear History WRC -> DOM : .rc-clear-history (onclick -> HIS.clear(); GM_notification?; flashBtn; toast; log) DOM --> WRC : wired ' Toggles (checkboxes) WRC -> DOM : .rc-toggle (bind cfg.get/set; toast; log.info) DOM --> WRC : wired ' Numeric inputs WRC -> DOM : .rc-num (bind cfg.get; on change -> parseInt -> cfg.set; toast; log.info) DOM --> WRC : wired ' Bridge key save/clear WRC -> DOM : .rc-save-bridge-key / .rc-clear-bridge-key (onclick -> cfg.set('api.bridgeKey', val|''); mask input; flashBtn; toast; log) DOM --> WRC : wired ' Config JSON save/reset WRC -> DOM : .rc-save-json (parse JSON; delete meta/runtime; deep-set cfg; refresh tools; toast/log), .rc-reset-defaults (confirm -> localStorage.removeItem(cfgKey) -> reload) DOM --> WRC : wired WRC --> CL : (void) deactivate WRC deactivate CL @enduml ' ==== METHOD: _loadToolsPanel() ============================================ @startuml title debug-panel:_loadToolsPanel(): \n Populate controls from cfg and render sanitized JSON participant "Caller" as CL participant "_loadToolsPanel()" as LTP participant "Config" as CFG participant "DOM" as DOM activate CL CL -> LTP : initial request activate LTP ' Toggles and nums LTP -> DOM : queryAll('.rc-toggle') / queryAll('.rc-num') DOM --> LTP : lists LTP -> CFG : get(key) for each CFG --> LTP : values LTP -> DOM : set .checked / .value accordingly ' Bridge key masked LTP -> CFG : get('api.bridgeKey') CFG --> LTP : key | '' LTP -> DOM : .rc-bridge-key.value = key ? '••••••••' : '' ' Config JSON sanitized LTP -> CFG : config (object) CFG --> LTP : dump LTP -> LTP : sanitized = deep clone; if api.bridgeKey -> mask LTP -> DOM : .rc-json.value = JSON.stringify(sanitized, null, 2) LTP --> CL : (void) deactivate LTP deactivate CL @enduml ' ==== METHOD: _startLogRefresh() =========================================== @startuml title debug-panel:_startLogRefresh(): \n Tail logger buffer every second and autoscroll participant "Caller" as CL participant "_startLogRefresh()" as LRF participant "Logger" as LOG participant "DOM" as DOM participant "Timer" as TMR activate CL CL -> LRF : initial request activate LRF LRF -> LRF : define renderLogs() LRF -> TMR : setInterval(renderLogs, 1000) TMR --> LRF : id LRF -> LRF : renderLogs() ' renderLogs inner flow LRF -> LOG : buffer? LOG --> LRF : buffer | undefined alt logger not ready LRF -> DOM : bodyLogs.innerHTML = "Logger not initialized yet..." else ready LRF -> LRF : rows = buffer.slice(-80) alt rows empty LRF -> DOM : "No logs yet. Waiting for activity..." else rows LRF -> DOM : bodyLogs.innerHTML = rows.map(...).join('') LRF -> DOM : bodyLogs.scrollTop = bodyLogs.scrollHeight end end LRF --> CL : (void) deactivate LRF deactivate CL @enduml ' ==== METHOD: bootstrap (instance + mount) ================================= @startuml title debug-panel:bootstrap: \n Instantiate panel and mount after DOM is ready participant "Caller" as CL participant "bootstrap" as BOOT participant "DOM" as DOM participant "DebugPanel()" as CTOR participant "mount()" as MNT activate CL CL -> BOOT : initial load activate BOOT BOOT -> CTOR : new DebugPanel() CTOR --> BOOT : panel BOOT -> DOM : document.readyState DOM --> BOOT : 'loading' | 'interactive/complete' alt loading BOOT -> DOM : addEventListener('DOMContentLoaded', () => panel.mount()) DOM --> BOOT : listener added else ready BOOT -> MNT : panel.mount() MNT --> BOOT : mounted end BOOT -> DOM : window.AI_REPO_DEBUG_PANEL = panel BOOT --> CL : (void) deactivate BOOT deactivate CL @enduml ' ==== LEGEND =============================================================== @startuml legend bottom == debug-panel UML Style Guide (for future edits) == • Scope: One .puml per module. Keep two views: (1) Activity "Branch Flow" for the whole module (partitions + soft colors), (2) Per-function/Per-method Sequence diagrams for each exported or significant internal function. • Sequence conventions: 1) First participant is the external caller (use "Caller" or a concrete origin). 2) Do NOT add a module/class lifeline; the name appears in the title only. 3) Include every directly-called method or subsystem as a participant (e.g., "mount()", "_wireControls()", "copyLast()", "_fallbackCopy()", "toast()", "flashBtn()", "Queue", "History", "Logger", "Config", "Timer", "DOM", "localStorage", "STORAGE_KEYS"). 4) Prefer simple messages; Use --> for returns; -> for calls. 5) Use activate/deactivate as you see fit for clarity. 6) Use alt blocks only when branches meaningfully change the message flow. • Activity view conventions: A) Start with module node then fork partitions for each function/method. B) One partition per function; soft background color; terminate branches with 'kill'. C) Keep wording aligned with code (e.g., masked bridge key, JSON sanitize, tail size ~80 rows). • Color palette (soft pastels) • Use --> for returns; -> for calls. • Participants use quoted method names for internals (e.g., "_loadToolsPanel()"), and plain nouns for systems ("DOM", "Timer", "localStorage"). • Keep this legend at the end of the file to standardize edits. endlegend @enduml