Adi12345 commited on
Commit
abab9e5
·
verified ·
1 Parent(s): 1facc74

Create dvnc_flip_insight_patch.py

Browse files
Files changed (1) hide show
  1. dvnc_flip_insight_patch.py +203 -0
dvnc_flip_insight_patch.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Any
5
+
6
+
7
+ PATCH_SCRIPT = r"""
8
+ <script>
9
+ (function () {
10
+ if (window.__dvncFlipPatchLoaded) return;
11
+ window.__dvncFlipPatchLoaded = true;
12
+
13
+ function findByFragment(fragment) {
14
+ return document.getElementById(fragment) || document.querySelector('[id*="' + fragment + '"]');
15
+ }
16
+
17
+ function getInteractiveNode(root) {
18
+ if (!root) return null;
19
+ return root.matches?.('textarea,input,button') ? root : root.querySelector?.('textarea,input,button');
20
+ }
21
+
22
+ function triggerRouteSwapPatched(idx) {
23
+ try {
24
+ const payloadRoot = findByFragment('route_swap_payload');
25
+ const payload = getInteractiveNode(payloadRoot);
26
+ if (!payload) {
27
+ console.warn('[DVNC patch] route_swap_payload not found');
28
+ return;
29
+ }
30
+
31
+ payload.focus?.();
32
+ payload.value = String(idx);
33
+
34
+ ['input', 'change'].forEach(function (name) {
35
+ payload.dispatchEvent(new Event(name, { bubbles: true }));
36
+ });
37
+
38
+ window.setTimeout(function () {
39
+ const applyRoot = findByFragment('route_swap_apply');
40
+ const applyBtn = getInteractiveNode(applyRoot) || applyRoot;
41
+ if (!applyBtn) {
42
+ console.warn('[DVNC patch] route_swap_apply not found');
43
+ return;
44
+ }
45
+ applyBtn.click?.();
46
+ }, 180);
47
+ } catch (err) {
48
+ console.error('[DVNC patch] triggerRouteSwap failed', err);
49
+ }
50
+ }
51
+
52
+ window.triggerRouteSwap = triggerRouteSwapPatched;
53
+
54
+ document.addEventListener('click', function (e) {
55
+ const mini = e.target.closest('.candidate-back .mini');
56
+ if (mini) return;
57
+
58
+ const card = e.target.closest('.candidate-card');
59
+ if (!card) return;
60
+
61
+ card.classList.toggle('flipped');
62
+ }, true);
63
+
64
+ document.addEventListener('keydown', function (e) {
65
+ const target = e.target;
66
+ if (!target || !target.closest) return;
67
+
68
+ const card = target.closest('.candidate-card');
69
+ if (!card) return;
70
+
71
+ if (e.key === 'Enter' || e.key === ' ') {
72
+ if (target.closest('.candidate-back .mini')) return;
73
+ e.preventDefault();
74
+ card.classList.toggle('flipped');
75
+ }
76
+ }, true);
77
+ })();
78
+ </script>
79
+ """
80
+
81
+ PATCH_STYLE = r"""
82
+ <style>
83
+ .candidate-card { cursor: pointer; }
84
+ .candidate-card.flipped .candidate-card-inner { transform: rotateY(180deg) !important; }
85
+ .candidate-card:hover .candidate-card-inner,
86
+ .candidate-card:focus .candidate-card-inner,
87
+ .candidate-card:focus-within .candidate-card-inner { transform: none !important; }
88
+ .candidate-back .mini { position: relative; z-index: 5; }
89
+ </style>
90
+ """
91
+
92
+
93
+ def _inject_assets(head: str) -> str:
94
+ if "__dvncFlipPatchLoaded" in head:
95
+ return head
96
+ return head + "\n" + PATCH_STYLE + "\n" + PATCH_SCRIPT
97
+
98
+
99
+ def _patch_head(module: Any) -> None:
100
+ for attr in ("HEAD", "head", "CUSTOM_HEAD"):
101
+ if hasattr(module, attr):
102
+ value = getattr(module, attr)
103
+ if isinstance(value, str):
104
+ setattr(module, attr, _inject_assets(value))
105
+ return
106
+
107
+
108
+ def _patch_cards_builder(module: Any) -> None:
109
+ if not hasattr(module, "build_cards_html"):
110
+ return
111
+
112
+ original = module.build_cards_html
113
+
114
+ def wrapped(*args, **kwargs):
115
+ out = original(*args, **kwargs)
116
+ if not isinstance(out, str):
117
+ return out
118
+ if "candidate-card" not in out:
119
+ return out
120
+
121
+ out = re.sub(
122
+ r'(<div\s+class="candidate-card"\b)',
123
+ r'\1 tabindex="0" role="button" aria-label="Flip insight card"',
124
+ out,
125
+ )
126
+ return out
127
+
128
+ module.build_cards_html = wrapped
129
+
130
+
131
+ def _build_timeline_from_state(module: Any, route_state: Any):
132
+ if route_state is None:
133
+ return None
134
+
135
+ builder = getattr(module, "build_agent_route_cards_html", None)
136
+ if not callable(builder):
137
+ return None
138
+
139
+ try:
140
+ variants = route_state.get("variants") if isinstance(route_state, dict) else None
141
+ active_idx = route_state.get("active_variant", 0) if isinstance(route_state, dict) else 0
142
+ if not variants or active_idx >= len(variants):
143
+ return None
144
+
145
+ variant = variants[active_idx] or {}
146
+ steps = variant.get("steps") or variant.get("route") or []
147
+ if not steps:
148
+ return None
149
+
150
+ normalized = []
151
+ for i, step in enumerate(steps):
152
+ if isinstance(step, dict):
153
+ normalized.append({
154
+ "step": step.get("step", i + 1),
155
+ "agent": step.get("agent", f"Step {i+1}"),
156
+ "tag": step.get("tag", step.get("mode", "")),
157
+ "summary": step.get("summary", step.get("description", "")),
158
+ })
159
+ else:
160
+ normalized.append({
161
+ "step": i + 1,
162
+ "agent": f"Step {i+1}",
163
+ "tag": "",
164
+ "summary": str(step),
165
+ })
166
+
167
+ return builder(normalized)
168
+ except Exception:
169
+ return None
170
+
171
+
172
+ def _patch_apply_route_swap(module: Any) -> None:
173
+ if not hasattr(module, "apply_route_swap"):
174
+ return
175
+
176
+ original = module.apply_route_swap
177
+
178
+ def wrapped(*args, **kwargs):
179
+ result = original(*args, **kwargs)
180
+ if not isinstance(result, tuple):
181
+ return result
182
+
183
+ if len(result) == 5:
184
+ chat_html, connectome_html, timeline_value, hypothesis_md, route_state = result
185
+ rebuilt = _build_timeline_from_state(module, route_state)
186
+ if rebuilt is not None:
187
+ gr = getattr(module, "gr", None)
188
+ if gr is not None and hasattr(gr, "update"):
189
+ timeline_value = gr.update(value=rebuilt)
190
+ else:
191
+ timeline_value = rebuilt
192
+ return chat_html, connectome_html, timeline_value, hypothesis_md, route_state
193
+
194
+ return result
195
+
196
+ module.apply_route_swap = wrapped
197
+
198
+
199
+ def apply_patch(module: Any) -> Any:
200
+ _patch_head(module)
201
+ _patch_cards_builder(module)
202
+ _patch_apply_route_swap(module)
203
+ return module