secondbrain-py 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brain/__init__.py +0 -0
- brain/__main__.py +18 -0
- brain/_capture_command.py +445 -0
- brain/_compose.py +52 -0
- brain/activity.py +206 -0
- brain/ask.py +631 -0
- brain/audio.py +591 -0
- brain/backfill/__init__.py +12 -0
- brain/backfill/search_extras.py +141 -0
- brain/backfill/source_rows.py +101 -0
- brain/bin/__init__.py +1 -0
- brain/bin/_launcher.py +107 -0
- brain/bin/down.py +8 -0
- brain/bin/launchd.py +268 -0
- brain/bin/monitor.py +570 -0
- brain/bin/rebuild.py +8 -0
- brain/bin/status.py +8 -0
- brain/bin/up.py +8 -0
- brain/brief.py +272 -0
- brain/capture.py +49 -0
- brain/chat.py +293 -0
- brain/cli.py +9760 -0
- brain/cli_claude.py +81 -0
- brain/cli_connect.py +285 -0
- brain/cli_demo.py +266 -0
- brain/config.py +1949 -0
- brain/connect.py +925 -0
- brain/db.py +540 -0
- brain/demo/__init__.py +452 -0
- brain/demo/corpus/manifest.json +403 -0
- brain/demo/embedder.py +74 -0
- brain/durations.py +84 -0
- brain/edit_session.py +156 -0
- brain/editor.py +67 -0
- brain/elicit/__init__.py +16 -0
- brain/elicit/detectors.py +250 -0
- brain/elicit/drafter.py +70 -0
- brain/elicit/queue.py +220 -0
- brain/elicit/schema.py +48 -0
- brain/elicit/session.py +445 -0
- brain/embedding_targets.py +54 -0
- brain/embeddings.py +424 -0
- brain/enrichment.py +808 -0
- brain/errors.py +357 -0
- brain/eval/__init__.py +129 -0
- brain/eval/answer_eval.py +281 -0
- brain/eval/baseline.py +265 -0
- brain/eval/concept_extraction.py +378 -0
- brain/eval/corpus.py +152 -0
- brain/eval/errors.py +19 -0
- brain/eval/graph_baseline.py +226 -0
- brain/eval/graph_retrieval.py +202 -0
- brain/eval/graph_runner.py +319 -0
- brain/eval/metrics.py +101 -0
- brain/eval/runner.py +223 -0
- brain/format.py +783 -0
- brain/gaps.py +390 -0
- brain/graph_rag/__init__.py +94 -0
- brain/graph_rag/_retrieval_common.py +113 -0
- brain/graph_rag/aggregates.py +303 -0
- brain/graph_rag/aliases/__init__.py +583 -0
- brain/graph_rag/backends/__init__.py +10 -0
- brain/graph_rag/backends/_age_helpers.py +473 -0
- brain/graph_rag/backends/age.py +782 -0
- brain/graph_rag/backends/base.py +272 -0
- brain/graph_rag/build.py +344 -0
- brain/graph_rag/communities.py +644 -0
- brain/graph_rag/communities_summary.py +437 -0
- brain/graph_rag/concepts.py +202 -0
- brain/graph_rag/cooccur.py +193 -0
- brain/graph_rag/cross_type.py +312 -0
- brain/graph_rag/extract.py +885 -0
- brain/graph_rag/fuse.py +371 -0
- brain/graph_rag/global_.py +412 -0
- brain/graph_rag/grouping.py +372 -0
- brain/graph_rag/person_resolver.py +167 -0
- brain/graph_rag/reconcile.py +792 -0
- brain/graph_rag/relational.py +353 -0
- brain/graph_rag/retrieve.py +526 -0
- brain/graph_rag/router.py +288 -0
- brain/graph_rag/schema.py +320 -0
- brain/graph_rag/sync.py +237 -0
- brain/graph_rag/tenancy.py +43 -0
- brain/graph_rag/themes.py +501 -0
- brain/graph_rag/weighting.py +202 -0
- brain/ingest/__init__.py +1926 -0
- brain/ingest/chunker.py +249 -0
- brain/ingest/docx.py +40 -0
- brain/ingest/gmail.py +621 -0
- brain/ingest/markdown.py +37 -0
- brain/ingest/pdf.py +61 -0
- brain/ingest/stdin.py +22 -0
- brain/ingest/sub_tokens.py +91 -0
- brain/ingest/text.py +16 -0
- brain/interactions.py +205 -0
- brain/maintenance.py +355 -0
- brain/mcp_server.py +3405 -0
- brain/migrations/001_init.sql +43 -0
- brain/migrations/002_qwen3_embedding.sql +17 -0
- brain/migrations/003_vault_model.sql +41 -0
- brain/migrations/004_relax_content_hash_uniqueness.sql +18 -0
- brain/migrations/005_derived_links.sql +67 -0
- brain/migrations/006_dedup_file_by_source_path.sql +25 -0
- brain/migrations/007_email_thread_and_draft.sql +15 -0
- brain/migrations/008_gmail_thread_unique.sql +11 -0
- brain/migrations/009_chunks_weighted_tsv.sql +28 -0
- brain/migrations/010_interactions.sql +30 -0
- brain/migrations/011_documents_summary.sql +23 -0
- brain/migrations/012_graphrag.sql +171 -0
- brain/migrations/013_graphrag_communities.sql +125 -0
- brain/migrations/014_graphrag_community_summary_hash.sql +33 -0
- brain/migrations/015_interactions_graph_targets.sql +89 -0
- brain/migrations/016_index_hygiene.sql +61 -0
- brain/migrations/017_elicit.sql +30 -0
- brain/migrations/018_review_gap_signal_kinds.sql +40 -0
- brain/migrations/019_search_queries.sql +35 -0
- brain/migrations/020_link_suggestions.sql +40 -0
- brain/migrations/021_timeline_doc_date.sql +34 -0
- brain/migrations/022_link_suggestions_undirected.sql +84 -0
- brain/migrations/023_search_queries_fts_count.sql +28 -0
- brain/quartz_overrides/__init__.py +8 -0
- brain/quartz_overrides/quartz/bootstrap-cli.mjs +65 -0
- brain/quartz_overrides/quartz/build.ts +568 -0
- brain/quartz_overrides/quartz/cli/args.js +152 -0
- brain/quartz_overrides/quartz/cli/build_partial_handler.js +544 -0
- brain/quartz_overrides/quartz/cli/handlers.js +636 -0
- brain/quartz_overrides/quartz/components/CommandPalette.tsx +172 -0
- brain/quartz_overrides/quartz/components/Explorer.tsx +198 -0
- brain/quartz_overrides/quartz/components/Footer.tsx +27 -0
- brain/quartz_overrides/quartz/components/Graph.tsx +468 -0
- brain/quartz_overrides/quartz/components/PageTitle.tsx +72 -0
- brain/quartz_overrides/quartz/components/RelatedDocs.tsx +38 -0
- brain/quartz_overrides/quartz/components/Search.tsx +161 -0
- brain/quartz_overrides/quartz/components/SummaryLede.tsx +72 -0
- brain/quartz_overrides/quartz/components/index.ts +92 -0
- brain/quartz_overrides/quartz/components/pages/TagContent.tsx +272 -0
- brain/quartz_overrides/quartz/components/scripts/commandPalette.inline.ts +665 -0
- brain/quartz_overrides/quartz/components/scripts/explorer.inline.ts +768 -0
- brain/quartz_overrides/quartz/components/scripts/graph.inline.ts +2302 -0
- brain/quartz_overrides/quartz/components/scripts/relatedDocs.inline.ts +163 -0
- brain/quartz_overrides/quartz/components/scripts/search.inline.ts +1011 -0
- brain/quartz_overrides/quartz/plugins/emitters/contentIndex.ts +546 -0
- brain/quartz_overrides/quartz/plugins/transformers/codeCopy.ts +94 -0
- brain/quartz_overrides/quartz/plugins/transformers/derivedFenceMark.ts +302 -0
- brain/quartz_overrides/quartz/plugins/transformers/emailThread.ts +148 -0
- brain/quartz_overrides/quartz/plugins/transformers/emptyDoorFilter.ts +213 -0
- brain/quartz_overrides/quartz/plugins/transformers/index.ts +114 -0
- brain/quartz_overrides/quartz/plugins/transformers/linkKindMark.ts +205 -0
- brain/quartz_overrides/quartz/plugins/transformers/linkSourceTag.ts +104 -0
- brain/quartz_overrides/quartz/plugins/transformers/relativeDate.ts +100 -0
- brain/quartz_overrides/quartz/plugins/transformers/reloadSignal.ts +131 -0
- brain/quartz_overrides/quartz/processors/parse.ts +371 -0
- brain/quartz_overrides/quartz/processors/parser_cache.ts +78 -0
- brain/quartz_overrides/quartz/static/brain-logo-dark.png +0 -0
- brain/quartz_overrides/quartz/static/brain-logo-light.png +0 -0
- brain/quartz_overrides/quartz/static/codeCopy.js +196 -0
- brain/quartz_overrides/quartz/static/emailThread.js +334 -0
- brain/quartz_overrides/quartz/static/favicon.ico +0 -0
- brain/quartz_overrides/quartz/static/icon.png +0 -0
- brain/quartz_overrides/quartz/static/linkSourceTag.js +104 -0
- brain/quartz_overrides/quartz/static/relativeDate.js +142 -0
- brain/quartz_overrides/quartz/static/reload.js +168 -0
- brain/quartz_overrides/quartz/styles/brain/_article.scss +252 -0
- brain/quartz_overrides/quartz/styles/brain/_atmosphere.scss +113 -0
- brain/quartz_overrides/quartz/styles/brain/_callouts.scss +180 -0
- brain/quartz_overrides/quartz/styles/brain/_cmdk.scss +7 -0
- brain/quartz_overrides/quartz/styles/brain/_code.scss +208 -0
- brain/quartz_overrides/quartz/styles/brain/_command_palette.scss +369 -0
- brain/quartz_overrides/quartz/styles/brain/_email_thread.scss +228 -0
- brain/quartz_overrides/quartz/styles/brain/_explorer.scss +142 -0
- brain/quartz_overrides/quartz/styles/brain/_home.scss +182 -0
- brain/quartz_overrides/quartz/styles/brain/_links.scss +322 -0
- brain/quartz_overrides/quartz/styles/brain/_marginalia.scss +117 -0
- brain/quartz_overrides/quartz/styles/brain/_motion.scss +175 -0
- brain/quartz_overrides/quartz/styles/brain/_people_hub.scss +100 -0
- brain/quartz_overrides/quartz/styles/brain/_related_docs.scss +137 -0
- brain/quartz_overrides/quartz/styles/brain/_search.scss +252 -0
- brain/quartz_overrides/quartz/styles/brain/_sidebar.scss +468 -0
- brain/quartz_overrides/quartz/styles/brain/_summary_lede.scss +56 -0
- brain/quartz_overrides/quartz/styles/brain/_surface.scss +43 -0
- brain/quartz_overrides/quartz/styles/brain/_tag_content.scss +118 -0
- brain/quartz_overrides/quartz/styles/brain/_tokens.scss +197 -0
- brain/quartz_overrides/quartz/styles/brain/_typography.scss +92 -0
- brain/quartz_overrides/quartz/styles/custom.scss +89 -0
- brain/quartz_overrides/quartz/styles/graph.scss +505 -0
- brain/quartz_overrides/quartz/util/ctx.ts +92 -0
- brain/quartz_overrides/quartz/util/fastpath_manifest.ts +608 -0
- brain/quartz_overrides/quartz/util/path.ts +358 -0
- brain/quartz_overrides/quartz/util/sourceIcons.ts +55 -0
- brain/quartz_overrides/quartz.config.ts +270 -0
- brain/quartz_overrides/quartz.layout.ts +314 -0
- brain/queries.py +1188 -0
- brain/rank_fusion.py +8 -0
- brain/resurface.py +210 -0
- brain/review/__init__.py +26 -0
- brain/review/emit.py +27 -0
- brain/review/queries.py +436 -0
- brain/review/render.py +196 -0
- brain/review/scans.py +355 -0
- brain/review/weekly.py +413 -0
- brain/search.py +704 -0
- brain/set_similarity.py +15 -0
- brain/setup.py +1205 -0
- brain/tags.py +56 -0
- brain/templates/Caddyfile.j2 +9 -0
- brain/templates/__init__.py +1 -0
- brain/templates/bin/__init__.py +1 -0
- brain/templates/bin/_brain-brief-fg.sh +25 -0
- brain/templates/bin/_brain-build-fg.sh +53 -0
- brain/templates/bin/_brain-watcher-fg.sh +65 -0
- brain/templates/bin/brain-down.sh +89 -0
- brain/templates/bin/brain-status.sh +83 -0
- brain/templates/bin/brain-up.sh +221 -0
- brain/templates/docker/age/Dockerfile +79 -0
- brain/templates/docker-compose.stock.yml.j2 +26 -0
- brain/templates/docker-compose.yml.j2 +34 -0
- brain/templates/env.example +190 -0
- brain/templates/launchd/__init__.py +1 -0
- brain/templates/launchd/com.brain.brief.plist.j2 +45 -0
- brain/templates/launchd/com.brain.build.plist.j2 +46 -0
- brain/templates/launchd/com.brain.watcher.plist.j2 +46 -0
- brain/templates/skill/SKILL.md +63 -0
- brain/templates/skill/__init__.py +1 -0
- brain/timeline.py +834 -0
- brain/todo.py +124 -0
- brain/uninstall.py +185 -0
- brain/vault/__init__.py +115 -0
- brain/vault/_atomic.py +25 -0
- brain/vault/daily_index.py +228 -0
- brain/vault/derived_links/__init__.py +50 -0
- brain/vault/derived_links/directory.py +683 -0
- brain/vault/derived_links/fence.py +408 -0
- brain/vault/derived_links/gws.py +64 -0
- brain/vault/derived_links/participants.py +143 -0
- brain/vault/derived_links/pass_runner.py +362 -0
- brain/vault/derived_links/rules.py +137 -0
- brain/vault/export.py +683 -0
- brain/vault/frontmatter.py +165 -0
- brain/vault/graph.py +620 -0
- brain/vault/graph_format.py +388 -0
- brain/vault/link_rewrite.py +235 -0
- brain/vault/links.py +260 -0
- brain/vault/note_builder.py +211 -0
- brain/vault/paths.py +55 -0
- brain/vault/quartz_overlay.py +236 -0
- brain/vault/rename.py +591 -0
- brain/vault/resolver.py +304 -0
- brain/vault/slug.py +127 -0
- brain/vault/sync.py +1513 -0
- brain/vault/sync_summaries.py +264 -0
- brain/vault/templates.py +145 -0
- brain/vault/watch.py +1052 -0
- brain/wiki/__init__.py +6 -0
- brain/wiki/_github_slugger.py +76 -0
- brain/wiki/_person_name.py +314 -0
- brain/wiki/build_homepage.py +541 -0
- brain/wiki/build_partial.py +273 -0
- brain/wiki/build_people.py +934 -0
- brain/wiki/build_related.py +758 -0
- brain/wiki/build_swap.py +585 -0
- brain/wiki/build_watcher.py +975 -0
- brain/wiki/edit_classifier.py +215 -0
- brain/wiki/errors.py +10 -0
- brain/wiki/fastpath_manifest.py +475 -0
- brain/wiki/fastpath_state.py +174 -0
- brain/wiki/install.py +296 -0
- brain/wiki/slug.py +111 -0
- secondbrain_py-0.2.1.dist-info/METADATA +195 -0
- secondbrain_py-0.2.1.dist-info/RECORD +273 -0
- secondbrain_py-0.2.1.dist-info/WHEEL +5 -0
- secondbrain_py-0.2.1.dist-info/entry_points.txt +11 -0
- secondbrain_py-0.2.1.dist-info/licenses/LICENSE +21 -0
- secondbrain_py-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Brain Explorer extras — P4.2 of the Wiki UX Overhaul.
|
|
2
|
+
//
|
|
3
|
+
// This file is installed at
|
|
4
|
+
// `<vault>/.quartz/quartz/styles/brain/_explorer.scss` by `brain vault
|
|
5
|
+
// render --overlay` and pulled into the global SCSS via `custom.scss`'s
|
|
6
|
+
// `@use "./brain/explorer"` line. Loaded after `_sidebar.scss` so any
|
|
7
|
+
// sticky-header / surface-tier rules in the sidebar partial paint
|
|
8
|
+
// underneath these explorer-content children.
|
|
9
|
+
//
|
|
10
|
+
// Scope of this file:
|
|
11
|
+
//
|
|
12
|
+
// * `.brain-explorer-ingested-toggle` — the "Show ingested" /
|
|
13
|
+
// "Hide ingested" pill that the inline script injects as the
|
|
14
|
+
// first child of `.explorer-content`. Drives state via
|
|
15
|
+
// `[data-active="true"]` (set by the inline script on toggle).
|
|
16
|
+
// * `.brain-explorer-month-header` — month-group header `<li>`
|
|
17
|
+
// injected by the P4.3 `buildMonthGroupedChildren` pass when
|
|
18
|
+
// descending into `_ingested/krisp/` or `_ingested/gmail/`.
|
|
19
|
+
// * `.brain-explorer-month-date` / `.brain-explorer-month-title` —
|
|
20
|
+
// the two-span structure inside each grouped file's link, so
|
|
21
|
+
// the date prefix can render in dimmed tabular numerals while
|
|
22
|
+
// the title keeps the regular ink color.
|
|
23
|
+
//
|
|
24
|
+
// Scope NOT covered here:
|
|
25
|
+
//
|
|
26
|
+
// * Folder-count badges, explorer indent guides, sticky search
|
|
27
|
+
// header — owned by `_sidebar.scss` (Lane C).
|
|
28
|
+
// * The `<ul class="explorer-ul">` tree itself — owned by upstream
|
|
29
|
+
// `quartz/components/styles/explorer.scss`.
|
|
30
|
+
//
|
|
31
|
+
// Tokens consumed (all from `_tokens.scss`):
|
|
32
|
+
//
|
|
33
|
+
// * `--surface-1` — toggle background when inactive
|
|
34
|
+
// * `--accent` — toggle bg when active
|
|
35
|
+
// * `--rule` — toggle border
|
|
36
|
+
// * `--ink-1` / `--ink-2` — text colors
|
|
37
|
+
// * `--motion-fast` — hover transition
|
|
38
|
+
|
|
39
|
+
.explorer .brain-explorer-ingested-toggle {
|
|
40
|
+
// Layout — full-width pill button so it reads as a control rail
|
|
41
|
+
// rather than a tag-pill. Sits above the tree but inside the
|
|
42
|
+
// collapsible explorer-content region.
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
width: 100%;
|
|
47
|
+
margin: 0 0 0.5rem 0;
|
|
48
|
+
padding: 0.3rem 0.6rem;
|
|
49
|
+
font: inherit;
|
|
50
|
+
font-size: 0.75rem;
|
|
51
|
+
letter-spacing: 0.04em;
|
|
52
|
+
// brain: lowercase by default so the chip reads "show ingested"
|
|
53
|
+
// not "SHOW INGESTED" — matches the P3.4 lowercase-tag contract.
|
|
54
|
+
text-transform: none;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
background: transparent;
|
|
57
|
+
color: var(--ink-2, var(--gray));
|
|
58
|
+
border: 1px solid var(--rule, var(--lightgray));
|
|
59
|
+
border-radius: 999px;
|
|
60
|
+
transition:
|
|
61
|
+
background var(--motion-fast, 120ms) ease,
|
|
62
|
+
color var(--motion-fast, 120ms) ease,
|
|
63
|
+
border-color var(--motion-fast, 120ms) ease;
|
|
64
|
+
|
|
65
|
+
&:hover,
|
|
66
|
+
&:focus-visible {
|
|
67
|
+
background: var(--accent-soft, var(--highlight));
|
|
68
|
+
color: var(--ink-1, var(--darkgray));
|
|
69
|
+
border-color: var(--accent, var(--secondary));
|
|
70
|
+
outline: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Active state — toggle ON, ingested visible. Reverse fg/bg so
|
|
74
|
+
// the active read matches the pattern used by the P3.2 search
|
|
75
|
+
// chips and the graph-view filter chips.
|
|
76
|
+
&[data-active="true"] {
|
|
77
|
+
background: var(--accent, var(--secondary));
|
|
78
|
+
color: var(--light, white);
|
|
79
|
+
border-color: var(--accent, var(--secondary));
|
|
80
|
+
|
|
81
|
+
&:hover,
|
|
82
|
+
&:focus-visible {
|
|
83
|
+
// Slight darken on hover so the active chip still gives
|
|
84
|
+
// pressed-state feedback.
|
|
85
|
+
background: color-mix(in srgb, var(--accent, var(--secondary)) 85%, black);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------
|
|
91
|
+
// P4.3 — Per-source month grouping inside `_ingested/{krisp,gmail}/`.
|
|
92
|
+
// The inline script (`buildMonthGroupedChildren`) injects a header
|
|
93
|
+
// `<li>` per month plus rewrites each file's link text into a
|
|
94
|
+
// `<span.brain-explorer-month-date> · <span.brain-explorer-month-title>`
|
|
95
|
+
// pair. Styling lives here so the upstream `explorer.scss` partial
|
|
96
|
+
// stays unforked.
|
|
97
|
+
// ---------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
.explorer .brain-explorer-month-header {
|
|
100
|
+
// Appearance — dimmed small-caps section heading that reads as a
|
|
101
|
+
// separator rather than as a navigable row. No hover/active state
|
|
102
|
+
// because the header is non-interactive (`role="presentation"`).
|
|
103
|
+
list-style: none;
|
|
104
|
+
margin: 0.6rem 0 0.15rem 0;
|
|
105
|
+
padding: 0.1rem 0;
|
|
106
|
+
font-family: var(--bodyFont, inherit);
|
|
107
|
+
font-size: 0.7rem;
|
|
108
|
+
letter-spacing: 0.06em;
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
color: var(--ink-3, var(--gray));
|
|
111
|
+
// Subtle hairline below the label so the eye picks up the section
|
|
112
|
+
// boundary without competing with the file rows.
|
|
113
|
+
border-bottom: 1px solid var(--rule, var(--lightgray));
|
|
114
|
+
// First header in a folder-outer flush against the top — drop the
|
|
115
|
+
// top margin so it doesn't collide with the parent folder's
|
|
116
|
+
// padding/indent.
|
|
117
|
+
&:first-child {
|
|
118
|
+
margin-top: 0.1rem;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.explorer .brain-explorer-month-date {
|
|
123
|
+
// Tabular numerals so `Mar 9` / `Apr 15` line up vertically when
|
|
124
|
+
// stacked. Lighter weight + dimmed color so the date prefix feels
|
|
125
|
+
// like marginalia, not the primary read.
|
|
126
|
+
display: inline-block;
|
|
127
|
+
min-width: 2.6rem;
|
|
128
|
+
margin-right: 0.25rem;
|
|
129
|
+
font-variant-numeric: tabular-nums;
|
|
130
|
+
font-feature-settings: "tnum" 1;
|
|
131
|
+
font-size: 0.78rem;
|
|
132
|
+
color: var(--ink-3, var(--gray));
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.explorer .brain-explorer-month-title {
|
|
137
|
+
// The title carries the regular ink color so it remains the primary
|
|
138
|
+
// read in the row. No layout overrides — let the link's own padding
|
|
139
|
+
// and ellipsis-truncation rules from the upstream `explorer.scss`
|
|
140
|
+
// continue to apply.
|
|
141
|
+
color: inherit;
|
|
142
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Brain home page card grid — Lane C of the 2026 commonplace-book redesign.
|
|
2
|
+
//
|
|
3
|
+
// This file is installed at
|
|
4
|
+
// `<vault>/.quartz/quartz/styles/brain/_home.scss` by `brain vault
|
|
5
|
+
// render --overlay` and pulled into the global SCSS via `custom.scss`'s
|
|
6
|
+
// `@use "./brain/home"` line.
|
|
7
|
+
//
|
|
8
|
+
// Scope of this file:
|
|
9
|
+
//
|
|
10
|
+
// * Detect the home page via `body[data-slug="index"]` (Quartz
|
|
11
|
+
// stamps the slug onto `<body>` from `renderPage.tsx`).
|
|
12
|
+
// * Inside the article body's top-level `<ul>` lists, render the
|
|
13
|
+
// direct child `<li>` items as a responsive card grid.
|
|
14
|
+
// * Cards: `--surface-1` background, `--shadow-md` on hover, accent
|
|
15
|
+
// border on hover, generous padding.
|
|
16
|
+
// * Wiki + ingested links INSIDE a card opt out of Lane B's
|
|
17
|
+
// underline + box-shadow source rail — the card itself IS the
|
|
18
|
+
// affordance, so the inner link should read as plain text.
|
|
19
|
+
//
|
|
20
|
+
// CSS-only — no markdown change. Degrades to a vertical list cleanly
|
|
21
|
+
// when the assumed `<ul>` shape isn't present (for a future home doc
|
|
22
|
+
// that uses different markup).
|
|
23
|
+
//
|
|
24
|
+
// Tokens consumed (from `_tokens.scss`):
|
|
25
|
+
// * `--surface-1` — card background
|
|
26
|
+
// * `--shadow-sm` / `--shadow-md` — resting + hover elevation
|
|
27
|
+
// * `--rule` — card border (resting)
|
|
28
|
+
// * `--accent-strong` — card border on hover
|
|
29
|
+
// * `--ink-0` / `--ink-1` / `--ink-2` — text + supporting copy
|
|
30
|
+
// * `--motion-fast` / `--motion-mid` / `--motion-easing` — transitions
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// 1. Card grid layout — only on the home page
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
//
|
|
36
|
+
// The home `index.md` is structured as `<h2>` section headings followed
|
|
37
|
+
// by `<ul>` lists where each `<li>` contains a wiki/ingested link plus
|
|
38
|
+
// a description (`<a>...</a> — text`). We turn each top-level `<ul>`
|
|
39
|
+
// into a CSS grid, with each `<li>` becoming a card.
|
|
40
|
+
//
|
|
41
|
+
// Why not target h2-anchored sections individually: h2 + ul is a
|
|
42
|
+
// repeated pattern (Doors, Topic clusters, etc.), and they all share
|
|
43
|
+
// the same card treatment. Repainting every `<ul>` inside the article
|
|
44
|
+
// is simpler and stays consistent if more sections land later.
|
|
45
|
+
//
|
|
46
|
+
// Why CSS-only: the plan flagged this explicitly — markdown stays
|
|
47
|
+
// human-edited and unsurprising; no markdown rewrite, no JS dependency.
|
|
48
|
+
// The grid degrades to a vertical list (the default `<ul>` rendering)
|
|
49
|
+
// in any browser that ignores `display: grid`.
|
|
50
|
+
|
|
51
|
+
body[data-slug="index"] {
|
|
52
|
+
article.popover-hint > ul,
|
|
53
|
+
// brain: also catch nested .popover-hint wrappers (Quartz's article
|
|
54
|
+
// body is `<article class="popover-hint">`, but we name both forms
|
|
55
|
+
// for resilience against future renderPage.tsx changes).
|
|
56
|
+
article > ul {
|
|
57
|
+
list-style: none;
|
|
58
|
+
padding: 0;
|
|
59
|
+
margin: 0.6rem 0 1.6rem 0;
|
|
60
|
+
|
|
61
|
+
// Responsive grid: 3 columns at desktop, 2 at tablet, 1 below.
|
|
62
|
+
// `auto-fit` + `minmax(240px, 1fr)` would also work but explicit
|
|
63
|
+
// breakpoints match the plan's "3/2/1" spec exactly and read
|
|
64
|
+
// clearly in devtools.
|
|
65
|
+
display: grid;
|
|
66
|
+
gap: 0.8rem;
|
|
67
|
+
grid-template-columns: 1fr;
|
|
68
|
+
|
|
69
|
+
@media (min-width: 768px) {
|
|
70
|
+
grid-template-columns: repeat(2, 1fr);
|
|
71
|
+
}
|
|
72
|
+
@media (min-width: 1024px) {
|
|
73
|
+
grid-template-columns: repeat(3, 1fr);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Each list item becomes a card.
|
|
77
|
+
> li {
|
|
78
|
+
box-sizing: border-box;
|
|
79
|
+
position: relative;
|
|
80
|
+
background: var(--surface-1);
|
|
81
|
+
border: 1px solid var(--rule);
|
|
82
|
+
border-radius: 8px;
|
|
83
|
+
padding: 1.2rem;
|
|
84
|
+
box-shadow: var(--shadow-sm);
|
|
85
|
+
color: var(--ink-1);
|
|
86
|
+
line-height: 1.5;
|
|
87
|
+
list-style: none;
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
transition:
|
|
90
|
+
transform var(--motion-mid) var(--motion-easing),
|
|
91
|
+
box-shadow var(--motion-mid) var(--motion-easing),
|
|
92
|
+
border-color var(--motion-mid) var(--motion-easing);
|
|
93
|
+
|
|
94
|
+
&:hover {
|
|
95
|
+
transform: translateY(-2px);
|
|
96
|
+
box-shadow: var(--shadow-md);
|
|
97
|
+
border-color: var(--accent-strong);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The leading wiki/ingested link inside the card reads as the
|
|
101
|
+
// card title — promote its visual weight.
|
|
102
|
+
//
|
|
103
|
+
// Stretched-link pattern: the leading `<a>` paints an invisible
|
|
104
|
+
// `::before` overlay across the entire card so a click anywhere
|
|
105
|
+
// in the card follows the link. Markdown stays as `<li>emoji
|
|
106
|
+
// <a>title</a> — desc</li>` (no markup change), and the link's
|
|
107
|
+
// accessible name still comes from its text. Tradeoff: text
|
|
108
|
+
// selection inside the card requires shift-click (same as
|
|
109
|
+
// Bootstrap's `.stretched-link`); acceptable here because the
|
|
110
|
+
// card's primary affordance is "navigate", not "read & copy".
|
|
111
|
+
> a:first-child {
|
|
112
|
+
display: inline;
|
|
113
|
+
font-family: var(--displayFont);
|
|
114
|
+
font-size: var(--fs-md);
|
|
115
|
+
font-weight: 600;
|
|
116
|
+
color: var(--ink-0);
|
|
117
|
+
margin-right: 0.25em;
|
|
118
|
+
|
|
119
|
+
&::before {
|
|
120
|
+
content: "";
|
|
121
|
+
position: absolute;
|
|
122
|
+
inset: 0;
|
|
123
|
+
border-radius: inherit;
|
|
124
|
+
z-index: 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Tag chips inside a card stay independently clickable — float
|
|
129
|
+
// them above the stretched-link overlay so the chip's own click
|
|
130
|
+
// target wins over the card-wide link.
|
|
131
|
+
a.tag-link {
|
|
132
|
+
position: relative;
|
|
133
|
+
z-index: 2;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// 2. Opt cards out of Lane B link-kind chrome
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
//
|
|
143
|
+
// Lane B's `_links.scss` paints `[data-brain-link-kind="wiki"]` with a
|
|
144
|
+
// terracotta underline + the `a.internal` reset, and
|
|
145
|
+
// `[data-brain-link-kind="ingested"]` with the source-tinted left
|
|
146
|
+
// rail. Inside a card, both treatments compete with the card's own
|
|
147
|
+
// hover affordance — the card lifts, that's the affordance, the
|
|
148
|
+
// link should read as plain card title text.
|
|
149
|
+
//
|
|
150
|
+
// Higher specificity than Lane B (adds the `body[data-slug="index"]`
|
|
151
|
+
// + `> li > a` chain) so this override wins without `!important`.
|
|
152
|
+
//
|
|
153
|
+
// brain (Lane C audit fix 2026-05-02): exclude `.tag-link` from the
|
|
154
|
+
// reset. Tag chips (rendered by `Component.TagList` and inline
|
|
155
|
+
// `[[tags/x]]` wiki-links) carry both `class="internal"` AND
|
|
156
|
+
// `class="tag-link"`. Without the `:not(.tag-link)` filter the
|
|
157
|
+
// reset blew away Lane B's pill chrome (background, border-radius,
|
|
158
|
+
// padding) — leaving a bare-text "tag" in the card. The chip *is*
|
|
159
|
+
// the affordance, the card lift is complementary; both should
|
|
160
|
+
// survive.
|
|
161
|
+
|
|
162
|
+
body[data-slug="index"] article ul > li > a[data-brain-link-kind="wiki"]:not(.tag-link),
|
|
163
|
+
body[data-slug="index"] article ul > li > a[data-brain-link-kind="ingested"]:not(.tag-link),
|
|
164
|
+
body[data-slug="index"] article ul > li > a.internal:not(.tag-link) {
|
|
165
|
+
text-decoration: none;
|
|
166
|
+
background: transparent;
|
|
167
|
+
border-radius: 0;
|
|
168
|
+
padding: 0;
|
|
169
|
+
// Source-rail box-shadow reset for ingested kind.
|
|
170
|
+
box-shadow: none;
|
|
171
|
+
|
|
172
|
+
&:hover {
|
|
173
|
+
text-decoration-thickness: 0;
|
|
174
|
+
box-shadow: none;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Tag-link chips inside cards stay as chips (the chip *is* the
|
|
179
|
+
// affordance, and the card's hover lift complements rather than
|
|
180
|
+
// competes with the chip's own micro-interaction). The
|
|
181
|
+
// `:not(.tag-link)` filter on the reset above is what preserves
|
|
182
|
+
// them — without it Lane B's pill chrome would be wiped.
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// Brain wiki — link type system styles (Lane B, the headline complaint).
|
|
2
|
+
//
|
|
3
|
+
// This file is installed at
|
|
4
|
+
// `<vault>/.quartz/quartz/styles/brain/_links.scss` by `brain vault
|
|
5
|
+
// render --overlay` and pulled into the global SCSS via
|
|
6
|
+
// `custom.scss`'s `@use "./brain/links"` line.
|
|
7
|
+
//
|
|
8
|
+
// Five distinct visual treatments — one per link kind. The
|
|
9
|
+
// `data-brain-link-kind` attribute is stamped at build time by the
|
|
10
|
+
// `linkKindMark.ts` transformer, which keys off the URL pattern
|
|
11
|
+
// (and the `data-brain-derived` attribute already stamped by
|
|
12
|
+
// `derivedFenceMark.ts` for evidence edges). For ingested links we
|
|
13
|
+
// also read `data-brain-source` (krisp/slack/gmail/manual), which is
|
|
14
|
+
// stamped at runtime by `linkSourceTag.js` because the source segment
|
|
15
|
+
// lives in the URL path but extracting it at build time would require
|
|
16
|
+
// a per-doc lookup.
|
|
17
|
+
//
|
|
18
|
+
// Token contract — every color, motion timing, and surface here is
|
|
19
|
+
// pulled from `_tokens.scss` so a future palette swap propagates
|
|
20
|
+
// automatically. Tokens consumed:
|
|
21
|
+
//
|
|
22
|
+
// * `--accent-soft`, `--accent-strong` — wiki underline + tag-chip hover
|
|
23
|
+
// * `--ink-2` — external dotted underline + derived dashed underline
|
|
24
|
+
// * `--surface-2` — tag-chip resting background
|
|
25
|
+
// * `--motion-fast`, `--motion-easing` — universal hover transitions
|
|
26
|
+
// * `--brain-source-{krisp,slack,gmail,manual}` — ingested left-rail color
|
|
27
|
+
//
|
|
28
|
+
// Underline pattern — uses `text-decoration-thickness` +
|
|
29
|
+
// `text-underline-offset` (2020+ CSS) instead of `border-bottom` so:
|
|
30
|
+
// * Descenders (`p`, `g`, `y`, `j`) aren't clipped by the rule.
|
|
31
|
+
// * Underline thickness scales with `font-size` automatically.
|
|
32
|
+
// * The underline can be transitioned (`text-decoration-thickness`
|
|
33
|
+
// is animatable in modern browsers; `border-bottom-width` is too,
|
|
34
|
+
// but border-bottom adds layout-shift risk via the box model).
|
|
35
|
+
//
|
|
36
|
+
// Selector strategy — every rule keys off `[data-brain-link-kind=...]`
|
|
37
|
+
// rather than `.internal` / `.external` / `.tag-link` so the styling
|
|
38
|
+
// is decoupled from Quartz's internal class naming. Stock Quartz
|
|
39
|
+
// classes survive (we don't strip them) but they aren't used as
|
|
40
|
+
// selectors here.
|
|
41
|
+
//
|
|
42
|
+
// brain a11y note: screen readers (NVDA / JAWS / VoiceOver / Chromium-
|
|
43
|
+
// derived) WILL announce the "↗" content of the external-link
|
|
44
|
+
// `::after` rule — CSS pseudo-element `content` is NOT auto-aria-
|
|
45
|
+
// hidden, contrary to the common myth. Stock Quartz's SVG icon had
|
|
46
|
+
// `aria-hidden="true"` for this reason; once we disable it (in
|
|
47
|
+
// `quartz.config.ts` via `externalLinkIcon: false`, to avoid double-
|
|
48
|
+
// arrow rendering with the `::after` glyph), the announcement
|
|
49
|
+
// returns. Accepted tradeoff — the alternative is reverting to
|
|
50
|
+
// stock's SVG and losing CSS color/opacity/transition control over
|
|
51
|
+
// the indicator. Revisit if a11y feedback surfaces.
|
|
52
|
+
|
|
53
|
+
// Feature flag for the universal visited-state desaturation. Default
|
|
54
|
+
// on; flip to `false` if testing reveals the saturate(0.7) filter
|
|
55
|
+
// muddies the design or interacts poorly with a future color tweak.
|
|
56
|
+
// `!default` means a downstream `@use "./brain/links" with (...)`
|
|
57
|
+
// can override without editing this file.
|
|
58
|
+
$enable-visited-desaturation: true !default;
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// 1. wiki — internal links to other vault notes (the most common kind)
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
//
|
|
64
|
+
// Subtle terracotta underline with a small offset so descenders
|
|
65
|
+
// breathe. Hover thickens the underline and shifts to the full-
|
|
66
|
+
// strength accent — a tactile-feeling micro-interaction that signals
|
|
67
|
+
// "this is clickable" without shouting.
|
|
68
|
+
a[data-brain-link-kind="wiki"] {
|
|
69
|
+
// brain: stock Quartz `quartz/styles/base.scss` paints `a.internal`
|
|
70
|
+
// with `background-color: var(--highlight)`, `padding: 0 0.1rem`, and
|
|
71
|
+
// `border-radius: 5px` — a chunky 12%-alpha terracotta pill. Lane B's
|
|
72
|
+
// wiki kind has equal specificity (0,1,1) but doesn't redeclare those
|
|
73
|
+
// three properties, so the pill bleeds through. Reset them explicitly
|
|
74
|
+
// here so the redesign's subtle underline reads as intended.
|
|
75
|
+
background: transparent;
|
|
76
|
+
border-radius: 0;
|
|
77
|
+
padding: 0;
|
|
78
|
+
text-decoration: underline;
|
|
79
|
+
text-decoration-color: var(--accent-soft);
|
|
80
|
+
text-decoration-thickness: 1px;
|
|
81
|
+
text-underline-offset: 0.18em;
|
|
82
|
+
// brain: `text-decoration-skip-ink: auto` is the browser default in
|
|
83
|
+
// modern engines but spell it out so descenders are explicitly
|
|
84
|
+
// skipped (no underline crossing through `g`/`y`/`p`).
|
|
85
|
+
text-decoration-skip-ink: auto;
|
|
86
|
+
transition:
|
|
87
|
+
text-decoration-thickness var(--motion-fast) var(--motion-easing),
|
|
88
|
+
text-decoration-color var(--motion-fast) var(--motion-easing);
|
|
89
|
+
|
|
90
|
+
&:hover {
|
|
91
|
+
text-decoration-thickness: 2px;
|
|
92
|
+
text-decoration-color: var(--accent-strong);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// 2. external — http(s)/mailto links that leave the vault
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
//
|
|
100
|
+
// Dotted underline in the muted ink color signals "different
|
|
101
|
+
// destination" without competing with wiki links for attention.
|
|
102
|
+
// The trailing `↗` arrow is the universal "leaves this site"
|
|
103
|
+
// affordance (Wikipedia, MDN, GOV.UK conventions).
|
|
104
|
+
a[data-brain-link-kind="external"] {
|
|
105
|
+
text-decoration: underline;
|
|
106
|
+
text-decoration-style: dotted;
|
|
107
|
+
text-decoration-color: var(--ink-2);
|
|
108
|
+
text-decoration-thickness: 1px;
|
|
109
|
+
text-underline-offset: 0.18em;
|
|
110
|
+
text-decoration-skip-ink: auto;
|
|
111
|
+
transition:
|
|
112
|
+
text-decoration-thickness var(--motion-fast) var(--motion-easing),
|
|
113
|
+
text-decoration-color var(--motion-fast) var(--motion-easing);
|
|
114
|
+
|
|
115
|
+
&:hover {
|
|
116
|
+
text-decoration-thickness: 2px;
|
|
117
|
+
text-decoration-color: var(--accent-strong);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// brain: external-link arrow indicator. Inline-baseline alignment
|
|
121
|
+
// keeps it riding on the text baseline; `font-size: 0.85em` shrinks
|
|
122
|
+
// it just enough to read as an icon, not a glyph.
|
|
123
|
+
&::after {
|
|
124
|
+
content: "↗";
|
|
125
|
+
font-size: 0.85em;
|
|
126
|
+
margin-left: 0.15em;
|
|
127
|
+
opacity: 0.7;
|
|
128
|
+
vertical-align: baseline;
|
|
129
|
+
// Don't underline the arrow itself (looks weird).
|
|
130
|
+
text-decoration: none;
|
|
131
|
+
display: inline-block;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// 3. tag — `#tag` and `[[tags/x]]` links rendered as pill chips
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
//
|
|
139
|
+
// Pill-shape chip with a leading `#`, small-caps tracking. Hover
|
|
140
|
+
// lifts the chip 1px and tints the background — the chip becomes a
|
|
141
|
+
// small button. `inline-flex` aligns the chip's baseline with
|
|
142
|
+
// surrounding running text without needing magic margins.
|
|
143
|
+
//
|
|
144
|
+
// Selector covers BOTH:
|
|
145
|
+
// * `[data-brain-link-kind="tag"]` — stamped by `linkKindMark.ts`
|
|
146
|
+
// for tag links the markdown pipeline produces (e.g. `#tag`
|
|
147
|
+
// inline syntax via OFM, or `[[tags/foo]]` wiki-link form).
|
|
148
|
+
// * `.tag-link` — Quartz's stock class on tag chips emitted by the
|
|
149
|
+
// `TagList` component (article header) and the `TagPage` emitter
|
|
150
|
+
// (tag page chips). These are JSX/hast nodes, not mdast, so
|
|
151
|
+
// `linkKindMark` never sees them — we lean on the upstream class
|
|
152
|
+
// for them. Listed second so the data-attribute is the canonical
|
|
153
|
+
// selector when both are present (they're equivalent rules; the
|
|
154
|
+
// comma form just widens reach).
|
|
155
|
+
a[data-brain-link-kind="tag"],
|
|
156
|
+
a.tag-link {
|
|
157
|
+
display: inline-flex;
|
|
158
|
+
align-items: baseline;
|
|
159
|
+
padding: 0.1rem 0.55rem;
|
|
160
|
+
background: var(--surface-2);
|
|
161
|
+
border-radius: 999px;
|
|
162
|
+
// brain (Linear variant): drop `smcp` — Geist doesn't ship the
|
|
163
|
+
// small-caps OpenType feature, so the declaration is a silent no-op.
|
|
164
|
+
// Lining numerals + slight letter-spacing keep the chip readable
|
|
165
|
+
// without uppercasing the text.
|
|
166
|
+
//
|
|
167
|
+
// brain (P3.4 — Wiki UX Overhaul, item 10): `text-transform: none`
|
|
168
|
+
// (explicit) — tags are the canonical lowercased values produced by
|
|
169
|
+
// `brain.tags.normalize_tags` (casefold + hyphen-separated). The
|
|
170
|
+
// earlier `uppercase` rule made every chip render `#AGENTIC-AI`,
|
|
171
|
+
// which contradicted the storage format and made the visual feel
|
|
172
|
+
// shouty. Locking `text-transform: none` here also defends against
|
|
173
|
+
// a future upstream Quartz default that might flip the default to
|
|
174
|
+
// uppercase. The DOM text from `TagList.tsx` is already lowercase,
|
|
175
|
+
// so this rule is the single seam between normalized storage and
|
|
176
|
+
// the user-visible chip.
|
|
177
|
+
font-feature-settings: "lnum";
|
|
178
|
+
text-transform: none;
|
|
179
|
+
letter-spacing: 0.04em;
|
|
180
|
+
text-decoration: none;
|
|
181
|
+
// brain: vertical-align nudge so the chip sits on the text baseline
|
|
182
|
+
// even with the inline padding. Without this, the pill rides ~2px
|
|
183
|
+
// below the baseline when wrapped inside running serif text.
|
|
184
|
+
vertical-align: baseline;
|
|
185
|
+
transition:
|
|
186
|
+
background var(--motion-fast) var(--motion-easing),
|
|
187
|
+
transform var(--motion-fast) var(--motion-easing),
|
|
188
|
+
color var(--motion-fast) var(--motion-easing);
|
|
189
|
+
|
|
190
|
+
// brain: leading `#` — opacity 0.5 so it reads as decoration, not
|
|
191
|
+
// content. `margin-right: 0.1em` gives it just enough breathing room
|
|
192
|
+
// from the tag name.
|
|
193
|
+
&::before {
|
|
194
|
+
content: "#";
|
|
195
|
+
opacity: 0.5;
|
|
196
|
+
margin-right: 0.1em;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
&:hover {
|
|
200
|
+
background: var(--accent-soft);
|
|
201
|
+
transform: translateY(-1px);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// 4. derived — Phase D evidence edges (auto-generated "Related" links)
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
//
|
|
209
|
+
// Italic + dashed underline + opacity 0.78 says "this is a hint, not
|
|
210
|
+
// an authored claim." `cursor: help` reinforces the pattern (browsers
|
|
211
|
+
// render `?` cursor; users learn that hovering reveals the rule).
|
|
212
|
+
// The `title` attribute is already populated by `derivedFenceMark.ts`
|
|
213
|
+
// with the rule shape (`same_day_participant`, `shared_thread`).
|
|
214
|
+
a[data-brain-link-kind="derived"] {
|
|
215
|
+
text-decoration: underline;
|
|
216
|
+
text-decoration-style: dashed;
|
|
217
|
+
text-decoration-color: var(--ink-2);
|
|
218
|
+
text-decoration-thickness: 1px;
|
|
219
|
+
text-underline-offset: 0.18em;
|
|
220
|
+
text-decoration-skip-ink: auto;
|
|
221
|
+
font-style: italic;
|
|
222
|
+
opacity: 0.78;
|
|
223
|
+
cursor: help;
|
|
224
|
+
transition:
|
|
225
|
+
opacity var(--motion-fast) var(--motion-easing),
|
|
226
|
+
text-decoration-color var(--motion-fast) var(--motion-easing);
|
|
227
|
+
|
|
228
|
+
&:hover {
|
|
229
|
+
opacity: 1;
|
|
230
|
+
text-decoration-color: var(--accent-strong);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// 5. ingested — vault-internal links into `_ingested/<source>/<slug>`
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
//
|
|
238
|
+
// Inherits the wiki underline pattern (terracotta thin → thick on
|
|
239
|
+
// hover) and adds a 2px source-tinted left rail via `box-shadow`
|
|
240
|
+
// (NOT `border-left` — `border-left` adds 2px to the box width and
|
|
241
|
+
// causes layout shift on the long form below; `inset` `box-shadow`
|
|
242
|
+
// paints inside the existing box so widths stay consistent).
|
|
243
|
+
//
|
|
244
|
+
// Source-color comes from a CSS custom property (`--source-color`)
|
|
245
|
+
// scoped per-link via the `[data-brain-source="..."]` selectors
|
|
246
|
+
// below. Default fallback is `--brain-source-manual` so any
|
|
247
|
+
// uncategorized ingested link still gets a rail.
|
|
248
|
+
a[data-brain-link-kind="ingested"] {
|
|
249
|
+
// brain: same reset as the wiki block — stock `a.internal` from
|
|
250
|
+
// `base.scss` paints a chunky 12%-alpha pill (background + padding +
|
|
251
|
+
// border-radius) that would mix with the source-tinted rail. Wipe
|
|
252
|
+
// background/border-radius and rebuild padding from scratch so the
|
|
253
|
+
// 0.35em rail offset is the only horizontal padding present.
|
|
254
|
+
background: transparent;
|
|
255
|
+
border-radius: 0;
|
|
256
|
+
// Padding rebuilt: zero on top/right/bottom (match wiki kind),
|
|
257
|
+
// 0.35em on the left to keep text off the source rail.
|
|
258
|
+
padding: 0 0 0 0.35em;
|
|
259
|
+
// Inherit wiki underline behaviour. SCSS doesn't have `extend`
|
|
260
|
+
// across the `[attr=value]` selector cleanly, so re-declare the
|
|
261
|
+
// five properties (kept in sync with the wiki block above by code
|
|
262
|
+
// review).
|
|
263
|
+
text-decoration: underline;
|
|
264
|
+
text-decoration-color: var(--accent-soft);
|
|
265
|
+
text-decoration-thickness: 1px;
|
|
266
|
+
text-underline-offset: 0.18em;
|
|
267
|
+
text-decoration-skip-ink: auto;
|
|
268
|
+
// The source-tinted rail. `inset` keeps it inside the box; the
|
|
269
|
+
// 2px width matches the wiki-link hover thickness so the visual
|
|
270
|
+
// weight is consistent between the two related kinds.
|
|
271
|
+
box-shadow: inset 2px 0 0 var(--source-color, var(--brain-source-manual));
|
|
272
|
+
transition:
|
|
273
|
+
text-decoration-thickness var(--motion-fast) var(--motion-easing),
|
|
274
|
+
text-decoration-color var(--motion-fast) var(--motion-easing),
|
|
275
|
+
box-shadow var(--motion-fast) var(--motion-easing);
|
|
276
|
+
|
|
277
|
+
&:hover {
|
|
278
|
+
text-decoration-thickness: 2px;
|
|
279
|
+
text-decoration-color: var(--accent-strong);
|
|
280
|
+
// Thicken the source rail on hover too — same "tactile" cue as
|
|
281
|
+
// the wiki underline grow.
|
|
282
|
+
box-shadow: inset 3px 0 0 var(--source-color, var(--brain-source-manual));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Source palette mappings. Each rule sets `--source-color` to the
|
|
286
|
+
// matching `--brain-source-*` token from `_tokens.scss`. Both light
|
|
287
|
+
// and dark mode `--brain-source-*` definitions live in `_tokens.scss`,
|
|
288
|
+
// so no per-mode override is needed here.
|
|
289
|
+
&[data-brain-source="krisp"] {
|
|
290
|
+
--source-color: var(--brain-source-krisp);
|
|
291
|
+
}
|
|
292
|
+
&[data-brain-source="slack"] {
|
|
293
|
+
--source-color: var(--brain-source-slack);
|
|
294
|
+
}
|
|
295
|
+
&[data-brain-source="gmail"] {
|
|
296
|
+
--source-color: var(--brain-source-gmail);
|
|
297
|
+
}
|
|
298
|
+
&[data-brain-source="manual"] {
|
|
299
|
+
--source-color: var(--brain-source-manual);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
// Universal: visited state
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
//
|
|
307
|
+
// `filter: saturate(0.7)` desaturates the link color slightly without
|
|
308
|
+
// overriding the kind-specific styling above (which uses
|
|
309
|
+
// `text-decoration-color`, not `color`, so a `color` override would
|
|
310
|
+
// be invisible anyway). Applies to every kind including chips and
|
|
311
|
+
// dashed derived links — visited state should read consistently
|
|
312
|
+
// across the whole link family.
|
|
313
|
+
//
|
|
314
|
+
// Gated by the SCSS variable at the top so it can be turned off from
|
|
315
|
+
// `custom.scss` via `@use "./brain/links" with ($enable-visited-desaturation: false)`
|
|
316
|
+
// if it ever clashes with another visual choice.
|
|
317
|
+
@if $enable-visited-desaturation {
|
|
318
|
+
a[data-brain-link-kind]:visited,
|
|
319
|
+
a.tag-link:visited {
|
|
320
|
+
filter: saturate(0.7);
|
|
321
|
+
}
|
|
322
|
+
}
|