Luca448 commited on
Commit
ffe05f5
·
1 Parent(s): 494787a

Savestate: Final fixes for detective board nodes, lines, auto-scroll centering and translations

Browse files
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.179</div>
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: `Query: ${queryLabel}`, group: 'query' });
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
- const sourceLabel = srcInfo.title.length > 40 ? srcInfo.title.substring(0, 40) + '...' : srcInfo.title;
162
- onGraphEvent({ type: 'node', id: `s_${doc.url}`, label: `Source: ${sourceLabel}`, group: 'source' });
 
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
- // Project the node's position to screen coordinates
47
- const pos = projectToScreen(node.x, node.y);
48
-
49
- // Canvas origin is at the center of the screen.
50
- // We want the node to appear in the lower third of the screen (e.g. at Y = innerHeight * 0.25 relative to center)
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
- const maxSpread = window.innerWidth < 600 ? 150 : 350;
201
- const randomOffset = (Math.random() - 0.5) * maxSpread;
202
-
203
- // The panLayer origin (0,0) is at the center of the screen.
204
- const posX = -currentY + randomOffset;
205
- const posY = originOffsetY + currentY + randomOffset;
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 (!svgLayer) return;
 
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
- // Create a bezier curve between the points
265
- const controlY = fromNode.y + (toNode.y - fromNode.y) / 2;
266
- // We add 5000 to X and Y because SVG is positioned at -5000px, -5000px
267
- const sx = fromNode.x + 5000;
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 d = `M ${sx},${sy} C ${sx},${cy} ${ex},${cy} ${ex},${ey}`;
274
- path.setAttribute('d', d);
 
275
 
276
- // Add path to SVG
277
- svgLayer.appendChild(path);
278
-
279
- // Draw Glow Dot moving along the path
280
- const dot = document.createElement('div');
281
- dot.className = `db-glow-dot ${edgeType}`;
282
- dot.style.offsetPath = `path('${d}')`;
 
 
 
283
 
284
- // Since offsetPath is relative to the SVG viewbox, and dot is in panLayer, we must position the dot
285
- dot.style.left = '-5000px';
286
- dot.style.top = '-5000px';
 
287
 
288
- panLayer.appendChild(dot);
289
-
290
- // Remove dot after animation to save DOM nodes
291
- setTimeout(() => {
292
- if (dot.parentNode) dot.remove();
293
- }, 3000);
 
 
 
 
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: none;
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);