From 1452d63d744a528cc011484ae274bc0541b5d181 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sat, 10 Oct 2026 09:51:07 +0000 Subject: [PATCH] feat: curl | sh installer for qc, shown on the homepage public/install.sh installs @profullstack/qryptchat (qc) with bun if present, else npm on Node >= 22, falling back to ~/.local when the global npm prefix is not writable. The homepage hero shows the one-liner with a copy button. Co-Authored-By: Claude Opus 5.5 --- public/install.sh | 50 ++++++++++++++++++++++++++++++++++++++++ src/app/home-content.jsx | 31 +++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100755 public/install.sh diff --git a/public/install.sh b/public/install.sh new file mode 100755 index 00000000..b77883b2 --- /dev/null +++ b/public/install.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# Install qc, the qrypt.chat terminal client (npm: @profullstack/qryptchat). +# +# curl -fsSL https://qrypt.chat/install.sh | sh +# +# Uses bun if present, otherwise npm (Node >= 22). When the global npm prefix +# is not writable it installs into ~/.local instead of asking for sudo. +set -eu + +PKG="@profullstack/qryptchat" + +say() { printf '%s\n' "$*"; } +die() { printf 'qc install: %s\n' "$*" >&2; exit 1; } +have() { command -v "$1" >/dev/null 2>&1; } + +say "Installing qc (qrypt.chat terminal client)..." + +if have bun; then + bun add -g "$PKG" + BIN_DIR="${BUN_INSTALL:-$HOME/.bun}/bin" +elif have npm && have node; then + major=$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0) + [ "$major" -ge 22 ] || die "Node >= 22 required (found $(node -v)). Upgrade Node or install bun: https://bun.sh" + prefix=$(npm config get prefix) + modules="$prefix/lib/node_modules" + [ -d "$modules" ] || modules="$prefix/lib" + if [ -w "$modules" ] && [ -w "$prefix/bin" ]; then + npm install -g "$PKG" + BIN_DIR="$prefix/bin" + else + BIN_DIR="$HOME/.local/bin" + say "npm prefix $prefix is not writable; installing into $HOME/.local" + npm install -g --prefix "$HOME/.local" "$PKG" + fi +else + die "needs bun (https://bun.sh) or Node >= 22 with npm (https://nodejs.org)" +fi + +say "" +if have qc; then + say "$(qc --version 2>/dev/null || echo qc) installed." +else + say "qc installed to $BIN_DIR, which is not on your PATH. Add it:" + say " export PATH=\"$BIN_DIR:\$PATH\"" +fi +say "" +say "Next:" +say " qc login # approve in your browser; keys are sealed to this device" +say " qc # open the TUI" +say " qc --help # CLI and MCP usage" diff --git a/src/app/home-content.jsx b/src/app/home-content.jsx index eb2e8983..4897e912 100644 --- a/src/app/home-content.jsx +++ b/src/app/home-content.jsx @@ -1,10 +1,24 @@ 'use client'; import Link from 'next/link'; +import { useState } from 'react'; import { useI18n } from '@/lib/hooks/useI18n.js'; +const INSTALL_CMD = 'curl -fsSL https://qrypt.chat/install.sh | sh'; + export default function HomeContent() { const { t } = useI18n(); + const [copied, setCopied] = useState(false); + + const copyInstall = async () => { + try { + await navigator.clipboard.writeText(INSTALL_CMD); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch { + // Clipboard blocked (insecure context or denied); the command stays selectable. + } + }; return ( <> @@ -19,6 +33,16 @@ export default function HomeContent() { {t('nav.register')} {t('nav.login')} +
+ Terminal client (qc): TUI, CLI and MCP +
+ + {INSTALL_CMD} + +
+
Illustration of a locked, end-to-end encrypted QryptChat conversation protected by post-quantum cryptography @@ -111,6 +135,13 @@ export default function HomeContent() { .hero-actions .btn-primary:hover { background-color: var(--color-bg-secondary); transform: translateY(-2px); box-shadow: 0 10px 25px rgba(0,0,0,.2); } .hero-actions .btn-secondary { background-color: transparent; color: white; border-color: rgba(255,255,255,.3); } .hero-actions .btn-secondary:hover { background-color: rgba(255,255,255,.1); border-color: white; } + .hero-install { margin-top: var(--space-8); } + .hero-install-label { display: block; font-size: .875rem; opacity: .8; margin-bottom: var(--space-2); } + .hero-install-cmd { display: flex; align-items: center; gap: var(--space-3); max-width: 100%; background: rgba(0,0,0,.35); border: 1px solid rgba(255,255,255,.2); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); } + .hero-install-cmd code { flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9375rem; color: #fff; text-align: left; user-select: all; } + .hero-install-prompt { opacity: .6; user-select: none; } + .hero-install-copy { flex-shrink: 0; background: transparent; color: #fff; border: 1px solid rgba(255,255,255,.35); border-radius: var(--radius-md); padding: var(--space-1) var(--space-3); font-size: .8125rem; font-weight: 600; cursor: pointer; } + .hero-install-copy:hover { background: rgba(255,255,255,.1); border-color: #fff; } .hero-image { width: 100%; height: auto; max-width: 500px; opacity: 0.9; filter: drop-shadow(0 10px 30px rgba(0,0,0,.3)); } .features { padding: var(--space-20) 0; background-color: var(--color-bg-secondary); } .section-header { text-align: center; margin-bottom: var(--space-16); }