jekyll-theme-zer0 1.27.0 → 1.28.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +386 -4
- data/_data/consumers.yml +147 -0
- data/_data/features.yml +77 -5
- data/_data/i18n/fr.yml +98 -0
- data/_data/i18n/manifest.yml +1502 -0
- data/_data/navigation/docs.yml +2 -0
- data/_data/navigation/main.yml +6 -0
- data/_data/series.yml +19 -0
- data/_data/theme-manifest.yml +348 -266
- data/_includes/README.md +21 -2
- data/_includes/analytics/google-tag-manager-body.html +11 -2
- data/_includes/analytics/google-tag-manager-head.html +16 -6
- data/_includes/analytics/posthog.html +33 -4
- data/_includes/components/abc-letter.html +43 -0
- data/_includes/components/book-card.html +42 -0
- data/_includes/components/book-nav.html +80 -0
- data/_includes/components/book-plate.html +31 -0
- data/_includes/components/book-toc.html +47 -0
- data/_includes/components/bookshelf.html +68 -0
- data/_includes/components/card-grid.html +60 -0
- data/_includes/components/data-card.html +95 -0
- data/_includes/components/halfmoon.html +5 -1
- data/_includes/components/page-feedback.html +65 -2
- data/_includes/components/theme-controls-bar.html +10 -2
- data/_includes/components/theme-customizer.html +8 -2
- data/_includes/content/seo.html +9 -3
- data/_includes/core/color-mode-init.html +13 -4
- data/_includes/core/favicon.html +46 -0
- data/_includes/core/head.html +9 -0
- data/_includes/custom/body-end.html +18 -0
- data/_includes/custom/body-start.html +17 -0
- data/_includes/custom/footer.html +18 -0
- data/_includes/custom/head.html +18 -0
- data/_includes/navigation/local-graph.html +28 -2
- data/_includes/navigation/nav-tree.html +3 -3
- data/_includes/navigation/sidebar-config.html +21 -0
- data/_includes/navigation/sidebar-nav.html +4 -0
- data/_includes/navigation/sidebar-pagetree.html +150 -0
- data/_includes/navigation/unified-drawer.html +8 -2
- data/_includes/obsidian/full-graph.html +165 -136
- data/_layouts/404.html +260 -0
- data/_layouts/README.md +2 -0
- data/_layouts/book-abc.html +106 -0
- data/_layouts/book-story.html +91 -0
- data/_layouts/book.html +113 -0
- data/_layouts/collection.html +17 -5
- data/_layouts/home.html +5 -2
- data/_layouts/landing.html +9 -0
- data/_layouts/news.html +155 -31
- data/_layouts/root.html +16 -2
- data/_layouts/section.html +57 -15
- data/_sass/components/_book.scss +423 -0
- data/_sass/core/_navbar.scss +13 -0
- data/_sass/core/_obsidian.scss +286 -6
- data/_sass/theme/_backgrounds.scss +21 -8
- data/assets/css/main.scss +1 -0
- data/assets/js/auto-hide-nav.js +5 -1
- data/assets/js/halfmoon.js +26 -0
- data/assets/js/obsidian-graph.js +702 -264
- data/assets/js/obsidian-local-graph.js +161 -54
- data/assets/js/search-modal.js +4 -1
- data/scripts/bin/manifest +31 -4
- data/scripts/bin/validate +5 -1
- data/scripts/install/README.md +47 -6
- data/scripts/install/ai/client.sh +302 -93
- data/scripts/install/ai/prompts/spec.schema.json +1 -1
- data/scripts/install/ai/wizard.sh +10 -5
- data/scripts/install/apply.sh +7 -3
- data/scripts/install/cli.sh +54 -4
- data/scripts/install/config.sh +167 -0
- data/scripts/install/doctor.sh +38 -0
- data/scripts/install/plan.sh +10 -0
- data/scripts/install/spec.sh +15 -7
- data/scripts/install/template.sh +4 -0
- data/scripts/propagate.rb +277 -0
- data/scripts/translate.rb +90 -0
- metadata +27 -2
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Component: sidebar-pagetree
|
|
3
|
+
Path: _includes/navigation/sidebar-pagetree.html
|
|
4
|
+
Purpose: "pages" sidebar mode — a live URL-hierarchy tree of the site's
|
|
5
|
+
pages under a base path, grouped by their first path segment
|
|
6
|
+
into collapsible sections. Zero curated _data/navigation/*.yml
|
|
7
|
+
file needed: the tree is derived from page permalinks, so it
|
|
8
|
+
stays in sync with the content automatically.
|
|
9
|
+
|
|
10
|
+
Use it for flat page trees that aren't a Jekyll collection
|
|
11
|
+
(the "collection" mode covers collections). Example: a docs
|
|
12
|
+
area whose pages live at /docs/<section>/<page>/.
|
|
13
|
+
|
|
14
|
+
Params (include.* override the page.sidebar hash):
|
|
15
|
+
base (required) URL prefix to root the tree at, e.g.
|
|
16
|
+
"/docs/wargames/". Only pages whose url is under this
|
|
17
|
+
prefix are listed.
|
|
18
|
+
order_by (optional) front-matter key to sort each section's
|
|
19
|
+
pages by. Jekyll `sort` is numeric-aware, so a numeric
|
|
20
|
+
field like `nav_order` yields natural (0,1,2,…,10)
|
|
21
|
+
order. Default: url (path order).
|
|
22
|
+
expand (optional) true to expand every section (default:
|
|
23
|
+
only the section containing the current page).
|
|
24
|
+
Front matter (per page, all optional):
|
|
25
|
+
sidebar_exclude: true — hide this page from the tree.
|
|
26
|
+
sidebar_label: "…" — override the link text (else title).
|
|
27
|
+
Heading: title/icon resolved by sidebar-config.html (page → site).
|
|
28
|
+
Depends on: site.html_pages, Bootstrap 5 collapse, Bootstrap Icons, the
|
|
29
|
+
nav-tree styles (_sass/core/_nav-tree.scss). Emits the same
|
|
30
|
+
nav-tree markup as nav-tree.html / sidebar-folders.html so
|
|
31
|
+
active-state, collapse and styling behave identically.
|
|
32
|
+
{%- endcomment -%}
|
|
33
|
+
|
|
34
|
+
{%- assign _base = include.base | default: page.sidebar.base -%}
|
|
35
|
+
{%- if _base -%}
|
|
36
|
+
{%- assign _order_by = include.order_by | default: page.sidebar.order_by -%}
|
|
37
|
+
{%- assign _expand_all = page.sidebar.expand | default: false -%}
|
|
38
|
+
|
|
39
|
+
{%- comment -%} Pool = plain pages + every collection document, so the tree
|
|
40
|
+
works for flat pages (site.html_pages) AND collection content
|
|
41
|
+
(site.documents) alike. Then keep those under the base prefix. {%- endcomment -%}
|
|
42
|
+
{%- assign _pool = site.html_pages | concat: site.documents -%}
|
|
43
|
+
{%- assign _pages = _pool | where_exp: "p", "p.url contains _base" | where_exp: "p", "p.sidebar_exclude != true" -%}
|
|
44
|
+
{%- comment -%} The base index page (url == base) becomes a top-level link. {%- endcomment -%}
|
|
45
|
+
{%- assign _base_index = _pages | where: "url", _base | first -%}
|
|
46
|
+
{%- comment -%} Everything else groups by its first path segment after base. {%- endcomment -%}
|
|
47
|
+
{%- assign _rest = _pages | where_exp: "p", "p.url != _base" -%}
|
|
48
|
+
{%- assign _groups = _rest | group_by_exp: "p", "p.url | remove_first: _base | split: '/' | first" | sort: "name" -%}
|
|
49
|
+
|
|
50
|
+
<div class="sidebar-pagetree w-100">
|
|
51
|
+
<ul class="list-unstyled nav-tree-root mb-0">
|
|
52
|
+
{%- if _base_index -%}
|
|
53
|
+
<li class="nav-tree-item" data-depth="0">
|
|
54
|
+
<a href="{{ _base_index.url | relative_url }}"
|
|
55
|
+
class="nav-tree-link d-flex align-items-start rounded py-1 px-2 text-decoration-none{% if page.url == _base_index.url %} active{% endif %}"{% if page.url == _base_index.url %} aria-current="page"{% endif %}>
|
|
56
|
+
<i class="bi bi-collection me-2 flex-shrink-0" aria-hidden="true"></i>
|
|
57
|
+
<span>{{ _base_index.sidebar_label | default: _base_index.title | default: "Overview" }}</span>
|
|
58
|
+
</a>
|
|
59
|
+
</li>
|
|
60
|
+
{%- endif -%}
|
|
61
|
+
|
|
62
|
+
{%- for group in _groups -%}
|
|
63
|
+
{%- if group.name == "" or group.name == nil -%}{%- continue -%}{%- endif -%}
|
|
64
|
+
{%- assign _section = group.name -%}
|
|
65
|
+
{%- capture _section_url -%}{{ _base }}{{ _section }}/{%- endcapture -%}
|
|
66
|
+
{%- assign _group_id = _section | slugify | prepend: "sidebar-pt-" -%}
|
|
67
|
+
{%- assign _index = group.items | where: "url", _section_url | first -%}
|
|
68
|
+
{%- assign _children = group.items | where_exp: "p", "p.url != _section_url" -%}
|
|
69
|
+
{%- if _order_by -%}
|
|
70
|
+
{%- assign _children = _children | sort: _order_by -%}
|
|
71
|
+
{%- else -%}
|
|
72
|
+
{%- assign _children = _children | sort: "url" -%}
|
|
73
|
+
{%- endif -%}
|
|
74
|
+
{%- assign _child_count = _children | size -%}
|
|
75
|
+
|
|
76
|
+
{%- comment -%} Label the section from its URL segment (humanized), not the
|
|
77
|
+
index page's title — index titles are often generic ("Index") or
|
|
78
|
+
verbose. Mirrors sidebar-folders.html. A page can still override its
|
|
79
|
+
own child label via `sidebar_label`. {%- endcomment -%}
|
|
80
|
+
{%- assign _title = _section | replace: "-", " " | replace: "_", " " | capitalize -%}
|
|
81
|
+
{%- assign _active_here = group.items | where: "url", page.url | size -%}
|
|
82
|
+
{%- if _expand_all or _active_here > 0 -%}{%- assign _expanded = true -%}{%- else -%}{%- assign _expanded = false -%}{%- endif -%}
|
|
83
|
+
|
|
84
|
+
<li class="nav-tree-item sidebar-pagetree-group" data-depth="0">
|
|
85
|
+
{%- if _child_count == 0 and _index -%}
|
|
86
|
+
{%- comment -%} Section with only its index page — plain leaf, no toggle. {%- endcomment -%}
|
|
87
|
+
<a href="{{ _index.url | relative_url }}"
|
|
88
|
+
class="nav-tree-link d-flex align-items-start rounded py-1 px-2 text-decoration-none{% if page.url == _index.url %} active{% endif %}"{% if page.url == _index.url %} aria-current="page"{% endif %}>
|
|
89
|
+
<i class="bi bi-folder2 me-2 flex-shrink-0" aria-hidden="true"></i>
|
|
90
|
+
<span>{{ _title }}</span>
|
|
91
|
+
</a>
|
|
92
|
+
{%- else -%}
|
|
93
|
+
<div class="d-flex align-items-center">
|
|
94
|
+
{%- if _index -%}
|
|
95
|
+
<a href="{{ _index.url | relative_url }}"
|
|
96
|
+
class="nav-tree-link flex-grow-1 d-inline-flex align-items-center rounded py-1 px-2 text-decoration-none{% if page.url == _index.url %} active{% endif %}"{% if page.url == _index.url %} aria-current="page"{% endif %}>
|
|
97
|
+
<i class="bi bi-folder2 me-2" aria-hidden="true"></i>
|
|
98
|
+
<span>{{ _title }}</span>
|
|
99
|
+
</a>
|
|
100
|
+
<button class="btn btn-sm btn-link nav-tree-toggle p-1{% unless _expanded %} collapsed{% endunless %}"
|
|
101
|
+
type="button"
|
|
102
|
+
data-bs-toggle="collapse"
|
|
103
|
+
data-bs-target="#{{ _group_id }}"
|
|
104
|
+
aria-expanded="{{ _expanded }}"
|
|
105
|
+
aria-controls="{{ _group_id }}"
|
|
106
|
+
aria-label="Toggle {{ _title }} section">
|
|
107
|
+
<i class="bi bi-chevron-down nav-tree-chevron" aria-hidden="true"></i>
|
|
108
|
+
</button>
|
|
109
|
+
{%- else -%}
|
|
110
|
+
<button class="btn btn-link nav-tree-toggle w-100 d-flex align-items-center text-start py-1 px-2{% unless _expanded %} collapsed{% endunless %}"
|
|
111
|
+
type="button"
|
|
112
|
+
data-bs-toggle="collapse"
|
|
113
|
+
data-bs-target="#{{ _group_id }}"
|
|
114
|
+
aria-expanded="{{ _expanded }}"
|
|
115
|
+
aria-controls="{{ _group_id }}">
|
|
116
|
+
<i class="bi bi-folder2 me-2" aria-hidden="true"></i>
|
|
117
|
+
<span class="flex-grow-1">{{ _title }}</span>
|
|
118
|
+
<i class="bi bi-chevron-down nav-tree-chevron" aria-hidden="true"></i>
|
|
119
|
+
</button>
|
|
120
|
+
{%- endif -%}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="collapse{% if _expanded %} show{% endif %}" id="{{ _group_id }}">
|
|
124
|
+
<ul class="list-unstyled nav-tree-children mb-0">
|
|
125
|
+
{%- for pg in _children -%}
|
|
126
|
+
{%- assign _pg_label = pg.sidebar_label | default: pg.title -%}
|
|
127
|
+
{%- unless _pg_label -%}
|
|
128
|
+
{%- assign _pg_label = pg.url | split: "/" | last | replace: "-", " " | replace: "_", " " | capitalize -%}
|
|
129
|
+
{%- endunless -%}
|
|
130
|
+
<li class="nav-tree-item" data-depth="1">
|
|
131
|
+
<a href="{{ pg.url | relative_url }}"
|
|
132
|
+
class="nav-tree-link d-flex align-items-start rounded py-1 px-2 text-decoration-none{% if page.url == pg.url %} active{% endif %}"{% if page.url == pg.url %} aria-current="page"{% endif %}>
|
|
133
|
+
<i class="bi bi-file-earmark-text me-2 flex-shrink-0" aria-hidden="true"></i>
|
|
134
|
+
<span>{{ _pg_label }}</span>
|
|
135
|
+
</a>
|
|
136
|
+
</li>
|
|
137
|
+
{%- endfor -%}
|
|
138
|
+
</ul>
|
|
139
|
+
</div>
|
|
140
|
+
{%- endif -%}
|
|
141
|
+
</li>
|
|
142
|
+
{%- endfor -%}
|
|
143
|
+
</ul>
|
|
144
|
+
</div>
|
|
145
|
+
{%- else -%}
|
|
146
|
+
<p class="text-body-secondary small mb-0">
|
|
147
|
+
<i class="bi bi-info-circle me-1" aria-hidden="true"></i>
|
|
148
|
+
Set <code>sidebar.base</code> (a URL prefix) to enable the page tree.
|
|
149
|
+
</p>
|
|
150
|
+
{%- endif -%}
|
|
@@ -87,10 +87,16 @@
|
|
|
87
87
|
data-bs-target="#info-section">
|
|
88
88
|
<i class="bi bi-gear me-1" aria-hidden="true"></i>Theme & appearance
|
|
89
89
|
</button>
|
|
90
|
+
{%- comment -%}
|
|
91
|
+
Opened via the shared [data-search-toggle] binding in search-modal.js
|
|
92
|
+
(which also closes this drawer first). A raw data-bs-target here once
|
|
93
|
+
pointed at "#search-modal", but the modal's real id is
|
|
94
|
+
#siteSearchModal — the button was dead.
|
|
95
|
+
{%- endcomment -%}
|
|
90
96
|
<button type="button"
|
|
91
97
|
class="btn btn-outline-secondary w-100"
|
|
92
|
-
data-
|
|
93
|
-
|
|
98
|
+
data-search-toggle
|
|
99
|
+
aria-haspopup="dialog">
|
|
94
100
|
<i class="bi bi-search me-1" aria-hidden="true"></i>Search
|
|
95
101
|
</button>
|
|
96
102
|
</div>
|
|
@@ -1,158 +1,187 @@
|
|
|
1
1
|
{% comment %} Features: ZER0-044, ZER0-045 {% endcomment %}
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
inset 0 1px 0 rgba(255, 255, 255, .03);
|
|
26
|
-
}
|
|
27
|
-
#obsidian-graph-stats {
|
|
28
|
-
display: flex;
|
|
29
|
-
flex-wrap: wrap;
|
|
30
|
-
gap: .375rem;
|
|
31
|
-
}
|
|
32
|
-
#obsidian-graph-stats .badge {
|
|
33
|
-
font-size: .8125rem;
|
|
34
|
-
font-weight: 500;
|
|
35
|
-
padding: .35rem .6rem;
|
|
36
|
-
}
|
|
37
|
-
.obsidian-graph-toolbar {
|
|
38
|
-
display: flex;
|
|
39
|
-
flex-wrap: wrap;
|
|
40
|
-
gap: .5rem .75rem;
|
|
41
|
-
align-items: center;
|
|
42
|
-
margin: .75rem 0;
|
|
43
|
-
padding: .625rem .75rem;
|
|
44
|
-
background: var(--bs-tertiary-bg, #f8f9fa);
|
|
45
|
-
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
46
|
-
border-radius: var(--bs-border-radius, .375rem);
|
|
47
|
-
}
|
|
48
|
-
.obsidian-graph-toolbar .form-control {
|
|
49
|
-
max-width: 280px;
|
|
50
|
-
flex: 1 1 200px;
|
|
51
|
-
}
|
|
52
|
-
.obsidian-graph-toolbar .form-check {
|
|
53
|
-
margin-bottom: 0;
|
|
54
|
-
}
|
|
55
|
-
#obsidian-graph-status {
|
|
56
|
-
flex-basis: 100%;
|
|
57
|
-
font-size: .8125rem;
|
|
58
|
-
color: var(--bs-secondary-color, #6c757d);
|
|
59
|
-
min-height: 1.25rem;
|
|
60
|
-
}
|
|
61
|
-
.obsidian-graph-legend {
|
|
62
|
-
display: flex;
|
|
63
|
-
flex-wrap: wrap;
|
|
64
|
-
gap: .5rem;
|
|
65
|
-
margin-top: .75rem;
|
|
66
|
-
font-size: .8125rem;
|
|
67
|
-
}
|
|
68
|
-
.obsidian-graph-legend > span {
|
|
69
|
-
display: inline-flex;
|
|
70
|
-
align-items: center;
|
|
71
|
-
gap: .375rem;
|
|
72
|
-
padding: .25rem .55rem;
|
|
73
|
-
background: var(--bs-body-bg, #fff);
|
|
74
|
-
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
75
|
-
border-radius: 999px;
|
|
76
|
-
color: var(--bs-body-color, #212529);
|
|
77
|
-
}
|
|
78
|
-
.obsidian-graph-legend .swatch {
|
|
79
|
-
display: inline-block;
|
|
80
|
-
width: .7rem;
|
|
81
|
-
height: .7rem;
|
|
82
|
-
border-radius: 50%;
|
|
83
|
-
box-shadow: 0 0 0 1px rgba(0, 0, 0, .12);
|
|
84
|
-
opacity: 0.82;
|
|
85
|
-
}
|
|
86
|
-
.obsidian-graph-legend .swatch-broken {
|
|
87
|
-
border: 1.5px dashed #dc3545;
|
|
88
|
-
background: transparent !important;
|
|
89
|
-
box-shadow: none;
|
|
90
|
-
}
|
|
91
|
-
.obsidian-graph-tips {
|
|
92
|
-
margin-top: .75rem;
|
|
93
|
-
padding: .625rem .75rem;
|
|
94
|
-
font-size: .8125rem;
|
|
95
|
-
color: var(--bs-secondary-color, #6c757d);
|
|
96
|
-
background: var(--bs-tertiary-bg, #f8f9fa);
|
|
97
|
-
border-left: 3px solid var(--bs-primary, #0d6efd);
|
|
98
|
-
border-radius: .25rem;
|
|
99
|
-
}
|
|
100
|
-
.obsidian-graph-tips strong {
|
|
101
|
-
color: var(--bs-body-color, #212529);
|
|
102
|
-
}
|
|
103
|
-
</style>
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
===================================================================
|
|
4
|
+
FULL GRAPH PAGE — Obsidian-style site knowledge graph
|
|
5
|
+
===================================================================
|
|
6
|
+
|
|
7
|
+
File: full-graph.html
|
|
8
|
+
Path: _includes/obsidian/full-graph.html
|
|
9
|
+
Included from: pages/_docs/obsidian/graph.md
|
|
10
|
+
Renderer: assets/js/obsidian-graph.js (vendored cytoscape.js)
|
|
11
|
+
Styles: _sass/core/_obsidian.scss § "Full graph page"
|
|
12
|
+
|
|
13
|
+
Mirrors the Obsidian desktop graph controls:
|
|
14
|
+
- Filters: search (tag:/path: operators), per-collection toggles with
|
|
15
|
+
counts + color swatches (doubles as the legend), orphans, unresolved
|
|
16
|
+
- Display: arrows, node size, link thickness, label fade threshold
|
|
17
|
+
- Forces: repulsion, link distance, animated re-layout
|
|
18
|
+
Settings persist to localStorage (zer0.obsidianGraph.v1).
|
|
19
|
+
|
|
20
|
+
Element IDs are load-bearing: obsidian-graph, obsidian-graph-stats,
|
|
21
|
+
obsidian-graph-search, obsidian-graph-fit, obsidian-graph-orphans are
|
|
22
|
+
asserted by test/visual/features/obsidian-graph.spec.js.
|
|
23
|
+
===================================================================
|
|
24
|
+
{%- endcomment -%}
|
|
104
25
|
|
|
105
26
|
# Obsidian Graph View
|
|
106
27
|
|
|
107
28
|
A live, force-directed map of every page on this site and every
|
|
108
29
|
`[[wiki-link]]` between them. This is the rendered-site equivalent of
|
|
109
|
-
Obsidian's
|
|
30
|
+
Obsidian's graph view — built from the same
|
|
110
31
|
[`assets/data/wiki-index.json`]({{ "/assets/data/wiki-index.json" | relative_url }})
|
|
111
32
|
that powers the [client-side resolver]({{ "/docs/obsidian/syntax-reference/" | relative_url }})
|
|
112
33
|
and [backlinks panel]({{ "/docs/obsidian/syntax-reference/#backlinks-panel" | relative_url }}).
|
|
113
34
|
|
|
114
35
|
<div id="obsidian-graph-stats" class="mb-2" aria-live="polite"></div>
|
|
115
36
|
|
|
116
|
-
<div class="obsidian-graph-
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
37
|
+
<div class="obsidian-graph-shell" id="obsidian-graph-shell">
|
|
38
|
+
<div id="obsidian-graph" role="group" aria-label="Site knowledge graph"></div>
|
|
39
|
+
<div class="obsidian-graph-controls d-print-none" id="obsidian-graph-controls">
|
|
40
|
+
<button type="button"
|
|
41
|
+
class="btn btn-sm obsidian-graph-controls-toggle"
|
|
42
|
+
data-bs-toggle="collapse"
|
|
43
|
+
data-bs-target="#obsidianGraphControlsBody"
|
|
44
|
+
aria-expanded="false"
|
|
45
|
+
aria-controls="obsidianGraphControlsBody">
|
|
46
|
+
<i class="bi bi-sliders me-1" aria-hidden="true"></i>Graph settings
|
|
47
|
+
<i class="bi bi-chevron-down ms-auto obsidian-graph-controls-chevron" aria-hidden="true"></i>
|
|
48
|
+
</button>
|
|
49
|
+
<div class="collapse obsidian-graph-controls-body" id="obsidianGraphControlsBody">
|
|
50
|
+
<div class="accordion accordion-flush" id="obsidianGraphAccordion">
|
|
51
|
+
<div class="accordion-item">
|
|
52
|
+
<h2 class="accordion-header">
|
|
53
|
+
<button class="accordion-button py-2" type="button"
|
|
54
|
+
data-bs-toggle="collapse" data-bs-target="#obsidianGraphFilters"
|
|
55
|
+
aria-expanded="true" aria-controls="obsidianGraphFilters">
|
|
56
|
+
<i class="bi bi-funnel me-2" aria-hidden="true"></i>Filters
|
|
57
|
+
</button>
|
|
58
|
+
</h2>
|
|
59
|
+
<div id="obsidianGraphFilters" class="accordion-collapse collapse show"
|
|
60
|
+
data-bs-parent="#obsidianGraphAccordion">
|
|
61
|
+
<div class="accordion-body py-2">
|
|
62
|
+
<input type="search"
|
|
63
|
+
class="form-control form-control-sm mb-2"
|
|
64
|
+
id="obsidian-graph-search"
|
|
65
|
+
placeholder="Search… (tag:foo, path:docs)"
|
|
66
|
+
aria-label="Filter graph nodes by title, tag: or path: operator" />
|
|
67
|
+
<div id="obsidian-graph-collections"
|
|
68
|
+
class="obsidian-graph-collections mb-2"
|
|
69
|
+
role="group"
|
|
70
|
+
aria-label="Toggle collections"></div>
|
|
71
|
+
<div class="form-check form-switch">
|
|
72
|
+
<input class="form-check-input" type="checkbox" role="switch"
|
|
73
|
+
id="obsidian-graph-orphans" />
|
|
74
|
+
<label class="form-check-label" for="obsidian-graph-orphans">Show orphans</label>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="form-check form-switch">
|
|
77
|
+
<input class="form-check-input" type="checkbox" role="switch"
|
|
78
|
+
id="obsidian-graph-broken" checked />
|
|
79
|
+
<label class="form-check-label" for="obsidian-graph-broken">Show unresolved links</label>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="accordion-item">
|
|
85
|
+
<h2 class="accordion-header">
|
|
86
|
+
<button class="accordion-button collapsed py-2" type="button"
|
|
87
|
+
data-bs-toggle="collapse" data-bs-target="#obsidianGraphDisplay"
|
|
88
|
+
aria-expanded="false" aria-controls="obsidianGraphDisplay">
|
|
89
|
+
<i class="bi bi-eye me-2" aria-hidden="true"></i>Display
|
|
90
|
+
</button>
|
|
91
|
+
</h2>
|
|
92
|
+
<div id="obsidianGraphDisplay" class="accordion-collapse collapse"
|
|
93
|
+
data-bs-parent="#obsidianGraphAccordion">
|
|
94
|
+
<div class="accordion-body py-2">
|
|
95
|
+
<div class="form-check form-switch mb-2">
|
|
96
|
+
<input class="form-check-input" type="checkbox" role="switch"
|
|
97
|
+
id="obsidian-graph-arrows" checked />
|
|
98
|
+
<label class="form-check-label" for="obsidian-graph-arrows">Link arrows</label>
|
|
99
|
+
</div>
|
|
100
|
+
<label class="form-label small mb-1" for="obsidian-graph-node-size">Node size</label>
|
|
101
|
+
<input type="range" class="form-range" id="obsidian-graph-node-size"
|
|
102
|
+
min="0.5" max="2" step="0.05" value="1" />
|
|
103
|
+
<label class="form-label small mb-1" for="obsidian-graph-link-width">Link thickness</label>
|
|
104
|
+
<input type="range" class="form-range" id="obsidian-graph-link-width"
|
|
105
|
+
min="0.5" max="3" step="0.1" value="1" />
|
|
106
|
+
<label class="form-label small mb-1" for="obsidian-graph-label-zoom">Label fade threshold</label>
|
|
107
|
+
<input type="range" class="form-range" id="obsidian-graph-label-zoom"
|
|
108
|
+
min="0.4" max="2.5" step="0.05" value="1.25"
|
|
109
|
+
aria-describedby="obsidian-graph-label-zoom-help" />
|
|
110
|
+
<p class="small text-secondary mb-0" id="obsidian-graph-label-zoom-help">
|
|
111
|
+
Lower shows labels sooner while zooming in.
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="accordion-item">
|
|
117
|
+
<h2 class="accordion-header">
|
|
118
|
+
<button class="accordion-button collapsed py-2" type="button"
|
|
119
|
+
data-bs-toggle="collapse" data-bs-target="#obsidianGraphForces"
|
|
120
|
+
aria-expanded="false" aria-controls="obsidianGraphForces">
|
|
121
|
+
<i class="bi bi-magnet me-2" aria-hidden="true"></i>Forces
|
|
122
|
+
</button>
|
|
123
|
+
</h2>
|
|
124
|
+
<div id="obsidianGraphForces" class="accordion-collapse collapse"
|
|
125
|
+
data-bs-parent="#obsidianGraphAccordion">
|
|
126
|
+
<div class="accordion-body py-2">
|
|
127
|
+
<label class="form-label small mb-1" for="obsidian-graph-repulsion">Repel force</label>
|
|
128
|
+
<input type="range" class="form-range" id="obsidian-graph-repulsion"
|
|
129
|
+
min="0.3" max="3" step="0.1" value="1" />
|
|
130
|
+
<label class="form-label small mb-1" for="obsidian-graph-link-distance">Link distance</label>
|
|
131
|
+
<input type="range" class="form-range" id="obsidian-graph-link-distance"
|
|
132
|
+
min="0.4" max="2.5" step="0.1" value="1" />
|
|
133
|
+
<div class="form-check form-switch mb-0">
|
|
134
|
+
<input class="form-check-input" type="checkbox" role="switch"
|
|
135
|
+
id="obsidian-graph-animate" checked />
|
|
136
|
+
<label class="form-check-label" for="obsidian-graph-animate">Animate layout</label>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="d-flex gap-2 p-2 border-top">
|
|
143
|
+
<button type="button" class="btn btn-outline-primary btn-sm flex-grow-1"
|
|
144
|
+
id="obsidian-graph-relayout">
|
|
145
|
+
<i class="bi bi-arrow-clockwise me-1" aria-hidden="true"></i>Re-run layout
|
|
146
|
+
</button>
|
|
147
|
+
<button type="button" class="btn btn-outline-secondary btn-sm"
|
|
148
|
+
id="obsidian-graph-reset"
|
|
149
|
+
title="Reset all graph settings to defaults">
|
|
150
|
+
Reset
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
136
154
|
</div>
|
|
137
|
-
<
|
|
155
|
+
<div class="obsidian-graph-zoombar btn-group-vertical d-print-none" role="group" aria-label="Graph zoom controls">
|
|
156
|
+
<button type="button" class="btn btn-sm" id="obsidian-graph-zoom-in"
|
|
157
|
+
aria-label="Zoom in" title="Zoom in">
|
|
158
|
+
<i class="bi bi-plus-lg" aria-hidden="true"></i>
|
|
159
|
+
</button>
|
|
160
|
+
<button type="button" class="btn btn-sm" id="obsidian-graph-zoom-out"
|
|
161
|
+
aria-label="Zoom out" title="Zoom out">
|
|
162
|
+
<i class="bi bi-dash-lg" aria-hidden="true"></i>
|
|
163
|
+
</button>
|
|
164
|
+
<button type="button" class="btn btn-sm" id="obsidian-graph-fit"
|
|
165
|
+
aria-label="Reset view" title="Reset view (clears search)">
|
|
166
|
+
<i class="bi bi-aspect-ratio" aria-hidden="true"></i>
|
|
167
|
+
</button>
|
|
168
|
+
<button type="button" class="btn btn-sm" id="obsidian-graph-fullscreen"
|
|
169
|
+
aria-label="Toggle fullscreen" title="Toggle fullscreen" aria-pressed="false">
|
|
170
|
+
<i class="bi bi-arrows-fullscreen" aria-hidden="true"></i>
|
|
171
|
+
</button>
|
|
172
|
+
</div>
|
|
173
|
+
<div id="obsidian-graph-preview" class="card shadow-sm obsidian-graph-preview" hidden aria-hidden="true"></div>
|
|
138
174
|
</div>
|
|
139
175
|
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
<div class="obsidian-graph-legend" aria-label="Graph legend">
|
|
143
|
-
<span><span class="swatch" style="background:#0d6efd"></span>Posts</span>
|
|
144
|
-
<span><span class="swatch" style="background:#198754"></span>Docs</span>
|
|
145
|
-
<span><span class="swatch" style="background:#6f42c1"></span>Notes</span>
|
|
146
|
-
<span><span class="swatch" style="background:#d63384"></span>Notebooks</span>
|
|
147
|
-
<span><span class="swatch" style="background:#fd7e14"></span>Quickstart</span>
|
|
148
|
-
<span><span class="swatch" style="background:#6c757d"></span>Pages</span>
|
|
149
|
-
<span><span class="swatch swatch-broken"></span>Broken links</span>
|
|
150
|
-
</div>
|
|
176
|
+
<p id="obsidian-graph-status" class="obsidian-graph-status" role="status"></p>
|
|
151
177
|
|
|
152
178
|
<div class="obsidian-graph-tips">
|
|
153
179
|
<strong>Tips:</strong> click a node to open the page · ⌘/Ctrl-click to
|
|
154
180
|
open in a new tab · drag to reposition · scroll to zoom · hover to
|
|
155
|
-
|
|
181
|
+
preview a page and highlight its neighborhood · search supports
|
|
182
|
+
<code>tag:foo</code> and <code>path:docs</code> operators · the
|
|
183
|
+
<em>Graph settings</em> panel filters collections and tunes display
|
|
184
|
+
& forces (persisted in your browser).
|
|
156
185
|
</div>
|
|
157
186
|
|
|
158
187
|
## How it's built
|