six-degrees 0.3.0 → 0.4.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/.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 +3 -3
- package/.next/standalone/.next/prerender-manifest.json +35 -7
- package/.next/standalone/.next/required-server-files.json +6 -1
- package/.next/standalone/.next/routes-manifest.json +18 -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 +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- 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 +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/import.html +1 -1
- package/.next/standalone/.next/server/app/import.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/launch.html +2 -2
- package/.next/standalone/.next/server/app/launch.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +12 -12
- 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/paths/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths.html +1 -1
- package/.next/standalone/.next/server/app/paths.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/profile.html +1 -1
- package/.next/standalone/.next/server/app/profile.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/queue.html +1 -1
- package/.next/standalone/.next/server/app/queue.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/scores/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/scores/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/scores/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/scores/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/scores/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/scores/page.js +13 -0
- package/.next/standalone/.next/server/app/scores/page.js.map +5 -0
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/scores.html +1 -0
- package/.next/standalone/.next/server/app/scores.meta +26 -0
- package/.next/standalone/.next/server/app/scores.rsc +24 -0
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +24 -0
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +28 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +13 -13
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup.html +1 -1
- package/.next/standalone/.next/server/app/setup.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i5hdu4._.js → [root-of-the-server]__03ydxxh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bat8nx._.js +8 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
- package/.next/standalone/.next/server/chunks/_02twlca._.js +10 -0
- package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +8 -6
- package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +13 -8
- package/.next/standalone/.next/server/chunks/{lib_rpc_02ndsg2.js → lib_rpc_03i8xef.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_08q5vp6.js → lib_rpc_08fc3rp.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_00tx2sm.js → lib_rpc_08r2t_q.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_02-qa4d.js → lib_rpc_0btpv1t.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
- package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0an6blh._.js → [root-of-the-server]__0zzcbqg._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1z1j3ek._.js → [root-of-the-server]__19ip1qq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0hw_qnj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1cyxocj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1vpfyd-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_scores_page_actions_0x-w58n.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +11 -9
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
- package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0x3i8za._.js +131 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_1bunoni._.js → node_modules_next_dist_1v8aef8._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/next-font-manifest.js +1 -1
- package/.next/standalone/.next/server/next-font-manifest.json +4 -0
- 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/prefetch-hints.json +14 -0
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
- package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
- package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +1 -0
- package/.next/standalone/.next/static/chunks/0muwt9bykzr-1.js +1 -0
- package/.next/standalone/.next/static/chunks/0w76iraw7ij3k.js +18 -0
- package/.next/standalone/.next/static/chunks/135n87ydxjrra.js +2 -0
- package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
- package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +1 -0
- package/.next/standalone/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
- package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
- package/.next/standalone/.next/static/chunks/2xjho0lroiwlq.js +1 -0
- package/.next/standalone/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
- package/.next/standalone/.next/static/chunks/3qh78g2aqhprd.js +2 -0
- package/.next/standalone/CHANGELOG.md +286 -0
- package/.next/standalone/CONTRIBUTING.md +15 -4
- package/.next/standalone/README.md +50 -29
- package/.next/standalone/SECURITY.md +10 -0
- package/.next/standalone/app/api/company-scores/legacy/route.js +1 -1
- package/.next/standalone/app/api/company-scores/route.js +1 -1
- package/.next/standalone/app/api/ingest/route.js +23 -2
- package/.next/standalone/app/api/outreach/route.js +107 -44
- package/.next/standalone/app/api/scraper/route.js +146 -17
- package/.next/standalone/app/api/settings/sector-preview/route.js +3 -2
- package/.next/standalone/app/api/settings/sector-suggestions/route.js +1 -1
- package/.next/standalone/app/api/update-images/route.js +41 -7
- package/.next/standalone/app/avatars/[file]/route.js +6 -1
- package/.next/standalone/app/components/Avatar.js +5 -2
- package/.next/standalone/app/components/ChainView.js +659 -469
- package/.next/standalone/app/components/CompanyScores.js +16 -10
- package/.next/standalone/app/components/EmptyState.js +1 -1
- package/.next/standalone/app/components/FieldStep.js +94 -0
- package/.next/standalone/app/components/ForceGraph.js +183 -768
- package/.next/standalone/app/components/GridView.js +9 -7
- package/.next/standalone/app/components/LegacyScoresCard.js +1 -1
- package/.next/standalone/app/components/LinkedInLimits.js +22 -7
- package/.next/standalone/app/components/ListView.js +6 -4
- package/.next/standalone/app/components/MappingProgress.js +5 -6
- package/.next/standalone/app/components/OnboardingGate.js +1 -1
- package/.next/standalone/app/components/OrbitGraph.js +33 -1
- package/.next/standalone/app/components/OutlinkQuest.js +5 -4
- package/.next/standalone/app/components/PathsAnalyzer.js +1 -1
- package/.next/standalone/app/components/RingsView.js +9 -8
- package/.next/standalone/app/components/SeparationView.js +96 -14
- package/.next/standalone/app/components/Sidebar.js +366 -287
- package/.next/standalone/app/components/TheirCircle.js +300 -0
- package/.next/standalone/app/components/settings/SectorPicker.js +202 -0
- package/.next/standalone/app/components/settings/SectorSection.js +21 -195
- package/.next/standalone/app/components/settings/TierSection.js +99 -0
- package/.next/standalone/app/components/useRequests.js +12 -0
- package/.next/standalone/app/components/useScanner.js +13 -0
- package/.next/standalone/app/components/views.js +3 -5
- package/.next/standalone/app/globals.css +7 -0
- package/.next/standalone/app/launch/page.js +6 -3
- package/.next/standalone/app/page.js +183 -74
- package/.next/standalone/app/paths/page.js +20 -14
- package/.next/standalone/app/profile/page.js +3 -3
- package/.next/standalone/app/queue/page.js +56 -60
- package/.next/standalone/app/scores/page.js +72 -0
- package/.next/standalone/app/settings/page.js +15 -3
- package/.next/standalone/app/setup/page.js +423 -260
- package/.next/standalone/db/schema.js +2 -1
- package/.next/standalone/eslint.config.mjs +3 -3
- package/.next/standalone/install.sh +3 -3
- package/.next/standalone/lib/chain-layout.js +139 -0
- package/.next/standalone/lib/circle.js +169 -0
- package/.next/standalone/lib/companies.js +1 -1
- package/.next/standalone/lib/csv.js +21 -10
- package/.next/standalone/lib/data-export.js +2 -1
- package/.next/standalone/lib/data-folder.js +4 -2
- package/.next/standalone/lib/db-client.js +1 -0
- package/.next/standalone/lib/galaxy.js +12 -0
- package/.next/standalone/lib/ingest.js +27 -8
- package/.next/standalone/lib/legacy-offer.js +7 -6
- package/.next/standalone/lib/legacy-scores.js +2 -2
- package/.next/standalone/lib/linkedin-limits.js +30 -8
- package/.next/standalone/lib/photos.js +47 -0
- package/.next/standalone/lib/quest.js +8 -3
- package/.next/standalone/lib/rarity.js +80 -0
- package/.next/standalone/lib/reach.js +113 -0
- package/.next/standalone/lib/requests-client.js +113 -0
- package/.next/standalone/lib/rpc.js +26 -15
- package/.next/standalone/lib/scan-state.js +1 -0
- package/.next/standalone/lib/scanner-setup.js +20 -1
- package/.next/standalone/lib/score-guess.js +60 -0
- package/.next/standalone/lib/scoring.js +1761 -69
- package/.next/standalone/lib/scraper-client.js +235 -17
- package/.next/standalone/lib/sector-directory.js +1 -1
- package/.next/standalone/lib/sector-focus.js +17 -3
- package/.next/standalone/lib/separation.js +4 -2
- package/.next/standalone/lib/settings-client.js +21 -0
- package/.next/standalone/lib/settings-effects.js +19 -1
- package/.next/standalone/lib/settings.js +7 -2
- package/.next/standalone/next.config.mjs +25 -3
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/public/demo-data.json +1 -1
- package/.next/standalone/scripts/build-app.mjs +11 -2
- package/.next/standalone/scripts/check-node.mjs +1 -1
- package/.next/standalone/scripts/gen-synthetic.mjs +1 -1
- package/.next/standalone/scripts/image_store.py +77 -21
- package/.next/standalone/scripts/scrape.py +305 -29
- package/.next/standalone/server.js +1 -1
- package/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
- package/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
- package/.next/static/chunks/0l1568qff0efj.js +1 -0
- package/.next/static/chunks/0muwt9bykzr-1.js +1 -0
- package/.next/static/chunks/0w76iraw7ij3k.js +18 -0
- package/.next/static/chunks/135n87ydxjrra.js +2 -0
- package/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
- package/.next/static/chunks/28bu26ausqzmn.js +1 -0
- package/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
- package/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
- package/.next/static/chunks/2xjho0lroiwlq.js +1 -0
- package/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
- package/.next/static/chunks/3qh78g2aqhprd.js +2 -0
- package/CHANGELOG.md +286 -0
- package/README.md +50 -29
- package/SECURITY.md +10 -0
- package/db/schema.js +2 -1
- package/lib/chain-layout.js +139 -0
- package/lib/circle.js +169 -0
- package/lib/companies.js +1 -1
- package/lib/csv.js +21 -10
- package/lib/data-export.js +2 -1
- package/lib/data-folder.js +4 -2
- package/lib/db-client.js +1 -0
- package/lib/galaxy.js +12 -0
- package/lib/ingest.js +27 -8
- package/lib/legacy-offer.js +7 -6
- package/lib/legacy-scores.js +2 -2
- package/lib/linkedin-limits.js +30 -8
- package/lib/photos.js +47 -0
- package/lib/quest.js +8 -3
- package/lib/rarity.js +80 -0
- package/lib/reach.js +113 -0
- package/lib/requests-client.js +113 -0
- package/lib/rpc.js +26 -15
- package/lib/scan-state.js +1 -0
- package/lib/scanner-setup.js +20 -1
- package/lib/score-guess.js +60 -0
- package/lib/scoring.js +1761 -69
- package/lib/scraper-client.js +235 -17
- package/lib/sector-directory.js +1 -1
- package/lib/sector-focus.js +17 -3
- package/lib/separation.js +4 -2
- package/lib/settings-client.js +21 -0
- package/lib/settings-effects.js +19 -1
- package/lib/settings.js +7 -2
- package/package.json +2 -2
- package/public/demo-data.json +1 -1
- package/scripts/check-node.mjs +1 -1
- package/scripts/image_store.py +77 -21
- package/scripts/scrape.py +305 -29
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0942v8n._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0hl_t7r._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +0 -132
- package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_16vay6l._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1kciwn4._.js +0 -3
- package/.next/standalone/.next/static/chunks/06x3jiiz96m5t.js +0 -1
- package/.next/standalone/.next/static/chunks/08k1crid-05dt.js +0 -1
- package/.next/standalone/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
- package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +0 -2
- package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +0 -1
- package/.next/standalone/.next/static/chunks/1chp3nx45meg5.js +0 -44
- package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
- package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +0 -1
- package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +0 -1
- package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +0 -1
- package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
- package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
- package/.next/standalone/app/components/BridgeRing.js +0 -753
- package/.next/static/chunks/06x3jiiz96m5t.js +0 -1
- package/.next/static/chunks/08k1crid-05dt.js +0 -1
- package/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
- package/.next/static/chunks/0q-_khndfxubv.js +0 -2
- package/.next/static/chunks/1c79veqedvubt.js +0 -1
- package/.next/static/chunks/1chp3nx45meg5.js +0 -44
- package/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
- package/.next/static/chunks/1uadqylrod5_v.js +0 -1
- package/.next/static/chunks/23u0zclgwpc4d.js +0 -1
- package/.next/static/chunks/289ktj97di6zg.js +0 -1
- package/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
- package/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { localPhoto } from '../../lib/photos';
|
|
4
|
+
|
|
3
5
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
4
6
|
const TIERS = ['S', 'A', 'B', 'C', 'D'];
|
|
5
7
|
|
|
@@ -38,8 +40,8 @@ export default function GridView({ connections, degree2 = [], onSelect, mode })
|
|
|
38
40
|
onMouseLeave={e => e.currentTarget.style.background = 'rgba(255,255,255,0.03)'}
|
|
39
41
|
>
|
|
40
42
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
|
|
41
|
-
{cl.bridge.profile_image_url ? (
|
|
42
|
-
<img src={cl.bridge.profile_image_url} alt="" style={{
|
|
43
|
+
{localPhoto(cl.bridge.profile_image_url) ? (
|
|
44
|
+
<img src={localPhoto(cl.bridge.profile_image_url)} alt="" style={{
|
|
43
45
|
width: 40, height: 40, borderRadius: '50%', objectFit: 'cover',
|
|
44
46
|
border: `2px solid ${TIER_COLORS[cl.bridge.tier]}`,
|
|
45
47
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -67,13 +69,13 @@ export default function GridView({ connections, degree2 = [], onSelect, mode })
|
|
|
67
69
|
{cl.members.slice(0, 8).map((m, i) => (
|
|
68
70
|
<div key={m.id} style={{
|
|
69
71
|
width: 22, height: 22, borderRadius: '50%', fontSize: 8, fontWeight: 700,
|
|
70
|
-
background: m.profile_image_url ? 'transparent' : (TIER_COLORS[m.tier] || '#333'),
|
|
72
|
+
background: localPhoto(m.profile_image_url) ? 'transparent' : (TIER_COLORS[m.tier] || '#333'),
|
|
71
73
|
color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
72
74
|
border: '2px solid #0a0a1a', marginLeft: i > 0 ? -6 : 0, position: 'relative', zIndex: 8 - i,
|
|
73
75
|
overflow: 'hidden',
|
|
74
76
|
}}>
|
|
75
|
-
{m.profile_image_url ? (
|
|
76
|
-
<img src={m.profile_image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
|
77
|
+
{localPhoto(m.profile_image_url) ? (
|
|
78
|
+
<img src={localPhoto(m.profile_image_url)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
|
77
79
|
) : m.name?.charAt(0)}
|
|
78
80
|
</div>
|
|
79
81
|
))}
|
|
@@ -131,8 +133,8 @@ export default function GridView({ connections, degree2 = [], onSelect, mode })
|
|
|
131
133
|
onMouseLeave={e => e.currentTarget.style.background = 'rgba(255,255,255,0.03)'}
|
|
132
134
|
>
|
|
133
135
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
134
|
-
{c.profile_image_url ? (
|
|
135
|
-
<img src={c.profile_image_url} alt="" style={{
|
|
136
|
+
{localPhoto(c.profile_image_url) ? (
|
|
137
|
+
<img src={localPhoto(c.profile_image_url)} alt="" style={{
|
|
136
138
|
width: 32, height: 32, borderRadius: '50%', objectFit: 'cover',
|
|
137
139
|
border: `2px solid ${TIER_COLORS[tier]}`,
|
|
138
140
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// The Scores tab: the one-time offer to keep the curated list's old scores as
|
|
4
4
|
// your own, after the list was made neutral (lib/legacy-offer.js decides who
|
|
5
5
|
// sees it and what "keep" writes). It sits at the top of the Scores tab until
|
|
6
6
|
// it's answered, then never shows again. Not while a CSV import or the sample
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
// The Scan page's view of what LinkedIn allows: the search budget
|
|
4
|
-
// lock, and everyone whose list was only partly
|
|
5
|
-
// TRAPS §35. The numbers come from
|
|
6
|
-
// lib/paused.js), which reads the same
|
|
3
|
+
// The Scan page's view of what LinkedIn allows: the search budget and the cap
|
|
4
|
+
// on profile views, the cooldown lock, and everyone whose list was only partly
|
|
5
|
+
// read, with a way back into each. TRAPS §16, §35. The numbers come from
|
|
6
|
+
// /api/scraper (lib/linkedin-limits.js, lib/paused.js), which reads the same
|
|
7
|
+
// files the scanner writes.
|
|
7
8
|
|
|
8
9
|
import { useState } from 'react';
|
|
9
10
|
|
|
@@ -11,6 +12,7 @@ const LINE = '1px solid rgba(255,255,255,0.1)';
|
|
|
11
12
|
const TIER = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
12
13
|
const DAILY = [25, 50, 100, 200, 500];
|
|
13
14
|
const MONTHLY = [100, 250, 500, 1000, 0];
|
|
15
|
+
const PROFILES = [10, 25, 50, 100];
|
|
14
16
|
|
|
15
17
|
const when = (ms) => new Date(ms).toLocaleString(undefined, { weekday: 'short', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
|
|
16
18
|
const day = (ms) => new Date(ms).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
|
@@ -55,6 +57,8 @@ export function BudgetBox({ li, onSetLimits, disabled }) {
|
|
|
55
57
|
if (!li) return null;
|
|
56
58
|
const { limits } = li;
|
|
57
59
|
const today = li.unreadable ? '?' : li.searchesToday;
|
|
60
|
+
const views = li.unreadable ? '?' : li.profilesToday;
|
|
61
|
+
const set = (change) => onSetLimits({ ...limits, ...change });
|
|
58
62
|
return (
|
|
59
63
|
<div style={{ padding: '12px 14px', borderRadius: 8, border: LINE, background: 'rgba(255,255,255,0.03)', fontSize: 12.5, color: '#b8c4c4' }}>
|
|
60
64
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: 14 }}>
|
|
@@ -66,16 +70,22 @@ export function BudgetBox({ li, onSetLimits, disabled }) {
|
|
|
66
70
|
<div><b style={{ color: '#fff' }}>{li.searchesMonth}</b> of {limits.monthly || 'no limit'} this month · resets {day(li.monthResets)}</div>
|
|
67
71
|
<Bar used={li.searchesMonth} cap={limits.monthly} />
|
|
68
72
|
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<div><b style={{ color: '#fff' }}>{views}</b> of {limits.profiles} profile views today</div>
|
|
75
|
+
<Bar used={li.unreadable ? limits.profiles : li.profilesToday} cap={limits.profiles} />
|
|
76
|
+
</div>
|
|
69
77
|
</div>
|
|
70
78
|
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', marginTop: 10 }}>
|
|
71
79
|
<span>Budget:</span>
|
|
72
|
-
<select value={limits.daily} disabled={disabled} onChange={(e) =>
|
|
80
|
+
<select value={limits.daily} disabled={disabled} onChange={(e) => set({ daily: Number(e.target.value) })} style={sel}>
|
|
73
81
|
{DAILY.map((n) => <option key={n} value={n}>{n} a day</option>)}
|
|
74
82
|
</select>
|
|
75
|
-
<select value={limits.monthly} disabled={disabled} onChange={(e) =>
|
|
83
|
+
<select value={limits.monthly} disabled={disabled} onChange={(e) => set({ monthly: Number(e.target.value) })} style={sel}>
|
|
76
84
|
{MONTHLY.map((n) => <option key={n} value={n}>{n ? `${n} a month` : 'no monthly cap (Premium)'}</option>)}
|
|
77
85
|
</select>
|
|
78
|
-
<
|
|
86
|
+
<select value={limits.profiles} disabled={disabled} onChange={(e) => set({ profiles: Number(e.target.value) })} style={sel}>
|
|
87
|
+
{PROFILES.map((n) => <option key={n} value={n}>{n} profile views a day</option>)}
|
|
88
|
+
</select>
|
|
79
89
|
</div>
|
|
80
90
|
<div style={{ marginTop: 8, color: '#778', lineHeight: 1.6 }}>
|
|
81
91
|
Every page of someone’s connections is one search. LinkedIn limits a free account’s people
|
|
@@ -83,6 +93,11 @@ export function BudgetBox({ li, onSetLimits, disabled }) {
|
|
|
83
93
|
When a budget is used, a scan saves what it read and stops; the next one carries on from the same page.
|
|
84
94
|
{limits.daily > 100 && <b style={{ color: '#FFD700' }}> {limits.daily} a day can use up a free account’s month in a day or two.</b>}
|
|
85
95
|
</div>
|
|
96
|
+
<div style={{ marginTop: 6, color: '#778', lineHeight: 1.6 }}>
|
|
97
|
+
Each circle scan opens the person’s profile once, which is one profile view. Profile views are what
|
|
98
|
+
LinkedIn restricted an account for, after about 20 in an hour. The scanner opens at most one a minute, and
|
|
99
|
+
once today’s are used, a scan stops before the next profile and tries that person next time.
|
|
100
|
+
</div>
|
|
86
101
|
</div>
|
|
87
102
|
);
|
|
88
103
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { localPhoto } from '../../lib/photos';
|
|
4
|
+
|
|
3
5
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
4
6
|
|
|
5
7
|
export default function ListView({ connections, degree2 = [], onSelect, mode }) {
|
|
@@ -47,8 +49,8 @@ export default function ListView({ connections, degree2 = [], onSelect, mode })
|
|
|
47
49
|
>
|
|
48
50
|
<span style={{ fontSize: 12, fontWeight: 700, color: '#444' }}>{i + 1}</span>
|
|
49
51
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
|
|
50
|
-
{cl.bridge.profile_image_url ? (
|
|
51
|
-
<img src={cl.bridge.profile_image_url} alt="" style={{
|
|
52
|
+
{localPhoto(cl.bridge.profile_image_url) ? (
|
|
53
|
+
<img src={localPhoto(cl.bridge.profile_image_url)} alt="" style={{
|
|
52
54
|
width: 28, height: 28, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
|
|
53
55
|
border: `2px solid ${TIER_COLORS[cl.bridge.tier]}`,
|
|
54
56
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -113,8 +115,8 @@ export default function ListView({ connections, degree2 = [], onSelect, mode })
|
|
|
113
115
|
onMouseLeave={e => e.currentTarget.style.background = i < 3 ? `${TIER_COLORS[c.tier]}08` : 'transparent'}
|
|
114
116
|
>
|
|
115
117
|
<span style={{ fontSize: 12, fontWeight: 700, color: i < 3 ? TIER_COLORS[c.tier] : '#444' }}>{i + 1}</span>
|
|
116
|
-
{c.profile_image_url ? (
|
|
117
|
-
<img src={c.profile_image_url} alt="" style={{
|
|
118
|
+
{localPhoto(c.profile_image_url) ? (
|
|
119
|
+
<img src={localPhoto(c.profile_image_url)} alt="" style={{
|
|
118
120
|
width: 28, height: 28, borderRadius: '50%', objectFit: 'cover',
|
|
119
121
|
border: `2px solid ${TIER_COLORS[c.tier]}`,
|
|
120
122
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { TIER_COLORS_CLASSIC as TIER_COLORS } from '../../lib/tiers';
|
|
4
|
+
import { reachIndex, circleState } from '../../lib/reach';
|
|
4
5
|
|
|
5
6
|
// How much of your 1st degree has had its circle opened.
|
|
6
7
|
//
|
|
@@ -19,13 +20,11 @@ const CARD = {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
export function computeMapping(degree1 = [], degree2 = [], skips = []) {
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
// One rule for whose circle is open, hidden or still to do: lib/reach.js.
|
|
24
|
+
const reach = reachIndex(degree1, degree2, { skips });
|
|
25
25
|
const state = (c) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return 'todo';
|
|
26
|
+
const s = circleState(c, reach);
|
|
27
|
+
return s === 'scanned' ? 'mapped' : s;
|
|
29
28
|
};
|
|
30
29
|
|
|
31
30
|
const counts = { mapped: 0, hidden: 0, todo: 0 };
|
|
@@ -24,7 +24,7 @@ export default function OnboardingGate({ children }) {
|
|
|
24
24
|
) : (
|
|
25
25
|
<div style={{ maxWidth: 420 }}>
|
|
26
26
|
<div style={{ fontSize: 18, fontWeight: 700, marginBottom: 8 }}>
|
|
27
|
-
Can’t reach
|
|
27
|
+
Can’t reach Six Degrees’ local server
|
|
28
28
|
</div>
|
|
29
29
|
<div style={{ color: '#888', fontSize: 14, lineHeight: 1.6 }}>
|
|
30
30
|
The page loaded but the app behind it is not answering. If you started it
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
20
|
+
import useRequests from './useRequests';
|
|
21
|
+
import { hasRequest } from '../../lib/requests-client';
|
|
20
22
|
import {
|
|
21
23
|
drag,
|
|
22
24
|
forceCollide,
|
|
@@ -30,6 +32,7 @@ import {
|
|
|
30
32
|
zoomTransform,
|
|
31
33
|
} from 'd3';
|
|
32
34
|
import { TIER_COLORS, TIER_ORDER, TIER_RING_RADIUS, initialsFor } from '../../lib/tiers';
|
|
35
|
+
import { localPhoto } from '../../lib/photos';
|
|
33
36
|
|
|
34
37
|
const GOLDEN_ANGLE = 2.399963;
|
|
35
38
|
const LABEL_ZOOM_THRESHOLD = 1.1;
|
|
@@ -71,6 +74,7 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
71
74
|
const zoomKRef = useRef(1);
|
|
72
75
|
const savedTransformRef = useRef(null);
|
|
73
76
|
const applySelectionRef = useRef(() => {});
|
|
77
|
+
const applyRequestsRef = useRef(() => {});
|
|
74
78
|
const focusNodeRef = useRef(() => {});
|
|
75
79
|
const [size, setSize] = useState({ w: 0, h: 0 });
|
|
76
80
|
const isMobile = useIsMobile();
|
|
@@ -122,6 +126,15 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
122
126
|
applySelectionRef.current(selectedId);
|
|
123
127
|
}, [selectedId]);
|
|
124
128
|
|
|
129
|
+
// So do requests you've sent (lib/requests-client.js): a request sent from
|
|
130
|
+
// any view rings that person's dots here at once.
|
|
131
|
+
const requests = useRequests();
|
|
132
|
+
const requestsRef = useRef(requests);
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
requestsRef.current = requests;
|
|
135
|
+
applyRequestsRef.current(requests);
|
|
136
|
+
}, [requests]);
|
|
137
|
+
|
|
125
138
|
useEffect(() => {
|
|
126
139
|
const svgEl = svgRef.current;
|
|
127
140
|
if (!svgEl || size.w < 40 || size.h < 40) return undefined;
|
|
@@ -362,7 +375,8 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
362
375
|
.attr('font-size', d.r * 0.8).attr('font-weight', 700)
|
|
363
376
|
.attr('fill', '#0b0b16').style('pointer-events', 'none');
|
|
364
377
|
|
|
365
|
-
|
|
378
|
+
// Only a photo saved on this computer (lib/photos.js).
|
|
379
|
+
const imageUrl = localPhoto(isUser ? userImage : conn?.profile_image_url);
|
|
366
380
|
if (imageUrl) {
|
|
367
381
|
const clipId = `og-clip-${safeId(d.id)}`;
|
|
368
382
|
defs.append('clipPath').attr('id', clipId).append('circle').attr('r', d.r);
|
|
@@ -545,6 +559,23 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
545
559
|
};
|
|
546
560
|
applySelectionRef.current(selectedIdRef.current);
|
|
547
561
|
|
|
562
|
+
// ---- requests you've sent ---------------------------------------------
|
|
563
|
+
// A dashed gold ring on every 2nd-degree dot you've asked, in step with
|
|
564
|
+
// the shared list, without rebuilding the scene.
|
|
565
|
+
applyRequestsRef.current = (state) => {
|
|
566
|
+
nodesLayer.selectAll('circle.og-asked').remove();
|
|
567
|
+
node.filter((d) => d.kind === 'd2' && hasRequest(d.connection, state))
|
|
568
|
+
.append('circle')
|
|
569
|
+
.attr('class', 'og-asked')
|
|
570
|
+
.attr('r', (d) => d.r + 2.2)
|
|
571
|
+
.attr('fill', 'none')
|
|
572
|
+
.attr('stroke', '#FFD700')
|
|
573
|
+
.attr('stroke-width', 1)
|
|
574
|
+
.attr('stroke-dasharray', '2 1.6')
|
|
575
|
+
.style('pointer-events', 'none');
|
|
576
|
+
};
|
|
577
|
+
applyRequestsRef.current(requestsRef.current);
|
|
578
|
+
|
|
548
579
|
// ---- fly-to ----------------------------------------------------------
|
|
549
580
|
const nodeById = new Map(renderNodes.map((n) => [n.id, n]));
|
|
550
581
|
focusNodeRef.current = (id) => {
|
|
@@ -591,6 +622,7 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
591
622
|
return () => {
|
|
592
623
|
sim?.stop();
|
|
593
624
|
applySelectionRef.current = () => {};
|
|
625
|
+
applyRequestsRef.current = () => {};
|
|
594
626
|
focusNodeRef.current = () => {};
|
|
595
627
|
svg.on('.zoom', null);
|
|
596
628
|
svg.on('click', null);
|
|
@@ -11,6 +11,7 @@ import { useMemo, useState } from 'react';
|
|
|
11
11
|
import Link from 'next/link';
|
|
12
12
|
import { buildQuest, XP_SEND, STAGE_SIZE } from '../../lib/quest';
|
|
13
13
|
import { initialsFor } from '../../lib/tiers';
|
|
14
|
+
import { localPhoto } from '../../lib/photos';
|
|
14
15
|
|
|
15
16
|
const TIER = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
16
17
|
const LINE = '1px solid rgba(255,255,255,0.1)';
|
|
@@ -24,8 +25,8 @@ function Face({ person, size = 36, ring }) {
|
|
|
24
25
|
display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: size * 0.34, fontWeight: 800,
|
|
25
26
|
border: `2px solid ${ring || color}`,
|
|
26
27
|
}}>{initialsFor(person?.name || '?')}</div>
|
|
27
|
-
{person?.profile_image_url && (
|
|
28
|
-
<img src={person.profile_image_url} alt="" onError={(e) => { e.currentTarget.style.display = 'none'; }}
|
|
28
|
+
{localPhoto(person?.profile_image_url) && (
|
|
29
|
+
<img src={localPhoto(person.profile_image_url)} alt="" onError={(e) => { e.currentTarget.style.display = 'none'; }}
|
|
29
30
|
style={{ position: 'absolute', inset: 0, width: size, height: size, borderRadius: '50%', objectFit: 'cover', border: `2px solid ${ring || color}` }} />
|
|
30
31
|
)}
|
|
31
32
|
</div>
|
|
@@ -47,8 +48,8 @@ function Ring({ progress, size = 86, color = '#FF6B35', children }) {
|
|
|
47
48
|
);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
export default function OutlinkQuest({ recs, sentIds, added, mappedIds, onSend, onUndo }) {
|
|
51
|
-
const quest = useMemo(() => buildQuest({ recs, sentIds, added, mappedIds }), [recs, sentIds, added, mappedIds]);
|
|
51
|
+
export default function OutlinkQuest({ recs, sentIds, added, mappedIds, reach, onSend, onUndo }) {
|
|
52
|
+
const quest = useMemo(() => buildQuest({ recs, sentIds, added, mappedIds, reach }), [recs, sentIds, added, mappedIds, reach]);
|
|
52
53
|
const [open, setOpen] = useState(null); // bridge id of the expanded cluster
|
|
53
54
|
const [toast, setToast] = useState(null);
|
|
54
55
|
const { level } = quest;
|
|
@@ -505,7 +505,7 @@ function Section({ title, children }) {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
// The company's score in people's power, where it comes from, and a control
|
|
508
|
-
// to set it (
|
|
508
|
+
// to set it (the Scores tab has every company).
|
|
509
509
|
function CompanyScoreLine({ name }) {
|
|
510
510
|
const { companies, setScore } = useCompanyScores();
|
|
511
511
|
const c = companies?.find((x) => x.name === name);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import { localPhoto } from '../../lib/photos';
|
|
4
5
|
|
|
5
6
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
6
7
|
const TIERS = ['S', 'A', 'B', 'C', 'D'];
|
|
@@ -58,8 +59,8 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
|
|
|
58
59
|
boxShadow: hoveredId === cl.bridge.id ? `0 0 20px ${TIER_COLORS[cl.bridge.tier]}60` : `0 0 10px ${TIER_COLORS[cl.bridge.tier]}20`,
|
|
59
60
|
transition: 'box-shadow 0.2s',
|
|
60
61
|
}}>
|
|
61
|
-
{cl.bridge.profile_image_url ? (
|
|
62
|
-
<img src={cl.bridge.profile_image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
|
62
|
+
{localPhoto(cl.bridge.profile_image_url) ? (
|
|
63
|
+
<img src={localPhoto(cl.bridge.profile_image_url)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
|
63
64
|
) : (
|
|
64
65
|
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
65
66
|
background: TIER_COLORS[cl.bridge.tier], color: cl.bridge.tier === 'S' ? '#000' : '#fff', fontSize: 20, fontWeight: 800 }}>
|
|
@@ -95,13 +96,13 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
|
|
|
95
96
|
<div style={{
|
|
96
97
|
width: size, height: size, borderRadius: '50%',
|
|
97
98
|
border: `2px solid ${TIER_COLORS[tier]}`,
|
|
98
|
-
overflow: 'hidden', background: p.profile_image_url ? '#111' : TIER_COLORS[tier],
|
|
99
|
+
overflow: 'hidden', background: localPhoto(p.profile_image_url) ? '#111' : TIER_COLORS[tier],
|
|
99
100
|
transition: 'transform 0.15s, box-shadow 0.15s',
|
|
100
101
|
transform: isHov ? 'scale(1.3)' : 'scale(1)',
|
|
101
102
|
boxShadow: isHov ? `0 0 12px ${TIER_COLORS[tier]}60` : 'none',
|
|
102
103
|
}}>
|
|
103
|
-
{p.profile_image_url ? (
|
|
104
|
-
<img src={p.profile_image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
|
104
|
+
{localPhoto(p.profile_image_url) ? (
|
|
105
|
+
<img src={localPhoto(p.profile_image_url)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
|
105
106
|
onError={e => { e.target.style.display = 'none'; e.target.parentElement.style.background = TIER_COLORS[tier]; }} />
|
|
106
107
|
) : (
|
|
107
108
|
<span style={{ display: 'flex', width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center',
|
|
@@ -202,7 +203,7 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
|
|
|
202
203
|
width: size, height: size, borderRadius: '50%',
|
|
203
204
|
border: `2px solid ${TIER_COLORS[tier]}`,
|
|
204
205
|
overflow: 'hidden',
|
|
205
|
-
background: c.profile_image_url ? '#111' : TIER_COLORS[tier],
|
|
206
|
+
background: localPhoto(c.profile_image_url) ? '#111' : TIER_COLORS[tier],
|
|
206
207
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
207
208
|
transition: 'transform 0.15s, box-shadow 0.15s',
|
|
208
209
|
transform: isHovered ? 'scale(1.3)' : 'scale(1)',
|
|
@@ -210,8 +211,8 @@ export default function RingsView({ connections, degree2 = [], onSelect, mode })
|
|
|
210
211
|
zIndex: isHovered ? 10 : 1,
|
|
211
212
|
position: 'relative',
|
|
212
213
|
}}>
|
|
213
|
-
{c.profile_image_url ? (
|
|
214
|
-
<img src={c.profile_image_url} alt="" style={{
|
|
214
|
+
{localPhoto(c.profile_image_url) ? (
|
|
215
|
+
<img src={localPhoto(c.profile_image_url)} alt="" style={{
|
|
215
216
|
width: '100%', height: '100%', objectFit: 'cover',
|
|
216
217
|
}} onError={e => {
|
|
217
218
|
e.target.style.display = 'none';
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
// appearing cannot feed a resize loop.
|
|
21
21
|
|
|
22
22
|
import { memo, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react';
|
|
23
|
-
import { separationPeople, compareWaysIn, summitLayout, shortName, TIERS } from '../../lib/separation';
|
|
23
|
+
import { separationPeople, compareWaysIn, summitLayout, shortName, TIERS, keyFor, routeIndex } from '../../lib/separation';
|
|
24
|
+
import { RARITY, rarityOf, rarityInfo, toggle } from '../../lib/rarity';
|
|
25
|
+
import { hasRequest } from '../../lib/requests-client';
|
|
26
|
+
import useRequests from './useRequests';
|
|
24
27
|
import { initialsFor } from '../../lib/tiers';
|
|
25
28
|
import Avatar from './Avatar';
|
|
26
29
|
|
|
@@ -63,7 +66,7 @@ function subline(person) {
|
|
|
63
66
|
return person.headline || person.role || person.company || '';
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
export default function SeparationView({ connections = [], degree2 = [], onSelect, userName, selectedId, tierColors = CLASSIC }) {
|
|
69
|
+
export default function SeparationView({ connections = [], degree2 = [], fullDegree1 = connections, fullDegree2 = degree2, onSelect, userName, selectedId, tierColors = CLASSIC }) {
|
|
67
70
|
const isMobile = useIsMobile();
|
|
68
71
|
const ROW_H = isMobile ? 68 : 56;
|
|
69
72
|
const K = isMobile ? 5 : 10;
|
|
@@ -73,6 +76,9 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
73
76
|
const q = useDeferredValue(query.trim().toLowerCase());
|
|
74
77
|
const [sort, setSort] = useState('power'); // 'power' | 'ways'
|
|
75
78
|
const [tier, setTier] = useState('all');
|
|
79
|
+
// Rarity beside the tier (lib/rarity.js): any bands switched on; none is everyone.
|
|
80
|
+
const [rarities, setRarities] = useState(() => new Set());
|
|
81
|
+
const requests = useRequests();
|
|
76
82
|
const [scrollY, setScrollY] = useState(0);
|
|
77
83
|
const [box, setBox] = useState({ w: 0, h: 800 });
|
|
78
84
|
// The scroll element lives in state as well as a ref: the ResizeObserver
|
|
@@ -90,11 +96,35 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
90
96
|
() => (sort === 'ways' ? [...model.people].sort(compareWaysIn) : model.people),
|
|
91
97
|
[model, sort],
|
|
92
98
|
);
|
|
99
|
+
// How rare the way in to each person is, counted across every circle you've
|
|
100
|
+
// scanned (not only the ones a bridge-tier filter leaves): never a score.
|
|
101
|
+
const waysAll = useMemo(() => routeIndex(fullDegree2), [fullDegree2]);
|
|
102
|
+
const rarityBy = useMemo(() => {
|
|
103
|
+
const m = new Map();
|
|
104
|
+
for (const p of model.people) m.set(p.key, rarityOf(p.person, waysAll.get(p.key)?.size || p.waysIn));
|
|
105
|
+
return m;
|
|
106
|
+
}, [model, waysAll]);
|
|
107
|
+
const byRarity = useMemo(() => {
|
|
108
|
+
const out = Object.fromEntries(RARITY.map((r) => [r.key, 0]));
|
|
109
|
+
for (const r of rarityBy.values()) out[r.key]++;
|
|
110
|
+
return out;
|
|
111
|
+
}, [rarityBy]);
|
|
112
|
+
const matches = useCallback((p, text) => (tier === 'all' || p.tier === tier)
|
|
113
|
+
&& (!rarities.size || rarities.has(rarityBy.get(p.key)?.key))
|
|
114
|
+
&& (!text || p.haystack.includes(text)), [tier, rarities, rarityBy]);
|
|
93
115
|
const visible = useMemo(() => {
|
|
94
|
-
if (!q && tier === 'all') return ordered;
|
|
95
|
-
return ordered.filter((p) => (
|
|
96
|
-
}, [ordered, q, tier]);
|
|
97
|
-
|
|
116
|
+
if (!q && tier === 'all' && !rarities.size) return ordered;
|
|
117
|
+
return ordered.filter((p) => matches(p, q));
|
|
118
|
+
}, [ordered, q, tier, rarities, matches]);
|
|
119
|
+
|
|
120
|
+
// Who you've already asked, or already know. The map is "who to ask next",
|
|
121
|
+
// so it moves on past them: send requests to its ten and the next ten come
|
|
122
|
+
// up. The list still shows everyone, marked.
|
|
123
|
+
const d1Keys = useMemo(() => new Set(fullDegree1.map(keyFor)), [fullDegree1]);
|
|
124
|
+
const statusOf = useCallback((p) => (d1Keys.has(p.key) ? 'connected' : hasRequest(p.person, requests) ? 'asked' : null),
|
|
125
|
+
[d1Keys, requests]);
|
|
126
|
+
const top = useMemo(() => visible.filter((p) => !statusOf(p)).slice(0, K), [visible, K, statusOf]);
|
|
127
|
+
const askedShown = useMemo(() => visible.reduce((n, p) => n + (statusOf(p) ? 1 : 0), 0), [visible, statusOf]);
|
|
98
128
|
const selectedKey = selectedId != null ? model.rowToKey.get(selectedId) ?? null : null;
|
|
99
129
|
|
|
100
130
|
// onSelect is read through a ref so the row and map callbacks never change
|
|
@@ -157,12 +187,14 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
157
187
|
|
|
158
188
|
const changeQuery = (v) => { setQuery(v); toTop(); };
|
|
159
189
|
const changeTier = (t) => { setTier(t); toTop(); };
|
|
190
|
+
const changeRarity = (r) => { setRarities((set) => toggle(set, r)); toTop(); };
|
|
191
|
+
const clearAll = () => { setQuery(''); setTier('all'); setRarities(new Set()); toTop(); };
|
|
160
192
|
const changeSort = (s) => { setSort(s); toTop(); };
|
|
161
193
|
const onSearchKey = (e) => {
|
|
162
194
|
if (e.key === 'Enter') {
|
|
163
195
|
// Read the live text, not the deferred copy the list is still catching up to.
|
|
164
196
|
const live = query.trim().toLowerCase();
|
|
165
|
-
const first = ordered.find((p) => (
|
|
197
|
+
const first = ordered.find((p) => matches(p, live));
|
|
166
198
|
if (first) onPick(first);
|
|
167
199
|
} else if (e.key === 'Escape') {
|
|
168
200
|
if (query) changeQuery('');
|
|
@@ -171,7 +203,7 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
171
203
|
};
|
|
172
204
|
|
|
173
205
|
const { summary } = model;
|
|
174
|
-
const filtered = !!q || tier !== 'all';
|
|
206
|
+
const filtered = !!q || tier !== 'all' || rarities.size > 0;
|
|
175
207
|
const youLabel = !userName || /^you$/i.test(String(userName).trim()) ? 'You' : initialsFor(userName);
|
|
176
208
|
const rows = visible.slice(start, end);
|
|
177
209
|
|
|
@@ -276,6 +308,23 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
276
308
|
</button>
|
|
277
309
|
);
|
|
278
310
|
})}
|
|
311
|
+
<span title={RARITY_NOTE} style={{ fontSize: 10, color: '#666', whiteSpace: 'nowrap', flexShrink: 0, textTransform: 'uppercase', letterSpacing: 0.5, marginLeft: 6, cursor: 'help' }}>Rarity</span>
|
|
312
|
+
{RARITY.map((r) => {
|
|
313
|
+
const on = rarities.has(r.key);
|
|
314
|
+
return (
|
|
315
|
+
<button key={r.key} type="button" aria-pressed={on} onClick={() => changeRarity(r.key)}
|
|
316
|
+
title={`${r.label}: ${r.range} mutual connection${r.range === '1' ? '' : 's'}. ${RARITY_NOTE}`} style={{
|
|
317
|
+
height: isMobile ? 40 : 30, padding: '0 10px', borderRadius: 15, cursor: 'pointer', flexShrink: 0,
|
|
318
|
+
fontSize: 12, fontWeight: 700, whiteSpace: 'nowrap',
|
|
319
|
+
border: `1px solid ${on ? r.color : 'rgba(255,255,255,0.1)'}`,
|
|
320
|
+
background: on ? `${r.color}22` : 'rgba(255,255,255,0.03)',
|
|
321
|
+
color: on ? r.color : '#999',
|
|
322
|
+
}}>
|
|
323
|
+
{r.label}
|
|
324
|
+
<span style={{ marginLeft: 5, fontWeight: 500, color: on ? r.color : '#666', opacity: 0.85 }}>{fmt(byRarity[r.key])}</span>
|
|
325
|
+
</button>
|
|
326
|
+
);
|
|
327
|
+
})}
|
|
279
328
|
</div>
|
|
280
329
|
</div>
|
|
281
330
|
|
|
@@ -290,9 +339,10 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
290
339
|
<div style={{ height: mapBlockH, boxSizing: 'border-box', paddingTop: 10 }}>
|
|
291
340
|
<div style={{ height: captionH - 10, overflow: 'hidden' }}>
|
|
292
341
|
<div style={{ fontSize: 12, fontWeight: 700, color: '#ddd', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
293
|
-
Top {fmt(top.length)} of {fmt(visible.length)}{filtered ? ' shown' : ''} · every way in drawn
|
|
342
|
+
Top {fmt(top.length)} you haven’t asked, of {fmt(visible.length)}{filtered ? ' shown' : ''} · every way in drawn
|
|
294
343
|
</div>
|
|
295
344
|
<div style={{ fontSize: 10, color: '#777', marginTop: 2, whiteSpace: isMobile ? 'normal' : 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', lineHeight: 1.35 }}>
|
|
345
|
+
{askedShown > 0 && <span style={{ color: '#FFD700' }}>{fmt(askedShown)} asked or connected, so it moved on · </span>}
|
|
296
346
|
<span style={{ color: ORANGE }}>solid orange</span> = top-scored bridge · dashed = other routes · dot size = ways in
|
|
297
347
|
</div>
|
|
298
348
|
</div>
|
|
@@ -335,6 +385,8 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
335
385
|
tierColors={tierColors}
|
|
336
386
|
onPick={onPick}
|
|
337
387
|
q={q}
|
|
388
|
+
status={statusOf(p)}
|
|
389
|
+
rarity={rarityBy.get(p.key)}
|
|
338
390
|
/>
|
|
339
391
|
))}
|
|
340
392
|
|
|
@@ -342,9 +394,9 @@ export default function SeparationView({ connections = [], degree2 = [], onSelec
|
|
|
342
394
|
<div style={{ position: 'absolute', top: 40, left: 0, right: 0, textAlign: 'center', color: '#888', fontSize: 13, padding: '0 16px' }}>
|
|
343
395
|
{q
|
|
344
396
|
? <>No one matches ‘{query.trim()}’. Search covers names, headlines, companies and who knows them.</>
|
|
345
|
-
: <>No
|
|
397
|
+
: <>No one here with those filters.</>}
|
|
346
398
|
<div style={{ marginTop: 12 }}>
|
|
347
|
-
<button type="button" onClick={
|
|
399
|
+
<button type="button" onClick={clearAll} style={{
|
|
348
400
|
padding: '8px 16px', borderRadius: 8, cursor: 'pointer', fontSize: 12, fontWeight: 700,
|
|
349
401
|
border: '1px solid rgba(255,255,255,0.15)', background: 'rgba(255,255,255,0.06)', color: '#fff',
|
|
350
402
|
}}>Clear</button>
|
|
@@ -495,7 +547,33 @@ function Via({ p, route, tierColors, compact }) {
|
|
|
495
547
|
);
|
|
496
548
|
}
|
|
497
549
|
|
|
498
|
-
|
|
550
|
+
/** "Request sent" or "Connected", and how rare the way in is. */
|
|
551
|
+
function Tags({ status, rarity }) {
|
|
552
|
+
const info = rarity ? rarityInfo(rarity.key) : null;
|
|
553
|
+
return (
|
|
554
|
+
<>
|
|
555
|
+
{status === 'asked' && (
|
|
556
|
+
<span title="You sent a request. It shows everywhere in the app." style={{
|
|
557
|
+
flexShrink: 0, fontSize: 9, fontWeight: 800, color: '#FFD700', border: '1px dashed rgba(255,215,0,0.6)', borderRadius: 4, padding: '0 4px',
|
|
558
|
+
}}>Request sent</span>
|
|
559
|
+
)}
|
|
560
|
+
{status === 'connected' && (
|
|
561
|
+
<span title="Already one of your connections" style={{
|
|
562
|
+
flexShrink: 0, fontSize: 9, fontWeight: 800, color: '#00ff88', border: '1px solid rgba(0,255,136,0.5)', borderRadius: 4, padding: '0 4px',
|
|
563
|
+
}}>Connected</span>
|
|
564
|
+
)}
|
|
565
|
+
{info && status !== 'connected' && (
|
|
566
|
+
<span title={`${rarity.count} mutual connection${rarity.count === 1 ? '' : 's'}${rarity.from === 'scans' ? ' (from your scans, so it can only go up)' : ''}. ${RARITY_NOTE}`} style={{
|
|
567
|
+
flexShrink: 0, fontSize: 9, fontWeight: 700, color: info.color, border: `1px solid ${info.color}55`, borderRadius: 4, padding: '0 4px',
|
|
568
|
+
}}>{info.label}</span>
|
|
569
|
+
)}
|
|
570
|
+
</>
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const RARITY_NOTE = 'Rarity is how many mutual connections lead to them: few is a rare way in, many is warm (likely to accept). It never changes a score or a tier.';
|
|
575
|
+
|
|
576
|
+
const Row = memo(function Row({ p, top, height, isMobile, selected, tierColors, onPick, q, status, rarity }) {
|
|
499
577
|
const { person } = p;
|
|
500
578
|
const c = tierColors[p.tier] || '#888';
|
|
501
579
|
const route = pickRoute(p, q);
|
|
@@ -532,8 +610,11 @@ const Row = memo(function Row({ p, top, height, isMobile, selected, tierColors,
|
|
|
532
610
|
|
|
533
611
|
{isMobile ? (
|
|
534
612
|
<div style={{ minWidth: 0, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
535
|
-
<span style={{
|
|
536
|
-
|
|
613
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: 4, minWidth: 0 }}>
|
|
614
|
+
<span style={{ ...ellipsis, fontSize: 13, fontWeight: 600, color: member ? '#888' : '#fff', minWidth: 0 }}>
|
|
615
|
+
{person.name}{member && <span style={{ fontSize: 10, color: '#666', fontWeight: 400 }}> · out of network</span>}
|
|
616
|
+
</span>
|
|
617
|
+
<Tags status={status} rarity={rarity} />
|
|
537
618
|
</span>
|
|
538
619
|
<Via p={p} route={route} tierColors={tierColors} compact />
|
|
539
620
|
<span style={{ ...ellipsis, fontSize: 10, color: '#666' }}>{subline(person)}</span>
|
|
@@ -544,6 +625,7 @@ const Row = memo(function Row({ p, top, height, isMobile, selected, tierColors,
|
|
|
544
625
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
|
|
545
626
|
<span style={{ ...ellipsis, fontSize: 13, fontWeight: 600, color: member ? '#888' : '#fff' }}>{person.name}</span>
|
|
546
627
|
<span style={{ flexShrink: 0, fontSize: 9, fontWeight: 800, color: c, border: `1px solid ${c}55`, borderRadius: 4, padding: '0 4px' }}>{p.tier}</span>
|
|
628
|
+
<Tags status={status} rarity={rarity} />
|
|
547
629
|
{member && <span style={{ flexShrink: 0, fontSize: 10, color: '#666' }}>out of network</span>}
|
|
548
630
|
</div>
|
|
549
631
|
<div style={{ ...ellipsis, fontSize: 10, color: '#777', marginTop: 2 }}>{subline(person)}</div>
|