mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-14 02:40:45 +08:00
Merge branch 'dev'
This commit is contained in:
12
style.css
12
style.css
@@ -554,9 +554,9 @@
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
background:
|
||||
radial-gradient(circle at 52% 34%, var(--bme-primary-dim, rgba(44, 92, 162, 0.16)), transparent 42%),
|
||||
radial-gradient(circle at 86% 16%, rgba(150, 91, 255, 0.08), transparent 34%),
|
||||
radial-gradient(circle at 12% 82%, rgba(0, 229, 255, 0.05), transparent 36%),
|
||||
radial-gradient(circle at 52% 34%, rgba(44, 92, 162, 0.07), transparent 42%),
|
||||
radial-gradient(circle at 86% 16%, rgba(150, 91, 255, 0.035), transparent 34%),
|
||||
radial-gradient(circle at 12% 82%, rgba(0, 229, 255, 0.025), transparent 36%),
|
||||
var(--bme-surface-lowest, #0e0e11);
|
||||
}
|
||||
|
||||
@@ -566,11 +566,11 @@
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
radial-gradient(circle, rgba(255, 255, 255, 0.52) 0 1px, transparent 1.4px),
|
||||
radial-gradient(circle, rgba(124, 248, 255, 0.28) 0 1px, transparent 1.6px);
|
||||
radial-gradient(circle, rgba(255, 255, 255, 0.28) 0 1px, transparent 1.4px),
|
||||
radial-gradient(circle, rgba(124, 248, 255, 0.13) 0 1px, transparent 1.6px);
|
||||
background-position: 18px 24px, 68px 86px;
|
||||
background-size: 134px 134px, 211px 211px;
|
||||
opacity: 0.16;
|
||||
opacity: 0.11;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ const canvasMockStats = {
|
||||
radialGradientCalls: 0,
|
||||
linearGradientCalls: 0,
|
||||
strokeCalls: 0,
|
||||
shadowBlurValues: [],
|
||||
};
|
||||
|
||||
function flushNextRaf(ms = 16) {
|
||||
@@ -102,7 +103,9 @@ function createNoopContext() {
|
||||
set lineCap(_value) {},
|
||||
set lineJoin(_value) {},
|
||||
set shadowColor(_value) {},
|
||||
set shadowBlur(_value) {},
|
||||
set shadowBlur(value) {
|
||||
canvasMockStats.shadowBlurValues.push(Number(value) || 0);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -176,6 +179,7 @@ function resetCanvasStats() {
|
||||
canvasMockStats.radialGradientCalls = 0;
|
||||
canvasMockStats.linearGradientCalls = 0;
|
||||
canvasMockStats.strokeCalls = 0;
|
||||
canvasMockStats.shadowBlurValues = [];
|
||||
}
|
||||
|
||||
function assertRendererNodesInsideRegions(renderer) {
|
||||
@@ -275,12 +279,20 @@ const { GraphRenderer } = await import("../ui/graph-renderer.js");
|
||||
});
|
||||
|
||||
const radius = renderer._nodeRadius({ type: "character", importance: 10 });
|
||||
const visualRadius = renderer._nodeVisualRadius({ type: "character", importance: 10 });
|
||||
assert.equal(radius, 14);
|
||||
assert.ok(visualRadius <= 8, "visual radius stays small and crisp");
|
||||
assert.ok(visualRadius < radius, "visual radius does not affect layout/collision radius");
|
||||
renderer.loadGraph(graph, { userPovAliases: ["Host"] });
|
||||
renderer.highlightNode("char-1");
|
||||
assertInputUnchanged(graph, before);
|
||||
assert.ok(canvasMockStats.radialGradientCalls > 0);
|
||||
assert.ok(canvasMockStats.linearGradientCalls > 0);
|
||||
assert.equal(
|
||||
Math.max(0, ...canvasMockStats.shadowBlurValues),
|
||||
0,
|
||||
"node visuals should not reintroduce heavy crystal-ball shadow blur",
|
||||
);
|
||||
renderer.destroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -1569,25 +1569,25 @@ export class GraphRenderer {
|
||||
const cy = my + ny * bend;
|
||||
|
||||
const relationColor = edgeColorForRelation(edge.relation);
|
||||
const baseAlpha = sameZone ? 0.035 + strength * 0.09 : 0.026 + strength * 0.07;
|
||||
const alpha = isDimmed ? 0.018 : (isConnectedToSelection ? 0.38 + strength * 0.28 : baseAlpha);
|
||||
const baseAlpha = sameZone ? 0.026 + strength * 0.052 : 0.02 + strength * 0.045;
|
||||
const alpha = isDimmed ? 0.012 : (isConnectedToSelection ? 0.28 + strength * 0.14 : baseAlpha);
|
||||
|
||||
if (isConnectedToSelection) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(from.x, from.y);
|
||||
ctx.quadraticCurveTo(cx, cy, to.x, to.y);
|
||||
ctx.strokeStyle = colorWithAlpha(relationColor, 0.12 + strength * 0.12);
|
||||
ctx.lineWidth = 2.8 + strength * 2.2;
|
||||
ctx.strokeStyle = colorWithAlpha(relationColor, 0.055 + strength * 0.055);
|
||||
ctx.lineWidth = 1.35 + strength * 0.95;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(from.x, from.y);
|
||||
ctx.quadraticCurveTo(cx, cy, to.x, to.y);
|
||||
ctx.strokeStyle = colorWithAlpha(isConnectedToSelection ? relationColor : '#c9dcff', alpha);
|
||||
ctx.strokeStyle = colorWithAlpha(isConnectedToSelection ? relationColor : '#9eb2cf', alpha);
|
||||
ctx.lineWidth = isConnectedToSelection
|
||||
? 1.35 + strength * 2.15
|
||||
: 0.35 + strength * 0.82;
|
||||
? 0.7 + strength * 0.72
|
||||
: 0.28 + strength * 0.44;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
@@ -1628,7 +1628,10 @@ export class GraphRenderer {
|
||||
const isSelected = node === this.selectedNode;
|
||||
const isHovered = node === this.hoveredNode;
|
||||
const isDimmed = focus.selectedNode && !focus.connectedNodes.has(node);
|
||||
const r = (isSelected ? baseRadius * 1.12 : baseRadius) * (isDimmed ? 0.6 : 1);
|
||||
const activeRadius = isSelected
|
||||
? Math.min(10, baseRadius * 1.22, baseRadius + 1.8)
|
||||
: (isHovered ? Math.min(9, baseRadius * 1.12, baseRadius + 1.1) : baseRadius);
|
||||
const r = activeRadius * (isDimmed ? 0.62 : 1);
|
||||
const transientHighlight = this._transientHighlights.get(node.id) || null;
|
||||
const scope = normalizeMemoryScope(node.raw?.scope);
|
||||
const outlineColor = scope.layer === 'pov'
|
||||
@@ -1645,63 +1648,32 @@ export class GraphRenderer {
|
||||
}
|
||||
|
||||
if (isSelected || isHovered) {
|
||||
const glowRadius = r + (isSelected ? 20 : 13);
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, glowRadius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = createCanvasGradient(
|
||||
ctx,
|
||||
'createRadialGradient',
|
||||
[node.x, node.y, Math.max(1, r * 0.45), node.x, node.y, glowRadius],
|
||||
[
|
||||
[0, colorWithAlpha(color, isSelected ? 0.52 : 0.34)],
|
||||
[0.55, colorWithAlpha(color, isSelected ? 0.22 : 0.14)],
|
||||
[1, colorWithAlpha(color, 0)],
|
||||
],
|
||||
colorWithAlpha(color, isSelected ? 0.24 : 0.14),
|
||||
);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, r + (isSelected ? 6 : 4), 0, Math.PI * 2);
|
||||
ctx.strokeStyle = colorWithAlpha(isSelected ? '#ffffff' : color, isSelected ? 0.72 : 0.5);
|
||||
ctx.lineWidth = isSelected ? 2.4 : 1.6;
|
||||
ctx.arc(node.x, node.y, r + (isSelected ? 5.2 : 3.8), 0, Math.PI * 2);
|
||||
ctx.strokeStyle = colorWithAlpha(color, isSelected ? 0.54 : 0.36);
|
||||
ctx.lineWidth = isSelected ? 1.05 : 0.85;
|
||||
ctx.stroke();
|
||||
|
||||
if (isSelected) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, r + 8.2, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = colorWithAlpha('#dbeafe', 0.22);
|
||||
ctx.lineWidth = 0.75;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, r, 0, Math.PI * 2);
|
||||
ctx.fillStyle = createCanvasGradient(
|
||||
ctx,
|
||||
'createRadialGradient',
|
||||
[
|
||||
node.x - r * 0.32,
|
||||
node.y - r * 0.34,
|
||||
Math.max(1, r * 0.16),
|
||||
node.x,
|
||||
node.y,
|
||||
r,
|
||||
],
|
||||
[
|
||||
[0, colorWithAlpha('#ffffff', isSelected ? 0.86 : 0.68)],
|
||||
[0.18, colorWithAlpha(color, isSelected ? 1 : 0.94)],
|
||||
[1, colorWithAlpha(color, isSelected ? 0.68 : 0.52)],
|
||||
],
|
||||
colorWithAlpha(color, isSelected ? 1 : 0.86),
|
||||
);
|
||||
ctx.fillStyle = colorWithAlpha(color, isSelected ? 0.96 : (isHovered ? 0.9 : 0.82));
|
||||
ctx.fill();
|
||||
|
||||
ctx.strokeStyle = isSelected ? '#fff' : colorWithAlpha(outlineColor, isHovered ? 0.9 : 0.64);
|
||||
ctx.lineWidth = isSelected ? 2.8 : (isHovered ? 1.8 : 1.1);
|
||||
ctx.strokeStyle = isSelected
|
||||
? colorWithAlpha('#eef6ff', 0.72)
|
||||
: colorWithAlpha(outlineColor, isHovered ? 0.58 : 0.38);
|
||||
ctx.lineWidth = isSelected ? 1.15 : (isHovered ? 0.95 : 0.65);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.shadowColor = colorWithAlpha(color, isSelected ? 0.5 : 0.2);
|
||||
ctx.shadowBlur = isSelected ? 16 : 7;
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x - r * 0.28, node.y - r * 0.34, Math.max(1.4, r * 0.16), 0, Math.PI * 2);
|
||||
ctx.fillStyle = colorWithAlpha('#ffffff', isSelected ? 0.7 : 0.5);
|
||||
ctx.fill();
|
||||
ctx.shadowBlur = 0;
|
||||
|
||||
ctx.font = `${this.config.labelFontSize}px Inter, sans-serif`;
|
||||
ctx.textAlign = 'center';
|
||||
const rect = node.regionRect;
|
||||
@@ -1720,21 +1692,21 @@ export class GraphRenderer {
|
||||
);
|
||||
if (isHovered || isSelected) {
|
||||
const metrics = ctx.measureText(labelDraw);
|
||||
const pillW = Math.min(maxLabelW + 12, metrics.width + 14);
|
||||
const pillH = 17;
|
||||
const pillW = Math.min(maxLabelW + 10, metrics.width + 12);
|
||||
const pillH = 16;
|
||||
const pillX = node.x - pillW / 2;
|
||||
const pillY = node.y + r + 6;
|
||||
const pillY = node.y + r + 6.5;
|
||||
ctx.beginPath();
|
||||
roundRectPath(ctx, pillX, pillY, pillW, pillH, 8);
|
||||
roundRectPath(ctx, pillX, pillY, pillW, pillH, 5);
|
||||
ctx.fillStyle = isSelected
|
||||
? 'rgba(7, 14, 32, 0.88)'
|
||||
: 'rgba(7, 14, 32, 0.72)';
|
||||
? 'rgba(8, 10, 16, 0.64)'
|
||||
: 'rgba(8, 10, 16, 0.52)';
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = colorWithAlpha(color, isSelected ? 0.48 : 0.32);
|
||||
ctx.strokeStyle = 'rgba(238, 246, 255, 0.09)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.fillStyle = `rgba(238,247,255,${isHovered || isSelected ? 0.96 : 0.62})`;
|
||||
ctx.fillStyle = `rgba(218,229,242,${isHovered || isSelected ? 0.88 : 0.52})`;
|
||||
ctx.fillText(labelDraw, node.x, node.y + r + 14);
|
||||
ctx.restore();
|
||||
}
|
||||
@@ -1754,38 +1726,23 @@ export class GraphRenderer {
|
||||
const kind = highlight.kind || 'recall';
|
||||
const drawPulse = (color, offset, alphaScale = 1) => {
|
||||
const pulse = reducedMotion ? 0.35 : phase;
|
||||
const ringRadius = radius + offset + pulse * 11;
|
||||
const glowRadius = ringRadius + 11;
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, glowRadius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = createCanvasGradient(
|
||||
ctx,
|
||||
'createRadialGradient',
|
||||
[node.x, node.y, Math.max(1, radius * 0.8), node.x, node.y, glowRadius],
|
||||
[
|
||||
[0, colorWithAlpha(color, 0.10 * fade * alphaScale)],
|
||||
[0.58, colorWithAlpha(color, 0.16 * fade * alphaScale)],
|
||||
[1, colorWithAlpha(color, 0)],
|
||||
],
|
||||
colorWithAlpha(color, 0.08 * fade * alphaScale),
|
||||
);
|
||||
ctx.fill();
|
||||
const ringRadius = radius + offset + pulse * 5.5;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, ringRadius, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = colorWithAlpha(color, (0.28 + phase * 0.34) * fade * alphaScale);
|
||||
ctx.lineWidth = 1.2 + phase * 1.4;
|
||||
ctx.strokeStyle = colorWithAlpha(color, (0.18 + phase * 0.18) * fade * alphaScale);
|
||||
ctx.lineWidth = 0.65 + phase * 0.45;
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
if (kind === 'mixed') {
|
||||
drawPulse('#7cf8ff', 7, 1);
|
||||
drawPulse('#b79cff', 14, 0.88);
|
||||
drawPulse('#7cf8ff', 4.2, 0.92);
|
||||
drawPulse('#b79cff', 7.5, 0.72);
|
||||
} else if (kind === 'extracted') {
|
||||
drawPulse('#b79cff', 9, 0.9);
|
||||
drawPulse('#75ffb1', 15, 0.5);
|
||||
drawPulse('#b79cff', 4.8, 0.82);
|
||||
drawPulse('#75ffb1', 7.8, 0.42);
|
||||
} else {
|
||||
drawPulse('#7cf8ff', 8, 1);
|
||||
drawPulse('#7cf8ff', 4.5, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1866,9 +1823,9 @@ export class GraphRenderer {
|
||||
Math.max(width, height) * 0.82,
|
||||
],
|
||||
[
|
||||
[0, colorWithAlpha(theme.primary || '#224b84', 0.2)],
|
||||
[0.34, colorWithAlpha(theme.secondary || '#141c48', 0.12)],
|
||||
[0.72, colorWithAlpha(theme.surfaceLow || '#070b1a', 0.5)],
|
||||
[0, colorWithAlpha(theme.primary || '#224b84', 0.12)],
|
||||
[0.34, colorWithAlpha(theme.secondary || '#141c48', 0.075)],
|
||||
[0.72, colorWithAlpha(theme.surfaceLow || '#070b1a', 0.58)],
|
||||
[1, theme.surfaceLowest || 'rgba(2, 4, 12, 0.96)'],
|
||||
],
|
||||
theme.surfaceLowest || 'rgba(2, 4, 12, 0.96)',
|
||||
@@ -1887,8 +1844,8 @@ export class GraphRenderer {
|
||||
Math.max(width, height) * 0.46,
|
||||
],
|
||||
[
|
||||
[0, colorWithAlpha(theme.accent3 || '#b073ff', 0.12)],
|
||||
[0.5, colorWithAlpha(theme.accent2 || '#57c7ff', 0.055)],
|
||||
[0, colorWithAlpha(theme.accent3 || '#b073ff', 0.055)],
|
||||
[0.5, colorWithAlpha(theme.accent2 || '#57c7ff', 0.032)],
|
||||
[1, 'rgba(0, 0, 0, 0)'],
|
||||
],
|
||||
'rgba(0, 0, 0, 0)',
|
||||
@@ -2053,12 +2010,12 @@ export class GraphRenderer {
|
||||
const importance = Number(node?.importance || 5);
|
||||
const type = String(node?.type || '').toLowerCase();
|
||||
if (type === 'character' || importance >= 9) {
|
||||
return Math.min(base * 1.18, base + 4);
|
||||
return Math.min(8, Math.max(4.8, base * 0.58));
|
||||
}
|
||||
if (type === 'event' || importance >= 6) {
|
||||
return Math.min(base * 1.08, base + 2);
|
||||
return Math.min(7, Math.max(4.2, base * 0.52));
|
||||
}
|
||||
return base;
|
||||
return Math.min(6.2, Math.max(3.4, base * 0.46));
|
||||
}
|
||||
|
||||
_ellipsisLabel(ctx, text, maxW) {
|
||||
|
||||
Reference in New Issue
Block a user