@tollbooth-dpyc/web 1.6.0 → 1.7.0
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/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/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
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
|
}
|
|
@@ -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.0",
|
|
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",
|