+35
-3
astro.config.mjs
+35
-3
astro.config.mjs
···
31
31
provider: fontProviders.fontsource(),
32
32
name: "IBM Plex Serif",
33
33
cssVariable: "--plex-serif",
34
-
fallbacks: [ 'Charter', 'Bitstream Charter', 'Sitka Text', 'Cambria', 'Georgia', "serif"],
34
+
},
35
+
{
36
+
provider: fontProviders.fontsource(),
37
+
name: "Libertinus Serif",
38
+
cssVariable: "--libertinus-serif",
39
+
},
40
+
{
41
+
provider: fontProviders.fontsource(),
42
+
name: "Noto Serif",
43
+
cssVariable: "--noto-serif",
44
+
},
45
+
{
46
+
provider: fontProviders.fontsource(),
47
+
name: "Lora",
48
+
cssVariable: "--lora",
35
49
},
36
50
{
37
51
provider: fontProviders.fontsource(),
38
-
name: "IBM Plex Mono",
52
+
name: "iA Writer Mono",
39
53
cssVariable: "--plex-mono",
54
+
},
55
+
{
56
+
provider: fontProviders.fontsource(),
57
+
name: "Intel One Mono",
58
+
cssVariable: "--intel-mono",
59
+
},
60
+
{
61
+
provider: fontProviders.fontsource(),
62
+
name: "Recursive",
63
+
cssVariable: "--recursive",
64
+
styles: ["oblique", "normal"],
65
+
weights: [300, 1000],
66
+
variationSettings: "'slnt' -15 0, 'CASL' 0 1, 'CRSV' 0 1, 'MONO' 0 1",
40
67
},
41
68
{
42
69
provider: fontProviders.fontsource(),
···
95
122
style: "italic",
96
123
},
97
124
],
98
-
}
125
+
},
126
+
{
127
+
provider: fontProviders.fontsource(),
128
+
name: "Jacquard 12",
129
+
cssVariable: "--jacquard-12",
130
+
},
99
131
],
100
132
},
101
133
});
+9
db/seed.ts
+9
db/seed.ts
···
4
4
await db.insert(Users).values([
5
5
{ id: 1, userDid: "test" },
6
6
{ id: 2, userDid: "another" },
7
+
{ userDid: "did:plc:dg2qmmjic7mmecrbvpuhtvh6", nickname: "haetae" },
7
8
]);
8
9
9
10
await db.insert(Works).values([
···
21
22
content: "<p>whoag i have <b>BOLD</b></p>",
22
23
tags: [{ label: "label", url: "#" }],
23
24
},
25
+
{
26
+
uri: "at://did:plc:dg2qmmjic7mmecrbvpuhtvh6/moe.fanfics.works/3lyeiyq32ek2o",
27
+
slug: "1236",
28
+
author: "did:plc:dg2qmmjic7mmecrbvpuhtvh6",
29
+
title: "testing title",
30
+
content: "what's up?! <b>bold</b> and <em>italics</em> should work.",
31
+
tags: "hey",
32
+
}
24
33
]);
25
34
}
+1
-2
src/actions/works.ts
+1
-2
src/actions/works.ts
···
158
158
});
159
159
}
160
160
161
-
// we'll just smush this in and pray
162
161
const result = await agent.com.atproto.repo.putRecord({
163
162
repo: work.author, // since the author will be a did
164
163
collection: "moe.fanfics.works",
···
167
166
title,
168
167
tags,
169
168
content,
169
+
createdAt: work.createdAt.toISOString(),
170
170
updatedAt: updatedAt.toISOString(),
171
171
},
172
172
validate: false,
173
-
swapRecord: rkey, // idk what this does
174
173
});
175
174
176
175
if (!result.success) {
+1
-17
src/assets/styles/global.css
+1
-17
src/assets/styles/global.css
···
30
30
--text-7xl: clamp(7.4506rem, 71.4115rem + -82.5302cqi, 52.8422rem);
31
31
--text-8xl: clamp(9.3132rem, 116.6654rem + -138.5189cqi, 85.4986rem);
32
32
--text-9xl: clamp(11.6415rem, 190.1667rem + -230.355cqi, 138.3368rem);
33
-
}
34
-
35
-
@custom-variant dark (&:where(
36
-
[data-theme=dark],
37
-
[data-theme=dracula],
38
-
[data-theme=synthwave],
39
-
[data-theme=halloween],
40
-
[data-theme=forest],
41
-
[data-theme=aqua],
42
-
[data-theme=black],
43
-
[data-theme=luxury],
44
-
[data-theme=business],
45
-
[data-theme=night],
46
-
[data-theme=coffee],
47
-
[data-theme=sunset],
48
-
[data-theme=abyss]
49
-
));
33
+
}
+1
-1
src/layouts/Layout.astro
+1
-1
src/layouts/Layout.astro
+2
-2
src/layouts/WorkPage.astro
+2
-2
src/layouts/WorkPage.astro
···
23
23
<a href="">previous chaptertitle</a>
24
24
)}
25
25
<!-- if theres more than one chapter, render this box -->
26
-
<select name="chapterSelect" id={`${slug}-chapters`}>
26
+
<select name="chapterSelect" id={`${slug}-chapters`} class="select">
27
27
<option value="default" selected>Choose chapter...</option>
28
28
<!-- map each chapter here -->
29
29
</select>
···
50
50
</div>
51
51
</header>
52
52
53
-
<section id={`${slug}-content`} class="prose lg:prose-xl">
53
+
<section id={`${slug}-content`}>
54
54
<!-- if work has its own style, render it here somehow -->
55
55
<details>
56
56
<summary>Author's notes</summary>
+1
-1
src/pages/not-found.astro
src/pages/errors/not-found.astro
+1
-1
src/pages/not-found.astro
src/pages/errors/not-found.astro
+62
-48
src/pages/works/[workId]/edit.astro
+62
-48
src/pages/works/[workId]/edit.astro
···
16
16
.limit(1);
17
17
18
18
if (!work) {
19
-
return Astro.redirect("/not-found");
19
+
return Astro.redirect("/errors/not-found");
20
20
}
21
21
22
22
if (!loggedInUser) {
···
24
24
}
25
25
26
26
if (work.Users.userDid !== loggedInUser.did) {
27
-
return Astro.redirect("/unauthorized");
27
+
return Astro.redirect("/errors/unauthorized");
28
28
}
29
29
30
30
const result = Astro.getActionResult(actions.worksActions.updateWork);
···
33
33
<Layout skipLink={`edit-${workId}`}>
34
34
<main id={`edit-${workId}`}>
35
35
<form action={actions.worksActions.updateWork} method="post">
36
-
<label for="title">title</label>
37
-
<input
38
-
type="text"
39
-
name="title"
40
-
id="title"
41
-
aria-describedby="title-error"
42
-
value={work.Works.title}
43
-
required
44
-
transition:persist
45
-
/>
46
-
{errors.title && (
47
-
<div id="title-error">
48
-
{errors.title}
49
-
</div>
50
-
)}
36
+
<fieldset class="fieldset">
37
+
<label for="title" class="label">Title</label>
38
+
<input
39
+
type="text"
40
+
name="title"
41
+
id="title"
42
+
class="input w-full"
43
+
aria-describedby="title-error"
44
+
value={work.Works.title}
45
+
required
46
+
transition:persist
47
+
/>
48
+
{errors.title && (
49
+
<div id="title-error">
50
+
{errors.title}
51
+
</div>
52
+
)}
53
+
</fieldset>
51
54
52
-
<label for="tags">add tags</label>
53
-
<input
54
-
type="text"
55
-
list="tags-list"
56
-
name="tags"
57
-
id="tags"
58
-
aria-describedby="tags-error"
59
-
value={work.Works.tags as string}
60
-
transition:persist
61
-
/>
62
-
<!-- could be cool to fetch tags from a tags server or smth? idk -->
63
-
<datalist id="tags-list">
64
-
<option value="test">here</option>
65
-
<option value="tag2">another</option>
66
-
<option value="tag3">try them all!</option>
67
-
</datalist>
68
-
{errors.tags && (
69
-
<div id="tags-error">
70
-
{errors.tags}
71
-
</div>
72
-
)}
55
+
<fieldset class="fieldset">
56
+
<label for="tags" class="label">Add tags</label>
57
+
<input
58
+
type="text"
59
+
list="tags-list"
60
+
name="tags"
61
+
id="tags"
62
+
class="input"
63
+
aria-describedby="tags-error"
64
+
value={work.Works.tags as string}
65
+
transition:persist
66
+
/>
67
+
<!-- could be cool to fetch tags from a tags server or smth? idk -->
68
+
<datalist id="tags-list">
69
+
<option value="test">here</option>
70
+
<option value="tag2">another</option>
71
+
<option value="tag3">try them all!</option>
72
+
</datalist>
73
+
{errors.tags && (
74
+
<div id="tags-error">
75
+
{errors.tags}
76
+
</div>
77
+
)}
78
+
</fieldset>
73
79
74
-
<label for="content">body</label>
75
-
<textarea name="content" id="content" aria-describedby="content-error" transition:persist>
76
-
{work.Works.content}
77
-
</textarea>
78
-
{errors.content && (
79
-
<div id="content-error">
80
-
{errors.content}
81
-
</div>
82
-
)}
80
+
<fieldset class="fieldset">
81
+
<label for="content" class="label">Body</label>
82
+
<textarea
83
+
name="content"
84
+
id="content"
85
+
class="textarea"
86
+
aria-describedby="content-error"
87
+
transition:persist
88
+
>
89
+
{work.Works.content.trim()}
90
+
</textarea>
91
+
{errors.content && (
92
+
<div id="content-error">
93
+
{errors.content}
94
+
</div>
95
+
)}
96
+
</fieldset>
83
97
84
-
<button>submit</button>
98
+
<button class="btn btn-primary">Submit</button>
85
99
</form>
86
100
87
101
{result?.error && (
+7
-4
src/pages/works/[workId]/index.astro
+7
-4
src/pages/works/[workId]/index.astro
···
6
6
const { workId } = Astro.params;
7
7
const loggedInUser = Astro.locals.loggedInUser;
8
8
9
-
// the work could be fetched from the database or from the pds
10
-
// would this potentially lighten db load? maybe
11
9
const [work] = await db.select()
12
10
.from(Works)
13
11
.where(eq(Works.slug, workId!))
···
21
19
<WorkPage
22
20
slug={work.Works.slug}
23
21
title={work.Works.title}
24
-
author={await didToHandle(work.Users.userDid)}
22
+
author={work.Users.nickname
23
+
? work.Users.nickname
24
+
: await didToHandle(work.Users.userDid)}
25
25
createdAt={work.Works.createdAt}
26
26
updatedAt={work.Works.updatedAt}
27
27
tags={work.Works.tags}
···
29
29
{(work.Users.userDid === loggedInUser?.did) && (
30
30
<a href={`/works/${workId}/edit`}>Edit your work</a>
31
31
)}
32
-
<Fragment set:html={work.Works.content} />
32
+
33
+
<div class="prose lg:prose-xl">
34
+
<Fragment set:html={work.Works.content} />
35
+
</div>
33
36
</WorkPage>
+11
-5
src/pages/works/index.astro
+11
-5
src/pages/works/index.astro
···
14
14
<Layout>
15
15
<h1 class="text-5xl">works</h1>
16
16
17
-
<main id="content">
17
+
<main id="content" class="flex flex-col gap-5 max-w-full">
18
18
{loggedInUser &&
19
19
<a href="/works/add">wanna write kid?</a>
20
20
}
21
-
21
+
22
22
{works.map(async ({ Works, Users }) => (
23
-
<article class="card">
23
+
<article class="card rounded-box shadow p-5">
24
24
<header>
25
25
<div class="flex items-center justify-between">
26
26
<hgroup class="flex-1">
27
27
<h2 class="card-title">
28
28
<a href={`/works/${Works.slug}`}>{Works.title}</a>
29
29
</h2>
30
-
<h3>{await didToHandle(Users.userDid)}</h3>
30
+
<h3>
31
+
{Users.nickname
32
+
? Users.nickname
33
+
: await didToHandle(Users.userDid)}
34
+
</h3>
31
35
</hgroup>
32
-
<time datetime={Works.createdAt.toISOString()}>{Works.createdAt}</time>
36
+
<time datetime={Works.createdAt.toISOString()}>
37
+
{Works.createdAt.toLocaleDateString()}
38
+
</time>
33
39
</div>
34
40
{JSON.stringify(Works.tags)}
35
41
{/* <ul class="flex flex-wrap gap-1.5">