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
Binary file added docs/docent-rust-demo.mp4
Binary file not shown.
58 changes: 35 additions & 23 deletions src/components/DocentInstall/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,38 @@ type Shell = 'unix' | 'windows';
// smartloop.ai/install* redirect (Cloudflare) to the scripts in the docent
// repo, so the one-liners always hand out the current script. The deploy also
// copies them into the site as a fallback (.github/workflows/deploy-pages.yml).
const installCommands: Record<Shell, string> = {
export const installCommands: Record<Shell, string> = {
unix: 'curl -fsSL https://smartloop.ai/install | sh',
windows: 'irm https://smartloop.ai/install.ps1 | iex',
};

// `location` tags the copy event in analytics (homepage vs /docent).
export default function DocentInstall({ location = 'home' }: { location?: string }): ReactNode {
const [shell, setShell] = useState<Shell>('unix');
const [copied, setCopied] = useState(false);
function CopyIcon({ done }: { done: boolean }): ReactNode {
const props = { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const, 'aria-hidden': true };
return done
? <svg {...props}><polyline points="20 6 9 17 4 12" /></svg>
: <svg {...props}><rect x="9" y="9" width="12" height="12" rx="2" /><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" /></svg>;
}

// Windows visitors land on the PowerShell line; everyone else gets curl.
// The visitor's shell: Windows gets the PowerShell line, everyone else curl.
// Client Hints first (Chromium), then the legacy fields; macOS and Linux
// share the curl line, so only Windows needs spotting. Renders as unix on the
// server and switches after hydration.
export function useInstallShell(): Shell {
const [shell, setShell] = useState<Shell>('unix');
useEffect(() => {
if (/win/i.test(navigator.platform) || /windows/i.test(navigator.userAgent)) setShell('windows');
const nav = navigator as Navigator & { userAgentData?: { platform?: string } };
const platform = nav.userAgentData?.platform || nav.platform || '';
if (/^win/i.test(platform) || /windows/i.test(nav.userAgent)) setShell('windows');
}, []);
return shell;
}

// The install line for the visitor's platform, with a copy button. No
// platform switch: the shell is detected. `location` tags the copy event in
// analytics (homepage vs /docent); `variant="hero"` centres it.
export default function DocentInstall({ location = 'home', variant = 'card' }: { location?: string; variant?: 'card' | 'hero' }): ReactNode {
const shell = useInstallShell();
const [copied, setCopied] = useState(false);

const copy = async () => {
try {
Expand All @@ -33,24 +51,18 @@ export default function DocentInstall({ location = 'home' }: { location?: string
};

return (
<div className={styles.install}>
<div className={styles.installTabs} role="tablist" aria-label="Platform">
{([['unix', 'macOS · Linux'], ['windows', 'Windows']] as const).map(([key, label]) => (
<button
key={key}
type="button"
role="tab"
aria-selected={shell === key}
className={`${styles.installTab} ${shell === key ? styles.installTabActive : ''}`}
onClick={() => setShell(key)}
>
{label}
</button>
))}
</div>
<div className={`${styles.install} ${variant === 'hero' ? styles.hero : ''}`}>
<div className={styles.installRow}>
<code><span>{shell === 'windows' ? '>' : '$'}</span>{installCommands[shell]}</code>
<button type="button" className={styles.copy} onClick={copy}>{copied ? 'copied' : 'copy'}</button>
<button
type="button"
className={styles.copy}
onClick={copy}
aria-label={copied ? 'Copied' : 'Copy the install command'}
title={copied ? 'Copied' : 'Copy'}
>
<CopyIcon done={copied} />
</button>
</div>
</div>
);
Expand Down
107 changes: 46 additions & 61 deletions src/components/DocentInstall/styles.module.css
Original file line number Diff line number Diff line change
@@ -1,83 +1,59 @@
/* The Docent install one-liner, with a tab per shell and a copy button.
Drawn as one of the site's soft cards: rounded, lightly lifted, with the
shell choice as a segmented pill rather than a ruled tab strip. */
/* The Docent install one-liner for the visitor's platform: one command bar
with an icon-only copy button, so the command is what reads first.
Left-aligned by default (it sits in a text column on the homepage);
the hero variant centres it. */
.install {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 12px;
width: 100%;
max-width: 520px;
max-width: 560px;
min-width: 0;
box-sizing: border-box;
border: 1px solid var(--sl-line);
border-radius: 14px;
background: var(--sl-panel);
box-shadow: 0 1px 2px rgba(10, 10, 20, 0.04), 0 12px 40px -12px rgba(10, 10, 20, 0.12);
overflow: hidden;
}

[data-theme='dark'] .install {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 40px -12px rgba(0, 0, 0, 0.6);
}

.installTabs {
display: flex;
align-self: flex-start;
gap: 2px;
margin: 10px 10px 0;
padding: 3px;
border-radius: 10px;
background: var(--sl-ground);
border: 1px solid var(--sl-line);
}

.installTab {
background: none;
border: none;
border-radius: 7px;
padding: 5px 12px;
font-family: var(--sl-display);
font-size: 0.8rem;
font-weight: 500;
color: var(--sl-dim);
cursor: pointer;
transition: background 0.18s, color 0.18s, box-shadow 0.18s;
}

.installTab:hover {
color: var(--sl-white);
}

.installTabActive {
color: var(--sl-white);
background: var(--sl-panel);
box-shadow: 0 1px 2px rgba(10, 10, 20, 0.08);
.hero {
max-width: 600px;
align-items: center;
}

.installRow {
display: flex;
align-items: center;
gap: 10px;
margin: 10px;
padding: 8px 8px 8px 14px;
align-self: stretch;
gap: 14px;
min-width: 0;
border-radius: 10px;
background: var(--sl-ground);
padding: 10px 10px 10px 20px;
border-radius: 14px;
border: 1px solid var(--sl-line);
background: var(--sl-panel);
box-shadow: 0 1px 2px rgba(10, 10, 20, 0.04), 0 12px 40px -12px rgba(10, 10, 20, 0.12);
}

[data-theme='dark'] .installRow {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 40px -12px rgba(0, 0, 0, 0.6);
}

.installRow code {
flex: 1;
min-width: 0;
overflow-x: auto;
white-space: nowrap;
scrollbar-width: none;
background: none;
border: none;
padding: 0;
text-align: left;
font-family: var(--sl-mono);
font-size: 0.8rem;
font-size: 0.88rem;
color: var(--sl-white);
}

.hero .installRow code {
font-size: 0.92rem;
}

.installRow code span {
color: var(--sl-accent-text);
margin-right: 10px;
Expand All @@ -86,19 +62,28 @@

.copy {
flex: none;
background: var(--sl-accent);
color: var(--sl-on-accent);
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
padding: 0;
border-radius: 8px;
padding: 7px 14px;
font-family: var(--sl-display);
font-size: 0.8rem;
font-weight: 500;
border: 1px solid var(--sl-line);
background: var(--sl-ground);
color: var(--sl-dim);
cursor: pointer;
box-shadow: 0 6px 18px -8px rgba(236, 72, 153, 0.7);
transition: background 0.18s;
transition: border-color 0.18s, color 0.18s;
}

.copy:hover {
background: var(--sl-accent-soft);
border-color: var(--sl-accent);
color: var(--sl-accent-text);
}

@media (max-width: 640px) {
.installRow code,
.hero .installRow code {
font-size: 0.78rem;
}
}
1 change: 1 addition & 0 deletions src/components/Footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default function Footer() {
<span>Made in San Francisco, CA</span>
<div className="sl-footer__links">
<Link to="/docs/getting-started">Docs</Link>
<Link to="/contact/?for=partner">Partners</Link>
<Link to="/terms">Terms</Link>
<Link to="/privacy">Privacy</Link>
<Link
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ export default function Header() {
<nav className="sl-menu__body">
<ul className="sl-menu__list">
<li className="sl-menu__item"><Link to="/pricing" className="sl-menu__link" onClick={closeMenu}>Get started</Link></li>
<li className="sl-menu__item"><Link to="/docent" className="sl-menu__link" onClick={closeMenu}>Docent</Link></li>
<li className="sl-menu__item"><Link to="/docs/intro" className="sl-menu__link" onClick={closeMenu}>Docs</Link></li>
<li className="sl-menu__item"><Link to="/contact/?for=partner" className="sl-menu__link" onClick={closeMenu}>Partners</Link></li>
</ul>
</nav>
</div>
Expand Down
Loading
Loading