starlight-theme-nova 0.0.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.
- package/LICENSE +21 -0
- package/lib/shiki.css +77 -0
- package/lib/styles.gen.css +106 -0
- package/package.json +50 -0
- package/src/components/CodeCopy.astro +37 -0
- package/src/components/Header.astro +22 -0
- package/src/components/MarkdownContent.astro +16 -0
- package/src/components/MobileMenuToggle.astro +5 -0
- package/src/components/PageFrame.astro +77 -0
- package/src/components/Search.astro +107 -0
- package/src/components/SiteTitle.astro +12 -0
- package/src/components/SocialIcons.astro +23 -0
- package/src/components/ThemeProvider.astro +5 -0
- package/src/components/ThemeSelect.astro +5 -0
- package/src/components/TwoColumnContent.astro +43 -0
- package/src/index.ts +57 -0
- package/src/shiki-config.ts +22 -0
- package/src/shiki-transformer-copy-button.ts +52 -0
- package/src/types.d.ts +11 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ocavue
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/lib/shiki.css
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
.sl-markdown-content .astro-code {
|
|
2
|
+
padding: 0;
|
|
3
|
+
font-size: 0.875rem;
|
|
4
|
+
border-radius: 0.5rem;
|
|
5
|
+
overflow-x: hidden;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: stretch;
|
|
9
|
+
|
|
10
|
+
border-width: 1px;
|
|
11
|
+
border-style: solid;
|
|
12
|
+
border-color: var(--sl-color-gray-5);
|
|
13
|
+
|
|
14
|
+
code {
|
|
15
|
+
display: grid;
|
|
16
|
+
align-items: stretch;
|
|
17
|
+
overflow-x: hidden;
|
|
18
|
+
padding: 0.75rem 0;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
min-width: min-content;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.line {
|
|
24
|
+
min-height: 1lh;
|
|
25
|
+
margin-left: 0;
|
|
26
|
+
margin-right: 0;
|
|
27
|
+
padding-left: 1rem;
|
|
28
|
+
padding-right: 1rem;
|
|
29
|
+
display: inline-block;
|
|
30
|
+
flex: 1;
|
|
31
|
+
flex-grow: 1;
|
|
32
|
+
width: 100%;
|
|
33
|
+
min-width: min-content;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.line.highlighted,
|
|
37
|
+
.line.diff.add,
|
|
38
|
+
.line.diff.remove {
|
|
39
|
+
display: inline-block;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.line.highlighted {
|
|
43
|
+
background-color: rgb(107 114 128 / 0.2) /* #6b7280 */;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.line.diff.add {
|
|
47
|
+
background-color: rgb(34 197 94 / 0.5) /* #22c55e */;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.line.diff.remove {
|
|
51
|
+
background-color: rgb(239 68 68 / 0.5) /* #ef4444 */;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.astro-code {
|
|
56
|
+
background-color: var(--shiki-light-bg);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
html.dark .astro-code {
|
|
60
|
+
background-color: var(--shiki-dark-bg);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.astro-code,
|
|
64
|
+
.astro-code span {
|
|
65
|
+
color: var(--shiki-light);
|
|
66
|
+
font-style: var(--shiki-light-font-style);
|
|
67
|
+
font-weight: var(--shiki-light-font-weight);
|
|
68
|
+
text-decoration: var(--shiki-light-text-decoration);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
html.dark .astro-code,
|
|
72
|
+
html.dark .astro-code span {
|
|
73
|
+
color: var(--shiki-dark);
|
|
74
|
+
font-style: var(--shiki-dark-font-style);
|
|
75
|
+
font-weight: var(--shiki-dark-font-weight);
|
|
76
|
+
text-decoration: var(--shiki-dark-text-decoration);
|
|
77
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* layer: preflights */
|
|
2
|
+
*,::before,::after{--nova-rotate:0;--nova-rotate-x:0;--nova-rotate-y:0;--nova-rotate-z:0;--nova-scale-x:1;--nova-scale-y:1;--nova-scale-z:1;--nova-skew-x:0;--nova-skew-y:0;--nova-translate-x:0;--nova-translate-y:0;--nova-translate-z:0;--nova-pan-x: ;--nova-pan-y: ;--nova-pinch-zoom: ;--nova-scroll-snap-strictness:proximity;--nova-ordinal: ;--nova-slashed-zero: ;--nova-numeric-figure: ;--nova-numeric-spacing: ;--nova-numeric-fraction: ;--nova-border-spacing-x:0;--nova-border-spacing-y:0;--nova-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--nova-ring-shadow:0 0 rgb(0 0 0 / 0);--nova-shadow-inset: ;--nova-shadow:0 0 rgb(0 0 0 / 0);--nova-ring-inset: ;--nova-ring-offset-width:0px;--nova-ring-offset-color:#fff;--nova-ring-width:0px;--nova-ring-color:rgb(147 197 253 / 0.5);--nova-blur: ;--nova-brightness: ;--nova-contrast: ;--nova-drop-shadow: ;--nova-grayscale: ;--nova-hue-rotate: ;--nova-invert: ;--nova-saturate: ;--nova-sepia: ;--nova-backdrop-blur: ;--nova-backdrop-brightness: ;--nova-backdrop-contrast: ;--nova-backdrop-grayscale: ;--nova-backdrop-hue-rotate: ;--nova-backdrop-invert: ;--nova-backdrop-opacity: ;--nova-backdrop-saturate: ;--nova-backdrop-sepia: ;}::backdrop{--nova-rotate:0;--nova-rotate-x:0;--nova-rotate-y:0;--nova-rotate-z:0;--nova-scale-x:1;--nova-scale-y:1;--nova-scale-z:1;--nova-skew-x:0;--nova-skew-y:0;--nova-translate-x:0;--nova-translate-y:0;--nova-translate-z:0;--nova-pan-x: ;--nova-pan-y: ;--nova-pinch-zoom: ;--nova-scroll-snap-strictness:proximity;--nova-ordinal: ;--nova-slashed-zero: ;--nova-numeric-figure: ;--nova-numeric-spacing: ;--nova-numeric-fraction: ;--nova-border-spacing-x:0;--nova-border-spacing-y:0;--nova-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--nova-ring-shadow:0 0 rgb(0 0 0 / 0);--nova-shadow-inset: ;--nova-shadow:0 0 rgb(0 0 0 / 0);--nova-ring-inset: ;--nova-ring-offset-width:0px;--nova-ring-offset-color:#fff;--nova-ring-width:0px;--nova-ring-color:rgb(147 197 253 / 0.5);--nova-blur: ;--nova-brightness: ;--nova-contrast: ;--nova-drop-shadow: ;--nova-grayscale: ;--nova-hue-rotate: ;--nova-invert: ;--nova-saturate: ;--nova-sepia: ;--nova-backdrop-blur: ;--nova-backdrop-brightness: ;--nova-backdrop-contrast: ;--nova-backdrop-grayscale: ;--nova-backdrop-hue-rotate: ;--nova-backdrop-invert: ;--nova-backdrop-opacity: ;--nova-backdrop-saturate: ;--nova-backdrop-sepia: ;}
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
--sl-content-width: 50rem;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Dark mode Starlight theme variables. */
|
|
9
|
+
:root {
|
|
10
|
+
--sl-color-white: white;
|
|
11
|
+
--sl-color-gray-1: #e5e7eb;
|
|
12
|
+
--sl-color-gray-2: #d1d5db;
|
|
13
|
+
--sl-color-gray-3: #9ca3af;
|
|
14
|
+
--sl-color-gray-4: #4b5563;
|
|
15
|
+
--sl-color-gray-5: #374151;
|
|
16
|
+
--sl-color-gray-6: #1f2937;
|
|
17
|
+
--sl-color-black: #030712;
|
|
18
|
+
--sl-color-accent-low: #030712;
|
|
19
|
+
--sl-color-accent: #4b5563;
|
|
20
|
+
--sl-color-accent-high: #f3f4f6;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Light mode Starlight theme variables */
|
|
24
|
+
:root[data-theme='light'] {
|
|
25
|
+
--sl-color-white: #111827;
|
|
26
|
+
--sl-color-gray-1: #1f2937;
|
|
27
|
+
--sl-color-gray-2: #374151;
|
|
28
|
+
--sl-color-gray-3: #6b7280;
|
|
29
|
+
--sl-color-gray-4: #9ca3af;
|
|
30
|
+
--sl-color-gray-5: #d1d5db;
|
|
31
|
+
--sl-color-gray-6: #e5e7eb;
|
|
32
|
+
--sl-color-gray-7: #f3f4f6;
|
|
33
|
+
--sl-color-black: white;
|
|
34
|
+
--sl-color-accent-low: #e5e7eb;
|
|
35
|
+
--sl-color-accent: #1f2937;
|
|
36
|
+
--sl-color-accent-high: #111827;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Style the Markdown heading links. */
|
|
40
|
+
.sl-markdown-content
|
|
41
|
+
:is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *))
|
|
42
|
+
> a {
|
|
43
|
+
color: var(--sl-color-white);
|
|
44
|
+
text-decoration: none;
|
|
45
|
+
|
|
46
|
+
&:hover {
|
|
47
|
+
text-decoration: underline;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* layer: shortcuts */
|
|
52
|
+
.nova-code-copy-button-icon-check{--nova-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M20 6L9 17l-5-5'/%3E%3C/svg%3E");-webkit-mask:var(--nova-icon) no-repeat;mask:var(--nova-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;width:1em;height:1em;display:block;width:100%;height:100%;}
|
|
53
|
+
.nova-code-copy-button-icon-clipboard{--nova-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3C/g%3E%3C/svg%3E");-webkit-mask:var(--nova-icon) no-repeat;mask:var(--nova-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;width:1em;height:1em;display:block;width:100%;height:100%;}
|
|
54
|
+
.nova-code-container{position:relative;}
|
|
55
|
+
.nova-code-copy-button{position:absolute;right:0.5rem;top:0.5rem;margin:0;width:1.5rem;height:1.5rem;border-width:1px;border-color:var(--sl-color-gray-5);border-radius:0.25rem;border-style:solid;background-color:rgb(243 244 246 / 0.3) /* #f3f4f6 */;padding:0.25rem;--nova-text-opacity:1;color:rgb(0 0 0 / var(--nova-text-opacity)) /* #000 */;--nova-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--nova-backdrop-blur) var(--nova-backdrop-brightness) var(--nova-backdrop-contrast) var(--nova-backdrop-grayscale) var(--nova-backdrop-hue-rotate) var(--nova-backdrop-invert) var(--nova-backdrop-opacity) var(--nova-backdrop-saturate) var(--nova-backdrop-sepia);backdrop-filter:var(--nova-backdrop-blur) var(--nova-backdrop-brightness) var(--nova-backdrop-contrast) var(--nova-backdrop-grayscale) var(--nova-backdrop-hue-rotate) var(--nova-backdrop-invert) var(--nova-backdrop-opacity) var(--nova-backdrop-saturate) var(--nova-backdrop-sepia);transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
|
|
56
|
+
.nova-page-frame-header{position:fixed;inset:0;z-index:var(--sl-z-index-navbar);box-sizing:border-box;width:100%;height:var(--sl-nav-height);border-width:0px;border-bottom-width:1px;border-color:var(--sl-color-hairline);border-style:solid;background-color:rgb(255 255 255 / 0.8) /* #fff */;padding-top:var(--sl-nav-pad-y);padding-bottom:var(--sl-nav-pad-y);padding-left:var(--sl-nav-pad-x);padding-right:var(--sl-nav-pad-x);padding-inline-end:var(--sl-nav-pad-x);--nova-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--nova-backdrop-blur) var(--nova-backdrop-brightness) var(--nova-backdrop-contrast) var(--nova-backdrop-grayscale) var(--nova-backdrop-hue-rotate) var(--nova-backdrop-invert) var(--nova-backdrop-opacity) var(--nova-backdrop-saturate) var(--nova-backdrop-sepia);backdrop-filter:var(--nova-backdrop-blur) var(--nova-backdrop-brightness) var(--nova-backdrop-contrast) var(--nova-backdrop-grayscale) var(--nova-backdrop-hue-rotate) var(--nova-backdrop-invert) var(--nova-backdrop-opacity) var(--nova-backdrop-saturate) var(--nova-backdrop-sepia);}
|
|
57
|
+
.nova-header-title{margin:-0.25rem;min-width:0;display:flex;overflow:clip;padding:0.25rem;}
|
|
58
|
+
.nova-header{box-sizing:border-box;height:100%;display:flex;align-items:center;gap:0.5rem;}
|
|
59
|
+
.nova-header-actions{display:none;align-items:center;gap:0.5rem;}
|
|
60
|
+
.nova-page-frame{min-height:100vh;display:flex;flex-direction:column;}
|
|
61
|
+
.nova-header-search{display:flex;}
|
|
62
|
+
.nova-code-copy-button:active{--nova-scale-x:0.9;--nova-scale-y:0.9;transform:translateX(var(--nova-translate-x)) translateY(var(--nova-translate-y)) translateZ(var(--nova-translate-z)) rotate(var(--nova-rotate)) rotateX(var(--nova-rotate-x)) rotateY(var(--nova-rotate-y)) rotateZ(var(--nova-rotate-z)) skewX(var(--nova-skew-x)) skewY(var(--nova-skew-y)) scaleX(var(--nova-scale-x)) scaleY(var(--nova-scale-y)) scaleZ(var(--nova-scale-z));}
|
|
63
|
+
.dark .nova-code-copy-button{background-color:rgb(75 85 99 / 0.3) /* #4b5563 */;--nova-text-opacity:1;color:rgb(255 255 255 / var(--nova-text-opacity)) /* #fff */;}
|
|
64
|
+
.dark .nova-page-frame-header{background-color:rgb(3 7 18 / 0.5) /* #030712 */;}
|
|
65
|
+
.dark .nova-code-copy-button:hover{background-color:rgb(107 114 128 / 0.5) /* #6b7280 */;}
|
|
66
|
+
.nova-code-copy-button:hover{background-color:rgb(229 231 235 / 0.5) /* #e5e7eb */;}
|
|
67
|
+
[data-has-sidebar] .nova-page-frame-header{padding-inline-end:calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size));}
|
|
68
|
+
.nova-code-container .nova-code-copy-button{opacity:0;}
|
|
69
|
+
.nova-code-container:hover .nova-code-copy-button{opacity:1;}
|
|
70
|
+
@media print{
|
|
71
|
+
.nova-header-actions,
|
|
72
|
+
.nova-header-search{display:none;}
|
|
73
|
+
}
|
|
74
|
+
@media (min-width: 50rem){
|
|
75
|
+
.nova-header-search{max-width:15rem;flex:1 1 0%;}
|
|
76
|
+
.nova-header-actions{display:flex;}
|
|
77
|
+
}
|
|
78
|
+
/* layer: default */
|
|
79
|
+
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;}
|
|
80
|
+
.visible{visibility:visible;}
|
|
81
|
+
.fixed{position:fixed;}
|
|
82
|
+
.relative{position:relative;}
|
|
83
|
+
.isolate{isolation:isolate;}
|
|
84
|
+
.me{margin-inline-end:1rem;}
|
|
85
|
+
.hidden{display:none;}
|
|
86
|
+
.size-8{width:2rem;height:2rem;}
|
|
87
|
+
.flex{display:flex;}
|
|
88
|
+
.flex-1{flex:1 1 0%;}
|
|
89
|
+
.transform{transform:translateX(var(--nova-translate-x)) translateY(var(--nova-translate-y)) translateZ(var(--nova-translate-z)) rotate(var(--nova-rotate)) rotateX(var(--nova-rotate-x)) rotateY(var(--nova-rotate-y)) rotateZ(var(--nova-rotate-z)) skewX(var(--nova-skew-x)) skewY(var(--nova-skew-y)) scaleX(var(--nova-scale-x)) scaleY(var(--nova-scale-y)) scaleZ(var(--nova-scale-z));}
|
|
90
|
+
.rounded-md{border-radius:0.375rem;}
|
|
91
|
+
.hover\:bg-gray-400\/30:hover{background-color:rgb(156 163 175 / 0.3) /* #9ca3af */;}
|
|
92
|
+
.p-2{padding:0.5rem;}
|
|
93
|
+
.color-\[var\(--sl-color-text\)\]{color:var(--sl-color-text) /* var(--sl-color-text) */;}
|
|
94
|
+
.backdrop-filter{-webkit-backdrop-filter:var(--nova-backdrop-blur) var(--nova-backdrop-brightness) var(--nova-backdrop-contrast) var(--nova-backdrop-grayscale) var(--nova-backdrop-hue-rotate) var(--nova-backdrop-invert) var(--nova-backdrop-opacity) var(--nova-backdrop-saturate) var(--nova-backdrop-sepia);backdrop-filter:var(--nova-backdrop-blur) var(--nova-backdrop-brightness) var(--nova-backdrop-contrast) var(--nova-backdrop-grayscale) var(--nova-backdrop-hue-rotate) var(--nova-backdrop-invert) var(--nova-backdrop-opacity) var(--nova-backdrop-saturate) var(--nova-backdrop-sepia);}
|
|
95
|
+
.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
|
|
96
|
+
.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
|
|
97
|
+
.ease-out{transition-timing-function:cubic-bezier(0, 0, 0.2, 1);}
|
|
98
|
+
@media print{
|
|
99
|
+
.print\:hidden{display:none;}
|
|
100
|
+
}
|
|
101
|
+
@media (min-width: 50rem){
|
|
102
|
+
.md\:border-r{border-right-width:1px;}
|
|
103
|
+
}
|
|
104
|
+
@media (min-width: 72rem){
|
|
105
|
+
.lg\:flex{display:flex;}
|
|
106
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "starlight-theme-nova",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "",
|
|
6
|
+
"author": "ocavue <ocavue@gmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"funding": "https://github.com/sponsors/ocavue",
|
|
9
|
+
"homepage": "https://github.com/ocavue/starlight-theme-nova#readme",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/ocavue/starlight-theme-nova.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": "https://github.com/ocavue/starlight-theme-nova/issues",
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./src/index.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./src/index.ts",
|
|
20
|
+
"./*.css": "./lib/*.css",
|
|
21
|
+
"./components/*.astro": "./src/components/*.astro"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"lib",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@shikijs/transformers": "^1.29.0",
|
|
29
|
+
"astro-theme-toggle": "^0.5.1",
|
|
30
|
+
"hast-util-is-element": "^3.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@astrojs/starlight": "^0.32.2",
|
|
34
|
+
"@iconify-json/bxl": "^1.0.0",
|
|
35
|
+
"@iconify-json/logos": "^1.2.4",
|
|
36
|
+
"@iconify-json/lucide": "^1.2.22",
|
|
37
|
+
"@iconify-json/tabler": "^1.0.0",
|
|
38
|
+
"@ocavue/tsconfig": "^0.2.0",
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"@unocss/cli": "^66.0.0",
|
|
41
|
+
"astro": "^5.1.5",
|
|
42
|
+
"typescript": "^5.7.2",
|
|
43
|
+
"unocss": "^66.0.0",
|
|
44
|
+
"unocss-preset-animations": "^1.1.1"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"dev": "unocss -w",
|
|
48
|
+
"build": "unocss"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
const DEFAULT_HTML = /* html */ `<span class="nova-code-copy-button-icon-clipboard"></span>`
|
|
3
|
+
const SUCCESS_HTML = /* html */ `<span class="nova-code-copy-button-icon-check"></span>`
|
|
4
|
+
|
|
5
|
+
class CodeCopyButton extends HTMLElement {
|
|
6
|
+
constructor() {
|
|
7
|
+
super()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connectedCallback() {
|
|
11
|
+
this.innerHTML = DEFAULT_HTML
|
|
12
|
+
|
|
13
|
+
const update = () => {
|
|
14
|
+
this.innerHTML = state === 'ready' ? DEFAULT_HTML : SUCCESS_HTML
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let state: 'ready' | 'success' = 'ready'
|
|
18
|
+
update()
|
|
19
|
+
|
|
20
|
+
this.addEventListener('click', () => {
|
|
21
|
+
if (state === 'ready') {
|
|
22
|
+
state = 'success'
|
|
23
|
+
update()
|
|
24
|
+
|
|
25
|
+
const code = this.dataset.code || ''
|
|
26
|
+
void navigator.clipboard.writeText(code)
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
state = 'ready'
|
|
29
|
+
update()
|
|
30
|
+
}, 1500)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
customElements.define('nova-code-copy-button', CodeCopyButton)
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
import LanguageSelect from '@astrojs/starlight/components/LanguageSelect.astro'
|
|
3
|
+
import Search from './Search.astro'
|
|
4
|
+
import SiteTitle from './SiteTitle.astro'
|
|
5
|
+
import SocialIcons from './SocialIcons.astro'
|
|
6
|
+
import ThemeSelect from './ThemeSelect.astro'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<div class="nova-header">
|
|
10
|
+
<div class="nova-header-title">
|
|
11
|
+
<SiteTitle />
|
|
12
|
+
</div>
|
|
13
|
+
<div class="flex-1"></div>
|
|
14
|
+
<div class="nova-header-search">
|
|
15
|
+
<Search />
|
|
16
|
+
</div>
|
|
17
|
+
<div class="nova-header-actions">
|
|
18
|
+
<SocialIcons />
|
|
19
|
+
<LanguageSelect />
|
|
20
|
+
<ThemeSelect />
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from '@astrojs/starlight/components/MarkdownContent.astro'
|
|
3
|
+
import CodeCopy from './CodeCopy.astro'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<Default>
|
|
7
|
+
<slot />
|
|
8
|
+
</Default>
|
|
9
|
+
|
|
10
|
+
<style is:global>
|
|
11
|
+
:root .sl-markdown-content dd:not(:where(.not-content *)) {
|
|
12
|
+
padding-inline-start: 2rem;
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
15
|
+
|
|
16
|
+
<CodeCopy />
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
import MobileMenuToggle from './MobileMenuToggle.astro'
|
|
3
|
+
|
|
4
|
+
const { hasSidebar } = Astro.locals.starlightRoute
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<div class="nova-page-frame">
|
|
8
|
+
<header class="nova-page-frame-header">
|
|
9
|
+
<slot name="header" />
|
|
10
|
+
</header>
|
|
11
|
+
{
|
|
12
|
+
hasSidebar && (
|
|
13
|
+
<nav
|
|
14
|
+
class="sidebar print:hidden"
|
|
15
|
+
aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}
|
|
16
|
+
>
|
|
17
|
+
<MobileMenuToggle />
|
|
18
|
+
<div
|
|
19
|
+
id="starlight__sidebar"
|
|
20
|
+
class:list={['sidebar-pane', 'md:border-r border-border']}
|
|
21
|
+
>
|
|
22
|
+
<div class="sidebar-content sl-flex">
|
|
23
|
+
<slot name="sidebar" />
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</nav>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
<div class="main-frame"><slot /></div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
.sidebar-pane {
|
|
34
|
+
visibility: var(--sl-sidebar-visibility, hidden);
|
|
35
|
+
position: fixed;
|
|
36
|
+
z-index: var(--sl-z-index-menu);
|
|
37
|
+
inset-block: var(--sl-nav-height) 0;
|
|
38
|
+
inset-inline-start: 0;
|
|
39
|
+
width: 100%;
|
|
40
|
+
background-color: var(--sl-color-black);
|
|
41
|
+
overflow-y: auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:global([aria-expanded='true']) ~ .sidebar-pane {
|
|
45
|
+
--sl-sidebar-visibility: visible;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.sidebar-content {
|
|
49
|
+
height: 100%;
|
|
50
|
+
min-height: max-content;
|
|
51
|
+
padding: 1rem var(--sl-sidebar-pad-x) 0;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 1rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@media (min-width: 50rem) {
|
|
57
|
+
.sidebar-content::after {
|
|
58
|
+
content: '';
|
|
59
|
+
padding-bottom: 1px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.main-frame {
|
|
64
|
+
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
|
|
65
|
+
padding-inline-start: var(--sl-content-inline-start);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (min-width: 50rem) {
|
|
69
|
+
:global([data-has-sidebar]) .header {
|
|
70
|
+
padding-inline-end: var(--sl-nav-pad-x);
|
|
71
|
+
}
|
|
72
|
+
.sidebar-pane {
|
|
73
|
+
--sl-sidebar-visibility: visible;
|
|
74
|
+
width: var(--sl-sidebar-width);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from '@astrojs/starlight/components/Search.astro'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<Default />
|
|
6
|
+
|
|
7
|
+
<style is:global>
|
|
8
|
+
::backdrop {
|
|
9
|
+
--sl-color-backdrop-overlay: hsl(0 0 0% / 0.9);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
[data-theme='light'] ::backdrop {
|
|
13
|
+
--sl-color-backdrop-overlay: hsl(0 0 100% / 0.9);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
17
|
+
site-search {
|
|
18
|
+
& dialog,
|
|
19
|
+
& dialog::backdrop {
|
|
20
|
+
transition:
|
|
21
|
+
display 300ms allow-discrete,
|
|
22
|
+
overlay 300ms allow-discrete;
|
|
23
|
+
animation-timing-function: ease-out;
|
|
24
|
+
animation-fill-mode: forwards;
|
|
25
|
+
will-change: transform, opacity, backdrop-filter, background-color;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
& dialog {
|
|
29
|
+
animation-name: sl-ocavue-search-dialog-zoom-out;
|
|
30
|
+
animation-duration: 150ms;
|
|
31
|
+
transform: translateZ(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
& dialog[open] {
|
|
35
|
+
animation-duration: 50ms;
|
|
36
|
+
animation-name: sl-ocavue-search-dialog-zoom-in;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
& dialog::backdrop {
|
|
40
|
+
-webkit-backdrop-filter: none;
|
|
41
|
+
backdrop-filter: none;
|
|
42
|
+
|
|
43
|
+
animation-duration: 150ms;
|
|
44
|
+
animation-name: sl-ocavue-search-backdrop-fade-out;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
& dialog[open]::backdrop {
|
|
48
|
+
animation-duration: 50ms;
|
|
49
|
+
animation-name: sl-ocavue-search-backdrop-fade-in;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@keyframes sl-ocavue-search-backdrop-fade-in {
|
|
55
|
+
from {
|
|
56
|
+
background-color: transparent;
|
|
57
|
+
}
|
|
58
|
+
to {
|
|
59
|
+
background-color: var(--sl-color-backdrop-overlay);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@keyframes sl-ocavue-search-backdrop-fade-out {
|
|
64
|
+
from {
|
|
65
|
+
background-color: var(--sl-color-backdrop-overlay);
|
|
66
|
+
}
|
|
67
|
+
to {
|
|
68
|
+
background-color: transparent;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
:root {
|
|
73
|
+
--sl-ocavue-search-dialog-zoom-scale: 1;
|
|
74
|
+
--sl-ocavue-search-dialog-zoom-translate-y: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (min-width: 72rem) {
|
|
78
|
+
:root {
|
|
79
|
+
--sl-ocavue-search-dialog-zoom-scale: 0.95;
|
|
80
|
+
--sl-ocavue-search-dialog-zoom-translate-y: -1rem;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@keyframes sl-ocavue-search-dialog-zoom-in {
|
|
85
|
+
from {
|
|
86
|
+
opacity: 0;
|
|
87
|
+
transform: scale(var(--sl-ocavue-search-dialog-zoom-scale))
|
|
88
|
+
translateY(var(--sl-ocavue-search-dialog-zoom-translate-y));
|
|
89
|
+
}
|
|
90
|
+
to {
|
|
91
|
+
opacity: 1;
|
|
92
|
+
transform: scale(1) translateY(0);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes sl-ocavue-search-dialog-zoom-out {
|
|
97
|
+
from {
|
|
98
|
+
opacity: 1;
|
|
99
|
+
transform: scale(1) translateY(0);
|
|
100
|
+
}
|
|
101
|
+
to {
|
|
102
|
+
opacity: 0;
|
|
103
|
+
transform: scale(var(--sl-ocavue-search-dialog-zoom-scale))
|
|
104
|
+
translateY(var(--sl-ocavue-search-dialog-zoom-translate-y));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Default from '@astrojs/starlight/components/SiteTitle.astro'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<span class="site-title-wrapper"><Default /></span>
|
|
6
|
+
|
|
7
|
+
<style>
|
|
8
|
+
.site-title-wrapper :global(.site-title) {
|
|
9
|
+
color: var(--sl-color-text);
|
|
10
|
+
font-size: var(--sl-text-h5);
|
|
11
|
+
}
|
|
12
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from '@astrojs/starlight/components'
|
|
3
|
+
import config from 'virtual:starlight/user-config'
|
|
4
|
+
|
|
5
|
+
type Platform = keyof NonNullable<typeof config.social>
|
|
6
|
+
type SocialConfig = NonNullable<NonNullable<typeof config.social>[Platform]>
|
|
7
|
+
const links = Object.entries(config.social || {}) as [Platform, SocialConfig][]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
links.length > 0 && (
|
|
12
|
+
<>
|
|
13
|
+
{
|
|
14
|
+
links.map(([platform, { label, url }]) => (
|
|
15
|
+
<a href={url} rel="me" class="flex p-2 transition hover:bg-gray-400/30 rounded-md size-8 color-[var(--sl-color-text)]">
|
|
16
|
+
<span class="sr-only">{label}</span>
|
|
17
|
+
<Icon name={platform} size="1rem" color="currentColor" />
|
|
18
|
+
</a>
|
|
19
|
+
))
|
|
20
|
+
}
|
|
21
|
+
</>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<div class="lg:flex">
|
|
2
|
+
{
|
|
3
|
+
Astro.locals.starlightRoute.toc && (
|
|
4
|
+
<aside class="right-sidebar-container print:hidden">
|
|
5
|
+
<div class="right-sidebar">
|
|
6
|
+
<slot name="right-sidebar" />
|
|
7
|
+
</div>
|
|
8
|
+
</aside>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
<div class="main-pane"><slot /></div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
.main-pane {
|
|
16
|
+
isolation: isolate;
|
|
17
|
+
overflow-x: hidden;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@media (min-width: 72rem) {
|
|
21
|
+
.right-sidebar-container {
|
|
22
|
+
order: 2;
|
|
23
|
+
position: relative;
|
|
24
|
+
width: var(--sl-sidebar-width);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.right-sidebar {
|
|
28
|
+
position: fixed;
|
|
29
|
+
top: 0;
|
|
30
|
+
right: 0;
|
|
31
|
+
border-inline-start: 1px solid var(--sl-color-hairline);
|
|
32
|
+
padding-top: var(--sl-nav-height);
|
|
33
|
+
height: 100vh;
|
|
34
|
+
width: var(--sl-sidebar-width);
|
|
35
|
+
overflow-y: auto;
|
|
36
|
+
scrollbar-width: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.main-pane {
|
|
40
|
+
flex: 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</style>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StarlightPlugin,
|
|
3
|
+
StarlightUserConfig,
|
|
4
|
+
} from '@astrojs/starlight/types'
|
|
5
|
+
|
|
6
|
+
import { shikiConfig } from './shiki-config'
|
|
7
|
+
|
|
8
|
+
const components = {
|
|
9
|
+
Header: 'starlight-theme-nova/components/Header.astro',
|
|
10
|
+
Search: 'starlight-theme-nova/components/Search.astro',
|
|
11
|
+
ThemeProvider: 'starlight-theme-nova/components/ThemeProvider.astro',
|
|
12
|
+
ThemeSelect: 'starlight-theme-nova/components/ThemeSelect.astro',
|
|
13
|
+
SocialIcons: 'starlight-theme-nova/components/SocialIcons.astro',
|
|
14
|
+
SiteTitle: 'starlight-theme-nova/components/SiteTitle.astro',
|
|
15
|
+
PageFrame: 'starlight-theme-nova/components/PageFrame.astro',
|
|
16
|
+
MobileMenuToggle: 'starlight-theme-nova/components/MobileMenuToggle.astro',
|
|
17
|
+
TwoColumnContent: 'starlight-theme-nova/components/TwoColumnContent.astro',
|
|
18
|
+
MarkdownContent: 'starlight-theme-nova/components/MarkdownContent.astro',
|
|
19
|
+
} as const
|
|
20
|
+
|
|
21
|
+
export default function starlightThemeNova(): StarlightPlugin {
|
|
22
|
+
return {
|
|
23
|
+
name: 'starlight-theme-nova',
|
|
24
|
+
hooks: {
|
|
25
|
+
setup({ config, updateConfig, addIntegration }) {
|
|
26
|
+
const newConfig = {
|
|
27
|
+
customCss: [
|
|
28
|
+
// Including any user CSS *after* our own.
|
|
29
|
+
'starlight-theme-nova/styles.gen.css',
|
|
30
|
+
'starlight-theme-nova/shiki.css',
|
|
31
|
+
...(config.customCss || []),
|
|
32
|
+
],
|
|
33
|
+
components: {
|
|
34
|
+
// Including any user components *after* our own.
|
|
35
|
+
...components,
|
|
36
|
+
...config.components,
|
|
37
|
+
},
|
|
38
|
+
expressiveCode: config.expressiveCode ?? false,
|
|
39
|
+
} satisfies Partial<StarlightUserConfig>
|
|
40
|
+
updateConfig(newConfig)
|
|
41
|
+
|
|
42
|
+
addIntegration({
|
|
43
|
+
name: 'starlight-theme-nova-integration',
|
|
44
|
+
hooks: {
|
|
45
|
+
'astro:config:setup': ({ updateConfig }) => {
|
|
46
|
+
updateConfig({
|
|
47
|
+
markdown: {
|
|
48
|
+
shikiConfig,
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
transformerMetaHighlight,
|
|
3
|
+
transformerNotationDiff,
|
|
4
|
+
transformerNotationHighlight,
|
|
5
|
+
} from '@shikijs/transformers'
|
|
6
|
+
import type { ShikiConfig } from 'astro'
|
|
7
|
+
|
|
8
|
+
import { transformerCopyButton } from './shiki-transformer-copy-button'
|
|
9
|
+
|
|
10
|
+
export const shikiConfig: ShikiConfig = {
|
|
11
|
+
themes: {
|
|
12
|
+
light: 'one-light',
|
|
13
|
+
dark: 'one-dark-pro',
|
|
14
|
+
},
|
|
15
|
+
defaultColor: false,
|
|
16
|
+
transformers: [
|
|
17
|
+
transformerNotationDiff(),
|
|
18
|
+
transformerNotationHighlight(),
|
|
19
|
+
transformerMetaHighlight(),
|
|
20
|
+
transformerCopyButton(),
|
|
21
|
+
],
|
|
22
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ShikiConfig } from 'astro'
|
|
2
|
+
import { isElement } from 'hast-util-is-element'
|
|
3
|
+
|
|
4
|
+
type ShikiTransformer = NonNullable<ShikiConfig['transformers']>[number]
|
|
5
|
+
|
|
6
|
+
const name = 'starlight-theme-nova-shiki-transformer-copy-button'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A transformer that adds a copy button to code blocks.
|
|
10
|
+
*/
|
|
11
|
+
export function transformerCopyButton(): ShikiTransformer {
|
|
12
|
+
return {
|
|
13
|
+
name: name,
|
|
14
|
+
root(node) {
|
|
15
|
+
if (node.children.length !== 1) {
|
|
16
|
+
throw new Error(`[${name}] Expected exactly one child`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const pre = node.children[0]
|
|
20
|
+
if (!isElement(pre, 'pre')) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`[${name}] Expected a <pre> element but got ${JSON.stringify({ ...pre, children: '...' })}`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
node.children = [
|
|
27
|
+
{
|
|
28
|
+
type: 'element',
|
|
29
|
+
tagName: 'div',
|
|
30
|
+
properties: {
|
|
31
|
+
class: 'nova-code-container not-content',
|
|
32
|
+
},
|
|
33
|
+
children: [
|
|
34
|
+
pre,
|
|
35
|
+
{
|
|
36
|
+
type: 'element',
|
|
37
|
+
tagName: 'nova-code-copy-button',
|
|
38
|
+
properties: {
|
|
39
|
+
type: 'button',
|
|
40
|
+
'data-code': this.source,
|
|
41
|
+
title: 'Copy code',
|
|
42
|
+
'aria-label': 'Copy code',
|
|
43
|
+
class: 'nova-code-copy-button',
|
|
44
|
+
},
|
|
45
|
+
children: [],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/types.d.ts
ADDED