diff --git a/web/src/App.tsx b/web/src/App.tsx index 2b6c588..7dfe092 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,9 +1,11 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' const INSTALL = 'npm i -g plx-converter' const GITHUB = 'https://github.com/samlehoy/plx' const NPM = 'https://www.npmjs.com/package/plx-converter' const DOCS = 'https://github.com/samlehoy/plx/tree/master/docs' +const ARL_GUIDE = 'https://github.com/samlehoy/plx/blob/master/docs/CONFIG.md#getting-the-cookies' +const NODEJS = 'https://nodejs.org' type Tier = { id: string @@ -99,6 +101,64 @@ function useReducedMotion(): boolean { return reduce } +async function copyToClipboard(text: string): Promise { + try { + await navigator.clipboard.writeText(text) + return true + } catch { + try { + const ta = document.createElement('textarea') + ta.value = text + ta.style.position = 'fixed' + ta.style.opacity = '0' + document.body.appendChild(ta) + ta.select() + document.execCommand('copy') + document.body.removeChild(ta) + return true + } catch { + return false + } + } +} + +function CopyInstallPill({ size, scrollToId }: { size: 'sm' | 'lg'; scrollToId?: string }) { + const [copied, setCopied] = useState(false) + const timeoutRef = useRef(undefined) + + useEffect(() => () => window.clearTimeout(timeoutRef.current), []) + + const handleClick = async () => { + const ok = await copyToClipboard(INSTALL) + if (ok) { + setCopied(true) + window.clearTimeout(timeoutRef.current) + timeoutRef.current = window.setTimeout(() => setCopied(false), 1400) + } + if (scrollToId) { + const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches + document.getElementById(scrollToId)?.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth' }) + } + } + + const base = size === 'lg' ? 'pill meta h-12 px-6' : 'pill meta h-11 px-5' + const color = copied ? 'bg-signal text-ground' : 'bg-ink text-ground hover:bg-signal' + + return ( + + ) +} + export default function App() { const reduce = useReducedMotion() const [tierIdx, setTierIdx] = useState(0) @@ -227,9 +287,6 @@ export default function App() { Spec Install Github - - {INSTALL} -
@@ -253,9 +310,6 @@ export default function App() { Github
- - {INSTALL} -
@@ -266,7 +320,8 @@ export default function App() { className="hero-stage relative bg-stage pt-16" style={{ minHeight: 'calc(100svh)' }} > -
+
+

Spotify ⇄ Deezer

Move your playlist. @@ -278,9 +333,7 @@ export default function App() { no OAuth app. One Deezer ARL cookie is enough. Reverse (Deezer → Spotify) is planned.

+
{/* pinned edge metadata */}
@@ -732,17 +786,16 @@ export default function App() { Move everything.

- Requires Node ≥ 22 and a Deezer ARL cookie —{' '} - - grab yours + Requires Node ≥ 22 and a Deezer ARL cookie — the login session Deezer stores + in your browser once you're signed in.{' '} + + Grab yours , stored locally, never sent anywhere else. Spotify stays anonymous.

- - {INSTALL} - +
+
+ + + → + + New to the command line? Four steps. + +
    +
  1. + Install{' '} + + Node.js + {' '} + — version 22 or later. +
  2. +
  3. Open Terminal (macOS/Linux) or Command Prompt / PowerShell (Windows).
  4. +
  5. + Paste {INSTALL} and + press Enter. +
  6. +
  7. + Run plx --version — + if it prints a version number, the install worked. +
  8. +
+
+
- Github + Github Npm Docs © plx · local-first · no telemetry