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,172 @@
1
+ // Brain command palette component (Lane C, Cmd/Ctrl-P).
2
+ //
3
+ // This file is a TEMPLATE. It is installed at
4
+ // `<vault>/.quartz/quartz/components/CommandPalette.tsx` by `brain
5
+ // vault render --overlay`. It does NOT compile or run from the brain
6
+ // repo itself; the imports below resolve against the dependencies
7
+ // Quartz pulls into the cloned workspace via `npm install`, not
8
+ // against any package brain ships.
9
+ //
10
+ // Tested against Quartz v4.5.x (April 2026). The component shape
11
+ // mirrors stock Quartz components — see `Graph.tsx` (also a
12
+ // brain-overlayed component) for the canonical pattern. If a future
13
+ // Quartz version restructures `QuartzComponent` / `QuartzComponentProps`,
14
+ // pull the latest reference component from
15
+ // https://github.com/jackyzha0/quartz/blob/v4/quartz/components/
16
+ // and re-apply the brain tweaks below.
17
+ //
18
+ // Strategy — full new component (not an upstream override): nothing
19
+ // in stock Quartz exposes a quick-open palette. We render the palette
20
+ // markup once per page (registered in `quartz.layout.ts` `afterBody`
21
+ // slot — global, ships with every page in the build), then a
22
+ // co-located inline script (`scripts/commandPalette.inline.ts`)
23
+ // owns the open/close lifecycle, source chips, fuzzy search, keyboard
24
+ // handling, and SPA-nav cleanup.
25
+ //
26
+ // brain (Lane C audit fix 2026-05-02): refactored to native
27
+ // `<dialog>` element. Native dialog gives us, for free:
28
+ // * Focus trap inside the modal while open (no manual `Tab` ring
29
+ // management needed).
30
+ // * Native `Escape` close behavior + `cancel` event we can intercept
31
+ // to keep the brain close-animation.
32
+ // * Automatic focus restoration to the previously-focused element
33
+ // on `dialog.close()`.
34
+ // * Native `::backdrop` pseudo-element — replaces the manual
35
+ // `.brain-cmdk-backdrop` div with a CSS-driven scrim that the
36
+ // browser positions above the rest of the page (top of the
37
+ // stacking context, no z-index gymnastics).
38
+ // All four are non-trivial to implement correctly by hand; using the
39
+ // platform primitive removes drift surface.
40
+ //
41
+ // Markup contract — must stay in sync with the selector list in
42
+ // `_command_palette.scss` (Lane C/P5.3 SCSS partial) and the DOM walk inside
43
+ // `commandPalette.inline.ts`. See those two files' top comments.
44
+ //
45
+ // brain (Lane C audit fix 2026-05-02): combobox semantics on the
46
+ // search input. Plain `<input aria-controls=...>` is an a11y no-op;
47
+ // the WAI-ARIA combobox pattern is `role="combobox"` +
48
+ // `aria-expanded` (toggled by the script) + `aria-haspopup="listbox"`
49
+ // + `aria-autocomplete="list"`. Pairs with `aria-activedescendant`
50
+ // (set per-keystroke by the script) so screen readers announce each
51
+ // arrow-key result.
52
+ //
53
+ // Responsibility (CLAUDE.md rule 8): this file owns the Preact
54
+ // component wrapper. Open/close + search logic lives in
55
+ // `scripts/commandPalette.inline.ts`; the visual rules live in
56
+ // `../styles/brain/_command_palette.scss`. Don't let them spill into each
57
+ // other.
58
+
59
+ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
60
+ // @ts-ignore — esbuild-loader resolves .ts to a bundled string; TS doesn't know.
61
+ import script from "./scripts/commandPalette.inline"
62
+ import { SOURCE_ICONS, SOURCE_CHIP_ORDER } from "../util/sourceIcons"
63
+
64
+ const CHIP_VALUES: ReadonlyArray<keyof typeof SOURCE_ICONS> = SOURCE_CHIP_ORDER
65
+
66
+ const CommandPalette: QuartzComponent = (_props: QuartzComponentProps) => {
67
+ // brain: native `<dialog>` element. The script calls `showModal()`
68
+ // to open and `close()` to dismiss; both manage focus + scroll-lock
69
+ // + the `::backdrop` pseudo-element automatically. The `id` is the
70
+ // selector hook used by `commandPalette.inline.ts` and `_command_palette.scss`
71
+ // — don't rename without updating both.
72
+ //
73
+ // brain: `aria-label` on the dialog gives screen readers a name
74
+ // when the dialog opens (replacing the now-unnecessary `role` /
75
+ // `aria-modal` attributes — native dialog provides both).
76
+ return (
77
+ <dialog id="brain-cmdk-root" class="brain-cmdk-root" aria-label="Command palette">
78
+ <div class="brain-cmdk-modal">
79
+ <div class="brain-cmdk-chips" role="group" aria-label="Filter quick open by source">
80
+ <button
81
+ type="button"
82
+ class="brain-cmdk-chip brain-cmdk-chip-all"
83
+ data-brain-source="__all__"
84
+ data-active="true"
85
+ aria-pressed="true"
86
+ >
87
+ All
88
+ </button>
89
+ {CHIP_VALUES.map((value) => (
90
+ <button
91
+ type="button"
92
+ class="brain-cmdk-chip"
93
+ data-brain-source={value}
94
+ data-active="true"
95
+ aria-pressed="true"
96
+ >
97
+ <span class="brain-cmdk-chip-icon" aria-hidden="true">
98
+ {SOURCE_ICONS[value]}
99
+ </span>
100
+ <span class="brain-cmdk-chip-label">{value}</span>
101
+ </button>
102
+ ))}
103
+ </div>
104
+ <input
105
+ class="brain-cmdk-input"
106
+ type="text"
107
+ autocomplete="off"
108
+ autocorrect="off"
109
+ spellcheck={false}
110
+ placeholder="Search the wiki…"
111
+ // brain (Lane C audit fix): WAI-ARIA combobox pattern.
112
+ // * role="combobox" tells AT this is a search input that
113
+ // drives an associated popup.
114
+ // * aria-expanded mirrors visibility (set by the script
115
+ // when the dialog opens / closes).
116
+ // * aria-haspopup="listbox" + aria-autocomplete="list"
117
+ // describes the popup shape.
118
+ // * aria-controls links to the listbox by id.
119
+ // * aria-activedescendant (set per-keystroke by the
120
+ // script) points to the currently-selected option's id
121
+ // so AT can announce it on each arrow press without
122
+ // moving DOM focus off the input.
123
+ role="combobox"
124
+ aria-expanded="false"
125
+ aria-haspopup="listbox"
126
+ aria-controls="brain-cmdk-results"
127
+ aria-autocomplete="list"
128
+ aria-label="Search the wiki"
129
+ />
130
+ {/* brain: results list rendered by the inline script. The
131
+ initial `<ul>` is empty; the script injects `<li
132
+ class="brain-cmdk-result" id="brain-cmdk-result-N">` rows
133
+ from the contentIndex match. Each `<li>` carries the
134
+ stable id the input's `aria-activedescendant` points at. */}
135
+ <ul
136
+ class="brain-cmdk-results"
137
+ id="brain-cmdk-results"
138
+ role="listbox"
139
+ aria-label="Search results"
140
+ ></ul>
141
+ <div class="brain-cmdk-empty" hidden>
142
+ No results.
143
+ </div>
144
+ <div class="brain-cmdk-footer" aria-hidden="true">
145
+ <kbd>↑</kbd>
146
+ <kbd>↓</kbd>
147
+ <span>navigate</span>
148
+ <span>·</span>
149
+ <kbd>↵</kbd>
150
+ <span>open</span>
151
+ <span>·</span>
152
+ <kbd>esc</kbd>
153
+ <span>close</span>
154
+ </div>
155
+ </div>
156
+ </dialog>
157
+ )
158
+ }
159
+
160
+ // brain: wire the inline script. esbuild-loader resolves the
161
+ // `.inline.ts` import to its compiled bundled string at build time;
162
+ // Quartz inlines the string into the page at `</body>` time when
163
+ // `afterDOMLoaded` is set.
164
+ CommandPalette.afterDOMLoaded = script
165
+
166
+ // brain: no component-css here — visuals live in the global
167
+ // `_command_palette.scss`, which is `@use`-imported via `custom.scss`. That
168
+ // ensures the styles ship with EVERY page (not just pages that mount
169
+ // the component), so first-paint of any page already has the modal's
170
+ // resting state (closed `<dialog>`) rendered without flashing.
171
+
172
+ export default (() => CommandPalette) satisfies QuartzComponentConstructor
@@ -0,0 +1,198 @@
1
+ // Brain wiki overlay — quartz/components/Explorer.tsx
2
+ //
3
+ // This file shadows the upstream ``~/brain-vault/.quartz/quartz/components/Explorer.tsx``.
4
+ // All upstream functionality is preserved verbatim. The only change is the
5
+ // replacement of the module-level ``numExplorers`` counter with a slug-derived
6
+ // deterministic ID.
7
+ //
8
+ // Amendment #8 (T4 — Plan B v3):
9
+ // The upstream Explorer uses a module-level counter (``let numExplorers = 0``)
10
+ // to generate unique aria-id attributes: ``explorer-0``, ``explorer-1``, etc.
11
+ // In a full build, the counter increments with render order; in a partial build
12
+ // (fresh Node process), the counter resets to 0. The same slug may therefore
13
+ // produce a different id in the two paths, breaking byte-exact HTML parity and
14
+ // confusing the browser's aria-controls association on page reload.
15
+ //
16
+ // Fix: replace the counter with a slug-derived deterministic id
17
+ // (``explorer-<slug-with-slashes-as-hyphens>``). The id is unique per page
18
+ // (each page renders exactly one Explorer), deterministic across builds, and
19
+ // independent of render order.
20
+ //
21
+ // This overlay applies in BOTH full-build and partial-build paths so both
22
+ // produce the same HTML for the same slug regardless of order or process lifetime.
23
+
24
+ import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
25
+ import style from "./styles/explorer.scss"
26
+
27
+ // @ts-ignore
28
+ import script from "./scripts/explorer.inline"
29
+ import { classNames } from "../util/lang"
30
+ import { i18n } from "../i18n"
31
+ import { FileTrieNode } from "../util/fileTrie"
32
+ import OverflowListFactory from "./OverflowList"
33
+ import { concatenateResources } from "../util/resources"
34
+
35
+ type OrderEntries = "sort" | "filter" | "map"
36
+
37
+ export interface Options {
38
+ title?: string
39
+ folderDefaultState: "collapsed" | "open"
40
+ folderClickBehavior: "collapse" | "link"
41
+ useSavedState: boolean
42
+ sortFn: (a: FileTrieNode, b: FileTrieNode) => number
43
+ filterFn: (node: FileTrieNode) => boolean
44
+ mapFn: (node: FileTrieNode) => void
45
+ order: OrderEntries[]
46
+ }
47
+
48
+ const defaultOptions: Options = {
49
+ folderDefaultState: "collapsed",
50
+ folderClickBehavior: "link",
51
+ useSavedState: true,
52
+ mapFn: (node) => {
53
+ return node
54
+ },
55
+ sortFn: (a, b) => {
56
+ // Sort order: folders first, then files. Sort folders and files alphabeticall
57
+ if ((!a.isFolder && !b.isFolder) || (a.isFolder && b.isFolder)) {
58
+ // numeric: true: Whether numeric collation should be used, such that "1" < "2" < "10"
59
+ // sensitivity: "base": Only strings that differ in base letters compare as unequal. Examples: a ≠ b, a = á, a = A
60
+ return a.displayName.localeCompare(b.displayName, undefined, {
61
+ numeric: true,
62
+ sensitivity: "base",
63
+ })
64
+ }
65
+
66
+ if (!a.isFolder && b.isFolder) {
67
+ return 1
68
+ } else {
69
+ return -1
70
+ }
71
+ },
72
+ filterFn: (node) => node.slugSegment !== "tags",
73
+ order: ["filter", "map", "sort"],
74
+ }
75
+
76
+ export type FolderState = {
77
+ path: string
78
+ collapsed: boolean
79
+ }
80
+
81
+ // brain overlay (Amendment #8): the upstream numExplorers counter is REMOVED.
82
+ // Each Explorer uses a slug-derived deterministic id instead.
83
+ // See file-level comment for full rationale.
84
+
85
+ export default ((userOpts?: Partial<Options>) => {
86
+ const opts: Options = { ...defaultOptions, ...userOpts }
87
+ const { OverflowList, overflowListAfterDOMLoaded } = OverflowListFactory()
88
+
89
+ // brain overlay (Amendment #8): accept fileData from props and derive a
90
+ // deterministic, slug-based aria-id for the Explorer component.
91
+ // ``/`` → ``-`` so ``COMPANY_REDACTED/AI-adoption`` becomes ``explorer-COMPANY_REDACTED-AI-adoption``.
92
+ const Explorer: QuartzComponent = ({ cfg, fileData, displayClass }: QuartzComponentProps) => {
93
+ // Slug-derived deterministic ID: stable across full-build render order and
94
+ // partial-build fresh processes. Replaces the upstream numExplorers counter.
95
+ const rawSlug = fileData?.slug ?? "root"
96
+ const slugId = rawSlug.replace(/\//g, "-")
97
+ const id = `explorer-${slugId}`
98
+
99
+ return (
100
+ <div
101
+ class={classNames(displayClass, "explorer")}
102
+ data-behavior={opts.folderClickBehavior}
103
+ data-collapsed={opts.folderDefaultState}
104
+ data-savestate={opts.useSavedState}
105
+ data-data-fns={JSON.stringify({
106
+ order: opts.order,
107
+ sortFn: opts.sortFn.toString(),
108
+ filterFn: opts.filterFn.toString(),
109
+ mapFn: opts.mapFn.toString(),
110
+ })}
111
+ >
112
+ <button
113
+ type="button"
114
+ class="explorer-toggle mobile-explorer hide-until-loaded"
115
+ data-mobile={true}
116
+ aria-controls={id}
117
+ >
118
+ <svg
119
+ xmlns="http://www.w3.org/2000/svg"
120
+ width="24"
121
+ height="24"
122
+ viewBox="0 0 24 24"
123
+ stroke-width="2"
124
+ stroke-linecap="round"
125
+ stroke-linejoin="round"
126
+ class="lucide-menu"
127
+ >
128
+ <line x1="4" x2="20" y1="12" y2="12" />
129
+ <line x1="4" x2="20" y1="6" y2="6" />
130
+ <line x1="4" x2="20" y1="18" y2="18" />
131
+ </svg>
132
+ </button>
133
+ <button
134
+ type="button"
135
+ class="title-button explorer-toggle desktop-explorer"
136
+ data-mobile={false}
137
+ aria-expanded={true}
138
+ >
139
+ <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
140
+ <svg
141
+ xmlns="http://www.w3.org/2000/svg"
142
+ width="14"
143
+ height="14"
144
+ viewBox="5 8 14 8"
145
+ fill="none"
146
+ stroke="currentColor"
147
+ stroke-width="2"
148
+ stroke-linecap="round"
149
+ stroke-linejoin="round"
150
+ class="fold"
151
+ >
152
+ <polyline points="6 9 12 15 18 9"></polyline>
153
+ </svg>
154
+ </button>
155
+ <div id={id} class="explorer-content" aria-expanded={false} role="group">
156
+ <OverflowList class="explorer-ul" />
157
+ </div>
158
+ <template id="template-file">
159
+ <li>
160
+ <a href="#"></a>
161
+ </li>
162
+ </template>
163
+ <template id="template-folder">
164
+ <li>
165
+ <div class="folder-container">
166
+ <svg
167
+ xmlns="http://www.w3.org/2000/svg"
168
+ width="12"
169
+ height="12"
170
+ viewBox="5 8 14 8"
171
+ fill="none"
172
+ stroke="currentColor"
173
+ stroke-width="2"
174
+ stroke-linecap="round"
175
+ stroke-linejoin="round"
176
+ class="folder-icon"
177
+ >
178
+ <polyline points="6 9 12 15 18 9"></polyline>
179
+ </svg>
180
+ <div>
181
+ <button class="folder-button">
182
+ <span class="folder-title"></span>
183
+ </button>
184
+ </div>
185
+ </div>
186
+ <div class="folder-outer">
187
+ <ul class="content"></ul>
188
+ </div>
189
+ </li>
190
+ </template>
191
+ </div>
192
+ )
193
+ }
194
+
195
+ Explorer.css = style
196
+ Explorer.afterDOMLoaded = concatenateResources(script, overflowListAfterDOMLoaded)
197
+ return Explorer
198
+ }) satisfies QuartzComponentConstructor
@@ -0,0 +1,27 @@
1
+ import { QuartzComponent, QuartzComponentConstructor } from "./types"
2
+
3
+ interface Options {
4
+ links: Record<string, string>
5
+ }
6
+
7
+ export default ((opts?: Options) => {
8
+ const githubUrl = opts?.links?.GitHub
9
+
10
+ const Footer: QuartzComponent = () => (
11
+ <footer>
12
+ <p>
13
+ Second Brain
14
+ {githubUrl ? (
15
+ <>
16
+ {" "}
17
+ (<a href={githubUrl}>GitHub</a>)
18
+ </>
19
+ ) : null}{" "}
20
+ · © 2026 · notes for my future self · powered by{" "}
21
+ <a href="https://quartz.jzhao.xyz/">Quartz</a>
22
+ </p>
23
+ </footer>
24
+ )
25
+
26
+ return Footer
27
+ }) satisfies QuartzComponentConstructor