Skip to content
Ryno

Overview

What Ryno does, and where to start if you have never opened it.

What Ryno does

Ryno sends Android and iOS deep links to a device from your machine. On an Android phone or an iOS simulator, it tells you what came back — the verdict, the logs, a screenshot. Android goes through ADB, iOS through the Xcode tools.

There is no server, no database and no account. Your workspace is a folder you own: every request in it is a file you can read, diff and review with the rest of your project, and your team gets the same ones when you commit it.

On a physical iPhone, Ryno can send a link but cannot check what the device did with it. It says the link was sent, and claims nothing more.

Where to start

Open a folder as your workspace, then send your first deep link. The pages are meant to be read in that order, and each one ends with the one that follows it.

Live components

The components below are the ones the app ships. Each one runs on this page — open the dialog and it really opens — and the code under it is the file that runs, read from the repository when the page is built. There is no second copy of it to keep in step.

Button
import { Button } from '@ryno/design-system';

export default function ButtonExample(): React.JSX.Element {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button>Run request</Button>
      <Button variant="outline">Open workspace folder…</Button>
      <Button variant="ghost">Cancel</Button>
    </div>
  );
}
Dialog
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
  Input,
} from '@ryno/design-system';

export default function DialogExample(): React.JSX.Element {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Rename request</Button>
      </DialogTrigger>
      <DialogContent closeLabel="Close">
        <DialogHeader>
          <DialogTitle>Rename request</DialogTitle>
          <DialogDescription>
            The request is a file in your workspace, so renaming it renames that file
            too.
          </DialogDescription>
        </DialogHeader>
        <Input aria-label="Request name" defaultValue="open-product" />
        <DialogFooter>
          <DialogClose asChild>
            <Button variant="ghost">Cancel</Button>
          </DialogClose>
          <DialogClose asChild>
            <Button>Rename</Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
Key badge
O/
import { Kbd, KeyLanguage, KeyToken } from '@ryno/design-system';

export default function KeyBadgeExample(): React.JSX.Element {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Kbd keys={[KeyToken.Mod, KeyToken.Enter]} lang={KeyLanguage.English} />
      <Kbd keys={[KeyToken.Mod, 'O']} lang={KeyLanguage.English} />
      <Kbd keys={[KeyToken.Shift, KeyToken.Tab]} lang={KeyLanguage.English} />
      <Kbd keys={['/']} lang={KeyLanguage.English} />
    </div>
  );
}

Surfaces

Switch the theme in the header and these surfaces change with it. They are the ones the app paints, down to the value, because this page names them and never sets a colour of its own.

Card

bg-card

Muted

bg-muted

Surface

bg-surface