starlight-cannoli-plugins 2.10.1 → 2.10.3
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.
|
@@ -16,7 +16,6 @@ function appendToActionPanel(element) {
|
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
18
|
function hideSingleLineGutters() {
|
|
19
|
-
console.log("Hiding Single Line Gutters");
|
|
20
19
|
const codeElements = document.querySelectorAll(
|
|
21
20
|
"div.expressive-code > figure > pre > code"
|
|
22
21
|
);
|
|
@@ -51,142 +50,199 @@ function syncTocLabelsFromHeadings() {
|
|
|
51
50
|
});
|
|
52
51
|
}
|
|
53
52
|
function tabbedH2Content() {
|
|
54
|
-
console.log("[tabbedH2Content] running");
|
|
55
53
|
const LS_KEY = "starlight-dom-patches:tabbed-content";
|
|
56
|
-
const
|
|
54
|
+
const contentContainer = document.querySelector(
|
|
57
55
|
".main-pane .sl-markdown-content"
|
|
58
56
|
);
|
|
59
|
-
if (!
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
const children = Array.from(container.children);
|
|
66
|
-
if (children.length === 0) {
|
|
67
|
-
console.log("[tabbedH2Content] content container is empty \u2014 aborting");
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
console.log(`[tabbedH2Content] container element:`, container);
|
|
71
|
-
console.log(
|
|
72
|
-
`[tabbedH2Content] found ${children.length} children:`,
|
|
73
|
-
children.map((c) => c.tagName).join(", ")
|
|
57
|
+
if (!contentContainer) return;
|
|
58
|
+
const starlightToc = document.querySelector("starlight-toc");
|
|
59
|
+
if (!starlightToc) return;
|
|
60
|
+
const contentChildren = Array.from(
|
|
61
|
+
contentContainer.children
|
|
74
62
|
);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
currentSection
|
|
63
|
+
if (contentChildren.length === 0) return;
|
|
64
|
+
function splitIntoSections() {
|
|
65
|
+
const isH2Wrapper = (element) => element.tagName === "DIV" && element.classList.contains("sl-heading-wrapper") && element.classList.contains("level-h2");
|
|
66
|
+
const preH2Nodes2 = [];
|
|
67
|
+
const h2Sections2 = [];
|
|
68
|
+
let currentSection = null;
|
|
69
|
+
for (const block of contentChildren) {
|
|
70
|
+
if (isH2Wrapper(block)) {
|
|
71
|
+
if (currentSection) h2Sections2.push(currentSection);
|
|
72
|
+
const h2Element = block.querySelector("h2");
|
|
73
|
+
currentSection = {
|
|
74
|
+
label: h2Element ? headingInnerHTML(h2Element) : "",
|
|
75
|
+
headingId: h2Element?.id,
|
|
76
|
+
nodes: [block]
|
|
77
|
+
};
|
|
78
|
+
} else if (currentSection === null) {
|
|
79
|
+
preH2Nodes2.push(block);
|
|
80
|
+
} else {
|
|
81
|
+
currentSection.nodes.push(block);
|
|
82
|
+
}
|
|
91
83
|
}
|
|
84
|
+
if (currentSection) h2Sections2.push(currentSection);
|
|
85
|
+
return { preH2Nodes: preH2Nodes2, h2Sections: h2Sections2 };
|
|
92
86
|
}
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
console.log(
|
|
97
|
-
`[tabbedH2Content] preH2Nodes: ${preH2Nodes.length}, h2Sections: ${h2Sections.length}, totalSections: ${totalSections}`
|
|
98
|
-
);
|
|
99
|
-
if (totalSections <= 1) {
|
|
100
|
-
console.log(
|
|
101
|
-
"[tabbedH2Content] only one section \u2014 aborting (no tabs needed)"
|
|
102
|
-
);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
87
|
+
const { preH2Nodes, h2Sections } = splitIntoSections();
|
|
88
|
+
const hasPreH2Content = preH2Nodes.length > 0;
|
|
89
|
+
if ((hasPreH2Content ? 1 : 0) + h2Sections.length <= 1) return;
|
|
105
90
|
const allSections = [];
|
|
106
|
-
if (
|
|
91
|
+
if (hasPreH2Content) allSections.push({ label: "Main", nodes: preH2Nodes });
|
|
107
92
|
allSections.push(...h2Sections);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
wrapper.appendChild(nav);
|
|
128
|
-
panels.forEach((p) => wrapper.appendChild(p));
|
|
129
|
-
container.appendChild(wrapper);
|
|
130
|
-
let activeTab = 0;
|
|
131
|
-
function activateTab(index) {
|
|
132
|
-
activeTab = index;
|
|
133
|
-
tabButtons.forEach((btn, i) => {
|
|
134
|
-
btn.dataset.active = String(i === index);
|
|
93
|
+
function buildTabs() {
|
|
94
|
+
const tabbedWrapper2 = document.createElement("div");
|
|
95
|
+
tabbedWrapper2.className = "tabbed-content";
|
|
96
|
+
const tabNav2 = document.createElement("div");
|
|
97
|
+
tabNav2.className = "tabbed-content-nav not-content";
|
|
98
|
+
const tabButtons2 = [];
|
|
99
|
+
const tabPanels2 = [];
|
|
100
|
+
allSections.forEach((section, sectionIndex) => {
|
|
101
|
+
const tabButton = document.createElement("button");
|
|
102
|
+
tabButton.className = "tabbed-content-tab";
|
|
103
|
+
tabButton.innerHTML = section.label;
|
|
104
|
+
tabButton.dataset.tab = String(sectionIndex);
|
|
105
|
+
tabButtons2.push(tabButton);
|
|
106
|
+
tabNav2.appendChild(tabButton);
|
|
107
|
+
const tabPanel = document.createElement("div");
|
|
108
|
+
tabPanel.className = "tabbed-content-panel";
|
|
109
|
+
tabPanel.dataset.panel = String(sectionIndex);
|
|
110
|
+
section.nodes.forEach((node) => tabPanel.appendChild(node));
|
|
111
|
+
tabPanels2.push(tabPanel);
|
|
135
112
|
});
|
|
136
|
-
|
|
137
|
-
|
|
113
|
+
tabbedWrapper2.appendChild(tabNav2);
|
|
114
|
+
tabPanels2.forEach((tabPanel) => tabbedWrapper2.appendChild(tabPanel));
|
|
115
|
+
return { tabbedWrapper: tabbedWrapper2, tabNav: tabNav2, tabButtons: tabButtons2, tabPanels: tabPanels2 };
|
|
116
|
+
}
|
|
117
|
+
function buildPagination() {
|
|
118
|
+
const paginationBar2 = document.createElement("div");
|
|
119
|
+
paginationBar2.className = "tabbed-content-pagination not-content";
|
|
120
|
+
const prevTabButton2 = document.createElement("button");
|
|
121
|
+
prevTabButton2.className = "tabbed-content-pagination-btn";
|
|
122
|
+
prevTabButton2.dataset.direction = "prev";
|
|
123
|
+
prevTabButton2.textContent = "\u2190 Previous Tab";
|
|
124
|
+
const nextTabButton2 = document.createElement("button");
|
|
125
|
+
nextTabButton2.className = "tabbed-content-pagination-btn";
|
|
126
|
+
nextTabButton2.dataset.direction = "next";
|
|
127
|
+
nextTabButton2.textContent = "Next Tab \u2192";
|
|
128
|
+
paginationBar2.appendChild(prevTabButton2);
|
|
129
|
+
paginationBar2.appendChild(nextTabButton2);
|
|
130
|
+
return { paginationBar: paginationBar2, prevTabButton: prevTabButton2, nextTabButton: nextTabButton2 };
|
|
131
|
+
}
|
|
132
|
+
function buildToggle() {
|
|
133
|
+
const viewToggleLabel = document.createElement("label");
|
|
134
|
+
viewToggleLabel.className = "toggle-checkbox-btn";
|
|
135
|
+
const viewToggleCheckbox2 = document.createElement("input");
|
|
136
|
+
viewToggleCheckbox2.type = "checkbox";
|
|
137
|
+
const viewToggleText = document.createElement("span");
|
|
138
|
+
viewToggleText.textContent = "Tabbed view";
|
|
139
|
+
viewToggleLabel.appendChild(viewToggleCheckbox2);
|
|
140
|
+
viewToggleLabel.appendChild(viewToggleText);
|
|
141
|
+
appendToActionPanel(viewToggleLabel);
|
|
142
|
+
return viewToggleCheckbox2;
|
|
143
|
+
}
|
|
144
|
+
const { tabbedWrapper, tabNav, tabButtons, tabPanels } = buildTabs();
|
|
145
|
+
const { paginationBar, prevTabButton, nextTabButton } = buildPagination();
|
|
146
|
+
contentContainer.appendChild(tabbedWrapper);
|
|
147
|
+
contentContainer.appendChild(paginationBar);
|
|
148
|
+
let activeTabIndex = 0;
|
|
149
|
+
function isTabNavVisible() {
|
|
150
|
+
const rect = tabNav.getBoundingClientRect();
|
|
151
|
+
return rect.top < window.innerHeight && rect.bottom > 0;
|
|
152
|
+
}
|
|
153
|
+
function activateTab(targetIndex, updateHash = true, scrollToNav = false) {
|
|
154
|
+
activeTabIndex = targetIndex;
|
|
155
|
+
tabButtons.forEach((tabButton, buttonIndex) => {
|
|
156
|
+
tabButton.dataset.active = String(buttonIndex === targetIndex);
|
|
157
|
+
});
|
|
158
|
+
tabPanels.forEach((tabPanel, panelIndex) => {
|
|
159
|
+
tabPanel.hidden = panelIndex !== targetIndex;
|
|
138
160
|
});
|
|
161
|
+
prevTabButton.disabled = targetIndex === 0;
|
|
162
|
+
nextTabButton.disabled = targetIndex === allSections.length - 1;
|
|
163
|
+
const scrollBehavior = getComputedStyle(document.documentElement).scrollBehavior;
|
|
164
|
+
tabButtons[targetIndex].scrollIntoView({
|
|
165
|
+
behavior: scrollBehavior,
|
|
166
|
+
block: "nearest",
|
|
167
|
+
inline: "nearest"
|
|
168
|
+
});
|
|
169
|
+
if (scrollToNav) {
|
|
170
|
+
tabNav.scrollIntoView({ behavior: scrollBehavior, block: "nearest" });
|
|
171
|
+
}
|
|
172
|
+
if (updateHash) {
|
|
173
|
+
const targetHeadingId = allSections[targetIndex].headingId;
|
|
174
|
+
if (targetHeadingId) {
|
|
175
|
+
history.replaceState(null, "", `#${targetHeadingId}`);
|
|
176
|
+
} else {
|
|
177
|
+
history.replaceState(
|
|
178
|
+
null,
|
|
179
|
+
"",
|
|
180
|
+
window.location.pathname + window.location.search
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function findPanelIndexForHash(urlHash) {
|
|
186
|
+
const targetHeadingId = urlHash.startsWith("#") ? urlHash.slice(1) : urlHash;
|
|
187
|
+
return tabPanels.findIndex(
|
|
188
|
+
(tabPanel) => tabPanel.querySelector(`#${CSS.escape(targetHeadingId)}`)
|
|
189
|
+
);
|
|
139
190
|
}
|
|
140
191
|
function setEnabled(enabled) {
|
|
141
|
-
|
|
142
|
-
|
|
192
|
+
tabbedWrapper.dataset.enabled = String(enabled);
|
|
193
|
+
tabNav.hidden = !enabled;
|
|
194
|
+
paginationBar.hidden = !enabled;
|
|
143
195
|
if (enabled) {
|
|
144
|
-
|
|
196
|
+
const hashPanelIndex = window.location.hash ? findPanelIndexForHash(window.location.hash) : -1;
|
|
197
|
+
activateTab(hashPanelIndex >= 0 ? hashPanelIndex : activeTabIndex, false);
|
|
145
198
|
} else {
|
|
146
|
-
|
|
147
|
-
|
|
199
|
+
tabPanels.forEach((tabPanel) => {
|
|
200
|
+
tabPanel.hidden = false;
|
|
148
201
|
});
|
|
149
202
|
}
|
|
150
203
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
204
|
+
function navigateToHash(urlHash) {
|
|
205
|
+
if (!urlHash || tabbedWrapper.dataset.enabled !== "true") return;
|
|
206
|
+
const targetPanelIndex = findPanelIndexForHash(urlHash);
|
|
207
|
+
if (targetPanelIndex >= 0 && targetPanelIndex !== activeTabIndex)
|
|
208
|
+
activateTab(targetPanelIndex, false);
|
|
209
|
+
}
|
|
210
|
+
function wireInteractions(viewToggleCheckbox2) {
|
|
211
|
+
prevTabButton.addEventListener("click", () => {
|
|
212
|
+
if (activeTabIndex > 0) {
|
|
213
|
+
const scrollToNav = !isTabNavVisible();
|
|
214
|
+
activateTab(activeTabIndex - 1, true, scrollToNav);
|
|
215
|
+
}
|
|
154
216
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
217
|
+
nextTabButton.addEventListener("click", () => {
|
|
218
|
+
if (activeTabIndex < allSections.length - 1) {
|
|
219
|
+
const scrollToNav = !isTabNavVisible();
|
|
220
|
+
activateTab(activeTabIndex + 1, true, scrollToNav);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
tabButtons.forEach((tabButton, buttonIndex) => {
|
|
224
|
+
tabButton.addEventListener("click", () => {
|
|
225
|
+
if (tabbedWrapper.dataset.enabled === "true") activateTab(buttonIndex);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
window.addEventListener(
|
|
229
|
+
"hashchange",
|
|
230
|
+
() => navigateToHash(window.location.hash)
|
|
231
|
+
);
|
|
232
|
+
viewToggleCheckbox2.addEventListener("change", () => {
|
|
233
|
+
setEnabled(viewToggleCheckbox2.checked);
|
|
234
|
+
localStorage.setItem(
|
|
235
|
+
LS_KEY,
|
|
236
|
+
viewToggleCheckbox2.checked ? "enabled" : "disabled"
|
|
237
|
+
);
|
|
161
238
|
});
|
|
162
239
|
}
|
|
163
|
-
|
|
164
|
-
"hashchange",
|
|
165
|
-
() => navigateToHash(window.location.hash)
|
|
166
|
-
);
|
|
167
|
-
if (window.location.hash) navigateToHash(window.location.hash);
|
|
168
|
-
const toggleLabel = document.createElement("label");
|
|
169
|
-
toggleLabel.className = "toggle-checkbox-btn";
|
|
170
|
-
const checkbox = document.createElement("input");
|
|
171
|
-
checkbox.type = "checkbox";
|
|
172
|
-
const toggleText = document.createElement("span");
|
|
173
|
-
toggleText.textContent = "Tabbed view";
|
|
174
|
-
toggleLabel.appendChild(checkbox);
|
|
175
|
-
toggleLabel.appendChild(toggleText);
|
|
176
|
-
appendToActionPanel(toggleLabel);
|
|
177
|
-
console.log(
|
|
178
|
-
"[tabbedH2Content] toggle checkbox injected into .cannoli-actionable panel"
|
|
179
|
-
);
|
|
240
|
+
const viewToggleCheckbox = buildToggle();
|
|
180
241
|
const initialEnabled = localStorage.getItem(LS_KEY) === "enabled";
|
|
181
|
-
|
|
182
|
-
`[tabbedH2Content] localStorage value: ${localStorage.getItem(LS_KEY)}, initialEnabled: ${initialEnabled}`
|
|
183
|
-
);
|
|
184
|
-
checkbox.checked = initialEnabled;
|
|
242
|
+
viewToggleCheckbox.checked = initialEnabled;
|
|
185
243
|
setEnabled(initialEnabled);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
localStorage.setItem(LS_KEY, checkbox.checked ? "enabled" : "disabled");
|
|
189
|
-
});
|
|
244
|
+
if (window.location.hash) navigateToHash(window.location.hash);
|
|
245
|
+
wireInteractions(viewToggleCheckbox);
|
|
190
246
|
}
|
|
191
247
|
function toggleAllDetails() {
|
|
192
248
|
const detailsElements = document.querySelectorAll(
|
|
@@ -220,7 +276,6 @@ function toggleAllDetails() {
|
|
|
220
276
|
});
|
|
221
277
|
}
|
|
222
278
|
function limitDetailsElementHeight() {
|
|
223
|
-
console.log("Wrapping details element contents");
|
|
224
279
|
const detailsElements = document.querySelectorAll(".main-pane details");
|
|
225
280
|
detailsElements.forEach((details) => {
|
|
226
281
|
if (details.children.length === 0) return;
|
|
@@ -171,6 +171,40 @@ div > div[class="page"] > svg {
|
|
|
171
171
|
margin-bottom: 1rem;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
.tabbed-content-pagination {
|
|
175
|
+
display: flex;
|
|
176
|
+
justify-content: space-between;
|
|
177
|
+
margin-top: 2rem;
|
|
178
|
+
|
|
179
|
+
&[hidden] {
|
|
180
|
+
display: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.tabbed-content-pagination-btn {
|
|
185
|
+
padding: 0.4rem 1rem;
|
|
186
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
187
|
+
border-radius: 6px;
|
|
188
|
+
background-color: transparent;
|
|
189
|
+
color: var(--sl-color-text);
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
font-size: 0.875em;
|
|
192
|
+
transition:
|
|
193
|
+
color 0.15s ease,
|
|
194
|
+
background-color 0.15s ease,
|
|
195
|
+
border-color 0.15s ease;
|
|
196
|
+
|
|
197
|
+
&:hover:not(:disabled) {
|
|
198
|
+
background-color: var(--sl-color-gray-6);
|
|
199
|
+
border-color: var(--sl-color-gray-4);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
&:disabled {
|
|
203
|
+
opacity: 0.35;
|
|
204
|
+
cursor: default;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
174
208
|
.toggle-checkbox-btn {
|
|
175
209
|
display: flex;
|
|
176
210
|
align-items: center;
|
package/package.json
CHANGED