six-degrees 0.4.11 → 0.5.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/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +9 -9
- 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 +12 -12
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- 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 +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 +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 +1 -1
- 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 +3 -2
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js +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 +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 +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/social/crm/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js +3 -3
- package/.next/standalone/.next/server/app/api/social/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 +14 -14
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +14 -14
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/scores.html +1 -1
- package/.next/standalone/.next/server/app/scores.rsc +14 -14
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/social.html +1 -1
- package/.next/standalone/.next/server/app/social.rsc +14 -14
- package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0bat8nx._.js → [root-of-the-server]__13dx4sd._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +8 -0
- package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
- 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_0851sk0._.js +8 -0
- package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +8 -0
- package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +8 -0
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +8 -8
- package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_scoring_1-e4ur0.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1ap1tu_._.js → _0gtv9jb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0jdkqgi._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +9 -0
- package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +9 -0
- package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +22 -10
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
- 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 +6 -5
- 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 +19 -3
- package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +1 -1
- 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/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 +1 -1
- package/.next/standalone/.next/static/chunks/03bziijw5srol.js +1 -0
- package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
- package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
- package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +34 -0
- package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
- package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +2 -0
- package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +1 -0
- package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +1 -0
- package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +43 -0
- package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +1 -0
- package/.next/standalone/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
- package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +1 -0
- package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +1 -0
- package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
- package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +1 -0
- package/.next/standalone/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
- package/.next/standalone/CHANGELOG.md +187 -0
- package/.next/standalone/app/api/ingest/route.js +40 -1
- package/.next/standalone/app/api/scraper/route.js +7 -1
- package/.next/standalone/app/api/settings/sector-preview/route.js +2 -1
- package/.next/standalone/app/components/AppTabs.js +59 -0
- package/.next/standalone/app/components/ChainView.js +303 -89
- package/.next/standalone/app/components/CirclePanel.js +162 -0
- package/.next/standalone/app/components/CompanyScores.js +2 -2
- package/.next/standalone/app/components/FieldStep.js +4 -4
- package/.next/standalone/app/components/FilterPanel.js +121 -183
- package/.next/standalone/app/components/ForceGraph.js +41 -3
- package/.next/standalone/app/components/GalaxyLab.js +26 -31
- package/.next/standalone/app/components/LegacyScoresCard.js +2 -2
- package/.next/standalone/app/components/MapControls.js +154 -0
- package/.next/standalone/app/components/NoteDetail.js +118 -0
- package/.next/standalone/app/components/PathsAnalyzer.js +147 -110
- package/.next/standalone/app/components/PeopleMap.js +308 -0
- package/.next/standalone/app/components/ScanRadar.js +129 -0
- package/.next/standalone/app/components/ScanStatusBar.js +71 -27
- package/.next/standalone/app/components/SeparationView.js +626 -118
- package/.next/standalone/app/components/Sidebar.js +28 -2
- package/.next/standalone/app/components/settings/NetworkHealthSection.js +1 -1
- package/.next/standalone/app/components/settings/SectorSection.js +2 -2
- package/.next/standalone/app/components/settings/TierSection.js +1 -1
- package/.next/standalone/app/components/settings/TitleSection.js +149 -0
- package/.next/standalone/app/components/views.js +15 -14
- package/.next/standalone/app/page.js +89 -156
- package/.next/standalone/app/paths/page.js +39 -27
- package/.next/standalone/app/profile/page.js +33 -5
- package/.next/standalone/app/queue/page.js +56 -34
- package/.next/standalone/app/scores/page.js +16 -66
- package/.next/standalone/app/settings/page.js +27 -13
- package/.next/standalone/app/setup/page.js +298 -205
- package/.next/standalone/app/social/SocialHub.js +374 -0
- package/.next/standalone/app/social/page.js +10 -362
- package/.next/standalone/lib/chain-layout.js +105 -35
- package/.next/standalone/lib/degrees.js +0 -6
- package/.next/standalone/lib/dot-rings.js +25 -0
- package/.next/standalone/lib/galaxy-lab.js +65 -6
- package/.next/standalone/lib/ingest.js +2 -0
- package/.next/standalone/lib/island.js +40 -0
- package/.next/standalone/lib/linkedin-limits.js +6 -2
- package/.next/standalone/lib/map-heat.js +20 -0
- package/.next/standalone/lib/network-health.js +1 -1
- package/.next/standalone/lib/notifications.js +121 -0
- package/.next/standalone/lib/people-map.js +82 -0
- package/.next/standalone/lib/rarity.js +45 -3
- package/.next/standalone/lib/reach.js +7 -6
- package/.next/standalone/lib/rpc.js +15 -4
- package/.next/standalone/lib/scan-pace.js +46 -0
- package/.next/standalone/lib/scoring.js +39 -8
- package/.next/standalone/lib/scraper-client.js +19 -6
- package/.next/standalone/lib/sector-focus.js +4 -4
- package/.next/standalone/lib/separation.js +137 -10
- package/.next/standalone/lib/settings-effects.js +20 -11
- package/.next/standalone/lib/settings.js +3 -0
- package/.next/standalone/lib/tier-grid.js +99 -0
- package/.next/standalone/lib/title-ranking.js +83 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/scrape.py +51 -13
- package/.next/static/chunks/03bziijw5srol.js +1 -0
- package/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
- package/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
- package/.next/static/chunks/0g_fy2034bp0x.js +34 -0
- package/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
- package/.next/static/chunks/0w-byiwb2zax8.js +2 -0
- package/.next/static/chunks/1g7jcovdejc9-.js +1 -0
- package/.next/static/chunks/1i51yylxdzht1.js +1 -0
- package/.next/static/chunks/1zf8swaasjf1e.js +43 -0
- package/.next/static/chunks/2rqtpccruavf2.js +1 -0
- package/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
- package/.next/static/chunks/30x3oq_st7a5r.js +1 -0
- package/.next/static/chunks/355ryio1b7sm1.js +1 -0
- package/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
- package/.next/static/chunks/3fci6qi39-1n5.js +1 -0
- package/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
- package/CHANGELOG.md +187 -0
- package/lib/chain-layout.js +105 -35
- package/lib/degrees.js +0 -6
- package/lib/dot-rings.js +25 -0
- package/lib/galaxy-lab.js +65 -6
- package/lib/ingest.js +2 -0
- package/lib/island.js +40 -0
- package/lib/linkedin-limits.js +6 -2
- package/lib/map-heat.js +20 -0
- package/lib/network-health.js +1 -1
- package/lib/notifications.js +121 -0
- package/lib/people-map.js +82 -0
- package/lib/rarity.js +45 -3
- package/lib/reach.js +7 -6
- package/lib/rpc.js +15 -4
- package/lib/scan-pace.js +46 -0
- package/lib/scoring.js +39 -8
- package/lib/scraper-client.js +19 -6
- package/lib/sector-focus.js +4 -4
- package/lib/separation.js +137 -10
- package/lib/settings-effects.js +20 -11
- package/lib/settings.js +3 -0
- package/lib/tier-grid.js +99 -0
- package/lib/title-ranking.js +83 -0
- package/package.json +1 -1
- package/scripts/scrape.py +51 -13
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17hjtzl._.js +0 -8
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20tmem7._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_03i8xef.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_05gt5ya.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_08fc3rp.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_08r2t_q.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0btpv1t.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +0 -8
- package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0-85h6g._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_08cfj2b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0c_y09g._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0l8cxmo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_components_19c2d9c._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +0 -3
- package/.next/standalone/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
- package/.next/standalone/.next/static/chunks/15sjx07xhpn6p.js +0 -1
- package/.next/standalone/.next/static/chunks/160wjb73ddwqd.js +0 -6
- package/.next/standalone/.next/static/chunks/1a74dky22dxd6.js +0 -1
- package/.next/standalone/.next/static/chunks/1c_n0973t07l0.js +0 -2
- package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
- package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +0 -1
- package/.next/standalone/.next/static/chunks/297e1z34yi--z.js +0 -1
- package/.next/standalone/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
- package/.next/standalone/.next/static/chunks/2kfhc58df2xyo.js +0 -1
- package/.next/standalone/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
- package/.next/standalone/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
- package/.next/standalone/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
- package/.next/standalone/app/components/OrbitGraph.js +0 -737
- package/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
- package/.next/static/chunks/15sjx07xhpn6p.js +0 -1
- package/.next/static/chunks/160wjb73ddwqd.js +0 -6
- package/.next/static/chunks/1a74dky22dxd6.js +0 -1
- package/.next/static/chunks/1c_n0973t07l0.js +0 -2
- package/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
- package/.next/static/chunks/236f0jtp68j_h.js +0 -1
- package/.next/static/chunks/297e1z34yi--z.js +0 -1
- package/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
- package/.next/static/chunks/2kfhc58df2xyo.js +0 -1
- package/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
- package/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
- package/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
- /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
- /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
- /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
|
@@ -2512,18 +2512,47 @@ export function readPerson(row) {
|
|
|
2512
2512
|
* points whatever else they list. `companyFor(name)` returns companyScore();
|
|
2513
2513
|
* `read` is the row's readPerson(), when it has been read already.
|
|
2514
2514
|
*/
|
|
2515
|
-
/**
|
|
2516
|
-
|
|
2517
|
-
|
|
2515
|
+
/** Does this title text have the words? Whole words only: "vc" isn't in "dvc", "cto" isn't in "doctor". */
|
|
2516
|
+
const titlePatterns = new Map();
|
|
2517
|
+
export function titleHas(text, words) {
|
|
2518
|
+
let re = titlePatterns.get(words);
|
|
2519
|
+
if (!re) {
|
|
2520
|
+
const esc = words.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2521
|
+
re = new RegExp(`(^|[^a-z0-9])${esc}($|[^a-z0-9])`);
|
|
2522
|
+
if (titlePatterns.size > 500) titlePatterns.clear();
|
|
2523
|
+
titlePatterns.set(words, re);
|
|
2524
|
+
}
|
|
2525
|
+
return re.test(String(text || '').toLowerCase());
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
/**
|
|
2529
|
+
* A title's points under your title ranking (Settings → Scores → Titles,
|
|
2530
|
+
* lib/title-ranking.js): a role whose words are in it counts what you gave
|
|
2531
|
+
* that role (the highest, if several match), else the level counts your
|
|
2532
|
+
* points for it, else the model's. No ranking: the model's.
|
|
2533
|
+
*/
|
|
2534
|
+
export function rankedPoints(role, titles) {
|
|
2535
|
+
if (!titles) return role.title.points;
|
|
2536
|
+
let match = null;
|
|
2537
|
+
for (const r of titles.roles || []) {
|
|
2538
|
+
if (titleHas(role.part, r.text) && (match == null || r.points > match)) match = r.points;
|
|
2539
|
+
}
|
|
2540
|
+
if (match != null) return match;
|
|
2541
|
+
return titles.points?.[role.title.key] ?? role.title.points;
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
/** A role's title points as they count: yours where you ranked them, 70% for a former role, capped for a student. */
|
|
2545
|
+
function rolePoints(role, student, titles) {
|
|
2546
|
+
const points = rankedPoints(role, titles) * (role.former ? FORMER_WEIGHT : 1);
|
|
2518
2547
|
return student ? Math.min(points, STUDENT_CAP) : points;
|
|
2519
2548
|
}
|
|
2520
2549
|
|
|
2521
|
-
export function scorePerson(row, companyFor = (name) => companyScore(name), read = readPerson(row)) {
|
|
2550
|
+
export function scorePerson(row, companyFor = (name) => companyScore(name), read = readPerson(row), titles = null) {
|
|
2522
2551
|
const { roles, student, reach } = read;
|
|
2523
2552
|
let best = null;
|
|
2524
2553
|
for (const role of roles) {
|
|
2525
2554
|
const co = companyFor(role.company);
|
|
2526
|
-
const points = rolePoints(role, student);
|
|
2555
|
+
const points = rolePoints(role, student, titles);
|
|
2527
2556
|
const core = points * (0.45 + 0.055 * co.score);
|
|
2528
2557
|
if (!best || core > best.core) best = { role, co, points, core };
|
|
2529
2558
|
}
|
|
@@ -2535,6 +2564,8 @@ export function scorePerson(row, companyFor = (name) => companyScore(name), read
|
|
|
2535
2564
|
const bonus = reachBonus(row?.headline, unknown ? 4 : best.co.base ?? best.co.score, reach);
|
|
2536
2565
|
const power = round1(best.core + bonus.points);
|
|
2537
2566
|
const title = { ...best.role.title, former: best.role.former, student, points: round1(best.points) };
|
|
2567
|
+
// Your ranking moved it: the card's "why" says so.
|
|
2568
|
+
if (titles && rankedPoints(best.role, titles) !== best.role.title.points) title.ranked = true;
|
|
2538
2569
|
if (best.role.former) title.label = `Former ${title.label}`;
|
|
2539
2570
|
if (student && best.role.title.level > 0) title.label = `Student · ${title.label}`;
|
|
2540
2571
|
const out = { title, company: best.role.company, companyScore: best.co.score, companySource: best.co.source, bonus, power, tier: tierFor(power) };
|
|
@@ -2652,7 +2683,7 @@ export function explainScore(s, boost = 0, { sectorLabel } = {}) {
|
|
|
2652
2683
|
const co = s.companySector
|
|
2653
2684
|
? `${s.company} (${s.companyScore}/10: ${s.companySector.base} + ${s.companySector.bonus} your sector${which ? `: ${which}` : ''})`
|
|
2654
2685
|
: `${s.company || 'no company found'} (${s.companyScore}/10${s.companySource === 'yours' ? ', your score' : ''})`;
|
|
2655
|
-
const bits = [`${s.title.label} (${s.title.points})`, co];
|
|
2686
|
+
const bits = [`${s.title.label} (${s.title.points}${s.title.ranked ? ', your ranking' : ''})`, co];
|
|
2656
2687
|
if (s.bonus.points) bits.push(`+${s.bonus.points} ${s.bonus.reasons.join(', ')}`);
|
|
2657
2688
|
if (boost) bits.push(`+${boost} strong circle`);
|
|
2658
2689
|
return bits.join(' · ');
|
|
@@ -2716,7 +2747,7 @@ export function readNetwork(rows = [], { industryOf, sectorsOf, groupOf } = {})
|
|
|
2716
2747
|
* (companyScore(), by name) and the curve's cut-offs (null on the fixed scale).
|
|
2717
2748
|
*/
|
|
2718
2749
|
export function scoreNetwork(rows = [], {
|
|
2719
|
-
overrides = new Map(), industryOf, sectorsOf, groupOf, focus, tierScale = 'fixed',
|
|
2750
|
+
overrides = new Map(), industryOf, sectorsOf, groupOf, focus, tierScale = 'fixed', titles = null,
|
|
2720
2751
|
read = readNetwork(rows, { industryOf, sectorsOf, groupOf }),
|
|
2721
2752
|
} = {}) {
|
|
2722
2753
|
// Each company is scored once; everyone there shares the answer.
|
|
@@ -2726,7 +2757,7 @@ export function scoreNetwork(rows = [], {
|
|
|
2726
2757
|
return companyScores.get(name);
|
|
2727
2758
|
};
|
|
2728
2759
|
const out = new Map();
|
|
2729
|
-
for (const r of rows) out.set(r.id, { ...scorePerson(r, companyFor, read.people.get(r)), boost: 0 });
|
|
2760
|
+
for (const r of rows) out.set(r.id, { ...scorePerson(r, companyFor, read.people.get(r), titles), boost: 0 });
|
|
2730
2761
|
// The curve's cut-offs come from your 1st-degree people before any circle
|
|
2731
2762
|
// boost, so a boost (which reads the graded tiers) can't move its own cut-off.
|
|
2732
2763
|
const cutoffs = tierScale === 'fixed' ? null
|
|
@@ -15,11 +15,27 @@ export async function scraperStatus() {
|
|
|
15
15
|
return r.json();
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Scan page → "Hide the Chrome window while scanning", remembered in this
|
|
20
|
+
* browser. Every way a scan starts asks here: the Scan page's own buttons used
|
|
21
|
+
* to post straight to the scanner without it, so the switch did nothing for
|
|
22
|
+
* the scans started from the very page it sits on.
|
|
23
|
+
*/
|
|
24
|
+
export const HIDE_CHROME_KEY = 'six-degrees-hide-chrome';
|
|
25
|
+
export function hideChromeOn(storage = typeof localStorage !== 'undefined' ? localStorage : null) {
|
|
26
|
+
try { return JSON.parse(storage?.getItem(HIDE_CHROME_KEY) || 'false') === true; } catch { return false; }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** What a scan start sends: the action, its details, and `headless` when Chrome is to stay hidden. */
|
|
30
|
+
export function scanRequest(action, extra = {}, hidden = hideChromeOn()) {
|
|
31
|
+
return { action, ...extra, ...(hidden ? { headless: true } : {}) };
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
export async function startScrape(action, name, extra = {}) {
|
|
19
35
|
const r = await fetch('/api/scraper', {
|
|
20
36
|
method: 'POST',
|
|
21
37
|
headers: { 'Content-Type': 'application/json' },
|
|
22
|
-
body: JSON.stringify(
|
|
38
|
+
body: JSON.stringify(scanRequest(action, { ...(name ? { name } : {}), ...extra })),
|
|
23
39
|
});
|
|
24
40
|
const d = await r.json().catch(() => ({}));
|
|
25
41
|
if (!r.ok) throw new Error(d.error || 'Could not start the scanner.');
|
|
@@ -211,11 +227,8 @@ export async function beginScrape(action, { name, id, onLog, ...extra } = {}) {
|
|
|
211
227
|
});
|
|
212
228
|
let started;
|
|
213
229
|
try {
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
let hidden = false;
|
|
217
|
-
try { hidden = JSON.parse(localStorage.getItem('six-degrees-hide-chrome') || 'false') === true; } catch {}
|
|
218
|
-
started = await startScrape(action, name, { ...(id ? { id } : {}), ...(hidden ? { headless: true } : {}), ...extra });
|
|
230
|
+
// startScrape adds "Hide the Chrome window" itself (scanRequest), however the scan was started.
|
|
231
|
+
started = await startScrape(action, name, { ...(id ? { id } : {}), ...extra });
|
|
219
232
|
} catch (err) {
|
|
220
233
|
// Refused, often because something else runs: ask what, straight away.
|
|
221
234
|
// Until the answer comes, only our own claim is taken back.
|
|
@@ -141,11 +141,11 @@ export function tierMoves(rows, tierBefore, tierAfter, { examples = 5, companyOf
|
|
|
141
141
|
*/
|
|
142
142
|
export function previewSectorFocus(rows = [], {
|
|
143
143
|
overrides = new Map(), industryOf, sectorsOf = sectorMatcher(), groupOf = sectorGroup, from = NO_FOCUS, to = NO_FOCUS, examples = 5,
|
|
144
|
-
tierScale, read = readNetwork(rows, { industryOf, sectorsOf, groupOf }),
|
|
144
|
+
tierScale, titles = null, read = readNetwork(rows, { industryOf, sectorsOf, groupOf }),
|
|
145
145
|
} = {}) {
|
|
146
|
-
// Both on the tier scale saved now (Scores → Tiers), as rescoring grades them.
|
|
147
|
-
const before = scoreNetwork(rows, { overrides, focus: from, tierScale, read });
|
|
148
|
-
const after = scoreNetwork(rows, { overrides, focus: to, tierScale, read });
|
|
146
|
+
// Both on the tier scale and title ranking saved now (Scores → Tiers, Titles), as rescoring grades them.
|
|
147
|
+
const before = scoreNetwork(rows, { overrides, focus: from, tierScale, titles, read });
|
|
148
|
+
const after = scoreNetwork(rows, { overrides, focus: to, tierScale, titles, read });
|
|
149
149
|
|
|
150
150
|
// Companies whose score moves, biggest first (by your people there now).
|
|
151
151
|
// Every company anyone names counts, a former employer too: its score moves
|
|
@@ -86,11 +86,6 @@ export function comparePower(a, b) {
|
|
|
86
86
|
}
|
|
87
87
|
const NO_ROUTE = { id: null, bridge: null };
|
|
88
88
|
|
|
89
|
-
/** "Ways in" order: most ways in first, then the power order. The rank shown stays the power rank. */
|
|
90
|
-
export function compareWaysIn(a, b) {
|
|
91
|
-
return b.waysIn - a.waysIn || comparePower(a, b);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
89
|
/**
|
|
95
90
|
* @param {object[]} degree2 2nd-degree rows (one per bridge per person)
|
|
96
91
|
* @param {object[]} connections 1st-degree rows, the bridges
|
|
@@ -236,6 +231,10 @@ export function dotRadius(waysIn) {
|
|
|
236
231
|
|
|
237
232
|
export const MIN_BRIDGE_GAP = 18;
|
|
238
233
|
const TOP_PAD = 16;
|
|
234
|
+
/** Grey dots for mutual connections in circles not scanned yet, at most this many drawn. */
|
|
235
|
+
export const GHOST_MAX = 60;
|
|
236
|
+
const GHOST_LABEL = 40; // room under the last connection for the line that counts them
|
|
237
|
+
const PILL_W_LAYOUT = 236; // SeparationView's PILL_W: the connection pills' width as cards
|
|
239
238
|
|
|
240
239
|
/**
|
|
241
240
|
* The summit map: You → the bridges → the top of the list, every route drawn.
|
|
@@ -249,10 +248,13 @@ const TOP_PAD = 16;
|
|
|
249
248
|
*
|
|
250
249
|
* @param {object[]} top the first K people of whatever the list shows
|
|
251
250
|
* @param {number} width map width in px
|
|
251
|
+
* @param {{ cards?: boolean }} opts cards: people drawn as cards and
|
|
252
|
+
* connections as pills (Blake, 2026-10-02), which need more room per row
|
|
252
253
|
* @returns {{ width, height, you, people, bridges, links, spokes }}
|
|
253
254
|
*/
|
|
254
|
-
export function summitLayout(top = [], width = 800, isMobile = false) {
|
|
255
|
-
const gap = isMobile ? 36 : 26;
|
|
255
|
+
export function summitLayout(top = [], width = 800, isMobile = false, { cards = false } = {}) {
|
|
256
|
+
const gap = cards ? 50 : isMobile ? 36 : 26;
|
|
257
|
+
const bridgeGap = cards ? 36 : MIN_BRIDGE_GAP;
|
|
256
258
|
const youX = 20;
|
|
257
259
|
const bx = Math.round(width * 0.42);
|
|
258
260
|
const px = Math.round(width * 0.58);
|
|
@@ -262,7 +264,7 @@ export function summitLayout(top = [], width = 800, isMobile = false) {
|
|
|
262
264
|
p,
|
|
263
265
|
x: px,
|
|
264
266
|
y: TOP_PAD + i * gap,
|
|
265
|
-
r: dotRadius(p.waysIn),
|
|
267
|
+
r: cards ? 0 : dotRadius(p.waysIn),
|
|
266
268
|
label: fit(`#${p.rank}${p.tied ? '=' : ''} ${p.person.name || ''}`, Math.floor((width - px - 60) / 6.2)),
|
|
267
269
|
}));
|
|
268
270
|
|
|
@@ -288,11 +290,16 @@ export function summitLayout(top = [], width = 800, isMobile = false) {
|
|
|
288
290
|
|
|
289
291
|
const y0 = TOP_PAD;
|
|
290
292
|
const y1 = people.length ? people[people.length - 1].y : TOP_PAD;
|
|
291
|
-
|
|
293
|
+
// Each connection sits level with the people they lead to (the middle of
|
|
294
|
+
// them), moved no more than it takes to keep `bridgeGap` from the next one,
|
|
295
|
+
// so a connection with one person sits on that person's row and the line
|
|
296
|
+
// between them runs straight (Blake, 2026-10-02: "the lines should line up
|
|
297
|
+
// with every box").
|
|
298
|
+
const ys = stackColumn(bridges.map((b) => b.bary), bridgeGap, y0);
|
|
292
299
|
const nameBudget = Math.max(4, Math.floor((bx - 10 - (youX + 22)) / 6));
|
|
293
300
|
bridges.forEach((b, j) => {
|
|
294
301
|
b.x = bx;
|
|
295
|
-
b.y =
|
|
302
|
+
b.y = ys[j];
|
|
296
303
|
b.r = 5;
|
|
297
304
|
if (b.unresolved) b.label = fit(`can't be named (${b.count})`, nameBudget);
|
|
298
305
|
else {
|
|
@@ -302,6 +309,15 @@ export function summitLayout(top = [], width = 800, isMobile = false) {
|
|
|
302
309
|
delete b.ys;
|
|
303
310
|
});
|
|
304
311
|
const nodeFor = new Map(bridges.map((b) => [b.unresolved ? UNRESOLVED : b.id, b]));
|
|
312
|
+
// What each connection is to the people on the map: the only door to some,
|
|
313
|
+
// the best way in to others, or only another way in.
|
|
314
|
+
for (const b of bridges) { b.onlyFor = 0; b.bestFor = 0; }
|
|
315
|
+
for (const pp of people) {
|
|
316
|
+
const first = pp.p.routes[0];
|
|
317
|
+
const bestNode = first ? nodeFor.get(first.bridge ? first.id : UNRESOLVED) : null;
|
|
318
|
+
if (bestNode) bestNode.bestFor++;
|
|
319
|
+
if (pp.p.waysIn === 1 && bestNode) bestNode.onlyFor++;
|
|
320
|
+
}
|
|
305
321
|
|
|
306
322
|
// Routes, per person: routes[0] is the top-scored bridge. Two unnamed routes
|
|
307
323
|
// for one person are one line to the one grey node.
|
|
@@ -326,3 +342,114 @@ export function summitLayout(top = [], width = 800, isMobile = false) {
|
|
|
326
342
|
const spokes = bridges.map((b) => ({ id: b.id, x1: you.x, y1: you.y, x2: b.x, y2: b.y }));
|
|
327
343
|
return { width, height, you, people, bridges, links, spokes };
|
|
328
344
|
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Heights as near the ones asked for as they can be while staying `gap`
|
|
348
|
+
* apart, in the order given (already sorted), never above `top`: neighbours
|
|
349
|
+
* that would overlap move apart by the same amount each way.
|
|
350
|
+
*/
|
|
351
|
+
export function stackColumn(wanted = [], gap = MIN_BRIDGE_GAP, top = 0) {
|
|
352
|
+
const blocks = [];
|
|
353
|
+
wanted.forEach((y, k) => {
|
|
354
|
+
blocks.push({ sum: y - k * gap, count: 1 });
|
|
355
|
+
while (blocks.length > 1 && blocks[blocks.length - 2].sum / blocks[blocks.length - 2].count > blocks[blocks.length - 1].sum / blocks[blocks.length - 1].count) {
|
|
356
|
+
const last = blocks.pop();
|
|
357
|
+
blocks[blocks.length - 1].sum += last.sum;
|
|
358
|
+
blocks[blocks.length - 1].count += last.count;
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const out = blocks.flatMap((b) => new Array(b.count).fill(b.sum / b.count)).map((q, k) => q + k * gap);
|
|
362
|
+
const lift = out.length && out[0] < top ? top - out[0] : 0;
|
|
363
|
+
return out.map((y) => y + lift);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Who the map aims at with the slider all the way to easy: whoever you share the
|
|
368
|
+
* most mutual connections with, scanned or not (Blake, 2026-10-02: "if we have
|
|
369
|
+
* mutuals but not scanned thats still a high probability … i dont want to hinder
|
|
370
|
+
* the algorithm since we havent scanned that person"). `mutualsOf` is LinkedIn's
|
|
371
|
+
* count where a scan saved one, never less than the ways in the app has drawn
|
|
372
|
+
* (lib/rarity.js). Then the most drawn, then the stronger person. Narrowing the
|
|
373
|
+
* tiers first (S only, say) keeps it to the people worth it.
|
|
374
|
+
*/
|
|
375
|
+
export function mostMutuals(people = [], mutualsOf = () => 0, limit = 7) {
|
|
376
|
+
return [...people].sort((a, b) => (Math.max(mutualsOf(b) || 0, b.waysIn) - Math.max(mutualsOf(a) || 0, a.waysIn))
|
|
377
|
+
|| b.waysIn - a.waysIn
|
|
378
|
+
|| comparePower(a, b)).slice(0, limit);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* How many people the map draws with the slider at `at` (lib/rarity.js): all
|
|
383
|
+
* `k` from the rare end to the middle, then fewer and fewer, down to the one
|
|
384
|
+
* person you're aiming at with the slider all the way to easy. Blake,
|
|
385
|
+
* 2026-10-02: at the rare end "it will show that one door"; at the other "all
|
|
386
|
+
* the connections going into that one person".
|
|
387
|
+
*/
|
|
388
|
+
export function mapCount(at, k = 10) {
|
|
389
|
+
const v = Math.max(0, Math.min(100, Number(at) || 0));
|
|
390
|
+
if (v <= 50) return k;
|
|
391
|
+
return Math.max(1, Math.round((k * (100 - v)) / 50));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* The map for one person: You → every connection of yours who leads to them →
|
|
396
|
+
* them, with rings round them. Routes the map can't name share one grey node,
|
|
397
|
+
* as in summitLayout, and it comes back in summitLayout's shape (`people` holds
|
|
398
|
+
* the one person, marked `big`), so one map draws both and can glide from one
|
|
399
|
+
* to the other. Pure, so it is tested without a browser.
|
|
400
|
+
*
|
|
401
|
+
* @returns {{ width, height, you, target, people, bridges, links, spokes }}
|
|
402
|
+
*/
|
|
403
|
+
export function convergeLayout(p, width = 800, isMobile = false, { cards = false, mutuals = 0 } = {}) {
|
|
404
|
+
const gap = cards ? 46 : isMobile ? 40 : 34;
|
|
405
|
+
const youX = 20;
|
|
406
|
+
const bx = Math.round(width * (isMobile ? 0.44 : 0.36));
|
|
407
|
+
const tx = Math.round(width * (isMobile ? 0.8 : cards ? 0.6 : 0.62));
|
|
408
|
+
const nodes = [];
|
|
409
|
+
let unnamed = null;
|
|
410
|
+
for (const r of p?.routes || []) {
|
|
411
|
+
if (r.bridge) nodes.push({ id: r.id, bridge: r.bridge, unresolved: false, count: 1 });
|
|
412
|
+
else if (unnamed) unnamed.count++;
|
|
413
|
+
else nodes.push((unnamed = { id: null, bridge: null, unresolved: true, count: 1 }));
|
|
414
|
+
}
|
|
415
|
+
// Mutual connections the app can't draw yet: they're in circles you haven't
|
|
416
|
+
// scanned. Grey dots under the connections who do lead there, each with a faint
|
|
417
|
+
// line in, so the count reads at a glance (GHOST_MAX at most, then "+N").
|
|
418
|
+
const unscanned = Math.max(0, Math.floor(Number(mutuals) || 0) - (p?.waysIn || 0));
|
|
419
|
+
const ghostCount = Math.min(GHOST_MAX, unscanned);
|
|
420
|
+
const ghostStep = cards ? 12 : 11;
|
|
421
|
+
const ghostW = cards ? PILL_W_LAYOUT - 16 : isMobile ? 96 : 120;
|
|
422
|
+
const perRow = Math.max(1, Math.floor(ghostW / ghostStep) + 1);
|
|
423
|
+
const ghostRows = Math.ceil(ghostCount / perRow);
|
|
424
|
+
const ghostH = ghostCount ? GHOST_LABEL + ghostRows * ghostStep : 0;
|
|
425
|
+
const block = Math.max(0, nodes.length - 1) * gap + ghostH;
|
|
426
|
+
const height = Math.max(isMobile ? 190 : cards ? 250 : 220, TOP_PAD * 2 + block + 48);
|
|
427
|
+
const mid = height / 2;
|
|
428
|
+
const top = mid - block / 2;
|
|
429
|
+
const nameBudget = Math.max(4, Math.floor((bx - 10 - (youX + 22)) / 6));
|
|
430
|
+
const bridges = nodes.map((n, j) => {
|
|
431
|
+
const full = n.unresolved ? `can't be named (${n.count})` : n.bridge.name || '';
|
|
432
|
+
return {
|
|
433
|
+
...n, x: bx, y: top + j * gap, r: 6,
|
|
434
|
+
primary: j === 0 && !n.unresolved,
|
|
435
|
+
label: full.length <= nameBudget ? full : fit(n.unresolved ? full : shortName(full), nameBudget),
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
const target = { x: tx, y: mid, r: cards ? 0 : 17, rings: cards ? [] : [32, 50, 70].filter((r) => r <= mid - 4) };
|
|
439
|
+
const links = bridges.map((b) => ({
|
|
440
|
+
key: p?.key, bridgeId: b.id, primary: b.primary, unresolved: b.unresolved, tier: b.bridge?.tier ?? null,
|
|
441
|
+
x1: b.x, y1: b.y, x2: target.x, y2: target.y,
|
|
442
|
+
}));
|
|
443
|
+
const you = { x: youX, y: mid, r: 14 };
|
|
444
|
+
const spokes = bridges.map((b) => ({ id: b.id, x1: you.x, y1: you.y, x2: b.x, y2: b.y }));
|
|
445
|
+
const people = p ? [{ key: p.key, p, x: target.x, y: target.y, r: target.r, rings: target.rings, big: true, label: '' }] : [];
|
|
446
|
+
const gy = top + Math.max(0, nodes.length - 1) * gap + GHOST_LABEL;
|
|
447
|
+
const gx = cards ? bx - PILL_W_LAYOUT + 8 : bx - ghostW;
|
|
448
|
+
const ghosts = Array.from({ length: ghostCount }, (_, i) => ({ x: gx + (i % perRow) * ghostStep, y: gy + Math.floor(i / perRow) * ghostStep }));
|
|
449
|
+
// How bright the person is: the share of your mutual connections with them that the map can draw.
|
|
450
|
+
const drawn = p?.waysIn ? p.waysIn / (p.waysIn + unscanned) : 1;
|
|
451
|
+
return {
|
|
452
|
+
width, height, you, target, people, bridges, links, spokes,
|
|
453
|
+
ghosts, unscanned, ghostLabel: ghostCount ? { x: gx, y: gy - 13 } : null, drawn,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
@@ -5,28 +5,37 @@
|
|
|
5
5
|
|
|
6
6
|
import { rescoreAll } from './rpc.js';
|
|
7
7
|
import { sameFocus, NO_FOCUS, tierMoves } from './sector-focus.js';
|
|
8
|
+
import { rankingFingerprint } from './title-ranking.js';
|
|
8
9
|
|
|
9
10
|
// Each entry: `changed(before, after)` says whether a change matters (by
|
|
10
11
|
// default, any change to the stored value), and `run(db, after, before)` does
|
|
11
12
|
// the work and returns what to tell the page.
|
|
13
|
+
// Rescore everyone and count who changed tier, from the stored tiers before and after.
|
|
14
|
+
function rescoreCountingMoves(db) {
|
|
15
|
+
const rows = () => db.prepare('SELECT id, degree, profile_url, tier FROM linkedin_connections').all();
|
|
16
|
+
try {
|
|
17
|
+
const before = new Map(rows().map((r) => [r.id, r.tier]));
|
|
18
|
+
const { scored } = rescoreAll();
|
|
19
|
+
const { people, up, down } = tierMoves(rows(), (r) => before.get(r.id), (r) => r.tier, { examples: 0 });
|
|
20
|
+
return { scored, people, moved: up + down, up, down };
|
|
21
|
+
} catch (err) {
|
|
22
|
+
throw new Error(`Saved, but rescoring your network failed (${err.message}). It will try again the next time the map loads.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
const EFFECTS = {
|
|
27
|
+
// Title points are part of everyone's score, so a new ranking rescores everyone, as a new tier scale does.
|
|
28
|
+
titleRanking: {
|
|
29
|
+
changed: (before, after) => rankingFingerprint(before) !== rankingFingerprint(after),
|
|
30
|
+
run: (db) => rescoreCountingMoves(db),
|
|
31
|
+
},
|
|
13
32
|
tierScale: {
|
|
14
33
|
// Tiers are graded when everyone is scored, so a new scale rescores
|
|
15
34
|
// everyone. The answer counts who changed tier, from the stored tiers
|
|
16
35
|
// before and after, with the sector focus's own counter (tierMoves): a
|
|
17
36
|
// person reachable through two bridges is two rows but one person, counted
|
|
18
37
|
// once at their closest degree.
|
|
19
|
-
run(db)
|
|
20
|
-
const rows = () => db.prepare('SELECT id, degree, profile_url, tier FROM linkedin_connections').all();
|
|
21
|
-
try {
|
|
22
|
-
const before = new Map(rows().map((r) => [r.id, r.tier]));
|
|
23
|
-
const { scored } = rescoreAll();
|
|
24
|
-
const { people, up, down } = tierMoves(rows(), (r) => before.get(r.id), (r) => r.tier, { examples: 0 });
|
|
25
|
-
return { scored, people, moved: up + down, up, down };
|
|
26
|
-
} catch (err) {
|
|
27
|
-
throw new Error(`Saved, but rescoring your network failed (${err.message}). It will try again the next time the map loads.`);
|
|
28
|
-
}
|
|
29
|
-
},
|
|
38
|
+
run: (db) => rescoreCountingMoves(db),
|
|
30
39
|
},
|
|
31
40
|
sectorFocus: {
|
|
32
41
|
// Only a change to what scoring uses: a strength with no sectors picked
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { SECTOR_FOCUS_SETTING, FIELD_ASKED_SETTING } from './sector-focus.js';
|
|
14
14
|
import { TIER_SCALE_SETTING } from './scoring.js';
|
|
15
|
+
import { TITLE_RANKING_SETTING } from './title-ranking.js';
|
|
15
16
|
|
|
16
17
|
export const SETTINGS = {
|
|
17
18
|
// Scores → Your sector: industries whose companies count for more (lib/sector-focus.js).
|
|
@@ -20,6 +21,8 @@ export const SETTINGS = {
|
|
|
20
21
|
fieldAsked: FIELD_ASKED_SETTING,
|
|
21
22
|
// Scores → Tiers: graded on your network's curve, or on the fixed scale (lib/scoring.js).
|
|
22
23
|
tierScale: TIER_SCALE_SETTING,
|
|
24
|
+
// Scores → Titles: how much each kind of title counts, and roles you're looking for (lib/title-ranking.js).
|
|
25
|
+
titleRanking: TITLE_RANKING_SETTING,
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
const KEY = 'settings';
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// The Filters panel's grid: five tiers down, six degrees across, the same in
|
|
2
|
+
// Network Circle and in Degrees.
|
|
3
|
+
//
|
|
4
|
+
// Blake, 2026-10-02: "keeping the tiers as is but enlarging them and having 6
|
|
5
|
+
// dots to let the user tap on to toggle or not if they want specific degrees
|
|
6
|
+
// showing for whatever tier … it will be universal for both".
|
|
7
|
+
//
|
|
8
|
+
// A grid is { hidden: [tiers switched off], degrees: { S: [1, 2], … } }: a tier
|
|
9
|
+
// row switches on and off as it always did, and remembers which degrees it
|
|
10
|
+
// shows while it's off. Counts are { S: { 1: n, 2: n, … }, … }: how many people
|
|
11
|
+
// there are at each tier and degree, so a dot with nobody behind it can't be
|
|
12
|
+
// tapped, and the last people on screen can't be switched off.
|
|
13
|
+
|
|
14
|
+
export const TIERS = ['S', 'A', 'B', 'C', 'D'];
|
|
15
|
+
export const GRID_DEGREES = [1, 2, 3, 4, 5, 6];
|
|
16
|
+
|
|
17
|
+
const tierOf = (tier) => (TIERS.includes(tier) ? tier : 'D'); // no tier: with D, as the views draw it
|
|
18
|
+
|
|
19
|
+
export const makeGrid = (degrees = GRID_DEGREES) => ({
|
|
20
|
+
hidden: [],
|
|
21
|
+
degrees: Object.fromEntries(TIERS.map((t) => [t, degrees.slice()])),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** Network Circle starts with your own connections; you pick the rest. */
|
|
25
|
+
export const NETWORK_GRID = Object.freeze(makeGrid([1]));
|
|
26
|
+
/** Degrees starts with everything it has. */
|
|
27
|
+
export const DEGREES_GRID = Object.freeze(makeGrid());
|
|
28
|
+
|
|
29
|
+
/** Is someone of this tier, at this degree, on screen? */
|
|
30
|
+
export function shows(grid, tier, degree) {
|
|
31
|
+
const t = tierOf(tier);
|
|
32
|
+
return !grid.hidden.includes(t) && grid.degrees[t].includes(degree);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const countAt = (counts, tier, degree) => counts?.[tier]?.[degree] || 0;
|
|
36
|
+
|
|
37
|
+
/** How many people one tier is showing. */
|
|
38
|
+
export function showingIn(grid, counts, tier) {
|
|
39
|
+
if (grid.hidden.includes(tier)) return 0;
|
|
40
|
+
return grid.degrees[tier].reduce((n, d) => n + countAt(counts, tier, d), 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** How many people the whole grid is showing. */
|
|
44
|
+
export const showing = (grid, counts) => TIERS.reduce((n, t) => n + showingIn(grid, counts, t), 0);
|
|
45
|
+
|
|
46
|
+
// A change that would leave nobody on screen isn't made.
|
|
47
|
+
const keep = (grid, next, counts) => (counts && showing(next, counts) === 0 ? grid : next);
|
|
48
|
+
|
|
49
|
+
/** One dot: that degree, for that tier. Tapping a dot in a tier that's off switches the tier on with it. */
|
|
50
|
+
export function toggleCell(grid, tier, degree, counts) {
|
|
51
|
+
const has = grid.degrees[tier].includes(degree);
|
|
52
|
+
const off = grid.hidden.includes(tier);
|
|
53
|
+
const degrees = off || !has
|
|
54
|
+
? [...new Set([...grid.degrees[tier], degree])].sort((a, b) => a - b)
|
|
55
|
+
: grid.degrees[tier].filter((d) => d !== degree);
|
|
56
|
+
return keep(grid, {
|
|
57
|
+
hidden: off ? grid.hidden.filter((t) => t !== tier) : grid.hidden,
|
|
58
|
+
degrees: { ...grid.degrees, [tier]: degrees },
|
|
59
|
+
}, counts);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A whole tier, off or back on with the degrees it had. */
|
|
63
|
+
export function toggleTier(grid, tier, counts) {
|
|
64
|
+
if (!grid.hidden.includes(tier)) return keep(grid, { ...grid, hidden: [...grid.hidden, tier] }, counts);
|
|
65
|
+
// Back on with nothing to show would look broken: give it what the others show.
|
|
66
|
+
const own = grid.degrees[tier];
|
|
67
|
+
const others = [...new Set(TIERS.flatMap((t) => (t === tier ? [] : grid.degrees[t])))].sort((a, b) => a - b);
|
|
68
|
+
return {
|
|
69
|
+
hidden: grid.hidden.filter((t) => t !== tier),
|
|
70
|
+
degrees: own.length ? grid.degrees : { ...grid.degrees, [tier]: others.length ? others : [1] },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** A whole degree: off if every tier with people there is showing it, otherwise on for all of them. */
|
|
75
|
+
export function toggleDegree(grid, degree, counts) {
|
|
76
|
+
const tiers = TIERS.filter((t) => !grid.hidden.includes(t) && (!counts || countAt(counts, t, degree) > 0));
|
|
77
|
+
if (!tiers.length) return grid;
|
|
78
|
+
const allOn = tiers.every((t) => grid.degrees[t].includes(degree));
|
|
79
|
+
const degrees = { ...grid.degrees };
|
|
80
|
+
for (const t of tiers) {
|
|
81
|
+
degrees[t] = allOn ? degrees[t].filter((d) => d !== degree) : [...new Set([...degrees[t], degree])].sort((a, b) => a - b);
|
|
82
|
+
}
|
|
83
|
+
return keep(grid, { ...grid, degrees }, counts);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Every tier back on, each with the degrees it had. */
|
|
87
|
+
export const showAllTiers = (grid) => (grid.hidden.length ? { ...grid, hidden: [] } : grid);
|
|
88
|
+
|
|
89
|
+
/** People by tier and degree, for the dots: `byDegree` is { 1: rows, 2: rows, … }. */
|
|
90
|
+
export function gridCounts(byDegree = {}) {
|
|
91
|
+
const counts = Object.fromEntries(TIERS.map((t) => [t, {}]));
|
|
92
|
+
for (const [degree, rows] of Object.entries(byDegree)) {
|
|
93
|
+
for (const row of rows || []) {
|
|
94
|
+
const t = tierOf(row?.tier);
|
|
95
|
+
counts[t][degree] = (counts[t][degree] || 0) + 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return counts;
|
|
99
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Settings → Scores → Titles: how much each kind of title counts, your way
|
|
2
|
+
// (Blake, 2026-10-02: "let the user change the rankings of titles as well …
|
|
3
|
+
// incase they value certain people or is using this for outreaching new
|
|
4
|
+
// founders", and people who "find more in that role" for outbound or hiring).
|
|
5
|
+
//
|
|
6
|
+
// Two parts, both in title points (the 0–10 a title is worth before the
|
|
7
|
+
// company weighs it, lib/scoring.js LEVELS):
|
|
8
|
+
// points a level's points, yours: Owner / Entrepreneur at 10, say, to put
|
|
9
|
+
// founders of small companies level with CEOs
|
|
10
|
+
// roles words in a title, and what anyone whose title has them counts:
|
|
11
|
+
// "account executive" at 10 puts every AE you can reach in S
|
|
12
|
+
// Nothing set scores exactly as before. The model applies it (scorePerson);
|
|
13
|
+
// this file is the setting and its presets.
|
|
14
|
+
|
|
15
|
+
import { LEVELS } from './scoring.js';
|
|
16
|
+
|
|
17
|
+
/** The ladder shown in Settings, top to bottom (the main kinds of title). */
|
|
18
|
+
export const RANKED_LEVELS = ['csuite', 'owner', 'vp', 'director', 'manager', 'senior', 'ic', 'unknown', 'intern', 'student'];
|
|
19
|
+
export const NO_RANKING = Object.freeze({ points: Object.freeze({}), roles: Object.freeze([]) });
|
|
20
|
+
export const MAX_ROLES = 20;
|
|
21
|
+
const MAX_ROLE_TEXT = 60;
|
|
22
|
+
|
|
23
|
+
const clampPoints = (v) => {
|
|
24
|
+
const n = Number(v);
|
|
25
|
+
if (!Number.isFinite(n) || n < 0 || n > 10) throw new Error('Title points go from 0 to 10.');
|
|
26
|
+
return Math.round(n * 2) / 2;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const TITLE_RANKING_SETTING = {
|
|
30
|
+
default: NO_RANKING,
|
|
31
|
+
parse(value) {
|
|
32
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('A title ranking is { points, roles }.');
|
|
33
|
+
const points = {};
|
|
34
|
+
for (const [key, v] of Object.entries(value.points || {})) {
|
|
35
|
+
if (!Object.hasOwn(LEVELS, key)) throw new Error(`There is no kind of title called '${key}'.`);
|
|
36
|
+
const p = clampPoints(v);
|
|
37
|
+
if (p !== LEVELS[key].points) points[key] = p; // the default needn't be stored
|
|
38
|
+
}
|
|
39
|
+
const roles = [];
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
for (const r of Array.isArray(value.roles) ? value.roles : []) {
|
|
42
|
+
const text = String(r?.text ?? '').trim().toLowerCase().replace(/\s+/g, ' ');
|
|
43
|
+
if (text.length < 2) throw new Error('A role needs at least two letters.');
|
|
44
|
+
if (text.length > MAX_ROLE_TEXT) throw new Error(`Keep a role under ${MAX_ROLE_TEXT} letters.`);
|
|
45
|
+
if (seen.has(text)) continue;
|
|
46
|
+
seen.add(text);
|
|
47
|
+
roles.push({ text, points: clampPoints(r.points) });
|
|
48
|
+
}
|
|
49
|
+
if (roles.length > MAX_ROLES) throw new Error(`Up to ${MAX_ROLES} roles.`);
|
|
50
|
+
return { points, roles };
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** The same ranking always gives the same text, so the stored scores can say which they were made with. */
|
|
55
|
+
export function rankingFingerprint(r = NO_RANKING) {
|
|
56
|
+
const points = Object.keys(r.points || {}).sort().map((k) => `${k}=${r.points[k]}`).join(',');
|
|
57
|
+
const roles = (r.roles || []).map((x) => `${x.text}=${x.points}`).sort().join(',');
|
|
58
|
+
return points || roles ? `${points}|${roles}` : 'none';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const isDefaultRanking = (r) => rankingFingerprint(r) === 'none';
|
|
62
|
+
|
|
63
|
+
/** Starting points for the common reasons to change it. Each replaces the whole ranking; you can adjust from there. */
|
|
64
|
+
export const RANKING_PRESETS = [
|
|
65
|
+
{ key: 'default', label: 'As the app ranks them', ranking: NO_RANKING, note: 'C-suite and founders first, then VPs, directors, managers.' },
|
|
66
|
+
{
|
|
67
|
+
key: 'founders', label: 'Founders first',
|
|
68
|
+
note: 'Owners and entrepreneurs count like CEOs, and anyone who says founder counts as much: for meeting founders, or selling to them.',
|
|
69
|
+
ranking: { points: { owner: 10 }, roles: [{ text: 'founder', points: 10 }, { text: 'co-founder', points: 10 }] },
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: 'investors', label: 'Investors',
|
|
73
|
+
note: 'Anyone who invests for a living rises to the top: for raising money.',
|
|
74
|
+
ranking: { points: {}, roles: ['investor', 'venture', 'angel', 'general partner', 'managing partner', 'principal at', 'vc'].map((text) => ({ text, points: 10 })) },
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: 'role', label: 'A role I’m looking for',
|
|
78
|
+
note: 'For outbound or hiring: add the role below, and everyone in it rises, whatever their level.',
|
|
79
|
+
ranking: null, // keeps what you have and opens the role box
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export { titleHas } from './scoring.js';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "six-degrees",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "six-degrees",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.5.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"os": [
|
|
12
12
|
"darwin",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "six-degrees",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Map your professional network as a galaxy — leverage tiers, bridges into new circles, and the shortest path to anyone. Runs locally on your own data.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Blake Burford",
|