getfilepress 0.1.1 → 0.1.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.
- package/README.md +10 -1
- package/package.json +1 -1
- package/packages/app/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/lib/components/NavIcon.svelte +19 -0
- package/packages/core/src/lib/components/SiteFooter.svelte +16 -2
- package/packages/core/src/lib/components/SiteHeader.svelte +14 -1
- package/packages/core/src/lib/config.ts +35 -1
- package/packages/core/src/lib/index.ts +2 -1
- package/packages/core/src/lib/styles/theme.css +19 -0
- package/packages/import/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,6 +84,15 @@ export default defineFilepressConfig({
|
|
|
84
84
|
author: 'Me',
|
|
85
85
|
postsPerPage: 10,
|
|
86
86
|
topics: [{ label: 'Essays', tag: 'essays' }],
|
|
87
|
+
nav: [
|
|
88
|
+
{ label: 'Posts', href: '/' },
|
|
89
|
+
{ label: 'GitHub', href: 'https://github.com/acme/site', icon: 'github' }
|
|
90
|
+
],
|
|
91
|
+
// Replaces the default RSS + Topics row when set.
|
|
92
|
+
footerLinks: [
|
|
93
|
+
{ label: 'RSS', href: '/rss.xml' },
|
|
94
|
+
{ label: 'GitHub', href: 'https://github.com/acme/site', icon: 'github' }
|
|
95
|
+
],
|
|
87
96
|
newsletter: {
|
|
88
97
|
url: 'https://buttondown.email/me',
|
|
89
98
|
blurb: 'Occasional notes.',
|
|
@@ -92,7 +101,7 @@ export default defineFilepressConfig({
|
|
|
92
101
|
});
|
|
93
102
|
```
|
|
94
103
|
|
|
95
|
-
`title` and `url` are required; missing values fail the build with a clear error.
|
|
104
|
+
`title` and `url` are required; missing values fail the build with a clear error. Nav/footer items may set `icon: 'github'` for a built-in mark.
|
|
96
105
|
|
|
97
106
|
## Theming
|
|
98
107
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@filepress/app",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The single SvelteKit app for filepress. Sites under sites/* supply config + posts + static; this package owns all routes.",
|
|
7
7
|
"scripts": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@filepress/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Reusable engine for filepress sites: content loader, Markdown pipeline, feed/sitemap builders, config helper, Essay theme, and shared Svelte components.",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { NavIconName } from '../config';
|
|
3
|
+
|
|
4
|
+
let { name }: { name: NavIconName } = $props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if name === 'github'}
|
|
8
|
+
<svg class="nav-icon" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false">
|
|
9
|
+
<path
|
|
10
|
+
fill="currentColor"
|
|
11
|
+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38
|
|
12
|
+
0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01
|
|
13
|
+
1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95
|
|
14
|
+
0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04
|
|
15
|
+
2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48
|
|
16
|
+
0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
17
|
+
/>
|
|
18
|
+
</svg>
|
|
19
|
+
{/if}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SiteConfig } from '../config';
|
|
3
|
+
import NavIcon from './NavIcon.svelte';
|
|
3
4
|
|
|
4
5
|
let { site, year = new Date().getFullYear() }: { site: SiteConfig; year?: number } = $props();
|
|
5
6
|
</script>
|
|
@@ -8,8 +9,21 @@
|
|
|
8
9
|
<div class="wrap">
|
|
9
10
|
<span>© {year} {site.author}</span>
|
|
10
11
|
<span class="footer-links">
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
{#each site.footerLinks as item (item.href)}
|
|
13
|
+
<a
|
|
14
|
+
href={item.href}
|
|
15
|
+
class:has-icon={Boolean(item.icon)}
|
|
16
|
+
class:nav-github={item.icon === 'github'}
|
|
17
|
+
{...(item.icon === 'github'
|
|
18
|
+
? { target: '_blank', rel: 'noopener noreferrer' }
|
|
19
|
+
: {})}
|
|
20
|
+
>
|
|
21
|
+
{#if item.icon}
|
|
22
|
+
<NavIcon name={item.icon} />
|
|
23
|
+
{/if}
|
|
24
|
+
<span class="nav-label">{item.label}</span>
|
|
25
|
+
</a>
|
|
26
|
+
{/each}
|
|
13
27
|
</span>
|
|
14
28
|
</div>
|
|
15
29
|
</footer>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SiteConfig } from '../config';
|
|
3
|
+
import NavIcon from './NavIcon.svelte';
|
|
3
4
|
|
|
4
5
|
let { site }: { site: SiteConfig } = $props();
|
|
5
6
|
</script>
|
|
@@ -21,7 +22,19 @@
|
|
|
21
22
|
</div>
|
|
22
23
|
<nav class="site-nav" aria-label="Primary">
|
|
23
24
|
{#each site.nav as item (item.href)}
|
|
24
|
-
<a
|
|
25
|
+
<a
|
|
26
|
+
href={item.href}
|
|
27
|
+
class:has-icon={Boolean(item.icon)}
|
|
28
|
+
class:nav-github={item.icon === 'github'}
|
|
29
|
+
{...(item.icon === 'github'
|
|
30
|
+
? { target: '_blank', rel: 'noopener noreferrer' }
|
|
31
|
+
: {})}
|
|
32
|
+
>
|
|
33
|
+
{#if item.icon}
|
|
34
|
+
<NavIcon name={item.icon} />
|
|
35
|
+
{/if}
|
|
36
|
+
<span class="nav-label">{item.label}</span>
|
|
37
|
+
</a>
|
|
25
38
|
{/each}
|
|
26
39
|
</nav>
|
|
27
40
|
</div>
|
|
@@ -22,9 +22,14 @@ export interface Topic {
|
|
|
22
22
|
tag: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/** Built-in icons that chrome can render beside a nav/footer label. */
|
|
26
|
+
export type NavIconName = 'github';
|
|
27
|
+
|
|
25
28
|
export interface NavItem {
|
|
26
29
|
label: string;
|
|
27
30
|
href: string;
|
|
31
|
+
/** Optional icon rendered with the label (e.g. GitHub mark). */
|
|
32
|
+
icon?: NavIconName;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
/** Fully-resolved site configuration (after defaults are applied). */
|
|
@@ -52,6 +57,11 @@ export interface SiteConfig {
|
|
|
52
57
|
*/
|
|
53
58
|
homePage: string | null;
|
|
54
59
|
nav: NavItem[];
|
|
60
|
+
/**
|
|
61
|
+
* Footer link row. Defaults to RSS + Topics when omitted.
|
|
62
|
+
* Pass an explicit list (including RSS/Topics if you still want them) to customize.
|
|
63
|
+
*/
|
|
64
|
+
footerLinks: NavItem[];
|
|
55
65
|
topics: Topic[];
|
|
56
66
|
newsletter: NewsletterConfig | null;
|
|
57
67
|
}
|
|
@@ -71,10 +81,33 @@ export interface SiteConfigInput {
|
|
|
71
81
|
/** Static page slug to show at `/` (post index then lives at `/writing`). */
|
|
72
82
|
homePage?: string;
|
|
73
83
|
nav?: NavItem[];
|
|
84
|
+
/** Custom footer links; replaces the default RSS + Topics row when set. */
|
|
85
|
+
footerLinks?: NavItem[];
|
|
74
86
|
topics?: Topic[];
|
|
75
87
|
newsletter?: NewsletterConfig | null;
|
|
76
88
|
}
|
|
77
89
|
|
|
90
|
+
const defaultFooterLinks: NavItem[] = [
|
|
91
|
+
{ label: 'RSS', href: '/rss.xml' },
|
|
92
|
+
{ label: 'Topics', href: '/topics' }
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
function normalizeNavItems(items: NavItem[] | undefined): NavItem[] | null {
|
|
96
|
+
if (!items?.length) return null;
|
|
97
|
+
return items.map((item) => {
|
|
98
|
+
const label = (item.label ?? '').trim();
|
|
99
|
+
const href = (item.href ?? '').trim();
|
|
100
|
+
if (!label || !href) {
|
|
101
|
+
throw new Error('filepress.config: nav/footerLinks entries need non-empty label and href.');
|
|
102
|
+
}
|
|
103
|
+
const icon = item.icon;
|
|
104
|
+
if (icon != null && icon !== 'github') {
|
|
105
|
+
throw new Error(`filepress.config: unsupported icon "${String(icon)}" (supported: github).`);
|
|
106
|
+
}
|
|
107
|
+
return icon ? { label, href, icon } : { label, href };
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
78
111
|
/** Path to page 1 of the chronological post index. */
|
|
79
112
|
export function postsIndexPath(site: Pick<SiteConfig, 'homePage'>): string {
|
|
80
113
|
return site.homePage ? '/writing' : '/';
|
|
@@ -131,7 +164,8 @@ export function defineFilepressConfig(input: SiteConfigInput): SiteConfig {
|
|
|
131
164
|
? Math.floor(input.postsPerPage as number)
|
|
132
165
|
: 10,
|
|
133
166
|
homePage,
|
|
134
|
-
nav: input.nav
|
|
167
|
+
nav: normalizeNavItems(input.nav) ?? defaultNav,
|
|
168
|
+
footerLinks: normalizeNavItems(input.footerLinks) ?? [...defaultFooterLinks],
|
|
135
169
|
topics: input.topics ?? [],
|
|
136
170
|
newsletter: input.newsletter ?? null
|
|
137
171
|
};
|
|
@@ -177,6 +177,24 @@ body {
|
|
|
177
177
|
color: var(--accent);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
.site-nav a.has-icon,
|
|
181
|
+
.site-footer a.has-icon {
|
|
182
|
+
display: inline-flex;
|
|
183
|
+
align-items: center;
|
|
184
|
+
gap: 0.4em;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.site-nav .nav-icon,
|
|
188
|
+
.site-footer .nav-icon {
|
|
189
|
+
display: block;
|
|
190
|
+
flex-shrink: 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.site-nav a.nav-github {
|
|
194
|
+
text-transform: none;
|
|
195
|
+
letter-spacing: 0.06em;
|
|
196
|
+
}
|
|
197
|
+
|
|
180
198
|
.site-footer {
|
|
181
199
|
border-top: 1px solid var(--rule);
|
|
182
200
|
margin-top: clamp(2.5rem, 6vw, 4rem);
|
|
@@ -195,6 +213,7 @@ body {
|
|
|
195
213
|
|
|
196
214
|
.site-footer .footer-links {
|
|
197
215
|
display: flex;
|
|
216
|
+
align-items: center;
|
|
198
217
|
gap: 1.1rem;
|
|
199
218
|
}
|
|
200
219
|
|