- multi-link bookmark cards now span 4/2 (matching the system widget) instead of 3/1, so they're not cramped and line up into an even 3-up row alongside it - the link list scrolls internally instead of stretching the card, so widgets in the same row stay equal height regardless of link count - BrandIcon gains a small "generic icon" escape hatch (currently just `globe`, via phosphor) for widgets that don't have a real brand mark - added a `coder` brand icon (Coder/code-server) for the Code Server link, since simple-icons has no official VS Code mark to use Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpxhtQY3CExQdMs4j7MmJe
57 lines
2.2 KiB
TypeScript
57 lines
2.2 KiB
TypeScript
import { ArrowUpRight } from "@phosphor-icons/react/ssr";
|
|
import type { Widget } from "@/lib/config/schema";
|
|
import { PN_CARD_CLASS } from "@/components/widgets/WidgetCard";
|
|
import { BrandIcon } from "@/components/widgets/BrandIcon";
|
|
|
|
type BookmarkWidget = Extract<Widget, { type: "bookmark" }>;
|
|
|
|
export function BookmarkWidget({ widget }: { widget: BookmarkWidget }) {
|
|
if (widget.links && widget.links.length > 0) {
|
|
return (
|
|
<div className={PN_CARD_CLASS} style={{ gridColumn: "span 4", gridRow: "span 2" }}>
|
|
<div className="flex items-center gap-2">
|
|
<BrandIcon slug={widget.icon} />
|
|
<span className="min-w-0 flex-1 truncate text-[15px] font-medium text-fg">{widget.name}</span>
|
|
</div>
|
|
{widget.description && <span className="text-xs text-fg-muted">{widget.description}</span>}
|
|
<div className="-mx-1 flex min-h-0 flex-1 flex-col divide-y divide-border overflow-y-auto">
|
|
{widget.links.map((link) => (
|
|
<a
|
|
key={link.href}
|
|
href={link.href}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group flex items-center gap-2 px-1 py-1.5 text-sm text-fg-muted no-underline hover:text-accent"
|
|
>
|
|
<BrandIcon slug={link.icon} size={14} />
|
|
<span className="min-w-0 flex-1 truncate">{link.name}</span>
|
|
<ArrowUpRight
|
|
size={13}
|
|
className="shrink-0 text-accent opacity-0 group-hover:opacity-100"
|
|
aria-hidden
|
|
/>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<a
|
|
href={widget.href}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className={`${PN_CARD_CLASS} text-inherit no-underline`}
|
|
style={{ gridColumn: "span 3" }}
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<BrandIcon slug={widget.icon} />
|
|
<span className="min-w-0 flex-1 truncate text-[15px] font-medium text-fg">{widget.name}</span>
|
|
<ArrowUpRight size={16} className="shrink-0 text-accent" aria-hidden />
|
|
</div>
|
|
{widget.description && <span className="text-xs text-fg-muted">{widget.description}</span>}
|
|
</a>
|
|
);
|
|
}
|