duto 0.261001.0 → 0.261011.0
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/package.json +1 -1
- package/src/commands/templates/pages/components/html.tsx +55 -0
- package/src/commands/templates/pages/components/theme-select.tsx +60 -0
- package/src/commands/templates/pages/i18n/en.ts +6 -0
- package/src/commands/templates/pages/i18n/pt-br.ts +6 -0
- package/src/commands/templates/pages/pages/404.tsx +1 -1
- package/src/commands/templates/pages/pages/blog/[slug].tsx +88 -0
- package/src/commands/templates/pages/pages/blog/index.tsx +74 -0
- package/src/commands/templates/pages/pages/index.tsx +17 -7
- package/src/commands/templates/pages/pages/layout.tsx +5 -3
- package/src/commands/templates/pages/public/favicon.ico +0 -0
- package/src/commands/templates/pages/public/favicon.svg +1 -0
- package/src/commands/templates/pages/style.css +110 -7
- package/src/commands/templates/pages/tsconfig.json +2 -0
- package/src/pages/cli/utils.ts +5 -2
- package/src/pages/client/app.tsx +177 -28
- package/src/pages/client/link.ts +15 -41
- package/src/pages/client/storage.ts +1 -1
- package/src/pages/client/theme.ts +49 -0
- package/src/pages/compiler/constants.ts +2 -0
- package/src/pages/compiler/index.ts +41 -20
- package/src/pages/compiler/meta.ts +7 -31
- package/src/pages/compiler/types.ts +15 -24
- package/src/pages/compiler/vite-plugin.ts +25 -34
- package/src/pages/components/link.tsx +11 -19
- package/src/pages/i18n/client.ts +3 -4
- package/src/pages/index.ts +4 -21
- package/src/server/app.ts +2 -1
- package/src/pages/client/router.ts +0 -63
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { t } from 'duto'
|
|
2
|
+
|
|
3
|
+
export type Props = {
|
|
4
|
+
children?: preact.ComponentChildren
|
|
5
|
+
class?: string
|
|
6
|
+
|
|
7
|
+
locale?: string
|
|
8
|
+
name?: string
|
|
9
|
+
title: string
|
|
10
|
+
description?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Html({
|
|
14
|
+
children,
|
|
15
|
+
class: className,
|
|
16
|
+
|
|
17
|
+
locale = 'en',
|
|
18
|
+
name = 'Duto',
|
|
19
|
+
title,
|
|
20
|
+
description = '',
|
|
21
|
+
...props
|
|
22
|
+
}: Props) {
|
|
23
|
+
const _class = [
|
|
24
|
+
'bg-gradient-to-br from-zinc-200 to-zinc-150 dark:from-zinc-800 dark:to-zinc-950 text-gray-600 dark:text-gray-300',
|
|
25
|
+
className,
|
|
26
|
+
].filter(Boolean).join(' ')
|
|
27
|
+
|
|
28
|
+
const _title = [name, t(title ?? '')].filter(Boolean).join(' › ')
|
|
29
|
+
const _description = t(description || title || '')
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<html class={_class} {...props}>
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="utf-8" />
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
36
|
+
|
|
37
|
+
<link rel="icon" type="image/x-icon" sizes="48x48" href="/favicon.ico" />
|
|
38
|
+
<link rel="icon" type="image/svg+xml" sizes="any" href="/favicon.svg" />
|
|
39
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
40
|
+
|
|
41
|
+
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" />
|
|
42
|
+
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)" />
|
|
43
|
+
|
|
44
|
+
{/* <link rel="sitemap" href="/sitemap-index.xml" /> */}
|
|
45
|
+
|
|
46
|
+
<title dangerouslySetInnerHTML={{ __html: _title }} />
|
|
47
|
+
{_description && <meta name="description" content={_description} />}
|
|
48
|
+
</head>
|
|
49
|
+
|
|
50
|
+
<body class="flex flex-col min-h-svh max-w-screen overflow-x-clip relative z-10 ">
|
|
51
|
+
{children}
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useSignal } from '@preact/signals'
|
|
2
|
+
import { useLayoutEffect } from 'preact/hooks'
|
|
3
|
+
import { t, getTheme, setTheme, onThemeChange } from 'duto'
|
|
4
|
+
|
|
5
|
+
import Select from './select'
|
|
6
|
+
|
|
7
|
+
function ThemeIcon(props: any) {
|
|
8
|
+
return (
|
|
9
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={[props.class || '', props.className || ''].filter(Boolean).join(' ')}><path d="M11 17a4 4 0 0 1-8 0V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2Z"/><path d="M16.7 13H19a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H7"/><path d="M 7 17h.01"/><path d="m11 8 2.3-2.3a2.4 2.4 0 0 1 3.404.004L18.6 7.6a2.4 2.4 0 0 1 .026 3.434L9.9 19.8"/></svg>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function ThemeSelect({ class: className = '' }) {
|
|
14
|
+
const options = [['auto', t`auto`], ['dark', t`dark`], ['light', t`light`]].map(([value, label]) => ({ value, label, selected: false }))
|
|
15
|
+
const selectedValue = useSignal(getTheme())
|
|
16
|
+
let unsubscribe: (() => void)[] = []
|
|
17
|
+
const handle = (e: any, theme?: string) => {
|
|
18
|
+
theme ??= getTheme()
|
|
19
|
+
selectedValue.value = theme
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
useLayoutEffect(() => {
|
|
23
|
+
unsubscribe = onThemeChange(handle)
|
|
24
|
+
selectedValue.value = getTheme()
|
|
25
|
+
|
|
26
|
+
window.addEventListener('popstate', handle)
|
|
27
|
+
return () => {
|
|
28
|
+
window.removeEventListener('popstate', handle)
|
|
29
|
+
unsubscribe.forEach(unsub => unsub?.())
|
|
30
|
+
}
|
|
31
|
+
}, [])
|
|
32
|
+
|
|
33
|
+
const handleChange = async (e: Event) => {
|
|
34
|
+
const target = e.target as HTMLSelectElement
|
|
35
|
+
if (target) {
|
|
36
|
+
setTheme(target.value)
|
|
37
|
+
selectedValue.value = target.value
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const containerClasses = [
|
|
42
|
+
'locale-select overflow-hidden text-ellipsis p-1 rounded-lg hover:bg-neutral-200 hover:text-zinc-900',
|
|
43
|
+
className,
|
|
44
|
+
].filter(Boolean).join(' ')
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Select
|
|
48
|
+
Icon={ThemeIcon}
|
|
49
|
+
options={options}
|
|
50
|
+
value={selectedValue.value}
|
|
51
|
+
width="6.25em"
|
|
52
|
+
class="theme-select outline-none text-ellipsis absolute inset-0 opacity-0 cursor-pointer"
|
|
53
|
+
containerClass={containerClasses}
|
|
54
|
+
id="theme-select"
|
|
55
|
+
onchange={handleChange}
|
|
56
|
+
width="6.25em"
|
|
57
|
+
id="theme-select"
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -5,6 +5,9 @@ export default {
|
|
|
5
5
|
notfound: 'not-found',
|
|
6
6
|
},
|
|
7
7
|
messages: {
|
|
8
|
+
auto: 'System default',
|
|
9
|
+
dark: 'Dark theme',
|
|
10
|
+
light: 'Light theme',
|
|
8
11
|
title: 'Welcome to Duto',
|
|
9
12
|
description: 'A fast, lightweight framework that keeps things simple!',
|
|
10
13
|
gotodocs: 'Go to docs',
|
|
@@ -13,5 +16,8 @@ export default {
|
|
|
13
16
|
backhome: 'Back to homepage',
|
|
14
17
|
about: 'About',
|
|
15
18
|
shop: 'Shop',
|
|
19
|
+
|
|
20
|
+
blog: 'Blog',
|
|
21
|
+
b1: 'This page is also static, the data was loaded via',
|
|
16
22
|
},
|
|
17
23
|
}
|
|
@@ -5,6 +5,9 @@ export default {
|
|
|
5
5
|
notfound: 'pagina-desconhecida',
|
|
6
6
|
},
|
|
7
7
|
messages: {
|
|
8
|
+
auto: 'Padrão do sistema',
|
|
9
|
+
dark: 'Escuro',
|
|
10
|
+
light: 'Claro',
|
|
8
11
|
title: 'Bem-vindo ao Duto',
|
|
9
12
|
description: 'Um framework rápido e leve que mantém as coisas simples!',
|
|
10
13
|
gotodocs: 'Documentação',
|
|
@@ -13,5 +16,8 @@ export default {
|
|
|
13
16
|
backhome: 'Ir para página inicial',
|
|
14
17
|
about: 'Sobre',
|
|
15
18
|
shop: 'Loja',
|
|
19
|
+
|
|
20
|
+
blog: 'Blog',
|
|
21
|
+
b1: 'Esta página também é estática, os dados foram carregados via',
|
|
16
22
|
},
|
|
17
23
|
}
|
|
@@ -3,7 +3,7 @@ import { t, Link } from 'duto'
|
|
|
3
3
|
export default function NotFound() {
|
|
4
4
|
return (
|
|
5
5
|
<div class="flex flex-col items-center justify-center text-center w-full text-lg py-16">
|
|
6
|
-
<h1 class="text-sky-
|
|
6
|
+
<h1 class="text-sky-gradient font-mono text-7xl lg:text-8xl">404</h1>
|
|
7
7
|
<p class="body-large mb-2 mt-4 text-3xl lg:text-4xl">{t`notfound`}</p>
|
|
8
8
|
<p class="body lg:body-large">{t`sorry404`}</p>
|
|
9
9
|
<Link to="/" class="border-b border-current mt-8">{t`backhome`}</Link>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
|
|
2
|
+
import { t, Link } from 'duto'
|
|
3
|
+
import type { Post, Posts } from '@/types'
|
|
4
|
+
import { JSJSON } from '../../../../packages/t0n/src/json'
|
|
5
|
+
import NotFound from '../404'
|
|
6
|
+
|
|
7
|
+
// Mock data
|
|
8
|
+
const ALL_POSTS: Posts = [
|
|
9
|
+
{
|
|
10
|
+
id: "hello-world",
|
|
11
|
+
title: "Hello World",
|
|
12
|
+
body:
|
|
13
|
+
"Welcome to my blog! This post was statically generated at build time using `getStaticProps`. When navigating here via the SPA router, `getProps` fetches the same data dynamically.",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "getting-started",
|
|
17
|
+
title: "Getting Started with SSG",
|
|
18
|
+
body:
|
|
19
|
+
"Static site generation combines the best of both worlds: fast initial loads with the flexibility of dynamic data fetching.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "signals-islands",
|
|
23
|
+
title: "Signals & Islands",
|
|
24
|
+
body:
|
|
25
|
+
"Preact Signals give us fine-grained reactivity. Combined with the islands architecture, we can ship near-zero JS by default.",
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export { Html } from '@/components/html'
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns one entry per post.
|
|
34
|
+
* Each entry MUST include the route param (`slug`) plus any page props.
|
|
35
|
+
* The build script uses `slug` to compute the output path.
|
|
36
|
+
*/
|
|
37
|
+
export function getStaticProps() {
|
|
38
|
+
return ALL_POSTS.map((post) => ({
|
|
39
|
+
slug: post.id, // required: matches [slug] in the filename
|
|
40
|
+
post,
|
|
41
|
+
}))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Browser-time data ─────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Called during SPA navigation. Receives route params from wouter.
|
|
48
|
+
*/
|
|
49
|
+
export async function getProps({ slug }: { slug?: string }) {
|
|
50
|
+
const post = (await (await fetch('https://jsonplaceholder.typicode.com/posts/'+ slug)).json())
|
|
51
|
+
return { post }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Component ─────────────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
type Props = {
|
|
57
|
+
post?: Post | null,
|
|
58
|
+
slug?: string,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default function BlogPost({ post, slug }: Props) {
|
|
62
|
+
if (!post?.title)
|
|
63
|
+
return <NotFound />
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<article class="mt-6 max-w-2xl">
|
|
68
|
+
<div>
|
|
69
|
+
<Link href="/blog">
|
|
70
|
+
←
|
|
71
|
+
<span class="ml-2">{t`blog`}</span>
|
|
72
|
+
</Link>
|
|
73
|
+
</div>
|
|
74
|
+
<h1 class="mt-2 text-4xl font-black text-sky-gradient">{post.title}</h1>
|
|
75
|
+
<p class="mt-6 leading-relaxed">{post.body}</p>
|
|
76
|
+
</article>
|
|
77
|
+
|
|
78
|
+
<div class="mt-12 p-4 rounded-lg text-sm bg-sky-50 text-sky-700 dark:bg-zinc-700 dark:text-white/80">
|
|
79
|
+
{[
|
|
80
|
+
['Route', window.$duto?.route?.pattern],
|
|
81
|
+
['Route path', window.$duto?.route?.path],
|
|
82
|
+
['Route params', JSJSON(window?.$duto?.route?.params || {})],
|
|
83
|
+
['Route search params', window?.$duto?.search?.toString()],
|
|
84
|
+
].map(([label, info]) => ( <div><b>{label}:</b> {info || 'n/a'}</div>))}
|
|
85
|
+
</div>
|
|
86
|
+
</>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// 'use client'
|
|
2
|
+
import { t, Link } from 'duto'
|
|
3
|
+
import type { Posts } from '@/types'
|
|
4
|
+
// import { Counter } from '@/components/counter2'
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
posts?: Posts,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { Html } from '@/components/html'
|
|
11
|
+
|
|
12
|
+
export async function getStaticProps() {
|
|
13
|
+
const posts = (await (await fetch('https://jsonplaceholder.typicode.com/posts')).json())
|
|
14
|
+
?.sort(() => 0.5 - Math.random())
|
|
15
|
+
.slice(0, 10)
|
|
16
|
+
|
|
17
|
+
return { posts }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function Blog({ posts }: Props) {
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<h1 class="title">Blog</h1>
|
|
24
|
+
|
|
25
|
+
<div class="highlight">
|
|
26
|
+
{t`b1`} <b><i>getStaticProps()</i></b> (SSG)
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
{!posts?.length && (
|
|
30
|
+
<p class="mt-8">No posts found.</p>
|
|
31
|
+
)}
|
|
32
|
+
|
|
33
|
+
<div class="mt-8 flex flex-col">
|
|
34
|
+
{posts?.map((post) => (
|
|
35
|
+
<Link to={`/blog/${post.id}`} key={post.id}
|
|
36
|
+
class="group block relative p-4 border-b border-gray-200 dark:border-zinc-700/70 transition-all duration-300 ease-out"
|
|
37
|
+
>
|
|
38
|
+
<div class="flex items-start gap-4">
|
|
39
|
+
<span class="text-sm font-mono text-gray-400 group-hover:text-sky-gradient mt-0.5 min-w-[24px]">
|
|
40
|
+
#{post.id}
|
|
41
|
+
</span>
|
|
42
|
+
|
|
43
|
+
<div class="flex-1 min-w-0">
|
|
44
|
+
<h2 class="text-base font-medium group-hover:text-sky-gradient">
|
|
45
|
+
{post.title}
|
|
46
|
+
</h2>
|
|
47
|
+
<p class="mt-1 text-sm text-gray-500 group-hover:text-gray-800 dark:group-hover:text-gray-400 line-clamp-2 pr-6">
|
|
48
|
+
{post.body}
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div class="absolute right-0 top-[35%] flex-shrink-0 bg-indigo-50 group-hover:bg-gray-100 dark:bg-gray-600/90 dark:group-hover:bg-gray-500 rounded-full p-1 transition-all duration-300 group-hover:translate-x-1">
|
|
53
|
+
<svg
|
|
54
|
+
class="size-5 text-gray-400 group-hover:text-sky-500 dark:group-hover:text-gray-300"
|
|
55
|
+
fill="none"
|
|
56
|
+
stroke="currentColor"
|
|
57
|
+
viewBox="0 0 24 24"
|
|
58
|
+
>
|
|
59
|
+
<path
|
|
60
|
+
strokeLinecap="round"
|
|
61
|
+
strokeLinejoin="round"
|
|
62
|
+
strokeWidth={2}
|
|
63
|
+
d="M9 5l7 7-7 7"
|
|
64
|
+
/>
|
|
65
|
+
</svg>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
</Link>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
</>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { t, Link } from 'duto'
|
|
2
2
|
|
|
3
|
+
export { Html } from '@/components/html'
|
|
4
|
+
|
|
3
5
|
export default function Index() {
|
|
4
6
|
return (
|
|
5
|
-
<div class="relative">
|
|
6
|
-
<h1 class="title text-sky-
|
|
7
|
+
<div class="relative space-y-8">
|
|
8
|
+
<h1 class="title text-sky-gradient lg:mt-2">{t`title`}</h1>
|
|
7
9
|
<p class="sub-title">{t`description`}</p>
|
|
8
|
-
<div class="
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
<div class="divide-y divide-gray-300 dark:divide-zinc-700/70">
|
|
11
|
+
{[
|
|
12
|
+
['https://hub.bi/docs/duto', t`gotodocs`],
|
|
13
|
+
['/blog', t`Blog`],
|
|
14
|
+
].map(([href, label]) => (
|
|
15
|
+
<Link
|
|
16
|
+
href={href}
|
|
17
|
+
class="group flex items-center justify-between gap-4 py-4 px-2.5"
|
|
18
|
+
>
|
|
19
|
+
<span class="font-semibold group-hover:text-sky-gradient">{label}</span>
|
|
20
|
+
<span class="text-lg mr-3 transition-transform group-hover:text-sky-600 dark:group-hover:text-sky-300 group-hover:translate-x-1">→</span>
|
|
21
|
+
</Link>
|
|
22
|
+
))}
|
|
13
23
|
</div>
|
|
14
24
|
</div>
|
|
15
25
|
)
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { ComponentChildren } from 'preact'
|
|
2
2
|
|
|
3
3
|
import LocaleSelect from '@/components/locale-select'
|
|
4
|
+
import ThemeSelect from '@/components/theme-select'
|
|
4
5
|
|
|
5
6
|
export default function Layout({ children }: { children: ComponentChildren }) {
|
|
6
7
|
return (
|
|
7
|
-
|
|
8
|
+
<>
|
|
8
9
|
<div class='absolute top-10 right-10'>
|
|
10
|
+
<ThemeSelect class='mr-5' />
|
|
9
11
|
<LocaleSelect />
|
|
10
12
|
</div>
|
|
11
13
|
|
|
12
|
-
<main class="flex justify-center items-center px-6 py-16 min-h-[calc(100vh-3.75rem)]" children={children} />
|
|
14
|
+
<main class="flex flex-col justify-center items-center px-6 py-16 min-h-[calc(100vh-3.75rem)]" children={children} />
|
|
13
15
|
|
|
14
16
|
<footer class="py-[1.1rem] relative text-base lex flex-row items-center mx-auto">
|
|
15
17
|
<a href="https://hub.bi?ref=duto" class="no-underline group flex items-center justify-center" target="_blank" rel="noopener noreferrer">
|
|
@@ -19,6 +21,6 @@ export default function Layout({ children }: { children: ComponentChildren }) {
|
|
|
19
21
|
HUB</span>
|
|
20
22
|
</a>
|
|
21
23
|
</footer>
|
|
22
|
-
|
|
24
|
+
</>
|
|
23
25
|
)
|
|
24
26
|
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="840" height="840" viewBox="0 0 840 840" fill="none"><style>path{color:black}@media (prefers-color-scheme: dark){path{color:#c6c6c6}}</style><path fill-rule="evenodd" clip-rule="evenodd" d="M832.965 446.25C837.573 438.269 839.999 429.216 839.999 420C839.999 410.784 837.573 401.731 832.965 393.75L645.173 78.75C640.565 70.7692 633.938 64.1418 625.957 59.534C617.976 54.9262 608.923 52.5002 599.708 52.5H240.293C231.077 52.5002 222.024 54.9262 214.043 59.534C206.063 64.1418 199.435 70.7692 194.828 78.75L7.03518 393.75C2.42735 401.731 0.001544 410.784 0.001544 420C0.001544 429.216 2.42735 438.269 7.03518 446.25L194.828 761.25C199.435 769.231 206.063 775.858 214.043 780.466C222.024 785.074 231.077 787.5 240.293 787.5H599.708C608.923 787.5 617.976 785.074 625.957 780.466C633.938 775.858 640.565 769.231 645.173 761.25L832.965 446.25Z" fill="currentColor"></path></svg>
|
|
@@ -1,19 +1,122 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
* {@apply border-border}
|
|
7
|
+
/* body {@apply bg-background text-foreground} */
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@utility s {
|
|
11
|
+
@apply w-[--value] h-[--value];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@variant children (& > *);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@theme {
|
|
18
|
+
--font-sans:
|
|
19
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
|
|
20
|
+
"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla",
|
|
21
|
+
sans-serif;
|
|
22
|
+
--font-mono:
|
|
23
|
+
ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas,
|
|
24
|
+
"Liberation Mono", monospace;
|
|
25
|
+
--font-serif:
|
|
26
|
+
Georgia, Garamond, Cambria, "Times New Roman", Times, ui-serif, serif;
|
|
27
|
+
|
|
28
|
+
--font-pixel: PixAntiqua, ui-sans-serif, system-ui, sans-serif;
|
|
29
|
+
/* Medium headers*/
|
|
30
|
+
/* sohne, "Helvetica Neue", Helvetica, Arial, sans-serif; */
|
|
31
|
+
|
|
32
|
+
--color-black: #0D0F14;
|
|
33
|
+
--color-white: #fff;
|
|
34
|
+
--color-text: #F041FF;
|
|
35
|
+
|
|
36
|
+
--color-accent: var(--color-gray-100);
|
|
37
|
+
/* --color-primary: #3F224D; */
|
|
38
|
+
--color-muted: #545864;
|
|
39
|
+
|
|
40
|
+
--color-border: var(--color-gray-200);
|
|
41
|
+
--color-grid: #e5e5e5;
|
|
42
|
+
|
|
43
|
+
--color-blue: #3245FF;
|
|
44
|
+
--color-purple: #BC52EE;
|
|
45
|
+
--color-purple-dark: #3F224D;
|
|
46
|
+
--color-red: #D83333;
|
|
47
|
+
--color-pink: #F041FF;
|
|
48
|
+
--color-pink-light: #E8C4F9;
|
|
49
|
+
--color-orange: #F8E42E;
|
|
50
|
+
--color-yellow: #FF7D54;
|
|
51
|
+
--color-hover: #E8C4F9;
|
|
52
|
+
--color-green: #4AF2C8;
|
|
53
|
+
|
|
54
|
+
--color-dark-100: #858B98;
|
|
55
|
+
--color-dark-300: #343841;
|
|
56
|
+
--color-dark-400: #18191E;
|
|
57
|
+
--color-dark-500: #4B4B4B;
|
|
58
|
+
--color-dark-600: #2C2C2C;
|
|
59
|
+
--color-dark-700: #11172A;
|
|
60
|
+
--color-dark-800: #0C0F19;
|
|
61
|
+
--color-dark-900: #060913;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
--sky-grad-1: rgb(2, 132, 199);
|
|
65
|
+
--sky-grad-2: rgb(14, 165, 233);
|
|
66
|
+
--sky-grad-3: rgb(56, 189, 248);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dark {
|
|
70
|
+
--color-white: #F2F6FA;
|
|
71
|
+
|
|
72
|
+
--color-accent: #17191E;
|
|
73
|
+
--color-primary: #BC52EE;
|
|
74
|
+
--color-muted: #BFC1C9;
|
|
75
|
+
|
|
76
|
+
--color-border: color-mix(in oklab, var(--color-gray-100) 10%, transparent);
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
--sky-grad-1: rgb(112, 209, 255);
|
|
80
|
+
--sky-grad-2: rgb(173, 228, 255);
|
|
81
|
+
--sky-grad-3: rgb(219, 243, 255);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* App styles */
|
|
85
|
+
|
|
3
86
|
html, body {
|
|
4
|
-
@apply overscroll-none
|
|
87
|
+
@apply overscroll-y-none;
|
|
88
|
+
/* font-size-adjust: ex-height 0.5; */
|
|
89
|
+
|
|
90
|
+
/* Standardize line height across different system fonts */
|
|
91
|
+
/* line-height: 1.5; */
|
|
92
|
+
/* Consider setting letter-spacing for more consistent text blocks */
|
|
93
|
+
/* letter-spacing: 0.01em; */
|
|
94
|
+
|
|
95
|
+
/* Advanced font metric overrides (modern browsers only) */
|
|
96
|
+
/* ascent-override: 90%;
|
|
97
|
+
descent-override: 25%;
|
|
98
|
+
line-gap-override: 0%; */
|
|
5
99
|
}
|
|
6
100
|
|
|
7
|
-
.text-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
101
|
+
.text-sky-gradient,
|
|
102
|
+
.group-hover\:text-sky-gradient:is(:where(.group):hover *),
|
|
103
|
+
.hover\:text-sky-gradient:hover {
|
|
104
|
+
@apply bg-clip-text text-transparent;
|
|
105
|
+
background-image: radial-gradient(
|
|
106
|
+
138.06% 1036.51% at 95.25% -2.54%,
|
|
107
|
+
var(--sky-grad-1) 14.06%,
|
|
108
|
+
var(--sky-grad-2) 51.02%,
|
|
109
|
+
var(--sky-grad-3) 79.09%
|
|
110
|
+
);
|
|
11
111
|
}
|
|
12
112
|
|
|
13
|
-
.text-sky-
|
|
113
|
+
/* .text-sky-gradient,
|
|
114
|
+
.group-hover\:text-sky-gradient:is(:where(.group):hover *),
|
|
115
|
+
.hover\:text-sky-gradient:hover
|
|
116
|
+
{
|
|
14
117
|
@apply bg-clip-text text-transparent;
|
|
15
118
|
background-image: radial-gradient(138.06% 1036.51% at 95.25% -2.54%, rgb(112, 209, 255) 14.06%, rgb(173, 228, 255) 51.02%, rgb(219, 243, 255) 79.09%);
|
|
16
|
-
}
|
|
119
|
+
} */
|
|
17
120
|
|
|
18
121
|
.title {
|
|
19
122
|
@apply text-4xl lg:text-[3.5rem] leading-[1.1] font-semibold tracking-tight text-balance
|
package/src/pages/cli/utils.ts
CHANGED
|
@@ -8,7 +8,7 @@ import config from '#/compiler/vite-plugin'
|
|
|
8
8
|
import { _root } from '@/utils'
|
|
9
9
|
import { match, match404 } from '#/compiler/routes'
|
|
10
10
|
import type { Metadata, PluginOptions } from '#/compiler/types'
|
|
11
|
-
import { EXTENSIONS_LIST } from '#/compiler/constants'
|
|
11
|
+
import { EXTENSIONS_LIST, NF_NAMES } from '#/compiler/constants'
|
|
12
12
|
|
|
13
13
|
export async function getConfig(name = 'config'): Promise<UserConfig & PluginOptions> {
|
|
14
14
|
let opts = {} as UserConfig
|
|
@@ -85,17 +85,20 @@ export function preview(
|
|
|
85
85
|
res = new Response(file)
|
|
86
86
|
} else {
|
|
87
87
|
matched = match(path)
|
|
88
|
-
|
|
88
|
+
const r404 = match404(meta, path) || '404'
|
|
89
|
+
route = matched?.name || r404
|
|
89
90
|
res = new Response(null, { status: 404 })
|
|
90
91
|
|
|
91
92
|
for (const asset of [
|
|
92
93
|
path,
|
|
93
94
|
join(path, 'index'),
|
|
95
|
+
r404,
|
|
94
96
|
'index',
|
|
95
97
|
]) {
|
|
96
98
|
file = Bun.file(join(_root, dist, asset +'.html'))
|
|
97
99
|
|
|
98
100
|
if (await file.exists()) {
|
|
101
|
+
NF_NAMES.includes(asset.split('/').pop()!) && (route = r404)
|
|
99
102
|
res = new Response(file)
|
|
100
103
|
break
|
|
101
104
|
}
|