learn and share notes on atproto (wip) 馃
malfestio.stormlightlabs.org/
readability
solid
axum
atproto
srs
1import type { Note } from "$lib/model";
2import type { WikiLink } from "$lib/wikilink";
3import { A } from "@solidjs/router";
4import type { Component } from "solid-js";
5import { For, Show } from "solid-js";
6
7type WikilinksPanelProps = { links: WikiLink[]; notes: Note[]; resolveNote: (title: string) => Note | null };
8
9/**
10 * Panel showing outgoing wikilinks from the current note
11 *
12 * Displays links with status (resolved/unresolved)
13 */
14export const WikilinksPanel: Component<WikilinksPanelProps> = (props) => {
15 const uniqueTitles = () => {
16 const seen = new Set<string>();
17 return props.links.filter((link) => {
18 const normalized = link.title.toLowerCase();
19 if (seen.has(normalized)) return false;
20 seen.add(normalized);
21 return true;
22 });
23 };
24
25 return (
26 <div class="space-y-2">
27 <h3 class="text-sm font-semibold text-slate-400 uppercase tracking-wide">Wikilinks</h3>
28 <Show when={props.links.length > 0} fallback={<p class="text-sm text-slate-500 italic">No outgoing links</p>}>
29 <ul class="space-y-1">
30 <For each={uniqueTitles()}>
31 {(link) => {
32 const resolved = () => props.resolveNote(link.title);
33 return (
34 <li class="text-sm">
35 <Show
36 when={resolved()}
37 fallback={
38 <span class="text-slate-500 flex items-center gap-1">
39 <span class="i-ri-link-unlink text-amber-500" />
40 <span class="line-through">{link.title}</span>
41 </span>
42 }>
43 {(note) => (
44 <A
45 href={`/notes/${note().id}`}
46 class="text-blue-400 hover:text-blue-300 flex items-center gap-1 transition-colors">
47 <span class="i-ri-link" />
48 {link.alias || link.title}
49 </A>
50 )}
51 </Show>
52 </li>
53 );
54 }}
55 </For>
56 </ul>
57 </Show>
58 </div>
59 );
60};
61
62export default WikilinksPanel;