nits
This commit is contained in:
@@ -29,7 +29,7 @@ function KeyCap({
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex select-none items-center justify-center overflow-hidden rounded-lg border text-sm font-medium',
|
||||
hero ? 'h-14 w-14 text-xl' : 'h-11',
|
||||
hero ? 'h-14 w-14 text-xl' : 'h-14',
|
||||
!hero && 'border-white/10 bg-white/5 text-white/30',
|
||||
hero &&
|
||||
state === 'idle' &&
|
||||
@@ -63,13 +63,13 @@ export function OnboardingKeyboard({
|
||||
}: OnboardingKeyboardProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-1.5">
|
||||
<KeyCap label="esc" className="w-12 text-xs" />
|
||||
<KeyCap label="esc" className="w-14 text-xs" />
|
||||
<div className="flex gap-1.5">
|
||||
<KeyCap label={highlightKey} hero state={state} progress={progress} />
|
||||
<KeyCap label="1" className="w-11" />
|
||||
<KeyCap label="1" className="w-14" />
|
||||
</div>
|
||||
<div className="flex gap-1.5">
|
||||
<KeyCap label="tab" className="w-16 text-xs" />
|
||||
<KeyCap label="tab" className="w-20 text-xs" />
|
||||
<KeyCap label="Q" className="w-12" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { WindowControls } from '@/components/window-controls';
|
||||
import { KeyHint } from '@/components/key-hint';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { OnboardingKeyboard } from './onboarding-keyboard';
|
||||
import { OnboardingStep } from './onboarding-step';
|
||||
@@ -10,14 +9,7 @@ import { ToggleStep } from './toggle-step';
|
||||
import { TapStep } from './tap-step';
|
||||
import { PermissionStep } from './permission-step';
|
||||
|
||||
const STEPS = [
|
||||
'welcome',
|
||||
'hold',
|
||||
'toggle',
|
||||
'tap',
|
||||
'permission',
|
||||
'done',
|
||||
] as const;
|
||||
const STEPS = ['welcome', 'hold', 'tap', 'toggle', 'permission'] as const;
|
||||
type Step = (typeof STEPS)[number];
|
||||
|
||||
/**
|
||||
@@ -34,6 +26,10 @@ export function OnboardingOverlay({ onComplete }: { onComplete: () => void }) {
|
||||
setStep((cur) => STEPS[Math.min(STEPS.indexOf(cur) + 1, STEPS.length - 1)]);
|
||||
}, []);
|
||||
|
||||
const goBack = useCallback(() => {
|
||||
setStep((cur) => STEPS[Math.max(0, STEPS.indexOf(cur) - 1)]);
|
||||
}, []);
|
||||
|
||||
// Focus the panel on the gesture steps so window-level key handling has a
|
||||
// home; the static steps autofocus their primary button instead, which lets
|
||||
// Enter activate it natively (no duplicate window handler).
|
||||
@@ -43,17 +39,27 @@ export function OnboardingOverlay({ onComplete }: { onComplete: () => void }) {
|
||||
}
|
||||
}, [step]);
|
||||
|
||||
// Esc skips the wizard from anywhere.
|
||||
// Esc skips the wizard from anywhere; ← (and Backspace) steps back.
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
onComplete();
|
||||
} else if (e.key === 'Enter' && step === 'welcome') {
|
||||
e.preventDefault();
|
||||
goNext();
|
||||
} else if (
|
||||
e.key === 'ArrowLeft' ||
|
||||
e.key === 'Backspace' ||
|
||||
e.key === 'Delete'
|
||||
) {
|
||||
e.preventDefault();
|
||||
goBack();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKey);
|
||||
return () => window.removeEventListener('keydown', onKey);
|
||||
}, [onComplete]);
|
||||
}, [onComplete, goNext, step, goBack]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen flex-col bg-background text-white">
|
||||
@@ -80,17 +86,7 @@ export function OnboardingOverlay({ onComplete }: { onComplete: () => void }) {
|
||||
{step === 'welcome' && (
|
||||
<OnboardingStep
|
||||
title="Welcome to llink"
|
||||
subtitle="llink is built for your keyboard. Let's learn the one key that matters, it takes 20 seconds."
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={goNext} autoFocus>
|
||||
Get started
|
||||
</Button>
|
||||
<KeyHint keys="Enter" className="text-xs text-white/40">
|
||||
to begin
|
||||
</KeyHint>
|
||||
</>
|
||||
}
|
||||
subtitle="To use llink, you must learn the keyboard shortcuts. Don't worry, once you learn them, you'll feel like your flowing."
|
||||
>
|
||||
<OnboardingKeyboard highlightKey="`" state="idle" />
|
||||
</OnboardingStep>
|
||||
@@ -98,33 +94,7 @@ export function OnboardingOverlay({ onComplete }: { onComplete: () => void }) {
|
||||
{step === 'hold' && <HoldStep onAdvance={goNext} />}
|
||||
{step === 'toggle' && <ToggleStep onAdvance={goNext} />}
|
||||
{step === 'tap' && <TapStep onAdvance={goNext} />}
|
||||
{step === 'permission' && <PermissionStep onAdvance={goNext} />}
|
||||
{step === 'done' && (
|
||||
<OnboardingStep
|
||||
title="You're all set"
|
||||
subtitle="That's everything you need to start sharing."
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={onComplete} autoFocus>
|
||||
Start using llink
|
||||
</Button>
|
||||
<KeyHint keys="?" className="text-xs text-white/40">
|
||||
for all shortcuts
|
||||
</KeyHint>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-2 text-sm text-white/70">
|
||||
<KeyHint keys="`" prefix="Hold">
|
||||
to talk
|
||||
</KeyHint>
|
||||
<KeyHint keys="V">switch audio / video</KeyHint>
|
||||
<KeyHint keys="`" prefix="Tap">
|
||||
to start and stop
|
||||
</KeyHint>
|
||||
</div>
|
||||
</OnboardingStep>
|
||||
)}
|
||||
{step === 'permission' && <PermissionStep onAdvance={onComplete} />}
|
||||
|
||||
<div className="flex items-center justify-center gap-1.5">
|
||||
{STEPS.map((s, i) => (
|
||||
@@ -141,6 +111,28 @@ export function OnboardingOverlay({ onComplete }: { onComplete: () => void }) {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex min-h-5 items-center justify-center gap-4 text-xs text-white/40">
|
||||
{index > 0 && (
|
||||
<KeyHint
|
||||
keys="←"
|
||||
onClick={goBack}
|
||||
title="Back (or press ←)"
|
||||
aria-label="Previous step"
|
||||
>
|
||||
back
|
||||
</KeyHint>
|
||||
)}
|
||||
{step === 'welcome' && (
|
||||
<KeyHint
|
||||
keys="Enter"
|
||||
onClick={goNext}
|
||||
title="Continue (or press Enter)"
|
||||
>
|
||||
continue
|
||||
</KeyHint>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
/** How long a success state lingers before the wizard auto-advances, in ms. */
|
||||
export const SUCCESS_DWELL_MS = 800;
|
||||
export const SUCCESS_DWELL_MS = 2000;
|
||||
|
||||
interface OnboardingStepProps {
|
||||
title: ReactNode;
|
||||
|
||||
Reference in New Issue
Block a user