failproofai 0.0.11-beta.8 → 0.0.11
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +6 -6
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +7 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js +2 -2
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[externals]__1_g_b3t._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0dwpg-h._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lnenda._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13i_sva._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_mqemn._.js +1 -1
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_audit_invite_route_actions_0-2n5sy.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_0-tu4ot._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_0ttxbz7._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_1bnh1y0._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_1epycqa._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_1wpdcgo._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +3 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1d4gx_t._.js → [root-of-the-server]__00uwqi6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0808sha._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1cd25c7._.js → [root-of-the-server]__0e4-6d8._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehe24g._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1-scthx._.js → [root-of-the-server]__0f62vu9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g253ve._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l13qf2._.js → [root-of-the-server]__0k65l27._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__15i0juc._.js → [root-of-the-server]__0kjb_s4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vxf0_g._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12mcauo._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0989_dx._.js → [root-of-the-server]__1e-x7j4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mt35_w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pcxxwg._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1uvfwgr._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0il3fl1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +49 -21
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0ihmbv4.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/static/chunks/{0d49wc5zca0u1.js → 02fywjt0by40a.js} +1 -1
- package/.next/standalone/.next/static/chunks/0f7d7hnbh4djs.js +1 -0
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +1 -0
- package/.next/standalone/.next/static/chunks/0xdx2ehtbdoeg.js +1 -0
- package/.next/standalone/.next/static/chunks/{1nd1e30h8s_mc.js → 1-a5rvq67k7ed.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1m2yj97j7f_km.js → 15csyj1_rf0-w.js} +1 -1
- package/.next/standalone/.next/static/chunks/1o0xa47736gi9.css +2 -0
- package/.next/standalone/.next/static/chunks/24cv31x607n7k.js +1 -0
- package/.next/standalone/.next/static/chunks/{3w8d8k_dca5rp.js → 2h0dkzyy0vocp.js} +1 -1
- package/.next/standalone/.next/static/chunks/2n_s8v1ae38_a.js +69 -0
- package/.next/standalone/.next/static/chunks/2y-jmvrjxz60x.js +6 -0
- package/.next/standalone/.next/static/chunks/{24z-bgbisv379.js → 3eik_d9qrvoft.js} +1 -1
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +1 -0
- package/.next/standalone/.next/static/chunks/{0j969hb6nujdf.js → 3v61675vr6jav.js} +1 -1
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-00qy7zfa7m--m.js → turbopack-3lrm4f20fz89b.js} +1 -1
- package/.next/standalone/app/api/audit/invite/route.ts +192 -0
- package/.next/standalone/app/api/audit/run/route.ts +35 -0
- package/.next/standalone/app/api/auth/login-request/route.ts +2 -2
- package/.next/standalone/app/api/auth/login-verify/route.ts +10 -2
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +39 -63
- package/.next/standalone/app/audit/_components/audit-poster.tsx +326 -0
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +23 -49
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +336 -0
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +187 -0
- package/.next/standalone/app/audit/_components/invite-dialog.tsx +230 -0
- package/.next/standalone/app/audit/_components/quirks-section.tsx +75 -0
- package/.next/standalone/app/audit/_components/share-templates.ts +63 -32
- package/.next/standalone/app/audit/_components/sigil.tsx +9 -66
- package/.next/standalone/app/audit/_components/strengths-section.tsx +20 -32
- package/.next/standalone/app/audit/audit-styles.css +778 -1786
- package/.next/standalone/app/components/sessions-list.tsx +77 -80
- package/.next/standalone/app/globals.css +241 -34
- package/.next/standalone/app/layout.tsx +1 -10
- package/.next/standalone/app/policies/hooks-client.tsx +45 -28
- package/.next/standalone/app/project/[name]/page.tsx +23 -79
- package/.next/standalone/app/projects/page.tsx +14 -23
- package/.next/standalone/assets/audit/poster-styles.css +1 -1
- package/.next/standalone/assets/audit/styles.css +11 -11
- package/.next/standalone/components/navbar.tsx +2 -37
- package/.next/standalone/components/reach-developers.tsx +10 -25
- package/.next/standalone/lib/auth/api-server-client.ts +28 -0
- package/.next/standalone/lib/client-telemetry.ts +4 -0
- package/.next/standalone/node_modules/@next/env/package.json +2 -2
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +15 -15
- package/.next/standalone/package.json +19 -14
- package/.next/standalone/server.js +1 -1
- package/README.md +2 -2
- package/bin/failproofai.mjs +24 -5
- package/dist/cli.mjs +2328 -381
- package/lib/auth/api-server-client.ts +28 -0
- package/lib/client-telemetry.ts +4 -0
- package/package.json +19 -14
- package/scripts/launch.ts +30 -4
- package/scripts/postinstall.mjs +10 -1
- package/scripts/skew-log-filter.ts +46 -0
- package/scripts/validate-mdx.ts +139 -0
- package/src/audit/cli.ts +330 -0
- package/src/audit/open-browser.ts +69 -0
- package/src/audit/social-proof.ts +34 -0
- package/src/auth/cli.ts +16 -13
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07tgnzi._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oeun7z._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12pit4m._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13ra2jq._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1b9z5-i._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ixjiy8._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1-1804f._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00jkjmt._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013du6r._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e85wxv._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0gfxvb1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1hlrq6y._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ihxdo5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1vvfde2._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_1k58rb_.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_1n-0xws.js +0 -3
- package/.next/standalone/.next/static/chunks/09ueq8s1as8xs.css +0 -2
- package/.next/standalone/.next/static/chunks/0qassxjx1ef04.js +0 -1
- package/.next/standalone/.next/static/chunks/0qxb5czqxe-vu.js +0 -1
- package/.next/standalone/.next/static/chunks/1dh06515j265n.js +0 -41
- package/.next/standalone/.next/static/chunks/29gs4efgi3hme.js +0 -6
- package/.next/standalone/.next/static/chunks/2mni177pnjx6u.js +0 -1
- package/.next/standalone/.next/static/chunks/2so39wg7mjbi7.js +0 -1
- package/.next/standalone/.next/static/chunks/3gti1qdk5epqn.js +0 -1
- package/.next/standalone/.next/static/chunks/3wycox197ouus.css +0 -1
- package/.next/standalone/app/audit/_components/findings-section.tsx +0 -135
- package/.next/standalone/app/audit/_components/identity-section.tsx +0 -126
- package/.next/standalone/app/audit/_components/policies-section.tsx +0 -194
- package/.next/standalone/app/audit/_components/return-section.tsx +0 -416
- package/.next/standalone/app/audit/_components/score-section.tsx +0 -179
- package/.next/standalone/app/audit/_components/share-dock.tsx +0 -265
- package/.next/standalone/app/audit/_components/show-off-cta.tsx +0 -135
- /package/.next/standalone/.next/static/{CVv2A0hMd24t0c0x3V-W_ → P_MIRSeoE296wkbE-Icin}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{CVv2A0hMd24t0c0x3V-W_ → P_MIRSeoE296wkbE-Icin}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{CVv2A0hMd24t0c0x3V-W_ → P_MIRSeoE296wkbE-Icin}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Section 01 — AUDIT POSTER. The single-screen shareable above-the-fold
|
|
5
|
+
* artifact. Replaces the old IdentitySection + ScoreSection + ShareDock
|
|
6
|
+
* triumvirate.
|
|
7
|
+
*
|
|
8
|
+
* Layout (inside the PNG capture region):
|
|
9
|
+
*
|
|
10
|
+
* ┌─────────────────────────────────────────────────────────────┐
|
|
11
|
+
* │ ▣ failproof_ai · audit № 01 of 08 · audited <date> │
|
|
12
|
+
* ├─────────────────────────────────────────────────────────────┤
|
|
13
|
+
* │ │
|
|
14
|
+
* │ <score>/100 the optimist ▓░▓░▓░▓░ │
|
|
15
|
+
* │ <rank> pace · conviction · forgetful ░▓░▓░▓░▓ │
|
|
16
|
+
* │ // only N% of agents are this archetype │
|
|
17
|
+
* │ │
|
|
18
|
+
* ├─────────────────────────────────────────────────────────────┤
|
|
19
|
+
* │ audit yours → failproof.ai │
|
|
20
|
+
* └─────────────────────────────────────────────────────────────┘
|
|
21
|
+
*
|
|
22
|
+
* Outside the capture region: three share buttons + scroll hint.
|
|
23
|
+
*/
|
|
24
|
+
import React, { forwardRef, useMemo, useState } from "react";
|
|
25
|
+
import { pickArchetypeVariant, type ArchetypeKey } from "@/src/audit/archetypes";
|
|
26
|
+
import { type Grade } from "@/src/audit/scoring";
|
|
27
|
+
import { getArchetypeRarityPct } from "@/src/audit/social-proof";
|
|
28
|
+
import { copyOrDownloadCard, downloadCard, shareCardNative, shareCardToastMessage } from "@/lib/share-card";
|
|
29
|
+
import { toast } from "@/app/components/toast";
|
|
30
|
+
import { usePostHog } from "@/contexts/PostHogContext";
|
|
31
|
+
import { Sigil } from "./sigil";
|
|
32
|
+
import { X_TEMPLATES, LI_TEMPLATES, pickTemplate, type ShareCtx } from "./share-templates";
|
|
33
|
+
|
|
34
|
+
const X_INTENT = (text: string) =>
|
|
35
|
+
`https://x.com/intent/tweet?text=${encodeURIComponent(text)}`;
|
|
36
|
+
// LinkedIn deprecated the share-offsite `summary` / `title` params, so they no
|
|
37
|
+
// longer pre-fill the post composer (it only scrapes OG tags from the URL). The
|
|
38
|
+
// feed route with `shareActive=true` + `text=` opens "start a post" with our
|
|
39
|
+
// text pre-filled — and the share templates already embed the befailproof.ai
|
|
40
|
+
// link inside that text.
|
|
41
|
+
const LI_INTENT = (text: string) =>
|
|
42
|
+
`https://www.linkedin.com/feed/?shareActive=true&text=${encodeURIComponent(text)}`;
|
|
43
|
+
|
|
44
|
+
interface Props {
|
|
45
|
+
archetypeKey: ArchetypeKey;
|
|
46
|
+
/** Stable seed for variant selection (project name is the natural fit). */
|
|
47
|
+
seed: string;
|
|
48
|
+
score: number;
|
|
49
|
+
grade: Grade;
|
|
50
|
+
/** Count of unenabled prescribed policies — passed to the share-text
|
|
51
|
+
* templates, not rendered on the poster itself. */
|
|
52
|
+
missing: number;
|
|
53
|
+
/** Audit timestamp (ISO string from cache). Rendered as ISO date in the
|
|
54
|
+
* top-right meta. UTC to keep the poster timezone-stable across shares. */
|
|
55
|
+
auditedAt: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoster(
|
|
59
|
+
{ archetypeKey, seed, score, grade, missing, auditedAt }: Props,
|
|
60
|
+
posterRef,
|
|
61
|
+
) {
|
|
62
|
+
const archetype = useMemo(
|
|
63
|
+
() => pickArchetypeVariant(archetypeKey, seed),
|
|
64
|
+
[archetypeKey, seed],
|
|
65
|
+
);
|
|
66
|
+
const rarityPct = getArchetypeRarityPct(archetypeKey);
|
|
67
|
+
const indexLabel = String(archetype.index).padStart(2, "0");
|
|
68
|
+
const auditedDate = useMemo(() => formatAuditedDate(auditedAt), [auditedAt]);
|
|
69
|
+
|
|
70
|
+
const { capture } = usePostHog();
|
|
71
|
+
const [busy, setBusy] = useState<null | "x" | "linkedin" | "download">(null);
|
|
72
|
+
|
|
73
|
+
const captureCardBlob = async (): Promise<Blob | null> => {
|
|
74
|
+
const node = (posterRef as React.MutableRefObject<HTMLDivElement | null>)?.current;
|
|
75
|
+
if (!node) return null;
|
|
76
|
+
// Capture via html-to-image instead of html2canvas. The former
|
|
77
|
+
// serializes the DOM into an SVG <foreignObject> and rasterizes
|
|
78
|
+
// it through the browser's native rendering engine — so dashed
|
|
79
|
+
// borders, the SVG logo, gradients, and font metrics render
|
|
80
|
+
// exactly as they do on screen. html2canvas reimplements CSS
|
|
81
|
+
// in JS and was producing broken dashes and a stray pink square
|
|
82
|
+
// on the logo's mask.
|
|
83
|
+
if (typeof document !== "undefined" && document.fonts?.ready) await document.fonts.ready;
|
|
84
|
+
await new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
85
|
+
// Clone the poster into an off-screen container with a fixed width
|
|
86
|
+
// so html-to-image captures a self-contained subtree. The live
|
|
87
|
+
// .poster sits inside a flex column (.poster-section's flex: 1)
|
|
88
|
+
// and uses margin: 0 auto for centering — capturing it directly
|
|
89
|
+
// inherits that parent context, which shifts content within an
|
|
90
|
+
// oversized canvas. The clone has no such context.
|
|
91
|
+
const liveRect = node.getBoundingClientRect();
|
|
92
|
+
const captureWidth = Math.round(liveRect.width);
|
|
93
|
+
const captureHeight = Math.round(liveRect.height);
|
|
94
|
+
|
|
95
|
+
const wrapper = document.createElement("div");
|
|
96
|
+
wrapper.style.cssText = [
|
|
97
|
+
"position: fixed",
|
|
98
|
+
"left: -10000px",
|
|
99
|
+
"top: 0",
|
|
100
|
+
`width: ${captureWidth}px`,
|
|
101
|
+
`height: ${captureHeight}px`,
|
|
102
|
+
"padding: 0",
|
|
103
|
+
"margin: 0",
|
|
104
|
+
"background: var(--bg)",
|
|
105
|
+
"z-index: -1",
|
|
106
|
+
"pointer-events: none",
|
|
107
|
+
].join(";");
|
|
108
|
+
|
|
109
|
+
const clone = node.cloneNode(true) as HTMLElement;
|
|
110
|
+
clone.style.cssText += [
|
|
111
|
+
"",
|
|
112
|
+
`width: ${captureWidth}px`,
|
|
113
|
+
`height: ${captureHeight}px`,
|
|
114
|
+
"max-width: none",
|
|
115
|
+
"min-width: 0",
|
|
116
|
+
"flex: 0 0 auto",
|
|
117
|
+
"margin: 0",
|
|
118
|
+
].join(";");
|
|
119
|
+
|
|
120
|
+
wrapper.appendChild(clone);
|
|
121
|
+
document.body.appendChild(wrapper);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const { toBlob } = await import("html-to-image");
|
|
125
|
+
return await toBlob(clone, {
|
|
126
|
+
backgroundColor: "#0e0e11",
|
|
127
|
+
pixelRatio: 2,
|
|
128
|
+
cacheBust: true,
|
|
129
|
+
width: captureWidth,
|
|
130
|
+
height: captureHeight,
|
|
131
|
+
});
|
|
132
|
+
} finally {
|
|
133
|
+
wrapper.remove();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const filenameFor = (channel: "x" | "linkedin" | "download") =>
|
|
138
|
+
`failproofai-${channel}-${grade.toLowerCase()}-${score}.png`;
|
|
139
|
+
|
|
140
|
+
const handleShare = async (channel: "x" | "linkedin" | "download") => {
|
|
141
|
+
if (busy) return;
|
|
142
|
+
setBusy(channel);
|
|
143
|
+
capture("audit_card_share_clicked", {
|
|
144
|
+
channel,
|
|
145
|
+
source: "poster",
|
|
146
|
+
score,
|
|
147
|
+
grade,
|
|
148
|
+
missing_policies: missing,
|
|
149
|
+
});
|
|
150
|
+
try {
|
|
151
|
+
const blob = await captureCardBlob().catch(() => null);
|
|
152
|
+
if (!blob) {
|
|
153
|
+
capture("audit_card_capture_completed", {
|
|
154
|
+
trigger: channel === "download" ? "download" : `share_${channel}`,
|
|
155
|
+
status: "error",
|
|
156
|
+
image_method: "failed",
|
|
157
|
+
source: "poster",
|
|
158
|
+
});
|
|
159
|
+
toast(shareCardToastMessage("failed"));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (channel === "download") {
|
|
164
|
+
const ok = downloadCard(blob, filenameFor(channel));
|
|
165
|
+
const method = ok ? "download" : "failed";
|
|
166
|
+
capture("audit_card_capture_completed", {
|
|
167
|
+
trigger: "download",
|
|
168
|
+
status: ok ? "success" : "error",
|
|
169
|
+
image_method: method,
|
|
170
|
+
source: "poster",
|
|
171
|
+
});
|
|
172
|
+
toast(shareCardToastMessage(method));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const shareCtx: ShareCtx = {
|
|
177
|
+
score,
|
|
178
|
+
arch: archetype.name.toLowerCase(),
|
|
179
|
+
grade,
|
|
180
|
+
missing,
|
|
181
|
+
};
|
|
182
|
+
const shareText = channel === "x"
|
|
183
|
+
? pickTemplate(X_TEMPLATES, seed, shareCtx)
|
|
184
|
+
: pickTemplate(LI_TEMPLATES, seed, shareCtx);
|
|
185
|
+
const native = await shareCardNative(blob, filenameFor(channel), shareText);
|
|
186
|
+
if (native) {
|
|
187
|
+
capture("audit_card_capture_completed", {
|
|
188
|
+
trigger: `share_${channel}`,
|
|
189
|
+
status: "success",
|
|
190
|
+
image_method: "native",
|
|
191
|
+
source: "poster",
|
|
192
|
+
});
|
|
193
|
+
toast(shareCardToastMessage("native"));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const fallbackMethod = await copyOrDownloadCard(blob, filenameFor(channel));
|
|
198
|
+
capture("audit_card_capture_completed", {
|
|
199
|
+
trigger: `share_${channel}`,
|
|
200
|
+
status: fallbackMethod === "failed" ? "error" : "success",
|
|
201
|
+
image_method: fallbackMethod,
|
|
202
|
+
source: "poster",
|
|
203
|
+
});
|
|
204
|
+
toast(shareCardToastMessage(fallbackMethod));
|
|
205
|
+
const intent = channel === "x" ? X_INTENT(shareText) : LI_INTENT(shareText);
|
|
206
|
+
globalThis.open(intent, "_blank", "noopener,noreferrer");
|
|
207
|
+
} finally {
|
|
208
|
+
setBusy(null);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<section className="poster-section" data-screen-label="01 Poster">
|
|
214
|
+
<div className="poster" ref={posterRef}>
|
|
215
|
+
<header className="poster-head">
|
|
216
|
+
<span className="poster-wordmark">
|
|
217
|
+
<img
|
|
218
|
+
src="/logo.svg"
|
|
219
|
+
alt="failproof_ai"
|
|
220
|
+
className="poster-logo"
|
|
221
|
+
/>
|
|
222
|
+
<span className="poster-sep">·</span> audit
|
|
223
|
+
</span>
|
|
224
|
+
<span className="poster-meta">
|
|
225
|
+
<span className="poster-ix">№ {indexLabel}</span>
|
|
226
|
+
<span className="of"> of 08</span>
|
|
227
|
+
<span className="poster-sep"> · </span>
|
|
228
|
+
audited {auditedDate}
|
|
229
|
+
</span>
|
|
230
|
+
</header>
|
|
231
|
+
|
|
232
|
+
<div className="poster-body">
|
|
233
|
+
{/* Sigil — visual anchor at the top of the centered stack */}
|
|
234
|
+
<div className="poster-sigil">
|
|
235
|
+
<Sigil archetypeKey={archetypeKey} />
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
{/* Persona block — name + keywords + rarity, centered */}
|
|
239
|
+
<div className="poster-persona">
|
|
240
|
+
<h1 className="persona-name">{archetype.name}</h1>
|
|
241
|
+
<div className="persona-keywords">
|
|
242
|
+
{archetype.keywords.map((k, i) => (
|
|
243
|
+
<React.Fragment key={k}>
|
|
244
|
+
<span className={`kw kw-${i}`}>{k}</span>
|
|
245
|
+
{i < archetype.keywords.length - 1 && (
|
|
246
|
+
<span className="kw-sep">·</span>
|
|
247
|
+
)}
|
|
248
|
+
</React.Fragment>
|
|
249
|
+
))}
|
|
250
|
+
</div>
|
|
251
|
+
{typeof rarityPct === "number" && (
|
|
252
|
+
<div className="persona-rarity">
|
|
253
|
+
<span className="lbl">{"// only"}</span>{" "}
|
|
254
|
+
<span className="pct">{rarityPct}%</span>{" "}
|
|
255
|
+
<span className="lbl">of agents are this archetype</span>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
{/* Score block — heroic number, centered in the card */}
|
|
261
|
+
<div className="poster-score">
|
|
262
|
+
<span className="score-n">{score}</span>
|
|
263
|
+
<span className="score-of">/100</span>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<footer className="poster-foot">
|
|
268
|
+
<span className="poster-brand">befailproof.ai</span>
|
|
269
|
+
<span className="poster-cta">
|
|
270
|
+
audit yours <span className="arrow">→</span>{" "}
|
|
271
|
+
<span className="cta-cmd">npx -y failproofai audit</span>
|
|
272
|
+
</span>
|
|
273
|
+
</footer>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div className="poster-share-row">
|
|
277
|
+
<button
|
|
278
|
+
type="button"
|
|
279
|
+
className="poster-share-btn"
|
|
280
|
+
onClick={() => handleShare("x")}
|
|
281
|
+
disabled={busy !== null}
|
|
282
|
+
>
|
|
283
|
+
<span className="mark" aria-hidden="true">𝕏</span>
|
|
284
|
+
{busy === "x" ? "rendering…" : "post your archetype"}
|
|
285
|
+
</button>
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
className="poster-share-btn"
|
|
289
|
+
onClick={() => handleShare("linkedin")}
|
|
290
|
+
disabled={busy !== null}
|
|
291
|
+
>
|
|
292
|
+
<span className="mark" aria-hidden="true">in</span>
|
|
293
|
+
{busy === "linkedin" ? "rendering…" : "share on linkedin"}
|
|
294
|
+
</button>
|
|
295
|
+
<button
|
|
296
|
+
type="button"
|
|
297
|
+
className="poster-share-btn"
|
|
298
|
+
onClick={() => handleShare("download")}
|
|
299
|
+
disabled={busy !== null}
|
|
300
|
+
>
|
|
301
|
+
<span className="mark" aria-hidden="true">↓</span>
|
|
302
|
+
{busy === "download" ? "rendering…" : "download poster"}
|
|
303
|
+
</button>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div className="poster-scroll-hint" aria-hidden="true">
|
|
307
|
+
scroll for full report <span className="arrow">↓</span>
|
|
308
|
+
</div>
|
|
309
|
+
</section>
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
/** UTC ISO date (YYYY-MM-DD) so the poster's date stays timezone-stable
|
|
314
|
+
* across the geographies it gets shared into. */
|
|
315
|
+
function formatAuditedDate(iso: string): string {
|
|
316
|
+
try {
|
|
317
|
+
const d = new Date(iso);
|
|
318
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
319
|
+
const y = d.getUTCFullYear();
|
|
320
|
+
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
321
|
+
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
322
|
+
return `${y}-${m}-${day}`;
|
|
323
|
+
} catch {
|
|
324
|
+
return iso;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Auth dialog — modal overlay shown when an unauthenticated user clicks
|
|
5
|
-
*
|
|
5
|
+
* a cadence button to set a reminder. Two-step flow:
|
|
6
6
|
*
|
|
7
7
|
* 1. Email entry → POST /api/auth/login-request
|
|
8
8
|
* 2. OTP entry → POST /api/auth/login-verify
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the dashboard's API route writes it to
|
|
10
|
+
* Calm chrome to match the rest of /audit: 1px borders, plain
|
|
11
|
+
* lowercase copy, no corner crosshair frame. The dialog never sees
|
|
12
|
+
* the refresh token — the dashboard's API route writes it to
|
|
13
|
+
* ~/.failproofai/auth.json.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -23,15 +24,15 @@ export interface AuthedUser {
|
|
|
23
24
|
|
|
24
25
|
interface Props {
|
|
25
26
|
open: boolean;
|
|
26
|
-
/**
|
|
27
|
+
/** Optional title. Defaults to "where to route the reminder?". */
|
|
27
28
|
headline?: string;
|
|
28
|
-
/**
|
|
29
|
-
|
|
29
|
+
/** Optional subtitle shown under the title on the email step. Defaults to
|
|
30
|
+
* "we'll send a one-time code to confirm." */
|
|
31
|
+
subhead?: string;
|
|
30
32
|
onClose: () => void;
|
|
31
33
|
/** Fired after successful verify. Caller decides what to do next. */
|
|
32
34
|
onAuthed: (user: AuthedUser) => void;
|
|
33
|
-
/** Telemetry tag identifying which CTA opened the dialog.
|
|
34
|
-
* "unknown" so existing call sites continue to compile. */
|
|
35
|
+
/** Telemetry tag identifying which CTA opened the dialog. */
|
|
35
36
|
source?: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -52,8 +53,8 @@ function describeFetchError(err: unknown): string {
|
|
|
52
53
|
|
|
53
54
|
export function AuthDialog({
|
|
54
55
|
open,
|
|
55
|
-
headline = "
|
|
56
|
-
|
|
56
|
+
headline = "where to route the reminder?",
|
|
57
|
+
subhead = "we'll send a one-time code to confirm.",
|
|
57
58
|
onClose,
|
|
58
59
|
onAuthed,
|
|
59
60
|
source = "unknown",
|
|
@@ -65,8 +66,6 @@ export function AuthDialog({
|
|
|
65
66
|
const emailInputRef = useRef<HTMLInputElement | null>(null);
|
|
66
67
|
const codeInputRef = useRef<HTMLInputElement | null>(null);
|
|
67
68
|
|
|
68
|
-
// Reset internal state every time the dialog opens. Also fire the
|
|
69
|
-
// funnel-opened event so we can measure dismissal vs verification rates.
|
|
70
69
|
useEffect(() => {
|
|
71
70
|
if (open) {
|
|
72
71
|
setStep({ kind: "email", error: null });
|
|
@@ -76,9 +75,6 @@ export function AuthDialog({
|
|
|
76
75
|
}
|
|
77
76
|
}, [capture, open, source]);
|
|
78
77
|
|
|
79
|
-
// Fire dismissed when the dialog closes WITHOUT a successful verify.
|
|
80
|
-
// We piggyback on `open` flipping false instead of intercepting every
|
|
81
|
-
// close path so resend / step transitions don't double-count.
|
|
82
78
|
const wasOpenRef = useRef(false);
|
|
83
79
|
useEffect(() => {
|
|
84
80
|
if (open) {
|
|
@@ -94,7 +90,6 @@ export function AuthDialog({
|
|
|
94
90
|
wasOpenRef.current = false;
|
|
95
91
|
}, [capture, open, source, step.kind]);
|
|
96
92
|
|
|
97
|
-
// Autofocus the right input as the step changes.
|
|
98
93
|
useEffect(() => {
|
|
99
94
|
if (!open) return;
|
|
100
95
|
const t = setTimeout(() => {
|
|
@@ -104,7 +99,6 @@ export function AuthDialog({
|
|
|
104
99
|
return () => clearTimeout(t);
|
|
105
100
|
}, [open, step.kind]);
|
|
106
101
|
|
|
107
|
-
// ESC to close.
|
|
108
102
|
useEffect(() => {
|
|
109
103
|
if (!open) return;
|
|
110
104
|
const onKey = (e: KeyboardEvent): void => {
|
|
@@ -114,7 +108,6 @@ export function AuthDialog({
|
|
|
114
108
|
return () => window.removeEventListener("keydown", onKey);
|
|
115
109
|
}, [open, busy, onClose]);
|
|
116
110
|
|
|
117
|
-
// Resend countdown ticker.
|
|
118
111
|
const resendActive = step.kind === "code" && step.resendIn > 0;
|
|
119
112
|
useEffect(() => {
|
|
120
113
|
if (!resendActive) return;
|
|
@@ -129,9 +122,6 @@ export function AuthDialog({
|
|
|
129
122
|
const requestCode = useCallback(
|
|
130
123
|
async (email: string, opts: { isResend?: boolean } = {}): Promise<void> => {
|
|
131
124
|
const { isResend = false } = opts;
|
|
132
|
-
// Show resend errors inline on the OTP step — the previously sent
|
|
133
|
-
// code is still usable. Only the first-send error path bounces back
|
|
134
|
-
// to the email step.
|
|
135
125
|
const setError = (msg: string) => {
|
|
136
126
|
if (isResend) {
|
|
137
127
|
setStep((s) => (s.kind === "code" ? { ...s, error: msg } : s));
|
|
@@ -268,11 +258,6 @@ export function AuthDialog({
|
|
|
268
258
|
}}
|
|
269
259
|
>
|
|
270
260
|
<div className="auth-dialog">
|
|
271
|
-
<span className="corner tl">┌</span>
|
|
272
|
-
<span className="corner tr">┐</span>
|
|
273
|
-
<span className="corner bl">└</span>
|
|
274
|
-
<span className="corner br">┘</span>
|
|
275
|
-
|
|
276
261
|
<button
|
|
277
262
|
type="button"
|
|
278
263
|
className="auth-close"
|
|
@@ -280,21 +265,17 @@ export function AuthDialog({
|
|
|
280
265
|
disabled={busy}
|
|
281
266
|
aria-label="close"
|
|
282
267
|
>
|
|
283
|
-
|
|
268
|
+
×
|
|
284
269
|
</button>
|
|
285
270
|
|
|
286
|
-
<div className="auth-label">━━ identity check</div>
|
|
287
271
|
<h2 id="auth-dialog-title" className="auth-headline">
|
|
288
272
|
{headline}
|
|
289
273
|
</h2>
|
|
290
274
|
|
|
291
275
|
{step.kind === "email" && (
|
|
292
276
|
<>
|
|
293
|
-
<p className="auth-sub">{
|
|
277
|
+
<p className="auth-sub">{subhead}</p>
|
|
294
278
|
<form onSubmit={onEmailSubmit} className="auth-form">
|
|
295
|
-
<label className="auth-field-label" htmlFor="auth-dialog-email">
|
|
296
|
-
email
|
|
297
|
-
</label>
|
|
298
279
|
<input
|
|
299
280
|
ref={emailInputRef}
|
|
300
281
|
id="auth-dialog-email"
|
|
@@ -311,7 +292,7 @@ export function AuthDialog({
|
|
|
311
292
|
{step.error && <div className="auth-error">{step.error}</div>}
|
|
312
293
|
<div className="auth-actions">
|
|
313
294
|
<button type="submit" className="auth-btn primary" disabled={busy}>
|
|
314
|
-
{busy ? "
|
|
295
|
+
{busy ? "sending…" : "send code"}
|
|
315
296
|
</button>
|
|
316
297
|
<button
|
|
317
298
|
type="button"
|
|
@@ -319,7 +300,7 @@ export function AuthDialog({
|
|
|
319
300
|
onClick={onClose}
|
|
320
301
|
disabled={busy}
|
|
321
302
|
>
|
|
322
|
-
|
|
303
|
+
cancel
|
|
323
304
|
</button>
|
|
324
305
|
</div>
|
|
325
306
|
</form>
|
|
@@ -329,14 +310,10 @@ export function AuthDialog({
|
|
|
329
310
|
{step.kind === "code" && (
|
|
330
311
|
<>
|
|
331
312
|
<p className="auth-sub">
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
check your inbox — it expires in {Math.ceil(step.expiresIn / 60)} min.
|
|
313
|
+
code sent to <span className="auth-email">{step.email}</span>.
|
|
314
|
+
expires in {Math.ceil(step.expiresIn / 60)} min.
|
|
335
315
|
</p>
|
|
336
316
|
<form onSubmit={onCodeSubmit} className="auth-form">
|
|
337
|
-
<label className="auth-field-label" htmlFor="auth-dialog-code">
|
|
338
|
-
one-time code
|
|
339
|
-
</label>
|
|
340
317
|
<input
|
|
341
318
|
ref={codeInputRef}
|
|
342
319
|
id="auth-dialog-code"
|
|
@@ -354,7 +331,7 @@ export function AuthDialog({
|
|
|
354
331
|
{step.error && <div className="auth-error">{step.error}</div>}
|
|
355
332
|
<div className="auth-actions">
|
|
356
333
|
<button type="submit" className="auth-btn primary" disabled={busy}>
|
|
357
|
-
{busy ? "
|
|
334
|
+
{busy ? "verifying…" : "verify"}
|
|
358
335
|
</button>
|
|
359
336
|
<button
|
|
360
337
|
type="button"
|
|
@@ -363,8 +340,8 @@ export function AuthDialog({
|
|
|
363
340
|
disabled={busy || step.resendIn > 0}
|
|
364
341
|
>
|
|
365
342
|
{step.resendIn > 0
|
|
366
|
-
? `
|
|
367
|
-
: "
|
|
343
|
+
? `resend in ${step.resendIn}s`
|
|
344
|
+
: "resend code"}
|
|
368
345
|
</button>
|
|
369
346
|
</div>
|
|
370
347
|
<button
|
|
@@ -382,15 +359,12 @@ export function AuthDialog({
|
|
|
382
359
|
{step.kind === "done" && (
|
|
383
360
|
<>
|
|
384
361
|
<p className="auth-sub">
|
|
385
|
-
<span className="auth-ok">✓</span>
|
|
362
|
+
<span className="auth-ok">✓</span> signed in as{" "}
|
|
386
363
|
<span className="auth-email">{step.user.email}</span>.
|
|
387
364
|
</p>
|
|
388
|
-
<p className="auth-sub" style={{ marginTop: 8 }}>
|
|
389
|
-
session saved locally.
|
|
390
|
-
</p>
|
|
391
365
|
<div className="auth-actions">
|
|
392
366
|
<button type="button" className="auth-btn primary" onClick={onClose}>
|
|
393
|
-
|
|
367
|
+
continue
|
|
394
368
|
</button>
|
|
395
369
|
</div>
|
|
396
370
|
</>
|