feat: Add info panel persistence setting and magnet icon to allow displaying last hovered node info.
This commit is contained in:
@@ -131,6 +131,9 @@ export class InfoPanel {
|
||||
});
|
||||
}
|
||||
|
||||
// State for persistence
|
||||
private lastValidNodeInfo: any = null;
|
||||
|
||||
updateInfo(): void {
|
||||
const settings = this.stateManager.state.settings;
|
||||
const isActive = this.magnifyGlass.state.active;
|
||||
@@ -143,7 +146,23 @@ export class InfoPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
const info = this.informationGatherer.gatherInformation();
|
||||
let info = this.informationGatherer.gatherInformation();
|
||||
|
||||
// PERSISTENCE LOGIC START
|
||||
if (info.hoveredNode) {
|
||||
// We have a valid node, update our cache
|
||||
this.lastValidNodeInfo = info;
|
||||
} else if (settings["🔍MagnifyGlass.InfoPanelPersist"] && this.lastValidNodeInfo) {
|
||||
// No node hovered, but persistence is ON and we have a cache.
|
||||
// Merge the CACHED node info into the CURRENT info (so we keep current cursor pos/zoom but show old node)
|
||||
info = {
|
||||
...info, // Current timestamp, cursor, zoom
|
||||
hoveredNode: this.lastValidNodeInfo.hoveredNode,
|
||||
hoveredWidget: this.lastValidNodeInfo.hoveredWidget
|
||||
};
|
||||
}
|
||||
// PERSISTENCE LOGIC END
|
||||
|
||||
this.stateManager.setCurrentInfo(info);
|
||||
|
||||
// Update the UI
|
||||
@@ -161,10 +180,13 @@ export class InfoPanel {
|
||||
}
|
||||
} else {
|
||||
this.stateManager.state.isHoveringNode = false;
|
||||
if (this.stateManager.state.lastNodeId !== null) {
|
||||
this.stateManager.state.lastNodeId = null;
|
||||
if (!this.stateManager.state.isPanelHovered) {
|
||||
this.stateManager.scheduleAutoCollapse();
|
||||
// Only collapse if we are NOT persisting
|
||||
if (!settings["🔍MagnifyGlass.InfoPanelPersist"]) {
|
||||
if (this.stateManager.state.lastNodeId !== null) {
|
||||
this.stateManager.state.lastNodeId = null;
|
||||
if (!this.stateManager.state.isPanelHovered) {
|
||||
this.stateManager.scheduleAutoCollapse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +207,7 @@ export class UIManager {
|
||||
this.elements.controls.innerHTML = `
|
||||
<button class="control-btn unlock-btn" title="Unlock/Lock Panel from Glass" data-action="pin">${Icons.unlock}</button>
|
||||
<button class="control-btn pin-btn" title="Pin/Unpin Panel Position (Prevent Drag)" data-action="lock">${Icons.pin}</button>
|
||||
<button class="control-btn persist-btn" title="Toggle Persist Mode (Sticky Info)" data-action="persist">${Icons.magnet}</button>
|
||||
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (I)" data-action="toggle-panel">${Icons.eye}</button>
|
||||
<button class="control-btn glass-btn" title="Toggle Glass Preview (G)" data-action="toggle-glass">${Icons.magnifyGlass}</button>
|
||||
`;
|
||||
@@ -242,6 +243,11 @@ export class UIManager {
|
||||
this.elements.panel.classList.toggle('panel-locked', this.stateManager.state.isPanelLocked);
|
||||
}
|
||||
break;
|
||||
case 'persist':
|
||||
this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"] = !this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"];
|
||||
this.updateControlStates();
|
||||
// Sync sidebar setting if needed (optional, sidebar updates on reopen usually)
|
||||
break;
|
||||
case 'toggle-panel':
|
||||
if (this.stateManager.state.isPanelVisible) {
|
||||
this.hide();
|
||||
@@ -342,6 +348,14 @@ export class UIManager {
|
||||
lockBtn.disabled = !this.stateManager.state.isPanelPinned;
|
||||
}
|
||||
|
||||
const persistBtn = this.elements.controls.querySelector('[data-action="persist"]') as HTMLButtonElement;
|
||||
if (persistBtn) {
|
||||
const isPersistConfigured = !!this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"];
|
||||
persistBtn.classList.toggle('active', isPersistConfigured);
|
||||
persistBtn.title = isPersistConfigured ? "Disable Sticky Info" : "Enable Sticky Info (Persist)";
|
||||
persistBtn.style.display = isPanelVisible ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
if (visibilityBtn) {
|
||||
// Active means "Panel is Visible"
|
||||
visibilityBtn.classList.toggle('active', isPanelVisible);
|
||||
|
||||
@@ -77,6 +77,11 @@ export const Icons = {
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"/>
|
||||
</svg>`,
|
||||
|
||||
// Magnet icon - persist/sticky mode
|
||||
magnet: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m6 15-4-4 6.75-6.77a7.79 7.79 0 0 1 11 11L13 22l-4-4 6.39-6.36a2.2 2.2 0 0 0-3.1-3.13l-6.29 6.49Z"/>
|
||||
</svg>`
|
||||
};
|
||||
|
||||
|
||||
@@ -57,4 +57,5 @@ export const DEFAULT_PANEL_SETTINGS: PanelSettings = {
|
||||
"🔍MagnifyGlass.InfoPanelTextColor": "#6b7280",
|
||||
"🔍MagnifyGlass.InfoPanelAccentColor": "#3b82f6",
|
||||
"🔍MagnifyGlass.InfoPanelFontSize": 14,
|
||||
"🔍MagnifyGlass.InfoPanelPersist": false,
|
||||
};
|
||||
|
||||
@@ -128,6 +128,22 @@ export function registerPanelSettings(stateManager: any, uiManager: any, positio
|
||||
}
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.InfoPanelPersist",
|
||||
name: "📊 Info Panel: Persist Node Info",
|
||||
type: "boolean",
|
||||
defaultValue: settings["🔍MagnifyGlass.InfoPanelPersist"],
|
||||
tooltip: "Keep displaying the last node's info until you hover over another node.",
|
||||
onChange: (value: any) => {
|
||||
if (stateManager) {
|
||||
stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"] = !!value;
|
||||
if (uiManager) {
|
||||
uiManager.updateControlStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: "🔍MagnifyGlass.InfoPanelAnimations",
|
||||
name: "📊 Info Panel: Animations",
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface PanelSettings {
|
||||
"🔍MagnifyGlass.InfoPanelTextColor": string;
|
||||
"🔍MagnifyGlass.InfoPanelAccentColor": string;
|
||||
"🔍MagnifyGlass.InfoPanelFontSize": number;
|
||||
"🔍MagnifyGlass.InfoPanelPersist": boolean;
|
||||
[key: string]: string | number | boolean; // Allow indexing
|
||||
}
|
||||
|
||||
|
||||
@@ -396,6 +396,23 @@ export function renderSettingsPanel(container: HTMLElement): void {
|
||||
`When enabled, glass stays visible. Toggle with ${activationKey}`
|
||||
));
|
||||
|
||||
// The instruction implies this should be in glassSection, but the content suggests info panel.
|
||||
// Placing it in glassSection as per instruction, assuming createToggle is intended.
|
||||
// The condition `if (getSettingValue("🔍MagnifyGlass.InfoPanelEnabled", true))` is removed
|
||||
// as it doesn't make sense for a glass setting to depend on info panel being enabled.
|
||||
// The tooltip and callback logic are kept as provided.
|
||||
glassSection.body.appendChild(createToggle('Persist Info',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelPersist', false),
|
||||
(checked) => {
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelPersist', checked);
|
||||
// Sync with UI controls if needed
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
if (infoPanel?.uiManager) {
|
||||
infoPanel.uiManager.updateControlStates();
|
||||
}
|
||||
}, 'Keep displaying the last node info when hovering empty space'
|
||||
));
|
||||
|
||||
container.appendChild(glassSection.section);
|
||||
|
||||
// ===== Hotkeys Section =====
|
||||
@@ -446,6 +463,19 @@ export function renderSettingsPanel(container: HTMLElement): void {
|
||||
`Show info panel with node details. Toggle: ${toggleHotkey}`
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createToggle('Persist Info',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelPersist', false),
|
||||
(checked) => {
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelPersist', checked);
|
||||
// Sync with UI controls if needed
|
||||
const infoPanel = (window as any).infoPanelManager;
|
||||
if (infoPanel?.uiManager) {
|
||||
infoPanel.uiManager.updateControlStates();
|
||||
}
|
||||
},
|
||||
'Keep displaying the last node info when hovering empty space'
|
||||
));
|
||||
|
||||
panelSection.body.appendChild(createSelect('Position',
|
||||
getSettingValue('🔍MagnifyGlass.InfoPanelPosition', 'Bottom'), PANEL_POSITIONS,
|
||||
(value) => setSettingValue('🔍MagnifyGlass.InfoPanelPosition', value),
|
||||
@@ -690,6 +720,7 @@ export function renderSettingsPanel(container: HTMLElement): void {
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelMaxHeight', 300);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelOpacity', 100);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelFontSize', 14);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelPersist', false);
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelTextColor', '#6b7280');
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelAccentColor', '#3b82f6');
|
||||
setSettingValue('🔍MagnifyGlass.InfoPanelAnimations', false);
|
||||
|
||||
Reference in New Issue
Block a user