mirror of
https://github.com/miro0o/miniWorldMap.git
synced 2026-07-22 07:46:00 +00:00
Release 0.2.3
This commit is contained in:
parent
f6cdeb16c6
commit
91d48e372a
13 changed files with 207 additions and 75 deletions
|
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 0.2.3
|
||||
|
||||
- Add a 2D Complete map action that shows the vault root with expanded node, link, depth, and outside-link detail budgets.
|
||||
- Allow farther 2D zoom-out for large complete maps.
|
||||
- Improve 2D panel wrapping and alignment so long labels remain readable.
|
||||
- Soften the 2D outside-link road color and legend swatch.
|
||||
|
||||
## 0.2.2
|
||||
|
||||
- Address Obsidian community plugin review findings around settings APIs, command names, deprecated slider tooltips, direct static styles, and config folder handling.
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Global plugin settings include:
|
|||
- 2D unresolved link handling.
|
||||
- Ignored folders.
|
||||
|
||||
The 2D view panel also includes per-view controls for atlas/focus mode, vault root, theme, depth, visible nodes, note-link budget, outside-link detail, exact outside-note limit, ring guides, and legend visibility.
|
||||
The 2D view panel also includes per-view controls for atlas/focus mode, vault root, complete map display, theme, depth, visible nodes, note-link budget, outside-link detail, exact outside-note limit, ring guides, and legend visibility.
|
||||
|
||||
The 3D view panel includes controls for search, recentering, reveal animation, visual style presets, theme presets, color themes, imported Obsidian graph colors, node sizing, bloom, physics, cruise motion, unresolved links, orphan nodes, quality tier, and default reset.
|
||||
|
||||
|
|
|
|||
66
main.js
66
main.js
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "mini-world-map",
|
||||
"name": "Mini World Map",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"minAppVersion": "1.13.0",
|
||||
"description": "Visualize your vault as a hierarchy-first world map with internal links layered on top.",
|
||||
"author": "Miro0o and Codex",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "mini-world-map",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mini-world-map",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"3d-force-graph": "1.80.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mini-world-map",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"description": "Hierarchy-first 2D radial rings and 3D map views for your Obsidian vault",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ const STRINGS: Record<string, Record<Language, string>> = {
|
|||
'view.atlas': { en: 'Atlas', zh: '图谱' },
|
||||
'view.focus': { en: 'Focus', zh: '聚焦' },
|
||||
'view.vaultRoot': { en: 'Vault root', zh: '库根目录' },
|
||||
'view.completeMap': { en: 'Complete map', zh: '完整地图' },
|
||||
'view.mode': { en: 'Map mode', zh: '地图模式' },
|
||||
'view.theme': { en: 'Theme', zh: '主题' },
|
||||
'theme.auto': { en: 'System', zh: '跟随系统' },
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import type { RadialLayout, RadialPoint, RadialRoute } from '../layout/radial/la
|
|||
import { ROOT_ID, type VisibleWorldGraph, type WorldEdge, type WorldNode } from '../world/types';
|
||||
import { NODE_FRAGMENT_SHADER, NODE_VERTEX_SHADER } from './shaders';
|
||||
|
||||
export const MIN_RADIAL_ZOOM = 0.0001;
|
||||
// Hard floor keeps wheel math and hit-tests finite while still allowing huge complete maps to fit.
|
||||
export const MIN_RADIAL_ZOOM = 0.00001;
|
||||
export const MAX_RADIAL_ZOOM = 6;
|
||||
const NODE_BASE_POINT = 4.8;
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ const PALETTES: Record<RadialResolvedScheme, RadialPalette> = {
|
|||
tree: '#93a3b8',
|
||||
link: '#a2acba',
|
||||
external: '#7b6fd6',
|
||||
externalLink: '#c98135',
|
||||
externalLink: '#b8752e',
|
||||
unresolved: '#dc4a4a',
|
||||
focus: '#3f7fe8',
|
||||
folder: '#7f92a8',
|
||||
|
|
@ -80,7 +81,7 @@ const PALETTES: Record<RadialResolvedScheme, RadialPalette> = {
|
|||
ringOpacity: 0.22,
|
||||
treeOpacity: 0.26,
|
||||
linkOpacity: 0.1,
|
||||
externalLinkOpacity: 0.16,
|
||||
externalLinkOpacity: 0.14,
|
||||
highlightOpacity: 0.92,
|
||||
nodeScale: 0.32,
|
||||
maxLabels: 170,
|
||||
|
|
@ -91,7 +92,7 @@ const PALETTES: Record<RadialResolvedScheme, RadialPalette> = {
|
|||
tree: '#8a8f9c',
|
||||
link: '#8b8f99',
|
||||
external: '#a78bfa',
|
||||
externalLink: '#f59e0b',
|
||||
externalLink: '#d97706',
|
||||
unresolved: '#fb7185',
|
||||
focus: '#8b7cf6',
|
||||
folder: '#d5d8de',
|
||||
|
|
@ -101,7 +102,7 @@ const PALETTES: Record<RadialResolvedScheme, RadialPalette> = {
|
|||
ringOpacity: 0.22,
|
||||
treeOpacity: 0.28,
|
||||
linkOpacity: 0.12,
|
||||
externalLinkOpacity: 0.16,
|
||||
externalLinkOpacity: 0.14,
|
||||
highlightOpacity: 0.98,
|
||||
nodeScale: 0.32,
|
||||
maxLabels: 300,
|
||||
|
|
|
|||
|
|
@ -165,18 +165,22 @@ export class Radial2DController extends Component {
|
|||
|
||||
private async rebuildNow(reason: string, token: number): Promise<void> {
|
||||
const radial = this.radial();
|
||||
const shouldReveal = ['start', 'manual', 'root', 'focus', 'atlas'].includes(reason);
|
||||
const shouldReveal = ['start', 'manual', 'root', 'focus', 'atlas', 'complete'].includes(reason);
|
||||
const loadingText = this.t('loading.radial');
|
||||
if (shouldReveal) {
|
||||
this.renderer?.showLoadingMask(loadingText);
|
||||
await animationFrames(2);
|
||||
if (this.disposed || token !== this.rebuildToken) return;
|
||||
}
|
||||
this.index.rebuild(radial);
|
||||
const indexSettings = this.state.showCompleteRoot ? { ...radial, includeUnresolvedLinks: true } : radial;
|
||||
this.index.rebuild(indexSettings);
|
||||
this.state.hoverHighlightMode = radial.hoverHighlightMode;
|
||||
this.state.labelVisibility = radial.labelVisibility;
|
||||
this.state.hiddenLegendItems = radial.hiddenLegendItems.slice();
|
||||
this.state.showLinkOverlay = radial.showLinkOverlay || !this.state.hiddenLegendItems.includes('link');
|
||||
if (this.state.showCompleteRoot) this.applyCompleteMapState();
|
||||
else {
|
||||
this.state.hiddenLegendItems = radial.hiddenLegendItems.slice();
|
||||
this.state.showLinkOverlay = radial.showLinkOverlay || !this.state.hiddenLegendItems.includes('link');
|
||||
}
|
||||
this.state.pinNeedsHoverLinks = this.pinnedPathsNeedHoverLinks();
|
||||
this.state.selectedNodeId = this.selectedNodeId;
|
||||
this.state.selectedLink = this.selectedLink;
|
||||
|
|
@ -488,7 +492,7 @@ export class Radial2DController extends Component {
|
|||
['controls', this.t('tab.controls')],
|
||||
['defaults', this.t('tab.defaults')],
|
||||
] as const) {
|
||||
const button = tabs.createEl('button', { cls: this.activePanelPage === id ? 'is-active' : '', text: label });
|
||||
const button = tabs.createEl('button', { cls: this.activePanelPage === id ? 'is-active' : '', text: label, attr: { title: label } });
|
||||
button.addEventListener('click', () => {
|
||||
this.activePanelPage = id;
|
||||
this.renderPanel();
|
||||
|
|
@ -504,7 +508,7 @@ export class Radial2DController extends Component {
|
|||
|
||||
private modeButton(parent: HTMLElement, label: string, mode: ViewMode): void {
|
||||
const active = this.settings.viewMode === mode;
|
||||
const button = parent.createEl('button', { cls: active ? 'is-active' : '', text: label });
|
||||
const button = parent.createEl('button', { cls: active ? 'is-active' : '', text: label, attr: { title: label } });
|
||||
button.addEventListener('click', () => this.onViewMode(mode));
|
||||
}
|
||||
|
||||
|
|
@ -573,7 +577,8 @@ export class Radial2DController extends Component {
|
|||
for (const edge of edges) {
|
||||
const neighbor = this.index.nodes.get(edge[side]);
|
||||
if (!neighbor) continue;
|
||||
const button = parent.createEl('button', { cls: 'mwm-link-row', text: `${neighbor.title} (${edge.weight})` });
|
||||
const label = `${neighbor.title} (${edge.weight})`;
|
||||
const button = parent.createEl('button', { cls: 'mwm-link-row', text: label, attr: { title: label } });
|
||||
button.addEventListener('click', () => this.inspectNode(neighbor.id));
|
||||
}
|
||||
}
|
||||
|
|
@ -647,9 +652,10 @@ export class Radial2DController extends Component {
|
|||
this.button(row, this.t('common.recenter'), () => this.centerCurrentView());
|
||||
this.button(row, this.t('common.rebuild'), () => this.rebuild('manual'));
|
||||
const row2 = parent.createDiv({ cls: 'galaxy-panel-row' });
|
||||
this.button(row2, this.t('view.atlas'), () => this.resetToAtlas(), this.state.mode === 'atlas');
|
||||
this.button(row2, this.t('view.atlas'), () => this.resetToAtlas(), this.state.mode === 'atlas' && !this.state.showCompleteRoot);
|
||||
this.button(row2, this.t('view.focus'), () => this.focusActiveNote(), this.state.mode === 'focus');
|
||||
this.button(row2, this.t('view.vaultRoot'), () => this.resetToRoot());
|
||||
this.button(row2, this.t('view.completeMap'), () => this.showCompleteMap(), this.state.showCompleteRoot);
|
||||
this.select(parent, this.t('view.theme'), this.radial().colorScheme, colorSchemeOptions(this.settings.language), (value) => {
|
||||
this.radial().colorScheme = normalizeColorScheme(value);
|
||||
this.saveSoon();
|
||||
|
|
@ -707,6 +713,7 @@ export class Radial2DController extends Component {
|
|||
this.state.mode = 'atlas';
|
||||
this.state.focusPath = null;
|
||||
if (node.type === 'folder') {
|
||||
this.leaveCompleteMap();
|
||||
this.state.rootPath = visualId;
|
||||
await this.queueRebuild('root');
|
||||
return;
|
||||
|
|
@ -716,6 +723,7 @@ export class Radial2DController extends Component {
|
|||
this.renderPanel();
|
||||
return;
|
||||
}
|
||||
this.leaveCompleteMap();
|
||||
this.state.rootPath = this.searchAtlasRoot(node, visualId);
|
||||
await this.queueRebuild('root');
|
||||
}
|
||||
|
|
@ -732,6 +740,42 @@ export class Radial2DController extends Component {
|
|||
if (this.renderer?.fitToLayout(rootId)) this.needsFit = false;
|
||||
}
|
||||
|
||||
private showCompleteMap(): void {
|
||||
this.state.showCompleteRoot = true;
|
||||
this.applyCompleteMapState();
|
||||
this.needsFit = true;
|
||||
this.rebuild('complete');
|
||||
}
|
||||
|
||||
private applyCompleteMapState(): void {
|
||||
this.state.mode = 'atlas';
|
||||
this.state.rootPath = ROOT_ID;
|
||||
this.state.focusPath = null;
|
||||
this.state.search = '';
|
||||
this.state.atlasDepth = MAX_ATLAS_DEPTH;
|
||||
this.state.nodeLimit = MAX_RENDER_NODE_LIMIT;
|
||||
this.state.linkLimit = MAX_LINK_LIMIT;
|
||||
this.state.externalLinkAnchorLimit = MAX_EXTERNAL_LINK_ANCHOR_LIMIT;
|
||||
this.state.showLinkOverlay = true;
|
||||
this.state.showExternalLinks = true;
|
||||
this.state.externalDetailMode = 'exact';
|
||||
this.state.hiddenLegendItems = [];
|
||||
}
|
||||
|
||||
private leaveCompleteMap(): void {
|
||||
if (!this.state.showCompleteRoot) return;
|
||||
const radial = this.radial();
|
||||
this.state.showCompleteRoot = false;
|
||||
this.state.atlasDepth = radial.atlasDepth;
|
||||
this.state.nodeLimit = radial.renderNodeLimit;
|
||||
this.state.linkLimit = radial.linkLimit;
|
||||
this.state.externalLinkAnchorLimit = radial.externalLinkAnchorLimit;
|
||||
this.state.showExternalLinks = radial.showExternalLinks;
|
||||
this.state.externalDetailMode = radial.externalDetailMode;
|
||||
this.state.hiddenLegendItems = radial.hiddenLegendItems.slice();
|
||||
this.state.showLinkOverlay = radial.showLinkOverlay || !this.state.hiddenLegendItems.includes('link');
|
||||
}
|
||||
|
||||
private centerNode(nodeId: string): void {
|
||||
const point = this.renderer?.nodePoint(nodeId);
|
||||
const view = this.renderer?.getView();
|
||||
|
|
@ -750,20 +794,24 @@ export class Radial2DController extends Component {
|
|||
private renderControlsPage(parent: HTMLElement): void {
|
||||
const radial = this.radial();
|
||||
this.numberInput(parent, this.t('control.depth'), this.state.atlasDepth, 1, MAX_ATLAS_DEPTH, 1, (value) => {
|
||||
this.leaveCompleteMap();
|
||||
this.state.atlasDepth = value;
|
||||
this.rebuild('depth');
|
||||
});
|
||||
this.numberInput(parent, this.t('control.nodes'), this.state.nodeLimit, 200, MAX_RENDER_NODE_LIMIT, 100, (value) => {
|
||||
this.leaveCompleteMap();
|
||||
this.state.nodeLimit = value;
|
||||
this.rebuild('nodes');
|
||||
});
|
||||
this.numberInput(parent, this.t('control.noteLinks'), this.state.linkLimit, 0, MAX_LINK_LIMIT, 50, (value) => {
|
||||
this.leaveCompleteMap();
|
||||
this.state.linkLimit = value;
|
||||
this.rebuild('links');
|
||||
});
|
||||
const hidden = new Set(radial.hiddenLegendItems);
|
||||
const hidden = new Set(this.state.hiddenLegendItems);
|
||||
const noteLinksVisible = this.state.showLinkOverlay && !hidden.has('link');
|
||||
this.toggle(parent, this.t('control.showNoteLinks'), noteLinksVisible, (value) => {
|
||||
this.leaveCompleteMap();
|
||||
radial.showLinkOverlay = value;
|
||||
this.state.showLinkOverlay = value;
|
||||
this.setLegendHidden('link', !value);
|
||||
|
|
@ -806,6 +854,7 @@ export class Radial2DController extends Component {
|
|||
this.renderPanel();
|
||||
});
|
||||
this.toggle(parent, this.t('control.outsideLinks'), this.state.showExternalLinks, (value) => {
|
||||
this.leaveCompleteMap();
|
||||
this.state.showExternalLinks = value;
|
||||
radial.showExternalLinks = value;
|
||||
this.saveSoon();
|
||||
|
|
@ -817,6 +866,7 @@ export class Radial2DController extends Component {
|
|||
this.state.externalDetailMode,
|
||||
outsideDetailOptions(this.settings.language),
|
||||
(value) => {
|
||||
this.leaveCompleteMap();
|
||||
this.state.externalDetailMode = normalizeExternalDetailMode(value);
|
||||
radial.externalDetailMode = this.state.externalDetailMode;
|
||||
this.saveSoon();
|
||||
|
|
@ -824,6 +874,7 @@ export class Radial2DController extends Component {
|
|||
},
|
||||
);
|
||||
this.numberInput(parent, this.t('control.exactOutsideFiles'), this.state.externalLinkAnchorLimit, 0, MAX_EXTERNAL_LINK_ANCHOR_LIMIT, 50, (value) => {
|
||||
this.leaveCompleteMap();
|
||||
this.state.externalLinkAnchorLimit = value;
|
||||
radial.externalLinkAnchorLimit = value;
|
||||
this.saveSoon();
|
||||
|
|
@ -866,7 +917,7 @@ export class Radial2DController extends Component {
|
|||
|
||||
private renderLegend(parent: HTMLElement): void {
|
||||
parent.createDiv({ cls: 'mwm-side-heading', text: this.t('control.legend') });
|
||||
const hidden = new Set(this.radial().hiddenLegendItems);
|
||||
const hidden = new Set(this.state.hiddenLegendItems);
|
||||
for (const [id, labelKey, titleKey, markerClass] of LEGEND_ITEM_DEFINITIONS) {
|
||||
const row = parent.createEl('label', {
|
||||
cls: hidden.has(id) ? 'mwm-legend-item is-muted' : 'mwm-legend-item',
|
||||
|
|
@ -879,6 +930,7 @@ export class Radial2DController extends Component {
|
|||
text.createSpan({ cls: 'mwm-legend-label', text: this.t(labelKey) });
|
||||
text.createSpan({ cls: 'mwm-legend-desc', text: this.t(titleKey) });
|
||||
checkbox.addEventListener('change', () => {
|
||||
this.leaveCompleteMap();
|
||||
const value = checkbox.checked;
|
||||
if (value) hidden.delete(id);
|
||||
else hidden.add(id);
|
||||
|
|
@ -938,7 +990,7 @@ export class Radial2DController extends Component {
|
|||
}
|
||||
|
||||
private button(parent: HTMLElement, label: string, onClick: () => void, active = false): HTMLButtonElement {
|
||||
const button = parent.createEl('button', { cls: active ? 'is-active' : '', text: label });
|
||||
const button = parent.createEl('button', { cls: active ? 'is-active' : '', text: label, attr: { title: label } });
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
}
|
||||
|
|
@ -951,7 +1003,7 @@ export class Radial2DController extends Component {
|
|||
}
|
||||
|
||||
private textArea(parent: HTMLElement, label: string, value: string, onChange: (value: string) => void): void {
|
||||
const field = parent.createEl('label', { cls: 'mwm-panel-field' });
|
||||
const field = parent.createEl('label', { cls: 'mwm-panel-field mwm-panel-field-stack' });
|
||||
field.createSpan({ text: label });
|
||||
const input = field.createEl('textarea');
|
||||
input.value = value;
|
||||
|
|
@ -1125,6 +1177,7 @@ export class Radial2DController extends Component {
|
|||
}
|
||||
|
||||
private resetToAtlas(): void {
|
||||
this.leaveCompleteMap();
|
||||
this.state.mode = 'atlas';
|
||||
this.state.rootPath = this.state.rootPath || ROOT_ID;
|
||||
this.state.focusPath = null;
|
||||
|
|
@ -1133,6 +1186,7 @@ export class Radial2DController extends Component {
|
|||
}
|
||||
|
||||
private resetToRoot(): void {
|
||||
this.leaveCompleteMap();
|
||||
this.state.mode = 'atlas';
|
||||
this.state.rootPath = ROOT_ID;
|
||||
this.state.focusPath = null;
|
||||
|
|
@ -1150,6 +1204,7 @@ export class Radial2DController extends Component {
|
|||
}
|
||||
|
||||
private useAsRoot(nodeId: string): void {
|
||||
this.leaveCompleteMap();
|
||||
this.state.mode = 'atlas';
|
||||
this.state.rootPath = nodeId;
|
||||
this.state.focusPath = null;
|
||||
|
|
@ -1164,6 +1219,7 @@ export class Radial2DController extends Component {
|
|||
}
|
||||
|
||||
private focusNote(nodeId: string): void {
|
||||
this.leaveCompleteMap();
|
||||
this.state.mode = 'focus';
|
||||
this.state.focusPath = nodeId;
|
||||
this.state.rootPath = ROOT_ID;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
DEFAULT_RADIAL_SETTINGS,
|
||||
MAX_ATLAS_DEPTH,
|
||||
MAX_EXTERNAL_LINK_ANCHOR_LIMIT,
|
||||
MAX_LINK_LIMIT,
|
||||
MAX_RENDER_NODE_LIMIT,
|
||||
|
|
@ -57,7 +58,9 @@ export function visualNodeId(model: WorldModel, id: string | null | undefined):
|
|||
function buildAtlasGraph(model: WorldModel, state: VisibleGraphState, settings: RadialSettings): VisibleWorldGraph {
|
||||
const rootId = model.nodes.has(state.rootPath) ? state.rootPath : ROOT_ID;
|
||||
const rootDepth = model.nodes.get(rootId)?.depth ?? 0;
|
||||
const maxDepth = clampNumber(state.atlasDepth, 1, 80, settings.atlasDepth);
|
||||
const maxDepth = state.showCompleteRoot
|
||||
? Math.max(MAX_ATLAS_DEPTH, model.stats.maxDepth - rootDepth)
|
||||
: clampNumber(state.atlasDepth, 1, MAX_ATLAS_DEPTH, settings.atlasDepth);
|
||||
const query = normalizedQuery(state.search);
|
||||
const visible = new Set<string>();
|
||||
|
||||
|
|
@ -186,12 +189,14 @@ function aggregateVisibleLinkEdges(
|
|||
const aggregate = new Map<string, WorldEdge>();
|
||||
const externalNodes = new Map<string, WorldNode>();
|
||||
const externalHierarchyEdges: WorldEdge[] = [];
|
||||
const externalLimit = clampNumber(
|
||||
state.externalLinkAnchorLimit,
|
||||
0,
|
||||
MAX_EXTERNAL_LINK_ANCHOR_LIMIT,
|
||||
settings.externalLinkAnchorLimit,
|
||||
);
|
||||
const externalLimit = state.showCompleteRoot
|
||||
? MAX_EXTERNAL_LINK_ANCHOR_LIMIT
|
||||
: clampNumber(
|
||||
state.externalLinkAnchorLimit,
|
||||
0,
|
||||
MAX_EXTERNAL_LINK_ANCHOR_LIMIT,
|
||||
settings.externalLinkAnchorLimit,
|
||||
);
|
||||
const externalContext = { fileCount: 0, overflowId: null as string | null };
|
||||
|
||||
for (const raw of model.linkEdges) {
|
||||
|
|
@ -269,7 +274,9 @@ function applyNodeBudget(
|
|||
focusId: string | null,
|
||||
): { nodes: WorldNode[]; hiddenNodeCount: number } {
|
||||
if (state.showCompleteRoot && nodes.length <= MAX_RENDER_NODE_LIMIT) return { nodes, hiddenNodeCount: 0 };
|
||||
const limit = clampNumber(state.nodeLimit, 200, MAX_RENDER_NODE_LIMIT, settings.renderNodeLimit);
|
||||
const limit = state.showCompleteRoot
|
||||
? MAX_RENDER_NODE_LIMIT
|
||||
: clampNumber(state.nodeLimit, 200, MAX_RENDER_NODE_LIMIT, settings.renderNodeLimit);
|
||||
if (nodes.length <= limit) return { nodes, hiddenNodeCount: 0 };
|
||||
|
||||
const candidates = nodes
|
||||
|
|
|
|||
43
styles.css
43
styles.css
|
|
@ -159,14 +159,17 @@
|
|||
}
|
||||
|
||||
.galaxy-panel-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(108px, 1fr));
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.galaxy-panel-row button {
|
||||
flex: 1 1 104px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 28px;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
|
|
@ -178,8 +181,9 @@
|
|||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.galaxy-panel-row button:hover {
|
||||
|
|
@ -604,7 +608,7 @@
|
|||
--mwm-legend-unresolved-color: #dc4a4a;
|
||||
--mwm-legend-tree-color: #93a3b8;
|
||||
--mwm-legend-link-color: #a2acba;
|
||||
--mwm-legend-external-link-color: #c98135;
|
||||
--mwm-legend-external-link-color: #b8752e;
|
||||
--mwm-floating-bg: rgba(255, 255, 255, 0.82);
|
||||
--mwm-floating-border: rgba(23, 32, 51, 0.14);
|
||||
--mwm-floating-color: #172033;
|
||||
|
|
@ -625,7 +629,7 @@
|
|||
--mwm-legend-unresolved-color: #fb7185;
|
||||
--mwm-legend-tree-color: #818cf8;
|
||||
--mwm-legend-link-color: #94a3b8;
|
||||
--mwm-legend-external-link-color: #fb923c;
|
||||
--mwm-legend-external-link-color: #d97706;
|
||||
--mwm-floating-bg: rgba(10, 14, 24, 0.64);
|
||||
--mwm-floating-border: rgba(255, 255, 255, 0.12);
|
||||
--mwm-floating-color: #e8ecf6;
|
||||
|
|
@ -788,10 +792,11 @@
|
|||
|
||||
.mwm-panel-tabs {
|
||||
gap: 4px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(58px, 1fr));
|
||||
}
|
||||
|
||||
.mwm-panel-tabs button {
|
||||
flex: 1 1 86px;
|
||||
min-height: 28px;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
|
|
@ -803,8 +808,9 @@
|
|||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.mwm-panel-tabs button.is-active,
|
||||
|
|
@ -830,8 +836,8 @@
|
|||
.mwm-panel-field,
|
||||
.mwm-panel-toggle {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: stretch;
|
||||
grid-template-columns: minmax(88px, 0.42fr) minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
@ -839,11 +845,17 @@
|
|||
.mwm-panel-toggle {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
justify-content: start;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.mwm-panel-field-stack {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.mwm-panel-field > span,
|
||||
.mwm-panel-toggle > span {
|
||||
min-width: 0;
|
||||
line-height: 1.2;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
|
|
@ -1068,6 +1080,7 @@
|
|||
.mwm-link-row,
|
||||
.mwm-pin-main {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 28px;
|
||||
padding: 5px 6px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
|
|
@ -1080,7 +1093,7 @@
|
|||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.mwm-pin-row,
|
||||
|
|
@ -1106,6 +1119,7 @@
|
|||
.mwm-pin-main {
|
||||
flex: 1;
|
||||
flex-basis: 150px;
|
||||
min-width: 0;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
@ -1117,6 +1131,13 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mwm-pin-meta {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mwm-floating-controls {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
|
|
|
|||
|
|
@ -155,6 +155,44 @@ describe('visible world graph', () => {
|
|||
expect(graph.focusId).toBe('Atlas/Topic A.md');
|
||||
expect(graph.nodesById.has('Atlas/Sub/Topic B.md')).toBe(true);
|
||||
});
|
||||
|
||||
it('expands the complete root atlas across depth and link budgets', () => {
|
||||
const deepRecords: { path: string; basename: string; kind: 'folder' | 'note' }[] = [
|
||||
{ path: 'Deep', basename: 'Deep', kind: 'folder' },
|
||||
];
|
||||
let current = 'Deep';
|
||||
for (let index = 0; index < 8; index++) {
|
||||
current = `${current}/Layer ${index}`;
|
||||
deepRecords.push({ path: current, basename: `Layer ${index}`, kind: 'folder' });
|
||||
}
|
||||
const deepNote = `${current}/Deep.md`;
|
||||
deepRecords.push({ path: deepNote, basename: 'Deep', kind: 'note' });
|
||||
for (let index = 0; index < 4; index++) {
|
||||
deepRecords.push({ path: `Note ${index}.md`, basename: `Note ${index}`, kind: 'note' });
|
||||
}
|
||||
const resolvedLinks = {
|
||||
[deepNote]: Object.fromEntries(Array.from({ length: 4 }, (_, index) => [`Note ${index}.md`, 1])),
|
||||
};
|
||||
const m = buildWorldMap(deepRecords, resolvedLinks, {}, DEFAULT_RADIAL_SETTINGS);
|
||||
const limitedState = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
|
||||
limitedState.atlasDepth = 1;
|
||||
limitedState.linkLimit = 1;
|
||||
limitedState.showLinkOverlay = true;
|
||||
const limited = buildVisibleWorldGraph(m, limitedState, DEFAULT_RADIAL_SETTINGS);
|
||||
|
||||
const completeState = defaultVisibleGraphState(DEFAULT_RADIAL_SETTINGS);
|
||||
completeState.atlasDepth = 1;
|
||||
completeState.linkLimit = 1;
|
||||
completeState.showLinkOverlay = true;
|
||||
completeState.showCompleteRoot = true;
|
||||
const complete = buildVisibleWorldGraph(m, completeState, DEFAULT_RADIAL_SETTINGS);
|
||||
|
||||
expect(limited.nodesById.has(deepNote)).toBe(false);
|
||||
expect(limited.linkEdges).toHaveLength(1);
|
||||
expect(complete.nodesById.has(deepNote)).toBe(true);
|
||||
expect(complete.hiddenNodeCount).toBe(0);
|
||||
expect(complete.linkEdges).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('radial layout', () => {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@
|
|||
"0.1.3": "1.5.0",
|
||||
"0.2.0": "1.5.0",
|
||||
"0.2.1": "1.5.0",
|
||||
"0.2.2": "1.13.0"
|
||||
"0.2.2": "1.13.0",
|
||||
"0.2.3": "1.13.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue