feat(canvas): 交互可读性 - 优化连线、字号与工作条
This commit is contained in:
50
web/src/lib/canvas/canvas-connection-visibility.ts
Normal file
50
web/src/lib/canvas/canvas-connection-visibility.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import type { CanvasDisplayConnection } from "@/types/canvas";
|
||||
|
||||
export const CANVAS_HIDE_NODE_CONNECTIONS_STORAGE_KEY = "canvas-hide-node-connections";
|
||||
|
||||
export function readCanvasHideNodeConnections() {
|
||||
if (typeof window === "undefined") return false;
|
||||
try {
|
||||
return window.localStorage.getItem(CANVAS_HIDE_NODE_CONNECTIONS_STORAGE_KEY) === "true";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function persistCanvasHideNodeConnections(value: boolean) {
|
||||
try {
|
||||
window.localStorage.setItem(CANVAS_HIDE_NODE_CONNECTIONS_STORAGE_KEY, String(value));
|
||||
} catch {
|
||||
// 浏览器禁用本地存储时保留当前会话内的选择。
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏模式只保留当前交互焦点的直接连线。
|
||||
* 该函数返回同一份结果给 Leafer 视觉层和 SVG 命中层,避免“看不见但仍能点到”的幽灵连线。
|
||||
*/
|
||||
export function filterCanvasDisplayConnections(
|
||||
connections: CanvasDisplayConnection[],
|
||||
options: {
|
||||
enabled: boolean;
|
||||
hoveredNodeId?: string | null;
|
||||
selectedNodeIds?: ReadonlySet<string>;
|
||||
activeNodeId?: string | null;
|
||||
selectedConnectionId?: string | null;
|
||||
},
|
||||
) {
|
||||
if (!options.enabled) return connections;
|
||||
|
||||
const focusedNodeIds = new Set<string>();
|
||||
if (options.hoveredNodeId) focusedNodeIds.add(options.hoveredNodeId);
|
||||
if (options.activeNodeId) focusedNodeIds.add(options.activeNodeId);
|
||||
options.selectedNodeIds?.forEach((nodeId) => focusedNodeIds.add(nodeId));
|
||||
|
||||
return connections.filter(({ connection, from, to }) =>
|
||||
connection.id === options.selectedConnectionId
|
||||
|| focusedNodeIds.has(connection.fromNodeId)
|
||||
|| focusedNodeIds.has(connection.toNodeId)
|
||||
|| focusedNodeIds.has(from.id)
|
||||
|| focusedNodeIds.has(to.id),
|
||||
);
|
||||
}
|
||||
19
web/src/lib/canvas/canvas-text-scale.ts
Normal file
19
web/src/lib/canvas/canvas-text-scale.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
const DEFAULT_TEXT_NODE_WIDTH = 340;
|
||||
const DEFAULT_TEXT_NODE_HEIGHT = 240;
|
||||
const DEFAULT_TEXT_FONT_SIZE = 14;
|
||||
const MIN_TEXT_FONT_SIZE = 8;
|
||||
const MAX_TEXT_FONT_SIZE = 72;
|
||||
|
||||
/**
|
||||
* Keep text readable while a text node is resized.
|
||||
*
|
||||
* The geometric mean makes the scale respond to both dimensions without making
|
||||
* a very wide, short node grow as aggressively as a purely width-based rule.
|
||||
*/
|
||||
export function canvasTextFontSize(width: number, height: number, baseFontSize = DEFAULT_TEXT_FONT_SIZE) {
|
||||
const safeWidth = Number.isFinite(width) && width > 0 ? width : DEFAULT_TEXT_NODE_WIDTH;
|
||||
const safeHeight = Number.isFinite(height) && height > 0 ? height : DEFAULT_TEXT_NODE_HEIGHT;
|
||||
const safeBaseFontSize = Number.isFinite(baseFontSize) && baseFontSize > 0 ? baseFontSize : DEFAULT_TEXT_FONT_SIZE;
|
||||
const scale = Math.sqrt((safeWidth / DEFAULT_TEXT_NODE_WIDTH) * (safeHeight / DEFAULT_TEXT_NODE_HEIGHT));
|
||||
return Math.min(MAX_TEXT_FONT_SIZE, Math.max(MIN_TEXT_FONT_SIZE, safeBaseFontSize * scale));
|
||||
}
|
||||
@@ -186,7 +186,7 @@ export type ToolDefinition = {
|
||||
/** 互斥开关:在 dock 中渲染为分段切换,而不是两个独立按钮 */
|
||||
switchGroup?: {
|
||||
value: (ctx: ToolContext) => string;
|
||||
options: Array<{ id: string; label: string; icon: ReactNode; value: string }>;
|
||||
options: Array<{ id: string; label: string; displayLabel?: string; icon: ReactNode; value: string }>;
|
||||
onChange: (ctx: ToolContext, value: string) => void;
|
||||
};
|
||||
/** 上下文可见性谓词——返回 false 时工具不渲染(不受 prefs 控制) */
|
||||
|
||||
Reference in New Issue
Block a user