@beatzball/create-litro 0.3.0 → 0.4.1

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.
@@ -36,6 +36,7 @@ export class StarlightPage extends LitElement {
36
36
  currentSlug: { type: String },
37
37
  currentPath: { type: String },
38
38
  noSidebar: { type: Boolean },
39
+ _navOpen: { state: true },
39
40
  };
40
41
 
41
42
  static override styles = css`
@@ -102,6 +103,14 @@ export class StarlightPage extends LitElement {
102
103
  line-height: 1.15;
103
104
  }
104
105
 
106
+ .nav-backdrop {
107
+ position: fixed;
108
+ inset: 0;
109
+ top: var(--sl-nav-height, 3.5rem);
110
+ background: rgba(0, 0, 0, 0.4);
111
+ z-index: 49;
112
+ }
113
+
105
114
  /* Responsive: hide sidebar and TOC on narrow screens */
106
115
  @media (max-width: 72rem) {
107
116
  .body {
@@ -110,7 +119,20 @@ export class StarlightPage extends LitElement {
110
119
  }
111
120
 
112
121
  .sidebar-wrap {
113
- display: none;
122
+ grid-area: unset;
123
+ position: fixed;
124
+ top: var(--sl-nav-height, 3.5rem);
125
+ left: 0;
126
+ z-index: 50;
127
+ height: calc(100vh - var(--sl-nav-height, 3.5rem));
128
+ width: var(--sl-sidebar-width, 16rem);
129
+ transform: translateX(-100%);
130
+ transition: transform 0.2s ease;
131
+ box-shadow: 2px 0 16px rgba(0, 0, 0, 0.12);
132
+ }
133
+
134
+ .sidebar-wrap.nav-open {
135
+ transform: translateX(0);
114
136
  }
115
137
  }
116
138
 
@@ -134,18 +156,40 @@ export class StarlightPage extends LitElement {
134
156
  currentSlug = '';
135
157
  currentPath = '';
136
158
  noSidebar = false;
159
+ _navOpen = false;
160
+
161
+ override updated(changed: Map<string, unknown>) {
162
+ if (changed.has('currentPath') && this._navOpen) {
163
+ this._navOpen = false;
164
+ }
165
+ }
166
+
167
+ private _handleNavToggle() {
168
+ this._navOpen = !this._navOpen;
169
+ }
170
+
171
+ private _closeNav() {
172
+ this._navOpen = false;
173
+ }
137
174
 
138
175
  override render() {
176
+ const hasSidebar = !this.noSidebar;
139
177
  return html`
140
178
  <div class="page-wrap">
141
179
  <starlight-header
142
180
  siteTitle="${this.siteTitle}"
143
181
  .nav="${this.nav}"
144
182
  currentPath="${this.currentPath}"
183
+ .navOpen="${this._navOpen}"
184
+ .hasSidebar="${hasSidebar}"
185
+ @sl-nav-toggle="${this._handleNavToggle}"
145
186
  ></starlight-header>
187
+ ${hasSidebar && this._navOpen ? html`
188
+ <div class="nav-backdrop" @click="${this._closeNav}"></div>
189
+ ` : ''}
146
190
  <div class="body${this.noSidebar ? ' no-sidebar' : ''}">
147
- ${!this.noSidebar ? html`
148
- <aside class="sidebar-wrap">
191
+ ${hasSidebar ? html`
192
+ <aside class="sidebar-wrap${this._navOpen ? ' nav-open' : ''}">
149
193
  <starlight-sidebar
150
194
  .groups="${this.sidebar}"
151
195
  currentSlug="${this.currentSlug}"
@@ -158,7 +202,7 @@ export class StarlightPage extends LitElement {
158
202
  <slot name="content"></slot>
159
203
  </div>
160
204
  </main>
161
- ${!this.noSidebar ? html`
205
+ ${hasSidebar ? html`
162
206
  <aside class="toc-wrap">
163
207
  <starlight-toc .entries="${this.toc}"></starlight-toc>
164
208
  </aside>
@@ -0,0 +1,40 @@
1
+ import hljs from 'highlight.js';
2
+
3
+ const NAMED_ENTITY_MAP: Record<string, string> = {
4
+ '&amp;': '&',
5
+ '&lt;': '<',
6
+ '&gt;': '>',
7
+ '&quot;': '"',
8
+ '&#39;': "'",
9
+ };
10
+
11
+ function decodeEntities(str: string): string {
12
+ return str.replace(/&#x[0-9a-fA-F]+;|&#[0-9]+;|&amp;|&lt;|&gt;|&quot;|&#39;/g, m => {
13
+ if (m.startsWith('&#x')) return String.fromCodePoint(parseInt(m.slice(3, -1), 16));
14
+ if (m.startsWith('&#')) return String.fromCodePoint(parseInt(m.slice(2, -1), 10));
15
+ return NAMED_ENTITY_MAP[m]!;
16
+ });
17
+ }
18
+
19
+ /**
20
+ * Post-processes an HTML string, replacing every
21
+ * <pre><code class="language-*">…</code></pre>
22
+ * with a syntax-highlighted version produced by highlight.js.
23
+ *
24
+ * Must be called server-side only (SSG build time).
25
+ */
26
+ export function applyHighlighting(html: string): string {
27
+ return html.replace(
28
+ /<pre><code class="language-([^"]+)">([\s\S]*?)<\/code><\/pre>/g,
29
+ (_match, lang: string, encoded: string) => {
30
+ const code = decodeEntities(encoded);
31
+ let highlighted: string;
32
+ try {
33
+ highlighted = hljs.highlight(code, { language: lang, ignoreIllegals: true }).value;
34
+ } catch {
35
+ highlighted = hljs.highlightAuto(code).value;
36
+ }
37
+ return `<pre><code class="hljs language-${lang}">${highlighted}</code></pre>`;
38
+ },
39
+ );
40
+ }
@@ -10,6 +10,7 @@
10
10
  export const starlightHead = [
11
11
  '<link rel="stylesheet" href="/shoelace/themes/light.css" />',
12
12
  '<link rel="stylesheet" href="/styles/starlight.css" />',
13
+ '<link rel="stylesheet" href="/styles/highlight.css" />',
13
14
  '<script>(function(){',
14
15
  'var s=localStorage.getItem("sl-theme");',
15
16
  'var t=s||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");',