Skip to content

Commit 85621e3

Browse files
committed
Design instance details and actor list
Codex redesigned the instance detail route, added a create-actor link, and introduced a dummy actor list. The user directed the design to remove the landing-page-style hero and reduce actor entries to handles only. The result was verified with mise run check, mise run build, and a local browser preview. Assisted-by: Codex:gpt-5.6-sol
1 parent c150cb2 commit 85621e3

2 files changed

Lines changed: 271 additions & 72 deletions

File tree

‎packages/web/src/routes/instance/[slug].tsx‎

Lines changed: 73 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -14,87 +14,88 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

17-
import { Link } from "@kobalte/core/link";
1817
import { Title } from "@solidjs/meta";
19-
import { Show } from "solid-js";
18+
import { A, useParams } from "@solidjs/router";
19+
import { For } from "solid-js";
2020

21-
// oxlint-disable-next-line capitalized-comments
22-
// import type { InstanceDetailQuery } from "./__generated__/InstanceDetailQuery.graphql";
21+
import styles from "~/styles/instance.module.css";
2322

24-
// const instanceDetailQuery = graphql`
25-
// query InstanceDetailQuery($instanceSlug: String!) {
26-
// instance: node(slug: $instanceSlug) {
27-
// ... on Instance {
28-
// host
29-
// nodeInfoUrl
30-
// }
31-
// }
32-
// }
33-
// `;
23+
const actors = ["@sherry", "@newsroom", "@garden"] as const;
3424

35-
// const loadInstanceQuery = query(
36-
// (instanceSlug: string) =>
37-
// loadQuery<InstanceDetailQuery>(
38-
// useRelayEnvironment()(),
39-
// instanceDetailQuery,
40-
// {
41-
// instanceSlug,
42-
// },
43-
// ),
44-
// "InstanceDetailQuery",
45-
// );
25+
export default function InstanceDetailPage() {
26+
const params = useParams();
27+
const host = () => params.slug ?? "social.example";
4628

47-
// export const route = {
48-
// preload({ params }) {
49-
// if (params.slug === undefined) {
50-
// throw new Error("Missing slug route parameter.");
51-
// }
52-
// return loadInstanceQuery(params.slug);
53-
// },
54-
// } satisfies RouteDefinition;
29+
return (
30+
<main class={styles.page}>
31+
<Title>{host()} — DrFed</Title>
5532

56-
// type RouteData = ReturnType<typeof loadInstanceQuery>;
33+
<A class={styles.backLink} href="/workspace">
34+
<span aria-hidden="true">←</span> All instances
35+
</A>
5736

58-
export default function InstanceDetailPage() {
59-
// oxlint-disable-next-line capitalized-comments
60-
// const data = createPreloadedQuery<InstanceDetailQuery>(
61-
// instanceDetailQuery,
62-
// () => props.data,
63-
// );
64-
//
37+
<header class={styles.header}>
38+
<h1>{host()}</h1>
39+
<A
40+
class={styles.createButton}
41+
href="/workspace/create/instance-demo/actors"
42+
>
43+
<span aria-hidden="true">+</span>
44+
Create actor
45+
</A>
46+
</header>
6547

66-
// oxlint-disable-next-line unicorn/consistent-function-scoping
67-
const data = () => ({
68-
instance: {
69-
host: "hi",
70-
nodeInfoUrl: "bye",
71-
},
72-
});
48+
<section class={styles.details} aria-labelledby="connection-title">
49+
<div>
50+
<p class={styles.sectionLabel}>Connection record</p>
51+
<h2 id="connection-title">Federation endpoints</h2>
52+
</div>
53+
<dl class={styles.endpointList}>
54+
<div>
55+
<dt>NodeInfo</dt>
56+
<dd>
57+
<a href={`https://${host()}/nodeinfo/2.1`}>/nodeinfo/2.1</a>
58+
</dd>
59+
</div>
60+
<div>
61+
<dt>WebFinger</dt>
62+
<dd>
63+
<a href={`https://${host()}/.well-known/webfinger`}>
64+
/.well-known/webfinger
65+
</a>
66+
</dd>
67+
</div>
68+
<div>
69+
<dt>Shared inbox</dt>
70+
<dd>
71+
<a href={`https://${host()}/inbox`}>/inbox</a>
72+
</dd>
73+
</div>
74+
</dl>
75+
</section>
7376

74-
return (
75-
// oxlint-disable-next-line typescript/no-unnecessary-condition
76-
<Show when={data()?.instance} fallback={<p>Instance not found.</p>}>
77-
{(instance) => (
78-
<main>
79-
<Title>{instance().host} — DrFed</Title>
80-
<h1>{instance().host}</h1>
81-
<Show when={instance()}>
82-
{/*oxlint-disable-next-line eslint/no-shadow */}
83-
{(instance) => (
84-
<Show when={instance().nodeInfoUrl}>
85-
{(nodeInfoUrl) => (
86-
<p>
87-
NodeInfo: <Link href={nodeInfoUrl()}>{nodeInfoUrl()}</Link>
88-
WebFingerURL:{" "}
89-
<Link href="">{`${instance().host}/.well-known/webfinger`}</Link>
90-
SharedInbox: <Link>TBD</Link>
91-
</p>
92-
)}
93-
</Show>
77+
<section class={styles.actors} aria-labelledby="actors-title">
78+
<header class={styles.sectionHeader}>
79+
<div>
80+
<p class={styles.sectionLabel}>Local identities</p>
81+
<h2 id="actors-title">Actors</h2>
82+
</div>
83+
<p>{actors.length} registered</p>
84+
</header>
85+
86+
<div class={styles.actorList}>
87+
<For each={actors}>
88+
{(handle) => (
89+
<article class={styles.actorCard}>
90+
<span class={styles.actorMarker} aria-hidden="true" />
91+
<p>
92+
{handle}@{host()}
93+
</p>
94+
</article>
9495
)}
95-
</Show>
96-
</main>
97-
)}
98-
</Show>
96+
</For>
97+
</div>
98+
</section>
99+
</main>
99100
);
100101
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
DrFed: A web-based platform for developing and debugging ActivityPub apps
3+
Copyright (C) 2026 DrFed team
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU Affero General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU Affero General Public License for more details.
14+
15+
You should have received a copy of the GNU Affero General Public License
16+
along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
.page {
20+
display: grid;
21+
gap: 1.5rem;
22+
padding-bottom: 3rem;
23+
}
24+
25+
.backLink {
26+
align-items: center;
27+
color: var(--ink-soft);
28+
display: inline-flex;
29+
font-size: 0.9rem;
30+
font-weight: 700;
31+
gap: 0.45rem;
32+
justify-self: start;
33+
text-decoration: none;
34+
}
35+
36+
.backLink:hover {
37+
color: var(--accent-strong);
38+
}
39+
40+
.header {
41+
align-items: center;
42+
border-bottom: 1px solid var(--line);
43+
display: flex;
44+
gap: 1rem;
45+
justify-content: space-between;
46+
padding: 0.25rem 0 1.25rem;
47+
}
48+
49+
.header h1 {
50+
font-family: var(--font-mono);
51+
font-size: clamp(1.5rem, 4vw, 2rem);
52+
line-height: 1.2;
53+
margin: 0;
54+
overflow-wrap: anywhere;
55+
}
56+
57+
.createButton {
58+
align-items: center;
59+
background: var(--accent);
60+
border-radius: var(--radius-sm);
61+
color: var(--on-accent);
62+
display: inline-flex;
63+
flex: 0 0 auto;
64+
font-weight: 800;
65+
gap: 0.5rem;
66+
min-height: 2.75rem;
67+
padding: 0.65rem 0.95rem;
68+
text-decoration: none;
69+
}
70+
71+
.createButton:hover {
72+
background: var(--accent-strong);
73+
}
74+
75+
.details,
76+
.actors {
77+
background: var(--card);
78+
border: 1px solid var(--line);
79+
border-radius: var(--radius);
80+
box-shadow: var(--shadow-sm);
81+
padding: clamp(1.25rem, 3vw, 1.75rem);
82+
}
83+
84+
.sectionLabel {
85+
color: var(--accent-strong);
86+
font-family: var(--font-mono);
87+
font-size: var(--text-label);
88+
font-weight: 600;
89+
letter-spacing: var(--tracking-label);
90+
margin: 0 0 0.35rem;
91+
text-transform: uppercase;
92+
}
93+
94+
.details h2,
95+
.sectionHeader h2 {
96+
font-size: 1.3rem;
97+
margin: 0;
98+
}
99+
100+
.endpointList {
101+
margin: 1.25rem 0 0;
102+
}
103+
104+
.endpointList div {
105+
align-items: baseline;
106+
border-top: 1px solid var(--line);
107+
display: grid;
108+
gap: 1rem;
109+
grid-template-columns: 8rem minmax(0, 1fr);
110+
padding: 0.9rem 0;
111+
}
112+
113+
.endpointList div:last-child {
114+
padding-bottom: 0;
115+
}
116+
117+
.endpointList dt {
118+
color: var(--ink-soft);
119+
font-size: 0.9rem;
120+
font-weight: 700;
121+
}
122+
123+
.endpointList dd {
124+
margin: 0;
125+
min-width: 0;
126+
}
127+
128+
.endpointList a {
129+
color: var(--ink);
130+
font-family: var(--font-mono);
131+
font-size: 0.85rem;
132+
overflow-wrap: anywhere;
133+
text-decoration-color: var(--accent-soft);
134+
text-underline-offset: 0.2rem;
135+
}
136+
137+
.sectionHeader {
138+
align-items: end;
139+
display: flex;
140+
justify-content: space-between;
141+
margin-bottom: 1.25rem;
142+
}
143+
144+
.sectionHeader > p {
145+
color: var(--ink-soft);
146+
font-family: var(--font-mono);
147+
font-size: 0.8rem;
148+
margin: 0 0 0.15rem;
149+
}
150+
151+
.actorList {
152+
border-top: 1px solid var(--line);
153+
}
154+
155+
.actorCard {
156+
align-items: center;
157+
display: flex;
158+
gap: 0.75rem;
159+
padding: 1rem 0;
160+
}
161+
162+
.actorCard + .actorCard {
163+
border-top: 1px solid var(--line);
164+
}
165+
166+
.actorMarker {
167+
background: var(--accent);
168+
border: 0.25rem solid var(--accent-tint);
169+
border-radius: 50%;
170+
height: 0.85rem;
171+
width: 0.85rem;
172+
}
173+
174+
.actorCard p {
175+
font-family: var(--font-mono);
176+
font-size: 0.9rem;
177+
margin: 0;
178+
overflow-wrap: anywhere;
179+
}
180+
181+
.backLink:focus-visible,
182+
.createButton:focus-visible,
183+
.endpointList a:focus-visible {
184+
outline: 3px solid var(--accent-soft);
185+
outline-offset: 0.2rem;
186+
}
187+
188+
@media (max-width: 520px) {
189+
.header {
190+
align-items: start;
191+
flex-direction: column;
192+
}
193+
194+
.endpointList div {
195+
gap: 0.25rem;
196+
grid-template-columns: 1fr;
197+
}
198+
}

0 commit comments

Comments
 (0)