# Save for Later

A tiny, dependency-light React module that turns **any link on your site into a
save target**. Left-click a link and a small chooser pops up at the cursor —
**Open · Open in new tab · Save for later**. "Save" lets the visitor drop the page
into a default list, an existing custom list, or a new list they create. A small
floating button pinned to the left-middle of the page opens a drawer of everything
they've saved.

Everything is stored **locally** in the browser (`localStorage`), synced across
tabs. No account, no backend, no tracking required.

> This is the open-source build. The hosted version on
> [entpoind.com](https://entpoind.com) adds one optional thing: when a visitor
> is signed in, their saved links also sync to their dashboard across devices.
> That bit is **not** in this build — it's pure local-first.

## What's in the box

```
save-for-later/
  save-for-later-provider.tsx   ← mount this once; the orchestrator
  link-chooser.tsx              ← the Open / Open-new-tab / Save popup
  save-panel.tsx                ← the "save to a list" dialog
  saved-links-fab.tsx           ← the left-middle floating button + drawer
  saved-links-view.tsx          ← the grouped saved-links list (reusable)
  store.ts                      ← localStorage store + React hooks
  types.ts                      ← the data types
  save-for-later.css            ← self-contained theme (override to rebrand)
  package.json                  ← peer deps + metadata
  LICENSE                       ← MIT
```

## Requirements (peer dependencies)

- **React** 18+ (uses `useSyncExternalStore`)
- **Tailwind CSS** 3 or 4 (the components use Tailwind utility classes for layout)
- **[lucide-react](https://lucide.dev)** (icons)

## Install

Copy the `save-for-later/` folder into your project (e.g. `components/save-for-later/`),
then install the icon peer dep:

```bash
npm install lucide-react
```

Import the theme once (in your global CSS or root layout):

```ts
import "./components/save-for-later/save-for-later.css";
```

## Use

Mount the provider once, near the root of your app. That's it — it wires the
site-wide link interception, the chooser, the save panel, and the floating button.

```tsx
// app/layout.tsx (Next.js App Router example)
"use client";
import { useRouter } from "next/navigation";
import { SaveForLaterProvider } from "@/components/save-for-later/save-for-later-provider";

export function SaveForLater() {
  const router = useRouter();
  // `router` is optional — pass it for SPA navigation on same-origin links;
  // omit it and same-origin "Open" falls back to window.location.
  return <SaveForLaterProvider router={router.push} />;
}
```

```tsx
// any plain React app
<SaveForLaterProvider />
```

### Show the saved list somewhere else

`SavedLinksView` is the grouped list the drawer renders — reuse it anywhere (a
sidebar, a dedicated page, a dashboard):

```tsx
import { SavedLinksView } from "@/components/save-for-later/saved-links-view";

<SavedLinksView />;
```

### Read the store directly

```tsx
import { useSaveForLater, useSavedCount } from "@/components/save-for-later/store";

const { lists, links, saveLink, removeLink, createList } = useSaveForLater();
const count = useSavedCount();
```

## Opting individual links out

The interceptor never touches right-clicks, middle-clicks, modifier-clicks,
`download` links, or non-page schemes (`mailto:`, `tel:`, `sms:`, `#`, …). To opt
a specific link or region out of interception, add `data-sfl-skip`:

```tsx
<a href="/somewhere" data-sfl-skip>Opens normally, no chooser</a>
<nav data-sfl-skip>…all links in here open normally…</nav>
```

## Optional: bring your own backend sync

`store.ts` exposes a generic `setSyncAdapter({ pull, push })` seam. Wire it to your
own backend to mirror saves server-side (the hosted Trustvice Receipts build uses this to
sync to a signed-in dashboard). Leave it unset for local-only — the default.

```ts
import { setSyncAdapter } from "@/components/save-for-later/store";

setSyncAdapter({
  pull: async () => fetch("/api/saved").then((r) => r.json()),
  push: (state) => { void fetch("/api/saved", { method: "PUT", body: JSON.stringify(state) }); },
});
```

## Restyling

Override any token from `save-for-later.css` in your own CSS:

```css
:root {
  --color-entpoind: #2563eb; /* your accent */
  --color-paper: #fafafa;
}
```

## License

MIT — see [LICENSE](./LICENSE). Built by [Trustvice Receipts](https://entpoind.com).
