Skip to content

Commit 0631279

Browse files
committed
'update'
1 parent 9de163e commit 0631279

File tree

5 files changed

+387
-5
lines changed

5 files changed

+387
-5
lines changed

src/HHH.md

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
---
2+
recommend: ⭐⭐⭐⭐⭐
3+
--githubUrl: "https://github.com/ChenZhu-Xie/xczphysics_SilverBullet/blob/main/STYLE/Theme/HHH.md"
4+
udpateDate: 2025-10-28
5+
---rawUrl: "https://raw.githubusercontent.com/ChenZhu-Xie/xczphysics_SilverBullet/main/STYLE/Theme/HHH.md"
6+
updateDate: "2025-11-01T13:24:02Z"
7+
lastCommitDate: "2025-10-31T09:45:25Z"
8+
---
9+
10+
# HierarchyHighlightHeadings - HHH Theme
11+
12+
## JS Part (Experimental)
13+
14+
### Step 1. Reload your space to load the space-lua from this page: ${widgets.commandButton("System: Reload")}
15+
16+
### Step 2. Save Library/PanelDragResize.js using this button: ${widgets.commandButton("Save: HierarchyHighlightHeadings.js")}
17+
18+
### Step 3. System Reload: ${widgets.commandButton("System: Reload")}
19+
20+
### Step 4. Reload UI: ${widgets.commandButton("Client: Reload UI")}
21+
22+
1. borrowed `JS inject` from [[CONFIG/View/Tree/Float]]
23+
2. https://community.silverbullet.md/t/hhh-hierarchyhighlightheadings-theme/3467
24+
25+
```space-lua
26+
local jsCode = [[
27+
const STATE_KEY = "__xhHighlightState_v2";
28+
29+
function getLevel(el) {
30+
for (let i = 1; i <= 6; i++) {
31+
if (el.classList && el.classList.contains(`sb-line-h${i}`)) return i;
32+
}
33+
const tag = el.tagName ? el.tagName.toLowerCase() : "";
34+
if (/^h[1-6]$/.test(tag)) return Number(tag[1]);
35+
return 0;
36+
}
37+
38+
function pickGroupRoot(start, container, groupSelector) {
39+
if (!groupSelector) return container;
40+
const g = start.closest(groupSelector);
41+
return g || container;
42+
}
43+
44+
function listHeadings(root, headingSelector) {
45+
return Array.from(root.querySelectorAll(headingSelector));
46+
}
47+
48+
function collectDescendants(startIndex, headings, startLevel) {
49+
const res = [];
50+
for (let i = startIndex + 1; i < headings.length; i++) {
51+
const lvl = getLevel(headings[i]);
52+
if (lvl <= startLevel) break;
53+
res.push(headings[i]);
54+
}
55+
return res;
56+
}
57+
58+
function collectAncestors(startIndex, headings, startLevel) {
59+
const res = [];
60+
let minLevel = startLevel;
61+
for (let i = startIndex - 1; i >= 0; i--) {
62+
const lvl = getLevel(headings[i]);
63+
if (lvl < minLevel) {
64+
res.push(headings[i]);
65+
minLevel = lvl;
66+
if (minLevel === 1) break;
67+
}
68+
}
69+
return res;
70+
}
71+
72+
function clearClasses(root) {
73+
root.querySelectorAll(".sb-active, .sb-active-anc, .sb-active-desc, .sb-active-current")
74+
.forEach(el => el.classList.remove("sb-active", "sb-active-anc", "sb-active-desc", "sb-active-current"));
75+
}
76+
77+
export function enableHighlight(opts = {}) {
78+
const containerSelector = opts.containerSelector || "#sb-main";
79+
const headingSelector = opts.headingSelector ||
80+
"h1, h2, h3, h4, h5, h6, .sb-line-h1, .sb-line-h2, .sb-line-h3, .sb-line-h4, .sb-line-h5, .sb-line-h6";
81+
const groupSelector = opts.groupSelector || ".sb-title-group";
82+
const debug = !!opts.debug;
83+
84+
const bind = () => {
85+
const container = document.querySelector(containerSelector);
86+
if (!container) { requestAnimationFrame(bind); return; }
87+
88+
const prev = window[STATE_KEY];
89+
if (prev && prev.cleanup) prev.cleanup();
90+
91+
function onPointerOver(e) {
92+
const h = e.target && e.target.closest && e.target.closest(headingSelector);
93+
if (!h || !container.contains(h)) return;
94+
95+
const groupRoot = pickGroupRoot(h, container, groupSelector);
96+
const headings = listHeadings(groupRoot, headingSelector);
97+
const startIndex = headings.indexOf(h);
98+
if (startIndex === -1) return;
99+
100+
clearClasses(container);
101+
102+
const startLevel = getLevel(h);
103+
const descendants = collectDescendants(startIndex, headings, startLevel);
104+
const ancestors = collectAncestors(startIndex, headings, startLevel);
105+
106+
h.classList.add("sb-active", "sb-active-current");
107+
descendants.forEach(el => el.classList.add("sb-active", "sb-active-desc"));
108+
ancestors.forEach(el => el.classList.add("sb-active", "sb-active-anc"));
109+
110+
if (debug) {
111+
console.log(
112+
"[HHH] level", startLevel,
113+
"anc", ancestors.length,
114+
"desc", descendants.length,
115+
"text:", (h.textContent || "").trim().slice(0, 60)
116+
);
117+
}
118+
}
119+
120+
function onPointerOut(e) {
121+
const from = e.target && e.target.closest && e.target.closest(headingSelector);
122+
const to = e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest(headingSelector);
123+
if (from && (!to || !container.contains(to))) {
124+
clearClasses(container);
125+
}
126+
}
127+
128+
function onPointerLeave() {
129+
clearClasses(container);
130+
}
131+
132+
container.addEventListener("pointerover", onPointerOver);
133+
container.addEventListener("pointerout", onPointerOut);
134+
container.addEventListener("pointerleave", onPointerLeave);
135+
136+
const mo = new MutationObserver(() => { clearClasses(container); });
137+
mo.observe(container, { childList: true, subtree: true });
138+
139+
window[STATE_KEY] = {
140+
cleanup() {
141+
try {
142+
container.removeEventListener("pointerover", onPointerOver);
143+
container.removeEventListener("pointerout", onPointerOut);
144+
container.removeEventListener("pointerleave", onPointerLeave);
145+
} catch {}
146+
try { mo.disconnect(); } catch {}
147+
clearClasses(container);
148+
}
149+
};
150+
151+
if (debug) console.log("[HHH] enabled");
152+
};
153+
154+
bind();
155+
}
156+
157+
export function disableHighlight() {
158+
const st = window[STATE_KEY];
159+
if (st && st.cleanup) st.cleanup();
160+
window[STATE_KEY] = null;
161+
}
162+
]]
163+
164+
command.define {
165+
name = "Save: HierarchyHighlightHeadings.js",
166+
hide = true,
167+
run = function()
168+
local jsFile = space.writeDocument("Library/HierarchyHighlightHeadings.js", jsCode)
169+
editor.flashNotification("HierarchyHighlightHeadings JS saved with size: " .. jsFile.size .. " bytes")
170+
end
171+
}
172+
```
173+
174+
```space-lua
175+
command.define {
176+
name = "Enable: HierarchyHighlightHeadings",
177+
run = function()
178+
js.import("/.fs/Library/HierarchyHighlightHeadings.js").enableHighlight()
179+
end
180+
}
181+
182+
command.define {
183+
name = "Disable HierarchyHighlightHeadings",
184+
hide = true,
185+
run = function()
186+
js.import("/.fs/Library/HierarchyHighlightHeadings.js").disableHighlight()
187+
end
188+
}
189+
```
190+
191+
1. borrowed `event.listen` from [[CONFIG/Edit/Read Only Toggle]]
192+
193+
```space-lua
194+
event.listen {
195+
name = 'system:ready',
196+
run = function(e)
197+
js.import("/.fs/Library/HierarchyHighlightHeadings.js").enableHighlight()
198+
end
199+
}
200+
```
201+
202+
## CSS part
203+
204+
1. Remember to Cancel the `1st space-style block` from [[STYLE/Theme/HHH-css]]
205+
206+
```space-style
207+
/* 默认半透明 */
208+
.sb-line-h1, .sb-line-h2, .sb-line-h3,
209+
.sb-line-h4, .sb-line-h5, .sb-line-h6 {
210+
opacity: var(--title-opacity);
211+
/* transition: opacity 0.2s; */
212+
}
213+
214+
/* 标题自身 hover 可高亮该标题 */
215+
.sb-line-h1:hover,
216+
.sb-line-h2:hover,
217+
.sb-line-h3:hover,
218+
.sb-line-h4:hover,
219+
.sb-line-h5:hover,
220+
.sb-line-h6:hover {
221+
opacity: 1 !important;
222+
}
223+
224+
/* 仅保留 JS 驱动的高亮 */
225+
.sb-active {
226+
opacity: 1 !important;
227+
}
228+
```
229+
230+
1. https://chatgpt.com/share/68fd0e6f-19d8-8010-95b8-c0f80a829e9b
231+
232+
```space-style
233+
:root {
234+
/* Dark theme 颜色变量 */
235+
--h1-color-dark: #ee82ee;
236+
--h2-color-dark: #6a5acd;
237+
--h3-color-dark: #4169e1;
238+
--h4-color-dark: #008000;
239+
--h5-color-dark: #ffff00;
240+
--h6-color-dark: #ffa500;
241+
242+
--h1-underline-dark: rgba(230,200,255,0.3);
243+
--h2-underline-dark: rgba(160,216,255,0.3);
244+
--h3-underline-dark: rgba(152,255,179,0.3);
245+
--h4-underline-dark: rgba(255,243,168,0.3);
246+
--h5-underline-dark: rgba(255,180,140,0.3);
247+
--h6-underline-dark: rgba(255,168,255,0.3);
248+
249+
250+
/* Light theme 颜色变量 */
251+
--h1-color-light: #6b2e8c;
252+
--h2-color-light: #1c4e8b;
253+
--h3-color-light: #1a6644;
254+
--h4-color-light: #a67c00;
255+
--h5-color-light: #b84c1c;
256+
--h6-color-light: #993399;
257+
258+
--h1-underline-light: rgba(107,46,140,0.3);
259+
--h2-underline-light: rgba(28,78,139,0.3);
260+
--h3-underline-light: rgba(26,102,68,0.3);
261+
--h4-underline-light: rgba(166,124,0,0.3);
262+
--h5-underline-light: rgba(184,76,28,0.3);
263+
--h6-underline-light: rgba(153,51,153,0.3);
264+
265+
--title-opacity: 0.9;
266+
}
267+
268+
/* 公共 H1–H6 样式 */
269+
.sb-line-h1, .sb-line-h2, .sb-line-h3,
270+
.sb-line-h4, .sb-line-h5, .sb-line-h6 {
271+
position: relative;
272+
opacity: var(--title-opacity);
273+
border-bottom-style: solid;
274+
border-bottom-width: 2px;
275+
border-image-slice: 1;
276+
}
277+
278+
/* Dark Theme */
279+
html[data-theme="dark"] {
280+
.sb-line-h1 { font-size:1.8em !important; color:var(--h1-color-dark)!important; border-bottom: 2px solid var(--h1-underline-dark); }
281+
.sb-line-h2 { font-size:1.6em !important; color:var(--h2-color-dark)!important; border-bottom: 2px solid var(--h2-underline-dark); }
282+
.sb-line-h3 { font-size:1.4em !important; color:var(--h3-color-dark)!important; border-bottom: 2px solid var(--h3-underline-dark); }
283+
.sb-line-h4 { font-size:1.2em !important; color:var(--h4-color-dark)!important; border-bottom: 2px solid var(--h4-underline-dark); }
284+
.sb-line-h5 { font-size:1em !important; color:var(--h5-color-dark)!important; border-bottom: 2px solid var(--h5-underline-dark); }
285+
.sb-line-h6 { font-size:1em !important; color:var(--h6-color-dark)!important; border-bottom: 2px solid var(--h6-underline-dark); }
286+
}
287+
288+
/* Light Theme */
289+
html[data-theme="light"] {
290+
.sb-line-h1 { font-size:1.8em !important; color:var(--h1-color-light)!important; border-bottom: 2px solid var(--h1-underline-light); }
291+
.sb-line-h2 { font-size:1.6em !important; color:var(--h2-color-light)!important; border-bottom: 2px solid var(--h2-underline-light); }
292+
.sb-line-h3 { font-size:1.4em !important; color:var(--h3-color-light)!important; border-bottom: 2px solid var(--h3-underline-light); }
293+
.sb-line-h4 { font-size:1.2em !important; color:var(--h4-color-light)!important; border-bottom: 2px solid var(--h4-underline-light); }
294+
.sb-line-h5 { font-size:1em !important; color:var(--h5-color-light)!important; border-bottom: 2px solid var(--h5-underline-light); }
295+
.sb-line-h6 { font-size:1em !important; color:var(--h6-color-light)!important; border-bottom: 2px solid var(--h6-underline-light); }
296+
}
297+
298+
/* 高亮类 */
299+
.sb-active {
300+
opacity: 1 !important;
301+
}
302+
```
303+
304+
```space-style
305+
306+
:root {
307+
--h-bg-alpha-dark: 20%; /* 深色主题 */
308+
--h-bg-alpha-light: 8%; /* 浅色主题 */
309+
}
310+
311+
/* 深色主题:hover 或 .sb-active 才上很淡背景 */
312+
html[data-theme="dark"] .sb-line-h1:hover,
313+
html[data-theme="dark"] .sb-line-h2:hover,
314+
html[data-theme="dark"] .sb-line-h3:hover,
315+
html[data-theme="dark"] .sb-line-h4:hover,
316+
html[data-theme="dark"] .sb-line-h5:hover,
317+
html[data-theme="dark"] .sb-line-h6:hover,
318+
html[data-theme="dark"] .sb-active {
319+
background-color: color-mix(in srgb, currentColor var(--h-bg-alpha-dark), transparent);
320+
}
321+
322+
/* 浅色主题:hover 或 .sb-active 才上很淡背景 */
323+
html[data-theme="light"] .sb-line-h1:hover,
324+
html[data-theme="light"] .sb-line-h2:hover,
325+
html[data-theme="light"] .sb-line-h3:hover,
326+
html[data-theme="light"] .sb-line-h4:hover,
327+
html[data-theme="light"] .sb-line-h5:hover,
328+
html[data-theme="light"] .sb-line-h6:hover,
329+
html[data-theme="light"] .sb-active {
330+
background-color: color-mix(in srgb, currentColor var(--h-bg-alpha-light), transparent);
331+
}
332+
333+
/* 深色:只在 hover 或 sb-active 时给标题行一个很淡的同色背景 */
334+
html[data-theme="dark"] :is(.sb-line-h1,.sb-line-h2,.sb-line-h3,.sb-line-h4,.sb-line-h5,.sb-line-h6):is(:hover,.sb-active) {
335+
background-color: color-mix(in srgb, currentColor var(--h-bg-alpha-dark), transparent);
336+
}
337+
338+
/* 浅色:同理 */
339+
html[data-theme="light"] :is(.sb-line-h1,.sb-line-h2,.sb-line-h3,.sb-line-h4,.sb-line-h5,.sb-line-h6):is(:hover,.sb-active) {
340+
background-color: color-mix(in srgb, currentColor var(--h-bg-alpha-light), transparent);
341+
}
342+
```

src/HelpShortcuts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ command.define {
1010
name = "Help: shortcuts",
1111
key = "Ctrl-h",
1212
run = function()
13-
local messages={ "ctrl+r: search header", "ctrl+s: search everywhere","ctrl+p: vscode palette","shift+alt+c: cursor position","alt+ctrl+n: new page", "alt+c: magneto"}
13+
local messages={ "ctrl-shift-h header picker", "ctrl-shift-t tag picker","alt-c: copy nearest","shift-ctrl-c: cursor position","alt-ctrl-n: new child","alt-ctrl-s: new sibling","ctrl-Alt-1: code block","!!last monday: chronos"}
1414
for i in pairs(messages) do
1515
editor.flashNotification(messages[i], "info")
1616
end

src/LLMInstructions.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
author: malys
3+
description: System prompt to pilot LLM in SB plugin coding.
4+
---
5+
# Instructions
6+
7+
> **tip** Hint
8+
> > I use [a RAG with silverbullet source code](https://deepwiki.com/silverbulletmd/silverbullet). It’s probably not the most powerful LLM, but it’s aware of source code.
9+
10+
11+
```
12+
I want generate a space-lua for [silverbullet](https://silverbullet.md/). space-lua language inherits from lua language with some specifc API. Please, follows thoses rules:
13+
- not use "pcall"
14+
- not use javascript library by default
15+
- if function debug_log exists, every function must be surrounded by debug_log
16+
- not use lua standard methods but space-lua silverbullet api (https://silverbullet.md/API)
17+
- string.byte(s, i?, j?)
18+
- string.char(...)
19+
- string.find(s, pattern, init?, plain?)
20+
- string.format(format, ...)
21+
- string.gsub(s, pattern, repl, n?)
22+
- string.match(s, pattern, init?)
23+
- string.gmatch(s, pattern)
24+
- string.len(s)
25+
- string.lower(s)
26+
- string.upper(s)
27+
- string.rep(s, n, sep?)
28+
- string.reverse(s)
29+
- string.sub(s, i, j?)
30+
- string.split(s, sep)
31+
- string.startsWith(s, prefix)
32+
- string.endsWith(s, suffix)
33+
- string.trim(s)
34+
- string.trimStart(s)
35+
- string.trimEnd(s)
36+
- string.matchRegex(s, pattern)
37+
- string.matchRegexAll(s, pattern)
38+
- use "local" functions and variables as it's possible
39+
- every function should be testable
40+
41+
```

0 commit comments

Comments
 (0)