secondbrain-py 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brain/__init__.py +0 -0
- brain/__main__.py +18 -0
- brain/_capture_command.py +445 -0
- brain/_compose.py +52 -0
- brain/activity.py +206 -0
- brain/ask.py +631 -0
- brain/audio.py +591 -0
- brain/backfill/__init__.py +12 -0
- brain/backfill/search_extras.py +141 -0
- brain/backfill/source_rows.py +101 -0
- brain/bin/__init__.py +1 -0
- brain/bin/_launcher.py +107 -0
- brain/bin/down.py +8 -0
- brain/bin/launchd.py +268 -0
- brain/bin/monitor.py +570 -0
- brain/bin/rebuild.py +8 -0
- brain/bin/status.py +8 -0
- brain/bin/up.py +8 -0
- brain/brief.py +272 -0
- brain/capture.py +49 -0
- brain/chat.py +293 -0
- brain/cli.py +9760 -0
- brain/cli_claude.py +81 -0
- brain/cli_connect.py +285 -0
- brain/cli_demo.py +266 -0
- brain/config.py +1949 -0
- brain/connect.py +925 -0
- brain/db.py +540 -0
- brain/demo/__init__.py +452 -0
- brain/demo/corpus/manifest.json +403 -0
- brain/demo/embedder.py +74 -0
- brain/durations.py +84 -0
- brain/edit_session.py +156 -0
- brain/editor.py +67 -0
- brain/elicit/__init__.py +16 -0
- brain/elicit/detectors.py +250 -0
- brain/elicit/drafter.py +70 -0
- brain/elicit/queue.py +220 -0
- brain/elicit/schema.py +48 -0
- brain/elicit/session.py +445 -0
- brain/embedding_targets.py +54 -0
- brain/embeddings.py +424 -0
- brain/enrichment.py +808 -0
- brain/errors.py +357 -0
- brain/eval/__init__.py +129 -0
- brain/eval/answer_eval.py +281 -0
- brain/eval/baseline.py +265 -0
- brain/eval/concept_extraction.py +378 -0
- brain/eval/corpus.py +152 -0
- brain/eval/errors.py +19 -0
- brain/eval/graph_baseline.py +226 -0
- brain/eval/graph_retrieval.py +202 -0
- brain/eval/graph_runner.py +319 -0
- brain/eval/metrics.py +101 -0
- brain/eval/runner.py +223 -0
- brain/format.py +783 -0
- brain/gaps.py +390 -0
- brain/graph_rag/__init__.py +94 -0
- brain/graph_rag/_retrieval_common.py +113 -0
- brain/graph_rag/aggregates.py +303 -0
- brain/graph_rag/aliases/__init__.py +583 -0
- brain/graph_rag/backends/__init__.py +10 -0
- brain/graph_rag/backends/_age_helpers.py +473 -0
- brain/graph_rag/backends/age.py +782 -0
- brain/graph_rag/backends/base.py +272 -0
- brain/graph_rag/build.py +344 -0
- brain/graph_rag/communities.py +644 -0
- brain/graph_rag/communities_summary.py +437 -0
- brain/graph_rag/concepts.py +202 -0
- brain/graph_rag/cooccur.py +193 -0
- brain/graph_rag/cross_type.py +312 -0
- brain/graph_rag/extract.py +885 -0
- brain/graph_rag/fuse.py +371 -0
- brain/graph_rag/global_.py +412 -0
- brain/graph_rag/grouping.py +372 -0
- brain/graph_rag/person_resolver.py +167 -0
- brain/graph_rag/reconcile.py +792 -0
- brain/graph_rag/relational.py +353 -0
- brain/graph_rag/retrieve.py +526 -0
- brain/graph_rag/router.py +288 -0
- brain/graph_rag/schema.py +320 -0
- brain/graph_rag/sync.py +237 -0
- brain/graph_rag/tenancy.py +43 -0
- brain/graph_rag/themes.py +501 -0
- brain/graph_rag/weighting.py +202 -0
- brain/ingest/__init__.py +1926 -0
- brain/ingest/chunker.py +249 -0
- brain/ingest/docx.py +40 -0
- brain/ingest/gmail.py +621 -0
- brain/ingest/markdown.py +37 -0
- brain/ingest/pdf.py +61 -0
- brain/ingest/stdin.py +22 -0
- brain/ingest/sub_tokens.py +91 -0
- brain/ingest/text.py +16 -0
- brain/interactions.py +205 -0
- brain/maintenance.py +355 -0
- brain/mcp_server.py +3405 -0
- brain/migrations/001_init.sql +43 -0
- brain/migrations/002_qwen3_embedding.sql +17 -0
- brain/migrations/003_vault_model.sql +41 -0
- brain/migrations/004_relax_content_hash_uniqueness.sql +18 -0
- brain/migrations/005_derived_links.sql +67 -0
- brain/migrations/006_dedup_file_by_source_path.sql +25 -0
- brain/migrations/007_email_thread_and_draft.sql +15 -0
- brain/migrations/008_gmail_thread_unique.sql +11 -0
- brain/migrations/009_chunks_weighted_tsv.sql +28 -0
- brain/migrations/010_interactions.sql +30 -0
- brain/migrations/011_documents_summary.sql +23 -0
- brain/migrations/012_graphrag.sql +171 -0
- brain/migrations/013_graphrag_communities.sql +125 -0
- brain/migrations/014_graphrag_community_summary_hash.sql +33 -0
- brain/migrations/015_interactions_graph_targets.sql +89 -0
- brain/migrations/016_index_hygiene.sql +61 -0
- brain/migrations/017_elicit.sql +30 -0
- brain/migrations/018_review_gap_signal_kinds.sql +40 -0
- brain/migrations/019_search_queries.sql +35 -0
- brain/migrations/020_link_suggestions.sql +40 -0
- brain/migrations/021_timeline_doc_date.sql +34 -0
- brain/migrations/022_link_suggestions_undirected.sql +84 -0
- brain/migrations/023_search_queries_fts_count.sql +28 -0
- brain/quartz_overrides/__init__.py +8 -0
- brain/quartz_overrides/quartz/bootstrap-cli.mjs +65 -0
- brain/quartz_overrides/quartz/build.ts +568 -0
- brain/quartz_overrides/quartz/cli/args.js +152 -0
- brain/quartz_overrides/quartz/cli/build_partial_handler.js +544 -0
- brain/quartz_overrides/quartz/cli/handlers.js +636 -0
- brain/quartz_overrides/quartz/components/CommandPalette.tsx +172 -0
- brain/quartz_overrides/quartz/components/Explorer.tsx +198 -0
- brain/quartz_overrides/quartz/components/Footer.tsx +27 -0
- brain/quartz_overrides/quartz/components/Graph.tsx +468 -0
- brain/quartz_overrides/quartz/components/PageTitle.tsx +72 -0
- brain/quartz_overrides/quartz/components/RelatedDocs.tsx +38 -0
- brain/quartz_overrides/quartz/components/Search.tsx +161 -0
- brain/quartz_overrides/quartz/components/SummaryLede.tsx +72 -0
- brain/quartz_overrides/quartz/components/index.ts +92 -0
- brain/quartz_overrides/quartz/components/pages/TagContent.tsx +272 -0
- brain/quartz_overrides/quartz/components/scripts/commandPalette.inline.ts +665 -0
- brain/quartz_overrides/quartz/components/scripts/explorer.inline.ts +768 -0
- brain/quartz_overrides/quartz/components/scripts/graph.inline.ts +2302 -0
- brain/quartz_overrides/quartz/components/scripts/relatedDocs.inline.ts +163 -0
- brain/quartz_overrides/quartz/components/scripts/search.inline.ts +1011 -0
- brain/quartz_overrides/quartz/plugins/emitters/contentIndex.ts +546 -0
- brain/quartz_overrides/quartz/plugins/transformers/codeCopy.ts +94 -0
- brain/quartz_overrides/quartz/plugins/transformers/derivedFenceMark.ts +302 -0
- brain/quartz_overrides/quartz/plugins/transformers/emailThread.ts +148 -0
- brain/quartz_overrides/quartz/plugins/transformers/emptyDoorFilter.ts +213 -0
- brain/quartz_overrides/quartz/plugins/transformers/index.ts +114 -0
- brain/quartz_overrides/quartz/plugins/transformers/linkKindMark.ts +205 -0
- brain/quartz_overrides/quartz/plugins/transformers/linkSourceTag.ts +104 -0
- brain/quartz_overrides/quartz/plugins/transformers/relativeDate.ts +100 -0
- brain/quartz_overrides/quartz/plugins/transformers/reloadSignal.ts +131 -0
- brain/quartz_overrides/quartz/processors/parse.ts +371 -0
- brain/quartz_overrides/quartz/processors/parser_cache.ts +78 -0
- brain/quartz_overrides/quartz/static/brain-logo-dark.png +0 -0
- brain/quartz_overrides/quartz/static/brain-logo-light.png +0 -0
- brain/quartz_overrides/quartz/static/codeCopy.js +196 -0
- brain/quartz_overrides/quartz/static/emailThread.js +334 -0
- brain/quartz_overrides/quartz/static/favicon.ico +0 -0
- brain/quartz_overrides/quartz/static/icon.png +0 -0
- brain/quartz_overrides/quartz/static/linkSourceTag.js +104 -0
- brain/quartz_overrides/quartz/static/relativeDate.js +142 -0
- brain/quartz_overrides/quartz/static/reload.js +168 -0
- brain/quartz_overrides/quartz/styles/brain/_article.scss +252 -0
- brain/quartz_overrides/quartz/styles/brain/_atmosphere.scss +113 -0
- brain/quartz_overrides/quartz/styles/brain/_callouts.scss +180 -0
- brain/quartz_overrides/quartz/styles/brain/_cmdk.scss +7 -0
- brain/quartz_overrides/quartz/styles/brain/_code.scss +208 -0
- brain/quartz_overrides/quartz/styles/brain/_command_palette.scss +369 -0
- brain/quartz_overrides/quartz/styles/brain/_email_thread.scss +228 -0
- brain/quartz_overrides/quartz/styles/brain/_explorer.scss +142 -0
- brain/quartz_overrides/quartz/styles/brain/_home.scss +182 -0
- brain/quartz_overrides/quartz/styles/brain/_links.scss +322 -0
- brain/quartz_overrides/quartz/styles/brain/_marginalia.scss +117 -0
- brain/quartz_overrides/quartz/styles/brain/_motion.scss +175 -0
- brain/quartz_overrides/quartz/styles/brain/_people_hub.scss +100 -0
- brain/quartz_overrides/quartz/styles/brain/_related_docs.scss +137 -0
- brain/quartz_overrides/quartz/styles/brain/_search.scss +252 -0
- brain/quartz_overrides/quartz/styles/brain/_sidebar.scss +468 -0
- brain/quartz_overrides/quartz/styles/brain/_summary_lede.scss +56 -0
- brain/quartz_overrides/quartz/styles/brain/_surface.scss +43 -0
- brain/quartz_overrides/quartz/styles/brain/_tag_content.scss +118 -0
- brain/quartz_overrides/quartz/styles/brain/_tokens.scss +197 -0
- brain/quartz_overrides/quartz/styles/brain/_typography.scss +92 -0
- brain/quartz_overrides/quartz/styles/custom.scss +89 -0
- brain/quartz_overrides/quartz/styles/graph.scss +505 -0
- brain/quartz_overrides/quartz/util/ctx.ts +92 -0
- brain/quartz_overrides/quartz/util/fastpath_manifest.ts +608 -0
- brain/quartz_overrides/quartz/util/path.ts +358 -0
- brain/quartz_overrides/quartz/util/sourceIcons.ts +55 -0
- brain/quartz_overrides/quartz.config.ts +270 -0
- brain/quartz_overrides/quartz.layout.ts +314 -0
- brain/queries.py +1188 -0
- brain/rank_fusion.py +8 -0
- brain/resurface.py +210 -0
- brain/review/__init__.py +26 -0
- brain/review/emit.py +27 -0
- brain/review/queries.py +436 -0
- brain/review/render.py +196 -0
- brain/review/scans.py +355 -0
- brain/review/weekly.py +413 -0
- brain/search.py +704 -0
- brain/set_similarity.py +15 -0
- brain/setup.py +1205 -0
- brain/tags.py +56 -0
- brain/templates/Caddyfile.j2 +9 -0
- brain/templates/__init__.py +1 -0
- brain/templates/bin/__init__.py +1 -0
- brain/templates/bin/_brain-brief-fg.sh +25 -0
- brain/templates/bin/_brain-build-fg.sh +53 -0
- brain/templates/bin/_brain-watcher-fg.sh +65 -0
- brain/templates/bin/brain-down.sh +89 -0
- brain/templates/bin/brain-status.sh +83 -0
- brain/templates/bin/brain-up.sh +221 -0
- brain/templates/docker/age/Dockerfile +79 -0
- brain/templates/docker-compose.stock.yml.j2 +26 -0
- brain/templates/docker-compose.yml.j2 +34 -0
- brain/templates/env.example +190 -0
- brain/templates/launchd/__init__.py +1 -0
- brain/templates/launchd/com.brain.brief.plist.j2 +45 -0
- brain/templates/launchd/com.brain.build.plist.j2 +46 -0
- brain/templates/launchd/com.brain.watcher.plist.j2 +46 -0
- brain/templates/skill/SKILL.md +63 -0
- brain/templates/skill/__init__.py +1 -0
- brain/timeline.py +834 -0
- brain/todo.py +124 -0
- brain/uninstall.py +185 -0
- brain/vault/__init__.py +115 -0
- brain/vault/_atomic.py +25 -0
- brain/vault/daily_index.py +228 -0
- brain/vault/derived_links/__init__.py +50 -0
- brain/vault/derived_links/directory.py +683 -0
- brain/vault/derived_links/fence.py +408 -0
- brain/vault/derived_links/gws.py +64 -0
- brain/vault/derived_links/participants.py +143 -0
- brain/vault/derived_links/pass_runner.py +362 -0
- brain/vault/derived_links/rules.py +137 -0
- brain/vault/export.py +683 -0
- brain/vault/frontmatter.py +165 -0
- brain/vault/graph.py +620 -0
- brain/vault/graph_format.py +388 -0
- brain/vault/link_rewrite.py +235 -0
- brain/vault/links.py +260 -0
- brain/vault/note_builder.py +211 -0
- brain/vault/paths.py +55 -0
- brain/vault/quartz_overlay.py +236 -0
- brain/vault/rename.py +591 -0
- brain/vault/resolver.py +304 -0
- brain/vault/slug.py +127 -0
- brain/vault/sync.py +1513 -0
- brain/vault/sync_summaries.py +264 -0
- brain/vault/templates.py +145 -0
- brain/vault/watch.py +1052 -0
- brain/wiki/__init__.py +6 -0
- brain/wiki/_github_slugger.py +76 -0
- brain/wiki/_person_name.py +314 -0
- brain/wiki/build_homepage.py +541 -0
- brain/wiki/build_partial.py +273 -0
- brain/wiki/build_people.py +934 -0
- brain/wiki/build_related.py +758 -0
- brain/wiki/build_swap.py +585 -0
- brain/wiki/build_watcher.py +975 -0
- brain/wiki/edit_classifier.py +215 -0
- brain/wiki/errors.py +10 -0
- brain/wiki/fastpath_manifest.py +475 -0
- brain/wiki/fastpath_state.py +174 -0
- brain/wiki/install.py +296 -0
- brain/wiki/slug.py +111 -0
- secondbrain_py-0.2.1.dist-info/METADATA +195 -0
- secondbrain_py-0.2.1.dist-info/RECORD +273 -0
- secondbrain_py-0.2.1.dist-info/WHEEL +5 -0
- secondbrain_py-0.2.1.dist-info/entry_points.txt +11 -0
- secondbrain_py-0.2.1.dist-info/licenses/LICENSE +21 -0
- secondbrain_py-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
// Custom Quartz v4 Graph component for brain vaults.
|
|
2
|
+
//
|
|
3
|
+
// This file is a TEMPLATE. It is installed at
|
|
4
|
+
// `<vault>/.quartz/quartz/components/Graph.tsx` by `brain vault
|
|
5
|
+
// render --overlay`, FULL-REPLACING upstream's stock Graph.tsx. It
|
|
6
|
+
// does NOT compile or run from the brain repo itself; the imports
|
|
7
|
+
// below resolve against the dependencies Quartz pulls into the
|
|
8
|
+
// cloned workspace via `npm install`, not against any package
|
|
9
|
+
// brain ships.
|
|
10
|
+
//
|
|
11
|
+
// Tested against Quartz v4.5.x (April 2026). When upstream churns,
|
|
12
|
+
// pull the latest Graph.tsx from
|
|
13
|
+
// https://github.com/jackyzha0/quartz/blob/v4/quartz/components/Graph.tsx
|
|
14
|
+
// and re-apply the brain tweaks below — `// brain:` for
|
|
15
|
+
// value/structural choices on upstream-supported fields, and
|
|
16
|
+
// `// brain-extension:` for keys/types that don't exist in stock
|
|
17
|
+
// Quartz. To enumerate every delta:
|
|
18
|
+
//
|
|
19
|
+
// grep -n "brain:" Graph.tsx
|
|
20
|
+
// grep -n "brain-extension:" Graph.tsx
|
|
21
|
+
//
|
|
22
|
+
// Strategy — full replacement (Option A): the brain modifications
|
|
23
|
+
// touch the renderer (color resolution, edge styling, recency
|
|
24
|
+
// sizing, filter pass) deeply enough that wrapping the upstream
|
|
25
|
+
// component is awkward. We vendor it verbatim and inline the
|
|
26
|
+
// brain deltas with `// brain:` comments so a future
|
|
27
|
+
// `diff -u <upstream> <ours>` is a usable upgrade tool.
|
|
28
|
+
//
|
|
29
|
+
// Responsibility (CLAUDE.md rule 8): this file owns the Preact
|
|
30
|
+
// component wrapper and the public `D3Config` type. Rendering
|
|
31
|
+
// logic lives in `scripts/graph.inline.ts`; the brain palette
|
|
32
|
+
// lives in `../styles/graph.scss`. Don't let them spill into each
|
|
33
|
+
// other.
|
|
34
|
+
|
|
35
|
+
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
36
|
+
// @ts-ignore — esbuild-loader resolves .scss/.ts to bundled strings; TS doesn't know.
|
|
37
|
+
import script from "./scripts/graph.inline"
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
import style from "./styles/graph.scss"
|
|
40
|
+
// brain: import the brain palette as a sibling stylesheet so it lands in the same
|
|
41
|
+
// component-css bundle as upstream's graph styles. Path resolves to
|
|
42
|
+
// `quartz/styles/graph.scss` after the overlay copy.
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
import brainStyle from "../styles/graph.scss"
|
|
45
|
+
import { i18n } from "../i18n"
|
|
46
|
+
import { classNames } from "../util/lang"
|
|
47
|
+
|
|
48
|
+
// brain-extension: dashed/translucent stroke style applied to derived edges (Phase D
|
|
49
|
+
// `_ingested/` fence output). Width / dash pattern / alpha all tunable from
|
|
50
|
+
// `quartz.layout.ts` so visuals can be dialed in without code edits.
|
|
51
|
+
export interface DerivedEdgeStyle {
|
|
52
|
+
dash: [number, number]
|
|
53
|
+
width: number
|
|
54
|
+
alpha: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// brain: defining D3Config locally (rather than declaration-merging upstream) keeps
|
|
58
|
+
// the brain-extension surface in one file. `scripts/graph.inline.ts` imports this
|
|
59
|
+
// exported type to read the renderer config off the `data-cfg` attribute.
|
|
60
|
+
export interface D3Config {
|
|
61
|
+
drag: boolean
|
|
62
|
+
zoom: boolean
|
|
63
|
+
depth: number
|
|
64
|
+
scale: number
|
|
65
|
+
repelForce: number
|
|
66
|
+
centerForce: number
|
|
67
|
+
linkDistance: number
|
|
68
|
+
fontSize: number
|
|
69
|
+
opacityScale: number
|
|
70
|
+
removeTags: string[]
|
|
71
|
+
showTags: boolean
|
|
72
|
+
focusOnHover?: boolean
|
|
73
|
+
enableRadial?: boolean
|
|
74
|
+
// brain-extension: per-tier color overrides. Maps a doc's frontmatter `tier` value
|
|
75
|
+
// to a CSS variable name (defined in `quartz/styles/graph.scss`). Falls through to
|
|
76
|
+
// `sourceColors` when a tier isn't matched, then to `--gray`.
|
|
77
|
+
tierColors?: Record<string, string>
|
|
78
|
+
// brain-extension: per-source color overrides, keyed by frontmatter `source`. Used
|
|
79
|
+
// as fallback when `tierColors` doesn't match.
|
|
80
|
+
sourceColors?: Record<string, string>
|
|
81
|
+
// brain-extension: drop nodes with degree 0 from the simulation. Stricter than
|
|
82
|
+
// upstream's "render isolated nodes anyway" default.
|
|
83
|
+
hideOrphans?: boolean
|
|
84
|
+
// brain-extension: drop `tags/<tag>` nodes from the simulation. Stricter than
|
|
85
|
+
// upstream's `showTags: false`, which only de-renders tag nodes (they still pull
|
|
86
|
+
// every tagged doc into the neighbourhood for graph-traversal).
|
|
87
|
+
hideTagNodes?: boolean
|
|
88
|
+
// brain-extension: list of frontmatter keys; nodes whose contentIndex entry has
|
|
89
|
+
// any matching truthy key are dropped. With the current contentIndex emitter
|
|
90
|
+
// only `tier` / `source` are surfaced; matching against other keys (e.g.
|
|
91
|
+
// `index`, `moc`) requires the emitter to widen its frontmatter passthrough.
|
|
92
|
+
hideByFrontmatter?: string[]
|
|
93
|
+
// brain-extension: dashed/translucent style for derived edges (Phase D fence
|
|
94
|
+
// output). Renderer ignores when absent — wiki edges still get upstream's solid
|
|
95
|
+
// stroke either way.
|
|
96
|
+
derivedEdgeStyle?: DerivedEdgeStyle
|
|
97
|
+
// brain-extension: scale node radius by recency of last edit so freshly-touched
|
|
98
|
+
// notes pop visually. Decay window pinned to one year in the renderer; final
|
|
99
|
+
// radius clamped to [1, 4].
|
|
100
|
+
recencySizing?: boolean
|
|
101
|
+
// brain-extension: render an in-graph search input. Type-only here; the runtime
|
|
102
|
+
// wiring (input element, debounced filter, SPA-nav-on-enter) lands with the
|
|
103
|
+
// upcoming search-and-filter customization.
|
|
104
|
+
searchEnabled?: boolean
|
|
105
|
+
// brain-extension: render filter chips for tier and/or source. Type-only here;
|
|
106
|
+
// the runtime wiring lands with the upcoming search-and-filter customization.
|
|
107
|
+
filterChips?: Array<"tier" | "source">
|
|
108
|
+
// brain-extension: scalar multiplier applied to every node's computed
|
|
109
|
+
// radius (and to the clamp bounds). 1 = sidebar default; >1 = bigger
|
|
110
|
+
// dots (typically wanted in the fullscreen modal where the canvas can
|
|
111
|
+
// absorb the larger marks). Renderer treats absent / non-finite values
|
|
112
|
+
// as 1.
|
|
113
|
+
nodeRadiusMultiplier?: number
|
|
114
|
+
// brain-extension: max character length for node labels. Anything
|
|
115
|
+
// longer is truncated to `labelMaxLength - 1` chars + `…`, with the
|
|
116
|
+
// full title restored on pointerover. Defaults to 10 (the dense
|
|
117
|
+
// fullscreen modal default); the small sidebar panel sets 25 so its
|
|
118
|
+
// few visible labels stay readable without a hover.
|
|
119
|
+
labelMaxLength?: number
|
|
120
|
+
// brain-extension: exponent applied to `numLinks` in the node-radius
|
|
121
|
+
// formula. The base radius is `2 + Math.pow(numLinks, exponent)`. The
|
|
122
|
+
// default (0.5 = sqrt) compresses hub-vs-leaf differences; bumping
|
|
123
|
+
// toward 0.8–1.0 produces the dramatic Obsidian-style hierarchy where
|
|
124
|
+
// a 50-link hub is visibly ~6× the radius of a 1-link leaf. Renderer
|
|
125
|
+
// treats absent / non-finite values as 0.5.
|
|
126
|
+
nodeRadiusGrowthExponent?: number
|
|
127
|
+
// brain-extension: base alpha for wiki (non-derived) edges. Defaults
|
|
128
|
+
// to 1 (fully opaque). Lower values fade edges so the graph reads as
|
|
129
|
+
// "labels first, structure second" — useful in dense fullscreen views
|
|
130
|
+
// where edge density would otherwise overpower the nodes.
|
|
131
|
+
wikiEdgeBaseAlpha?: number
|
|
132
|
+
// brain-extension: minimum incident-link count to treat a node as a
|
|
133
|
+
// hub and bypass label truncation. Defaults to 6 (sidebar / local-
|
|
134
|
+
// fullscreen sweet spot). Set higher (~25–30) on the global graph
|
|
135
|
+
// since at depth=-1 hundreds of central docs would otherwise wall-
|
|
136
|
+
// of-text. Renderer treats absent / non-finite values as 6.
|
|
137
|
+
hubLabelThreshold?: number
|
|
138
|
+
// brain-extension: upper clamp on the per-node radius before the
|
|
139
|
+
// `nodeRadiusMultiplier` is applied. Defaults to 10 (Quartz's
|
|
140
|
+
// historical ceiling). Lift on dense modal views so hubs can
|
|
141
|
+
// visually dominate — clamps as `nodeRadiusCeiling * radiusMul`,
|
|
142
|
+
// so 25 with radiusMul=2.5 → effective max radius 62.5px.
|
|
143
|
+
nodeRadiusCeiling?: number
|
|
144
|
+
// brain-extension: fontSize multiplier applied to label text for
|
|
145
|
+
// nodes whose link count meets `hubLabelThreshold`. Default 1
|
|
146
|
+
// (no boost). 1.5–2.0 makes hub labels visually pop without
|
|
147
|
+
// affecting leaf/intermediate labels.
|
|
148
|
+
hubLabelFontMultiplier?: number
|
|
149
|
+
// brain-extension: render the diagnostic workbench shell around this graph.
|
|
150
|
+
// The workbench keeps the graph canvas but adds a mode rail and inspector
|
|
151
|
+
// for incoming/outgoing/derived/issue signals.
|
|
152
|
+
diagnosticWorkbench?: boolean
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface GraphOptions {
|
|
156
|
+
localGraph: Partial<D3Config> | undefined
|
|
157
|
+
globalGraph: Partial<D3Config> | undefined
|
|
158
|
+
workbenchGraph: Partial<D3Config> | undefined
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// brain-extension: shared default mapping for tier / source palettes. Pulled into a
|
|
162
|
+
// const so both `localGraph` and `globalGraph` defaults reference the same source of
|
|
163
|
+
// truth — renaming a CSS variable in `graph.scss` only requires the change here, not
|
|
164
|
+
// in two places.
|
|
165
|
+
const defaultTierColors: Record<string, string> = {
|
|
166
|
+
vault: "--brain-tier-vault",
|
|
167
|
+
ingested: "--brain-tier-ingested",
|
|
168
|
+
}
|
|
169
|
+
const defaultSourceColors: Record<string, string> = {
|
|
170
|
+
krisp: "--brain-source-krisp",
|
|
171
|
+
slack: "--brain-source-slack",
|
|
172
|
+
gmail: "--brain-source-gmail",
|
|
173
|
+
manual: "--brain-source-manual",
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const defaultOptions: GraphOptions = {
|
|
177
|
+
localGraph: {
|
|
178
|
+
drag: true,
|
|
179
|
+
zoom: true,
|
|
180
|
+
depth: 1,
|
|
181
|
+
scale: 1.1,
|
|
182
|
+
repelForce: 0.5,
|
|
183
|
+
centerForce: 0.3,
|
|
184
|
+
linkDistance: 30,
|
|
185
|
+
fontSize: 0.6,
|
|
186
|
+
opacityScale: 1,
|
|
187
|
+
showTags: true,
|
|
188
|
+
removeTags: [],
|
|
189
|
+
focusOnHover: false,
|
|
190
|
+
enableRadial: false,
|
|
191
|
+
// brain-extension: defaults so a layout that omits these still gets brain
|
|
192
|
+
// coloring out of the box. Layout-supplied values shallow-merge over these.
|
|
193
|
+
tierColors: defaultTierColors,
|
|
194
|
+
sourceColors: defaultSourceColors,
|
|
195
|
+
},
|
|
196
|
+
globalGraph: {
|
|
197
|
+
drag: true,
|
|
198
|
+
zoom: true,
|
|
199
|
+
depth: -1,
|
|
200
|
+
scale: 0.9,
|
|
201
|
+
repelForce: 0.5,
|
|
202
|
+
centerForce: 0.2,
|
|
203
|
+
linkDistance: 30,
|
|
204
|
+
fontSize: 0.6,
|
|
205
|
+
opacityScale: 1,
|
|
206
|
+
showTags: true,
|
|
207
|
+
removeTags: [],
|
|
208
|
+
focusOnHover: true,
|
|
209
|
+
enableRadial: true,
|
|
210
|
+
// brain-extension: same defaults as the local graph; layout config in
|
|
211
|
+
// `quartz.layout.ts` can override either tier or source per-graph.
|
|
212
|
+
tierColors: defaultTierColors,
|
|
213
|
+
sourceColors: defaultSourceColors,
|
|
214
|
+
},
|
|
215
|
+
workbenchGraph: {
|
|
216
|
+
drag: true,
|
|
217
|
+
zoom: true,
|
|
218
|
+
depth: -1,
|
|
219
|
+
scale: 0.9,
|
|
220
|
+
repelForce: 0.5,
|
|
221
|
+
centerForce: 0.2,
|
|
222
|
+
linkDistance: 30,
|
|
223
|
+
fontSize: 0.6,
|
|
224
|
+
opacityScale: 1,
|
|
225
|
+
showTags: true,
|
|
226
|
+
removeTags: [],
|
|
227
|
+
focusOnHover: true,
|
|
228
|
+
enableRadial: true,
|
|
229
|
+
tierColors: defaultTierColors,
|
|
230
|
+
sourceColors: defaultSourceColors,
|
|
231
|
+
diagnosticWorkbench: true,
|
|
232
|
+
},
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export default ((opts?: Partial<GraphOptions>) => {
|
|
236
|
+
const Graph: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
|
237
|
+
// brain: defensive guard for catastrophic component-instantiation failure (cfg
|
|
238
|
+
// missing or non-object). The brain-extension keys are all optional, so missing
|
|
239
|
+
// or wrong-shape values fall through to gray-default in the renderer; this
|
|
240
|
+
// throw is for the case where the overlay was misapplied and the component is
|
|
241
|
+
// being constructed without Quartz's standard cfg context at all.
|
|
242
|
+
if (!cfg || typeof cfg !== "object") {
|
|
243
|
+
throw new Error(
|
|
244
|
+
"brain Graph component instantiated without cfg — was the overlay applied to the right Quartz workspace?",
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
|
|
248
|
+
const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
|
|
249
|
+
const workbenchGraph = {
|
|
250
|
+
...defaultOptions.workbenchGraph,
|
|
251
|
+
...opts?.workbenchGraph,
|
|
252
|
+
}
|
|
253
|
+
return (
|
|
254
|
+
<div class={classNames(displayClass, "graph")}>
|
|
255
|
+
<h3>{i18n(cfg.locale).components.graph.title}</h3>
|
|
256
|
+
<div class="graph-outer">
|
|
257
|
+
<div class="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
|
|
258
|
+
{/* brain-extension: third affordance that opens THIS page's local
|
|
259
|
+
graph (depth=1 — only the page + its connected nodes) in the
|
|
260
|
+
same fullscreen modal the global icons use. Search + tier/source
|
|
261
|
+
chips are forced on so the focused view stays explorable. Click
|
|
262
|
+
handler in graph.inline.ts. */}
|
|
263
|
+
<button
|
|
264
|
+
class="local-graph-fullscreen-icon"
|
|
265
|
+
aria-label="Local Graph (fullscreen)"
|
|
266
|
+
>
|
|
267
|
+
<svg
|
|
268
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
269
|
+
viewBox="0 0 24 24"
|
|
270
|
+
fill="none"
|
|
271
|
+
stroke="currentColor"
|
|
272
|
+
stroke-width="2"
|
|
273
|
+
stroke-linecap="round"
|
|
274
|
+
stroke-linejoin="round"
|
|
275
|
+
>
|
|
276
|
+
<polyline points="4 9 4 4 9 4" />
|
|
277
|
+
<polyline points="20 9 20 4 15 4" />
|
|
278
|
+
<polyline points="4 15 4 20 9 20" />
|
|
279
|
+
<polyline points="20 15 20 20 15 20" />
|
|
280
|
+
</svg>
|
|
281
|
+
</button>
|
|
282
|
+
{/* brain-extension: second affordance that opens the global graph in
|
|
283
|
+
"stock mode" — every brain renderer extension (tier/source colors,
|
|
284
|
+
derived-edge styling, recency sizing, search input, filter chips,
|
|
285
|
+
orphan/tag-node hiding) is suppressed so the user sees the corpus
|
|
286
|
+
with stock-Quartz visual semantics. Renders to the left of the
|
|
287
|
+
brain-customized globe icon; click handler in graph.inline.ts. */}
|
|
288
|
+
<button class="brain-stock-graph-icon" aria-label="Global Graph (stock view)">
|
|
289
|
+
<svg
|
|
290
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
291
|
+
viewBox="0 0 24 24"
|
|
292
|
+
fill="currentColor"
|
|
293
|
+
xmlSpace="preserve"
|
|
294
|
+
>
|
|
295
|
+
<circle cx="5" cy="5" r="1.6" />
|
|
296
|
+
<circle cx="12" cy="5" r="1.6" />
|
|
297
|
+
<circle cx="19" cy="5" r="1.6" />
|
|
298
|
+
<circle cx="5" cy="12" r="1.6" />
|
|
299
|
+
<circle cx="12" cy="12" r="1.6" />
|
|
300
|
+
<circle cx="19" cy="12" r="1.6" />
|
|
301
|
+
<circle cx="5" cy="19" r="1.6" />
|
|
302
|
+
<circle cx="12" cy="19" r="1.6" />
|
|
303
|
+
<circle cx="19" cy="19" r="1.6" />
|
|
304
|
+
</svg>
|
|
305
|
+
</button>
|
|
306
|
+
{/* brain-extension: separate diagnostic workbench affordance. It opens
|
|
307
|
+
its own fullscreen modal and does not change the existing global /
|
|
308
|
+
stock / local-fullscreen buttons. */}
|
|
309
|
+
<button class="brain-graph-workbench-icon" aria-label="Graph Diagnostic Workbench">
|
|
310
|
+
<svg
|
|
311
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
312
|
+
viewBox="0 0 24 24"
|
|
313
|
+
fill="none"
|
|
314
|
+
stroke="currentColor"
|
|
315
|
+
stroke-width="2"
|
|
316
|
+
stroke-linecap="round"
|
|
317
|
+
stroke-linejoin="round"
|
|
318
|
+
>
|
|
319
|
+
<circle cx="8" cy="8" r="3" />
|
|
320
|
+
<circle cx="16" cy="16" r="3" />
|
|
321
|
+
<path d="M10.2 10.2 13.8 13.8" />
|
|
322
|
+
<path d="M15.5 6.5 18 4" />
|
|
323
|
+
<path d="M6 18l2.5-2.5" />
|
|
324
|
+
</svg>
|
|
325
|
+
</button>
|
|
326
|
+
<button class="global-graph-icon" aria-label="Global Graph">
|
|
327
|
+
<svg
|
|
328
|
+
version="1.1"
|
|
329
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
330
|
+
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
331
|
+
x="0px"
|
|
332
|
+
y="0px"
|
|
333
|
+
viewBox="0 0 55 55"
|
|
334
|
+
fill="currentColor"
|
|
335
|
+
xmlSpace="preserve"
|
|
336
|
+
>
|
|
337
|
+
<path
|
|
338
|
+
d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
|
|
339
|
+
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
|
|
340
|
+
c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
|
|
341
|
+
C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
|
|
342
|
+
c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
|
|
343
|
+
v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
|
|
344
|
+
s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
|
|
345
|
+
C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2
|
|
346
|
+
S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4
|
|
347
|
+
s1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2
|
|
348
|
+
s-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"
|
|
349
|
+
/>
|
|
350
|
+
</svg>
|
|
351
|
+
</button>
|
|
352
|
+
</div>
|
|
353
|
+
<div class="global-graph-outer">
|
|
354
|
+
<div class="global-graph-container" data-cfg={JSON.stringify(globalGraph)}></div>
|
|
355
|
+
</div>
|
|
356
|
+
<div class="brain-graph-workbench-outer">
|
|
357
|
+
<div
|
|
358
|
+
class="brain-graph-workbench-container"
|
|
359
|
+
data-cfg={JSON.stringify({
|
|
360
|
+
...globalGraph,
|
|
361
|
+
...workbenchGraph,
|
|
362
|
+
diagnosticWorkbench: true,
|
|
363
|
+
filterChips: ["tier", "source"],
|
|
364
|
+
searchEnabled: true,
|
|
365
|
+
hideOrphans: false,
|
|
366
|
+
focusOnHover: true,
|
|
367
|
+
nodeRadiusMultiplier: 2,
|
|
368
|
+
nodeRadiusGrowthExponent: 0.85,
|
|
369
|
+
wikiEdgeBaseAlpha: 0.25,
|
|
370
|
+
opacityScale: 6,
|
|
371
|
+
fontSize: 1.0,
|
|
372
|
+
hubLabelThreshold: 8,
|
|
373
|
+
labelMaxLength: 24,
|
|
374
|
+
nodeRadiusCeiling: 30,
|
|
375
|
+
hubLabelFontMultiplier: 1.8,
|
|
376
|
+
})}
|
|
377
|
+
></div>
|
|
378
|
+
</div>
|
|
379
|
+
{/* brain-extension: separate modal container for the fullscreen LOCAL
|
|
380
|
+
graph (depth=1 — current page + its connected nodes). Reuses the
|
|
381
|
+
same outer/active CSS as `.global-graph-outer` for the scrim +
|
|
382
|
+
centered panel; data-cfg is a clone of the inline localGraph
|
|
383
|
+
config with `filterChips` forced on so the focused view stays
|
|
384
|
+
explorable. Click handler in graph.inline.ts. */}
|
|
385
|
+
<div class="local-graph-outer">
|
|
386
|
+
<div
|
|
387
|
+
class="local-graph-container"
|
|
388
|
+
data-cfg={JSON.stringify({
|
|
389
|
+
...localGraph,
|
|
390
|
+
filterChips: ["tier", "source"],
|
|
391
|
+
// brain-extension: Obsidian-grade spread — first-pass
|
|
392
|
+
// calibration after side-by-side comparison with the
|
|
393
|
+
// Obsidian docs vault graph. Repel ~6× sidebar default,
|
|
394
|
+
// linkDistance ~8× sidebar default, centerForce nearly
|
|
395
|
+
// off so the layout breathes outward instead of tugging
|
|
396
|
+
// back to the middle.
|
|
397
|
+
repelForce: 3.0,
|
|
398
|
+
linkDistance: 240,
|
|
399
|
+
centerForce: 0.05,
|
|
400
|
+
// brain-extension: 2.5× node radius (was 2×) so even after
|
|
401
|
+
// the wider spread the dots don't read as tiny specks.
|
|
402
|
+
nodeRadiusMultiplier: 2.5,
|
|
403
|
+
// brain-extension: steeper degree → radius curve. Default
|
|
404
|
+
// 0.5 (sqrt) capped hub-vs-leaf ratio at ~3×; bumping to
|
|
405
|
+
// 0.85 gives ~6–8× ratio so hubs visibly dominate the
|
|
406
|
+
// layout the way Obsidian's "Settings" / "Sync settings"
|
|
407
|
+
// nodes do.
|
|
408
|
+
nodeRadiusGrowthExponent: 0.85,
|
|
409
|
+
// brain-extension: fade wiki edges to 30% opacity so the
|
|
410
|
+
// graph reads as "labels and nodes first, edges as
|
|
411
|
+
// context." Matches Obsidian's barely-there grey strokes.
|
|
412
|
+
// Hover-highlighted edges still pop to full alpha via the
|
|
413
|
+
// existing `l.active` branch.
|
|
414
|
+
wikiEdgeBaseAlpha: 0.3,
|
|
415
|
+
// brain-extension: counteract the zoom-tied label-opacity
|
|
416
|
+
// formula `(transform.k * opacityScale - 1) / 3.75` —
|
|
417
|
+
// the wide spread above forces fit-zoom k < 1, which
|
|
418
|
+
// otherwise clamps every label's alpha to 0. Bumping
|
|
419
|
+
// opacityScale to 6 keeps labels readable at the
|
|
420
|
+
// initial fit (k≈0.4 → alpha≈0.4) while still letting
|
|
421
|
+
// user-initiated zoom-in further sharpen them.
|
|
422
|
+
opacityScale: 6,
|
|
423
|
+
// brain-extension: lift the per-node radius ceiling so
|
|
424
|
+
// hubs (the page itself + a few sub-hubs at depth=1)
|
|
425
|
+
// can grow past the historical 10×radiusMul cap. With
|
|
426
|
+
// ceiling=25 and radiusMul=2.5, max radius is 62.5px —
|
|
427
|
+
// big enough to dominate the layout the way Obsidian's
|
|
428
|
+
// structural hubs do.
|
|
429
|
+
nodeRadiusCeiling: 25,
|
|
430
|
+
// brain-extension: hub-node labels render at 1.7× the
|
|
431
|
+
// base fontSize so they immediately read as "this is the
|
|
432
|
+
// page / a major sub-hub" without competing with leaf
|
|
433
|
+
// labels.
|
|
434
|
+
hubLabelFontMultiplier: 1.7,
|
|
435
|
+
// brain-extension: hide orphans by default in the
|
|
436
|
+
// fullscreen modal (toggle chip in the controls rail
|
|
437
|
+
// brings them back). At depth=1 the only "orphan" is
|
|
438
|
+
// typically a few stray ingested docs that don't link
|
|
439
|
+
// anywhere — they add noise without contributing to the
|
|
440
|
+
// page-graph story.
|
|
441
|
+
hideOrphans: true,
|
|
442
|
+
// brain-extension: bump label size from the 0.6 default to
|
|
443
|
+
// 1.0 for the fullscreen modal — the canvas has plenty of
|
|
444
|
+
// room and the sidebar's small-text constraint doesn't
|
|
445
|
+
// apply here. Matches the global-graph fontSize override
|
|
446
|
+
// in quartz.layout.ts so the two fullscreen modals read
|
|
447
|
+
// with consistent typography.
|
|
448
|
+
fontSize: 1.0,
|
|
449
|
+
// brain-extension: same 10-char truncation as the global
|
|
450
|
+
// graph — at depth=1 with the wider spread above, full
|
|
451
|
+
// titles still wall-of-text near the central hub. Hover
|
|
452
|
+
// restores the full title.
|
|
453
|
+
labelMaxLength: 10,
|
|
454
|
+
})}
|
|
455
|
+
></div>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// brain: concatenate upstream's component styles with the brain palette so both
|
|
462
|
+
// ship through Quartz's component-css pipeline. Order doesn't matter — they
|
|
463
|
+
// target disjoint selectors (`:root` palette vs `.graph` layout).
|
|
464
|
+
Graph.css = style + "\n" + brainStyle
|
|
465
|
+
Graph.afterDOMLoaded = script
|
|
466
|
+
|
|
467
|
+
return Graph
|
|
468
|
+
}) satisfies QuartzComponentConstructor
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { pathToRoot } from "../util/path"
|
|
2
|
+
import { classNames } from "../util/lang"
|
|
3
|
+
import { i18n } from "../i18n"
|
|
4
|
+
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
5
|
+
|
|
6
|
+
const PageTitle: QuartzComponent = ({ fileData, cfg, displayClass }: QuartzComponentProps) => {
|
|
7
|
+
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
|
|
8
|
+
const baseDir = pathToRoot(fileData.slug!)
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<h2 class={classNames(displayClass, "page-title")}>
|
|
12
|
+
<a href={baseDir}>
|
|
13
|
+
<img
|
|
14
|
+
class="brain-page-logo brain-page-logo-light"
|
|
15
|
+
src="/static/brain-logo-light.png"
|
|
16
|
+
alt=""
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
width="48"
|
|
19
|
+
height="48"
|
|
20
|
+
style="width: 3rem; height: 3rem; display: block; flex: 0 0 auto;"
|
|
21
|
+
/>
|
|
22
|
+
<img
|
|
23
|
+
class="brain-page-logo brain-page-logo-dark"
|
|
24
|
+
src="/static/brain-logo-dark.png"
|
|
25
|
+
alt=""
|
|
26
|
+
aria-hidden="true"
|
|
27
|
+
width="48"
|
|
28
|
+
height="48"
|
|
29
|
+
style="width: 3rem; height: 3rem; display: block; flex: 0 0 auto;"
|
|
30
|
+
/>
|
|
31
|
+
<span>{title}</span>
|
|
32
|
+
</a>
|
|
33
|
+
</h2>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
PageTitle.css = `
|
|
38
|
+
.page-title {
|
|
39
|
+
font-size: 1.75rem;
|
|
40
|
+
margin: 0;
|
|
41
|
+
font-family: var(--titleFont);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.page-title > a {
|
|
45
|
+
align-items: center;
|
|
46
|
+
color: inherit;
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
gap: 0.55rem;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.brain-page-logo {
|
|
53
|
+
display: block;
|
|
54
|
+
flex: 0 0 auto;
|
|
55
|
+
height: 3rem;
|
|
56
|
+
width: 3rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.brain-page-logo-dark {
|
|
60
|
+
display: none !important;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
:root[saved-theme="dark"] .brain-page-logo-light {
|
|
64
|
+
display: none !important;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
:root[saved-theme="dark"] .brain-page-logo-dark {
|
|
68
|
+
display: block !important;
|
|
69
|
+
}
|
|
70
|
+
`
|
|
71
|
+
|
|
72
|
+
export default (() => PageTitle) satisfies QuartzComponentConstructor
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Brain related-docs sidebar component — Phase 5.2 of the Wiki UX Overhaul.
|
|
2
|
+
//
|
|
3
|
+
// This file is a TEMPLATE. It is installed at
|
|
4
|
+
// `<vault>/.quartz/quartz/components/RelatedDocs.tsx` by `brain vault
|
|
5
|
+
// render --overlay`. It does NOT hydrate from the brain repo itself;
|
|
6
|
+
// dynamic behavior lives in `scripts/relatedDocs.inline.ts` and is
|
|
7
|
+
// attached through `RelatedDocs.afterDOMLoaded`.
|
|
8
|
+
//
|
|
9
|
+
// The backend precomputes `/static/related/<slug>.json` during the
|
|
10
|
+
// atomic wiki build. This component renders the stable sidebar hooks;
|
|
11
|
+
// the inline script lazy-fetches the JSON for the current page and
|
|
12
|
+
// fills the list if related notes exist.
|
|
13
|
+
|
|
14
|
+
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
15
|
+
// @ts-ignore — esbuild-loader rewrites this to a bundled string.
|
|
16
|
+
import script from "./scripts/relatedDocs.inline"
|
|
17
|
+
import { classNames } from "../util/lang"
|
|
18
|
+
|
|
19
|
+
const RelatedDocs: QuartzComponent = ({ displayClass, fileData }: QuartzComponentProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<section
|
|
22
|
+
class={classNames(displayClass, "brain-related-docs")}
|
|
23
|
+
data-brain-related-slug={fileData.slug}
|
|
24
|
+
aria-label="Related notes"
|
|
25
|
+
hidden
|
|
26
|
+
>
|
|
27
|
+
<h3>Related</h3>
|
|
28
|
+
<ol class="brain-related-docs-list"></ol>
|
|
29
|
+
<p class="brain-related-docs-empty" hidden>
|
|
30
|
+
No related notes.
|
|
31
|
+
</p>
|
|
32
|
+
</section>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
RelatedDocs.afterDOMLoaded = script
|
|
37
|
+
|
|
38
|
+
export default (() => RelatedDocs) satisfies QuartzComponentConstructor
|