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,117 @@
1
+ // Brain marginalia — Lane D of the 2026 commonplace-book redesign.
2
+ //
3
+ // This file is installed at
4
+ // `<vault>/.quartz/quartz/styles/brain/_marginalia.scss` by `brain
5
+ // vault render --overlay` and pulled into the global SCSS via
6
+ // `custom.scss`'s `@use "./brain/marginalia"` line (between
7
+ // `article` and `callouts`).
8
+ //
9
+ // What this file owns: at desktop+ viewports (≥1280px), the right
10
+ // sidebar's `<div class="backlinks">` block reads as figure-margin
11
+ // side-notes — narrow column width, italic Newsreader, dimmed ink,
12
+ // tight per-link spacing — so backlinks register as "the
13
+ // commonplace book's marginal annotations" rather than "another
14
+ // chrome panel."
15
+ //
16
+ // brain (Lane D — fallback path taken):
17
+ //
18
+ // The original Lane D plan considered moving `.backlinks` out of
19
+ // `.right.sidebar` and rendering it as a true sibling of `.center`
20
+ // in the article column's right gutter — the "real Tufte
21
+ // side-note" approach. We did NOT take that path. Reasons:
22
+ //
23
+ // 1. Quartz's three-column grid is defined in `base.scss` via
24
+ // `templateAreas` (`grid-sidebar-left grid-center
25
+ // grid-sidebar-right`). Extracting `.backlinks` into a fourth
26
+ // grid area requires modifying the template at the source —
27
+ // either by editing `base.scss` (which the overlay tries hard
28
+ // to leave untouched, since stock Quartz's `joinStyles()` and
29
+ // grid map machinery in `variables.scss` are tightly coupled),
30
+ // or by duplicating the grid declaration here, which then
31
+ // becomes a fragile fork.
32
+ //
33
+ // 2. The backlinks component itself is rendered by Quartz's layout
34
+ // engine into the `right` slot (see `quartz.layout.ts` line
35
+ // 144). To make `.backlinks` a sibling of `.center` in the DOM
36
+ // we'd need to either move the component declaration out of
37
+ // `right: [...]` (and lose its sticky-sidebar positioning for
38
+ // everything else in that column), or split the component
39
+ // tree, neither of which is a single-file SCSS concern.
40
+ //
41
+ // 3. The team-lead's brief explicitly endorses this fallback:
42
+ // "If marginalia turns out architecturally infeasible without
43
+ // layout surgery, document why and fall back to a 'softer'
44
+ // treatment: the right-sidebar Backlinks block gets the same
45
+ // italic / smaller / dimmed treatment it would have in
46
+ // marginalia, but stays in its current position. This is
47
+ // acceptable — design intent is preserved, layout architecture
48
+ // isn't violated."
49
+ //
50
+ // The fallback preserves ~95% of the visual intent: backlinks read
51
+ // as marginalia (narrow, italic, dimmed, compact), they just live
52
+ // in the right sidebar instead of the article's right gutter. A
53
+ // future redesign that's willing to fork `base.scss` can take the
54
+ // architectural path; this file is the current best-effort
55
+ // styling contract.
56
+ //
57
+ // Below the desktop breakpoint (1280px and narrower) this file makes
58
+ // NO changes — the right sidebar falls back to its Lane C card
59
+ // styling from `_sidebar.scss`. That's deliberate: at 1200px or
60
+ // 1024px the backlinks panel needs the full sidebar width to read,
61
+ // and squeezing it into a 18ch column would force every backlink
62
+ // title to truncate.
63
+ //
64
+ // Tokens consumed (all from `_tokens.scss`):
65
+ // * `--ink-2` — dimmed marginalia ink color
66
+ // * `--fs-sm` / `--fs-xs` — smaller-than-body type
67
+ // * `--rule` — vertical rule on the marginalia column edge
68
+ // * `--bodyFont` — Newsreader italic for the side-note voice
69
+ // (printed marginalia is always body type one
70
+ // notch smaller than running prose, not display
71
+ // type — even though Lane A's project-wide
72
+ // italic convention reaches for Fraunces, that
73
+ // rule applies to inline emphasis, not to a
74
+ // whole-block side-note voice)
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // 1. Marginalia treatment at desktop+ viewports
78
+ // ---------------------------------------------------------------------------
79
+ //
80
+ // 1280px is one notch above Quartz's `$desktop` breakpoint (1200px).
81
+ // At 1200-1279px the right sidebar is already cramped (the local
82
+ // graph eats most of the vertical space); marginalia would compete
83
+ // with the graph and ToC for too little room. At 1280px+ there's
84
+ // enough vertical breathing room to make the backlinks panel feel
85
+ // like figure-margin annotations rather than another stacked card.
86
+ //
87
+ // Why scope to `.right.sidebar > .backlinks` (not just `.backlinks`)?
88
+ // Quartz emits `<div class="backlinks">` from `Component.Backlinks`
89
+ // (see `quartz/components/Backlinks.tsx` line 32). The same component
90
+ // could in theory appear elsewhere (e.g. `.center` slot for a
91
+ // custom layout); we only want the marginalia treatment when the
92
+ // component is sitting in the right sidebar position. The descendant
93
+ // selector enforces that scope.
94
+ //
95
+ // brain: this rule wins over Lane C's `.sidebar.right .backlinks`
96
+ // card chrome (background, border-radius, shadow, padding) because
97
+ // it's both later in the cascade AND uses overrides where they
98
+ // matter (background → transparent, padding → 0). Where Lane C
99
+ // rules don't conflict (e.g. nothing about font-style) we inherit
100
+ // Lane C's defaults silently — which is fine.
101
+
102
+ // brain (2026-05-06): the marginalia treatment was REMOVED per user
103
+ // feedback that Backlinks read as visually inconsistent with the
104
+ // Related and Table of Contents cards above it (190px column vs ~256px
105
+ // full-width cards). Backlinks now inherits Lane C's full-width card
106
+ // chrome from `_sidebar.scss` (`background: var(--surface-1)`,
107
+ // `border-radius: 8px`, `box-shadow: var(--shadow-sm)`,
108
+ // `padding: 0.6rem 0.8rem`), matching the ToC and Related panels above
109
+ // it. The italic / dimmed / narrow side-note voice is dropped — sidebar
110
+ // reads as a stack of three coordinated panels instead.
111
+ //
112
+ // If a future redesign wants the Tufte side-note treatment back, the
113
+ // architectural path is to extract `.backlinks` into a fourth grid
114
+ // area in `base.scss`'s template (see the original analysis in this
115
+ // file's git history at commit 2eaf550) — re-applying the SCSS without
116
+ // also moving the DOM out of `.right.sidebar` reproduces the exact
117
+ // width-mismatch bug this removal addresses.
@@ -0,0 +1,175 @@
1
+ // Brain motion — Lane D of the 2026 commonplace-book redesign.
2
+ //
3
+ // This file is installed at
4
+ // `<vault>/.quartz/quartz/styles/brain/_motion.scss` by `brain vault
5
+ // render --overlay` and pulled into the global SCSS via
6
+ // `custom.scss`'s `@use "./brain/motion"` line (between
7
+ // `atmosphere` and `links`).
8
+ //
9
+ // What this file owns:
10
+ //
11
+ // 1. Page-load reveal animation — staggered slide/fade for the
12
+ // three-column shell on first paint. Sidebar-left slides in
13
+ // from the left, center fades up, sidebar-right slides in from
14
+ // the right; offsets ramped 50ms / 150ms / 250ms so the eye
15
+ // reads them in sequence rather than as a single twitch.
16
+ // 2. `prefers-reduced-motion` accessibility gate — the entire
17
+ // animation block sits inside a `(prefers-reduced-motion:
18
+ // no-preference)` media query so a user with the OS-level
19
+ // reduce-motion setting never sees the slide-in. Without the
20
+ // gate the WCAG 2.2 SC 2.3.3 (Animation from Interactions)
21
+ // regression risk is real for vestibular-disorder users.
22
+ // 3. Documentation pointer to the canonical motion-token knobs.
23
+ // Lane A's `_tokens.scss` defines `--motion-fast`, `--motion-mid`,
24
+ // `--motion-slow`, and `--motion-easing` — every other partial
25
+ // consumes those tokens for hover / focus / transition timings.
26
+ // This file does NOT redefine them; it just notes where they
27
+ // live so a future maintainer knows the single source of truth.
28
+ //
29
+ // Tokens consumed (all from `_tokens.scss`):
30
+ // * `--motion-easing` — smooth-out-quint cubic-bezier for the
31
+ // reveal animations.
32
+ //
33
+ // Tokens NOT redefined here (deliberate — see above):
34
+ // * `--motion-fast` (150ms) — hover transitions, link underline
35
+ // reveals (Lane B `_links.scss`), code-copy button (Lane C).
36
+ // * `--motion-mid` (250ms) — Cmd-K modal entrance, ToC card hovers,
37
+ // callout chrome.
38
+ // * `--motion-slow` (400ms) — page-load reveal duration; the
39
+ // stagger offsets below cap at 250ms so the cumulative reveal
40
+ // finishes around 650ms (slow + last-stagger), short enough to
41
+ // not feel laggy but long enough to register as "the page
42
+ // composed itself" rather than "the page snapped in."
43
+ //
44
+ // brain (Lane D): hover micro-interactions are already wired in
45
+ // Lanes B (link underlines) and C (sidebar icon trio, code-copy
46
+ // button, callout chrome). This file deliberately does NOT
47
+ // re-declare those — the `--motion-fast/mid/slow` + `--motion-easing`
48
+ // tokens declared in `_tokens.scss` are the canonical knobs, and
49
+ // the consuming partials read them directly. If a future change
50
+ // wants to retune motion globally, change the tokens once in
51
+ // `_tokens.scss` and every consumer updates.
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // 1. Page-load reveal — staggered three-column entrance
55
+ // ---------------------------------------------------------------------------
56
+ //
57
+ // The animation block is wrapped in
58
+ // `(prefers-reduced-motion: no-preference)` — the negative form
59
+ // matches when the OS-level reduce-motion setting is OFF. Users
60
+ // with reduce-motion ON see the page paint statically, no slide,
61
+ // no fade.
62
+ //
63
+ // Stagger order:
64
+ // * `.left.sidebar` — 50ms delay, slides 12px from the left
65
+ // * `.center` — 150ms delay, fades up 8px
66
+ // * `.right.sidebar` — 250ms delay, slides 12px from the right
67
+ //
68
+ // Why this order: the eye reads the page from outside-in. The
69
+ // left-sidebar (search + explorer) and right-sidebar (graph + ToC +
70
+ // backlinks) are reference scaffolding; the center is the article
71
+ // itself. By staggering the chrome before the content, we cue the
72
+ // reader's eye toward the center column on arrival — the page
73
+ // "composes itself" around the article rather than dumping
74
+ // everything in at once.
75
+ //
76
+ // `animation-fill-mode: both` (the `both` keyword in the shorthand)
77
+ // applies the `from` keyframe state during the delay window. Without
78
+ // it, the elements would paint at their final state during the 50/
79
+ // 150/250ms delay, then snap back to `from` and run the animation —
80
+ // the dreaded "FOUC of motion." `both` makes the delay window the
81
+ // `from` state.
82
+ //
83
+ // SPA navigation (Quartz's `enableSPA` + micromorph) patches DOM in
84
+ // place rather than replacing it, so `.left.sidebar` / `.center` /
85
+ // `.right.sidebar` are typically NOT re-mounted between routes. The
86
+ // animation fires once per hard load, which is the desired behavior
87
+ // — the cinematic entrance shouldn't replay on every internal click.
88
+
89
+ // brain (Lane D audit fix 2026-05-02): the original commit hardcoded
90
+ // the stagger delays as `0.05s`, `0.15s`, `0.25s`. Project rule is
91
+ // tokens-only — the literals were swapped for `--motion-stagger-1/2/3`
92
+ // declared in `_tokens.scss` (next to the existing `--motion-fast/
93
+ // mid/slow` block). The duration `var(--motion-slow)` was already a
94
+ // token; no change needed there.
95
+
96
+ // brain (Linear-variant feedback round 4): `animation-fill-mode` is
97
+ // `backwards`, NOT `both`. With `both`, the final keyframe's
98
+ // `transform: none` was getting interpolated into
99
+ // `matrix(1, 0, 0, 1, 0, 0)` (the identity) and stuck on the element
100
+ // permanently. A non-`none` transform on `.right.sidebar` creates a
101
+ // containing block for `position: fixed` descendants, which broke the
102
+ // global-graph modal — it anchored to the sidebar instead of the
103
+ // viewport, rendering as a narrow strip on the right edge.
104
+ //
105
+ // `backwards` applies the `from` keyframe BEFORE animation starts (so
106
+ // the entrance animation has its hidden-and-shifted starting state
107
+ // during the delay window) and lets the element fall back to its
108
+ // natural style (transform: none, opacity: 1) after the animation
109
+ // completes. No leftover identity-transform → no leftover containing
110
+ // block → fixed-position modals anchor to viewport correctly.
111
+ @media (prefers-reduced-motion: no-preference) {
112
+ .left.sidebar {
113
+ animation: brain-slide-in-left var(--motion-slow) var(--motion-easing) var(--motion-stagger-1) backwards;
114
+ }
115
+
116
+ .center {
117
+ animation: brain-fade-up var(--motion-slow) var(--motion-easing) var(--motion-stagger-2) backwards;
118
+ }
119
+
120
+ .right.sidebar {
121
+ animation: brain-slide-in-right var(--motion-slow) var(--motion-easing) var(--motion-stagger-3) backwards;
122
+ }
123
+ }
124
+
125
+ // Keyframes live OUTSIDE the media query — declaring them inside
126
+ // would scope them to that query in a way some renderers handle
127
+ // inconsistently. The `@keyframes` themselves are inert until
128
+ // referenced by an `animation` property; defining them globally is
129
+ // the conventional shape.
130
+
131
+ // brain (Linear-variant feedback round 4): the `to` keyframes MUST end
132
+ // with `transform: none`, NOT `transform: translateX(0)` /
133
+ // `translateY(0)`. Reason: a non-`none` transform value on any
134
+ // ancestor creates a containing block for `position: fixed` descendants
135
+ // (per CSS spec). With `animation-fill-mode: both` (set on the
136
+ // `.left.sidebar / .center / .right.sidebar` rules above), the final
137
+ // keyframe's transform value sticks AFTER the animation finishes —
138
+ // leaving `matrix(1,0,0,1,0,0)` on the sidebars permanently. That CB
139
+ // caused the global-graph modal (which is `position: fixed; left: 0;
140
+ // width: 100vw` inside `.right.sidebar`) to anchor to the sidebar
141
+ // instead of the viewport, rendering as a narrow strip on the right
142
+ // edge of the page. `transform: none` in the `to` keyframe restores
143
+ // the default and the modal anchors back to the viewport.
144
+ @keyframes brain-slide-in-left {
145
+ from {
146
+ opacity: 0;
147
+ transform: translateX(-12px);
148
+ }
149
+ to {
150
+ opacity: 1;
151
+ transform: none;
152
+ }
153
+ }
154
+
155
+ @keyframes brain-fade-up {
156
+ from {
157
+ opacity: 0;
158
+ transform: translateY(8px);
159
+ }
160
+ to {
161
+ opacity: 1;
162
+ transform: none;
163
+ }
164
+ }
165
+
166
+ @keyframes brain-slide-in-right {
167
+ from {
168
+ opacity: 0;
169
+ transform: translateX(12px);
170
+ }
171
+ to {
172
+ opacity: 1;
173
+ transform: none;
174
+ }
175
+ }
@@ -0,0 +1,100 @@
1
+ // Brain People Hub — Phase C of the 2026-05-07 People Hub plan.
2
+ //
3
+ // This file is installed at
4
+ // `<vault>/.quartz/quartz/styles/brain/_people_hub.scss` by `brain
5
+ // vault render --overlay` and pulled into the global SCSS via
6
+ // `custom.scss`'s `@use "./brain/people_hub"` line.
7
+ //
8
+ // Scope of this file:
9
+ //
10
+ // * Distinct heading + frontmatter treatment for `kind: people`
11
+ // pages so the user can tell at a glance they're on a hub page
12
+ // (a person's roster) vs. a regular doc.
13
+ // * Index page (`/people/index`) gets a slightly heavier treatment
14
+ // to read as a directory landing page.
15
+ //
16
+ // Targeting strategy:
17
+ //
18
+ // Quartz exposes `body[data-slug=...]` (set in `renderPage.tsx`)
19
+ // but does NOT expose a `data-kind` attribute for arbitrary
20
+ // frontmatter fields. We key off the slug prefix instead — every
21
+ // people-hub page lives under `people/...` (per the
22
+ // `<vault>/people/<slug>.md` emit contract in
23
+ // `src/brain/wiki/build_people.py`), so `body[data-slug^="people/"]`
24
+ // selects the entire hub. The index page gets an additional rule
25
+ // keyed off `body[data-slug="people/index"]` for landing-page treatment.
26
+ //
27
+ // Tokens consumed (all from `_tokens.scss`):
28
+ //
29
+ // * `--accent-strong` — page-kind accent rail
30
+ // * `--surface-1` — H1 underline backdrop
31
+ // * `--rule` — primary-email block hairline
32
+ // * `--ink-0` / `--ink-2` — text colors
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // 1. Per-person pages — `body[data-slug^="people/"]` minus the index.
36
+ //
37
+ // We rely on a sibling rule below to refine the index landing page.
38
+ // The :not() selector keeps individual roster pages tighter (a
39
+ // distinct accent rail under the H1) and lets the index claim the
40
+ // fuller card treatment.
41
+ // ---------------------------------------------------------------------------
42
+
43
+ body[data-slug^="people/"]:not([data-slug="people/index"]) {
44
+ // Page-kind accent: a colored vertical rail to the left of the H1
45
+ // so the person's name reads as a heading-of-headings, not just
46
+ // another doc title. Uses padding (not margin) so the rail aligns
47
+ // with the article column rather than the page edge.
48
+ article > h1:first-of-type {
49
+ border-left: 4px solid var(--accent-strong, var(--secondary));
50
+ padding-left: 0.6rem;
51
+ // Subtle background so the rail reads as an enclosed badge rather
52
+ // than a stray vertical line. Falls back to `--gray` if the surface
53
+ // token isn't defined (older theme setups).
54
+ background: linear-gradient(
55
+ to right,
56
+ var(--surface-1, transparent) 0%,
57
+ transparent 60%
58
+ );
59
+ }
60
+
61
+ // Primary-email line: bold the label, faintly indent the value, give
62
+ // it a hairline underline so it reads as a header field rather than
63
+ // body prose. Targets the first `<p>` inside the article (the
64
+ // emitter writes "**Primary email:** [...]" as the very first
65
+ // paragraph).
66
+ article > p:first-of-type {
67
+ border-bottom: 1px solid var(--rule, var(--lightgray));
68
+ padding-bottom: 0.4rem;
69
+ margin-bottom: 1.2rem;
70
+ }
71
+ }
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // 2. Index page — `body[data-slug="people/index"]`.
75
+ //
76
+ // The index lists every person; we want it to read as a "directory
77
+ // landing" page — heavier H1 treatment, a touch more breathing room
78
+ // between roster rows. Ordering matters: this block must come AFTER
79
+ // the per-person block above so its more specific selector wins
80
+ // (the per-person rule's `:not([data-slug="people/index"])` already
81
+ // excludes the index, but defense in depth).
82
+ // ---------------------------------------------------------------------------
83
+
84
+ body[data-slug="people/index"] {
85
+ article > h1:first-of-type {
86
+ // Heavier weight than the per-person H1; this is a landing page
87
+ // for the whole hub.
88
+ font-weight: 700;
89
+ border-bottom: 2px solid var(--accent-strong, var(--secondary));
90
+ padding-bottom: 0.4rem;
91
+ margin-bottom: 1.4rem;
92
+ }
93
+
94
+ // Roster rows — one per person — get a touch more vertical
95
+ // breathing room so the curated badge + name + doc count reads
96
+ // as a glanceable directory rather than a dense bullet list.
97
+ article ul > li {
98
+ padding: 0.2rem 0;
99
+ }
100
+ }
@@ -0,0 +1,137 @@
1
+ // Brain related-docs sidebar — Phase 5.2 of the Wiki UX Overhaul.
2
+ //
3
+ // Loaded globally via `custom.scss`. The component only renders in the
4
+ // right sidebar for content pages; the runtime hides the whole panel
5
+ // until `/static/related/<slug>.json` returns valid rows.
6
+
7
+ .sidebar.right {
8
+ .brain-related-docs {
9
+ background: var(--surface-1);
10
+ border-radius: 8px;
11
+ padding: 0.6rem 0.8rem;
12
+ box-shadow: var(--shadow-sm);
13
+ box-sizing: border-box;
14
+ // brain: allow the panel to shrink in the right-sidebar flex column
15
+ // so a tall related list never pushes ToC / Backlinks off the
16
+ // viewport. Without `min-height: 0` flex children refuse to shrink
17
+ // below their content size, which is what produced the original
18
+ // "can't scroll to the ToC" bug on hub pages.
19
+ min-height: 0;
20
+ // brain: become a flex column ourselves so the inner <ol> can be
21
+ // flex-shrunk to whatever height the parent sidebar grants this
22
+ // section. Without this, the section flex-shrinks but the inner
23
+ // list keeps its hardcoded `max-height: 32vh` and renders past the
24
+ // section's box (overflow: visible) — visually spilling onto the
25
+ // Backlinks panel below it. Pairs with `min-height: 0` on the list.
26
+ display: flex;
27
+ flex-direction: column;
28
+
29
+ &[hidden] {
30
+ display: none;
31
+ }
32
+
33
+ > h3 {
34
+ margin-top: 0;
35
+ margin-bottom: 0.4rem;
36
+ font-weight: 600;
37
+ font-size: 0.7rem;
38
+ letter-spacing: 0.06em;
39
+ text-transform: uppercase;
40
+ color: var(--ink-1);
41
+ }
42
+ }
43
+
44
+ // brain: cap the related-docs list at ~one third of the viewport with
45
+ // its own internal scroll. The panel chrome (background, h3, padding)
46
+ // stays put; only the rows scroll. Combined with reordering the ToC
47
+ // above this panel in `quartz.layout.ts`, this guarantees the ToC and
48
+ // Backlinks panels remain reachable on hub pages with many related
49
+ // notes (COMPANY_REDACTED, COMPANY_REDACTED, etc. routinely pull 12+).
50
+ .brain-related-docs-list {
51
+ list-style: none;
52
+ margin: 0;
53
+ padding: 0;
54
+ display: grid;
55
+ gap: 0.35rem;
56
+ // brain: cap natural height at 32vh so a packed related set still
57
+ // leaves room for Backlinks below. `min-height: 0` plus the section
58
+ // being a flex column above lets the right sidebar's flex layout
59
+ // shrink the list (with its own internal scroll) when the column is
60
+ // squeezed — keeps the list inside the section's box instead of
61
+ // spilling onto the Backlinks panel below.
62
+ max-height: 32vh;
63
+ min-height: 0;
64
+ overflow-y: auto;
65
+ overscroll-behavior: contain;
66
+ scrollbar-width: thin;
67
+ scrollbar-color: var(--lightgray) transparent;
68
+
69
+ &::-webkit-scrollbar {
70
+ width: 4px;
71
+ }
72
+
73
+ &::-webkit-scrollbar-thumb {
74
+ background: var(--lightgray);
75
+ border-radius: 2px;
76
+ }
77
+ }
78
+
79
+ .brain-related-docs-item {
80
+ margin: 0;
81
+
82
+ > a {
83
+ display: grid;
84
+ grid-template-columns: 1.25rem minmax(0, 1fr);
85
+ gap: 0.45rem;
86
+ align-items: start;
87
+ padding: 0.35rem 0;
88
+ color: var(--ink-1);
89
+ text-decoration: none;
90
+ border-radius: 6px;
91
+ transition:
92
+ background var(--motion-fast) var(--motion-easing),
93
+ color var(--motion-fast) var(--motion-easing);
94
+
95
+ &:hover {
96
+ background: var(--accent-soft);
97
+ color: var(--ink-0);
98
+ }
99
+ }
100
+ }
101
+
102
+ .brain-related-docs-source {
103
+ width: 1.25rem;
104
+ line-height: 1.25;
105
+ text-align: center;
106
+ font-size: var(--fs-sm);
107
+ }
108
+
109
+ .brain-related-docs-body {
110
+ min-width: 0;
111
+ display: grid;
112
+ gap: 0.12rem;
113
+ }
114
+
115
+ .brain-related-docs-title {
116
+ color: var(--ink-0);
117
+ font-weight: 600;
118
+ font-size: var(--fs-sm);
119
+ line-height: 1.25;
120
+ }
121
+
122
+ .brain-related-docs-snippet {
123
+ display: -webkit-box;
124
+ color: var(--ink-2);
125
+ font-size: var(--fs-xs);
126
+ line-height: 1.35;
127
+ -webkit-line-clamp: 2;
128
+ -webkit-box-orient: vertical;
129
+ overflow: hidden;
130
+ }
131
+
132
+ .brain-related-docs-empty {
133
+ margin: 0;
134
+ color: var(--ink-2);
135
+ font-size: var(--fs-xs);
136
+ }
137
+ }