606 lines
18 KiB
Plaintext
606 lines
18 KiB
Plaintext
' ===================================================================
|
|
' File: main.puml
|
|
' Purpose: Single source of truth for module-level activity + per-method sequences.
|
|
' Module: main.js — Legacy entry + convenience API; wires observer and exposes helpers.
|
|
' 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 (main.js) ==========================================
|
|
@startuml
|
|
title main.js — Branch Flow (full module)
|
|
|
|
start
|
|
:main;
|
|
|
|
fork
|
|
' -------- AIRepoCommander.constructor() --------
|
|
partition "AIRepoCommander.constructor()" #E7FAE3 {
|
|
:constructor;
|
|
:isInitialized=false; observer=null;\nprocessed=WeakSet();\nmessageSelectors=[assistant selectors];
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- initialize() --------
|
|
partition "initialize()" #FFF6D1 {
|
|
:initialize;
|
|
:guard: if already initialized -> warn + return;\nlog meta+debug+api;\nlog config summary;
|
|
:startObserver(); if cfg.ui.processExisting -> scanExisting(); exposeAPI();\nset isInitialized=true; log initialized; trace globals;
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- startObserver() --------
|
|
partition "startObserver()" #FFE1DB {
|
|
:startObserver;
|
|
:create MutationObserver(cb);\ncb skips when runtime.paused;\ncount assistant messages (direct + nested); call processMessage(el);\nobserve document.body {childList,subtree};\nlog started;
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- isAssistantMessage(el) --------
|
|
partition "isAssistantMessage(el)" #DCF9EE {
|
|
:isAssistantMessage;
|
|
:return any selector matches(el);
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- processMessage(el) --------
|
|
partition "processMessage(el)" #FFE6F0 {
|
|
:processMessage;
|
|
:skip if processed.has(el);\ncommands=extractCommands(el);\nif none -> trace & return;\nmark processed; cap to cfg.queue.maxPerMessage;
|
|
:for each cmd i: if history.isProcessed(el,i) -> addRetryButton(); else run(el,cmd,i);
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- extractCommands(el) --------
|
|
partition "extractCommands(el)" #E6F3FF {
|
|
:extractCommands;
|
|
:regex /@bridge@[\\s\\S]*?@end@/g over el.textContent;\nreturn blocks[];
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- run(el, commandText, index) --------
|
|
partition "run(el, commandText, index)" #F0E6FA {
|
|
:run;
|
|
:trace + markProcessed(el,index);\nparsed = PARSER.parse(commandText);\nvalidation = PARSER.validate(parsed);
|
|
:if !validation.isValid -> error + addRetryButton();\nif example -> info + return;\noptional debounce via cfg.execution.debounceDelay;
|
|
:label = `Command ${i+1}`; EXECUTOR.execute(parsed, el, label);\nlog success; catch -> error + addRetryButton();
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- addRetryButton(el, txt, idx) --------
|
|
partition "addRetryButton(el, txt, idx)" #E7FAF7 {
|
|
:addRetryButton;
|
|
:create <button> Run Again #idx;\nwire click -> run(el, txt, idx);\nappend to el;
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- scanExisting() --------
|
|
partition "scanExisting()" #FFF2E7 {
|
|
:scanExisting;
|
|
:nodes = querySelectorAll(messageSelectors);\nlog count; for each assistant node -> processMessage(el);\nlog scanned summary;
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- exposeAPI() --------
|
|
partition "exposeAPI()" #E7F7FF {
|
|
:exposeAPI;
|
|
:window.AI_REPO = {version,config,logger,history, pause(), resume(), clearHistory()};\nwindow.AI_REPO_STOP() => disable API + pause + clear queue + error logs;\nwindow.AI_REPO_SET_KEY(k) -> cfg.set('api.bridgeKey',k);
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- delay(ms) --------
|
|
partition "delay(ms)" #FFF7E7 {
|
|
:delay;
|
|
:return Promise(setTimeout(ms));
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- destroy() --------
|
|
partition "destroy()" #F7E7FF {
|
|
:destroy;
|
|
:observer?.disconnect(); processed=WeakSet(); isInitialized=false; log destroyed;
|
|
kill
|
|
}
|
|
fork again
|
|
' -------- bootstrap (DOMContentLoaded / ready) --------
|
|
partition "bootstrap (DOMContentLoaded / ready)" #E7E7FF {
|
|
:if document.readyState==='loading' -> on DOMContentLoaded: new AIRepoCommander().initialize();\nelse: new AIRepoCommander().initialize();\nAI_REPO_DETECTOR?.start();
|
|
kill
|
|
}
|
|
end fork
|
|
@enduml
|
|
|
|
' ==== METHOD: AIRepoCommander.constructor() =================================
|
|
@startuml
|
|
title main:constructor(): \n Prepare state and assistant selectors
|
|
|
|
participant "Caller" as CL
|
|
participant "constructor()" as CTOR
|
|
|
|
activate CL
|
|
CL -> CTOR : new AIRepoCommander()
|
|
activate CTOR
|
|
CTOR -> CTOR : isInitialized=false; observer=null
|
|
CTOR -> CTOR : processed=new WeakSet()
|
|
CTOR -> CTOR : messageSelectors=[ '[data-message-author-role=\"assistant\"]', '.chat-message:not([data-message-author-role=\"user\"])', '.message-content' ]
|
|
CTOR --> CL : instance
|
|
deactivate CTOR
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: initialize() ==================================================
|
|
@startuml
|
|
title main:initialize(): \n Log config, start observer, optionally scan, expose API
|
|
|
|
participant "Caller" as CL
|
|
participant "initialize()" as INIT
|
|
participant "Logger" as LOG
|
|
participant "Config" as CFG
|
|
participant "startObserver()" as OBS
|
|
participant "scanExisting()" as SCN
|
|
participant "exposeAPI()" as EXP
|
|
|
|
activate CL
|
|
CL -> INIT : initial request
|
|
activate INIT
|
|
|
|
alt already initialized
|
|
INIT -> LOG : warn('Already initialized, skipping')
|
|
LOG --> INIT : ok
|
|
INIT --> CL : (void)
|
|
deactivate INIT
|
|
deactivate CL
|
|
return
|
|
end
|
|
|
|
INIT -> LOG : info('AI Repo Commander initializing', {version, debugLevel, apiEnabled})
|
|
LOG --> INIT : ok
|
|
INIT -> LOG : verbose('Configuration summary', {...})
|
|
LOG --> INIT : ok
|
|
|
|
INIT -> OBS : startObserver()
|
|
OBS --> INIT : observing
|
|
|
|
INIT -> CFG : get('ui.processExisting')
|
|
CFG --> INIT : boolean
|
|
alt processExisting == true
|
|
INIT -> LOG : verbose('Will process existing messages on page')
|
|
LOG --> INIT : ok
|
|
INIT -> SCN : scanExisting()
|
|
SCN --> INIT : done
|
|
end
|
|
|
|
INIT -> EXP : exposeAPI()
|
|
EXP --> INIT : exposed
|
|
|
|
INIT -> INIT : isInitialized = true
|
|
INIT -> LOG : info('AI Repo Commander initialized')
|
|
LOG --> INIT : ok
|
|
INIT -> LOG : trace('Exposed globals:', Object.keys(window).filter(k => k.startsWith('AI_REPO')))
|
|
LOG --> INIT : ok
|
|
|
|
INIT --> CL : (void)
|
|
deactivate INIT
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: startObserver() ==============================================
|
|
@startuml
|
|
title main:startObserver(): \n Watch document for assistant messages and process them
|
|
|
|
participant "Caller" as CL
|
|
participant "startObserver()" as SOB
|
|
participant "MutationObserver" as MO
|
|
participant "Logger" as LOG
|
|
participant "Config" as CFG
|
|
participant "processMessage(el)" as PRO
|
|
participant "DOM" as DOM
|
|
|
|
activate CL
|
|
CL -> SOB : initial request
|
|
activate SOB
|
|
|
|
SOB -> MO : new MutationObserver(callback)
|
|
MO --> SOB : observer
|
|
|
|
' begin observing
|
|
SOB -> DOM : observer.observe(document.body, {childList:true, subtree:true})
|
|
DOM --> SOB : ok
|
|
SOB -> LOG : verbose('MutationObserver started, watching document.body')
|
|
LOG --> SOB : ok
|
|
|
|
' callback behavior (sketch)
|
|
SOB -> CFG : get('runtime.paused')
|
|
CFG --> SOB : paused?
|
|
alt paused
|
|
SOB -> LOG : trace('Mutations ignored (paused)')
|
|
else active
|
|
SOB -> SOB : assistantMsgCount = 0
|
|
' for each m of mutations
|
|
SOB -> SOB : for addedNodes: if element & isAssistantMessage(n) -> ++count; processMessage(n);\nscan n.querySelectorAll(...) and process each
|
|
SOB -> LOG : verbose(`Detected ${assistantMsgCount} assistant message(s)`) ' only if >0
|
|
end
|
|
|
|
SOB --> CL : (void)
|
|
deactivate SOB
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: isAssistantMessage(el) =======================================
|
|
@startuml
|
|
title main:isAssistantMessage(el): \n Match against known assistant selectors
|
|
|
|
participant "Caller" as CL
|
|
participant "isAssistantMessage(el)" as IAM
|
|
|
|
activate CL
|
|
CL -> IAM : el
|
|
activate IAM
|
|
IAM -> IAM : return messageSelectors.some(sel => el.matches?.(sel))
|
|
IAM --> CL : true/false
|
|
deactivate IAM
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: processMessage(el) ===========================================
|
|
@startuml
|
|
title main:processMessage(el): \n Dedup, extract commands, cap, and run or add retry
|
|
|
|
participant "Caller" as CL
|
|
participant "processMessage(el)" as PRO
|
|
participant "Logger" as LOG
|
|
participant "Config" as CFG
|
|
participant "History" as HIS
|
|
participant "extractCommands(el)" as EXT
|
|
participant "run(el,cmd,idx)" as RUN
|
|
participant "addRetryButton(el,txt,idx)" as ARB
|
|
|
|
activate CL
|
|
CL -> PRO : el
|
|
activate PRO
|
|
|
|
alt processed.has(el)
|
|
PRO -> LOG : trace('Message already processed, skipping')
|
|
LOG --> PRO : ok
|
|
PRO --> CL : (void)
|
|
deactivate PRO
|
|
deactivate CL
|
|
return
|
|
end
|
|
|
|
PRO -> EXT : extractCommands(el)
|
|
EXT --> PRO : commands[]
|
|
|
|
alt commands.length == 0
|
|
PRO -> LOG : trace('No commands found in message')
|
|
LOG --> PRO : ok
|
|
PRO --> CL : (void)
|
|
deactivate PRO
|
|
deactivate CL
|
|
return
|
|
end
|
|
|
|
PRO -> LOG : verbose(`Found ${commands.length} command block(s) in message`)
|
|
LOG --> PRO : ok
|
|
PRO -> PRO : processed.add(el)
|
|
|
|
PRO -> CFG : get('queue.maxPerMessage')
|
|
CFG --> PRO : maxPerMsg
|
|
PRO -> PRO : toProcess = commands.slice(0, maxPerMsg)
|
|
alt commands.length > maxPerMsg
|
|
PRO -> LOG : warn(`Message has ${commands.length} commands, limiting to first ${maxPerMsg}`)
|
|
LOG --> PRO : ok
|
|
end
|
|
|
|
loop for each (cmdText, idx) in toProcess
|
|
PRO -> HIS : isProcessed(el, idx)
|
|
HIS --> PRO : bool
|
|
alt already processed
|
|
PRO -> LOG : verbose(`Command #${idx+1} already executed, adding retry button`)
|
|
LOG --> PRO : ok
|
|
PRO -> ARB : addRetryButton(el, cmdText, idx)
|
|
ARB --> PRO : button added
|
|
else not processed
|
|
PRO -> LOG : verbose(`Queueing command #${idx+1} for execution`)
|
|
LOG --> PRO : ok
|
|
PRO -> RUN : run(el, cmdText, idx)
|
|
RUN --> PRO : (void)
|
|
end
|
|
end
|
|
|
|
PRO --> CL : (void)
|
|
deactivate PRO
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: extractCommands(el) ==========================================
|
|
@startuml
|
|
title main:extractCommands(el): \n Collect @bridge@...@end@ blocks with a global regex
|
|
|
|
participant "Caller" as CL
|
|
participant "extractCommands(el)" as EXT
|
|
|
|
activate CL
|
|
CL -> EXT : el
|
|
activate EXT
|
|
EXT -> EXT : text = el.textContent || ''
|
|
EXT -> EXT : out=[]; re=/@bridge@[\\s\\S]*?@end@/g; while (m=re.exec(text)) out.push(m[0])
|
|
EXT --> CL : out
|
|
deactivate EXT
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: run(el, commandText, index) ==================================
|
|
@startuml
|
|
title main:run(el, commandText, index): \n Parse, validate, optional debounce, execute via executor
|
|
|
|
participant "Caller" as CL
|
|
participant "run(el,commandText,index)" as RUN
|
|
participant "Logger" as LOG
|
|
participant "History" as HIS
|
|
participant "Parser.parse()" as PAR
|
|
participant "Parser.validate()" as VAL
|
|
participant "Config" as CFG
|
|
participant "Executor.execute()" as EXE
|
|
participant "Timer" as TMR
|
|
|
|
activate CL
|
|
CL -> RUN : el, commandText, index
|
|
activate RUN
|
|
|
|
RUN -> LOG : trace(`Starting run() for #${index+1}`, { preview })
|
|
LOG --> RUN : ok
|
|
RUN -> HIS : markProcessed(el, index)
|
|
HIS --> RUN : ok
|
|
|
|
RUN -> PAR : parse(commandText)
|
|
PAR --> RUN : parsed
|
|
|
|
RUN -> LOG : verbose(`Parsed command #${index+1}:`, { action:parsed.action, repo:parsed.repo, path:parsed.path })
|
|
LOG --> RUN : ok
|
|
|
|
RUN -> VAL : validate(parsed)
|
|
VAL --> RUN : validation
|
|
|
|
alt !validation.isValid
|
|
RUN -> LOG : error('Command validation failed', { errors, command:parsed.action })
|
|
LOG --> RUN : ok
|
|
RUN -> RUN : addRetryButton(el, commandText, index)
|
|
RUN --> CL : (void)
|
|
deactivate RUN
|
|
deactivate CL
|
|
return
|
|
else validation.example
|
|
RUN -> LOG : info('Skipping example command', { action: parsed.action })
|
|
LOG --> RUN : ok
|
|
RUN --> CL : (void)
|
|
deactivate RUN
|
|
deactivate CL
|
|
return
|
|
end
|
|
|
|
RUN -> CFG : get('execution.debounceDelay') || 0
|
|
CFG --> RUN : debounce
|
|
alt debounce > 0
|
|
RUN -> LOG : trace(`Debouncing for ${debounce}ms before execution`)
|
|
LOG --> RUN : ok
|
|
RUN -> TMR : setTimeout(debounce)
|
|
TMR --> RUN : elapsed
|
|
end
|
|
|
|
RUN -> LOG : verbose(`Executing command #${index+1}: ${parsed.action}`)
|
|
LOG --> RUN : ok
|
|
RUN -> EXE : execute(parsed, el, `Command ${index+1}`)
|
|
EXE --> RUN : result
|
|
RUN -> LOG : verbose(`Command #${index+1} completed successfully`)
|
|
LOG --> RUN : ok
|
|
|
|
RUN --> CL : (void)
|
|
deactivate RUN
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: addRetryButton(el, commandText, idx) =========================
|
|
@startuml
|
|
title main:addRetryButton(el, commandText, idx): \n Append a Run Again button bound to run()
|
|
|
|
participant "Caller" as CL
|
|
participant "addRetryButton(el,txt,idx)" as ARB
|
|
participant "DOM" as DOM
|
|
participant "run(el,txt,idx)" as RUN
|
|
|
|
activate CL
|
|
CL -> ARB : el, commandText, idx
|
|
activate ARB
|
|
ARB -> DOM : createElement('button'); set text, styles
|
|
DOM --> ARB : btn
|
|
ARB -> DOM : btn.addEventListener('click', () => RUN(...))
|
|
DOM --> ARB : wired
|
|
ARB -> DOM : el.appendChild(btn)
|
|
DOM --> ARB : appended
|
|
ARB --> CL : (void)
|
|
deactivate ARB
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: scanExisting() ===============================================
|
|
@startuml
|
|
title main:scanExisting(): \n Walk current DOM for assistant messages and process them
|
|
|
|
participant "Caller" as CL
|
|
participant "scanExisting()" as SCN
|
|
participant "Logger" as LOG
|
|
participant "DOM" as DOM
|
|
participant "isAssistantMessage(el)" as IAM
|
|
participant "processMessage(el)" as PRO
|
|
|
|
activate CL
|
|
CL -> SCN : initial request
|
|
activate SCN
|
|
|
|
SCN -> DOM : querySelectorAll(messageSelectors.join(','))
|
|
DOM --> SCN : nodes[]
|
|
SCN -> LOG : verbose(`Scanning ${nodes.length} existing message(s) on page`)
|
|
LOG --> SCN : ok
|
|
|
|
SCN -> SCN : processed=0
|
|
loop for each el in nodes
|
|
SCN -> IAM : isAssistantMessage(el)
|
|
IAM --> SCN : bool
|
|
alt true
|
|
SCN -> SCN : processed++
|
|
SCN -> PRO : processMessage(el)
|
|
PRO --> SCN : done
|
|
end
|
|
end
|
|
|
|
SCN -> LOG : info(`Scanned ${processed} existing assistant message(s)`)
|
|
LOG --> SCN : ok
|
|
SCN --> CL : (void)
|
|
deactivate SCN
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: exposeAPI() ===================================================
|
|
@startuml
|
|
title main:exposeAPI(): \n Publish helpers on window: AI_REPO, AI_REPO_STOP, AI_REPO_SET_KEY
|
|
|
|
participant "Caller" as CL
|
|
participant "exposeAPI()" as EXP
|
|
participant "window" as WIN
|
|
participant "Config" as CFG
|
|
participant "Logger" as LOG
|
|
participant "Queue" as QUE
|
|
participant "History" as HIS
|
|
|
|
activate CL
|
|
CL -> EXP : initial request
|
|
activate EXP
|
|
|
|
' AI_REPO object
|
|
EXP -> CFG : get('meta.version')
|
|
CFG --> EXP : version
|
|
EXP -> WIN : WIN.AI_REPO = { version, config:CFG, logger:LOG, history:HIS, pause(), resume(), clearHistory() }
|
|
WIN --> EXP : ok
|
|
|
|
' STOP function
|
|
EXP -> WIN : WIN.AI_REPO_STOP = () => { CFG.set('api.enabled', false); CFG.set('runtime.paused', true); const n = QUE.size()?; QUE.clear()?; LOG.error('🚨 EMERGENCY STOP...', {n}); LOG.error('API disabled and scanning paused'); }
|
|
WIN --> EXP : ok
|
|
|
|
' BridgeKey setter
|
|
EXP -> WIN : WIN.AI_REPO_SET_KEY = (k) => { if (string && trim) CFG.set('api.bridgeKey', trim); LOG.info('Bridge key updated'); return true; else LOG.warn('Invalid bridge key'); return false; }
|
|
WIN --> EXP : ok
|
|
|
|
EXP --> CL : (void)
|
|
deactivate EXP
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: delay(ms) =====================================================
|
|
@startuml
|
|
title main:delay(ms): \n Promise that resolves after ms
|
|
|
|
participant "Caller" as CL
|
|
participant "delay(ms)" as DLY
|
|
participant "Timer" as TMR
|
|
|
|
activate CL
|
|
CL -> DLY : ms
|
|
activate DLY
|
|
DLY -> TMR : setTimeout(ms)
|
|
TMR --> DLY : elapsed
|
|
DLY --> CL : Promise resolved
|
|
deactivate DLY
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: destroy() =====================================================
|
|
@startuml
|
|
title main:destroy(): \n Disconnect observer, reset flags, log teardown
|
|
|
|
participant "Caller" as CL
|
|
participant "destroy()" as DST
|
|
participant "Logger" as LOG
|
|
|
|
activate CL
|
|
CL -> DST : initial request
|
|
activate DST
|
|
DST -> DST : observer?.disconnect()
|
|
DST -> DST : processed = new WeakSet()
|
|
DST -> DST : isInitialized = false
|
|
DST -> LOG : info('AI Repo Commander destroyed')
|
|
LOG --> DST : ok
|
|
DST --> CL : (void)
|
|
deactivate DST
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== METHOD: bootstrap (DOMContentLoaded / ready) ==========================
|
|
@startuml
|
|
title main:bootstrap: \n Instantiate and initialize; start advanced detector when ready
|
|
|
|
participant "Caller" as CL
|
|
participant "bootstrap" as BOOT
|
|
participant "DOM" as DOM
|
|
participant "AIRepoCommander()" as CTOR
|
|
participant "initialize()" as INIT
|
|
participant "Detector.start()" as DET
|
|
|
|
activate CL
|
|
CL -> BOOT : module load
|
|
activate BOOT
|
|
|
|
BOOT -> DOM : document.readyState
|
|
DOM --> BOOT : 'loading' | 'complete'
|
|
alt loading
|
|
BOOT -> DOM : addEventListener('DOMContentLoaded', () => { WIN.AI_REPO_MAIN = new AIRepoCommander(); AI_REPO_MAIN.initialize(); })
|
|
DOM --> BOOT : listener attached
|
|
else ready
|
|
BOOT -> CTOR : new AIRepoCommander()
|
|
CTOR --> BOOT : instance
|
|
BOOT -> DOM : WIN.AI_REPO_MAIN = instance
|
|
BOOT -> INIT : AI_REPO_MAIN.initialize()
|
|
INIT --> BOOT : initialized
|
|
BOOT -> DET : AI_REPO_DETECTOR?.start()
|
|
DET --> BOOT : started | skipped
|
|
end
|
|
|
|
BOOT --> CL : (void)
|
|
deactivate BOOT
|
|
deactivate CL
|
|
@enduml
|
|
|
|
' ==== LEGEND ===============================================================
|
|
@startuml
|
|
legend bottom
|
|
== main 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 method.
|
|
|
|
• Sequence conventions:
|
|
1) First participant is the external caller (use "Caller" or a concrete origin).
|
|
2) Do NOT add a class lifeline; the class/module name appears in the title only.
|
|
3) Include every directly-called helper/system as a participant
|
|
(e.g., "Logger", "Config", "History", "Parser", "Executor", "Queue", "MutationObserver", "Detector", "DOM", "Timer", "window").
|
|
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 method.
|
|
B) One partition per method; soft background color; terminate branches with 'kill'.
|
|
C) Keep wording aligned with code (e.g., debounceDelay, maxPerMessage, selector list, detector.start in ready path).
|
|
|
|
• Color palette (soft pastels)
|
|
• Use --> for returns; -> for calls.
|
|
• Participants use quoted method names for internals, plain nouns for systems ("DOM", "Timer", "MutationObserver", "Detector").
|
|
• Keep this legend at the end of the file to standardize edits.
|
|
endlegend
|
|
@enduml
|