@tollbooth-dpyc/web 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/DebugPanel.d.ts +2 -2
- package/dist/react/DebugPanel.js +4 -4
- package/dist/react/NpubGate.js +18 -15
- package/dist/react/SignInLinks.d.ts +4 -0
- package/dist/react/SignInLinks.js +1 -1
- package/dist/react/SignInSteps.d.ts +12 -8
- package/dist/react/SignInSteps.js +7 -5
- package/dist/react/debugSpacer.d.ts +10 -6
- package/dist/react/debugSpacer.js +12 -6
- package/dist/react/signInFlow.d.ts +4 -2
- package/dist/react/signInFlow.js +10 -10
- package/dist/react/signInTools.d.ts +6 -0
- package/dist/react/signInTools.js +8 -0
- package/package.json +1 -1
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* Touch-first: every control is a real button at least 40px square, nothing
|
|
15
15
|
* hides behind hover, and the page under the bar stays tappable.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
* spacer as tall as the
|
|
17
|
+
* While open, the panel keeps its own space: beside the fixed bar it renders
|
|
18
|
+
* an in-flow spacer as tall as the log is right now (measured with a
|
|
19
19
|
* ResizeObserver) plus the iOS safe-area inset, so the page's last line can
|
|
20
20
|
* always scroll clear of it. Mount it LAST in the app shell so that spacer is
|
|
21
21
|
* the last thing in the page's flow.
|
package/dist/react/DebugPanel.js
CHANGED
|
@@ -15,8 +15,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
15
15
|
* Touch-first: every control is a real button at least 40px square, nothing
|
|
16
16
|
* hides behind hover, and the page under the bar stays tappable.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
* spacer as tall as the
|
|
18
|
+
* While open, the panel keeps its own space: beside the fixed bar it renders
|
|
19
|
+
* an in-flow spacer as tall as the log is right now (measured with a
|
|
20
20
|
* ResizeObserver) plus the iOS safe-area inset, so the page's last line can
|
|
21
21
|
* always scroll clear of it. Mount it LAST in the app shell so that spacer is
|
|
22
22
|
* the last thing in the page's flow.
|
|
@@ -25,7 +25,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
25
25
|
import { onProofExpired } from "../client.js";
|
|
26
26
|
import { captureGlobalErrors, clearDebug, debugLogText, debugPush, debugSeverity, } from "../debugLog.js";
|
|
27
27
|
import { readStored, writeStored } from "../storage.js";
|
|
28
|
-
import {
|
|
28
|
+
import { debugSpacerPx, debugSpacerRoom } from "./debugSpacer.js";
|
|
29
29
|
import { useDebugLog } from "./useDebugLog.js";
|
|
30
30
|
const OPEN_KEY = "debug-open";
|
|
31
31
|
const TYPE_INK = {
|
|
@@ -100,7 +100,7 @@ export default function DebugPanel({ title = "Debug", defaultOpen = false, captu
|
|
|
100
100
|
: notices > 0
|
|
101
101
|
? "bg-[var(--tb-notice-bg)] text-[var(--tb-notice-ink)] border-[var(--tb-notice-line)]"
|
|
102
102
|
: "bg-[var(--tb-surface-2)] text-[var(--tb-ink)] border-[var(--tb-line)]";
|
|
103
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": "true", "data-tb-debug-spacer": "", style: { height:
|
|
103
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": "true", "data-tb-debug-spacer": "", style: { height: debugSpacerRoom(open, barHeight), flexShrink: 0 } }), _jsxs("div", { ref: barRef, className: "pointer-events-none fixed inset-x-0 bottom-0 z-50 flex flex-col items-end", children: [_jsxs("div", { className: "pointer-events-auto flex flex-wrap justify-end gap-1 pr-3", children: [open && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: copy, className: plainTab, "aria-label": "Copy the whole log", children: copied === "ok" ? "Copied" : copied === "failed" ? "Copy failed" : "Copy" }), _jsx("button", { type: "button", onClick: clearDebug, className: plainTab, children: "Clear" })] })), _jsxs("button", { type: "button", onClick: toggle, "aria-expanded": open, className: `${tab} ${toggleTone}`, children: [open ? "Hide" : title, " (", log.length, failures > 0 ? ` · ${failures} err` : "", notices > 0 ? ` · ${notices} notice` : "", ")"] })] }), open && (_jsxs("div", { className: "pointer-events-auto max-h-[40vh] w-full overflow-y-auto border-t border-[var(--tb-line)] bg-[var(--tb-surface)] p-3 font-mono text-xs text-[var(--tb-ink)]", children: [children && _jsx("div", { className: "mb-2 border-b border-[var(--tb-line)] pb-2 font-sans", children: children }), log.length === 0 && _jsx("div", { className: "text-[var(--tb-muted)]", children: "No MCP activity yet." }), log.map((entry, i) => {
|
|
104
104
|
const sev = debugSeverity(entry);
|
|
105
105
|
const flag = sev === "ok" ? null : FLAGGED[sev];
|
|
106
106
|
return (_jsxs("div", { className: `-mx-1 flex gap-2 rounded px-1 py-0.5 ${flag?.row ?? ""}`, children: [_jsx("span", { className: "shrink-0 text-[var(--tb-muted)]", children: entry.ts }), _jsxs("span", { className: `w-14 shrink-0 ${flag?.ink ?? TYPE_INK[entry.type]}`, children: [sev === "notice" ? "notice" : entry.type, sev === "failure" && entry.type !== "error" ? " !" : ""] }), _jsx("span", { className: `min-w-0 break-all ${flag?.ink ?? ""}`, children: entry.message })] }, log.length - i));
|
package/dist/react/NpubGate.js
CHANGED
|
@@ -24,23 +24,28 @@ import SignInLinks, {} from "./SignInLinks.js";
|
|
|
24
24
|
import SignInSteps, {} from "./SignInSteps.js";
|
|
25
25
|
import { gateStage } from "./signInFlow.js";
|
|
26
26
|
import { card, errBox, ghost, input, muted, primary, warnBox } from "./ui.js";
|
|
27
|
+
// Left to right, three tiles to a row, wrapping; the tile under way is the
|
|
28
|
+
// only one drawn in ink and accent — the rest recede.
|
|
27
29
|
const STEPS_LOOK = {
|
|
28
|
-
root: "mt-6
|
|
29
|
-
step: "
|
|
30
|
-
done: "
|
|
31
|
-
current: "
|
|
32
|
-
todo: "
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
root: "mt-6 grid grid-cols-3 gap-y-3",
|
|
31
|
+
step: "relative flex flex-col items-center px-1 text-center transition-colors",
|
|
32
|
+
done: "text-[var(--tb-muted)] opacity-60",
|
|
33
|
+
current: "text-[var(--tb-ink)]",
|
|
34
|
+
todo: "text-[var(--tb-muted)]",
|
|
35
|
+
glyph: "text-2xl leading-none",
|
|
36
|
+
label: "mt-1 text-[11px] leading-tight font-medium",
|
|
37
|
+
actor: "text-[10px] leading-tight opacity-70",
|
|
38
|
+
connector: "absolute right-[-0.35rem] top-2 text-base leading-none text-[var(--tb-muted)] opacity-50 [li:nth-child(3n)_&]:hidden",
|
|
36
39
|
};
|
|
37
40
|
const LINKS_LOOK = {
|
|
38
41
|
root: "mt-6 space-y-2",
|
|
39
42
|
lead: `text-xs ${muted}`,
|
|
40
|
-
list: "
|
|
41
|
-
link: `${card}
|
|
43
|
+
list: "grid grid-cols-1 sm:grid-cols-2 gap-2",
|
|
44
|
+
link: `${card} flex items-center gap-3 px-3 py-2.5 hover:border-[var(--tb-accent)] transition-colors`,
|
|
45
|
+
logo: "h-10 w-10 shrink-0 rounded-[10px]",
|
|
46
|
+
text: "min-w-0",
|
|
42
47
|
name: "block text-sm font-medium",
|
|
43
|
-
line: `block text-xs ${muted}`,
|
|
48
|
+
line: `block text-xs leading-snug ${muted}`,
|
|
44
49
|
credit: `text-[10px] leading-relaxed ${muted}`,
|
|
45
50
|
};
|
|
46
51
|
export default function NpubGate({ onLogin, startFresh = false, operatorHash, notice, welcome, steps = true, links = true, linksCredit = true, classNames: c = {}, }) {
|
|
@@ -162,7 +167,7 @@ export default function NpubGate({ onLogin, startFresh = false, operatorHash, no
|
|
|
162
167
|
setPendingProof("");
|
|
163
168
|
setError("");
|
|
164
169
|
}
|
|
165
|
-
return (_jsxs("div", { className: cx("max-w-md mx-auto mt-12 px-4", c.root), children: [_jsxs("h1", { className: "text-xl font-semibold mb-1", children: ["Sign in to ", appName] }), welcome && _jsx("div", { className: cx("text-sm mb-3", c.welcome), children: welcome }), _jsxs("p", { className: cx(`text-sm mb-5 ${muted}`, c.register), children: [appName, " is an operator on the Tollbooth DPYC\u2122 network. DPYC\u2122 \u2014 Don't Pester Your Customer\u2122: no email, no password, no KYC. Your Nostr key is your identity."] }), notice && _jsx("div", { className: `mb-5 ${warnBox}`, children: notice }), stage === "begin" && recents.length > 0 && (_jsxs("div", { className: "mb-5", children: [_jsx("div", { className: `text-xs uppercase tracking-wider mb-2 ${muted}`, children: "Recent identities" }), _jsx("div", { className: "space-y-1.5", children: recents.map((e) => (_jsxs("div", { className: "flex gap-1.5", children: [_jsxs("button", { type: "button", onClick: () => reuseRecent(e), disabled: busy, title: `Re-enter as ${e.npub} on the cached proof`, className: `${card} flex-1 flex items-center justify-between px-3 py-2.5 text-left hover:border-[var(--tb-accent)]`, children: [_jsxs("span", { className: "font-mono text-xs truncate", children: [e.npub.slice(0, 12), "\u2026", e.npub.slice(-6)] }), _jsxs("span", { className: `text-xs shrink-0 ml-2 ${muted}`, children: [ttl(e.expiresAt), " left"] })] }), _jsx("button", { type: "button", onClick: () => forget(e.npub), disabled: busy, title: "Forget this identity", "aria-label": "Forget this identity", className: `w-9 rounded-lg border border-[var(--tb-line)] ${muted}`, children: "\u00D7" })] }, e.npub))) })] })), _jsxs("div", { className: `${card} p-4 space-y-3`, children: [stage === "begin" ? (_jsxs(_Fragment, { children: [_jsx("label", { className: `block text-xs uppercase tracking-wider ${muted}`, htmlFor: "tb-npub-field", children: "
|
|
170
|
+
return (_jsxs("div", { className: cx("max-w-md mx-auto mt-12 px-4", c.root), children: [_jsxs("h1", { className: "text-xl font-semibold mb-1", children: ["Sign in to ", appName] }), welcome && _jsx("div", { className: cx("text-sm mb-3", c.welcome), children: welcome }), _jsxs("p", { className: cx(`text-sm mb-5 ${muted}`, c.register), children: [appName, " is an operator on the Tollbooth DPYC\u2122 network. DPYC\u2122 \u2014 Don't Pester Your Customer\u2122: no email, no password, no KYC. Your Nostr key is your identity."] }), notice && _jsx("div", { className: `mb-5 ${warnBox}`, children: notice }), stage === "begin" && recents.length > 0 && (_jsxs("div", { className: "mb-5", children: [_jsx("div", { className: `text-xs uppercase tracking-wider mb-2 ${muted}`, children: "Recent identities" }), _jsx("div", { className: "space-y-1.5", children: recents.map((e) => (_jsxs("div", { className: "flex gap-1.5", children: [_jsxs("button", { type: "button", onClick: () => reuseRecent(e), disabled: busy, title: `Re-enter as ${e.npub} on the cached proof`, className: `${card} flex-1 flex items-center justify-between px-3 py-2.5 text-left hover:border-[var(--tb-accent)]`, children: [_jsxs("span", { className: "font-mono text-xs truncate", children: [e.npub.slice(0, 12), "\u2026", e.npub.slice(-6)] }), _jsxs("span", { className: `text-xs shrink-0 ml-2 ${muted}`, children: [ttl(e.expiresAt), " left"] })] }), _jsx("button", { type: "button", onClick: () => forget(e.npub), disabled: busy, title: "Forget this identity", "aria-label": "Forget this identity", className: `w-9 rounded-lg border border-[var(--tb-line)] ${muted}`, children: "\u00D7" })] }, e.npub))) })] })), _jsxs("div", { className: `${card} p-4 space-y-3`, children: [stage === "begin" ? (_jsxs(_Fragment, { children: [_jsx("label", { className: `block text-xs uppercase tracking-wider ${muted}`, htmlFor: "tb-npub-field", children: "Enter npub (secure) or nsec (convenient)" }), _jsx("input", { id: "tb-npub-field", type: isNsec ? "password" : "text", value: value, onChange: (e) => {
|
|
166
171
|
setValue(e.target.value);
|
|
167
172
|
setGeneratedHint(false);
|
|
168
173
|
}, onKeyDown: (e) => {
|
|
@@ -172,9 +177,7 @@ export default function NpubGate({ onLogin, startFresh = false, operatorHash, no
|
|
|
172
177
|
signInWithNsec();
|
|
173
178
|
else
|
|
174
179
|
void begin();
|
|
175
|
-
}, placeholder: "npub1\u2026
|
|
176
|
-
? "Your nsec stays in this browser and signs each call."
|
|
177
|
-
: "We send a message to your npub. Approve it from your Nostr client — your signature is the proof." }), _jsx("button", { type: "button", onClick: generateKey, className: ghost, children: "Generate a new key" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: `${warnBox} space-y-1.5`, children: [_jsx("div", { className: "font-medium", children: "Message sent \u2014 open your Nostr client." }), _jsx("div", { children: "Reply with any text. Your signature on that reply is the proof." }), operatorHash && (_jsxs("div", { children: ["Sender fingerprint: ", _jsxs("span", { className: "font-mono", children: ["\uD83D\uDD12 ", operatorHash] })] }))] }), pendingProof && (_jsxs("div", { className: "rounded-lg p-3 text-xs border border-[var(--tb-accent)] space-y-2", children: [_jsx("div", { className: `uppercase tracking-wider text-[10px] ${muted}`, children: "Confirmation code" }), _jsx("div", { className: "font-mono text-base select-all", children: pendingProof }), _jsxs("div", { className: `leading-relaxed ${muted}`, children: ["The same code appears in the message. ", _jsx("b", { children: "Approve it only if the codes match." }), " If they differ \u2014 or the message points you somewhere other than this site \u2014 do not reply."] })] })), _jsx("button", { type: "button", onClick: () => void finish(), disabled: busy, className: primary, children: busy ? "Checking…" : "I've replied — verify" }), _jsx("button", { type: "button", onClick: () => void begin(), disabled: busy, className: ghost, children: "Resend" }), _jsx("button", { type: "button", onClick: reset, disabled: busy, className: `w-full text-xs py-1.5 ${muted}`, children: "Use a different npub" })] })), error && _jsx(Trouble, { raw: error, appName: appName }), note && _jsx("div", { className: `text-xs text-center italic ${muted}`, children: note })] }), steps && _jsx(SignInSteps, { stage: gateStage(stage, busy), classNames: { ...STEPS_LOOK, ...c.steps } }), links && _jsx(SignInLinks, { credit: linksCredit, classNames: { ...LINKS_LOOK, ...c.links } })] }));
|
|
180
|
+
}, placeholder: "npub1\u2026", spellCheck: false, autoCapitalize: "off", autoCorrect: "off", className: input }), generatedHint && (_jsxs("div", { className: warnBox, children: ["New key generated. ", _jsx("b", { children: "Save this nsec" }), " somewhere safe \u2014 it's the only copy and it lives only in this browser."] })), _jsx("button", { type: "button", onClick: () => (isNsec ? signInWithNsec() : void begin()), disabled: !valid || busy, className: primary, children: busy ? "Sending…" : isNpub ? "Send message" : "Sign in" }), isNsec && _jsx("p", { className: `text-xs ${muted}`, children: "Your nsec stays in this browser and signs each call." }), isNpub && (_jsx("p", { className: `text-xs ${muted}`, children: "We send a message to your npub. Approve it from your Nostr client \u2014 your signature is the proof." })), _jsx("p", { className: `text-xs pt-1 ${muted}`, children: "Need a new identity?" }), _jsx("button", { type: "button", onClick: generateKey, className: ghost, children: "Generate a new key" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: `${warnBox} space-y-1.5`, children: [_jsx("div", { className: "font-medium", children: "Message sent \u2014 open your Nostr client." }), _jsx("div", { children: "Reply with any text. Your signature on that reply is the proof." }), operatorHash && (_jsxs("div", { children: ["Sender fingerprint: ", _jsxs("span", { className: "font-mono", children: ["\uD83D\uDD12 ", operatorHash] })] }))] }), pendingProof && (_jsxs("div", { className: "rounded-lg p-3 text-xs border border-[var(--tb-accent)] space-y-2", children: [_jsx("div", { className: `uppercase tracking-wider text-[10px] ${muted}`, children: "Confirmation code" }), _jsx("div", { className: "font-mono text-base select-all", children: pendingProof }), _jsxs("div", { className: `leading-relaxed ${muted}`, children: ["The same code appears in the message. ", _jsx("b", { children: "Approve it only if the codes match." }), " If they differ \u2014 or the message points you somewhere other than this site \u2014 do not reply."] })] })), _jsx("button", { type: "button", onClick: () => void finish(), disabled: busy, className: primary, children: busy ? "Checking…" : "I've replied — verify" }), _jsx("button", { type: "button", onClick: () => void begin(), disabled: busy, className: ghost, children: "Resend" }), _jsx("button", { type: "button", onClick: reset, disabled: busy, className: `w-full text-xs py-1.5 ${muted}`, children: "Use a different npub" })] })), error && _jsx(Trouble, { raw: error, appName: appName }), note && _jsx("div", { className: `text-xs text-center italic ${muted}`, children: note })] }), steps && _jsx(SignInSteps, { stage: gateStage(stage, busy), classNames: { ...STEPS_LOOK, ...c.steps } }), links && _jsx(SignInLinks, { credit: linksCredit, classNames: { ...LINKS_LOOK, ...c.links } })] }));
|
|
178
181
|
}
|
|
179
182
|
/** A failure, read to the person: the lead first, the service's own words underneath. */
|
|
180
183
|
function Trouble({ raw, appName }) {
|
|
@@ -15,6 +15,10 @@ export interface SignInLinksClassNames {
|
|
|
15
15
|
list?: string;
|
|
16
16
|
item?: string;
|
|
17
17
|
link?: string;
|
|
18
|
+
/** The app's mark. */
|
|
19
|
+
logo?: string;
|
|
20
|
+
/** Around the name and line, beside the mark. */
|
|
21
|
+
text?: string;
|
|
18
22
|
name?: string;
|
|
19
23
|
line?: string;
|
|
20
24
|
credit?: string;
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cx } from "./cx.js";
|
|
3
3
|
import { SIGN_IN_LINKS, SIGN_IN_LINKS_CREDIT } from "./signInTools.js";
|
|
4
4
|
export default function SignInLinks({ links = SIGN_IN_LINKS, lead = "New to Nostr? These two are all it takes.", credit = true, classNames: c = {}, }) {
|
|
5
|
-
return (_jsxs("nav", { "aria-label": "Tools for signing in", className: c.root, children: [lead && _jsx("p", { className: c.lead, children: lead }), _jsx("ul", { className: c.list, children: links.map((l) => (_jsx("li", { className: c.item, children: _jsxs("a", { href: l.href, target: "_blank", rel: "noopener noreferrer", className: cx(c.link), children: [_jsx("span", { className: c.name, children: l.name }), _jsx("span", { className: c.line, children: l.line })] }) }, l.id))) }), credit && _jsx("p", { className: c.credit, children: SIGN_IN_LINKS_CREDIT })] }));
|
|
5
|
+
return (_jsxs("nav", { "aria-label": "Tools for signing in", className: c.root, children: [lead && _jsx("p", { className: c.lead, children: lead }), _jsx("ul", { className: c.list, children: links.map((l) => (_jsx("li", { className: c.item, children: _jsxs("a", { href: l.href, target: "_blank", rel: "noopener noreferrer", className: cx(c.link), children: [l.logo && _jsx("img", { src: l.logo, alt: "", width: 40, height: 40, className: c.logo }), _jsxs("span", { className: c.text, children: [_jsx("span", { className: c.name, children: l.name }), _jsx("span", { className: c.line, children: l.line })] })] }) }, l.id))) }), credit && _jsx("p", { className: c.credit, children: SIGN_IN_LINKS_CREDIT })] }));
|
|
6
6
|
}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* The sequence under the sign-in card: what happens, who does it, and which
|
|
3
3
|
* step is under way now — read from the gate's real stage, never guessed.
|
|
4
4
|
*
|
|
5
|
-
* Mechanics only. It renders an ordered list
|
|
6
|
-
* (`done` / `current` / `todo`)
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Mechanics only. It renders an ordered list — meant to run left to right and
|
|
6
|
+
* wrap — with `data-state` on each step (`done` / `current` / `todo`),
|
|
7
|
+
* `aria-current="step"` on the ones under way, a glyph, the label, the actor
|
|
8
|
+
* as a caption, and a `connector` between tiles; every class is the site's
|
|
9
|
+
* through `classNames`. Without them the markup is plain and inherits from
|
|
10
|
+
* the page.
|
|
9
11
|
*/
|
|
10
12
|
import { type SignInActor, type SignInStage, type SignInStep } from "./signInFlow.ts";
|
|
11
13
|
export interface SignInStepsClassNames {
|
|
@@ -15,11 +17,13 @@ export interface SignInStepsClassNames {
|
|
|
15
17
|
done?: string;
|
|
16
18
|
current?: string;
|
|
17
19
|
todo?: string;
|
|
18
|
-
/** The
|
|
19
|
-
|
|
20
|
-
/** Who acts. */
|
|
21
|
-
actor?: string;
|
|
20
|
+
/** The emoji. */
|
|
21
|
+
glyph?: string;
|
|
22
22
|
label?: string;
|
|
23
|
+
/** Who acts, as a caption. */
|
|
24
|
+
actor?: string;
|
|
25
|
+
/** The mark between tiles (not after the last). */
|
|
26
|
+
connector?: string;
|
|
23
27
|
}
|
|
24
28
|
export interface SignInStepsProps {
|
|
25
29
|
stage: SignInStage;
|
|
@@ -3,16 +3,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* The sequence under the sign-in card: what happens, who does it, and which
|
|
4
4
|
* step is under way now — read from the gate's real stage, never guessed.
|
|
5
5
|
*
|
|
6
|
-
* Mechanics only. It renders an ordered list
|
|
7
|
-
* (`done` / `current` / `todo`)
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Mechanics only. It renders an ordered list — meant to run left to right and
|
|
7
|
+
* wrap — with `data-state` on each step (`done` / `current` / `todo`),
|
|
8
|
+
* `aria-current="step"` on the ones under way, a glyph, the label, the actor
|
|
9
|
+
* as a caption, and a `connector` between tiles; every class is the site's
|
|
10
|
+
* through `classNames`. Without them the markup is plain and inherits from
|
|
11
|
+
* the page.
|
|
10
12
|
*/
|
|
11
13
|
import { cx } from "./cx.js";
|
|
12
14
|
import { SIGN_IN_ACTORS, SIGN_IN_STEPS, stepState, } from "./signInFlow.js";
|
|
13
15
|
export default function SignInSteps({ stage, steps = SIGN_IN_STEPS, actors = SIGN_IN_ACTORS, label = "How signing in works", classNames: c = {}, }) {
|
|
14
16
|
return (_jsx("ol", { "aria-label": label, className: c.root, children: steps.map((s, i) => {
|
|
15
17
|
const state = stepState(s, stage);
|
|
16
|
-
return (_jsxs("li", { "data-state": state, "aria-current": state === "current" ? "step" : undefined, className: cx(c.step, c[state]), children: [_jsx("span", { "aria-hidden": true, className: c.
|
|
18
|
+
return (_jsxs("li", { "data-state": state, "aria-current": state === "current" ? "step" : undefined, className: cx(c.step, c[state]), children: [_jsx("span", { "aria-hidden": true, className: c.glyph, children: s.glyph }), _jsx("span", { className: c.label, children: s.label }), _jsx("span", { className: c.actor, children: actors[s.actor] }), i < steps.length - 1 && (_jsx("span", { "aria-hidden": true, className: c.connector, children: "\u203A" }))] }, s.id));
|
|
17
19
|
}) }));
|
|
18
20
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The room the DebugPanel keeps for itself.
|
|
2
|
+
* The room the DebugPanel keeps for itself — only while its log is open.
|
|
3
3
|
*
|
|
4
|
-
* The panel is fixed to the bottom of the viewport,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The panel is fixed to the bottom of the viewport. Collapsed, it is one small
|
|
5
|
+
* tab in the corner and overlays the page: an in-flow spacer for it stole a
|
|
6
|
+
* whole row from every screen (owner, 2026-09-27: "we want an overlay and not
|
|
7
|
+
* a row stealer"). Open, the log is tall and would hide whatever the page puts
|
|
8
|
+
* last — a Save button, a loading quote — so then, and only then, the panel
|
|
9
|
+
* renders an in-flow spacer as tall as it is, plus the iOS home-indicator
|
|
10
|
+
* inset, so the page can scroll its last line clear.
|
|
9
11
|
*/
|
|
10
12
|
/** The collapsed tab: one `min-h-10` (2.5rem) button. Used until measured. */
|
|
11
13
|
export declare const DEBUG_BAR_PX = 40;
|
|
@@ -13,3 +15,5 @@ export declare const DEBUG_BAR_PX = 40;
|
|
|
13
15
|
export declare function debugSpacerPx(measured: number | null | undefined): number;
|
|
14
16
|
/** The spacer's CSS height: the panel's height plus the safe-area inset. */
|
|
15
17
|
export declare function debugSpacerHeight(measured: number | null | undefined): string;
|
|
18
|
+
/** What the spacer takes: nothing while the panel is collapsed, its height while open. */
|
|
19
|
+
export declare function debugSpacerRoom(open: boolean, measured: number | null | undefined): string;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The room the DebugPanel keeps for itself.
|
|
2
|
+
* The room the DebugPanel keeps for itself — only while its log is open.
|
|
3
3
|
*
|
|
4
|
-
* The panel is fixed to the bottom of the viewport,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The panel is fixed to the bottom of the viewport. Collapsed, it is one small
|
|
5
|
+
* tab in the corner and overlays the page: an in-flow spacer for it stole a
|
|
6
|
+
* whole row from every screen (owner, 2026-09-27: "we want an overlay and not
|
|
7
|
+
* a row stealer"). Open, the log is tall and would hide whatever the page puts
|
|
8
|
+
* last — a Save button, a loading quote — so then, and only then, the panel
|
|
9
|
+
* renders an in-flow spacer as tall as it is, plus the iOS home-indicator
|
|
10
|
+
* inset, so the page can scroll its last line clear.
|
|
9
11
|
*/
|
|
10
12
|
/** The collapsed tab: one `min-h-10` (2.5rem) button. Used until measured. */
|
|
11
13
|
export const DEBUG_BAR_PX = 40;
|
|
@@ -19,3 +21,7 @@ export function debugSpacerPx(measured) {
|
|
|
19
21
|
export function debugSpacerHeight(measured) {
|
|
20
22
|
return `calc(${debugSpacerPx(measured)}px + env(safe-area-inset-bottom, 0px))`;
|
|
21
23
|
}
|
|
24
|
+
/** What the spacer takes: nothing while the panel is collapsed, its height while open. */
|
|
25
|
+
export function debugSpacerRoom(open, measured) {
|
|
26
|
+
return open ? debugSpacerHeight(measured) : "0px";
|
|
27
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The pure half of `SignInSteps` (named apart from it: the filesystem is case-blind): what happens when someone signs in, in the
|
|
3
3
|
* order it happens, and which step is under way for a given gate stage.
|
|
4
4
|
*
|
|
5
|
-
* Nine steps, four actors. The words are the visitor's, not the protocol's:
|
|
5
|
+
* Nine steps, four actors, one emoji each. The words are the visitor's, not the protocol's:
|
|
6
6
|
* "the operator" (never MCP), "a proof" (never dpop), "this page" (never FE).
|
|
7
7
|
* A site may pass its own list to `SignInSteps`; this one is the default.
|
|
8
8
|
*/
|
|
@@ -12,7 +12,9 @@ export type SignInActor = "you" | "page" | "operator" | "client";
|
|
|
12
12
|
export interface SignInStep {
|
|
13
13
|
id: string;
|
|
14
14
|
actor: SignInActor;
|
|
15
|
-
/**
|
|
15
|
+
/** One emoji that pictures the step. */
|
|
16
|
+
glyph: string;
|
|
17
|
+
/** Two or three words, on the tile. */
|
|
16
18
|
label: string;
|
|
17
19
|
/** The stage during which this step is the one under way. */
|
|
18
20
|
at: SignInStage;
|
package/dist/react/signInFlow.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The pure half of `SignInSteps` (named apart from it: the filesystem is case-blind): what happens when someone signs in, in the
|
|
3
3
|
* order it happens, and which step is under way for a given gate stage.
|
|
4
4
|
*
|
|
5
|
-
* Nine steps, four actors. The words are the visitor's, not the protocol's:
|
|
5
|
+
* Nine steps, four actors, one emoji each. The words are the visitor's, not the protocol's:
|
|
6
6
|
* "the operator" (never MCP), "a proof" (never dpop), "this page" (never FE).
|
|
7
7
|
* A site may pass its own list to `SignInSteps`; this one is the default.
|
|
8
8
|
*/
|
|
@@ -13,15 +13,15 @@ export const SIGN_IN_ACTORS = {
|
|
|
13
13
|
client: "Your Nostr client",
|
|
14
14
|
};
|
|
15
15
|
export const SIGN_IN_STEPS = [
|
|
16
|
-
{ id: "enter", actor: "you", label: "Enter your npub", at: "begin" },
|
|
17
|
-
{ id: "ask", actor: "page",
|
|
18
|
-
{ id: "message", actor: "operator",
|
|
19
|
-
{ id: "approve", actor: "you", label: "Approve
|
|
20
|
-
{ id: "reply", actor: "client", label: "
|
|
21
|
-
{ id: "verify", actor: "you", label: "Tap Verify", at: "awaiting" },
|
|
22
|
-
{ id: "check", actor: "operator",
|
|
23
|
-
{ id: "token", actor: "operator",
|
|
24
|
-
{ id: "unlock", actor: "page", label: "
|
|
16
|
+
{ id: "enter", actor: "you", glyph: "\u{1F511}", label: "Enter your npub", at: "begin" },
|
|
17
|
+
{ id: "ask", actor: "page", glyph: "\u{1F4E8}", label: "Ask for a proof", at: "sending" },
|
|
18
|
+
{ id: "message", actor: "operator", glyph: "\u{1F4AC}", label: "Message to your client", at: "awaiting" },
|
|
19
|
+
{ id: "approve", actor: "you", glyph: "\u2705", label: "Approve, for a while", at: "awaiting" },
|
|
20
|
+
{ id: "reply", actor: "client", glyph: "\u21A9\uFE0F", label: "Signed reply returns", at: "awaiting" },
|
|
21
|
+
{ id: "verify", actor: "you", glyph: "\u{1F446}", label: "Tap Verify", at: "awaiting" },
|
|
22
|
+
{ id: "check", actor: "operator", glyph: "\u{1F50D}", label: "Signature checked", at: "checking" },
|
|
23
|
+
{ id: "token", actor: "operator", glyph: "\u{1F39F}\uFE0F", label: "Proof handed over", at: "checking" },
|
|
24
|
+
{ id: "unlock", actor: "page", glyph: "\u{1F513}", label: "Unlocked", at: "done" },
|
|
25
25
|
];
|
|
26
26
|
const ORDER = ["begin", "sending", "awaiting", "checking", "done"];
|
|
27
27
|
/** Whether `step` is behind, under way, or ahead of `stage`. */
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* link stays right as its listings move. Pricing Studio's App Store page is
|
|
8
8
|
* public (id 6760925205). Apple's wording rule: an app is *available on the
|
|
9
9
|
* App Store*, never *at*.
|
|
10
|
+
*
|
|
11
|
+
* Each link carries its app's own mark as a 96 px PNG data URI, bundled so the
|
|
12
|
+
* sign-in page makes no third-party request: 0xchat's from its GitHub
|
|
13
|
+
* organisation, Pricing Studio's from its own asset catalog.
|
|
10
14
|
*/
|
|
11
15
|
export interface SignInLink {
|
|
12
16
|
id: string;
|
|
@@ -14,6 +18,8 @@ export interface SignInLink {
|
|
|
14
18
|
href: string;
|
|
15
19
|
/** One short line under the name. */
|
|
16
20
|
line: string;
|
|
21
|
+
/** The app's own mark, as a data URI (bundled, so no third-party request on sign-in). */
|
|
22
|
+
logo?: string;
|
|
17
23
|
}
|
|
18
24
|
export declare const SIGN_IN_LINKS: readonly SignInLink[];
|
|
19
25
|
/** Shown once, small, wherever the links are — unless the page already carries a legal footer. */
|
|
@@ -7,19 +7,27 @@
|
|
|
7
7
|
* link stays right as its listings move. Pricing Studio's App Store page is
|
|
8
8
|
* public (id 6760925205). Apple's wording rule: an app is *available on the
|
|
9
9
|
* App Store*, never *at*.
|
|
10
|
+
*
|
|
11
|
+
* Each link carries its app's own mark as a 96 px PNG data URI, bundled so the
|
|
12
|
+
* sign-in page makes no third-party request: 0xchat's from its GitHub
|
|
13
|
+
* organisation, Pricing Studio's from its own asset catalog.
|
|
10
14
|
*/
|
|
15
|
+
const OXCHAT_MARK = "data:image/png;base64,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";
|
|
16
|
+
const PRICING_STUDIO_MARK = "data:image/png;base64,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";
|
|
11
17
|
export const SIGN_IN_LINKS = [
|
|
12
18
|
{
|
|
13
19
|
id: "0xchat",
|
|
14
20
|
name: "0xchat",
|
|
15
21
|
href: "https://0xchat.com",
|
|
16
22
|
line: "A Nostr client for iPhone, Android and desktop. It holds your key and answers the sign-in message.",
|
|
23
|
+
logo: OXCHAT_MARK,
|
|
17
24
|
},
|
|
18
25
|
{
|
|
19
26
|
id: "pricing-studio",
|
|
20
27
|
name: "Pricing Studio",
|
|
21
28
|
href: "https://apps.apple.com/us/app/pricing-studio/id6760925205",
|
|
22
29
|
line: "Where operators price their tools, live. Available on the App Store.",
|
|
30
|
+
logo: PRICING_STUDIO_MARK,
|
|
23
31
|
},
|
|
24
32
|
];
|
|
25
33
|
/** Shown once, small, wherever the links are — unless the page already carries a legal footer. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tollbooth-dpyc/web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Shared browser SDK for Tollbooth DPYC operator front ends: npub sign-in, proofs, MCP client, profile and wallet",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Lonnie VanZandt",
|