+35
-3
astro.config.mjs
+35
-3
astro.config.mjs
···
31
provider: fontProviders.fontsource(),
32
name: "IBM Plex Serif",
33
cssVariable: "--plex-serif",
34
-
fallbacks: [ 'Charter', 'Bitstream Charter', 'Sitka Text', 'Cambria', 'Georgia', "serif"],
35
},
36
{
37
provider: fontProviders.fontsource(),
38
-
name: "IBM Plex Mono",
39
cssVariable: "--plex-mono",
40
},
41
{
42
provider: fontProviders.fontsource(),
···
95
style: "italic",
96
},
97
],
98
-
}
99
],
100
},
101
});
···
31
provider: fontProviders.fontsource(),
32
name: "IBM Plex Serif",
33
cssVariable: "--plex-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",
49
},
50
{
51
provider: fontProviders.fontsource(),
52
+
name: "iA Writer Mono",
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",
67
},
68
{
69
provider: fontProviders.fontsource(),
···
122
style: "italic",
123
},
124
],
125
+
},
126
+
{
127
+
provider: fontProviders.fontsource(),
128
+
name: "Jacquard 12",
129
+
cssVariable: "--jacquard-12",
130
+
},
131
],
132
},
133
});
+9
db/seed.ts
+9
db/seed.ts
···
4
await db.insert(Users).values([
5
{ id: 1, userDid: "test" },
6
{ id: 2, userDid: "another" },
7
+
{ userDid: "did:plc:dg2qmmjic7mmecrbvpuhtvh6", nickname: "haetae" },
8
]);
9
10
await db.insert(Works).values([
···
22
content: "<p>whoag i have <b>BOLD</b></p>",
23
tags: [{ label: "label", url: "#" }],
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
+
}
33
]);
34
}
+1
-2
src/actions/works.ts
+1
-2
src/actions/works.ts
···
158
});
159
}
160
161
-
// we'll just smush this in and pray
162
const result = await agent.com.atproto.repo.putRecord({
163
repo: work.author, // since the author will be a did
164
collection: "moe.fanfics.works",
···
167
title,
168
tags,
169
content,
170
updatedAt: updatedAt.toISOString(),
171
},
172
validate: false,
173
-
swapRecord: rkey, // idk what this does
174
});
175
176
if (!result.success) {
···
158
});
159
}
160
161
const result = await agent.com.atproto.repo.putRecord({
162
repo: work.author, // since the author will be a did
163
collection: "moe.fanfics.works",
···
166
title,
167
tags,
168
content,
169
+
createdAt: work.createdAt.toISOString(),
170
updatedAt: updatedAt.toISOString(),
171
},
172
validate: false,
173
});
174
175
if (!result.success) {
+1
-17
src/assets/styles/global.css
+1
-17
src/assets/styles/global.css
···
30
--text-7xl: clamp(7.4506rem, 71.4115rem + -82.5302cqi, 52.8422rem);
31
--text-8xl: clamp(9.3132rem, 116.6654rem + -138.5189cqi, 85.4986rem);
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
-
));
+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
<a href="">previous chaptertitle</a>
24
)}
25
<!-- if theres more than one chapter, render this box -->
26
-
<select name="chapterSelect" id={`${slug}-chapters`}>
27
<option value="default" selected>Choose chapter...</option>
28
<!-- map each chapter here -->
29
</select>
···
50
</div>
51
</header>
52
53
-
<section id={`${slug}-content`} class="prose lg:prose-xl">
54
<!-- if work has its own style, render it here somehow -->
55
<details>
56
<summary>Author's notes</summary>
···
23
<a href="">previous chaptertitle</a>
24
)}
25
<!-- if theres more than one chapter, render this box -->
26
+
<select name="chapterSelect" id={`${slug}-chapters`} class="select">
27
<option value="default" selected>Choose chapter...</option>
28
<!-- map each chapter here -->
29
</select>
···
50
</div>
51
</header>
52
53
+
<section id={`${slug}-content`}>
54
<!-- if work has its own style, render it here somehow -->
55
<details>
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
.limit(1);
17
18
if (!work) {
19
-
return Astro.redirect("/not-found");
20
}
21
22
if (!loggedInUser) {
···
24
}
25
26
if (work.Users.userDid !== loggedInUser.did) {
27
-
return Astro.redirect("/unauthorized");
28
}
29
30
const result = Astro.getActionResult(actions.worksActions.updateWork);
···
33
<Layout skipLink={`edit-${workId}`}>
34
<main id={`edit-${workId}`}>
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
-
)}
51
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
-
)}
73
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
-
)}
83
84
-
<button>submit</button>
85
</form>
86
87
{result?.error && (
···
16
.limit(1);
17
18
if (!work) {
19
+
return Astro.redirect("/errors/not-found");
20
}
21
22
if (!loggedInUser) {
···
24
}
25
26
if (work.Users.userDid !== loggedInUser.did) {
27
+
return Astro.redirect("/errors/unauthorized");
28
}
29
30
const result = Astro.getActionResult(actions.worksActions.updateWork);
···
33
<Layout skipLink={`edit-${workId}`}>
34
<main id={`edit-${workId}`}>
35
<form action={actions.worksActions.updateWork} method="post">
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>
54
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>
79
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>
97
98
+
<button class="btn btn-primary">Submit</button>
99
</form>
100
101
{result?.error && (
+7
-4
src/pages/works/[workId]/index.astro
+7
-4
src/pages/works/[workId]/index.astro
···
6
const { workId } = Astro.params;
7
const loggedInUser = Astro.locals.loggedInUser;
8
9
-
// the work could be fetched from the database or from the pds
10
-
// would this potentially lighten db load? maybe
11
const [work] = await db.select()
12
.from(Works)
13
.where(eq(Works.slug, workId!))
···
21
<WorkPage
22
slug={work.Works.slug}
23
title={work.Works.title}
24
-
author={await didToHandle(work.Users.userDid)}
25
createdAt={work.Works.createdAt}
26
updatedAt={work.Works.updatedAt}
27
tags={work.Works.tags}
···
29
{(work.Users.userDid === loggedInUser?.did) && (
30
<a href={`/works/${workId}/edit`}>Edit your work</a>
31
)}
32
-
<Fragment set:html={work.Works.content} />
33
</WorkPage>
···
6
const { workId } = Astro.params;
7
const loggedInUser = Astro.locals.loggedInUser;
8
9
const [work] = await db.select()
10
.from(Works)
11
.where(eq(Works.slug, workId!))
···
19
<WorkPage
20
slug={work.Works.slug}
21
title={work.Works.title}
22
+
author={work.Users.nickname
23
+
? work.Users.nickname
24
+
: await didToHandle(work.Users.userDid)}
25
createdAt={work.Works.createdAt}
26
updatedAt={work.Works.updatedAt}
27
tags={work.Works.tags}
···
29
{(work.Users.userDid === loggedInUser?.did) && (
30
<a href={`/works/${workId}/edit`}>Edit your work</a>
31
)}
32
+
33
+
<div class="prose lg:prose-xl">
34
+
<Fragment set:html={work.Works.content} />
35
+
</div>
36
</WorkPage>
+11
-5
src/pages/works/index.astro
+11
-5
src/pages/works/index.astro
···
14
<Layout>
15
<h1 class="text-5xl">works</h1>
16
17
-
<main id="content">
18
{loggedInUser &&
19
<a href="/works/add">wanna write kid?</a>
20
}
21
-
22
{works.map(async ({ Works, Users }) => (
23
-
<article class="card">
24
<header>
25
<div class="flex items-center justify-between">
26
<hgroup class="flex-1">
27
<h2 class="card-title">
28
<a href={`/works/${Works.slug}`}>{Works.title}</a>
29
</h2>
30
-
<h3>{await didToHandle(Users.userDid)}</h3>
31
</hgroup>
32
-
<time datetime={Works.createdAt.toISOString()}>{Works.createdAt}</time>
33
</div>
34
{JSON.stringify(Works.tags)}
35
{/* <ul class="flex flex-wrap gap-1.5">
···
14
<Layout>
15
<h1 class="text-5xl">works</h1>
16
17
+
<main id="content" class="flex flex-col gap-5 max-w-full">
18
{loggedInUser &&
19
<a href="/works/add">wanna write kid?</a>
20
}
21
+
22
{works.map(async ({ Works, Users }) => (
23
+
<article class="card rounded-box shadow p-5">
24
<header>
25
<div class="flex items-center justify-between">
26
<hgroup class="flex-1">
27
<h2 class="card-title">
28
<a href={`/works/${Works.slug}`}>{Works.title}</a>
29
</h2>
30
+
<h3>
31
+
{Users.nickname
32
+
? Users.nickname
33
+
: await didToHandle(Users.userDid)}
34
+
</h3>
35
</hgroup>
36
+
<time datetime={Works.createdAt.toISOString()}>
37
+
{Works.createdAt.toLocaleDateString()}
38
+
</time>
39
</div>
40
{JSON.stringify(Works.tags)}
41
{/* <ul class="flex flex-wrap gap-1.5">