Feat v0.4.7 note graph
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 22s
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-go-pg (pull_request) Successful in 2m48s
CI/CD / test-sqlite (pull_request) Successful in 3m5s
CI/CD / build-and-deploy (pull_request) Successful in 2m29s

Canvas force-directed graph view for notes surface. GET /graph API
endpoint returns all non-archived notes as nodes (with tags) and
resolved wikilinks as edges. Force simulation with repulsion,
attraction, and center gravity. Click focus dims unconnected nodes,
shift+click chains neighborhoods, double-click opens note. Hover
tooltips show title, tags, and connection count. Nodes colored by
folder (10-color palette), tag filter dims non-matching. Orphan
nodes shown with dashed stroke and hide toggle. Zoom/pan via scroll
wheel and mouse drag. Retina-aware canvas rendering with CSS
variable resolution via getComputedStyle.

Notes package 0.7.0 → 0.8.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 08:59:12 +00:00
parent 50d991001d
commit 6acfeab456
7 changed files with 615 additions and 24 deletions

View File

@@ -706,6 +706,84 @@
.sidebar-outline__item--h1 { font-weight: 600; color: var(--text); }
.sidebar-outline__item--h2 { font-weight: 500; }
/* ── Graph Pane ─────────────────────────── */
.graph-pane {
flex: 1;
position: relative;
overflow: hidden;
background: var(--bg-surface, #fff);
min-height: 0;
}
.graph-pane canvas {
display: block;
width: 100%;
height: 100%;
cursor: grab;
}
.graph-pane canvas:active { cursor: grabbing; }
.graph-toolbar {
position: absolute;
top: 12px;
right: 12px;
display: flex;
gap: 8px;
align-items: center;
z-index: 5;
font-size: 12px;
color: var(--text-2, #666);
}
.graph-toolbar label {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
user-select: none;
}
.graph-toolbar input[type="checkbox"] { margin: 0; }
.graph-tooltip {
position: absolute;
pointer-events: none;
background: var(--bg-raised, #fff);
border: 1px solid var(--border, #ddd);
border-radius: var(--radius, 6px);
padding: 8px 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
z-index: 10;
max-width: 220px;
font-size: 12px;
}
.graph-tooltip__title {
font-weight: 600;
font-size: 13px;
margin-bottom: 2px;
}
.graph-tooltip__tags {
display: flex;
gap: 4px;
flex-wrap: wrap;
margin-top: 4px;
}
.graph-tooltip__tags .tag-pill {
font-size: 10px;
padding: 1px 6px;
border-radius: 8px;
background: var(--bg-hover, #eee);
color: var(--text-2, #666);
}
.graph-tooltip__edges {
font-size: 11px;
color: var(--text-3, #999);
margin-top: 4px;
}
.graph-empty {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: var(--text-3, #999);
font-size: 14px;
}
/* ── Responsive ──────────────────────────── */
@media (max-width: 700px) {
.notes-sidebar { width: 100%; border-right: none; border-bottom: 1px solid var(--border); max-height: 40vh; }
@@ -713,4 +791,5 @@
.folder-tree { max-height: 30vh; }
.notes-editor__body--split { grid-template-columns: 1fr; }
.notes-editor__body--split .notes-preview { display: none; }
.graph-pane { min-height: 50vh; }
}