-1
src/lib/index.ts
-1
src/lib/index.ts
···
1
-
// place files you want to import through the `$lib` alias in this folder.
+7
src/routes/+layout.svelte
+7
src/routes/+layout.svelte
+6
-2
src/routes/~/+layout.svelte
+6
-2
src/routes/~/+layout.svelte
···
18
18
</script>
19
19
20
20
<header class="header">
21
-
<h1><a href="/~/">hello, {data.name}</a></h1>
22
-
<button onclick={logOut}>log out</button>
21
+
<h1><a href="/~/">athost</a></h1>
22
+
<button popovertarget="menu">menu</button>
23
+
<div id="menu" class="menu" popover>
24
+
<p>hello, {data.name}</p>
25
+
<button onclick={logOut}>log out</button>
26
+
</div>
23
27
</header>
24
28
25
29
<div class="body">
+2
-2
src/routes/~/+layout.ts
+2
-2
src/routes/~/+layout.ts
···
12
12
export const load: LayoutLoad = async () => {
13
13
try {
14
14
const did = localStorage.getItem("did") as any;
15
-
const session = await getSession(did, { allowStale: true });
15
+
const session = await getSession(did);
16
16
17
17
const handler = new OAuthUserAgent(session);
18
18
const rpc = new Client({ handler });
···
23
23
24
24
if (isXRPCErrorPayload(data)) throw new Error("couldn't load profile");
25
25
26
-
return { session, did, name: data.displayName };
26
+
return { session, pds: session.info.aud, did, name: data.displayName };
27
27
} catch (e) {
28
28
console.error(e);
29
29
redirect(303, "/");
+4
-4
src/routes/~/+page.svelte
+4
-4
src/routes/~/+page.svelte
···
15
15
const form = e.currentTarget;
16
16
const formdata = new FormData(e.currentTarget);
17
17
18
-
const name = formdata.get("name");
19
-
if (typeof name !== "string") throw new Error("invalid name");
18
+
const rkey = formdata.get("rkey");
19
+
if (typeof rkey !== "string") throw new Error("invalid rkey");
20
20
21
21
const record = {
22
22
assets: [],
···
24
24
};
25
25
26
26
await rpc.post("com.atproto.repo.createRecord", {
27
-
input: { repo: data.did, collection: "com.jakelazaroff.test", rkey: name, record },
27
+
input: { repo: data.did, collection: "com.jakelazaroff.test", rkey, record },
28
28
});
29
29
await invalidate("collection:com.jakelazaroff.test");
30
30
form.reset();
···
32
32
</script>
33
33
34
34
<form onsubmit={createWebsite}>
35
-
<input type="text" name="name" minlength={1} maxlength={512} pattern="[A-Za-z0-9.\-]+" />
35
+
<input type="text" name="rkey" minlength={1} maxlength={512} pattern="[A-Za-z0-9.\-]+" />
36
36
<button>create</button>
37
37
</form>
38
38
+44
-13
src/routes/~/sites/[name]/+page.svelte
+44
-13
src/routes/~/sites/[name]/+page.svelte
···
1
1
<script lang="ts">
2
-
import { invalidate } from "$app/navigation";
2
+
import { goto, invalidate } from "$app/navigation";
3
3
4
4
import type {} from "@atcute/atproto";
5
5
import { isXRPCErrorPayload } from "@atcute/client";
···
10
10
11
11
const rpc = client(data.session);
12
12
13
-
async function deleteWebsite(rkey: string) {
13
+
async function deleteBundle(e: SubmitEvent & { currentTarget: HTMLFormElement }) {
14
+
e.preventDefault();
15
+
const form = e.currentTarget;
16
+
const formdata = new FormData(form);
17
+
18
+
const rkey = formdata.get("rkey");
19
+
if (typeof rkey !== "string") throw new Error("invalid rkey");
20
+
14
21
await rpc.post("com.atproto.repo.deleteRecord", {
15
22
input: { repo: data.did, collection: "com.jakelazaroff.test", rkey },
16
23
});
24
+
goto("/~/");
17
25
}
18
26
19
27
async function deployBundle(e: SubmitEvent & { currentTarget: HTMLFormElement }) {
···
21
29
const form = e.currentTarget;
22
30
const formdata = new FormData(form);
23
31
24
-
const name = formdata.get("name");
25
-
if (typeof name !== "string") throw new Error("invalid name");
32
+
const rkey = formdata.get("rkey");
33
+
if (typeof rkey !== "string") throw new Error("invalid rkey");
34
+
35
+
let description = formdata.get("description");
36
+
if (typeof description !== "string" || !description) description = "Uploaded on website";
26
37
27
38
const files = formdata.getAll("files");
28
39
const assets: { path: string; file: any }[] = [];
···
38
49
});
39
50
}
40
51
41
-
const record = { assets, createdAt: new Date().toISOString() };
52
+
const record = { description, assets, createdAt: new Date().toISOString() };
42
53
await rpc.post("com.atproto.repo.putRecord", {
43
-
input: { repo: data.did, collection: "com.jakelazaroff.test", rkey: name, record },
54
+
input: { repo: data.did, collection: "com.jakelazaroff.test", rkey, record },
44
55
});
45
56
if (isXRPCErrorPayload(data)) throw new Error("couldn't deploy");
57
+
58
+
invalidate(`rkey:${rkey}`);
46
59
}
47
60
</script>
48
61
49
62
<header class="header">
50
63
<h2>{params.name}</h2>
51
-
<button onclick={() => deleteWebsite(params.name)}>delete</button>
52
64
</header>
53
65
54
-
<ul>
55
-
{#each data.record.value.assets as asset}
56
-
<li>{asset.path}</li>
57
-
{/each}
58
-
</ul>
66
+
<details>
67
+
<summary>
68
+
<span>{data.record.value.description || data.record.cid}</span>
69
+
<time>{data.record.value.createdAt}</time>
70
+
</summary>
71
+
<ul>
72
+
{#each data.record.value.assets as asset}
73
+
<li>
74
+
<span>{asset.path}</span>
75
+
<a
76
+
target="_blank"
77
+
href="{data.pds}xrpc/com.atproto.sync.getBlob?did={data.did}&cid={asset.file.ref.$link}"
78
+
>open</a
79
+
>
80
+
</li>
81
+
{/each}
82
+
</ul>
83
+
</details>
59
84
60
85
<form onsubmit={deployBundle}>
61
-
<input type="hidden" name="name" value={params.name} />
86
+
<input type="hidden" name="rkey" value={params.name} />
87
+
<input name="description" />
62
88
<input type="file" name="files" />
63
89
<button>upload</button>
64
90
</form>
91
+
92
+
<form onsubmit={deleteBundle}>
93
+
<input name="rkey" pattern={params.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} />
94
+
<button>delete</button>
95
+
</form>
+1
-1
src/routes/~/sites/[name]/+page.ts
+1
-1
src/routes/~/sites/[name]/+page.ts
+60
src/styles/reset.css
+60
src/styles/reset.css
···
1
+
*,
2
+
*::before,
3
+
*::after {
4
+
box-sizing: border-box;
5
+
}
6
+
7
+
* {
8
+
margin: 0;
9
+
padding: 0;
10
+
}
11
+
12
+
html {
13
+
scrollbar-gutter: stable;
14
+
}
15
+
16
+
body {
17
+
line-height: 1.5;
18
+
}
19
+
20
+
:where(img, picture, video, canvas, svg) {
21
+
display: block;
22
+
max-inline-size: 100%;
23
+
}
24
+
25
+
:where(input, button, textarea, select) {
26
+
font: inherit;
27
+
letter-spacing: inherit;
28
+
word-spacing: inherit;
29
+
color: currentColor;
30
+
}
31
+
32
+
:where(p, h1, h2, h3, h4, h5, h6) {
33
+
overflow-wrap: break-word;
34
+
}
35
+
36
+
:where(ol, ul) {
37
+
list-style: none;
38
+
}
39
+
40
+
:not([class]) {
41
+
&:where(h1, h2, h3, h4, h5, h6) {
42
+
margin-block: 0.75em;
43
+
line-height: 1.25;
44
+
text-wrap: balance;
45
+
letter-spacing: -0.05ch;
46
+
}
47
+
48
+
&:where(p, ol, ul) {
49
+
margin-block: 1em;
50
+
}
51
+
52
+
&:where(ol, ul) {
53
+
padding-inline-start: 1.5em;
54
+
list-style: revert;
55
+
}
56
+
57
+
&:where(li) {
58
+
margin-block: 0.5em;
59
+
}
60
+
}
src/styles/supreme-variable.woff2
src/styles/supreme-variable.woff2
This is a binary file and will not be displayed.