starlight-cannoli-plugins 2.10.0 → 2.10.2

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.
@@ -1,23 +1,21 @@
1
1
  import "../../chunk-4VNS5WPM.js";
2
2
 
3
3
  // src/plugins/starlight-dom-patches/page-script.ts
4
- var TOC_NAV_SELECTOR = "nav[aria-labelledby='starlight__on-this-page']";
5
4
  function appendToActionPanel(element) {
6
- const tocNav = document.querySelector(TOC_NAV_SELECTOR);
7
- if (!tocNav) return false;
8
- let panel = tocNav.parentElement?.querySelector(
5
+ const starlightToc = document.querySelector("starlight-toc");
6
+ if (!starlightToc) return false;
7
+ let panel = starlightToc.querySelector(
9
8
  ":scope > div.cannoli-actionable"
10
- ) ?? null;
9
+ );
11
10
  if (!panel) {
12
11
  panel = document.createElement("div");
13
12
  panel.className = "cannoli-actionable";
14
- tocNav.parentNode.insertBefore(panel, tocNav);
13
+ starlightToc.prepend(panel);
15
14
  }
16
15
  panel.appendChild(element);
17
16
  return true;
18
17
  }
19
18
  function hideSingleLineGutters() {
20
- console.log("Hiding Single Line Gutters");
21
19
  const codeElements = document.querySelectorAll(
22
20
  "div.expressive-code > figure > pre > code"
23
21
  );
@@ -52,142 +50,176 @@ function syncTocLabelsFromHeadings() {
52
50
  });
53
51
  }
54
52
  function tabbedH2Content() {
55
- console.log("[tabbedH2Content] running");
56
53
  const LS_KEY = "starlight-dom-patches:tabbed-content";
57
- const container = document.querySelector(
54
+ const contentContainer = document.querySelector(
58
55
  ".main-pane .sl-markdown-content"
59
56
  );
60
- if (!container) {
61
- console.log(
62
- "[tabbedH2Content] no .main-pane .sl-markdown-content found \u2014 aborting"
63
- );
64
- return;
65
- }
66
- const children = Array.from(container.children);
67
- if (children.length === 0) {
68
- console.log("[tabbedH2Content] content container is empty \u2014 aborting");
69
- return;
70
- }
71
- console.log(`[tabbedH2Content] container element:`, container);
72
- console.log(
73
- `[tabbedH2Content] found ${children.length} children:`,
74
- 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
75
62
  );
76
- const preH2Nodes = [];
77
- const h2Sections = [];
78
- let currentSection = null;
79
- const isH2Wrapper = (el) => el.tagName === "DIV" && el.classList.contains("sl-heading-wrapper") && el.classList.contains("level-h2");
80
- for (const child of children) {
81
- if (isH2Wrapper(child)) {
82
- if (currentSection) h2Sections.push(currentSection);
83
- const h2 = child.querySelector("h2");
84
- currentSection = {
85
- label: h2 ? headingInnerHTML(h2) : "",
86
- nodes: [child]
87
- };
88
- } else if (currentSection === null) {
89
- preH2Nodes.push(child);
90
- } else {
91
- currentSection.nodes.push(child);
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
+ }
92
83
  }
84
+ if (currentSection) h2Sections2.push(currentSection);
85
+ return { preH2Nodes: preH2Nodes2, h2Sections: h2Sections2 };
93
86
  }
94
- if (currentSection) h2Sections.push(currentSection);
95
- const hasPreContent = preH2Nodes.length > 0;
96
- const totalSections = (hasPreContent ? 1 : 0) + h2Sections.length;
97
- console.log(
98
- `[tabbedH2Content] preH2Nodes: ${preH2Nodes.length}, h2Sections: ${h2Sections.length}, totalSections: ${totalSections}`
99
- );
100
- if (totalSections <= 1) {
101
- console.log(
102
- "[tabbedH2Content] only one section \u2014 aborting (no tabs needed)"
103
- );
104
- return;
105
- }
87
+ const { preH2Nodes, h2Sections } = splitIntoSections();
88
+ const hasPreH2Content = preH2Nodes.length > 0;
89
+ if ((hasPreH2Content ? 1 : 0) + h2Sections.length <= 1) return;
106
90
  const allSections = [];
107
- if (hasPreContent) allSections.push({ label: "Main", nodes: preH2Nodes });
91
+ if (hasPreH2Content) allSections.push({ label: "Main", nodes: preH2Nodes });
108
92
  allSections.push(...h2Sections);
109
- const wrapper = document.createElement("div");
110
- wrapper.className = "tabbed-content";
111
- const nav = document.createElement("div");
112
- nav.className = "tabbed-content-nav not-content";
113
- const tabButtons = [];
114
- const panels = [];
115
- allSections.forEach((section, i) => {
116
- const btn = document.createElement("button");
117
- btn.className = "tabbed-content-tab";
118
- btn.innerHTML = section.label;
119
- btn.dataset.tab = String(i);
120
- tabButtons.push(btn);
121
- nav.appendChild(btn);
122
- const panel = document.createElement("div");
123
- panel.className = "tabbed-content-panel";
124
- panel.dataset.panel = String(i);
125
- section.nodes.forEach((node) => panel.appendChild(node));
126
- panels.push(panel);
127
- });
128
- wrapper.appendChild(nav);
129
- panels.forEach((p) => wrapper.appendChild(p));
130
- container.appendChild(wrapper);
131
- let activeTab = 0;
132
- function activateTab(index) {
133
- activeTab = index;
134
- tabButtons.forEach((btn, i) => {
135
- 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);
112
+ });
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 activateTab(targetIndex, updateHash = true) {
150
+ activeTabIndex = targetIndex;
151
+ tabButtons.forEach((tabButton, buttonIndex) => {
152
+ tabButton.dataset.active = String(buttonIndex === targetIndex);
136
153
  });
137
- panels.forEach((panel, i) => {
138
- panel.hidden = i !== index;
154
+ tabPanels.forEach((tabPanel, panelIndex) => {
155
+ tabPanel.hidden = panelIndex !== targetIndex;
139
156
  });
157
+ prevTabButton.disabled = targetIndex === 0;
158
+ nextTabButton.disabled = targetIndex === allSections.length - 1;
159
+ if (updateHash) {
160
+ const targetHeadingId = allSections[targetIndex].headingId;
161
+ if (targetHeadingId) {
162
+ history.replaceState(null, "", `#${targetHeadingId}`);
163
+ } else {
164
+ history.replaceState(
165
+ null,
166
+ "",
167
+ window.location.pathname + window.location.search
168
+ );
169
+ }
170
+ }
140
171
  }
141
172
  function setEnabled(enabled) {
142
- wrapper.dataset.enabled = String(enabled);
143
- nav.hidden = !enabled;
173
+ tabbedWrapper.dataset.enabled = String(enabled);
174
+ tabNav.hidden = !enabled;
175
+ paginationBar.hidden = !enabled;
144
176
  if (enabled) {
145
- activateTab(activeTab);
177
+ activateTab(activeTabIndex, false);
146
178
  } else {
147
- panels.forEach((panel) => {
148
- panel.hidden = false;
179
+ tabPanels.forEach((tabPanel) => {
180
+ tabPanel.hidden = false;
149
181
  });
150
182
  }
151
183
  }
152
- tabButtons.forEach((btn, i) => {
153
- btn.addEventListener("click", () => {
154
- if (wrapper.dataset.enabled === "true") activateTab(i);
184
+ function navigateToHash(urlHash) {
185
+ if (!urlHash || tabbedWrapper.dataset.enabled !== "true") return;
186
+ const targetHeadingId = urlHash.startsWith("#") ? urlHash.slice(1) : urlHash;
187
+ tabPanels.forEach((tabPanel, panelIndex) => {
188
+ if (tabPanel.querySelector(`#${CSS.escape(targetHeadingId)}`) && panelIndex !== activeTabIndex)
189
+ activateTab(panelIndex, false);
155
190
  });
156
- });
157
- function navigateToHash(hash) {
158
- if (!hash || wrapper.dataset.enabled !== "true") return;
159
- const id = hash.startsWith("#") ? hash.slice(1) : hash;
160
- panels.forEach((panel, i) => {
161
- if (panel.querySelector(`#${CSS.escape(id)}`)) activateTab(i);
191
+ }
192
+ function wireInteractions(viewToggleCheckbox2) {
193
+ prevTabButton.addEventListener("click", () => {
194
+ if (activeTabIndex > 0) activateTab(activeTabIndex - 1);
195
+ });
196
+ nextTabButton.addEventListener("click", () => {
197
+ if (activeTabIndex < allSections.length - 1)
198
+ activateTab(activeTabIndex + 1);
199
+ });
200
+ tabButtons.forEach((tabButton, buttonIndex) => {
201
+ tabButton.addEventListener("click", () => {
202
+ if (tabbedWrapper.dataset.enabled === "true") activateTab(buttonIndex);
203
+ });
204
+ });
205
+ window.addEventListener(
206
+ "hashchange",
207
+ () => navigateToHash(window.location.hash)
208
+ );
209
+ viewToggleCheckbox2.addEventListener("change", () => {
210
+ setEnabled(viewToggleCheckbox2.checked);
211
+ localStorage.setItem(
212
+ LS_KEY,
213
+ viewToggleCheckbox2.checked ? "enabled" : "disabled"
214
+ );
162
215
  });
163
216
  }
164
- window.addEventListener(
165
- "hashchange",
166
- () => navigateToHash(window.location.hash)
167
- );
168
- if (window.location.hash) navigateToHash(window.location.hash);
169
- const toggleLabel = document.createElement("label");
170
- toggleLabel.className = "toggle-checkbox-btn";
171
- const checkbox = document.createElement("input");
172
- checkbox.type = "checkbox";
173
- const toggleText = document.createElement("span");
174
- toggleText.textContent = "Tabbed view";
175
- toggleLabel.appendChild(checkbox);
176
- toggleLabel.appendChild(toggleText);
177
- appendToActionPanel(toggleLabel);
178
- console.log(
179
- "[tabbedH2Content] toggle checkbox injected into .cannoli-actionable panel"
180
- );
217
+ const viewToggleCheckbox = buildToggle();
181
218
  const initialEnabled = localStorage.getItem(LS_KEY) === "enabled";
182
- console.log(
183
- `[tabbedH2Content] localStorage value: ${localStorage.getItem(LS_KEY)}, initialEnabled: ${initialEnabled}`
184
- );
185
- checkbox.checked = initialEnabled;
219
+ viewToggleCheckbox.checked = initialEnabled;
186
220
  setEnabled(initialEnabled);
187
- checkbox.addEventListener("change", () => {
188
- setEnabled(checkbox.checked);
189
- localStorage.setItem(LS_KEY, checkbox.checked ? "enabled" : "disabled");
190
- });
221
+ if (window.location.hash) navigateToHash(window.location.hash);
222
+ wireInteractions(viewToggleCheckbox);
191
223
  }
192
224
  function toggleAllDetails() {
193
225
  const detailsElements = document.querySelectorAll(
@@ -221,7 +253,6 @@ function toggleAllDetails() {
221
253
  });
222
254
  }
223
255
  function limitDetailsElementHeight() {
224
- console.log("Wrapping details element contents");
225
256
  const detailsElements = document.querySelectorAll(".main-pane details");
226
257
  detailsElements.forEach((details) => {
227
258
  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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "starlight-cannoli-plugins",
3
3
  "type": "module",
4
- "version": "2.10.0",
4
+ "version": "2.10.2",
5
5
  "description": "Starlight plugins for automatic sidebar generation and link validation",
6
6
  "license": "ISC",
7
7
  "main": "./dist/index.js",