Skip to content

feat: improve concept map layout#7

Merged
bobthearsonist merged 11 commits into
mainfrom
issue/2-map-layout
May 5, 2026
Merged

feat: improve concept map layout#7
bobthearsonist merged 11 commits into
mainfrom
issue/2-map-layout

Conversation

@bobthearsonist

Copy link
Copy Markdown
Owner

Summary

  • add a deterministic layout pass for Obsidian-generated sidecars
  • group connected concepts into stable columns and tier nodes by graph degree, type, and status
  • apply the layout on write and render while preserving the existing sidecar schema
  • document that LLM positions are now seed positions

Validation

  • npm run typecheck -- --pretty false
  • npm run build

Fixes #2

Remaining risks

  • This is a practical first-pass heuristic; larger real-world daily notes may still need tuning after visual review.
  • Pinned sidecars keep their curated/manual positions by design.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Local Test vault smoke test completed after the agent report. I deployed this branch to the Test vault, force-regenerated 0 Daily ADHD Brain Logs/20260502.md, and verified the graph renders in Obsidian preview mode. Results: 22 nodes / 23 edges, one non-embedded Visual Notes container, graph rect about 1908x450, legend present, usage footer present. Sidecar deterministic position bounds were x=220..1630 and y=120..856; that confirms the layout code is applied, but the x span is still fairly wide and may need tuning for normal pane widths.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

After a proper local screenshot comparison, this layout change does not currently satisfy issue #2. I compared main vs this branch using the same daily note and same generated sidecar. Main keeps the graph relatively compact; PR #7 spreads nodes across the full card width, creates a lot of whitespace, and makes relationships harder to scan. We should not merge this as-is. The branch is still useful as a first deterministic-layout spike, but it needs another iteration with visual acceptance criteria and screenshot comparison before it is merge-ready.

@bobthearsonist bobthearsonist marked this pull request as draft May 2, 2026 18:30
@bobthearsonist

Copy link
Copy Markdown
Owner Author

Follow-up: the right conclusion is stronger than my previous comment. Both main and this PR's layout suck; this branch is only a deterministic-layout spike, not a shippable solution. The next iteration should optimize for actual visual quality with screenshot-based acceptance, not just deterministic positions. Keeping this PR draft.

bobthearsonist added a commit that referenced this pull request May 2, 2026
Replace the wide tier-grid layout with anchor-centered radial clusters based on the original hook strategy. Add deterministic layout metrics evidence for PR #7 comparison when screenshots are unavailable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bobthearsonist

Copy link
Copy Markdown
Owner Author

Updated deterministic layout with compact anchor-centered clusters instead of the prior wide tier grid. No local Obsidian screenshot capture was available, so I added automated layout evidence via pnpm --filter @visual-notes/obsidian-plugin layout:metrics.

Evidence from the PR #7 spread fixture:

  • before: width 1410px, height 700px, max radius 865.4px, 4 components
  • after: width 795px, height 680px, max radius 481.1px, 4 components
  • improvement: width -43.6%, max radius -44.4%
  • max x after layout: 910px, keeping the graph readable at normal pane width

Validation run:

  • pnpm --filter @visual-notes/obsidian-plugin layout:metrics
  • pnpm -r typecheck
  • pnpm -r build

Keeping PR #7 draft pending visual screenshot confirmation in Obsidian.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Follow-up local Obsidian verification after compact layout update: deployed this branch to the Test vault and force-regenerated 0 Daily ADHD Brain Logs/20260502.md. The real sidecar now produces 19 nodes / 17 edges with compact positive bounds x=20..910, y=40..745. This fixes the previous negative-Y clipping risk while preserving the compact cluster improvement. Validation rerun: npm run layout:metrics, npm run typecheck, npm run build. Keeping PR draft until screenshot review confirms the visual is actually acceptable.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

@copilot review

Copilot AI commented May 3, 2026

Copy link
Copy Markdown
Contributor

@copilot review

Code review of the three commits (709223c6366d60dffe149):

typecheck / build / layout:metrics — all pass.


Findings

1. shiftComponentIntoVisibleCanvas is a no-op (dead code)
setPosition calls clampPosition before pushing each position, so every position is already ≥ VISIBLE_MIN_X/VISIBLE_MIN_Y by the time shiftComponentIntoVisibleCanvas runs. Math.max(0, VISIBLE_MIN_X - minX) will always be 0. The function can be removed, or clampPosition should be moved out of setPosition so the shift approach can actually take effect.

2. Empty-graph edge case in calculateLayoutMetrics
Math.min(...[]) returns Infinity and Math.max(...[]) returns -Infinity, so minX, maxX, maxDistanceFromCentroid, etc. are garbage when sidecar.nodes is empty. Shouldn't affect normal rendering paths, but the metrics script fixture always has nodes so this masks the issue.

3. Cross-component overlap is possible
Each layoutComponent call starts with a fresh usedPositions = [], so collision detection is local to one component. Two clusters placed by columnForComponent at CLUSTER_START_X + 0 * 450 and CLUSTER_START_X + 1 * 450 (260px and 710px) can still have their slot offsets produce overlapping absolute positions (e.g., an outcome slot at +345x from anchor 260 = 605px can land inside a systems slot at -120x from anchor 710 = 590px).

4. context and other PREFERRED_SLOTS share slots
Both start with { x: 0, y: -235 } and { x: 145, y: -235 }. When a component has both context and other nodes, the first node of each bucket will collide and the slot-scan fallback will kick in for one of them.

5. Double layout application
Layout is applied in main.ts at write time and again in renderer.ts at render time. For non-pinned sidecars this is idempotent (positions in the sidecar don't feed back into the algorithm), but it doubles the compute on every render tick. Acceptable for current graph sizes; worth a note for future optimisation.


Positive observations

  • Union-find component detection and path-compression are correctly implemented.
  • Pinned-sidecar bypass is clean.
  • layout-metrics.mjs gives a reproducible regression guard without requiring Obsidian; the 43 % width / 44 % max-radius improvement over the prior spike is well-evidenced.
  • Keeping the PR as draft pending a real Obsidian screenshot is the right call.

Issues 1 and 4 are the only ones likely to cause visible layout bugs in edge cases; the rest are quality/edge-case notes.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Tested the current PR branch locally in the real Test Obsidian vault after syncing to the latest branch tip and rebuilding/sideloading the plugin.

Validation performed:

  • built plugins/obsidian-plugin successfully
  • deployed main.js, manifest.json, and styles.css into ~/SynologyDrive/Test/.obsidian/plugins/visual-notes
  • opened the real daily note 0 Daily ADHD Brain Logs/20260502.md
  • confirmed the sidecar has 19 nodes / 17 edges with positive bounds (x=20..910, y=40..745)
  • captured a real Obsidian screenshot of the rendered note

Result: this is now actually tested in Obsidian, not just checked by metrics. The compact layout is visibly better than the earlier wide PR #7 attempt because it fits in the card without the huge horizontal sprawl. It is still not perfect: labels are small and the clusters could be more readable, so I am keeping the PR in draft unless/until we decide this visual quality is acceptable.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Updated PR #7 with a repeatable semantic-lane layout strategy instead of fan/radial placement.

What changed:

  • Replaced local component slotting with deterministic semantic lanes: systems/anchors left, outcomes next, decisions/context/details right, balanced rows for card fit.
  • Added primary-edge crossing optimization and kept weak/cross-domain links visually secondary.
  • Defensive empty-graph metrics now return finite values.
  • Added root npm run layout:metrics plus quality gates for dimensions, card-fit scale, close pairs, min distance, and primary-edge crossings.
  • Increased label readability and kept edge labels horizontal.

Validation:

  • npm run layout:metrics
  • npm run layout:metrics -- /Users/dislexicmofo/SynologyDrive/Test/0 Daily ADHD Brain Logs/20260502-overview.json ✅ real sidecar after metrics: 870w x 530h, fit 0.89, min distance 102px, close pairs 0, primary crossings 0
  • npm run build
  • npm run lint
  • npm run typecheck
  • Built and deployed to Test vault plugin path.

Screenshot artifact:

  • /Users/dislexicmofo/.copilot/session-state/a59aa179-6761-4f28-912b-fed3d88a4d30/files/pr7-layout-fleet/obsidian-pr7-layout-reloaded.png

Honest screenshot verdict: much better and now genuinely readable in the Obsidian card; not a polished infographic, but good enough for the PR to leave draft in my opinion. I left the PR draft status unchanged for maintainer control.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Blocking preservation fix added and pushed.

Commit: 8225e50 fix(obsidian): preserve stored sidecar positions on render

What changed:

  • Removed applyDeterministicLayout from src/renderer.ts; normal viewing now renders the parsed sidecar exactly as stored.
  • Deterministic layout remains in the extraction/write path in main.ts, so positions change only when a sidecar is intentionally generated/updated/force-regenerated.
  • Added a layout:metrics render-persistence guard that fails if the renderer imports/calls deterministic layout or stops passing the parsed sidecar directly to renderGraph.

Validation:

  • npm run layout:metrics ✅ includes render persistence guard
  • npm run layout:metrics -- /Users/dislexicmofo/SynologyDrive/Test/0 Daily ADHD Brain Logs/20260502-overview.json
  • npm run build
  • npm run lint
  • npm run typecheck

Important behavior: archived/old captures are preserved because installing PR #7 no longer reflows stored sidecars at render time. Only intentional extraction/regeneration writes the new layout into JSON.

Screenshot note: I redeployed the build, but the latest OS screenshot attempts produced a black capture artifact, so the prior readable screenshot remains the useful visual artifact. This preservation fix is code/metric validated.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

@copilot review

Copilot AI commented May 3, 2026

Copy link
Copy Markdown
Contributor

@copilot review

Code review of eb0416d (semantic lane layout) + 8225e50 (render persistence):

typecheck / build / layout:metrics — all pass.

All 5 findings from the previous review are addressed:

  • shiftComponentIntoVisibleCanvas dead code removed (entire slot approach replaced)
  • ✅ Empty-graph Math.min/max([]) — fixed with early return in calculateLayoutMetrics
  • ✅ Cross-component overlap — replaced with packComponentLayouts with proper row-wrapping
  • context/other slot collision — slot system gone
  • ✅ Double layout in renderer — applyDeterministicLayout removed from renderer.ts, guard in metrics script confirms it stays removed

New findings

1. evaluateRenderPersistence uses fragile string matching
Substring checks like rendererSource.includes("this.renderGraph(sidecar);") will silently fail (and miss the guard failure) if whitespace or indentation changes. A refactor like this.renderGraph( sidecar ) would pass TypeScript and the build but trip the gate. Low probability but worth knowing.

2. findColumnForOverflow can grow beyond SEMANTIC_COLUMN_COUNT
When all 4 semantic columns hit rowLimit, columns.push([]) silently creates a 5th column. For LLM outputs that concentrate nodes in one lane, this means the final layout could exceed READABLE_CARD_WIDTH. The quality gate would catch it at metrics time, but the silent growth could be confusing. A cap at SEMANTIC_COLUMN_COUNT + 1 (or just accepting the gate failure as the safety net) would make the intent clearer.

3. optimizeColumnOrderForCrossings calls countColumnEdgeCrossings twice per swap candidate
countColumnEdgeCrossings is O(e²). For today's graph sizes this is fine, but for bigger daily notes (50+ nodes) this could become perceptible. No action needed now.

4. _kind stub accepted but never used
layoutNodes(..., _kind?: ...) takes the sidecar kind but ignores it. Fine as a forward-looking hook; the underscore prefix communicates intent.


Metrics summary

fixture width height fit minDist closePairs crossings
PR #7 spread 510px 408px 1.11 102 0 0
20-node daily 870px 428px 1.01 102 0 6

63.8% width reduction on the horizontal spread fixture. The 20-node fixture fits in the card (fit ≥ 1.0) with zero close pairs and crossings within the 6-crossing gate. The semantically-driven lane assignment is significantly cleaner than the radial slot approach. No blockers.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Layout redesign iteration pushed: 2191fdc

Changes:

  • Replaced the prior lane/fan feel with primary-edge story clusters packed as compact cards.
  • Kept saved-position rendering intact: renderer still uses preset sidecar positions and does not call applyDeterministicLayout; layout applies on extraction/write and metrics --write only.
  • Increased default readability with larger node font/boxes, tighter fit padding, weak-edge label suppression, and faint story-card grouping.
  • Expanded metrics with edge crossings, close pairs, max primary/weak edge length, weak-edge length budget, component bounds, outlier ratio, card fit scale, and estimated rendered font size.

Validation:

  • npm run layout:metrics: passed
  • npm run build: passed
  • npm run lint: passed
  • npm run typecheck: passed
  • Deployed main.js, manifest.json, styles.css to /Users/dislexicmofo/SynologyDrive/Test/.obsidian/plugins/visual-notes
  • Re-laid out Test daily sidecar for /Users/dislexicmofo/SynologyDrive/Test/0 Daily ADHD Brain Logs/20260502.md

Screenshot:
/Users/dislexicmofo/.copilot/session-state/a59aa179-6761-4f28-912b-fed3d88a4d30/files/pr7-cluster-redesign/obsidian-pr7-cluster-redesign-final.png

Honest verdict: materially better than the rejected screenshot: no far-right outlier, labels are readable at default fit, and the daily overview now reads as grouped story cards rather than one raw dump. Remaining imperfection: the Visual Notes story still has some dense edge-label overlap in the middle, but weak/cross-domain links are de-emphasized and the overall structure is intentional enough for PR review.

@bobthearsonist bobthearsonist marked this pull request as ready for review May 3, 2026 15:54
@bobthearsonist

Copy link
Copy Markdown
Owner Author

Final layout iteration pushed.

What changed:

  • weak/context singleton nodes now attach to the nearest relevant story cluster instead of becoming detached bottom/outlier groups
  • story containers are visual grouping only; their tiny labels are hidden to reduce clutter
  • graph viewport height was reduced after clustering so the card no longer feels like a huge blank canvas
  • saved-position rendering remains preserved: the renderer does not run deterministic layout at view time

Validation:

  • npm run layout:metrics
  • npm run typecheck
  • npm run build
  • npm run lint
  • deployed to the Test vault and restarted Obsidian
  • captured real Obsidian window screenshot: /Users/dislexicmofo/.copilot/session-state/a59aa179-6761-4f28-912b-fed3d88a4d30/files/pr7-final-polish/obsidian-pr7-final-polish.png

Verdict: this is materially better than the prior fan/lane/outlier versions and good enough for review.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

@copilot review

@bobthearsonist

Copy link
Copy Markdown
Owner Author

@copilot review latest commit eb20611

Copilot AI commented May 3, 2026

Copy link
Copy Markdown
Contributor

@copilot review

Code review of 2191fdc (story-cluster redesign) + eb20611 (context node attachment / final polish):

typecheck / build / lint / layout:metrics — all pass.

All 4 new findings from the previous review are addressed:

  • ✅ Fragile evaluateRenderPersistence string matching — now also guards displayLabel and font-size 17, and the gates cover both the no-op and the wrong-value failure modes
  • findColumnForOverflow silent column growth — semantic-lane approach replaced entirely
  • ✅ O(e²) per-swap crossing counting — removed with the new approach
  • _kind stub — removed

New findings

1. pullWeakAttachmentsClose is effectively dead code after buildVisualComponents
buildVisualComponents absorbs every singleton node into a multi-node story cluster before packStoryClusters runs. The pullWeakAttachmentsClose call immediately after receives components where every component has nodes.length > 1, so the if (component.nodes.length > 1) return guard fires for all of them and nothing moves. The one edge-case where it could run (graph with zero story clusters — all nodes isolated) is also a no-op since there are no story targets to attach to. The function can be removed without changing behavior; or if it's intended as a future hook it could be documented as such.

2. Layout and renderer story-grouping can diverge
buildVisualComponents (layout, write-time) merges singletons into the nearest story cluster by index proximity. buildStoryGroups (renderer, view-time) independently re-computes components from stored positions and edges, then collects singletons into a separate "Cross-cutting insights" card. So a singleton node that was absorbed and positioned inside cluster A at write time will appear visually inside the "Cross-cutting insights" story card at render time — not inside the cluster A card. The position is correct (it's near cluster A), but the story card border will be wrong. Not a crash, but could be confusing.

3. evaluateRenderPersistence guards the magic literal '"font-size": 17'
Same category as the previous "this.renderGraph(sidecar);" concern. A harmless refactor like extracting NODE_FONT_SIZE into renderer.ts or adding a trailing space would silence the guard. Consider documenting this constraint with an inline comment so future refactors know why the literal must match.

4. Taxi routing with compound nodes may produce edge clipping artifacts
Cytoscape's taxi router routes edges around compound node boundaries, which can produce extra bends or small overlapping arrows when both endpoints are in the same story card. This is a rendering engine concern, not a code bug — worth knowing if story-internal edge labels look odd in Obsidian.


Metrics summary

fixture width height fit minDist closePairs crossings maxEdge font
PR #7 spread 590px 530px 1.13 110 0 0 244px 19.2px
20-node daily 790px 640px 0.96 110 0 3 424px 16.3px

58.2% width reduction on the spread fixture; 17.7% on the 20-node. Both pass all gates. The 20-node fixture is close to three ceilings simultaneously (fit=0.96 vs 0.85 gate, maxEdge=424 vs 430 gate, height=640 vs 660 gate) — a slightly denser real sidecar could stress these.

Finding 1 is the only one likely to cause confusion during future changes; findings 2–4 are cosmetic or environmental. No blockers.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Final follow-up after local code review:

  • fixed a renderer off-by-one where exactly one singleton/cross-cutting node could be rendered without a story-card group
  • reran npm run layout:metrics, npm run typecheck, npm run build, and npm run lint

The latest validated screenshot remains: /Users/dislexicmofo/.copilot/session-state/a59aa179-6761-4f28-912b-fed3d88a4d30/files/pr7-final-polish/obsidian-pr7-final-polish.png.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

@copilot review latest singleton grouping fix

Copilot AI commented May 3, 2026

Copy link
Copy Markdown
Contributor

@copilot review latest singleton grouping fix

Code review of b450241 (singleton story grouping fix):

typecheck / build / lint / layout:metrics — all pass.


What the fix does

The old reduce approach created a __singletons__ sentinel as groupNodes[0], then sliced it off, then filtered length > 1. With exactly one singleton the group became [sentinel, singleton].slice(1)[singleton] → filtered out by length > 1. The fix removes the sentinel entirely: flatMap collects all singletons into one plain VisualNotesNode[], appended to storyGroups, and the filter becomes length > 0. The logic is now correct for 0, 1, or N singletons.


New findings

1. No new issues introduced. The isSingletonStory predicate is still correct: singletons by definition have no primary edges connecting them, so primaryEdges.every(...) returns true and they are labelled "Cross-cutting insights". ✅
groupTopLeft on a one-element singletonStory calls Math.min(...[x]) which is x — safe. ✅
The singletonStory is sorted with the rest by groupTopLeft, so its story-card position reflects the topmost-leftmost singleton. ✅


Carry-over findings (status unchanged)

  • Finding 1 (pullWeakAttachmentsClose dead code) — still present, no behavior change.
  • Finding 2 (layout/renderer story-grouping divergence) — still present; singletons absorbed into a cluster at write-time may appear in the "Cross-cutting insights" card at render-time. Cosmetic; now at least they always get a card.
  • Finding 3 (fragile '"font-size": 17' literal guard) — still present, low risk.
  • Finding 4 (taxi routing / compound node edge clipping) — rendering engine concern, unchanged.

No blockers. This is a clean simplification that fixes the reported off-by-one.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

MVP renderer recovery update:

  • Restored flat sidecar node/edge rendering with no story-card compound parents.
  • Preserves saved sidecar positions and avoids render-time deterministic layout.
  • Keeps all relationship labels visible and readable.
  • Uses compact paint-safe 118x54 nodes with 13px labels; exact Cytoscape width/height: label caused Obsidian to keep 12 runtime nodes but visibly paint only 7.
  • Added scripts/renderer-contract.mjs and wired it into npm run validate.

Validation run:

  • npm run validate
  • npm run build
  • npm run lint
  • Obsidian Test vault smoke: files/pr7-final-readable-labels/obsidian-final-readable-labels.png, 12 visible node components detected.

@bobthearsonist

Copy link
Copy Markdown
Owner Author

@copilot review

@bobthearsonist

Copy link
Copy Markdown
Owner Author

Review follow-up pushed:

  • Increased relationship label readability (14px/700, darker text, stronger halo).
  • Preserved all-node rendering in Obsidian smoke: files/pr7-final-readable-labels/obsidian-final-readable-labels.png, 12 visible node components detected.
  • Addressed local review finding: theme changes now update Cytoscape colors without resetting user pan/zoom; ResizeObserver still handles real container resize fitting.
  • Validation remains green: npm run validate && npm run build && npm run lint.

Copilot AI commented May 5, 2026

Copy link
Copy Markdown
Contributor

@copilot review

Code review of 9c3d06f (restore MVP graph rendering) + 428eece (relationship label readability):

validate:layout / validate:renderer / build / lint — all pass.

Note: npm run validate fails in the sandbox due to TypeScript 5.9's stricter treatment of the pre-existing deprecated moduleResolution: "node" and baseUrl options in tsconfig.json — this is not introduced by these commits. validate:layout, validate:renderer, and build all pass independently.


Status of previous carry-over findings

  • pullWeakAttachmentsClose dead code — removed; layout.ts fully redesigned
  • ✅ Layout/renderer story-grouping divergence — moot; renderer is now flat with no story-card synthesis
  • evaluateRenderPersistence magic font-size literal — still present but now double-checked by the independent renderer-contract.mjs
  • ✅ Taxi routing / compound node edge clipping — compound nodes gone

New findings

1. improvesFlatBoardOrder is O(n²) per pass × 3 seeds × up to 8 passes
For FLAT_BOARD_MAX_NODES = 28 with ~20 edges, flatBoardCost (called per swap attempt) runs a crossing O(e²) inner loop — roughly a few hundred thousand operations per layout call. At write/extraction time this is fine. Worth noting if FLAT_BOARD_MAX_NODES is raised significantly or if layout is ever called synchronously in a hot path.

2. validate script depends on typecheck which fails in TypeScript ≥ 5.6 due to deprecated tsconfig options
tsc -noEmit -skipLibCheck exits 2 with moduleResolution: "node" and baseUrl deprecation errors in TypeScript 5.9 (this sandbox). The pre-existing build and lint scripts use the same tsc invocation but inside the build chain so this is latent. The fix is adding "ignoreDeprecations": "6.0" to tsconfig.json or migrating to moduleResolution: "bundler". Not introduced by these commits but surfaced by wiring validate to typecheck.

3. evaluateFixtureContract validates data consistency, not rendered output
renderer-contract.mjs checks that the Profisee fixture has 12 unique node IDs, 11 edges with non-empty labels, and valid endpoints. It does not run applyDeterministicLayout or verify positions, fit metrics, or rendering. Adequate as a structural smoke test; the quality gates in layout-metrics.mjs cover the metric side.

4. Fragile string-literal guards persist (unchanged category)
evaluateRenderPersistence in layout-metrics.mjs and evaluateRendererSourceContract in renderer-contract.mjs both guard the same literals ('"font-size": 14', '"font-weight": 700', '"text-background-opacity": 0.95', etc.). Having two independent guards reduces the chance of a silent miss, but both would still be bypassed by an extracting-constant refactor. Both scripts now agree on the same literals, which is good.


Metrics summary

fixture width height fit minDist closePairs crossings maxEdge font
PR #7 spread 680px 375px 1.09 125 0 0 211px 13px
20-node daily 680px 375px 1.09 125 0 0 302px 13px
Profisee hook 340px 375px 1.09 125 0 0 211px 13px

51.8% width reduction on the horizontal spread fixture; zero edge crossings and close pairs on all fixtures. The flat-board optimizer handles both the 12-node Profisee fixture and the 17-node daily fixture without crossings — a significant improvement over the previous story-cluster approach (3 crossings on 20-node, outlier clusters). No blockers.

bobthearsonist and others added 11 commits May 5, 2026 14:42
Group connected daily overview concepts into stable columns and tiers based on node type, status, and graph degree before writing or rendering sidecars.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the wide tier-grid layout with anchor-centered radial clusters based on the original hook strategy. Add deterministic layout metrics evidence for PR #7 comparison when screenshots are unavailable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shift generated compact layout components into the visible graph canvas so deterministic positions do not clip above or left of the Cytoscape viewport.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the compact/fan-style positioning with deterministic semantic lanes, balanced rows, crossing optimization, and explicit layout metric gates for card fit/readability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stop applying deterministic layout in the renderer so archived captures keep their saved positions. Keep deterministic layout in the extraction/write path for intentional sidecar updates and add a metrics guard for the renderer behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Group daily overview nodes into compact story cards, de-emphasize weak edges, and expand layout metrics to catch readability, outlier, crossing, and edge-length regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep weakly related daily-overview context near its strongest story cluster so generated maps avoid detached outliers while preserving saved-position rendering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep single cross-cutting context nodes inside a story-card group so generated overviews do not lose visual grouping when only one singleton is present.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore flat sidecar rendering so saved graph nodes and relationships render without synthetic story-card compounds. Keep all relationship labels visible, preserve preset positions, and use an Obsidian-safe delayed viewport fit.

Add renderer contract validation for the Profisee MVP fixture and tune compact paint-safe node sizing so all nodes remain visible in Obsidian.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Increase relationship label contrast and size so edge labels remain readable in the Obsidian-rendered concept map while preserving all-node rendering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoid refitting the Cytoscape viewport when Obsidian theme changes only update colors. Container resize still schedules a fit through ResizeObserver.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bobthearsonist bobthearsonist merged commit dda4b52 into main May 5, 2026
bobthearsonist added a commit that referenced this pull request May 5, 2026
Replace the wide tier-grid layout with anchor-centered radial clusters based on the original hook strategy. Add deterministic layout metrics evidence for PR #7 comparison when screenshots are unavailable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bobthearsonist bobthearsonist deleted the issue/2-map-layout branch May 5, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve generated concept map layout quality

2 participants