AI-Repo-Commander/Docs/diagrams/file_diagrams/paste-submit.puml

399 lines
11 KiB
Plaintext

' ===================================================================
' File: paste-submit.puml
' Purpose: Single source of truth for module-level activity + per-function sequences.
' Module: paste-submit.js — Find composer, paste text robustly, and optionally submit.
' 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 (paste-submit.js) ==================================
@startuml
title paste-submit.js — Branch Flow (full module)
start
:paste-submit;
fork
' -------- findComposer() --------
partition "findComposer()" #E7FAE3 {
:findComposer;
:iterate selectors in priority order;\nskip hidden/disabled/offsetParent null (unless fixed);
:return first visible editor or null;
kill
}
fork again
' -------- findSendButton(scopeEl) --------
partition "findSendButton(scopeEl)" #FFF6D1 {
:findSendButton;
:scope = closest(form/composer/main/body);\nquery candidate buttons; filter disabled/hidden;
:return button or null;
kill
}
fork again
' -------- pressEnter(el) --------
partition "pressEnter(el)" #FFE1DB {
:pressEnter;
:dispatch keydown/keypress/keyup Enter;\nreturn success boolean;
kill
}
fork again
' -------- waitReady(timeoutMs) --------
partition "waitReady(timeoutMs)" #DCF9EE {
:waitReady;
:loop until timeout: findComposer();\nensure not busy and editor empty;
:return true/false;
kill
}
fork again
' -------- pasteInto(el, text) --------
partition "pasteInto(el, text)" #FFE6F0 {
:pasteInto;
:payload = maybe add trailing newline (cfg.ui.appendTrailingNewline);
:try ClipboardEvent path -> return true;
:if ProseMirror -> set text + input -> true;
:if contentEditable -> selection insert + input -> true;
:if textarea/input -> value + input -> true;
:fallback GM_setClipboard + alert -> true;
:else return false;
kill
}
fork again
' -------- submitToComposer(text) --------
partition "submitToComposer(text)" #E6F3FF {
:submitToComposer;
:auto = cfg.ui.autoSubmit;\nok = waitReady(...);\nif !ok -> warn + false;
:el = findComposer(); pasteInto if text;\npostPasteDelay; if !auto -> true;
:btn = findSendButton(el)? click : pressEnter(el);
:return boolean;
kill
}
end fork
@enduml
' ==== METHOD: findComposer() ===============================================
@startuml
title paste-submit:findComposer(): \n Locate a visible, interactable chat editor element
participant "Caller" as CL
participant "findComposer()" as FC
participant "DOM" as DOM
activate CL
CL -> FC : initial request
activate FC
FC -> FC : for s in selectors (priority list)
FC -> DOM : querySelector(s)
DOM --> FC : el | null
alt el found
FC -> FC : st = getComputedStyle(el)\nif hidden/visibility:hidden -> continue\nif offsetParent==null && position!='fixed' -> continue
alt visible + interactable
FC --> CL : el
deactivate FC
deactivate CL
return
else skip
end
end
FC --> CL : null
deactivate FC
deactivate CL
@enduml
' ==== METHOD: findSendButton(scopeEl) ======================================
@startuml
title paste-submit:findSendButton(scopeEl): \n Find a nearby, visible Send/Submit button
participant "Caller" as CL
participant "findSendButton(scopeEl)" as FSB
participant "DOM" as DOM
activate CL
CL -> FSB : scopeEl
activate FSB
FSB -> DOM : scope = scopeEl.closest('form,[data-testid="composer"],main,body') || document
DOM --> FSB : scope
FSB -> FSB : for s in candidate selectors
FSB -> DOM : scope.querySelector(s) || document.querySelector(s)
DOM --> FSB : b | null
alt b found
FSB -> FSB : st = getComputedStyle(b); disabled/hidden/offsetParent?
alt clickable
FSB --> CL : b
deactivate FSB
deactivate CL
return
else continue
end
end
FSB --> CL : null
deactivate FSB
deactivate CL
@enduml
' ==== METHOD: pressEnter(el) ===============================================
@startuml
title paste-submit:pressEnter(el): \n Simulate Enter keystrokes against the editor
participant "Caller" as CL
participant "pressEnter(el)" as PE
participant "DOM" as DOM
activate CL
CL -> PE : el
activate PE
loop for t in ['keydown','keypress','keyup']
PE -> DOM : dispatch KeyboardEvent(t,{Enter, bubbles:true, cancelable:true})
DOM --> PE : ok (boolean)
alt not ok
PE --> CL : false
deactivate PE
deactivate CL
return
end
end
PE --> CL : true
deactivate PE
deactivate CL
@enduml
' ==== METHOD: waitReady(timeoutMs) =========================================
@startuml
title paste-submit:waitReady(timeoutMs): \n Ensure composer exists, is idle, and empty
participant "Caller" as CL
participant "waitReady(timeoutMs)" as WR
participant "findComposer()" as FC
participant "DOM" as DOM
participant "Timer" as TMR
activate CL
CL -> WR : timeoutMs
activate WR
WR -> WR : start = now
loop until now - start < timeoutMs
WR -> FC : findComposer()
FC --> WR : el | null
alt el present
WR -> WR : current = (el.textContent || el.value || '').trim()
WR -> DOM : search closest(...).querySelector('[aria-busy="true"],[data-state="loading"],.typing-indicator')
DOM --> WR : busyEl | null
alt !busyEl && current.length == 0
WR --> CL : true
deactivate WR
deactivate CL
return
end
end
WR -> TMR : setTimeout(200ms)
TMR --> WR : tick
end
WR --> CL : false
deactivate WR
deactivate CL
@enduml
' ==== METHOD: pasteInto(el, text) ==========================================
@startuml
title paste-submit:pasteInto(el, text): \n Paste via multiple strategies from cleanest to fallback
participant "Caller" as CL
participant "pasteInto(el, text)" as PI
participant "Config" as CFG
participant "DOM" as DOM
participant "GM_setClipboard" as CLP
activate CL
CL -> PI : el, text
activate PI
' payload prep
PI -> CFG : get('ui.appendTrailingNewline')
CFG --> PI : boolean
PI -> PI : payload = (append?\n text.endsWith('\\n')?text:text+'\\n' : text)
' Strategy 1: ClipboardEvent path
PI -> PI : try { dt = new DataTransfer(); dt.setData('text/plain', payload) }
PI -> DOM : dispatch ClipboardEvent('paste', {clipboardData:dt,bubbles:true,cancelable:true})
DOM --> PI : dispatched/blocked
alt event accepted and not defaultPrevented
PI --> CL : true
deactivate PI
deactivate CL
return
end
' Strategy 2: ProseMirror
PI -> PI : if el.classList.contains('ProseMirror')\n el.innerHTML=''; append text node; dispatch 'input'
alt ProseMirror handled
PI --> CL : true
deactivate PI
deactivate CL
return
end
' Strategy 3: contentEditable selection
PI -> PI : if el.isContentEditable or contenteditable='true'
PI -> DOM : ensure selection range at end
DOM --> PI : range | null
alt range present
PI -> DOM : range.deleteContents(); insertTextNode(payload); move caret; dispatch 'input'
DOM --> PI : ok
PI --> CL : true
deactivate PI
deactivate CL
return
end
' Strategy 4: textarea/input
PI -> PI : if tagName in {TEXTAREA, INPUT}\n el.value=payload; dispatch 'input'
alt text control handled
PI --> CL : true
deactivate PI
deactivate CL
return
end
' Strategy 5: clipboard fallback
PI -> CLP : GM_setClipboard(payload, {type:'text',mimetype:'text/plain'})
CLP --> PI : ok | error
alt ok
PI -> DOM : alert('Content copied to clipboard. Press Ctrl/Cmd+V to paste.')
DOM --> PI : shown
PI --> CL : true
else failed
PI --> CL : false
end
deactivate PI
deactivate CL
@enduml
' ==== METHOD: submitToComposer(text) =======================================
@startuml
title paste-submit:submitToComposer(text): \n Wait until ready, paste text, and optionally auto-submit
participant "Caller" as CL
participant "submitToComposer(text)" as SUB
participant "Config" as CFG
participant "Logger" as LOG
participant "waitReady(timeoutMs)" as WR
participant "findComposer()" as FC
participant "pasteInto(el, text)" as PI
participant "findSendButton(scopeEl)" as FSB
participant "pressEnter(el)" as PE
participant "Timer" as TMR
activate CL
CL -> SUB : text
activate SUB
' readiness
SUB -> CFG : get('ui.autoSubmit')
CFG --> SUB : auto (bool)
SUB -> CFG : get('execution.settleCheckMs') || 1200
CFG --> SUB : settleMs
SUB -> WR : waitReady(settleMs)
WR --> SUB : ok (bool)
alt !ok
SUB -> LOG : warn("Composer not ready")
SUB --> CL : false
deactivate SUB
deactivate CL
return
end
' find composer + paste
SUB -> FC : findComposer()
FC --> SUB : el | null
alt el is null
SUB -> LOG : warn("Composer not found")
SUB --> CL : false
deactivate SUB
deactivate CL
return
end
alt text provided
SUB -> PI : pasteInto(el, text)
PI --> SUB : pasted (bool)
alt !pasted
SUB -> LOG : warn("Paste failed")
SUB --> CL : false
deactivate SUB
deactivate CL
return
end
end
' post-paste delay
SUB -> CFG : get('ui.postPasteDelayMs') || 600
CFG --> SUB : postMs
SUB -> TMR : setTimeout(postMs)
TMR --> SUB : wake
alt auto == false
SUB --> CL : true
deactivate SUB
deactivate CL
return
end
' submit by button or enter
SUB -> FSB : findSendButton(el)
FSB --> SUB : btn | null
alt btn found
SUB -> SUB : btn.click()
SUB --> CL : true
else no button
SUB -> PE : pressEnter(el)
PE --> SUB : ok (bool)
SUB --> CL : ok
end
deactivate SUB
deactivate CL
@enduml
' ==== LEGEND ===============================================================
@startuml
legend bottom
== paste-submit 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 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 module lifeline; the module name appears in the title only.
3) Include every directly-called method or subsystem as a participant
(e.g., "findComposer()", "pasteInto()", "findSendButton()", "submitToComposer()", "pressEnter()", "waitReady()", "Config", "Logger", "DOM", "Timer", "GM_setClipboard").
4) Prefer simple messages; Use --> for returns; -> for calls.
5) Use activate/deactivate as you see fit for clarity (no strict rule).
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., selector heuristics, delay knobs, config flags).
• Color palette (soft pastels)
• Use --> for returns; -> for calls.
• Participants use quoted method names for internals (e.g., "pasteInto()"), and plain nouns for systems ("DOM", "GM_setClipboard", "Timer").
• Keep this legend at the end of the file to standardize edits.
endlegend
@enduml