npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@remotedraw/svelte

v0.5.0

Published

Svelte stores and typed clients for RemoteDraw receivers and senders.

Readme

@remotedraw/svelte

Svelte adapter for RemoteDraw. It wraps the framework-free @remotedraw/client receiver store in the Svelte store contract, so receiver state works with $store auto-subscription. It has no dependency on the Svelte runtime — the store contract is structural.

npm install @remotedraw/svelte   # npm
pnpm add @remotedraw/svelte      # pnpm
yarn add @remotedraw/svelte      # Yarn
bun add @remotedraw/svelte       # bun

To use an unreleased build from a RemoteDraw checkout instead, see Working from a checkout.

Usage

<script lang="ts">
  import {
    createHttpReceiverClient,
    createRemoteDrawReceiver,
  } from "@remotedraw/svelte";
  import { onDestroy } from "svelte";

  export let session: {
    id: string;
    receiverToken: string;
    joinUrl: string;
    // From the create response, beside `session`: when the QR dies.
    joinTokenExpiresAt: number;
  };

  const receiver = createRemoteDrawReceiver({
    receiver: createHttpReceiverClient("https://api.remotedraw.com"),
    session,
  });
  receiver.start();
  onDestroy(() => receiver.stop());
</script>

<svg viewBox="0 0 1000 1000">
  {#each $receiver.drawings as drawing (drawing.id)}
    <polyline
      points={drawing.points.map((p) => `${p.x * 1000},${p.y * 1000}`).join(" ")}
      fill="none"
      stroke="#151512"
      stroke-width="6"
      stroke-linecap="round"
    />
  {/each}
</svg>

All HTTP clients, joinTokenFromInput, geometry helpers, and types are re-exported from @remotedraw/client, so one import covers backend session clients, sender clients, and receiver state.

Open On My Phone

When the phone is the user's own app rather than a scan, createDirectSender is the Svelte half of the direct-sender flow. It is the same state machine React's useDirectSender binds (createDirectSenderController in @remotedraw/client), so the two adapters cannot disagree about when a launch has failed.

<script lang="ts">
  import { createDirectSender, createRemoteDrawReceiver } from "@remotedraw/svelte";

  export let receiver: ReturnType<typeof createRemoteDrawReceiver>;

  const sender = createDirectSender({
    receiver,
    // YOUR endpoint, which calls `connectSender` with the rd_sk_... key.
    // The key never reaches the browser.
    connect: () =>
      fetch("/api/remotedraw/direct-sender", { method: "POST" }).then((response) =>
        response.json(),
      ),
  });
</script>

<button on:click={() => sender.connect()} disabled={$sender.status === "connecting"}>
  {$sender.status === "ready" ? "Waiting for your phone…" : "Draw on my phone"}
</button>

{#if $sender.status === "error"}
  <p>{$sender.error?.message}</p>
  <!-- Render your own QR from $receiver.joinUrl here. -->
{/if}

| Option | Type | Default | Notes | | --- | --- | --- | --- | | connect | () => Promise<DirectSenderResult \| null \| undefined> | — | required; your own backend call. It may return the raw connectSender response, a create-session response carrying it under senderConnection, or just { launchUrl } | | receiver | the store from createRemoteDrawReceiver | — | supplies the session and sender list that drive drawing, submitted, and expired. Without it the store stops at ready | | autoLaunch | boolean | true | false mints without navigating; call launch() yourself | | openUrl | (url: string) => void | window.location.href = url | | | onError | (error: RemoteDrawLaunchError) => void | — | |

$sender is { status, launchUrl, senderToken, senderId, connection, error }, where status is idle \| connecting \| ready \| drawing \| submitted \| expired \| error and error is a RemoteDrawLaunchError with kind: "connect-failed" \| "no-launch-url" \| "launch-blocked". The store also exposes connect(), launch(), reset(), and stop() (which detaches from the receiver store).

A custom scheme that nothing has registered fails silently — no error, no navigation, no event — so the only evidence a launch failed is a status that stays ready. Time it out yourself and show the QR; React's RemoteDrawLaunchButton does exactly this with a 12 s default.

When a session pauses, is rate-limited or ends

$receiver.status says what the last read did, using the same table as every kit (receiverFailureDisposition, re-exported from @remotedraw/client):

  • "retrying": a transient failure (too_many_requests, temporarily_unavailable, internal_error, request_timeout, network errors). The session and ink stay; the store polls again on its own, never before $receiver.retryAt (the server's Retry-After). Nothing to do.
  • "paused": billing, scope or version (billing_limit_reached, wallet_balance_exhausted, billing_not_configured, included_capacity_exhausted, capability_not_granted, insufficient_scope, beta_access_required, sdk_too_old, other 4xx). The session and ink stay and reading stops until you resume.
  • "stopped" with $receiver.error: the session is over (invalid_receiver_token, session_not_found, session_not_active, receiver_lease_expired, 401/404/410), and $receiver.session.status is "ended" (or "expired"). Create a new session.

refetch() or start() on a paused or stopped store is an explicit resume: the failure clears in one update and one new read follows (after retryAt if a Retry-After is still running). stop() keeps the halt for those credentials, so a later start() resumes the same way; setSession() with other credentials starts fresh.

{#if $receiver.status === "paused"}
  <p>Drawing is paused ({$receiver.error?.message}).</p>
  <button on:click={() => receiver.refetch()}>Try again</button>
{/if}

The capture controller (createCaptureController, also re-exported) wraps the same rules in phases and events: see When a session pauses, is rate-limited or ends in @remotedraw/client. For the capture components in a Svelte page, use <remotedraw-capture> from @remotedraw/elements (its "Frameworks" section covers Svelte 5). While paused it hides the board, its pass reads "Code unavailable" and it shows no error line, so show the reason from its remotedraw-error event: see its pause section.

What is not here yet

Pairing UI, sender pads, and receiver rendering are React-only today: this package deliberately has no .svelte files and no dependency on the Svelte runtime, so anything visual is markup you write against the stores above. The behavior contract those React components implement is documented in docs/plans/2026-06-11-multi-framework-sdk.md so Svelte components can follow.