+34
-34
appview/pages/templates/repo/new.html
+34
-34
appview/pages/templates/repo/new.html
···
2
2
3
3
{{ define "content" }}
4
4
<h1>new repo</h1>
5
-
<form>
6
-
<label for="name">repo name</label>
7
-
<input
8
-
type="text"
9
-
id="name"
10
-
name="name"
11
-
class="px-1 border-2 border-blue-100"
12
-
required
13
-
/>
5
+
<form hx-post="/repo/new" class="mt-6 space-y-6" hx-swap="none">
6
+
<div class="space-y-2">
7
+
<label for="name" class="block">repo name</label>
8
+
<input
9
+
type="text"
10
+
id="name"
11
+
name="name"
12
+
required
13
+
class="w-full max-w-md"
14
+
/>
15
+
</div>
14
16
15
-
<fieldset class="border-blue-100 border-2">
16
-
<legend>select a knot:</legend>
17
-
{{ range .Knots }}
18
-
<label>
19
-
<input
20
-
class="px-1 border-2 border-blue-500"
21
-
type="radio"
22
-
name="domain"
23
-
value="{{ . }}"
24
-
/>
25
-
{{ . }} </label
26
-
><br />
27
-
{{ else }}
28
-
<p>no knots available</p>
29
-
{{ end }}
17
+
<fieldset class="space-y-3">
18
+
<legend class="font-medium">select a knot</legend>
19
+
<div class="space-y-2">
20
+
{{ range .Knots }}
21
+
<div>
22
+
<label class="inline-flex items-center">
23
+
<input
24
+
type="radio"
25
+
name="domain"
26
+
value="{{ . }}"
27
+
class="mr-2"
28
+
/>
29
+
<span>{{ . }}</span>
30
+
</label>
31
+
</div>
32
+
{{ else }}
33
+
<p>No knots available</p>
34
+
{{ end }}
35
+
</div>
30
36
</fieldset>
31
37
32
-
<button
33
-
type="submit"
34
-
hx-post="/repo/new"
35
-
hx-swap="none"
36
-
class="my-2 btn"
37
-
>
38
-
create repo
39
-
</button>
38
+
<div class="space-y-2">
39
+
<button type="submit" class="btn">create repo</button>
40
+
<div id="repo" class="error"></div>
41
+
</div>
40
42
</form>
41
-
42
-
<div id="repo" class="error"></div>
43
43
{{ end }}
+54
-28
appview/pages/templates/settings/keys.html
+54
-28
appview/pages/templates/settings/keys.html
···
3
3
{{ define "content" }}
4
4
<h1>settings</h1>
5
5
6
-
<h2>profile</h2>
7
-
<p><strong>handle:</strong> {{ .LoggedInUser.Handle }}</p>
8
-
<p><strong>did:</strong> {{ .LoggedInUser.Did }}</p>
9
-
<p><strong>pds:</strong> {{ .LoggedInUser.Pds }}</p>
6
+
<section class="mb-8">
7
+
<h2 class="text-xl mb-4">profile</h2>
8
+
<dl class="grid grid-cols-[auto_1fr] gap-x-4">
9
+
<dt>handle</dt>
10
+
<dd>{{ .LoggedInUser.Handle }}</dd>
11
+
<dt>did</dt>
12
+
<dd>{{ .LoggedInUser.Did }}</dd>
13
+
<dt>pds</dt>
14
+
<dd>{{ .LoggedInUser.Pds }}</dd>
15
+
</dl>
16
+
</section>
10
17
11
-
<h2>ssh keys</h2>
12
-
<form hx-put="/settings/keys">
13
-
<label for="name">key name:</label>
14
-
<input type="text" id="name" name="name" required />
18
+
<section>
19
+
<h2 class="text-xl mb-4">ssh keys</h2>
20
+
<form
21
+
hx-put="/settings/keys"
22
+
hx-swap="none"
23
+
class="max-w-2xl space-y-4 mb-8"
24
+
>
25
+
<div>
26
+
<label for="name" class="block mb-1">key name</label>
27
+
<input
28
+
type="text"
29
+
id="name"
30
+
name="name"
31
+
required
32
+
class="w-full"
33
+
/>
34
+
</div>
15
35
16
-
<label for="key">pub key:</label>
17
-
<textarea
18
-
id="key"
19
-
name="key"
20
-
placeholder="ssh-rsa AAAAAA..."
21
-
required
22
-
></textarea>
36
+
<div>
37
+
<label for="key" class="block mb-1">pub key</label>
38
+
<textarea
39
+
id="key"
40
+
name="key"
41
+
placeholder="ssh-rsa AAAAAA..."
42
+
required
43
+
class="w-full h-24"
44
+
></textarea>
45
+
</div>
46
+
47
+
<button class="btn" type="submit">add key</button>
23
48
24
-
<button class="btn my-2" type="submit">add key</button>
25
-
</form>
49
+
<div id="settings-keys" class="error"></div>
50
+
</form>
26
51
27
-
<h3>existing keys</h3>
28
-
<ul id="key-list">
29
-
{{ range .PubKeys }}
30
-
<li>
31
-
<strong>{{ .Name }}</strong><br />
32
-
<code>{{ .Key }}</code>
33
-
</li>
34
-
{{ else }}
35
-
<p>no ssh keys added yet</p>
36
-
{{ end }}
37
-
</ul>
52
+
<h3 class="text-lg mb-2">existing keys</h3>
53
+
<ul id="key-list" class="space-y-4">
54
+
{{ range .PubKeys }}
55
+
<li class="p-4 bg-gray-50 rounded">
56
+
<h4 class="font-bold mb-2">{{ .Name }}</h4>
57
+
<code class="block text-sm break-all">{{ .Key }}</code>
58
+
</li>
59
+
{{ else }}
60
+
<p class="text-gray-600">no ssh keys added yet</p>
61
+
{{ end }}
62
+
</ul>
63
+
</section>
38
64
{{ end }}
+5
-2
appview/state/settings.go
+5
-2
appview/state/settings.go
···
30
30
func (s *State) SettingsKeys(w http.ResponseWriter, r *http.Request) {
31
31
switch r.Method {
32
32
case http.MethodGet:
33
-
w.Write([]byte("unimplemented"))
33
+
s.pages.Notice(w, "settings-keys", "Unimplemented.")
34
34
log.Println("unimplemented")
35
35
return
36
36
case http.MethodPut:
···
43
43
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
44
44
if err != nil {
45
45
log.Printf("parsing public key: %s", err)
46
+
s.pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a <strong>public</strong> key.")
46
47
return
47
48
}
48
49
49
50
if err := s.db.AddPublicKey(did, name, key); err != nil {
50
51
log.Printf("adding public key: %s", err)
52
+
s.pages.Notice(w, "settings-keys", "Failed to add public key.")
51
53
return
52
54
}
53
55
···
66
68
// invalid record
67
69
if err != nil {
68
70
log.Printf("failed to create record: %s", err)
71
+
s.pages.Notice(w, "settings-keys-bad", "Failed to create record.")
69
72
return
70
73
}
71
74
72
75
log.Println("created atproto record: ", resp.Uri)
73
-
76
+
s.pages.HxLocation(w, "/settings")
74
77
return
75
78
}
76
79
}
+27
-12
input.css
+27
-12
input.css
···
11
11
h1 {
12
12
@apply text-2xl;
13
13
@apply font-sans;
14
-
@apply text-gray-900;
14
+
@apply text-black;
15
15
@apply py-4;
16
16
}
17
17
18
18
::selection {
19
19
@apply bg-green-400;
20
-
@apply text-gray-900;
20
+
@apply text-black;
21
21
@apply bg-opacity-30;
22
22
}
23
23
a {
24
-
@apply underline text-blue-600 hover:text-blue-800 visited:text-purple-600;
24
+
@apply underline text-black hover:text-gray-800 visited:text-gray-600;
25
+
}
26
+
27
+
@layer base {
28
+
label {
29
+
@apply block text-sm text-black;
30
+
}
31
+
input {
32
+
@apply bg-white border border-black rounded-sm focus:ring-black p-2;
33
+
}
34
+
textarea {
35
+
@apply bg-white border border-black rounded-sm focus:ring-black p-2;
36
+
}
25
37
}
26
38
27
39
@layer components {
28
40
.btn {
29
41
@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center
30
42
justify-center bg-transparent px-2 pb-[0.2rem] text-base
31
-
text-gray-900 before:absolute before:inset-0 before:-z-10
32
-
before:block before:rounded-sm before:border before:border-blue-200
33
-
before:bg-white before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#e5edff]
34
-
before:content-[''] hover:before:border-blue-300
35
-
hover:before:bg-blue-50
36
-
hover:before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#e5edff]
43
+
text-black before:absolute before:inset-0 before:-z-10
44
+
before:block before:rounded-sm before:border before:border-black
45
+
before:bg-white before:shadow-[0_2px_2px_0_rgba(0,0,0,0.1),inset_0_-2px_0_0_#ffffff]
46
+
before:content-[''] hover:before:border-gray-800
47
+
hover:before:bg-gray-50
48
+
hover:before:shadow-[0_2px_2px_0_rgba(0,0,0,0.1),inset_0_-2px_0_0_#ffffff]
37
49
focus:outline-none focus-visible:before:outline
38
-
focus-visible:before:outline-4 focus-visible:before:outline-blue-500
39
-
active:before:shadow-[inset_0_2px_2px_0_rgba(20,20,96,0.1)];
50
+
focus-visible:before:outline-4 focus-visible:before:outline-black
51
+
active:before:shadow-[inset_0_2px_2px_0_rgba(0,0,0,0.1)];
40
52
}
41
53
}
42
54
@layer utilities {
43
55
.error {
44
-
@apply py-1 border-red-400 text-red-600;
56
+
@apply py-1 border-black text-black;
57
+
}
58
+
.success {
59
+
@apply py-1 border-black text-black;
45
60
}
46
61
}
47
62
}