4 / dvnc_flip_insight_patch.py
Adi12345's picture
Create dvnc_flip_insight_patch.py
abab9e5 verified
Raw
History Blame Contribute Delete
6.05 kB
from __future__ import annotations
import re
from typing import Any
PATCH_SCRIPT = r"""
<script>
(function () {
if (window.__dvncFlipPatchLoaded) return;
window.__dvncFlipPatchLoaded = true;
function findByFragment(fragment) {
return document.getElementById(fragment) || document.querySelector('[id*="' + fragment + '"]');
}
function getInteractiveNode(root) {
if (!root) return null;
return root.matches?.('textarea,input,button') ? root : root.querySelector?.('textarea,input,button');
}
function triggerRouteSwapPatched(idx) {
try {
const payloadRoot = findByFragment('route_swap_payload');
const payload = getInteractiveNode(payloadRoot);
if (!payload) {
console.warn('[DVNC patch] route_swap_payload not found');
return;
}
payload.focus?.();
payload.value = String(idx);
['input', 'change'].forEach(function (name) {
payload.dispatchEvent(new Event(name, { bubbles: true }));
});
window.setTimeout(function () {
const applyRoot = findByFragment('route_swap_apply');
const applyBtn = getInteractiveNode(applyRoot) || applyRoot;
if (!applyBtn) {
console.warn('[DVNC patch] route_swap_apply not found');
return;
}
applyBtn.click?.();
}, 180);
} catch (err) {
console.error('[DVNC patch] triggerRouteSwap failed', err);
}
}
window.triggerRouteSwap = triggerRouteSwapPatched;
document.addEventListener('click', function (e) {
const mini = e.target.closest('.candidate-back .mini');
if (mini) return;
const card = e.target.closest('.candidate-card');
if (!card) return;
card.classList.toggle('flipped');
}, true);
document.addEventListener('keydown', function (e) {
const target = e.target;
if (!target || !target.closest) return;
const card = target.closest('.candidate-card');
if (!card) return;
if (e.key === 'Enter' || e.key === ' ') {
if (target.closest('.candidate-back .mini')) return;
e.preventDefault();
card.classList.toggle('flipped');
}
}, true);
})();
</script>
"""
PATCH_STYLE = r"""
<style>
.candidate-card { cursor: pointer; }
.candidate-card.flipped .candidate-card-inner { transform: rotateY(180deg) !important; }
.candidate-card:hover .candidate-card-inner,
.candidate-card:focus .candidate-card-inner,
.candidate-card:focus-within .candidate-card-inner { transform: none !important; }
.candidate-back .mini { position: relative; z-index: 5; }
</style>
"""
def _inject_assets(head: str) -> str:
if "__dvncFlipPatchLoaded" in head:
return head
return head + "\n" + PATCH_STYLE + "\n" + PATCH_SCRIPT
def _patch_head(module: Any) -> None:
for attr in ("HEAD", "head", "CUSTOM_HEAD"):
if hasattr(module, attr):
value = getattr(module, attr)
if isinstance(value, str):
setattr(module, attr, _inject_assets(value))
return
def _patch_cards_builder(module: Any) -> None:
if not hasattr(module, "build_cards_html"):
return
original = module.build_cards_html
def wrapped(*args, **kwargs):
out = original(*args, **kwargs)
if not isinstance(out, str):
return out
if "candidate-card" not in out:
return out
out = re.sub(
r'(<div\s+class="candidate-card"\b)',
r'\1 tabindex="0" role="button" aria-label="Flip insight card"',
out,
)
return out
module.build_cards_html = wrapped
def _build_timeline_from_state(module: Any, route_state: Any):
if route_state is None:
return None
builder = getattr(module, "build_agent_route_cards_html", None)
if not callable(builder):
return None
try:
variants = route_state.get("variants") if isinstance(route_state, dict) else None
active_idx = route_state.get("active_variant", 0) if isinstance(route_state, dict) else 0
if not variants or active_idx >= len(variants):
return None
variant = variants[active_idx] or {}
steps = variant.get("steps") or variant.get("route") or []
if not steps:
return None
normalized = []
for i, step in enumerate(steps):
if isinstance(step, dict):
normalized.append({
"step": step.get("step", i + 1),
"agent": step.get("agent", f"Step {i+1}"),
"tag": step.get("tag", step.get("mode", "")),
"summary": step.get("summary", step.get("description", "")),
})
else:
normalized.append({
"step": i + 1,
"agent": f"Step {i+1}",
"tag": "",
"summary": str(step),
})
return builder(normalized)
except Exception:
return None
def _patch_apply_route_swap(module: Any) -> None:
if not hasattr(module, "apply_route_swap"):
return
original = module.apply_route_swap
def wrapped(*args, **kwargs):
result = original(*args, **kwargs)
if not isinstance(result, tuple):
return result
if len(result) == 5:
chat_html, connectome_html, timeline_value, hypothesis_md, route_state = result
rebuilt = _build_timeline_from_state(module, route_state)
if rebuilt is not None:
gr = getattr(module, "gr", None)
if gr is not None and hasattr(gr, "update"):
timeline_value = gr.update(value=rebuilt)
else:
timeline_value = rebuilt
return chat_html, connectome_html, timeline_value, hypothesis_md, route_state
return result
module.apply_route_swap = wrapped
def apply_patch(module: Any) -> Any:
_patch_head(module)
_patch_cards_builder(module)
_patch_apply_route_swap(module)
return module