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.
Files changed (273) hide show
  1. brain/__init__.py +0 -0
  2. brain/__main__.py +18 -0
  3. brain/_capture_command.py +445 -0
  4. brain/_compose.py +52 -0
  5. brain/activity.py +206 -0
  6. brain/ask.py +631 -0
  7. brain/audio.py +591 -0
  8. brain/backfill/__init__.py +12 -0
  9. brain/backfill/search_extras.py +141 -0
  10. brain/backfill/source_rows.py +101 -0
  11. brain/bin/__init__.py +1 -0
  12. brain/bin/_launcher.py +107 -0
  13. brain/bin/down.py +8 -0
  14. brain/bin/launchd.py +268 -0
  15. brain/bin/monitor.py +570 -0
  16. brain/bin/rebuild.py +8 -0
  17. brain/bin/status.py +8 -0
  18. brain/bin/up.py +8 -0
  19. brain/brief.py +272 -0
  20. brain/capture.py +49 -0
  21. brain/chat.py +293 -0
  22. brain/cli.py +9760 -0
  23. brain/cli_claude.py +81 -0
  24. brain/cli_connect.py +285 -0
  25. brain/cli_demo.py +266 -0
  26. brain/config.py +1949 -0
  27. brain/connect.py +925 -0
  28. brain/db.py +540 -0
  29. brain/demo/__init__.py +452 -0
  30. brain/demo/corpus/manifest.json +403 -0
  31. brain/demo/embedder.py +74 -0
  32. brain/durations.py +84 -0
  33. brain/edit_session.py +156 -0
  34. brain/editor.py +67 -0
  35. brain/elicit/__init__.py +16 -0
  36. brain/elicit/detectors.py +250 -0
  37. brain/elicit/drafter.py +70 -0
  38. brain/elicit/queue.py +220 -0
  39. brain/elicit/schema.py +48 -0
  40. brain/elicit/session.py +445 -0
  41. brain/embedding_targets.py +54 -0
  42. brain/embeddings.py +424 -0
  43. brain/enrichment.py +808 -0
  44. brain/errors.py +357 -0
  45. brain/eval/__init__.py +129 -0
  46. brain/eval/answer_eval.py +281 -0
  47. brain/eval/baseline.py +265 -0
  48. brain/eval/concept_extraction.py +378 -0
  49. brain/eval/corpus.py +152 -0
  50. brain/eval/errors.py +19 -0
  51. brain/eval/graph_baseline.py +226 -0
  52. brain/eval/graph_retrieval.py +202 -0
  53. brain/eval/graph_runner.py +319 -0
  54. brain/eval/metrics.py +101 -0
  55. brain/eval/runner.py +223 -0
  56. brain/format.py +783 -0
  57. brain/gaps.py +390 -0
  58. brain/graph_rag/__init__.py +94 -0
  59. brain/graph_rag/_retrieval_common.py +113 -0
  60. brain/graph_rag/aggregates.py +303 -0
  61. brain/graph_rag/aliases/__init__.py +583 -0
  62. brain/graph_rag/backends/__init__.py +10 -0
  63. brain/graph_rag/backends/_age_helpers.py +473 -0
  64. brain/graph_rag/backends/age.py +782 -0
  65. brain/graph_rag/backends/base.py +272 -0
  66. brain/graph_rag/build.py +344 -0
  67. brain/graph_rag/communities.py +644 -0
  68. brain/graph_rag/communities_summary.py +437 -0
  69. brain/graph_rag/concepts.py +202 -0
  70. brain/graph_rag/cooccur.py +193 -0
  71. brain/graph_rag/cross_type.py +312 -0
  72. brain/graph_rag/extract.py +885 -0
  73. brain/graph_rag/fuse.py +371 -0
  74. brain/graph_rag/global_.py +412 -0
  75. brain/graph_rag/grouping.py +372 -0
  76. brain/graph_rag/person_resolver.py +167 -0
  77. brain/graph_rag/reconcile.py +792 -0
  78. brain/graph_rag/relational.py +353 -0
  79. brain/graph_rag/retrieve.py +526 -0
  80. brain/graph_rag/router.py +288 -0
  81. brain/graph_rag/schema.py +320 -0
  82. brain/graph_rag/sync.py +237 -0
  83. brain/graph_rag/tenancy.py +43 -0
  84. brain/graph_rag/themes.py +501 -0
  85. brain/graph_rag/weighting.py +202 -0
  86. brain/ingest/__init__.py +1926 -0
  87. brain/ingest/chunker.py +249 -0
  88. brain/ingest/docx.py +40 -0
  89. brain/ingest/gmail.py +621 -0
  90. brain/ingest/markdown.py +37 -0
  91. brain/ingest/pdf.py +61 -0
  92. brain/ingest/stdin.py +22 -0
  93. brain/ingest/sub_tokens.py +91 -0
  94. brain/ingest/text.py +16 -0
  95. brain/interactions.py +205 -0
  96. brain/maintenance.py +355 -0
  97. brain/mcp_server.py +3405 -0
  98. brain/migrations/001_init.sql +43 -0
  99. brain/migrations/002_qwen3_embedding.sql +17 -0
  100. brain/migrations/003_vault_model.sql +41 -0
  101. brain/migrations/004_relax_content_hash_uniqueness.sql +18 -0
  102. brain/migrations/005_derived_links.sql +67 -0
  103. brain/migrations/006_dedup_file_by_source_path.sql +25 -0
  104. brain/migrations/007_email_thread_and_draft.sql +15 -0
  105. brain/migrations/008_gmail_thread_unique.sql +11 -0
  106. brain/migrations/009_chunks_weighted_tsv.sql +28 -0
  107. brain/migrations/010_interactions.sql +30 -0
  108. brain/migrations/011_documents_summary.sql +23 -0
  109. brain/migrations/012_graphrag.sql +171 -0
  110. brain/migrations/013_graphrag_communities.sql +125 -0
  111. brain/migrations/014_graphrag_community_summary_hash.sql +33 -0
  112. brain/migrations/015_interactions_graph_targets.sql +89 -0
  113. brain/migrations/016_index_hygiene.sql +61 -0
  114. brain/migrations/017_elicit.sql +30 -0
  115. brain/migrations/018_review_gap_signal_kinds.sql +40 -0
  116. brain/migrations/019_search_queries.sql +35 -0
  117. brain/migrations/020_link_suggestions.sql +40 -0
  118. brain/migrations/021_timeline_doc_date.sql +34 -0
  119. brain/migrations/022_link_suggestions_undirected.sql +84 -0
  120. brain/migrations/023_search_queries_fts_count.sql +28 -0
  121. brain/quartz_overrides/__init__.py +8 -0
  122. brain/quartz_overrides/quartz/bootstrap-cli.mjs +65 -0
  123. brain/quartz_overrides/quartz/build.ts +568 -0
  124. brain/quartz_overrides/quartz/cli/args.js +152 -0
  125. brain/quartz_overrides/quartz/cli/build_partial_handler.js +544 -0
  126. brain/quartz_overrides/quartz/cli/handlers.js +636 -0
  127. brain/quartz_overrides/quartz/components/CommandPalette.tsx +172 -0
  128. brain/quartz_overrides/quartz/components/Explorer.tsx +198 -0
  129. brain/quartz_overrides/quartz/components/Footer.tsx +27 -0
  130. brain/quartz_overrides/quartz/components/Graph.tsx +468 -0
  131. brain/quartz_overrides/quartz/components/PageTitle.tsx +72 -0
  132. brain/quartz_overrides/quartz/components/RelatedDocs.tsx +38 -0
  133. brain/quartz_overrides/quartz/components/Search.tsx +161 -0
  134. brain/quartz_overrides/quartz/components/SummaryLede.tsx +72 -0
  135. brain/quartz_overrides/quartz/components/index.ts +92 -0
  136. brain/quartz_overrides/quartz/components/pages/TagContent.tsx +272 -0
  137. brain/quartz_overrides/quartz/components/scripts/commandPalette.inline.ts +665 -0
  138. brain/quartz_overrides/quartz/components/scripts/explorer.inline.ts +768 -0
  139. brain/quartz_overrides/quartz/components/scripts/graph.inline.ts +2302 -0
  140. brain/quartz_overrides/quartz/components/scripts/relatedDocs.inline.ts +163 -0
  141. brain/quartz_overrides/quartz/components/scripts/search.inline.ts +1011 -0
  142. brain/quartz_overrides/quartz/plugins/emitters/contentIndex.ts +546 -0
  143. brain/quartz_overrides/quartz/plugins/transformers/codeCopy.ts +94 -0
  144. brain/quartz_overrides/quartz/plugins/transformers/derivedFenceMark.ts +302 -0
  145. brain/quartz_overrides/quartz/plugins/transformers/emailThread.ts +148 -0
  146. brain/quartz_overrides/quartz/plugins/transformers/emptyDoorFilter.ts +213 -0
  147. brain/quartz_overrides/quartz/plugins/transformers/index.ts +114 -0
  148. brain/quartz_overrides/quartz/plugins/transformers/linkKindMark.ts +205 -0
  149. brain/quartz_overrides/quartz/plugins/transformers/linkSourceTag.ts +104 -0
  150. brain/quartz_overrides/quartz/plugins/transformers/relativeDate.ts +100 -0
  151. brain/quartz_overrides/quartz/plugins/transformers/reloadSignal.ts +131 -0
  152. brain/quartz_overrides/quartz/processors/parse.ts +371 -0
  153. brain/quartz_overrides/quartz/processors/parser_cache.ts +78 -0
  154. brain/quartz_overrides/quartz/static/brain-logo-dark.png +0 -0
  155. brain/quartz_overrides/quartz/static/brain-logo-light.png +0 -0
  156. brain/quartz_overrides/quartz/static/codeCopy.js +196 -0
  157. brain/quartz_overrides/quartz/static/emailThread.js +334 -0
  158. brain/quartz_overrides/quartz/static/favicon.ico +0 -0
  159. brain/quartz_overrides/quartz/static/icon.png +0 -0
  160. brain/quartz_overrides/quartz/static/linkSourceTag.js +104 -0
  161. brain/quartz_overrides/quartz/static/relativeDate.js +142 -0
  162. brain/quartz_overrides/quartz/static/reload.js +168 -0
  163. brain/quartz_overrides/quartz/styles/brain/_article.scss +252 -0
  164. brain/quartz_overrides/quartz/styles/brain/_atmosphere.scss +113 -0
  165. brain/quartz_overrides/quartz/styles/brain/_callouts.scss +180 -0
  166. brain/quartz_overrides/quartz/styles/brain/_cmdk.scss +7 -0
  167. brain/quartz_overrides/quartz/styles/brain/_code.scss +208 -0
  168. brain/quartz_overrides/quartz/styles/brain/_command_palette.scss +369 -0
  169. brain/quartz_overrides/quartz/styles/brain/_email_thread.scss +228 -0
  170. brain/quartz_overrides/quartz/styles/brain/_explorer.scss +142 -0
  171. brain/quartz_overrides/quartz/styles/brain/_home.scss +182 -0
  172. brain/quartz_overrides/quartz/styles/brain/_links.scss +322 -0
  173. brain/quartz_overrides/quartz/styles/brain/_marginalia.scss +117 -0
  174. brain/quartz_overrides/quartz/styles/brain/_motion.scss +175 -0
  175. brain/quartz_overrides/quartz/styles/brain/_people_hub.scss +100 -0
  176. brain/quartz_overrides/quartz/styles/brain/_related_docs.scss +137 -0
  177. brain/quartz_overrides/quartz/styles/brain/_search.scss +252 -0
  178. brain/quartz_overrides/quartz/styles/brain/_sidebar.scss +468 -0
  179. brain/quartz_overrides/quartz/styles/brain/_summary_lede.scss +56 -0
  180. brain/quartz_overrides/quartz/styles/brain/_surface.scss +43 -0
  181. brain/quartz_overrides/quartz/styles/brain/_tag_content.scss +118 -0
  182. brain/quartz_overrides/quartz/styles/brain/_tokens.scss +197 -0
  183. brain/quartz_overrides/quartz/styles/brain/_typography.scss +92 -0
  184. brain/quartz_overrides/quartz/styles/custom.scss +89 -0
  185. brain/quartz_overrides/quartz/styles/graph.scss +505 -0
  186. brain/quartz_overrides/quartz/util/ctx.ts +92 -0
  187. brain/quartz_overrides/quartz/util/fastpath_manifest.ts +608 -0
  188. brain/quartz_overrides/quartz/util/path.ts +358 -0
  189. brain/quartz_overrides/quartz/util/sourceIcons.ts +55 -0
  190. brain/quartz_overrides/quartz.config.ts +270 -0
  191. brain/quartz_overrides/quartz.layout.ts +314 -0
  192. brain/queries.py +1188 -0
  193. brain/rank_fusion.py +8 -0
  194. brain/resurface.py +210 -0
  195. brain/review/__init__.py +26 -0
  196. brain/review/emit.py +27 -0
  197. brain/review/queries.py +436 -0
  198. brain/review/render.py +196 -0
  199. brain/review/scans.py +355 -0
  200. brain/review/weekly.py +413 -0
  201. brain/search.py +704 -0
  202. brain/set_similarity.py +15 -0
  203. brain/setup.py +1205 -0
  204. brain/tags.py +56 -0
  205. brain/templates/Caddyfile.j2 +9 -0
  206. brain/templates/__init__.py +1 -0
  207. brain/templates/bin/__init__.py +1 -0
  208. brain/templates/bin/_brain-brief-fg.sh +25 -0
  209. brain/templates/bin/_brain-build-fg.sh +53 -0
  210. brain/templates/bin/_brain-watcher-fg.sh +65 -0
  211. brain/templates/bin/brain-down.sh +89 -0
  212. brain/templates/bin/brain-status.sh +83 -0
  213. brain/templates/bin/brain-up.sh +221 -0
  214. brain/templates/docker/age/Dockerfile +79 -0
  215. brain/templates/docker-compose.stock.yml.j2 +26 -0
  216. brain/templates/docker-compose.yml.j2 +34 -0
  217. brain/templates/env.example +190 -0
  218. brain/templates/launchd/__init__.py +1 -0
  219. brain/templates/launchd/com.brain.brief.plist.j2 +45 -0
  220. brain/templates/launchd/com.brain.build.plist.j2 +46 -0
  221. brain/templates/launchd/com.brain.watcher.plist.j2 +46 -0
  222. brain/templates/skill/SKILL.md +63 -0
  223. brain/templates/skill/__init__.py +1 -0
  224. brain/timeline.py +834 -0
  225. brain/todo.py +124 -0
  226. brain/uninstall.py +185 -0
  227. brain/vault/__init__.py +115 -0
  228. brain/vault/_atomic.py +25 -0
  229. brain/vault/daily_index.py +228 -0
  230. brain/vault/derived_links/__init__.py +50 -0
  231. brain/vault/derived_links/directory.py +683 -0
  232. brain/vault/derived_links/fence.py +408 -0
  233. brain/vault/derived_links/gws.py +64 -0
  234. brain/vault/derived_links/participants.py +143 -0
  235. brain/vault/derived_links/pass_runner.py +362 -0
  236. brain/vault/derived_links/rules.py +137 -0
  237. brain/vault/export.py +683 -0
  238. brain/vault/frontmatter.py +165 -0
  239. brain/vault/graph.py +620 -0
  240. brain/vault/graph_format.py +388 -0
  241. brain/vault/link_rewrite.py +235 -0
  242. brain/vault/links.py +260 -0
  243. brain/vault/note_builder.py +211 -0
  244. brain/vault/paths.py +55 -0
  245. brain/vault/quartz_overlay.py +236 -0
  246. brain/vault/rename.py +591 -0
  247. brain/vault/resolver.py +304 -0
  248. brain/vault/slug.py +127 -0
  249. brain/vault/sync.py +1513 -0
  250. brain/vault/sync_summaries.py +264 -0
  251. brain/vault/templates.py +145 -0
  252. brain/vault/watch.py +1052 -0
  253. brain/wiki/__init__.py +6 -0
  254. brain/wiki/_github_slugger.py +76 -0
  255. brain/wiki/_person_name.py +314 -0
  256. brain/wiki/build_homepage.py +541 -0
  257. brain/wiki/build_partial.py +273 -0
  258. brain/wiki/build_people.py +934 -0
  259. brain/wiki/build_related.py +758 -0
  260. brain/wiki/build_swap.py +585 -0
  261. brain/wiki/build_watcher.py +975 -0
  262. brain/wiki/edit_classifier.py +215 -0
  263. brain/wiki/errors.py +10 -0
  264. brain/wiki/fastpath_manifest.py +475 -0
  265. brain/wiki/fastpath_state.py +174 -0
  266. brain/wiki/install.py +296 -0
  267. brain/wiki/slug.py +111 -0
  268. secondbrain_py-0.2.1.dist-info/METADATA +195 -0
  269. secondbrain_py-0.2.1.dist-info/RECORD +273 -0
  270. secondbrain_py-0.2.1.dist-info/WHEEL +5 -0
  271. secondbrain_py-0.2.1.dist-info/entry_points.txt +11 -0
  272. secondbrain_py-0.2.1.dist-info/licenses/LICENSE +21 -0
  273. 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