sayou-visualizer 0.0.9__py3-none-any.whl → 0.0.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sayou/visualizer/renderer/analytic_kg_renderer.py +453 -0
- sayou/visualizer/renderer/{kg_renderer.py → showcase_kg_renderer.py} +7 -4
- {sayou_visualizer-0.0.9.dist-info → sayou_visualizer-0.0.10.dist-info}/METADATA +1 -1
- {sayou_visualizer-0.0.9.dist-info → sayou_visualizer-0.0.10.dist-info}/RECORD +5 -4
- {sayou_visualizer-0.0.9.dist-info → sayou_visualizer-0.0.10.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from sayou.core.base_component import BaseComponent
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AnalyticKGRenderer(BaseComponent):
|
|
8
|
+
"""
|
|
9
|
+
Renders an interactive 2D Knowledge Graph optimized for deep analysis and topology exploration.
|
|
10
|
+
|
|
11
|
+
Agnostic to data domains (Code, Subtitles, Text), it visualizes structural relationships
|
|
12
|
+
using a 'Hub & Spoke' layout. Features interactive filtering, search capabilities,
|
|
13
|
+
and detailed node inspection (Visual IDE/Viewer) to derive insights from complex connections.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
component_name = "AnalyticKGRenderer"
|
|
17
|
+
|
|
18
|
+
STYLE_SHEET = [
|
|
19
|
+
# [Global Nodes]
|
|
20
|
+
{
|
|
21
|
+
"selector": "node",
|
|
22
|
+
"style": {
|
|
23
|
+
"label": "data(label)",
|
|
24
|
+
"color": "#ecf0f1",
|
|
25
|
+
"font-size": "10px",
|
|
26
|
+
"text-valign": "center",
|
|
27
|
+
"text-halign": "center",
|
|
28
|
+
"text-wrap": "wrap",
|
|
29
|
+
"text-max-width": "100px",
|
|
30
|
+
"background-color": "#95a5a6",
|
|
31
|
+
"border-width": 1,
|
|
32
|
+
"border-color": "#7f8c8d",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
# [File Node]
|
|
36
|
+
{
|
|
37
|
+
"selector": "node[type='file']",
|
|
38
|
+
"style": {
|
|
39
|
+
"shape": "rectangle",
|
|
40
|
+
"background-color": "#2c3e50",
|
|
41
|
+
"width": 60,
|
|
42
|
+
"height": 60,
|
|
43
|
+
"font-size": "12px",
|
|
44
|
+
"font-weight": "bold",
|
|
45
|
+
"border-width": 2,
|
|
46
|
+
"border-color": "#00d2d3",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
# [Class Node]
|
|
50
|
+
{
|
|
51
|
+
"selector": "node[type='class']",
|
|
52
|
+
"style": {
|
|
53
|
+
"shape": "diamond",
|
|
54
|
+
"background-color": "#8e44ad",
|
|
55
|
+
"width": 40,
|
|
56
|
+
"height": 40,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
# [Method/Function]
|
|
60
|
+
{
|
|
61
|
+
"selector": "node[type='method'], node[type='function']",
|
|
62
|
+
"style": {
|
|
63
|
+
"shape": "ellipse",
|
|
64
|
+
"background-color": "#e67e22",
|
|
65
|
+
"width": 25,
|
|
66
|
+
"height": 25,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
# [Code Chunk]
|
|
70
|
+
{
|
|
71
|
+
"selector": "node[type='code_block']",
|
|
72
|
+
"style": {
|
|
73
|
+
"shape": "round-rectangle",
|
|
74
|
+
"background-color": "#7f8c8d",
|
|
75
|
+
"width": 15,
|
|
76
|
+
"height": 15,
|
|
77
|
+
"label": "",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
# [Package/Library]
|
|
81
|
+
{
|
|
82
|
+
"selector": "node[type='library'], node[type='package']",
|
|
83
|
+
"style": {
|
|
84
|
+
"shape": "hexagon",
|
|
85
|
+
"background-color": "#16a085",
|
|
86
|
+
"width": 50,
|
|
87
|
+
"height": 50,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
# [Edges]
|
|
91
|
+
{
|
|
92
|
+
"selector": "edge",
|
|
93
|
+
"style": {
|
|
94
|
+
"width": 1,
|
|
95
|
+
"curve-style": "bezier",
|
|
96
|
+
"opacity": 0.6,
|
|
97
|
+
"arrow-scale": 1,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
# 1. Structure Line (contains) -> Gray Dashed Line (Skeleton)
|
|
101
|
+
{
|
|
102
|
+
"selector": "edge[edgeType='sayou:contains']",
|
|
103
|
+
"style": {
|
|
104
|
+
"line-color": "#7f8c8d",
|
|
105
|
+
"target-arrow-color": "#7f8c8d",
|
|
106
|
+
"target-arrow-shape": "circle",
|
|
107
|
+
"width": 1.5,
|
|
108
|
+
"line-style": "dashed",
|
|
109
|
+
"opacity": 0.7,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
# 2. Logic Line (imports) -> Cyan Dashed Line (Flow)
|
|
113
|
+
{
|
|
114
|
+
"selector": "edge[edgeType='sayou:imports']",
|
|
115
|
+
"style": {
|
|
116
|
+
"line-color": "#00d2d3",
|
|
117
|
+
"target-arrow-color": "#00d2d3",
|
|
118
|
+
"target-arrow-shape": "triangle",
|
|
119
|
+
"line-style": "dashed",
|
|
120
|
+
"width": 2,
|
|
121
|
+
"opacity": 0.9,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
# 3. Inheritance Line (inherits) -> Red Solid Line
|
|
125
|
+
{
|
|
126
|
+
"selector": "edge[edgeType='sayou:inherits']",
|
|
127
|
+
"style": {
|
|
128
|
+
"line-color": "#ff6b6b",
|
|
129
|
+
"target-arrow-color": "#ff6b6b",
|
|
130
|
+
"target-arrow-shape": "triangle",
|
|
131
|
+
"width": 3,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
# [Interaction]
|
|
135
|
+
{
|
|
136
|
+
"selector": ".highlighted",
|
|
137
|
+
"style": {
|
|
138
|
+
"background-color": "#f1c40f",
|
|
139
|
+
"line-color": "#f1c40f",
|
|
140
|
+
"target-arrow-color": "#f1c40f",
|
|
141
|
+
"opacity": 1,
|
|
142
|
+
"z-index": 999,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"selector": ".faded",
|
|
147
|
+
"style": {"opacity": 0.05, "label": ""},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"selector": ".found",
|
|
151
|
+
"style": {
|
|
152
|
+
"border-width": 4,
|
|
153
|
+
"border-color": "#e056fd",
|
|
154
|
+
"background-color": "#e056fd",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"selector": "node.no-label",
|
|
159
|
+
"style": {
|
|
160
|
+
"text-opacity": 0,
|
|
161
|
+
"text-background-opacity": 0,
|
|
162
|
+
"text-border-opacity": 0,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"selector": "edge.hidden-edge",
|
|
167
|
+
"style": {"display": "none"},
|
|
168
|
+
},
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
def render(self, json_path: str, output_path: str = "sayou_analyst_view.html"):
|
|
172
|
+
if not os.path.exists(json_path):
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
with open(json_path, "r", encoding="utf-8") as f:
|
|
176
|
+
raw_data = json.load(f)
|
|
177
|
+
|
|
178
|
+
elements = []
|
|
179
|
+
|
|
180
|
+
# 1. Nodes (No Parents logic)
|
|
181
|
+
for node in raw_data.get("nodes", []):
|
|
182
|
+
node_id = node.get("node_id")
|
|
183
|
+
attrs = node.get("attributes", {})
|
|
184
|
+
n_cls = node.get("node_class", "unknown").lower()
|
|
185
|
+
|
|
186
|
+
# Type Check
|
|
187
|
+
cy_type = "unknown"
|
|
188
|
+
if "file" in n_cls:
|
|
189
|
+
cy_type = "file"
|
|
190
|
+
elif "class" in n_cls:
|
|
191
|
+
cy_type = "class"
|
|
192
|
+
elif "method" in n_cls:
|
|
193
|
+
cy_type = "method"
|
|
194
|
+
elif "function" in n_cls:
|
|
195
|
+
cy_type = "function"
|
|
196
|
+
elif "library" in n_cls:
|
|
197
|
+
cy_type = "library"
|
|
198
|
+
elif "code" in n_cls:
|
|
199
|
+
cy_type = "code_block"
|
|
200
|
+
|
|
201
|
+
# Labeling
|
|
202
|
+
label = attrs.get("label") or node.get("friendly_name") or node_id
|
|
203
|
+
if cy_type == "file":
|
|
204
|
+
label = os.path.basename(attrs.get("sayou:filePath", label))
|
|
205
|
+
elif cy_type == "class":
|
|
206
|
+
label = attrs.get("meta:class_name", label)
|
|
207
|
+
elif cy_type in ["method", "function"]:
|
|
208
|
+
label = attrs.get("function_name", label)
|
|
209
|
+
|
|
210
|
+
# Code Text
|
|
211
|
+
code_text = attrs.get("schema:text", "")
|
|
212
|
+
cy_data = {
|
|
213
|
+
"id": node_id,
|
|
214
|
+
"label": label,
|
|
215
|
+
"type": cy_type,
|
|
216
|
+
"code": code_text,
|
|
217
|
+
"meta": attrs,
|
|
218
|
+
}
|
|
219
|
+
elements.append({"group": "nodes", "data": cy_data})
|
|
220
|
+
|
|
221
|
+
# 2. Edges
|
|
222
|
+
for edge in raw_data.get("edges", []):
|
|
223
|
+
elements.append(
|
|
224
|
+
{
|
|
225
|
+
"group": "edges",
|
|
226
|
+
"data": {
|
|
227
|
+
"source": edge.get("source"),
|
|
228
|
+
"target": edge.get("target"),
|
|
229
|
+
"edgeType": edge.get("type", "relates"),
|
|
230
|
+
"label": edge.get("type", "").split(":")[-1],
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
self._generate_html(elements, output_path)
|
|
236
|
+
self._log(f"✅ Pure Graph View saved to: {output_path}")
|
|
237
|
+
|
|
238
|
+
def _generate_html(self, elements: list, output_path: str):
|
|
239
|
+
style_json = json.dumps(self.STYLE_SHEET)
|
|
240
|
+
elements_json = json.dumps(elements)
|
|
241
|
+
|
|
242
|
+
html_content = f"""
|
|
243
|
+
<!DOCTYPE html>
|
|
244
|
+
<html>
|
|
245
|
+
<head>
|
|
246
|
+
<meta charset="UTF-8">
|
|
247
|
+
<title>Sayou Code Universe</title>
|
|
248
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.28.1/cytoscape.min.js"></script>
|
|
249
|
+
<script src="https://unpkg.com/layout-base/layout-base.js"></script>
|
|
250
|
+
<script src="https://unpkg.com/cose-base/cose-base.js"></script>
|
|
251
|
+
<script src="https://unpkg.com/cytoscape-fcose/cytoscape-fcose.js"></script>
|
|
252
|
+
|
|
253
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
|
254
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
255
|
+
|
|
256
|
+
<style>
|
|
257
|
+
body {{ font-family: 'Segoe UI', sans-serif; margin: 0; background: #1e272e; color: #dcdde1; overflow: hidden; display: flex; }}
|
|
258
|
+
#cy {{ flex-grow: 1; height: 100vh; }}
|
|
259
|
+
|
|
260
|
+
/* Sidebar & Controls */
|
|
261
|
+
#controls {{
|
|
262
|
+
position: absolute; top: 20px; left: 20px; z-index: 100;
|
|
263
|
+
background: rgba(47, 54, 64, 0.95); padding: 15px; border-radius: 8px;
|
|
264
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.3); border: 1px solid #4b6584; width: 280px;
|
|
265
|
+
}}
|
|
266
|
+
#tooltip {{
|
|
267
|
+
position: absolute;
|
|
268
|
+
display: none;
|
|
269
|
+
background: rgba(0, 0, 0, 0.8);
|
|
270
|
+
color: #fff;
|
|
271
|
+
padding: 5px 10px;
|
|
272
|
+
border-radius: 4px;
|
|
273
|
+
font-size: 11px;
|
|
274
|
+
pointer-events: none;
|
|
275
|
+
z-index: 1000;
|
|
276
|
+
border: 1px solid #00d2d3;
|
|
277
|
+
}}
|
|
278
|
+
.search-group {{ display: flex; gap: 5px; margin-bottom: 10px; }}
|
|
279
|
+
input[type="text"] {{ flex-grow: 1; background: #222; border: 1px solid #57606f; color: white; padding: 6px; border-radius: 4px; }}
|
|
280
|
+
button {{ background: #4b6584; border: none; color: white; padding: 6px 12px; border-radius: 4px; cursor: pointer; }}
|
|
281
|
+
button:hover {{ background: #00d2d3; color: #000; }}
|
|
282
|
+
|
|
283
|
+
#sidebar {{
|
|
284
|
+
width: 450px; height: 100vh; background: #2f3640; border-left: 1px solid #4b6584;
|
|
285
|
+
display: flex; flex-direction: column; transform: translateX(450px); transition: 0.3s;
|
|
286
|
+
position: absolute; right: 0; top: 0; z-index: 200; box-shadow: -5px 0 15px rgba(0,0,0,0.5);
|
|
287
|
+
}}
|
|
288
|
+
#sidebar.open {{ transform: translateX(0); }}
|
|
289
|
+
.header {{ padding: 20px; border-bottom: 1px solid #4b6584; background: #252a34; }}
|
|
290
|
+
.node-title {{ font-size: 18px; color: #fff; font-weight: bold; margin: 0; }}
|
|
291
|
+
.content {{ flex-grow: 1; overflow-y: auto; padding: 0; background: #282c34; }}
|
|
292
|
+
pre {{ margin: 0; }}
|
|
293
|
+
</style>
|
|
294
|
+
</head>
|
|
295
|
+
<body>
|
|
296
|
+
<div id="controls">
|
|
297
|
+
<div style="font-weight:bold; color:#00d2d3; margin-bottom:10px;">Sayou Graph</div>
|
|
298
|
+
<div class="search-group">
|
|
299
|
+
<input type="text" id="search-input" placeholder="Search..." onkeyup="if(event.key === 'Enter') searchNode()">
|
|
300
|
+
<button onclick="searchNode()">🔍</button>
|
|
301
|
+
</div>
|
|
302
|
+
<div style="margin-bottom: 10px; display: flex; gap: 5px;">
|
|
303
|
+
<button id="btn-label" class="active" onclick="toggleLabels()" style="flex:1;">Labels ON</button>
|
|
304
|
+
<button id="btn-line" class="active" onclick="toggleLines()" style="flex:1;">Lines ON</button>
|
|
305
|
+
</div>
|
|
306
|
+
<div style="font-size:11px; color:#aaa; border-top:1px solid #57606f; padding-top:10px;">
|
|
307
|
+
<button onclick="runLayout()">Re-Layout</button>
|
|
308
|
+
<button onclick="resetView()">Reset Cam</button>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<div id="cy"></div>
|
|
313
|
+
<div id="tooltip"></div>
|
|
314
|
+
|
|
315
|
+
<div id="sidebar">
|
|
316
|
+
<div class="header">
|
|
317
|
+
<h2 class="node-title" id="sb-title">Details</h2>
|
|
318
|
+
<div id="sb-desc" style="font-size:12px; color:#aaa; margin-top:5px;"></div>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="content">
|
|
321
|
+
<pre><code id="sb-code" class="language-python"></code></pre>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<script>
|
|
326
|
+
var cy = cytoscape({{
|
|
327
|
+
container: document.getElementById('cy'),
|
|
328
|
+
elements: {elements_json},
|
|
329
|
+
style: {style_json},
|
|
330
|
+
layout: {{
|
|
331
|
+
name: 'fcose',
|
|
332
|
+
quality: 'proof',
|
|
333
|
+
nodeSeparation: 75,
|
|
334
|
+
idealEdgeLength: edge => edge.data('edgeType') === 'sayou:contains' ? 50 : 200,
|
|
335
|
+
animate: false
|
|
336
|
+
}}
|
|
337
|
+
}});
|
|
338
|
+
|
|
339
|
+
function runLayout() {{
|
|
340
|
+
cy.layout({{ name: 'fcose', animate: true, animationDuration: 800 }}).run();
|
|
341
|
+
}}
|
|
342
|
+
|
|
343
|
+
// [Search]
|
|
344
|
+
function searchNode() {{
|
|
345
|
+
var query = document.getElementById('search-input').value.toLowerCase();
|
|
346
|
+
if(!query) return;
|
|
347
|
+
cy.elements().removeClass('found');
|
|
348
|
+
var found = cy.nodes().filter(ele => ele.data('label').toLowerCase().includes(query));
|
|
349
|
+
if(found.length > 0) {{
|
|
350
|
+
found.addClass('found');
|
|
351
|
+
// cy.animate({{ fit: {{ eles: found, padding: 50 }}, duration: 500 }});
|
|
352
|
+
// cy.center(found);
|
|
353
|
+
}}
|
|
354
|
+
}}
|
|
355
|
+
|
|
356
|
+
function resetView() {{
|
|
357
|
+
cy.elements().removeClass('highlighted faded found');
|
|
358
|
+
document.getElementById('sidebar').classList.remove('open');
|
|
359
|
+
cy.animate({{ fit: {{ padding: 50 }} }});
|
|
360
|
+
}}
|
|
361
|
+
|
|
362
|
+
// 1. Label Toggle (ON/OFF)
|
|
363
|
+
var labelsVisible = true;
|
|
364
|
+
function toggleLabels() {{
|
|
365
|
+
labelsVisible = !labelsVisible;
|
|
366
|
+
var btn = document.getElementById('btn-label');
|
|
367
|
+
|
|
368
|
+
if (labelsVisible) {{
|
|
369
|
+
cy.nodes().removeClass('no-label');
|
|
370
|
+
btn.innerText = "Labels ON";
|
|
371
|
+
btn.style.background = "#4b6584";
|
|
372
|
+
btn.style.color = "white";
|
|
373
|
+
}} else {{
|
|
374
|
+
cy.nodes().addClass('no-label');
|
|
375
|
+
btn.innerText = "Labels OFF";
|
|
376
|
+
btn.style.background = "#2f3542";
|
|
377
|
+
btn.style.color = "#747d8c";
|
|
378
|
+
}}
|
|
379
|
+
}}
|
|
380
|
+
|
|
381
|
+
// 2. Line Toggle (ON/OFF)
|
|
382
|
+
var linesVisible = true;
|
|
383
|
+
function toggleLines() {{
|
|
384
|
+
linesVisible = !linesVisible;
|
|
385
|
+
var btn = document.getElementById('btn-line');
|
|
386
|
+
// var edges = cy.edges('[edgeType="sayou:contains"]');
|
|
387
|
+
var edges = cy.edges();
|
|
388
|
+
|
|
389
|
+
if (linesVisible) {{
|
|
390
|
+
edges.removeClass('hidden-edge');
|
|
391
|
+
btn.innerText = "Lines ON";
|
|
392
|
+
btn.style.background = "#4b6584";
|
|
393
|
+
btn.style.color = "white";
|
|
394
|
+
}} else {{
|
|
395
|
+
edges.addClass('hidden-edge');
|
|
396
|
+
btn.innerText = "Lines OFF";
|
|
397
|
+
btn.style.background = "#2f3542";
|
|
398
|
+
btn.style.color = "#747d8c";
|
|
399
|
+
}}
|
|
400
|
+
}}
|
|
401
|
+
|
|
402
|
+
// [Click Interaction]
|
|
403
|
+
cy.on('tap', 'node', function(evt){{
|
|
404
|
+
var node = evt.target;
|
|
405
|
+
|
|
406
|
+
// Highlight neighbors
|
|
407
|
+
cy.elements().removeClass('highlighted faded');
|
|
408
|
+
var neighbors = node.neighborhood().add(node);
|
|
409
|
+
cy.elements().addClass('faded');
|
|
410
|
+
neighbors.removeClass('faded').addClass('highlighted');
|
|
411
|
+
|
|
412
|
+
// Sidebar
|
|
413
|
+
document.getElementById('sidebar').classList.add('open');
|
|
414
|
+
document.getElementById('sb-title').innerText = node.data('label');
|
|
415
|
+
document.getElementById('sb-desc').innerText = (node.data('type') || 'Unknown').toUpperCase() + ' | ' + node.id();
|
|
416
|
+
|
|
417
|
+
var codeArea = document.getElementById('sb-code');
|
|
418
|
+
codeArea.innerText = node.data('code') || JSON.stringify(node.data('meta'), null, 2);
|
|
419
|
+
hljs.highlightElement(codeArea);
|
|
420
|
+
}});
|
|
421
|
+
|
|
422
|
+
cy.on('tap', function(evt){{
|
|
423
|
+
if(evt.target === cy) resetView();
|
|
424
|
+
}});
|
|
425
|
+
|
|
426
|
+
var tooltip = document.getElementById('tooltip');
|
|
427
|
+
|
|
428
|
+
cy.on('mouseover', 'node', function(evt){{
|
|
429
|
+
var node = evt.target;
|
|
430
|
+
var label = node.data('label') || node.data('id');
|
|
431
|
+
|
|
432
|
+
tooltip.style.display = 'block';
|
|
433
|
+
tooltip.innerText = label;
|
|
434
|
+
tooltip.style.left = evt.renderedPosition.x + 'px';
|
|
435
|
+
// var pos = node.renderedPosition();
|
|
436
|
+
// tooltip.style.left = (pos.x + 10) + 'px';
|
|
437
|
+
// tooltip.style.top = (pos.y + 10) + 'px';
|
|
438
|
+
}});
|
|
439
|
+
|
|
440
|
+
cy.on('mousemove', function(evt){{
|
|
441
|
+
tooltip.style.left = (evt.renderedPosition.x + 15) + 'px';
|
|
442
|
+
tooltip.style.top = (evt.renderedPosition.y + 15) + 'px';
|
|
443
|
+
}});
|
|
444
|
+
|
|
445
|
+
cy.on('mouseout', 'node', function(){{
|
|
446
|
+
tooltip.style.display = 'none';
|
|
447
|
+
}});
|
|
448
|
+
</script>
|
|
449
|
+
</body>
|
|
450
|
+
</html>
|
|
451
|
+
"""
|
|
452
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
453
|
+
f.write(html_content)
|
|
@@ -5,13 +5,16 @@ from collections import defaultdict
|
|
|
5
5
|
from sayou.core.base_component import BaseComponent
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class ShowcaseKGRenderer(BaseComponent):
|
|
9
9
|
"""
|
|
10
|
-
Renders 3D
|
|
11
|
-
|
|
10
|
+
Renders an immersive 3D Knowledge Graph designed for high-level visualization and presentation.
|
|
11
|
+
|
|
12
|
+
Focuses on the 'Big Picture' and aesthetic impact using spatial layouts and semantic coloring.
|
|
13
|
+
Ideal for demonstrating data scale, clustering patterns, and global connectivity (Wow Effect),
|
|
14
|
+
rather than granular node analysis.
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
|
-
component_name = "
|
|
17
|
+
component_name = "ShowcaseKGRenderer"
|
|
15
18
|
|
|
16
19
|
def render(self, json_path: str, output_path: str = "kg_view_3d.html"):
|
|
17
20
|
if not os.path.exists(json_path):
|
|
@@ -3,11 +3,12 @@ sayou/visualizer/pipeline.py,sha256=HCNxVEVO_XfhCIX5ZmTlab36EDmSx22J_916enSVGL8,
|
|
|
3
3
|
sayou/visualizer/core/exceptions.py,sha256=Mk5UtIfim7i9688c4qAKP7kB1GpLPM29t94HbQM9fhw,99
|
|
4
4
|
sayou/visualizer/core/schemas.py,sha256=qn44BINevFZF_ALBhh20DS4GyMo5HV3UzqY4UTh_p3A,381
|
|
5
5
|
sayou/visualizer/interfaces/base_renderer.py,sha256=orllTXlqM4-wDemOWbcZX8zF708KOdWFgoqZh8MeAzE,760
|
|
6
|
-
sayou/visualizer/renderer/
|
|
6
|
+
sayou/visualizer/renderer/analytic_kg_renderer.py,sha256=I5ccv7JUCGvPE79c2d1O-XuYwrK3fxk9qj4Xzyx_WLg,16404
|
|
7
7
|
sayou/visualizer/renderer/pyvis_renderer.py,sha256=2HKv_qAKKNHMKLwC7xoIn3EZ5oFLpgds7LPgK4EeXfA,2408
|
|
8
|
+
sayou/visualizer/renderer/showcase_kg_renderer.py,sha256=vbVWP3XTVOrmybv9ok57dig0PszXM28Fd7cRudonfHw,11803
|
|
8
9
|
sayou/visualizer/tracer/graph_tracer.py,sha256=j0dqd0_67ZnQCNjn5siKHXXsXasWim9olYqUQA2jKxk,3638
|
|
9
10
|
sayou/visualizer/tracer/rich_tracer.py,sha256=ik7J1P7AMTN47lkjMLE7iTlRuksUpwSKluvWdpY6kdQ,2406
|
|
10
11
|
sayou/visualizer/tracer/websocket_tracer.py,sha256=OZLg4jTfuxp6IwDacmAACKZ_0FirZFhyLysqh9QyrJA,1626
|
|
11
|
-
sayou_visualizer-0.0.
|
|
12
|
-
sayou_visualizer-0.0.
|
|
13
|
-
sayou_visualizer-0.0.
|
|
12
|
+
sayou_visualizer-0.0.10.dist-info/METADATA,sha256=HOeqPwEwHpHZ29VQ_473xYTYn3ff4RSvg0s-4g1qPUc,16681
|
|
13
|
+
sayou_visualizer-0.0.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
+
sayou_visualizer-0.0.10.dist-info/RECORD,,
|
|
File without changes
|