diff --git a/src/components/InstallPicker.astro b/src/components/InstallPicker.astro
index 6e5304d..36935e8 100644
--- a/src/components/InstallPicker.astro
+++ b/src/components/InstallPicker.astro
@@ -14,6 +14,8 @@ const releasesUrl =
: 'https://github.com/git-fire/git-rain/releases';
const labelId = `platform-label-${product}`;
+const platformGroupId = `platform-group-${product}`;
+const methodLabelId = `install-method-label-${product}`;
---
{!embedded &&
{title}
}
-
-
+
{embedded ? 'Install' : 'Platform'}
+
+
+
+
+
+
+
+
diff --git a/src/scripts/install-pickers.ts b/src/scripts/install-pickers.ts
index c09d0c3..e9699de 100644
--- a/src/scripts/install-pickers.ts
+++ b/src/scripts/install-pickers.ts
@@ -1,7 +1,29 @@
export type ProductId = 'git-fire' | 'git-rain';
type OsFamily = 'macos' | 'windows' | 'linux' | 'go';
-const STORAGE_KEY = 'git-fire-site-install-os';
+const STORAGE_KEY_OS = 'git-fire-site-install-os';
+const STORAGE_KEY_LINUX_METHOD = 'git-fire-site-install-linux-method';
+const STORAGE_KEY_WIN_METHOD = 'git-fire-site-install-windows-method';
+
+const PLATFORM_ORDER: OsFamily[] = ['macos', 'windows', 'linux', 'go'];
+
+const LINUX_METHOD_LABELS: Record = {
+ deb: '.deb (APT)',
+ rpm: '.rpm (DNF/YUM)',
+ script: 'curl install script',
+ brew: 'Homebrew (Linux)',
+ manual: 'Binary archive (manual)',
+};
+
+const WIN_METHOD_LABELS: Record = {
+ winget: 'winget',
+ manual: 'Binary (manual)',
+};
+
+function coerceOs(value: string | undefined): OsFamily | null {
+ if (value === 'macos' || value === 'windows' || value === 'linux' || value === 'go') return value;
+ return null;
+}
function detectOs(): OsFamily {
if (typeof navigator === 'undefined') return 'go';
@@ -19,9 +41,30 @@ function detectOs(): OsFamily {
return 'go';
}
+/** Best-effort from browser user agent only — many Linux browsers omit distro. */
+function detectLinuxPackageFamily(): 'deb' | 'rpm' | 'unknown' {
+ if (typeof navigator === 'undefined') return 'unknown';
+ const ua = navigator.userAgent.toLowerCase();
+ if (
+ /\b(fedora|rhel|red hat|centos|cent stream|rocky|alma|scientific|oracle linux|opensuse|tumbleweed|suse linux|gecko\s*linux|amazon linux|amzn|mageia|pclinuxos|miracle\s*linux|nobara|ultramarine|openmandriva)\b/.test(
+ ua,
+ )
+ ) {
+ return 'rpm';
+ }
+ if (
+ /\b(ubuntu|debian|linux mint|pop[_!]os|elementary|kali|raspberry|raspbian|neon|zorin|parrot|deepin|mx linux|bodhi|devuan|knoppix|bunsenlabs|linux lite|kubuntu|xubuntu|lubuntu|edubuntu|ubuntu studio|linuxfx)\b/.test(
+ ua,
+ )
+ ) {
+ return 'deb';
+ }
+ return 'unknown';
+}
+
function storedOs(): OsFamily | null {
try {
- const v = sessionStorage.getItem(STORAGE_KEY);
+ const v = sessionStorage.getItem(STORAGE_KEY_OS);
if (v === 'macos' || v === 'windows' || v === 'linux' || v === 'go') return v;
} catch {
/* private mode */
@@ -31,107 +74,499 @@ function storedOs(): OsFamily | null {
function persistOs(value: OsFamily) {
try {
- sessionStorage.setItem(STORAGE_KEY, value);
+ sessionStorage.setItem(STORAGE_KEY_OS, value);
+ } catch {
+ /* ignore */
+ }
+}
+
+function storedLinuxMethod(): string | null {
+ try {
+ return sessionStorage.getItem(STORAGE_KEY_LINUX_METHOD);
+ } catch {
+ return null;
+ }
+}
+
+function persistLinuxMethod(id: string) {
+ try {
+ sessionStorage.setItem(STORAGE_KEY_LINUX_METHOD, id);
} catch {
/* ignore */
}
}
-function commandBlock(product: ProductId, os: OsFamily): { command: string; note: string } {
+function storedWindowsMethod(): string | null {
+ try {
+ return sessionStorage.getItem(STORAGE_KEY_WIN_METHOD);
+ } catch {
+ return null;
+ }
+}
+
+function persistWindowsMethod(id: string) {
+ try {
+ sessionStorage.setItem(STORAGE_KEY_WIN_METHOD, id);
+ } catch {
+ /* ignore */
+ }
+}
+
+function orderedLinuxMethodIds(product: ProductId): string[] {
+ const fam = detectLinuxPackageFamily();
if (product === 'git-fire') {
- switch (os) {
- case 'macos':
- return {
- command: 'brew tap git-fire/tap\nbrew install git-fire',
- note: 'Requires Homebrew. Same commands work on Linuxbrew.',
- };
- case 'windows':
- return {
- command: 'winget install git-fire',
- note: 'If the short ID is not available yet: winget install git-fire.git-fire',
- };
- case 'linux':
- return {
- command:
- 'curl -fsSL https://raw.githubusercontent.com/git-fire/git-fire/main/scripts/install.sh | bash',
- note: 'Remote code — inspect scripts/install.sh in the repo first when you have time. Prefer release assets + checksums for production rollouts.',
- };
- case 'go':
- default:
- return {
- command: 'go install github.com/git-fire/git-fire@latest',
- note: 'Requires Go 1.24.2+. Ensure $HOME/go/bin is on your PATH.',
- };
- }
+ return orderPool(fam, ['script', 'deb', 'rpm', 'brew'] as const);
}
+ return orderPool(fam, ['deb', 'rpm', 'manual'] as const);
+}
- // git-rain
- switch (os) {
- case 'macos':
- return {
- command: 'brew tap git-fire/tap\nbrew install git-rain',
- note: 'Requires Homebrew.',
- };
- case 'windows':
- return {
- command: 'winget install git-rain.git-rain',
- note: 'Windows Package Manager (winget).',
- };
- case 'linux':
+function orderPool(fam: 'deb' | 'rpm' | 'unknown', neutralOrder: readonly T[]): T[] {
+ const neutral = [...neutralOrder];
+ if (fam === 'deb' && neutral.some((x) => x === 'deb')) {
+ return ['deb' as T, ...neutral.filter((x) => x !== 'deb')];
+ }
+ if (fam === 'rpm' && neutral.some((x) => x === 'rpm')) {
+ return ['rpm' as T, ...neutral.filter((x) => x !== 'rpm')];
+ }
+ return neutral;
+}
+
+function buildMethodDefs(product: ProductId, os: OsFamily): { id: string; label: string }[] {
+ if (os === 'windows') {
+ return [
+ { id: 'winget', label: WIN_METHOD_LABELS.winget },
+ { id: 'manual', label: WIN_METHOD_LABELS.manual },
+ ];
+ }
+ if (os === 'linux') {
+ return orderedLinuxMethodIds(product).map((id) => ({
+ id,
+ label: LINUX_METHOD_LABELS[id] ?? id,
+ }));
+ }
+ return [];
+}
+
+function coerceMethodForRoot(root: HTMLElement, choice: string): string {
+ const product = root.dataset.product as ProductId;
+ const os = selectedOs(root);
+ const ids = buildMethodDefs(product, os).map((d) => d.id);
+ if (ids.includes(choice)) return choice;
+ return ids[0] ?? choice;
+}
+
+function getEffectiveMethodForRoot(root: HTMLElement): string {
+ const product = root.dataset.product as ProductId;
+ const os = selectedOs(root);
+ const ids = buildMethodDefs(product, os).map((d) => d.id);
+ if (ids.length === 0) return 'winget';
+ const raw = os === 'linux' ? storedLinuxMethod() : os === 'windows' ? storedWindowsMethod() : null;
+ if (raw && ids.includes(raw)) return raw;
+ return ids[0]!;
+}
+
+function linuxDetectHint(): string {
+ const fam = detectLinuxPackageFamily();
+ if (fam === 'deb') {
+ return 'Your browser’s user agent looks DEB-based (best-effort). Installers that match are listed first.';
+ }
+ return 'Your browser’s user agent looks RPM-based (best-effort). Installers that match are listed first.';
+}
+
+function commandMacosGo(product: ProductId, os: OsFamily): { command: string; note: string } {
+ if (product === 'git-fire') {
+ if (os === 'macos') {
return {
- command:
- '# Download .deb or .rpm from GitHub Releases, then:\n# Debian/Ubuntu:\nsudo dpkg -i ./git-rain__amd64.deb\n\n# Fedora/RHEL:\nsudo dnf install ./git-rain__amd64.rpm',
- note: 'There is no first-party curl installer for git-rain — use packages from Releases or Go install.',
+ command: 'brew tap git-fire/tap\nbrew install git-fire',
+ note: 'Requires Homebrew. Same commands work on Linuxbrew.',
};
- case 'go':
+ }
+ return {
+ command: 'go install github.com/git-fire/git-fire@latest',
+ note: 'Requires Go 1.24.2+. Ensure $HOME/go/bin is on your PATH.',
+ };
+ }
+ if (os === 'macos') {
+ return {
+ command: 'brew tap git-fire/tap\nbrew install git-rain',
+ note: 'Requires Homebrew.',
+ };
+ }
+ return {
+ command: 'go install github.com/git-fire/git-rain@latest',
+ note: 'Requires Go 1.24.2+. Ensure $HOME/go/bin is on your PATH.',
+ };
+}
+
+function fireLinuxDeb(): { command: string; note: string } {
+ return {
+ command: 'sudo dpkg -i ./git-fire__amd64.deb',
+ note: 'Download the matching .deb from GitHub Releases and replace with the release tag (for example v0.2.0). Verify checksums when you can.',
+ };
+}
+
+function fireLinuxRpm(): { command: string; note: string } {
+ return {
+ command: 'sudo dnf install ./git-fire__amd64.rpm',
+ note: 'Download the matching .rpm from GitHub Releases and replace with the release tag. On older systems you can use yum instead of dnf. Verify checksums when you can.',
+ };
+}
+
+function fireLinuxScript(): { command: string; note: string } {
+ return {
+ command:
+ 'curl -fsSL https://raw.githubusercontent.com/git-fire/git-fire/main/scripts/install.sh | bash',
+ note: 'Remote code — inspect scripts/install.sh in the repo first when you have time. Prefer release assets + checksums for production rollouts.',
+ };
+}
+
+function fireLinuxBrew(): { command: string; note: string } {
+ return {
+ command: 'brew tap git-fire/tap\nbrew install git-fire',
+ note: 'Homebrew on Linux (Linuxbrew). Same tap as macOS.',
+ };
+}
+
+function rainLinuxDeb(): { command: string; note: string } {
+ return {
+ command: 'sudo dpkg -i ./git-rain__amd64.deb',
+ note: 'Download the .deb from GitHub Releases and replace with the published tag. Verify checksums when you can.',
+ };
+}
+
+function rainLinuxRpm(): { command: string; note: string } {
+ return {
+ command: 'sudo dnf install ./git-rain__amd64.rpm',
+ note: 'Download the .rpm from GitHub Releases and replace with the published tag. Verify checksums when you can.',
+ };
+}
+
+function rainLinuxManual(): { command: string; note: string } {
+ return {
+ command:
+ '# Download the archive for your platform from GitHub Releases, extract, then:\nchmod +x git-rain\nsudo mv git-rain /usr/local/bin/',
+ note: 'Official “binary archive” path from the git-rain README — place the binary on your PATH.',
+ };
+}
+
+function fireWindowsWinget(): { command: string; note: string } {
+ return {
+ command: 'winget install git-fire',
+ note: 'If the short ID is not available yet: winget install git-fire.git-fire',
+ };
+}
+
+function fireWindowsManual(): { command: string; note: string } {
+ return {
+ command:
+ '# Download git-fire.exe for your arch from GitHub Releases, then in PowerShell:\nNew-Item -ItemType Directory -Force "$env:USERPROFILE\\bin" | Out-Null\nMove-Item .\\git-fire.exe "$env:USERPROFILE\\bin\\git-fire.exe" -Force',
+ note: 'Add %USERPROFILE%\\bin to your user PATH if it is not already there (see git-fire README).',
+ };
+}
+
+function rainWindowsWinget(): { command: string; note: string } {
+ return {
+ command: 'winget install git-rain.git-rain',
+ note: 'Windows Package Manager (winget).',
+ };
+}
+
+function rainWindowsManual(): { command: string; note: string } {
+ return {
+ command:
+ '# Download git-rain.exe for your arch from GitHub Releases, then in PowerShell:\nNew-Item -ItemType Directory -Force "$env:USERPROFILE\\bin" | Out-Null\nMove-Item .\\git-rain.exe "$env:USERPROFILE\\bin\\git-rain.exe" -Force',
+ note: 'Add %USERPROFILE%\\bin to your user PATH if it is not already there (see git-rain README).',
+ };
+}
+
+function resolveCommand(product: ProductId, os: OsFamily, methodId: string): { command: string; note: string } {
+ if (os === 'macos' || os === 'go') return commandMacosGo(product, os);
+ if (os === 'windows') {
+ if (product === 'git-fire') {
+ return methodId === 'manual' ? fireWindowsManual() : fireWindowsWinget();
+ }
+ return methodId === 'manual' ? rainWindowsManual() : rainWindowsWinget();
+ }
+ if (product === 'git-fire') {
+ switch (methodId) {
+ case 'deb':
+ return fireLinuxDeb();
+ case 'rpm':
+ return fireLinuxRpm();
+ case 'brew':
+ return fireLinuxBrew();
+ case 'script':
+ default:
+ return fireLinuxScript();
+ }
+ }
+ switch (methodId) {
+ case 'rpm':
+ return rainLinuxRpm();
+ case 'manual':
+ return rainLinuxManual();
+ case 'deb':
default:
- return {
- command: 'go install github.com/git-fire/git-rain@latest',
- note: 'Requires Go 1.24.2+. Ensure $HOME/go/bin is on your PATH.',
- };
+ return rainLinuxDeb();
}
}
-function renderPicker(root: HTMLElement) {
+function selectedOs(root: HTMLElement): OsFamily {
+ const active = root.querySelector(
+ '[data-install-platform][aria-checked="true"]',
+ );
+ return coerceOs(active?.dataset.installPlatform) ?? PLATFORM_ORDER[0];
+}
+
+function syncPlatformButtons(root: HTMLElement, os: OsFamily) {
+ root.querySelectorAll('[data-install-platform]').forEach((btn) => {
+ const v = coerceOs(btn.dataset.installPlatform);
+ const isSelected = v === os;
+ btn.setAttribute('aria-checked', isSelected ? 'true' : 'false');
+ btn.tabIndex = isSelected ? 0 : -1;
+ btn.classList.toggle('install-picker__platform-btn--active', isSelected);
+ });
+}
+
+function syncMethodButtons(root: HTMLElement, methodId: string) {
+ root.querySelectorAll('[data-install-method]').forEach((btn) => {
+ const isSelected = btn.dataset.installMethod === methodId;
+ btn.setAttribute('aria-checked', isSelected ? 'true' : 'false');
+ btn.tabIndex = isSelected ? 0 : -1;
+ btn.classList.toggle('install-picker__method-btn--active', isSelected);
+ });
+}
+
+function ensureMethodControls(root: HTMLElement, product: ProductId, os: OsFamily) {
+ const area = root.querySelector('[data-install-method-area]');
+ const group = root.querySelector('[data-install-method-radiogroup]');
+ if (!area || !group) return;
+
+ if (os !== 'linux' && os !== 'windows') {
+ area.hidden = true;
+ area.setAttribute('aria-hidden', 'true');
+ return;
+ }
+
+ area.hidden = false;
+ area.removeAttribute('aria-hidden');
+
+ const rebuildKey = `${product}:${os}`;
+ if (root.dataset.installMethodBuilt !== rebuildKey) {
+ root.dataset.installMethodBuilt = rebuildKey;
+ group.replaceChildren();
+ for (const def of buildMethodDefs(product, os)) {
+ const btn = document.createElement('button');
+ btn.type = 'button';
+ btn.role = 'radio';
+ btn.setAttribute('aria-checked', 'false');
+ btn.className = 'install-picker__method-btn';
+ btn.dataset.installMethod = def.id;
+ btn.textContent = def.label;
+ btn.tabIndex = -1;
+ group.append(btn);
+ }
+ }
+
+ const effective = getEffectiveMethodForRoot(root);
+ syncMethodButtons(root, effective);
+}
+
+function updateDetectHint(root: HTMLElement, os: OsFamily) {
+ const hint = root.querySelector('[data-install-detect-hint]');
+ if (!hint) return;
+ if (os === 'linux' && detectLinuxPackageFamily() !== 'unknown') {
+ hint.hidden = false;
+ hint.textContent = linuxDetectHint();
+ } else {
+ hint.hidden = true;
+ hint.textContent = '';
+ }
+}
+
+function updateCommandBlock(root: HTMLElement) {
const product = root.dataset.product as ProductId;
if (product !== 'git-fire' && product !== 'git-rain') return;
- const select = root.querySelector('[data-install-select]');
const pre = root.querySelector('[data-install-command]');
const noteEl = root.querySelector('[data-install-note]');
- if (!select || !pre || !noteEl) return;
+ if (!pre || !noteEl) return;
- const os = select.value as OsFamily;
- const { command, note } = commandBlock(product, os);
- pre.textContent = command;
- noteEl.textContent = note;
+ const os = selectedOs(root);
+ const block =
+ os === 'linux' || os === 'windows'
+ ? resolveCommand(product, os, getEffectiveMethodForRoot(root))
+ : commandMacosGo(product, os);
+ pre.textContent = block.command;
+ noteEl.textContent = block.note;
+}
+
+function renderPicker(root: HTMLElement) {
+ const product = root.dataset.product as ProductId;
+ if (product !== 'git-fire' && product !== 'git-rain') return;
+
+ const os = selectedOs(root);
+ ensureMethodControls(root, product, os);
+ updateDetectHint(root, os);
+ updateCommandBlock(root);
}
function setPlatformEverywhere(os: OsFamily) {
persistOs(os);
document.querySelectorAll('[data-install-picker]').forEach((root) => {
- const select = root.querySelector('[data-install-select]');
- if (select) select.value = os;
+ syncPlatformButtons(root, os);
renderPicker(root);
});
}
+function setInstallMethodEverywhere(os: OsFamily, rawMethodId: string) {
+ if (os === 'linux') persistLinuxMethod(rawMethodId);
+ else if (os === 'windows') persistWindowsMethod(rawMethodId);
+ else return;
+
+ document.querySelectorAll('[data-install-picker]').forEach((root) => {
+ if (selectedOs(root) !== os) return;
+ const effective = coerceMethodForRoot(root, rawMethodId);
+ syncMethodButtons(root, effective);
+ updateCommandBlock(root);
+ });
+}
+
+function bindPlatformRadiogroup(root: HTMLElement) {
+ const group = root.querySelector('.install-picker__platforms');
+ const buttons = root.querySelectorAll('[data-install-platform]');
+ if (!group || buttons.length === 0) return;
+
+ group.addEventListener('keydown', (event) => {
+ if (!(event.target instanceof HTMLButtonElement)) return;
+ if (!event.target.matches('[data-install-platform]')) return;
+
+ const current = coerceOs(event.target.dataset.installPlatform);
+ if (!current) return;
+
+ let next: OsFamily | null = null;
+
+ switch (event.key) {
+ case 'ArrowRight':
+ case 'ArrowDown': {
+ event.preventDefault();
+ const i = PLATFORM_ORDER.indexOf(current);
+ next = PLATFORM_ORDER[Math.min(i + 1, PLATFORM_ORDER.length - 1)];
+ break;
+ }
+ case 'ArrowLeft':
+ case 'ArrowUp': {
+ event.preventDefault();
+ const i = PLATFORM_ORDER.indexOf(current);
+ next = PLATFORM_ORDER[Math.max(i - 1, 0)];
+ break;
+ }
+ case 'Home': {
+ event.preventDefault();
+ next = PLATFORM_ORDER[0];
+ break;
+ }
+ case 'End': {
+ event.preventDefault();
+ next = PLATFORM_ORDER[PLATFORM_ORDER.length - 1];
+ break;
+ }
+ default:
+ return;
+ }
+
+ if (next) {
+ setPlatformEverywhere(next);
+ queueMicrotask(() => {
+ root.querySelector(`[data-install-platform="${next}"]`)?.focus();
+ });
+ }
+ });
+
+ buttons.forEach((btn) => {
+ btn.addEventListener('click', () => {
+ const os = coerceOs(btn.dataset.installPlatform);
+ if (os) setPlatformEverywhere(os);
+ });
+ });
+}
+
+function bindMethodInteraction(root: HTMLElement) {
+ root.addEventListener('click', (event) => {
+ const t = event.target;
+ if (!(t instanceof Element)) return;
+ const btn = t.closest('[data-install-method]');
+ if (!btn || !root.contains(btn)) return;
+ const os = selectedOs(root);
+ if (os !== 'linux' && os !== 'windows') return;
+ const id = btn.dataset.installMethod;
+ if (!id) return;
+ setInstallMethodEverywhere(os, id);
+ });
+
+ root.addEventListener('keydown', (event) => {
+ if (!(event.target instanceof HTMLButtonElement)) return;
+ if (!event.target.matches('[data-install-method]')) return;
+ const os = selectedOs(root);
+ if (os !== 'linux' && os !== 'windows') return;
+
+ const group = root.querySelector('[data-install-method-radiogroup]');
+ if (!group?.contains(event.target)) return;
+
+ const order = [...group.querySelectorAll('[data-install-method]')].map(
+ (b) => b.dataset.installMethod ?? '',
+ );
+ const current = event.target.dataset.installMethod ?? '';
+ const idx = order.indexOf(current);
+ if (idx < 0) return;
+
+ let nextIdx: number | null = null;
+ switch (event.key) {
+ case 'ArrowRight':
+ case 'ArrowDown':
+ event.preventDefault();
+ nextIdx = Math.min(idx + 1, order.length - 1);
+ break;
+ case 'ArrowLeft':
+ case 'ArrowUp':
+ event.preventDefault();
+ nextIdx = Math.max(idx - 1, 0);
+ break;
+ case 'Home':
+ event.preventDefault();
+ nextIdx = 0;
+ break;
+ case 'End':
+ event.preventDefault();
+ nextIdx = order.length - 1;
+ break;
+ default:
+ return;
+ }
+ if (nextIdx === null) return;
+ const nextId = order[nextIdx];
+ if (nextId) {
+ setInstallMethodEverywhere(os, nextId);
+ queueMicrotask(() => {
+ root.querySelector(`[data-install-method="${nextId}"]`)?.focus();
+ });
+ }
+ });
+}
+
function bindPicker(root: HTMLElement) {
const product = root.dataset.product as ProductId;
if (product !== 'git-fire' && product !== 'git-rain') return;
- const select = root.querySelector('[data-install-select]');
const copyBtn = root.querySelector('[data-install-copy]');
-
- if (!select || !copyBtn) return;
+ if (!copyBtn) return;
const initial = storedOs() ?? detectOs();
- select.value = initial;
+ syncPlatformButtons(root, initial);
renderPicker(root);
- select.addEventListener('change', () => {
- setPlatformEverywhere(select.value as OsFamily);
- });
+ bindPlatformRadiogroup(root);
+ bindMethodInteraction(root);
copyBtn.addEventListener('click', async () => {
const pre = root.querySelector('[data-install-command]');
diff --git a/src/styles/global.css b/src/styles/global.css
index e80686f..deb6675 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -445,7 +445,7 @@ a:hover {
border-color: color-mix(in srgb, var(--color-border) 80%, var(--color-rain));
}
-.install-picker--embedded label {
+.install-picker--embedded .install-picker__label {
margin-bottom: var(--space-xs);
}
@@ -470,25 +470,139 @@ a:hover {
font-size: 0.8125rem;
}
-.install-picker label {
+.install-picker--embedded .install-picker__methods {
+ max-width: 100%;
+}
+
+.install-picker--embedded .install-picker__method-btn {
+ font-size: 0.6875rem;
+}
+
+.install-picker--embedded .install-picker__detect-hint {
+ font-size: 0.6875rem;
+ margin-bottom: var(--space-xs);
+}
+
+.install-picker__label {
display: block;
font-size: 0.8125rem;
font-weight: 600;
color: var(--color-muted);
- margin-bottom: var(--space-sm);
+ margin: 0 0 var(--space-sm);
+}
+
+.install-picker__label--method {
+ margin-top: var(--space-xs);
+ margin-bottom: var(--space-xs);
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
}
-.install-picker select {
- width: 100%;
+.install-picker__platforms {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-xs);
+ margin-bottom: var(--space-md);
max-width: 22rem;
- padding: var(--space-sm) var(--space-md);
+}
+
+.install-picker__platform-btn {
font: inherit;
- font-size: 0.9375rem;
- color: var(--color-text);
+ font-size: 0.8125rem;
+ font-weight: 600;
+ padding: var(--space-xs) var(--space-sm);
+ border-radius: var(--radius-sm);
+ border: 1px solid color-mix(in srgb, var(--color-border) 85%, var(--color-text));
background: var(--color-bg);
- border: 1px solid color-mix(in srgb, var(--color-border) 80%, var(--color-text));
+ color: var(--color-muted);
+ cursor: pointer;
+ transition:
+ border-color 0.15s ease,
+ background 0.15s ease,
+ color 0.15s ease;
+}
+
+.install-picker__platform-btn:hover {
+ color: var(--color-text);
+ border-color: color-mix(in srgb, var(--color-border) 55%, var(--color-text));
+}
+
+.install-picker[data-product='git-fire'] .install-picker__platform-btn--active {
+ color: var(--color-fire);
+ border-color: color-mix(in srgb, var(--color-fire) 45%, var(--color-border));
+ background: color-mix(in srgb, var(--color-fire) 12%, var(--color-bg));
+}
+
+.install-picker[data-product='git-rain'] .install-picker__platform-btn--active {
+ color: var(--color-rain);
+ border-color: color-mix(in srgb, var(--color-rain) 45%, var(--color-border));
+ background: color-mix(in srgb, var(--color-rain) 12%, var(--color-bg));
+}
+
+.install-picker__method-area {
+ margin-bottom: var(--space-sm);
+}
+
+.install-picker__method-area[hidden] {
+ display: none;
+}
+
+.install-picker__methods {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-xs);
+ margin-bottom: var(--space-sm);
+ max-width: 28rem;
+}
+
+.install-picker__method-btn {
+ font: inherit;
+ font-size: 0.75rem;
+ font-weight: 600;
+ padding: 0.2rem 0.45rem;
border-radius: var(--radius-sm);
- margin-bottom: var(--space-md);
+ border: 1px dashed color-mix(in srgb, var(--color-border) 70%, var(--color-muted));
+ background: color-mix(in srgb, var(--color-bg) 92%, var(--color-surface));
+ color: var(--color-muted);
+ cursor: pointer;
+ transition:
+ border-color 0.15s ease,
+ background 0.15s ease,
+ color 0.15s ease,
+ border-style 0.15s ease;
+}
+
+.install-picker__method-btn:hover {
+ color: var(--color-text);
+ border-color: color-mix(in srgb, var(--color-border) 50%, var(--color-text));
+ border-style: solid;
+}
+
+.install-picker[data-product='git-fire'] .install-picker__method-btn--active {
+ color: var(--color-fire);
+ border-style: solid;
+ border-color: color-mix(in srgb, var(--color-fire) 40%, var(--color-border));
+ background: color-mix(in srgb, var(--color-fire) 10%, var(--color-bg));
+}
+
+.install-picker[data-product='git-rain'] .install-picker__method-btn--active {
+ color: var(--color-rain);
+ border-style: solid;
+ border-color: color-mix(in srgb, var(--color-rain) 40%, var(--color-border));
+ background: color-mix(in srgb, var(--color-rain) 10%, var(--color-bg));
+}
+
+.install-picker__detect-hint {
+ font-size: 0.75rem;
+ line-height: 1.4;
+ color: var(--color-muted);
+ margin: 0 0 var(--space-sm);
+ max-width: 32rem;
+}
+
+.install-picker__detect-hint[hidden] {
+ display: none;
}
.install-picker pre {
@@ -513,7 +627,7 @@ a:hover {
margin-bottom: var(--space-md);
}
-.install-picker button {
+.install-picker__actions button {
font: inherit;
font-size: 0.875rem;
font-weight: 600;
@@ -526,7 +640,7 @@ a:hover {
transition: border-color 0.15s ease, background 0.15s ease;
}
-.install-picker button:hover {
+.install-picker__actions button:hover {
border-color: var(--color-rain-dim);
background: color-mix(in srgb, var(--color-rain) 12%, var(--color-surface));
}