Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,28 @@ export default defineConfig({
customCss: ['./src/styles/global.css'],
expressiveCode: {
themes: ['starlight-light', 'starlight-dark'],
styleOverrides: {
borderRadius: '0.75rem',
codeFontFamily: 'var(--font-mono)',
uiFontFamily: 'var(--font-sans)',
frames: {
// Literal hex, not `var(--color-brand)`: Expressive Code parses these
// as colors (e.g. for opacity/contrast math), which breaks on CSS
// custom properties. #8b3dff is the brand purple in both themes.
editorActiveTabIndicatorTopColor: '#8b3dff',
editorActiveTabIndicatorBottomColor: '#8b3dff',
terminalTitlebarDotsForeground: '#8b3dff',
terminalTitlebarDotsOpacity: '0.6',
},
},
},
components: {
Header: './src/components/starlight/Header.astro',
PageFrame: './src/components/starlight/PageFrame.astro',
EditLink: './src/components/starlight/EditLink.astro',
PageTitle: './src/components/starlight/PageTitle.astro',
Search: './src/components/starlight/Search.astro',
Sidebar: './src/components/starlight/Sidebar.astro',
},
plugins: [
starlightSidebarTopics(sidebarTopics),
Expand Down
36 changes: 26 additions & 10 deletions src/src/components/SiteFooter.astro
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
---
import SiteBrand from '~/components/SiteBrand.astro';
import { NAV_ITEMS } from '~/components/SiteNav.astro';
import { loadProjects } from '~/lib/manifest/load';
import { SITE } from '~/lib/site';
import { withBase } from '~/lib/urls';

// Shuffled at build time so the footer doesn't always spotlight the same projects.
const footerProjects = loadProjects()
.filter((project) => project.status !== 'archived')
.toSorted(() => Math.random() - 0.5)
.slice(0, 3);
---

<footer class="border-t border-border bg-surface">
<div class="pv-container py-10">
<div class="grid gap-8 md:grid-cols-[2fr_1fr_1fr_1fr]">
<div class="grid gap-8 md:grid-cols-[2fr_1fr_1fr_1fr_1fr]">
<div class="max-w-sm space-y-3">
<SiteBrand />
<p class="text-sm text-muted">
Expand Down Expand Up @@ -35,15 +42,15 @@ import { withBase } from '~/lib/urls';
<nav aria-label="Projects">
<h2 class="text-xs font-semibold uppercase tracking-wider text-muted">Projects</h2>
<ul class="mt-3 list-none space-y-2 text-sm">
<li>
<a href="https://github.com/purview-dev/telemetry-sourcegenerator" class="pv-link"
>Telemetry SourceGenerator</a>
</li>
<li>
<a href="https://github.com/purview-dev/event-sourcing" class="pv-link"
>Event Sourcing</a>
</li>
<li><a href="https://github.com/purview-dev/zodsharp" class="pv-link">Purview.ZodSharp</a></li>
{
footerProjects.map((project) => (
<li>
<a href={withBase(`/projects/${project.id}/`)} class="pv-link">
{project.name}
</a>
</li>
))
}
<li><a href={withBase('/projects/')} class="pv-link">All projects</a></li>
</ul>
</nav>
Expand All @@ -57,6 +64,15 @@ import { withBase } from '~/lib/urls';
<li><a href={withBase('/about/')} class="pv-link">About</a></li>
</ul>
</nav>

<nav aria-label="For LLMs">
<h2 class="text-xs font-semibold uppercase tracking-wider text-muted">For LLMs</h2>
<ul class="mt-3 list-none space-y-2 text-sm">
<li><a href={withBase('/llms.txt')} class="pv-link">llms.txt</a></li>
<li><a href={withBase('/llms-small.txt')} class="pv-link">llms-small.txt</a></li>
<li><a href={withBase('/llms-full.txt')} class="pv-link">llms-full.txt</a></li>
</ul>
</nav>
</div>

<div
Expand Down
56 changes: 38 additions & 18 deletions src/src/components/islands/InstallPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ import type { InstallOption, InstallTab } from '~/lib/install';

import { useState } from 'preact/hooks';

function CodeBlock({ code }: { code: string }) {
/** macOS-style traffic light dots that frame the code window, matching Expressive Code. */
function FrameDots() {
return (
<span class="flex shrink-0 gap-1.5" aria-hidden="true">
<span class="h-2.5 w-2.5 rounded-full bg-[#ff5f56]" />
<span class="h-2.5 w-2.5 rounded-full bg-[#ffbd2e]" />
<span class="h-2.5 w-2.5 rounded-full bg-[#27c93f]" />
</span>
);
}

function CodeBlock({ code, title }: { code: string; title?: string }) {
const [copied, setCopied] = useState(false);

const copy = async () => {
Expand All @@ -18,17 +29,28 @@ function CodeBlock({ code }: { code: string }) {
};

return (
<div class="border-border bg-code-bg flex items-start justify-between gap-3 rounded-lg border px-4 py-3">
<pre class="min-w-0 flex-1 overflow-x-auto text-sm leading-relaxed">
<div class="pv-code-frame">
<div class="pv-code-frame-header">
<FrameDots />
<span class="min-w-0 flex-1 truncate text-center text-xs font-medium text-white/50">
{title}
</span>
<button
type="button"
onClick={copy}
class={[
'shrink-0 rounded-md border px-2.5 py-1 text-xs font-medium transition-colors',
copied
? 'border-[#27c93f]/40 bg-[#27c93f]/15 text-[#7ee89a]'
: 'border-white/10 bg-white/5 text-white/70 hover:border-white/25 hover:bg-white/10 hover:text-white',
].join(' ')}
>
{copied ? 'Copied ✓' : 'Copy'}
</button>
</div>
<pre class="pv-code-frame-body">
<code>{code}</code>
</pre>
<button
type="button"
onClick={copy}
class="border-border bg-surface hover:border-brand/40 shrink-0 rounded-md border px-3 py-1.5 text-xs font-medium transition-colors"
>
{copied ? 'Copied ✓' : 'Copy'}
</button>
</div>
);
}
Expand All @@ -41,7 +63,7 @@ function CopyBlock({ label, detail, code }: InstallOption) {
{detail && <p class="text-muted text-xs">{detail}</p>}
</div>
<div class="mt-2">
<CodeBlock code={code} />
<CodeBlock code={code} title={label} />
</div>
</div>
);
Expand Down Expand Up @@ -79,7 +101,7 @@ export default function InstallPanel({ tabs }: Props) {
aria-label="Install options"
onKeyDown={onKeyDown}
tabIndex={-1}
class="border-border flex flex-wrap gap-x-1 border-b"
class="flex flex-wrap gap-1 px-1"
>
{tabs.map((tabOption, index) => (
<button
Expand All @@ -92,10 +114,8 @@ export default function InstallPanel({ tabs }: Props) {
tabIndex={active === index ? 0 : -1}
onClick={() => setActive(index)}
class={[
'-mb-px border-b-2 px-3 py-2 text-sm font-medium transition-colors',
active === index
? 'border-brand text-brand-emphasis'
: 'border-transparent text-muted hover:border-border hover:text-foreground',
'pv-install-tab',
active === index ? 'pv-install-tab-active' : 'pv-install-tab-inactive',
].join(' ')}
>
{tabOption.label}
Expand All @@ -106,7 +126,7 @@ export default function InstallPanel({ tabs }: Props) {
role="tabpanel"
id={`install-panel-${active}`}
aria-labelledby={`install-tab-${active}`}
class="mt-4 space-y-4"
class="pv-install-panel-body space-y-4"
>
{tab.snippets.length > 1 ? (
tab.snippets.map((snippet) => (
Expand All @@ -115,7 +135,7 @@ export default function InstallPanel({ tabs }: Props) {
) : (
<div>
{tab.detail && <p class="text-muted mb-2 text-xs">{tab.detail}</p>}
<CodeBlock code={tab.snippets[0]?.code ?? ''} />
<CodeBlock code={tab.snippets[0]?.code ?? ''} title={tab.label} />
</div>
)}
</div>
Expand Down
Loading
Loading