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
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Floating share dock — always-visible bottom-right panel on the /audit
|
|
5
|
-
* dashboard. Three buttons (X, LinkedIn, download) that all share the
|
|
6
|
-
* `.share-btn` styling used by the inline buttons in `identity-section`,
|
|
7
|
-
* so the visual rhythm carries between surfaces.
|
|
8
|
-
*
|
|
9
|
-
* Capture flow:
|
|
10
|
-
* 1. Dock click → ref'd `.archetype-frame` is captured to a PNG blob
|
|
11
|
-
* via html2canvas.
|
|
12
|
-
* 2. `copyOrDownloadCard` writes the blob to the clipboard if the
|
|
13
|
-
* browser supports it, falling back to a download. The user gets a
|
|
14
|
-
* toast telling them what to do next.
|
|
15
|
-
* 3. For shares, the X / LinkedIn intent window opens with the
|
|
16
|
-
* templated text.
|
|
17
|
-
*
|
|
18
|
-
* UX:
|
|
19
|
-
* - Collapses to a single 48px pink FAB via a header caret. Preference
|
|
20
|
-
* persists across page navigations within a session.
|
|
21
|
-
* - Slide-in animation on mount (respects prefers-reduced-motion).
|
|
22
|
-
* - Hidden on viewports < 760px (mobile) — the inline buttons are
|
|
23
|
-
* already visible there and a floating dock would cover content.
|
|
24
|
-
* - Hidden until the archetype hero has actually mounted (frameRef
|
|
25
|
-
* resolves to a node). On the empty / running states we render
|
|
26
|
-
* nothing.
|
|
27
|
-
*/
|
|
28
|
-
import React, { useEffect, useState } from "react";
|
|
29
|
-
import { type ArchetypeKey, pickArchetypeVariant } from "@/src/audit/archetypes";
|
|
30
|
-
import { type Grade } from "@/src/audit/scoring";
|
|
31
|
-
import { copyOrDownloadCard, downloadCard, shareCardNative, shareCardToastMessage } from "@/lib/share-card";
|
|
32
|
-
import { toast } from "@/app/components/toast";
|
|
33
|
-
import { usePostHog } from "@/contexts/PostHogContext";
|
|
34
|
-
import { X_TEMPLATES, LI_TEMPLATES, pickTemplate, type ShareCtx } from "./share-templates";
|
|
35
|
-
|
|
36
|
-
const SITE_URL = "https://befailproof.ai";
|
|
37
|
-
const X_INTENT = (text: string) =>
|
|
38
|
-
`https://x.com/intent/tweet?text=${encodeURIComponent(text)}`;
|
|
39
|
-
const LI_INTENT = (text: string) =>
|
|
40
|
-
`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(SITE_URL)}&summary=${encodeURIComponent(text)}`;
|
|
41
|
-
|
|
42
|
-
const COLLAPSED_KEY = "failproofai:audit:share-dock-collapsed";
|
|
43
|
-
|
|
44
|
-
interface Props {
|
|
45
|
-
/** Ref to the `.archetype-frame` to capture. Same node `identity-section`
|
|
46
|
-
* forwards into IdentitySection's `frameRef`. */
|
|
47
|
-
frameRef: React.RefObject<HTMLDivElement | null>;
|
|
48
|
-
archetypeKey: ArchetypeKey;
|
|
49
|
-
/** Seed for deterministic archetype-variant copy. Same as IdentitySection's
|
|
50
|
-
* `seed` prop — usually the inferred project name. */
|
|
51
|
-
seed: string;
|
|
52
|
-
score: number;
|
|
53
|
-
grade: Grade;
|
|
54
|
-
missing: number;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function ShareDock({ frameRef, archetypeKey, seed, score, grade, missing }: Props) {
|
|
58
|
-
const [collapsed, setCollapsed] = useState(false);
|
|
59
|
-
const [busy, setBusy] = useState<null | "x" | "linkedin" | "download">(null);
|
|
60
|
-
const { capture } = usePostHog();
|
|
61
|
-
const archetype = pickArchetypeVariant(archetypeKey, seed);
|
|
62
|
-
const archetypeDisplayName = archetype.name;
|
|
63
|
-
|
|
64
|
-
// Restore collapsed preference from sessionStorage on first mount.
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
try {
|
|
67
|
-
if (typeof window !== "undefined" && window.sessionStorage.getItem(COLLAPSED_KEY) === "1") {
|
|
68
|
-
setCollapsed(true);
|
|
69
|
-
}
|
|
70
|
-
} catch { /* private mode, etc. — fall through to default */ }
|
|
71
|
-
}, []);
|
|
72
|
-
|
|
73
|
-
const toggle = (next: boolean) => {
|
|
74
|
-
setCollapsed(next);
|
|
75
|
-
try { window.sessionStorage.setItem(COLLAPSED_KEY, next ? "1" : "0"); } catch { /* ignore */ }
|
|
76
|
-
capture("audit_share_dock_toggled", { collapsed: next });
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const captureCardBlob = async (): Promise<Blob | null> => {
|
|
80
|
-
const node = frameRef.current;
|
|
81
|
-
if (!node) return null;
|
|
82
|
-
node.classList.add("capturing");
|
|
83
|
-
try {
|
|
84
|
-
if (typeof document !== "undefined" && document.fonts?.ready) await document.fonts.ready;
|
|
85
|
-
await new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
86
|
-
const html2canvas = (await import("html2canvas")).default;
|
|
87
|
-
const canvas = await html2canvas(node, {
|
|
88
|
-
backgroundColor: "#0e0e11",
|
|
89
|
-
scale: 2,
|
|
90
|
-
logging: false,
|
|
91
|
-
useCORS: true,
|
|
92
|
-
});
|
|
93
|
-
return await new Promise<Blob | null>((resolve) => {
|
|
94
|
-
canvas.toBlob((blob) => resolve(blob), "image/png");
|
|
95
|
-
});
|
|
96
|
-
} finally {
|
|
97
|
-
node.classList.remove("capturing");
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const filenameFor = (channel: "x" | "linkedin" | "download") =>
|
|
102
|
-
`failproofai-${channel}-${grade.toLowerCase()}-${score}.png`;
|
|
103
|
-
|
|
104
|
-
const handleShare = async (channel: "x" | "linkedin" | "download") => {
|
|
105
|
-
if (busy) return;
|
|
106
|
-
setBusy(channel);
|
|
107
|
-
capture("audit_card_share_clicked", {
|
|
108
|
-
channel: channel === "download" ? "download" : channel,
|
|
109
|
-
source: "dock",
|
|
110
|
-
score,
|
|
111
|
-
grade,
|
|
112
|
-
missing_policies: missing,
|
|
113
|
-
});
|
|
114
|
-
try {
|
|
115
|
-
const blob = await captureCardBlob().catch(() => null);
|
|
116
|
-
if (!blob) {
|
|
117
|
-
capture("audit_card_capture_completed", {
|
|
118
|
-
trigger: channel === "download" ? "download" : `share_${channel}`,
|
|
119
|
-
status: "error",
|
|
120
|
-
image_method: "failed",
|
|
121
|
-
source: "dock",
|
|
122
|
-
});
|
|
123
|
-
toast(shareCardToastMessage("failed"));
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Dedicated "save audit-card" button: always download, never clipboard.
|
|
128
|
-
if (channel === "download") {
|
|
129
|
-
const ok = downloadCard(blob, filenameFor(channel));
|
|
130
|
-
const method = ok ? "download" : "failed";
|
|
131
|
-
capture("audit_card_capture_completed", {
|
|
132
|
-
trigger: "download",
|
|
133
|
-
status: ok ? "success" : "error",
|
|
134
|
-
image_method: method,
|
|
135
|
-
source: "dock",
|
|
136
|
-
});
|
|
137
|
-
toast(shareCardToastMessage(method));
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// X / LinkedIn share: try the native share sheet with file first
|
|
142
|
-
// (one-tap "image attached" on iOS / Android / recent Safari / recent
|
|
143
|
-
// Chrome). Fall back to clipboard + opening the intent URL when the
|
|
144
|
-
// native share sheet isn't available or the user dismissed it.
|
|
145
|
-
const shareCtx: ShareCtx = {
|
|
146
|
-
score,
|
|
147
|
-
arch: archetypeDisplayName.toLowerCase(),
|
|
148
|
-
grade,
|
|
149
|
-
missing,
|
|
150
|
-
};
|
|
151
|
-
// One of five tone-matched templates (quirky for X, professional for
|
|
152
|
-
// LinkedIn), chosen deterministically from the behaviour-fingerprint
|
|
153
|
-
// seed so the copy fits this run and stays stable on re-share.
|
|
154
|
-
const shareText = channel === "x"
|
|
155
|
-
? pickTemplate(X_TEMPLATES, seed, shareCtx)
|
|
156
|
-
: pickTemplate(LI_TEMPLATES, seed, shareCtx);
|
|
157
|
-
const native = await shareCardNative(blob, filenameFor(channel), shareText);
|
|
158
|
-
if (native) {
|
|
159
|
-
capture("audit_card_capture_completed", {
|
|
160
|
-
trigger: `share_${channel}`,
|
|
161
|
-
status: "success",
|
|
162
|
-
image_method: "native",
|
|
163
|
-
source: "dock",
|
|
164
|
-
});
|
|
165
|
-
toast(shareCardToastMessage("native"));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const fallbackMethod = await copyOrDownloadCard(blob, filenameFor(channel));
|
|
170
|
-
capture("audit_card_capture_completed", {
|
|
171
|
-
trigger: `share_${channel}`,
|
|
172
|
-
status: "success",
|
|
173
|
-
image_method: fallbackMethod,
|
|
174
|
-
source: "dock",
|
|
175
|
-
});
|
|
176
|
-
toast(shareCardToastMessage(fallbackMethod));
|
|
177
|
-
const intent = channel === "x"
|
|
178
|
-
? X_INTENT(shareText)
|
|
179
|
-
: LI_INTENT(shareText);
|
|
180
|
-
globalThis.open(intent, "_blank", "noopener,noreferrer");
|
|
181
|
-
} finally {
|
|
182
|
-
setBusy(null);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
// Render the collapsed FAB. Single pink-tile pulse that re-expands the dock.
|
|
187
|
-
if (collapsed) {
|
|
188
|
-
return (
|
|
189
|
-
<button
|
|
190
|
-
type="button"
|
|
191
|
-
className="share-dock-fab"
|
|
192
|
-
aria-label="Expand share dock"
|
|
193
|
-
onClick={() => toggle(false)}
|
|
194
|
-
>
|
|
195
|
-
<span aria-hidden="true">𝕏</span>
|
|
196
|
-
</button>
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return (
|
|
201
|
-
<aside
|
|
202
|
-
className="share-dock"
|
|
203
|
-
aria-label="Share your audit"
|
|
204
|
-
data-busy={busy ? "true" : undefined}
|
|
205
|
-
>
|
|
206
|
-
<header className="share-dock-head">
|
|
207
|
-
<span className="share-dock-eyebrow"><span aria-hidden="true">━━</span>share your audit</span>
|
|
208
|
-
<button
|
|
209
|
-
type="button"
|
|
210
|
-
className="share-dock-caret"
|
|
211
|
-
onClick={() => toggle(true)}
|
|
212
|
-
aria-label="Collapse share dock"
|
|
213
|
-
>
|
|
214
|
-
<span aria-hidden="true">▾</span>
|
|
215
|
-
</button>
|
|
216
|
-
</header>
|
|
217
|
-
<div className="share-dock-stack">
|
|
218
|
-
<button
|
|
219
|
-
type="button"
|
|
220
|
-
className="share-btn share-btn--x"
|
|
221
|
-
onClick={() => handleShare("x")}
|
|
222
|
-
disabled={busy !== null}
|
|
223
|
-
>
|
|
224
|
-
<span className="share-btn-mark share-btn-mark--x" aria-hidden="true">𝕏</span>
|
|
225
|
-
<span className="share-btn-body">
|
|
226
|
-
<span className="share-btn-eyebrow">share on</span>
|
|
227
|
-
<span className="share-btn-label">{busy === "x" ? "rendering…" : "X · Twitter"}</span>
|
|
228
|
-
</span>
|
|
229
|
-
<span className="share-btn-arrow" aria-hidden="true">→</span>
|
|
230
|
-
</button>
|
|
231
|
-
<button
|
|
232
|
-
type="button"
|
|
233
|
-
className="share-btn share-btn--li"
|
|
234
|
-
onClick={() => handleShare("linkedin")}
|
|
235
|
-
disabled={busy !== null}
|
|
236
|
-
>
|
|
237
|
-
<span className="share-btn-mark share-btn-mark--li" aria-hidden="true">in</span>
|
|
238
|
-
<span className="share-btn-body">
|
|
239
|
-
<span className="share-btn-eyebrow">share on</span>
|
|
240
|
-
<span className="share-btn-label">{busy === "linkedin" ? "rendering…" : "LinkedIn"}</span>
|
|
241
|
-
</span>
|
|
242
|
-
<span className="share-btn-arrow" aria-hidden="true">→</span>
|
|
243
|
-
</button>
|
|
244
|
-
<button
|
|
245
|
-
type="button"
|
|
246
|
-
className="share-btn share-btn--dl"
|
|
247
|
-
onClick={() => handleShare("download")}
|
|
248
|
-
disabled={busy !== null}
|
|
249
|
-
>
|
|
250
|
-
<span className="share-btn-mark share-btn-mark--dl" aria-hidden="true">↓</span>
|
|
251
|
-
<span className="share-btn-body">
|
|
252
|
-
<span className="share-btn-eyebrow">save</span>
|
|
253
|
-
<span className="share-btn-label">{busy === "download" ? "rendering…" : "audit-card"}</span>
|
|
254
|
-
</span>
|
|
255
|
-
<span className="share-btn-arrow" aria-hidden="true">↓</span>
|
|
256
|
-
</button>
|
|
257
|
-
</div>
|
|
258
|
-
<footer className="share-dock-foot">
|
|
259
|
-
<span aria-hidden="true">▮▮</span>image auto-attaches via paste
|
|
260
|
-
</footer>
|
|
261
|
-
</aside>
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
ShareDock.displayName = "ShareDock";
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Section 01b — SHOW OFF CTA. Big bordered strip directly after the
|
|
5
|
-
* identity card. Sigil on the left, "show off your agent." headline +
|
|
6
|
-
* sub on the middle, "→ MAKE POSTER" action button on the right.
|
|
7
|
-
*
|
|
8
|
-
* Clicking the action captures the IdentitySection's archetype-frame
|
|
9
|
-
* DOM via html2canvas and triggers a PNG download. The capture target
|
|
10
|
-
* is passed in via a ref (avoids querying the DOM by class).
|
|
11
|
-
*/
|
|
12
|
-
import React, { useState } from "react";
|
|
13
|
-
import { ARCHETYPES, type ArchetypeKey } from "@/src/audit/archetypes";
|
|
14
|
-
import { usePostHog } from "@/contexts/PostHogContext";
|
|
15
|
-
import { Sigil } from "./sigil";
|
|
16
|
-
|
|
17
|
-
interface Props {
|
|
18
|
-
archetypeKey: ArchetypeKey;
|
|
19
|
-
/** Ref to the IdentitySection's `.archetype-frame` div — captured to PNG. */
|
|
20
|
-
identityFrameRef: React.RefObject<HTMLDivElement | null>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function buildFilename(archetypeKey: ArchetypeKey): string {
|
|
24
|
-
const date = new Date().toISOString().slice(0, 10);
|
|
25
|
-
return `failproofai-${archetypeKey}-${date}.png`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function ShowOffCTA({ archetypeKey, identityFrameRef }: Props) {
|
|
29
|
-
const archetype = ARCHETYPES[archetypeKey];
|
|
30
|
-
const { capture } = usePostHog();
|
|
31
|
-
const [state, setState] = useState<"idle" | "busy" | "done" | "error">("idle");
|
|
32
|
-
|
|
33
|
-
const handleMakePoster = async () => {
|
|
34
|
-
const node = identityFrameRef.current;
|
|
35
|
-
if (!node || state === "busy") return;
|
|
36
|
-
capture("audit_poster_clicked", {
|
|
37
|
-
archetype: archetypeKey,
|
|
38
|
-
});
|
|
39
|
-
setState("busy");
|
|
40
|
-
/** Add a capture-only class that locks font sizes, the grid layout,
|
|
41
|
-
* and disables clamp()/text-shadow rules html2canvas renders
|
|
42
|
-
* unreliably. CSS lives in audit-styles.css under `.capturing`. */
|
|
43
|
-
node.classList.add("capturing");
|
|
44
|
-
try {
|
|
45
|
-
// Wait for the display font (Bitcount Prop Single) to load — otherwise
|
|
46
|
-
// html2canvas captures a fallback that has different metrics and the
|
|
47
|
-
// archetype name overlaps the tagline / sigil column.
|
|
48
|
-
if (typeof document !== "undefined" && document.fonts?.ready) {
|
|
49
|
-
await document.fonts.ready;
|
|
50
|
-
}
|
|
51
|
-
// Force a single rAF so the .capturing class is applied to layout
|
|
52
|
-
// before html2canvas reads computed styles.
|
|
53
|
-
await new Promise<void>((r) => requestAnimationFrame(() => r()));
|
|
54
|
-
|
|
55
|
-
const html2canvas = (await import("html2canvas")).default;
|
|
56
|
-
// Bleed: include the frame's 8px box-shadow in the capture rect.
|
|
57
|
-
const bleed = 12;
|
|
58
|
-
const canvas = await html2canvas(node, {
|
|
59
|
-
backgroundColor: "#131316",
|
|
60
|
-
scale: 2,
|
|
61
|
-
logging: false,
|
|
62
|
-
useCORS: true,
|
|
63
|
-
x: -bleed,
|
|
64
|
-
y: -bleed,
|
|
65
|
-
width: node.offsetWidth + bleed * 2,
|
|
66
|
-
height: node.offsetHeight + bleed * 2,
|
|
67
|
-
windowWidth: Math.max(1100, node.offsetWidth + bleed * 2),
|
|
68
|
-
});
|
|
69
|
-
await new Promise<void>((resolve) => {
|
|
70
|
-
canvas.toBlob((blob) => {
|
|
71
|
-
if (!blob) { resolve(); return; }
|
|
72
|
-
const url = URL.createObjectURL(blob);
|
|
73
|
-
const a = document.createElement("a");
|
|
74
|
-
a.href = url;
|
|
75
|
-
a.download = buildFilename(archetypeKey);
|
|
76
|
-
document.body.appendChild(a);
|
|
77
|
-
a.click();
|
|
78
|
-
document.body.removeChild(a);
|
|
79
|
-
URL.revokeObjectURL(url);
|
|
80
|
-
resolve();
|
|
81
|
-
}, "image/png");
|
|
82
|
-
});
|
|
83
|
-
capture("audit_poster_completed", {
|
|
84
|
-
status: "success",
|
|
85
|
-
archetype: archetypeKey,
|
|
86
|
-
});
|
|
87
|
-
setState("done");
|
|
88
|
-
setTimeout(() => setState("idle"), 2000);
|
|
89
|
-
} catch (err) {
|
|
90
|
-
console.error("poster capture failed:", err);
|
|
91
|
-
capture("audit_poster_completed", {
|
|
92
|
-
status: "error",
|
|
93
|
-
archetype: archetypeKey,
|
|
94
|
-
});
|
|
95
|
-
setState("error");
|
|
96
|
-
setTimeout(() => setState("idle"), 2000);
|
|
97
|
-
} finally {
|
|
98
|
-
node.classList.remove("capturing");
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const actionLabel =
|
|
103
|
-
state === "busy" ? "rendering…"
|
|
104
|
-
: state === "done" ? "downloaded ✓"
|
|
105
|
-
: state === "error" ? "render failed"
|
|
106
|
-
: "make poster";
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<section className="showoff" data-screen-label="01b Show off">
|
|
110
|
-
<button
|
|
111
|
-
type="button"
|
|
112
|
-
className="showoff-cta"
|
|
113
|
-
onClick={handleMakePoster}
|
|
114
|
-
disabled={state === "busy"}
|
|
115
|
-
style={{ cursor: state === "busy" ? "wait" : "pointer", width: "100%", textAlign: "left" }}
|
|
116
|
-
>
|
|
117
|
-
<span className="showoff-glyph" aria-hidden="true">
|
|
118
|
-
<Sigil archetypeKey={archetypeKey} hideLabel />
|
|
119
|
-
</span>
|
|
120
|
-
<span className="showoff-copy">
|
|
121
|
-
<span className="showoff-label">━━ shareable poster</span>
|
|
122
|
-
<span className="showoff-headline">show off your agent.</span>
|
|
123
|
-
<span className="showoff-sub">
|
|
124
|
-
generate a one-page poster of your {archetype.name}.
|
|
125
|
-
score, percentile, sigil. ready to post.
|
|
126
|
-
</span>
|
|
127
|
-
</span>
|
|
128
|
-
<span className="showoff-action">
|
|
129
|
-
<span className="showoff-arrow">→</span>
|
|
130
|
-
<span className="showoff-action-label">{actionLabel}</span>
|
|
131
|
-
</span>
|
|
132
|
-
</button>
|
|
133
|
-
</section>
|
|
134
|
-
);
|
|
135
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|