Spaces:
Paused
Paused
Savestate: Final fixes for detective board nodes, lines, auto-scroll centering and translations
Browse files- index.html +1 -1
- src/ai/research-agent.js +4 -3
- src/ai/research-canvas.js +46 -56
- src/styles/detective-board.css +2 -2
index.html
CHANGED
|
@@ -65,7 +65,7 @@
|
|
| 65 |
</head>
|
| 66 |
<body class="dark-theme in-chat-view" data-bg-style="fluid-dark">
|
| 67 |
|
| 68 |
-
<div id="live-version-badge" style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); opacity: 0.6; z-index: 99999; pointer-events: none; font-weight: 500; font-family: monospace; letter-spacing: 1px; background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">BUILD 1.8.
|
| 69 |
|
| 70 |
<!-- SVG Filters for Liquid Glass Refraction -->
|
| 71 |
<svg width="0" height="0" style="position: absolute; pointer-events: none;">
|
|
|
|
| 65 |
</head>
|
| 66 |
<body class="dark-theme in-chat-view" data-bg-style="fluid-dark">
|
| 67 |
|
| 68 |
+
<div id="live-version-badge" style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); opacity: 0.6; z-index: 99999; pointer-events: none; font-weight: 500; font-family: monospace; letter-spacing: 1px; background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">BUILD 1.8.180</div>
|
| 69 |
|
| 70 |
<!-- SVG Filters for Liquid Glass Refraction -->
|
| 71 |
<svg width="0" height="0" style="position: absolute; pointer-events: none;">
|
src/ai/research-agent.js
CHANGED
|
@@ -127,7 +127,7 @@ export class ResearchAgent {
|
|
| 127 |
for (let i = 0; i < queries.length; i++) {
|
| 128 |
try {
|
| 129 |
const queryLabel = queries[i].length > 40 ? queries[i].substring(0, 40) + '...' : queries[i];
|
| 130 |
-
onGraphEvent({ type: 'node', id: `q_${iteration}_${i}`, label: `
|
| 131 |
onGraphEvent({ type: 'edge', from: 'root', to: `q_${iteration}_${i}` });
|
| 132 |
|
| 133 |
const hits = await this.searchDDG(queries[i], 3);
|
|
@@ -158,8 +158,9 @@ export class ResearchAgent {
|
|
| 158 |
newDocs.forEach(doc => {
|
| 159 |
const srcInfo = uniqueSourcesMap.get(doc.url);
|
| 160 |
if (srcInfo && srcInfo.queryId) {
|
| 161 |
-
|
| 162 |
-
|
|
|
|
| 163 |
onGraphEvent({ type: 'edge', from: srcInfo.queryId, to: `s_${doc.url}` });
|
| 164 |
}
|
| 165 |
});
|
|
|
|
| 127 |
for (let i = 0; i < queries.length; i++) {
|
| 128 |
try {
|
| 129 |
const queryLabel = queries[i].length > 40 ? queries[i].substring(0, 40) + '...' : queries[i];
|
| 130 |
+
onGraphEvent({ type: 'node', id: `q_${iteration}_${i}`, label: `Suchanfrage: ${queryLabel}`, group: 'query' });
|
| 131 |
onGraphEvent({ type: 'edge', from: 'root', to: `q_${iteration}_${i}` });
|
| 132 |
|
| 133 |
const hits = await this.searchDDG(queries[i], 3);
|
|
|
|
| 158 |
newDocs.forEach(doc => {
|
| 159 |
const srcInfo = uniqueSourcesMap.get(doc.url);
|
| 160 |
if (srcInfo && srcInfo.queryId) {
|
| 161 |
+
let sourceLabel = srcInfo.title;
|
| 162 |
+
if (sourceLabel.length > 35) sourceLabel = sourceLabel.substring(0, 35) + '...';
|
| 163 |
+
onGraphEvent({ type: 'node', id: `s_${doc.url}`, label: `Quelle: ${sourceLabel}`, group: 'source' });
|
| 164 |
onGraphEvent({ type: 'edge', from: srcInfo.queryId, to: `s_${doc.url}` });
|
| 165 |
}
|
| 166 |
});
|
src/ai/research-canvas.js
CHANGED
|
@@ -43,22 +43,11 @@ function scrollToLatestNode() {
|
|
| 43 |
const node = nodesMap.get(latestNodeId);
|
| 44 |
if (!node) return;
|
| 45 |
|
| 46 |
-
//
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
// So target Y on screen = +innerHeight * 0.25
|
| 52 |
-
const targetScreenY = window.innerHeight * 0.25;
|
| 53 |
-
const panScreenY = targetScreenY - pos.y;
|
| 54 |
-
|
| 55 |
-
// Center horizontally
|
| 56 |
-
const panScreenX = -pos.x;
|
| 57 |
-
|
| 58 |
-
// Convert screen pan to canvas pan
|
| 59 |
-
const canvasPan = unprojectFromScreen(panScreenX, panScreenY);
|
| 60 |
-
currentPanX = canvasPan.x;
|
| 61 |
-
currentPanY = canvasPan.y;
|
| 62 |
|
| 63 |
canvas.style.transition = 'background-position 0.5s ease-out';
|
| 64 |
panLayer.style.transition = 'transform 0.5s ease-out';
|
|
@@ -197,13 +186,14 @@ export function addNode(id, label, type = 'query') {
|
|
| 197 |
|
| 198 |
// Spine equation to go straight down on screen: X must decrease as Y increases
|
| 199 |
// We use currentY as the progression along the spine.
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
| 207 |
nodesMap.set(id, { x: posX, y: posY, type, label });
|
| 208 |
currentY += (150 + Math.random() * 100);
|
| 209 |
latestNodeId = id;
|
|
@@ -251,44 +241,44 @@ export function addNode(id, label, type = 'query') {
|
|
| 251 |
}
|
| 252 |
|
| 253 |
export function addEdge(fromId, toId) {
|
| 254 |
-
if (!
|
|
|
|
| 255 |
const fromNode = nodesMap.get(fromId);
|
| 256 |
const toNode = nodesMap.get(toId);
|
| 257 |
-
if (!fromNode || !toNode) return;
|
| 258 |
-
|
| 259 |
-
// Draw SVG Path
|
| 260 |
-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
| 261 |
-
const edgeType = toNode.type === 'source' ? 'type-source' : 'type-query';
|
| 262 |
-
path.setAttribute('class', `db-edge ${edgeType}`);
|
| 263 |
|
| 264 |
-
|
| 265 |
-
const
|
| 266 |
-
|
| 267 |
-
const
|
| 268 |
-
const sy = fromNode.y + 5000;
|
| 269 |
-
const ex = toNode.x + 5000;
|
| 270 |
-
const ey = toNode.y + 5000;
|
| 271 |
-
const cy = controlY + 5000;
|
| 272 |
|
| 273 |
-
const
|
| 274 |
-
|
|
|
|
| 275 |
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
|
|
|
|
|
|
|
|
|
| 283 |
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
|
|
|
| 287 |
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
}
|
|
|
|
| 43 |
const node = nodesMap.get(latestNodeId);
|
| 44 |
if (!node) return;
|
| 45 |
|
| 46 |
+
// We want the node to be directly in the center of the screen horizontally
|
| 47 |
+
// and slightly below the center vertically (e.g. 15% of screen height).
|
| 48 |
+
const targetOffset = unprojectFromScreen(0, window.innerHeight * 0.15);
|
| 49 |
+
currentPanX = -node.x + targetOffset.x;
|
| 50 |
+
currentPanY = -node.y + targetOffset.y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
canvas.style.transition = 'background-position 0.5s ease-out';
|
| 53 |
panLayer.style.transition = 'transform 0.5s ease-out';
|
|
|
|
| 186 |
|
| 187 |
// Spine equation to go straight down on screen: X must decrease as Y increases
|
| 188 |
// We use currentY as the progression along the spine.
|
| 189 |
+
let posX, posY;
|
| 190 |
+
if (id === 'root') {
|
| 191 |
+
posX = 0; posY = originOffsetY;
|
| 192 |
+
} else {
|
| 193 |
+
// No zigzag, straight down the spine
|
| 194 |
+
posX = -currentY;
|
| 195 |
+
posY = originOffsetY + currentY;
|
| 196 |
+
}
|
| 197 |
nodesMap.set(id, { x: posX, y: posY, type, label });
|
| 198 |
currentY += (150 + Math.random() * 100);
|
| 199 |
latestNodeId = id;
|
|
|
|
| 241 |
}
|
| 242 |
|
| 243 |
export function addEdge(fromId, toId) {
|
| 244 |
+
if (!nodesMap.has(fromId) || !nodesMap.has(toId)) return;
|
| 245 |
+
|
| 246 |
const fromNode = nodesMap.get(fromId);
|
| 247 |
const toNode = nodesMap.get(toId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
|
| 249 |
+
const minX = Math.min(fromNode.x, toNode.x);
|
| 250 |
+
const minY = Math.min(fromNode.y, toNode.y);
|
| 251 |
+
const maxX = Math.max(fromNode.x, toNode.x);
|
| 252 |
+
const maxY = Math.max(fromNode.y, toNode.y);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
+
const padding = 20;
|
| 255 |
+
const width = (maxX - minX) + padding * 2;
|
| 256 |
+
const height = (maxY - minY) + padding * 2;
|
| 257 |
|
| 258 |
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
| 259 |
+
svg.setAttribute('class', 'db-edge-svg');
|
| 260 |
+
svg.style.position = 'absolute';
|
| 261 |
+
svg.style.left = `${minX - padding}px`;
|
| 262 |
+
svg.style.top = `${minY - padding}px`;
|
| 263 |
+
svg.style.width = `${width}px`;
|
| 264 |
+
svg.style.height = `${height}px`;
|
| 265 |
+
svg.style.pointerEvents = 'none';
|
| 266 |
+
svg.style.overflow = 'visible';
|
| 267 |
+
svg.style.zIndex = '1';
|
| 268 |
|
| 269 |
+
const sx = fromNode.x - (minX - padding);
|
| 270 |
+
const sy = fromNode.y - (minY - padding);
|
| 271 |
+
const ex = toNode.x - (minX - padding);
|
| 272 |
+
const ey = toNode.y - (minY - padding);
|
| 273 |
|
| 274 |
+
const controlY = sy + (ey - sy) / 2;
|
| 275 |
+
|
| 276 |
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
| 277 |
+
const edgeType = toNode.type === 'source' ? 'type-source' : 'type-query';
|
| 278 |
+
path.setAttribute('class', `db-edge ${edgeType}`);
|
| 279 |
+
const d = `M ${sx},${sy} C ${sx},${controlY} ${ex},${controlY} ${ex},${ey}`;
|
| 280 |
+
path.setAttribute('d', d);
|
| 281 |
+
|
| 282 |
+
svg.appendChild(path);
|
| 283 |
+
if (panLayer) panLayer.appendChild(svg);
|
| 284 |
}
|
src/styles/detective-board.css
CHANGED
|
@@ -174,8 +174,8 @@
|
|
| 174 |
align-items: center;
|
| 175 |
justify-content: center;
|
| 176 |
cursor: pointer;
|
| 177 |
-
opacity: 0;
|
| 178 |
-
pointer-events:
|
| 179 |
transition: opacity 0.3s, transform 0.3s, background 0.2s;
|
| 180 |
z-index: 999999;
|
| 181 |
backdrop-filter: blur(8px);
|
|
|
|
| 174 |
align-items: center;
|
| 175 |
justify-content: center;
|
| 176 |
cursor: pointer;
|
| 177 |
+
opacity: 0.4;
|
| 178 |
+
pointer-events: auto;
|
| 179 |
transition: opacity 0.3s, transform 0.3s, background 0.2s;
|
| 180 |
z-index: 999999;
|
| 181 |
backdrop-filter: blur(8px);
|