six-degrees 0.4.3 → 0.4.5
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 +3 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +41 -13
- package/.next/standalone/.next/routes-manifest.json +12 -0
- 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 +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 +18 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +18 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -13
- 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.nft.json +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.nft.json +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.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.nft.json +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.nft.json +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.nft.json +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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/social/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/social/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/social/route.js +8 -0
- package/.next/standalone/.next/server/app/api/social/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/social/route_client-reference-manifest.js +3 -0
- 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.nft.json +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 +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 +22 -21
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +20 -19
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +20 -19
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/launch/page.js +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 +22 -21
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/page.js +1 -1
- 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 +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 +22 -21
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/profile/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/queue/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/scores/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/setup/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +22 -21
- 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 +16 -15
- package/.next/standalone/.next/server/app/social/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/social/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/social/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/social/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/social/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/social/page.js +13 -0
- package/.next/standalone/.next/server/app/social/page.js.map +5 -0
- package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/social.html +1 -0
- package/.next/standalone/.next/server/app/social.meta +26 -0
- package/.next/standalone/.next/server/app/social.rsc +26 -0
- package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +26 -0
- package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +30 -0
- package/.next/standalone/.next/server/app-paths-manifest.json +3 -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]__17kn1mk._.js +8 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0uag-k5._.js → [root-of-the-server]__1dmc7-t._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1xh07bp._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_social_route_actions_1_3c6qn.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16hs8kw._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1feicqq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jeztd-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_005q9n1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0ezj4e0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0hw_qnj._.js → _0wxbq78._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1cyxocj._.js → _1ap1tu_._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1mj739k._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_217nddo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_social_page_actions_176i1kl.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_0i3fw_m._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +17 -4
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +3 -0
- 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/04a3zockahetx.js +1 -0
- package/.next/standalone/.next/static/chunks/{2wwmljnf7yn_5.js → 0a5iadnmt9u9k.js} +1 -1
- package/.next/standalone/.next/static/chunks/0g_p4p7oci18u.js +1 -0
- package/.next/standalone/.next/static/chunks/0y1yms5kez2f3.js +1 -0
- package/.next/standalone/.next/static/chunks/14m1zu-8warte.js +1 -0
- package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +1 -0
- package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +1 -0
- package/.next/standalone/.next/static/chunks/{1zwjy1mmpkdrg.js → 24m745q_85u34.js} +2 -2
- package/.next/standalone/.next/static/chunks/2_zu7rmb8flzl.js +2 -0
- package/.next/standalone/.next/static/chunks/38wwtni5e1ec1.js +31 -0
- package/.next/standalone/.next/static/chunks/3p_pwu5gx2i9h.js +1 -0
- package/.next/{static/chunks/0_3ubivay59bq.js → standalone/.next/static/chunks/3zw8bv7-mc4n6.js} +1 -1
- package/.next/standalone/CHANGELOG.md +58 -0
- package/.next/standalone/app/api/network/route.js +11 -0
- package/.next/standalone/app/api/scraper/route.js +2 -0
- package/.next/standalone/app/api/social/route.js +96 -0
- package/.next/standalone/app/components/AutoScanButton.js +41 -0
- package/.next/standalone/app/components/ChainView.js +7 -0
- package/.next/standalone/app/components/FilterPanel.js +6 -0
- package/.next/standalone/app/components/ForceGraph.js +257 -39
- package/.next/standalone/app/components/GalaxyLab.js +275 -0
- package/.next/standalone/app/components/ScanStatusBar.js +22 -8
- package/.next/standalone/app/components/SeparationView.js +9 -1
- package/.next/standalone/app/components/Sidebar.js +88 -26
- package/.next/standalone/app/components/SocialAutoSync.js +34 -0
- package/.next/standalone/app/components/settings/NetworkHealthSection.js +85 -0
- package/.next/standalone/app/layout.js +3 -0
- package/.next/standalone/app/page.js +29 -0
- package/.next/standalone/app/scores/page.js +3 -0
- package/.next/standalone/app/setup/page.js +1 -1
- package/.next/standalone/app/social/page.js +281 -0
- package/.next/standalone/lib/experimental-client.js +38 -0
- package/.next/standalone/lib/galaxy-export.js +152 -0
- package/.next/standalone/lib/galaxy-lab.js +296 -0
- package/.next/standalone/lib/insights.js +54 -0
- package/.next/standalone/lib/linkedin-export.js +156 -0
- package/.next/standalone/lib/network-health.js +42 -0
- package/.next/standalone/lib/network.js +1 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/scrape.py +118 -2
- package/.next/static/chunks/04a3zockahetx.js +1 -0
- package/.next/static/chunks/{2wwmljnf7yn_5.js → 0a5iadnmt9u9k.js} +1 -1
- package/.next/static/chunks/0g_p4p7oci18u.js +1 -0
- package/.next/static/chunks/0y1yms5kez2f3.js +1 -0
- package/.next/static/chunks/14m1zu-8warte.js +1 -0
- package/.next/static/chunks/1e1n0kkz-psdb.js +1 -0
- package/.next/static/chunks/236f0jtp68j_h.js +1 -0
- package/.next/static/chunks/{1zwjy1mmpkdrg.js → 24m745q_85u34.js} +2 -2
- package/.next/static/chunks/2_zu7rmb8flzl.js +2 -0
- package/.next/static/chunks/38wwtni5e1ec1.js +31 -0
- package/.next/static/chunks/3p_pwu5gx2i9h.js +1 -0
- package/.next/{standalone/.next/static/chunks/0_3ubivay59bq.js → static/chunks/3zw8bv7-mc4n6.js} +1 -1
- package/CHANGELOG.md +58 -0
- package/lib/experimental-client.js +38 -0
- package/lib/galaxy-export.js +152 -0
- package/lib/galaxy-lab.js +296 -0
- package/lib/insights.js +54 -0
- package/lib/linkedin-export.js +156 -0
- package/lib/network-health.js +42 -0
- package/lib/network.js +1 -0
- package/package.json +1 -1
- package/scripts/scrape.py +118 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d3vjsq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m4vk0b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_012mop3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1163x49._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1av11ws._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1ig7em7._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1vpfyd-._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +0 -3
- package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +0 -1
- package/.next/standalone/.next/static/chunks/0f4ugd6v3ph0-.js +0 -1
- package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +0 -1
- package/.next/standalone/.next/static/chunks/10ogurbcuf3nq.js +0 -1
- package/.next/standalone/.next/static/chunks/1489t7jrjojk_.js +0 -1
- package/.next/standalone/.next/static/chunks/1zunq4tk-u3ng.js +0 -1
- package/.next/standalone/.next/static/chunks/2p5yt1o9tnxjn.js +0 -2
- package/.next/standalone/.next/static/chunks/2pyo4j84prgp6.js +0 -18
- package/.next/standalone/.next/static/chunks/3kllnkb3hof1s.js +0 -1
- package/.next/static/chunks/0cuuo2jwc9h94.js +0 -1
- package/.next/static/chunks/0f4ugd6v3ph0-.js +0 -1
- package/.next/static/chunks/0l1568qff0efj.js +0 -1
- package/.next/static/chunks/10ogurbcuf3nq.js +0 -1
- package/.next/static/chunks/1489t7jrjojk_.js +0 -1
- package/.next/static/chunks/1zunq4tk-u3ng.js +0 -1
- package/.next/static/chunks/2p5yt1o9tnxjn.js +0 -2
- package/.next/static/chunks/2pyo4j84prgp6.js +0 -18
- package/.next/static/chunks/3kllnkb3hof1s.js +0 -1
- /package/.next/standalone/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_ssgManifest.js +0 -0
- /package/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_buildManifest.js +0 -0
- /package/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{814ehZBeFO2rUKle7mzX- → 6LINKa6nyesvIEUnLDSck}/_ssgManifest.js +0 -0
package/.next/{standalone/.next/static/chunks/0_3ubivay59bq.js → static/chunks/3zw8bv7-mc4n6.js}
RENAMED
|
@@ -30,4 +30,4 @@
|
|
|
30
30
|
font-weight:700;font-size:13px;cursor:pointer;border:none;margin-bottom:16px}</style></head>
|
|
31
31
|
<body><h3 style="color:${_[e]}">${e}-Tier (${n.length})</h3>
|
|
32
32
|
<button class="oa" onclick="document.querySelectorAll('.btn').forEach((a,i)=>setTimeout(()=>a.click(),i*300))">
|
|
33
|
-
Open All</button>${n.map(e=>'<a class="btn" href="'+e.profile_url+'" target="_blank">'+e.name+" · "+parseFloat(e.power_score||0).toFixed(1)+"</a>").join("")}</body></html>`;window.open(URL.createObjectURL(new Blob([t],{type:"text/html"})),"_blank")},style:{marginLeft:"auto",padding:"2px 8px",borderRadius:3,border:"none",cursor:"pointer",background:"#0077B5",color:"#fff",fontSize:8,fontWeight:700},children:["Open All ",e]})]}),n.map(e=>(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"5px 8px 5px 16px",borderBottom:"1px solid rgba(255,255,255,0.02)",background:b.has(e.id)?"rgba(255,215,0,0.05)":"transparent",cursor:"pointer"},onMouseEnter:t=>{b.has(e.id)||(t.currentTarget.style.background="rgba(255,255,255,0.04)")},onMouseLeave:t=>{b.has(e.id)||(t.currentTarget.style.background="transparent")},children:[(0,t.jsx)("input",{type:"checkbox",checked:b.has(e.id),onChange:()=>X(e.id),onClick:e=>e.stopPropagation(),style:{cursor:"pointer",flexShrink:0}}),(0,c.localPhoto)(e.profile_image_url)?(0,t.jsx)("img",{src:(0,c.localPhoto)(e.profile_image_url),alt:"",style:{width:28,height:28,borderRadius:"50%",objectFit:"cover",flexShrink:0,border:`1.5px solid ${_[e.tier]||"#555"}`},onError:e=>{e.target.style.display="none"}}):(0,t.jsx)("div",{style:{width:28,height:28,borderRadius:"50%",flexShrink:0,fontSize:10,fontWeight:700,background:_[e.tier]||"#555",color:"S"===e.tier?"#000":"#fff",display:"flex",alignItems:"center",justifyContent:"center"},children:e.name?.charAt(0)}),(0,t.jsxs)("div",{style:{flex:1,minWidth:0},onClick:()=>X(e.id),children:[(0,t.jsx)("div",{style:{fontSize:11,fontWeight:600,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),(0,t.jsx)("div",{style:{fontSize:8,color:"#666",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.headline?.substring(0,45)})]}),(0,t.jsx)("span",{style:{fontSize:9,fontWeight:700,color:_[e.tier],flexShrink:0},children:parseFloat(e.power_score||0).toFixed(1)}),q.has(e.id)?(0,t.jsx)("span",{style:{padding:"3px 6px",borderRadius:3,fontSize:8,fontWeight:700,background:"rgba(255,107,53,0.15)",color:"#FF6B35",flexShrink:0},children:"Sent"}):(0,t.jsx)("a",{href:e.profile_url,target:"_blank",rel:"noopener noreferrer",onClick:t=>{t.stopPropagation(),(0,j.markRequested)(e,{bridgeId:e.source_connection_id}).catch(()=>{})},style:{padding:"3px 6px",borderRadius:3,fontSize:8,fontWeight:700,background:"#0077B5",color:"#fff",textDecoration:"none",flexShrink:0},children:"Add"})]},e.id))]},e)}):r.map(e=>(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"6px 8px",borderBottom:"1px solid rgba(255,255,255,0.03)",background:b.has(e.id)?"rgba(255,215,0,0.05)":"transparent",cursor:"pointer"},onMouseEnter:t=>{b.has(e.id)||(t.currentTarget.style.background="rgba(255,255,255,0.04)")},onMouseLeave:t=>{b.has(e.id)||(t.currentTarget.style.background="transparent")},children:[(0,t.jsx)("input",{type:"checkbox",checked:b.has(e.id),onChange:()=>X(e.id),onClick:e=>e.stopPropagation(),style:{cursor:"pointer",flexShrink:0}}),(0,c.localPhoto)(e.profile_image_url)?(0,t.jsx)("img",{src:(0,c.localPhoto)(e.profile_image_url),alt:"",style:{width:32,height:32,borderRadius:"50%",objectFit:"cover",flexShrink:0,border:`1.5px solid ${_[e.tier]||"#555"}`},onError:e=>{e.target.style.display="none",e.target.nextSibling.style.display="flex"}}):null,(0,t.jsx)("div",{style:{width:32,height:32,borderRadius:"50%",flexShrink:0,fontSize:12,fontWeight:700,background:_[e.tier]||"#555",color:"S"===e.tier?"#000":"#fff",display:(0,c.localPhoto)(e.profile_image_url)?"none":"flex",alignItems:"center",justifyContent:"center"},children:e.name?.charAt(0)}),(0,t.jsxs)("div",{style:{flex:1,minWidth:0},onClick:()=>X(e.id),children:[(0,t.jsx)("div",{style:{fontSize:12,fontWeight:600,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),(0,t.jsx)("div",{style:{fontSize:9,color:"#666",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.headline?.substring(0,50)})]}),e.bridge&&(0,t.jsxs)("span",{style:{fontSize:8,color:"#555",flexShrink:0,maxWidth:60,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:["via ",e.bridge.name?.split(" ")[0]]}),(0,t.jsx)("div",{style:{textAlign:"right",flexShrink:0,minWidth:32},children:(0,t.jsx)("div",{style:{fontSize:10,fontWeight:700,color:_[e.tier]},children:parseFloat(e.power_score).toFixed(1)})}),q.has(e.id)?(0,t.jsx)("span",{style:{padding:"4px 8px",borderRadius:4,fontSize:9,fontWeight:700,background:"rgba(255,107,53,0.15)",color:"#FF6B35",flexShrink:0},children:"⏳ Sent"}):(0,t.jsx)("a",{href:e.profile_url,target:"_blank",rel:"noopener noreferrer",onClick:t=>{t.stopPropagation(),(0,j.markRequested)(e,{bridgeId:e.source_connection_id}).catch(()=>{})},style:{padding:"4px 8px",borderRadius:4,fontSize:9,fontWeight:700,background:"#0077B5",color:"#fff",textDecoration:"none",flexShrink:0},children:"Add"})]},e.id))]},e))]})]})})]})}e.s(["default",0,function(){return(0,t.jsx)(y.default,{children:(0,t.jsx)(F,{})})}],77305)},19821,e=>{"use strict";async function t(e){let t=e?`/api/network?userId=${encodeURIComponent(e)}`:"/api/network",r=await fetch(t);if(!r.ok)throw Error(`Failed to load network (${r.status})`);let n=await r.json();return{degree1:n.degree1||[],degree2:n.degree2||[],degree3:n.degree3||[]}}e.s(["loadNetwork",0,t])},36774,e=>{"use strict";let t=/^[A-Za-z0-9_-]+\.(webp|jpg|jpeg|png)$/,r="/avatars/";e.s(["localPhoto",0,function(e){return"string"==typeof e&&e.startsWith(r)&&t.test(e.slice(r.length))?e:null}])},48235,7214,e=>{"use strict";var t=e.i(36774);let r={S:0,A:1,B:2,C:3,D:4};function n(e){let t=parseFloat(e?.power_score);return Number.isFinite(t)?t:0}function i(e){return Math.round(10*n(e))/10}function o(e){return String(e?.profile_url||"").split(/[?#]/)[0].replace(/\/+$/,"")||`id:${e?.id}`}let l=(e,t)=>String(e??"").localeCompare(String(t??""),void 0,{numeric:!0});function s(e,t){return(r[e.tier]??9)-(r[t.tier]??9)||n(t)-n(e)||String(e.name||"").localeCompare(String(t.name||""))||l(e.id,t.id)}function d(e,t){return e.bridge&&t.bridge?s(e.bridge,t.bridge):e.bridge?-1:t.bridge?1:l(e.id,t.id)}function a(e,t){return t.score-e.score||t.waysIn-e.waysIn||d(e.routes[0]||c,t.routes[0]||c)||String(e.person.name||"").localeCompare(String(t.person.name||""))||(e.key<t.key?-1:+(e.key>t.key))}let c={id:null,bridge:null};function g(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean);return t.length<2?t[0]||"":`${t[0]} ${t[t.length-1].charAt(0)}.`}function p(e,t){let r=String(e||"");return t<=1?r?"…":"":r.length>t?`${r.slice(0,t-1)}…`:r}function h(e,t){return e?t.scanned.has(e.id)||e.profile_url&&t.read?.has(o(e))?"scanned":e.profile_url&&t.hidden.has(o(e))?"hidden":"todo":"todo"}function f(e,t){if(!e||1!==e.degree||null==e.unlocked_from_bridge_id)return null;let r=h(e,t);return"todo"===r?"ready":r}e.s(["TIERS",0,["S","A","B","C","D"],"compareBridges",0,s,"compareWaysIn",0,function(e,t){return t.waysIn-e.waysIn||a(e,t)},"keyFor",0,o,"routeIndex",0,function(e=[]){let t=new Map;for(let r of e||[]){let e=o(r),n=t.get(e);n||(n=new Set,t.set(e,n)),n.add(r.source_connection_id??null)}return t},"routesFor",0,function(e,t,r){return e?[...t?.get(o(e))??(null!=e.source_connection_id?new Set([e.source_connection_id]):new Set)].map(e=>({id:e,bridge:r?.get(e)??null})).sort(d):[]},"score",0,n,"score1",0,i,"separationPeople",0,function(e=[],r=[]){let n=new Map;for(let e of r||[])n.set(e.id,e);let s=new Map,c=new Map;for(let r of e||[]){let e=o(r);c.set(r.id,e);let n=s.get(e);n?function(e,r){let n=i(e)-i(r);if(n)return n>0;let o=!!(0,t.localPhoto)(e.profile_image_url);return!!(0,t.localPhoto)(r.profile_image_url)!==o?o:0>l(e.id,r.id)}(r,n.person)&&(n.person=r):(n={key:e,person:r,rowIds:[],routeIds:new Set},s.set(e,n)),n.rowIds.push(r.id),n.routeIds.add(r.source_connection_id??null)}let g={people:s.size,byTier:{S:0,A:0,B:0,C:0,D:0},multi:0,bridges:0,unresolvedIds:0,peopleOnlyUnresolved:0,onlyUnresolvedVia:0},p=new Set,h=new Set,f=new Set,u=[];for(let e of s.values()){let t=[...e.routeIds].map(e=>({id:e,bridge:n.get(e)??null}));t.sort(d);let r=t.filter(e=>e.bridge);for(let e of t)(e.bridge?p:h).add(e.id);if(!r.length)for(let e of(g.peopleOnlyUnresolved++,t))f.add(e.id);let{person:o}=e,l=o.tier;void 0!==g.byTier[l]&&g.byTier[l]++,t.length>1&&g.multi++,u.push({key:e.key,person:o,score:i(o),tier:l,routes:t,waysIn:t.length,rowIds:e.rowIds,haystack:[o.name,o.headline,o.company,o.role,...r.map(e=>e.bridge.name)].filter(Boolean).join("\n").toLowerCase(),rank:0,tied:!1})}return g.bridges=p.size,g.unresolvedIds=h.size,g.onlyUnresolvedVia=f.size,u.sort(a),function(e){for(let t=0;t<e.length;t++){let r=e[t],n=e[t-1],i=n&&n.score===r.score&&n.waysIn===r.waysIn;r.rank=i?n.rank:t+1,i&&(r.tied=!0,n.tied=!0)}}(u),{people:u,rowToKey:c,summary:g}},"shortName",0,g,"summitLayout",0,function(e=[],t=800,r=!1){let n=r?36:26,i=Math.round(.42*t),o=Math.round(.58*t),l=e.map((e,r)=>{var i;return{key:e.key,p:e,x:o,y:16+r*n,r:(i=e.waysIn)>=4?8.5:3===i?7:2===i?5.5:4,label:p(`#${e.rank}${e.tied?"=":""} ${e.person.name||""}`,Math.floor((t-o-60)/6.2))}}),d=new Map,a="\0unresolved";for(let e of l)for(let t of e.p.routes){let r=t.bridge?t.id:a,n=d.get(r);n||(n={id:t.bridge?t.id:null,bridge:t.bridge,unresolved:!t.bridge,ys:[],count:0},d.set(r,n)),n.count++,n.ys.push(e.y)}let c=[...d.values()].map(e=>({...e,bary:e.ys.reduce((e,t)=>e+t,0)/e.ys.length}));c.sort((e,t)=>e.bary-t.bary||e.unresolved-t.unresolved||(e.bridge&&t.bridge?s(e.bridge,t.bridge):0));let h=l.length?l[l.length-1].y:16,f=c.length>1?Math.max(18,(h-16)/(c.length-1)):0,u=Math.max(4,Math.floor((i-10-42)/6));c.forEach((e,t)=>{if(e.x=i,e.y=1===c.length?(16+h)/2:16+t*f,e.r=5,e.unresolved)e.label=p(`can't be named (${e.count})`,u);else{let t=e.bridge.name||"";e.label=t.length<=u?t:p(g(t),u)}delete e.ys});let x=new Map(c.map(e=>[e.unresolved?a:e.id,e])),b=[];for(let e of l){let t=new Set;e.p.routes.forEach((r,n)=>{let i=r.bridge?r.id:a;if(t.has(i))return;t.add(i);let o=x.get(i);b.push({key:e.key,bridgeId:o.id,primary:0===n&&!o.unresolved,unresolved:o.unresolved,tier:o.bridge?.tier??null,x1:o.x,y1:o.y,x2:e.x,y2:e.y})})}let y=Math.max(h,c.length?c[c.length-1].y:0),m=l.length?Math.ceil(y+16):0,S={x:20,y:l.length?(16+Math.max(h,y))/2:16,r:14},k=c.map(e=>({id:e.id,x1:S.x,y1:S.y,x2:e.x,y2:e.y}));return{width:t,height:m,you:S,people:l,bridges:c,links:b,spokes:k}}],7214),e.s(["circleScanCost",0,function(e=100){let t=Math.max(1,Math.min(100,Math.round(e)||100));return{profileViews:1,searches:t,minutes:t>=100?55:Math.max(5,Math.round(.55*t))}},"circleState",0,h,"reachIndex",0,function(e=[],t=[],r={}){let n=new Set;for(let e of t||[])e?.source_connection_id!=null&&n.add(e.source_connection_id);let i=new Set;for(let e of r?.skips||[])e?.profileUrl&&i.add(o({profile_url:e.profileUrl}));let l=new Set;for(let e of r?.read||[])e&&l.add(o({profile_url:e}));let s=new Map;for(let[e,t]of Object.entries(r?.lists||{}))e&&t&&s.set(o({profile_url:e}),t);let d=new Map;for(let t of e||[]){let e=t?.unlocked_from_bridge_id;if(null==e||1!==t.degree)continue;let r=d.get(e);r||d.set(e,r=[]),r.push(t)}return{scanned:n,read:l,hidden:i,reachedFrom:d,lists:s}},"reachState",0,f,"readyByCircle",0,function(e){let t=new Map;for(let[r,n]of e.reachedFrom){let i=n.filter(t=>"ready"===f(t,e)).length;i&&t.set(r,i)}return t},"readyToScan",0,function(e,t){return(e||[]).filter(e=>"ready"===f(e,t)).sort(s)},"scanBars",0,function(e,t){let r=h(e,t),n=e?.profile_url?t.lists?.get(o(e)):null;return"hidden"===r?null:n?n.more?n.total?Math.max(1,Math.min(4,Math.round(5*Math.min(1,10*n.pages/n.total)))):2:5:"scanned"===r?2:null}],48235)},5258,e=>{"use strict";e.s(["TIER_COLORS",0,{S:"#f6c344",A:"#a78bfa",B:"#60a5fa",C:"#94a3b8",D:"#6b7280"},"TIER_COLORS_CLASSIC",0,{S:"#FFD700",A:"#9B59B6",B:"#3498DB",C:"#95A5A6",D:"#BDC3C7"},"TIER_ORDER",0,["S","A","B","C","D"],"TIER_RING_RADIUS",0,{S:150,A:230,B:310,C:390,D:470},"initialsFor",0,function(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean);return 0===t.length?"?":(t[0].charAt(0)+(t.length>1?t[t.length-1].charAt(0):"")).toUpperCase()}])}]);
|
|
33
|
+
Open All</button>${n.map(e=>'<a class="btn" href="'+e.profile_url+'" target="_blank">'+e.name+" · "+parseFloat(e.power_score||0).toFixed(1)+"</a>").join("")}</body></html>`;window.open(URL.createObjectURL(new Blob([t],{type:"text/html"})),"_blank")},style:{marginLeft:"auto",padding:"2px 8px",borderRadius:3,border:"none",cursor:"pointer",background:"#0077B5",color:"#fff",fontSize:8,fontWeight:700},children:["Open All ",e]})]}),n.map(e=>(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"5px 8px 5px 16px",borderBottom:"1px solid rgba(255,255,255,0.02)",background:b.has(e.id)?"rgba(255,215,0,0.05)":"transparent",cursor:"pointer"},onMouseEnter:t=>{b.has(e.id)||(t.currentTarget.style.background="rgba(255,255,255,0.04)")},onMouseLeave:t=>{b.has(e.id)||(t.currentTarget.style.background="transparent")},children:[(0,t.jsx)("input",{type:"checkbox",checked:b.has(e.id),onChange:()=>X(e.id),onClick:e=>e.stopPropagation(),style:{cursor:"pointer",flexShrink:0}}),(0,c.localPhoto)(e.profile_image_url)?(0,t.jsx)("img",{src:(0,c.localPhoto)(e.profile_image_url),alt:"",style:{width:28,height:28,borderRadius:"50%",objectFit:"cover",flexShrink:0,border:`1.5px solid ${_[e.tier]||"#555"}`},onError:e=>{e.target.style.display="none"}}):(0,t.jsx)("div",{style:{width:28,height:28,borderRadius:"50%",flexShrink:0,fontSize:10,fontWeight:700,background:_[e.tier]||"#555",color:"S"===e.tier?"#000":"#fff",display:"flex",alignItems:"center",justifyContent:"center"},children:e.name?.charAt(0)}),(0,t.jsxs)("div",{style:{flex:1,minWidth:0},onClick:()=>X(e.id),children:[(0,t.jsx)("div",{style:{fontSize:11,fontWeight:600,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),(0,t.jsx)("div",{style:{fontSize:8,color:"#666",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.headline?.substring(0,45)})]}),(0,t.jsx)("span",{style:{fontSize:9,fontWeight:700,color:_[e.tier],flexShrink:0},children:parseFloat(e.power_score||0).toFixed(1)}),q.has(e.id)?(0,t.jsx)("span",{style:{padding:"3px 6px",borderRadius:3,fontSize:8,fontWeight:700,background:"rgba(255,107,53,0.15)",color:"#FF6B35",flexShrink:0},children:"Sent"}):(0,t.jsx)("a",{href:e.profile_url,target:"_blank",rel:"noopener noreferrer",onClick:t=>{t.stopPropagation(),(0,j.markRequested)(e,{bridgeId:e.source_connection_id}).catch(()=>{})},style:{padding:"3px 6px",borderRadius:3,fontSize:8,fontWeight:700,background:"#0077B5",color:"#fff",textDecoration:"none",flexShrink:0},children:"Add"})]},e.id))]},e)}):r.map(e=>(0,t.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"6px 8px",borderBottom:"1px solid rgba(255,255,255,0.03)",background:b.has(e.id)?"rgba(255,215,0,0.05)":"transparent",cursor:"pointer"},onMouseEnter:t=>{b.has(e.id)||(t.currentTarget.style.background="rgba(255,255,255,0.04)")},onMouseLeave:t=>{b.has(e.id)||(t.currentTarget.style.background="transparent")},children:[(0,t.jsx)("input",{type:"checkbox",checked:b.has(e.id),onChange:()=>X(e.id),onClick:e=>e.stopPropagation(),style:{cursor:"pointer",flexShrink:0}}),(0,c.localPhoto)(e.profile_image_url)?(0,t.jsx)("img",{src:(0,c.localPhoto)(e.profile_image_url),alt:"",style:{width:32,height:32,borderRadius:"50%",objectFit:"cover",flexShrink:0,border:`1.5px solid ${_[e.tier]||"#555"}`},onError:e=>{e.target.style.display="none",e.target.nextSibling.style.display="flex"}}):null,(0,t.jsx)("div",{style:{width:32,height:32,borderRadius:"50%",flexShrink:0,fontSize:12,fontWeight:700,background:_[e.tier]||"#555",color:"S"===e.tier?"#000":"#fff",display:(0,c.localPhoto)(e.profile_image_url)?"none":"flex",alignItems:"center",justifyContent:"center"},children:e.name?.charAt(0)}),(0,t.jsxs)("div",{style:{flex:1,minWidth:0},onClick:()=>X(e.id),children:[(0,t.jsx)("div",{style:{fontSize:12,fontWeight:600,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),(0,t.jsx)("div",{style:{fontSize:9,color:"#666",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.headline?.substring(0,50)})]}),e.bridge&&(0,t.jsxs)("span",{style:{fontSize:8,color:"#555",flexShrink:0,maxWidth:60,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:["via ",e.bridge.name?.split(" ")[0]]}),(0,t.jsx)("div",{style:{textAlign:"right",flexShrink:0,minWidth:32},children:(0,t.jsx)("div",{style:{fontSize:10,fontWeight:700,color:_[e.tier]},children:parseFloat(e.power_score).toFixed(1)})}),q.has(e.id)?(0,t.jsx)("span",{style:{padding:"4px 8px",borderRadius:4,fontSize:9,fontWeight:700,background:"rgba(255,107,53,0.15)",color:"#FF6B35",flexShrink:0},children:"⏳ Sent"}):(0,t.jsx)("a",{href:e.profile_url,target:"_blank",rel:"noopener noreferrer",onClick:t=>{t.stopPropagation(),(0,j.markRequested)(e,{bridgeId:e.source_connection_id}).catch(()=>{})},style:{padding:"4px 8px",borderRadius:4,fontSize:9,fontWeight:700,background:"#0077B5",color:"#fff",textDecoration:"none",flexShrink:0},children:"Add"})]},e.id))]},e))]})]})})]})}e.s(["default",0,function(){return(0,t.jsx)(y.default,{children:(0,t.jsx)(F,{})})}],77305)},19821,e=>{"use strict";async function t(e){let t=e?`/api/network?userId=${encodeURIComponent(e)}`:"/api/network",r=await fetch(t);if(!r.ok)throw Error(`Failed to load network (${r.status})`);let n=await r.json();return{degree1:n.degree1||[],degree2:n.degree2||[],degree3:n.degree3||[],ties:Number(n.ties)||0}}e.s(["loadNetwork",0,t])},36774,e=>{"use strict";let t=/^[A-Za-z0-9_-]+\.(webp|jpg|jpeg|png)$/,r="/avatars/";e.s(["localPhoto",0,function(e){return"string"==typeof e&&e.startsWith(r)&&t.test(e.slice(r.length))?e:null}])},48235,e=>{"use strict";var t=e.i(7214);function r(e,r){return e?r.scanned.has(e.id)||e.profile_url&&r.read?.has((0,t.keyFor)(e))?"scanned":e.profile_url&&r.hidden.has((0,t.keyFor)(e))?"hidden":"todo":"todo"}function n(e,t){if(!e||1!==e.degree||null==e.unlocked_from_bridge_id)return null;let n=r(e,t);return"todo"===n?"ready":n}e.s(["circleScanCost",0,function(e=100){let t=Math.max(1,Math.min(100,Math.round(e)||100));return{profileViews:1,searches:t,minutes:t>=100?55:Math.max(5,Math.round(.55*t))}},"circleState",0,r,"reachIndex",0,function(e=[],r=[],n={}){let i=new Set;for(let e of r||[])e?.source_connection_id!=null&&i.add(e.source_connection_id);let o=new Set;for(let e of n?.skips||[])e?.profileUrl&&o.add((0,t.keyFor)({profile_url:e.profileUrl}));let l=new Set;for(let e of n?.read||[])e&&l.add((0,t.keyFor)({profile_url:e}));let s=new Map;for(let[e,r]of Object.entries(n?.lists||{}))e&&r&&s.set((0,t.keyFor)({profile_url:e}),r);let d=new Map;for(let t of e||[]){let e=t?.unlocked_from_bridge_id;if(null==e||1!==t.degree)continue;let r=d.get(e);r||d.set(e,r=[]),r.push(t)}return{scanned:i,read:l,hidden:o,reachedFrom:d,lists:s}},"reachState",0,n,"readyByCircle",0,function(e){let t=new Map;for(let[r,i]of e.reachedFrom){let o=i.filter(t=>"ready"===n(t,e)).length;o&&t.set(r,o)}return t},"readyToScan",0,function(e,r){return(e||[]).filter(e=>"ready"===n(e,r)).sort(t.compareBridges)},"scanBars",0,function(e,n){let i=r(e,n),o=e?.profile_url?n.lists?.get((0,t.keyFor)(e)):null;return"hidden"===i?null:o?o.more?o.total?Math.max(1,Math.min(4,Math.round(5*Math.min(1,10*o.pages/o.total)))):2:5:"scanned"===i?2:null}])},7214,e=>{"use strict";var t=e.i(36774);let r={S:0,A:1,B:2,C:3,D:4};function n(e){let t=parseFloat(e?.power_score);return Number.isFinite(t)?t:0}function i(e){return Math.round(10*n(e))/10}function o(e){return String(e?.profile_url||"").split(/[?#]/)[0].replace(/\/+$/,"")||`id:${e?.id}`}let l=(e,t)=>String(e??"").localeCompare(String(t??""),void 0,{numeric:!0});function s(e,t){return(r[e.tier]??9)-(r[t.tier]??9)||n(t)-n(e)||String(e.name||"").localeCompare(String(t.name||""))||l(e.id,t.id)}function d(e,t){return e.bridge&&t.bridge?s(e.bridge,t.bridge):e.bridge?-1:t.bridge?1:l(e.id,t.id)}function a(e,t){return t.score-e.score||t.waysIn-e.waysIn||d(e.routes[0]||c,t.routes[0]||c)||String(e.person.name||"").localeCompare(String(t.person.name||""))||(e.key<t.key?-1:+(e.key>t.key))}let c={id:null,bridge:null};function g(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean);return t.length<2?t[0]||"":`${t[0]} ${t[t.length-1].charAt(0)}.`}function p(e,t){let r=String(e||"");return t<=1?r?"…":"":r.length>t?`${r.slice(0,t-1)}…`:r}e.s(["TIERS",0,["S","A","B","C","D"],"compareBridges",0,s,"compareWaysIn",0,function(e,t){return t.waysIn-e.waysIn||a(e,t)},"keyFor",0,o,"routeIndex",0,function(e=[]){let t=new Map;for(let r of e||[]){let e=o(r),n=t.get(e);n||(n=new Set,t.set(e,n)),n.add(r.source_connection_id??null)}return t},"routesFor",0,function(e,t,r){return e?[...t?.get(o(e))??(null!=e.source_connection_id?new Set([e.source_connection_id]):new Set)].map(e=>({id:e,bridge:r?.get(e)??null})).sort(d):[]},"score",0,n,"score1",0,i,"separationPeople",0,function(e=[],r=[]){let n=new Map;for(let e of r||[])n.set(e.id,e);let s=new Map,c=new Map;for(let r of e||[]){let e=o(r);c.set(r.id,e);let n=s.get(e);n?function(e,r){let n=i(e)-i(r);if(n)return n>0;let o=!!(0,t.localPhoto)(e.profile_image_url);return!!(0,t.localPhoto)(r.profile_image_url)!==o?o:0>l(e.id,r.id)}(r,n.person)&&(n.person=r):(n={key:e,person:r,rowIds:[],routeIds:new Set},s.set(e,n)),n.rowIds.push(r.id),n.routeIds.add(r.source_connection_id??null)}let g={people:s.size,byTier:{S:0,A:0,B:0,C:0,D:0},multi:0,bridges:0,unresolvedIds:0,peopleOnlyUnresolved:0,onlyUnresolvedVia:0},p=new Set,h=new Set,f=new Set,u=[];for(let e of s.values()){let t=[...e.routeIds].map(e=>({id:e,bridge:n.get(e)??null}));t.sort(d);let r=t.filter(e=>e.bridge);for(let e of t)(e.bridge?p:h).add(e.id);if(!r.length)for(let e of(g.peopleOnlyUnresolved++,t))f.add(e.id);let{person:o}=e,l=o.tier;void 0!==g.byTier[l]&&g.byTier[l]++,t.length>1&&g.multi++,u.push({key:e.key,person:o,score:i(o),tier:l,routes:t,waysIn:t.length,rowIds:e.rowIds,haystack:[o.name,o.headline,o.company,o.role,...r.map(e=>e.bridge.name)].filter(Boolean).join("\n").toLowerCase(),rank:0,tied:!1})}return g.bridges=p.size,g.unresolvedIds=h.size,g.onlyUnresolvedVia=f.size,u.sort(a),function(e){for(let t=0;t<e.length;t++){let r=e[t],n=e[t-1],i=n&&n.score===r.score&&n.waysIn===r.waysIn;r.rank=i?n.rank:t+1,i&&(r.tied=!0,n.tied=!0)}}(u),{people:u,rowToKey:c,summary:g}},"shortName",0,g,"summitLayout",0,function(e=[],t=800,r=!1){let n=r?36:26,i=Math.round(.42*t),o=Math.round(.58*t),l=e.map((e,r)=>{var i;return{key:e.key,p:e,x:o,y:16+r*n,r:(i=e.waysIn)>=4?8.5:3===i?7:2===i?5.5:4,label:p(`#${e.rank}${e.tied?"=":""} ${e.person.name||""}`,Math.floor((t-o-60)/6.2))}}),d=new Map,a="\0unresolved";for(let e of l)for(let t of e.p.routes){let r=t.bridge?t.id:a,n=d.get(r);n||(n={id:t.bridge?t.id:null,bridge:t.bridge,unresolved:!t.bridge,ys:[],count:0},d.set(r,n)),n.count++,n.ys.push(e.y)}let c=[...d.values()].map(e=>({...e,bary:e.ys.reduce((e,t)=>e+t,0)/e.ys.length}));c.sort((e,t)=>e.bary-t.bary||e.unresolved-t.unresolved||(e.bridge&&t.bridge?s(e.bridge,t.bridge):0));let h=l.length?l[l.length-1].y:16,f=c.length>1?Math.max(18,(h-16)/(c.length-1)):0,u=Math.max(4,Math.floor((i-10-42)/6));c.forEach((e,t)=>{if(e.x=i,e.y=1===c.length?(16+h)/2:16+t*f,e.r=5,e.unresolved)e.label=p(`can't be named (${e.count})`,u);else{let t=e.bridge.name||"";e.label=t.length<=u?t:p(g(t),u)}delete e.ys});let x=new Map(c.map(e=>[e.unresolved?a:e.id,e])),b=[];for(let e of l){let t=new Set;e.p.routes.forEach((r,n)=>{let i=r.bridge?r.id:a;if(t.has(i))return;t.add(i);let o=x.get(i);b.push({key:e.key,bridgeId:o.id,primary:0===n&&!o.unresolved,unresolved:o.unresolved,tier:o.bridge?.tier??null,x1:o.x,y1:o.y,x2:e.x,y2:e.y})})}let y=Math.max(h,c.length?c[c.length-1].y:0),m=l.length?Math.ceil(y+16):0,S={x:20,y:l.length?(16+Math.max(h,y))/2:16,r:14},k=c.map(e=>({id:e.id,x1:S.x,y1:S.y,x2:e.x,y2:e.y}));return{width:t,height:m,you:S,people:l,bridges:c,links:b,spokes:k}}])},5258,e=>{"use strict";e.s(["TIER_COLORS",0,{S:"#f6c344",A:"#a78bfa",B:"#60a5fa",C:"#94a3b8",D:"#6b7280"},"TIER_COLORS_CLASSIC",0,{S:"#FFD700",A:"#9B59B6",B:"#3498DB",C:"#95A5A6",D:"#BDC3C7"},"TIER_ORDER",0,["S","A","B","C","D"],"TIER_RING_RADIUS",0,{S:150,A:230,B:310,C:390,D:470},"initialsFor",0,function(e){let t=String(e||"").trim().split(/\s+/).filter(Boolean);return 0===t.length?"?":(t[0].charAt(0)+(t.length>1?t[t.length-1].charAt(0):"")).toUpperCase()}])}]);
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.5] - 2026-09-29
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Physics lab for the Galaxy (experimental, in Filters).** Sliders for the forces that lay the
|
|
13
|
+
Galaxy out, in the spirit of Obsidian's graph: *Gravity* (a pull in towards you), *Rings* (how
|
|
14
|
+
hard each tier holds its ring; at 0 the Galaxy finds its own shape), *Push*, *Pull* (how hard
|
|
15
|
+
each person pulls the people who came through them) and *Distance*, plus dot size, line
|
|
16
|
+
thickness, names, and dots sized by power score or by how many hang off them. *Clusters* sets
|
|
17
|
+
it up so each connection bunches their circle round them. Hovering a dot lights up its branch,
|
|
18
|
+
everyone behind it and the chain back to you. **Replay** plays your network growing by the
|
|
19
|
+
date you connected, with a time slider; each circle arrives with its connection. Everything
|
|
20
|
+
moves the Galaxy in place, and *Reset* puts today's layout back. The replay needs "connected
|
|
21
|
+
on" dates, so the sample network has nothing to replay.
|
|
22
|
+
- **More in the physics lab.** *Colour by* tier, degree, company (the eight most common) or
|
|
23
|
+
warmth (from the Social tab), with a legend to match. *Find* lights up everyone whose name,
|
|
24
|
+
company or role matches and dims the rest; Enter flies to the best one and opens their card.
|
|
25
|
+
The replay has a length (5 s to 1 min), *Loop*, your job starts and posts from the Social tab
|
|
26
|
+
marked on its timeline, and the job you were at in its date. *Saved layouts* keep slider
|
|
27
|
+
settings under a name. *Save a picture* makes a PNG of the Galaxy at twice the size, and
|
|
28
|
+
*Record the replay* saves it as a video.
|
|
29
|
+
- **Names on or off** for the Galaxy, in Filters.
|
|
30
|
+
|
|
31
|
+
## [0.4.4] - 2026-09-29
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Social (experimental): a tab for your relationships.** It works from your own LinkedIn data:
|
|
35
|
+
- **Your export:** the folder LinkedIn emails you from *Settings → Data privacy → Get a copy
|
|
36
|
+
of your data*, read in the app window with no traffic to LinkedIn.
|
|
37
|
+
- **A live messages sync:** reads your messages list once in your own Chrome, by hand or once
|
|
38
|
+
a day in the daytime, using no search budget.
|
|
39
|
+
|
|
40
|
+
It shows:
|
|
41
|
+
- how many of your connections are warm, cool, dormant or never messaged;
|
|
42
|
+
- who's waiting on a reply (S and A tier first), or has unread messages;
|
|
43
|
+
- your career chapters, with the connections made in each and how many you're still in
|
|
44
|
+
touch with;
|
|
45
|
+
- whether posting works (new connections the week after a post against a usual week);
|
|
46
|
+
- how many connection requests came to you and how many you sent.
|
|
47
|
+
|
|
48
|
+
Only dates, who wrote last and counts are kept, one file per profile in the data folder, and
|
|
49
|
+
*Forget it* removes it. Message text is never kept. Email addresses are kept only if you tick
|
|
50
|
+
the box. The live sync hasn't been tried on a live account yet.
|
|
51
|
+
- **Auto scan.** With the Scan page's experimental all-day box ticked, an *Auto scan* button sits
|
|
52
|
+
beside Scan and starts the all-day Auto-Bridge with your Scan page choices. The status bar sits
|
|
53
|
+
under the tabs, dimmed while it's ready, and lights up once it runs.
|
|
54
|
+
- **Insights on a connection's card.** A collapsible panel with their circle's size (*Explore →*
|
|
55
|
+
opens it in Bridge Chains), how many only they reach, whose circle overlaps theirs most
|
|
56
|
+
(*Open →*), the tier mix of their circle, where most of it works, how much is scanned (*Scan →*
|
|
57
|
+
or *Finish →*), when you connected and how many requests you have out through them, and their
|
|
58
|
+
rank by who only they reach. *Show them →* opens Separation filtered to the people only they
|
|
59
|
+
reach, and *S only →* to the S-tier people in their circle. All from what the app already
|
|
60
|
+
keeps. It replaces the *Only through …* box.
|
|
61
|
+
- **Network health on the Scores tab.** How much of your 2nd degree you reach two or more ways,
|
|
62
|
+
the effective reach of your own network (Burt's effective size, from the ties between your
|
|
63
|
+
connections that circle scans keep), and your five connections who reach the most people no
|
|
64
|
+
one else does, each with *Explore →*. With fewer than five circles scanned it says it's an
|
|
65
|
+
early estimate. Only your own numbers: never a percentile against other people.
|
|
66
|
+
|
|
9
67
|
## [0.4.3] - 2026-09-29
|
|
10
68
|
|
|
11
69
|
### Added
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// The experimental all-day Auto-Bridge (Auto scan), as the header button and
|
|
2
|
+
// the status bar see it. The switch is the Scan page's checkbox, kept in this
|
|
3
|
+
// browser (its useRemembered: JSON under six-degrees-experimental-auto); the page
|
|
4
|
+
// announces a change with the 'six-degrees:experimental' event.
|
|
5
|
+
|
|
6
|
+
import { beginScrape } from './scraper-client';
|
|
7
|
+
|
|
8
|
+
const KEY = 'six-degrees-experimental-auto';
|
|
9
|
+
|
|
10
|
+
export function watchAllDay(change) {
|
|
11
|
+
window.addEventListener('storage', change);
|
|
12
|
+
window.addEventListener('six-degrees:experimental', change);
|
|
13
|
+
return () => {
|
|
14
|
+
window.removeEventListener('storage', change);
|
|
15
|
+
window.removeEventListener('six-degrees:experimental', change);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function allDayNow() {
|
|
20
|
+
try { return localStorage.getItem(KEY) === 'true'; } catch { return false; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const remembered = (key, fallback) => {
|
|
24
|
+
try { const v = localStorage.getItem(key); return v === null ? fallback : JSON.parse(v); } catch { return fallback; }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Start Auto scan with the Scan page's remembered choices (order, pages, finish), 10 people, S and A first. */
|
|
28
|
+
export function startAutoScan() {
|
|
29
|
+
const order = remembered('six-degrees-bridge-order', 'newest');
|
|
30
|
+
return beginScrape('auto-bridge', {
|
|
31
|
+
experimental: true,
|
|
32
|
+
maxBridges: 10,
|
|
33
|
+
order,
|
|
34
|
+
tiers: order === 'score' ? ['S', 'A'] : [],
|
|
35
|
+
maxPages: remembered('six-degrees-bridge-pages-v2', 100),
|
|
36
|
+
deeper: remembered('six-degrees-bridge-finish', true),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Save the Galaxy as a picture, or record its replay as a video (the physics
|
|
2
|
+
// lab, experimental). The Galaxy registers its <svg> here while it's on screen;
|
|
3
|
+
// the lab's buttons call these. Nothing leaves the computer: the file is saved
|
|
4
|
+
// like any other download.
|
|
5
|
+
|
|
6
|
+
import { clockNow, setClock, play, pause } from './galaxy-lab.js';
|
|
7
|
+
|
|
8
|
+
let current = null; // { svg, stamp: () => text or null, legend: () => [[label, colour]] }
|
|
9
|
+
|
|
10
|
+
export function registerGalaxy(entry) {
|
|
11
|
+
current = entry;
|
|
12
|
+
return () => { if (current === entry) current = null; };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const galaxyShown = () => current != null;
|
|
16
|
+
|
|
17
|
+
// The lab's highlight rules live in the page's CSS, which a picture of the
|
|
18
|
+
// <svg> alone doesn't carry, so they ride along inside it.
|
|
19
|
+
const EXPORT_CSS = `
|
|
20
|
+
.lab-focus .gn:not(.lit), .lab-focus .gl:not(.lit) { opacity: 0.06; }
|
|
21
|
+
.lab-focus .gl.lit { stroke-opacity: 0.7; }
|
|
22
|
+
.lab-focus .dot-rings, .lab-focus .catalyst-ring { opacity: 0.15; }
|
|
23
|
+
.lab-find .gn:not(.found), .lab-find .gl:not(.found) { opacity: 0.07; }
|
|
24
|
+
.lab-find .gn.found { stroke: #fff; stroke-width: 2px; }
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
function svgImage(svg) {
|
|
28
|
+
const clone = svg.cloneNode(true);
|
|
29
|
+
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
30
|
+
// The page's font, which the <svg> inherits on screen but not in a file.
|
|
31
|
+
clone.setAttribute('font-family', getComputedStyle(svg).fontFamily || 'system-ui, sans-serif');
|
|
32
|
+
const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
|
|
33
|
+
style.textContent = EXPORT_CSS;
|
|
34
|
+
clone.insertBefore(style, clone.firstChild);
|
|
35
|
+
const xml = new XMLSerializer().serializeToString(clone);
|
|
36
|
+
const img = new Image();
|
|
37
|
+
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xml)}`;
|
|
38
|
+
return img.decode().then(() => img);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// One frame: the Galaxy, its date while a replay runs, the legend, and a small credit.
|
|
42
|
+
async function drawFrame(ctx, w, h) {
|
|
43
|
+
const img = await svgImage(current.svg);
|
|
44
|
+
ctx.fillStyle = '#0a0a1a';
|
|
45
|
+
ctx.fillRect(0, 0, w, h);
|
|
46
|
+
ctx.drawImage(img, 0, 0, w, h);
|
|
47
|
+
const scale = w / current.svg.clientWidth;
|
|
48
|
+
ctx.save();
|
|
49
|
+
ctx.scale(scale, scale);
|
|
50
|
+
const cw = w / scale;
|
|
51
|
+
const ch = h / scale;
|
|
52
|
+
const stamp = current.stamp();
|
|
53
|
+
if (stamp) {
|
|
54
|
+
ctx.font = '600 15px -apple-system, system-ui, sans-serif';
|
|
55
|
+
const tw = ctx.measureText(stamp).width;
|
|
56
|
+
ctx.fillStyle = 'rgba(10,15,30,0.85)';
|
|
57
|
+
ctx.beginPath();
|
|
58
|
+
ctx.roundRect?.(cw / 2 - tw / 2 - 16, ch - 52, tw + 32, 32, 16);
|
|
59
|
+
ctx.fill();
|
|
60
|
+
ctx.fillStyle = '#e6edf5';
|
|
61
|
+
ctx.textAlign = 'center';
|
|
62
|
+
ctx.fillText(stamp, cw / 2, ch - 31);
|
|
63
|
+
}
|
|
64
|
+
ctx.textAlign = 'left';
|
|
65
|
+
ctx.font = '11px -apple-system, system-ui, sans-serif';
|
|
66
|
+
let x = 20;
|
|
67
|
+
for (const [label, colour] of current.legend()) {
|
|
68
|
+
ctx.fillStyle = colour;
|
|
69
|
+
ctx.beginPath();
|
|
70
|
+
ctx.arc(x + 4, ch - 24, 4, 0, Math.PI * 2);
|
|
71
|
+
ctx.fill();
|
|
72
|
+
ctx.fillStyle = '#9aa';
|
|
73
|
+
ctx.fillText(label, x + 12, ch - 20);
|
|
74
|
+
x += 20 + ctx.measureText(label).width;
|
|
75
|
+
}
|
|
76
|
+
ctx.textAlign = 'right';
|
|
77
|
+
ctx.fillStyle = 'rgba(255,255,255,0.35)';
|
|
78
|
+
ctx.fillText('Six Degrees', cw - 16, 24);
|
|
79
|
+
ctx.restore();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function save(blob, name) {
|
|
83
|
+
const url = URL.createObjectURL(blob);
|
|
84
|
+
const a = document.createElement('a');
|
|
85
|
+
a.href = url;
|
|
86
|
+
a.download = name;
|
|
87
|
+
// Anything appended to <body> is position: fixed (TRAPS §29). Gone at once.
|
|
88
|
+
a.style.position = 'fixed';
|
|
89
|
+
a.style.left = '-9999px';
|
|
90
|
+
document.body.appendChild(a);
|
|
91
|
+
a.click();
|
|
92
|
+
a.remove();
|
|
93
|
+
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const today = () => new Date().toISOString().slice(0, 10);
|
|
97
|
+
|
|
98
|
+
function canvasFor(scale) {
|
|
99
|
+
const w = Math.round(current.svg.clientWidth * scale);
|
|
100
|
+
const h = Math.round(current.svg.clientHeight * scale);
|
|
101
|
+
const canvas = document.createElement('canvas');
|
|
102
|
+
canvas.width = w;
|
|
103
|
+
canvas.height = h;
|
|
104
|
+
return { canvas, ctx: canvas.getContext('2d'), w, h };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The Galaxy as it is on screen, at twice the size, as a PNG. */
|
|
108
|
+
export async function savePicture() {
|
|
109
|
+
if (!current) throw new Error('Open the Galaxy first.');
|
|
110
|
+
const { canvas, ctx, w, h } = canvasFor(2);
|
|
111
|
+
await drawFrame(ctx, w, h);
|
|
112
|
+
const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
|
|
113
|
+
save(blob, `six-degrees-galaxy-${today()}.png`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const canRecord = () => typeof window !== 'undefined' && typeof window.MediaRecorder !== 'undefined'
|
|
117
|
+
&& typeof HTMLCanvasElement !== 'undefined' && typeof HTMLCanvasElement.prototype.captureStream === 'function';
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Plays the replay from the start and records it: a video the length of the
|
|
121
|
+
* replay, at 12 frames a second. Resolves when it's saved.
|
|
122
|
+
*/
|
|
123
|
+
export async function recordReplay() {
|
|
124
|
+
if (!current) throw new Error('Open the Galaxy first.');
|
|
125
|
+
if (!canRecord()) throw new Error('This window can’t record video.');
|
|
126
|
+
const { canvas, ctx, w, h } = canvasFor(1);
|
|
127
|
+
const type = ['video/mp4;codecs=avc1', 'video/webm;codecs=vp9', 'video/webm', 'video/mp4'].find((t) => MediaRecorder.isTypeSupported(t));
|
|
128
|
+
if (!type) throw new Error('This window can’t record video.');
|
|
129
|
+
await drawFrame(ctx, w, h);
|
|
130
|
+
const recorder = new MediaRecorder(canvas.captureStream(12), { mimeType: type, videoBitsPerSecond: 6_000_000 });
|
|
131
|
+
const chunks = [];
|
|
132
|
+
recorder.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); };
|
|
133
|
+
const done = new Promise((resolve) => { recorder.onstop = resolve; });
|
|
134
|
+
recorder.start(250);
|
|
135
|
+
pause();
|
|
136
|
+
setClock({ at: clockNow().min });
|
|
137
|
+
play();
|
|
138
|
+
let drawing = false;
|
|
139
|
+
await new Promise((resolve) => {
|
|
140
|
+
const tick = setInterval(async () => {
|
|
141
|
+
if (drawing) return;
|
|
142
|
+
drawing = true;
|
|
143
|
+
try { await drawFrame(ctx, w, h); } finally { drawing = false; }
|
|
144
|
+
if (!clockNow().playing) { clearInterval(tick); setTimeout(resolve, 600); }
|
|
145
|
+
}, 1000 / 12);
|
|
146
|
+
});
|
|
147
|
+
await drawFrame(ctx, w, h);
|
|
148
|
+
recorder.stop();
|
|
149
|
+
await done;
|
|
150
|
+
const ext = type.startsWith('video/mp4') ? 'mp4' : 'webm';
|
|
151
|
+
save(new Blob(chunks, { type: type.split(';')[0] }), `six-degrees-replay-${today()}.${ext}`);
|
|
152
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// The Galaxy's physics lab (experimental): Obsidian-style sliders for the forces
|
|
2
|
+
// that lay the Galaxy out, and a replay of your network growing by the date you
|
|
3
|
+
// connected. The Filter panel writes here (GalaxyLab.js); the Galaxy reads it
|
|
4
|
+
// (ForceGraph.js) and changes the running layout in place, never rebuilding.
|
|
5
|
+
//
|
|
6
|
+
// Two stores: the sliders, remembered in this browser, and the replay's clock,
|
|
7
|
+
// which isn't. Both follow useSyncExternalStore's rules: a snapshot only changes
|
|
8
|
+
// when something in it does.
|
|
9
|
+
|
|
10
|
+
import { keyFor } from './separation.js';
|
|
11
|
+
import { warmthOf } from './linkedin-export.js';
|
|
12
|
+
|
|
13
|
+
const KEY = 'six-degrees-galaxy-lab';
|
|
14
|
+
const LAYOUTS_KEY = 'six-degrees-galaxy-layouts';
|
|
15
|
+
|
|
16
|
+
/** The layout as it has always been. Every slider is a multiple of today's value, or 0 for off. */
|
|
17
|
+
export const LAB_DEFAULTS = Object.freeze({
|
|
18
|
+
on: false, // the lab is on: without it, the Galaxy ignores everything below
|
|
19
|
+
gravity: 0, // a pull on every dot towards you, in the middle
|
|
20
|
+
rings: 1, // how hard each tier (or degree) holds its ring; 0 lets it find its own shape
|
|
21
|
+
push: 15, // how hard dots push each other apart
|
|
22
|
+
pull: 1, // how hard each person pulls the people who came through them
|
|
23
|
+
distance: 1, // how far out those people sit
|
|
24
|
+
dotSize: 1,
|
|
25
|
+
lines: 1,
|
|
26
|
+
sizeBy: 'power', // 'power' (power score) or 'reach' (how many hang off them)
|
|
27
|
+
names: 'key', // 'key' (you, S tier, catalysts) or 'all' (all your connections)
|
|
28
|
+
branch: true, // hovering a dot lights up its branch and dims the rest
|
|
29
|
+
colourBy: 'tier', // 'tier', 'degree', 'company' or 'warmth' (from the Social tab)
|
|
30
|
+
speed: 15, // a replay's length, in seconds
|
|
31
|
+
loop: false, // start the replay again when it ends
|
|
32
|
+
labels: true, // names on the Galaxy at all: the Filter panel's switch, honoured with the lab off too
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** The slider settings a saved layout keeps (not whether the lab is on, nor the names switch). */
|
|
36
|
+
const LAYOUT_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance', 'dotSize', 'lines', 'sizeBy', 'names', 'branch', 'colourBy', 'speed', 'loop'];
|
|
37
|
+
|
|
38
|
+
/** Let the layout find its own shape: no rings, each connection's circle bunched round them. */
|
|
39
|
+
export const CLUSTERS = Object.freeze({ gravity: 0.25, rings: 0, push: 25, pull: 2.5, distance: 0.35, sizeBy: 'reach' });
|
|
40
|
+
|
|
41
|
+
const listeners = new Set();
|
|
42
|
+
let cache = null;
|
|
43
|
+
|
|
44
|
+
function read() {
|
|
45
|
+
try {
|
|
46
|
+
const raw = JSON.parse(localStorage.getItem(KEY) || 'null');
|
|
47
|
+
return raw && typeof raw === 'object' ? { ...LAB_DEFAULTS, ...raw } : LAB_DEFAULTS;
|
|
48
|
+
} catch { return LAB_DEFAULTS; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function labNow() {
|
|
52
|
+
if (!cache) cache = read();
|
|
53
|
+
return cache;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function setLab(patch) {
|
|
57
|
+
cache = patch === null ? { ...LAB_DEFAULTS, on: labNow().on, labels: labNow().labels } : { ...labNow(), ...patch };
|
|
58
|
+
try { localStorage.setItem(KEY, JSON.stringify(cache)); } catch {}
|
|
59
|
+
listeners.forEach((f) => f());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function watchLab(f) {
|
|
63
|
+
listeners.add(f);
|
|
64
|
+
const other = (e) => { if (e.key === KEY) { cache = null; f(); } };
|
|
65
|
+
window.addEventListener('storage', other);
|
|
66
|
+
return () => { listeners.delete(f); window.removeEventListener('storage', other); };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** What the Galaxy should use: the sliders while the lab is on, today's layout otherwise (with the names switch). */
|
|
70
|
+
export const effectiveLab = (lab) => (lab.on ? lab : lab.labels === LAB_DEFAULTS.labels ? LAB_DEFAULTS : { ...LAB_DEFAULTS, labels: lab.labels });
|
|
71
|
+
|
|
72
|
+
// Saved layouts: your own named slider settings, in this browser.
|
|
73
|
+
let layoutsCache = null;
|
|
74
|
+
const layoutListeners = new Set();
|
|
75
|
+
export function layoutsNow() {
|
|
76
|
+
if (!layoutsCache) {
|
|
77
|
+
try { layoutsCache = JSON.parse(localStorage.getItem(LAYOUTS_KEY) || '[]'); } catch { layoutsCache = []; }
|
|
78
|
+
if (!Array.isArray(layoutsCache)) layoutsCache = [];
|
|
79
|
+
}
|
|
80
|
+
return layoutsCache;
|
|
81
|
+
}
|
|
82
|
+
function writeLayouts(list) {
|
|
83
|
+
layoutsCache = list;
|
|
84
|
+
try { localStorage.setItem(LAYOUTS_KEY, JSON.stringify(list)); } catch {}
|
|
85
|
+
layoutListeners.forEach((f) => f());
|
|
86
|
+
}
|
|
87
|
+
export function watchLayouts(f) { layoutListeners.add(f); return () => layoutListeners.delete(f); }
|
|
88
|
+
export function saveLayout(name) {
|
|
89
|
+
const label = String(name || '').trim().slice(0, 40);
|
|
90
|
+
if (!label) return;
|
|
91
|
+
const lab = labNow();
|
|
92
|
+
const settings = Object.fromEntries(LAYOUT_KEYS.map((k) => [k, lab[k]]));
|
|
93
|
+
writeLayouts([...layoutsNow().filter((l) => l.name !== label), { name: label, settings }]);
|
|
94
|
+
}
|
|
95
|
+
export function applyLayout(name) {
|
|
96
|
+
const found = layoutsNow().find((l) => l.name === name);
|
|
97
|
+
if (found) setLab(Object.fromEntries(LAYOUT_KEYS.filter((k) => k in found.settings).map((k) => [k, found.settings[k]])));
|
|
98
|
+
}
|
|
99
|
+
export function forgetLayout(name) { writeLayouts(layoutsNow().filter((l) => l.name !== name)); }
|
|
100
|
+
|
|
101
|
+
// The replay's clock. `at` is a time in ms (null: everything shows); `min` and
|
|
102
|
+
// `max` are the first and last dates in the network on screen, set by the Galaxy
|
|
103
|
+
// (both null when it has no dates, as in the sample network).
|
|
104
|
+
// The same store holds the lab's other passing state: what Find is looking
|
|
105
|
+
// for (and a count that asks the Galaxy to fly to the first match), and the
|
|
106
|
+
// Social tab's data once it's loaded.
|
|
107
|
+
const clockListeners = new Set();
|
|
108
|
+
let clock = { at: null, playing: false, min: null, max: null, of: 0, find: '', fly: 0, found: 0, social: undefined };
|
|
109
|
+
let raf = null;
|
|
110
|
+
|
|
111
|
+
export const clockNow = () => clock;
|
|
112
|
+
|
|
113
|
+
export function watchClock(f) {
|
|
114
|
+
clockListeners.add(f);
|
|
115
|
+
return () => clockListeners.delete(f);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function setClock(patch) {
|
|
119
|
+
const next = { ...clock, ...patch };
|
|
120
|
+
if (Object.keys(patch).every((k) => Object.is(next[k], clock[k]))) return;
|
|
121
|
+
clock = next;
|
|
122
|
+
clockListeners.forEach((f) => f());
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Play from where the slider is (or the start) to the last date, over the lab's replay length. */
|
|
126
|
+
export function play() {
|
|
127
|
+
if (clock.min == null || clock.max == null || clock.max <= clock.min) return;
|
|
128
|
+
const span = clock.max - clock.min;
|
|
129
|
+
const REPLAY_MS = Math.max(1, Number(labNow().speed) || 15) * 1000;
|
|
130
|
+
const from = clock.at == null || clock.at >= clock.max ? clock.min : clock.at;
|
|
131
|
+
const started = performance.now() - ((from - clock.min) / span) * REPLAY_MS;
|
|
132
|
+
setClock({ at: from, playing: true });
|
|
133
|
+
const step = (now) => {
|
|
134
|
+
const at = Math.min(clock.max, clock.min + ((now - started) / REPLAY_MS) * span);
|
|
135
|
+
setClock({ at });
|
|
136
|
+
if (at >= clock.max) {
|
|
137
|
+
raf = null;
|
|
138
|
+
setClock({ playing: false });
|
|
139
|
+
if (labNow().loop) setTimeout(() => { if (!raf && clock.at === clock.max) { setClock({ at: clock.min }); play(); } }, 1200);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
raf = requestAnimationFrame(step);
|
|
143
|
+
};
|
|
144
|
+
cancelAnimationFrame(raf);
|
|
145
|
+
raf = requestAnimationFrame(step);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function pause() {
|
|
149
|
+
cancelAnimationFrame(raf);
|
|
150
|
+
raf = null;
|
|
151
|
+
setClock({ playing: false });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Back to everything showing. */
|
|
155
|
+
export function stopReplay() {
|
|
156
|
+
pause();
|
|
157
|
+
setClock({ at: null });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* When each dot appears in the replay: your connections on the day you
|
|
162
|
+
* connected, and everyone in a circle with the connection whose circle it is
|
|
163
|
+
* (they came with them). Undated connections are there from the start.
|
|
164
|
+
* `parentOf` maps an id to the id it hangs off, as the Galaxy draws it.
|
|
165
|
+
*/
|
|
166
|
+
export function bornTimes(nodes, parentOf) {
|
|
167
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
168
|
+
const memo = new Map();
|
|
169
|
+
const bornOf = (n, depth = 0) => {
|
|
170
|
+
if (memo.has(n.id)) return memo.get(n.id);
|
|
171
|
+
let t = -Infinity;
|
|
172
|
+
const p = parentOf.get(n.id);
|
|
173
|
+
if (n.degree === 1 || p == null || depth > 6) {
|
|
174
|
+
const own = n.connected_date ? Date.parse(n.connected_date) : NaN;
|
|
175
|
+
t = Number.isNaN(own) ? -Infinity : own;
|
|
176
|
+
}
|
|
177
|
+
if (p != null && byId.has(p) && depth <= 6) t = Math.max(t, bornOf(byId.get(p), depth + 1));
|
|
178
|
+
memo.set(n.id, t);
|
|
179
|
+
return t;
|
|
180
|
+
};
|
|
181
|
+
return nodes.map((n) => bornOf(n));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** How many hang off each dot, all the way down (not counting itself). */
|
|
185
|
+
export function reachCounts(nodes, parentOf) {
|
|
186
|
+
const out = new Map(nodes.map((n) => [n.id, 0]));
|
|
187
|
+
for (const n of nodes) {
|
|
188
|
+
let p = parentOf.get(n.id);
|
|
189
|
+
for (let hops = 0; p != null && hops < 7; hops++) {
|
|
190
|
+
out.set(p, (out.get(p) || 0) + 1);
|
|
191
|
+
p = parentOf.get(p);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** The Social tab's data, loaded once: messages merged per profile, your jobs and your posts. */
|
|
198
|
+
let socialLoad = null;
|
|
199
|
+
export function loadSocial() {
|
|
200
|
+
if (!socialLoad) {
|
|
201
|
+
socialLoad = fetch('/api/social').then((r) => (r.ok ? r.json() : {})).then((d) => {
|
|
202
|
+
setClock({ social: d.social ? mergeSocial(d.social) : null });
|
|
203
|
+
}).catch(() => setClock({ social: null }));
|
|
204
|
+
}
|
|
205
|
+
return socialLoad;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The export and the live sync together, per profile key, and the "now" warmth
|
|
210
|
+
* is judged at: the newest thing the data knows, so an older export reads as it was.
|
|
211
|
+
*/
|
|
212
|
+
export function mergeSocial(social) {
|
|
213
|
+
const merged = new Map();
|
|
214
|
+
for (const [url, v] of Object.entries(social?.people || {})) merged.set(keyFor({ profile_url: url }), { ...v });
|
|
215
|
+
for (const [url, v] of Object.entries(social?.live || {})) {
|
|
216
|
+
const k = keyFor({ profile_url: url });
|
|
217
|
+
const cur = merged.get(k) || { total: 0, recent: 0 };
|
|
218
|
+
if (v.last && (!cur.last || v.last > cur.last)) cur.last = v.last;
|
|
219
|
+
cur.unread = v.unread;
|
|
220
|
+
cur.total = Math.max(cur.total || 0, 1);
|
|
221
|
+
merged.set(k, cur);
|
|
222
|
+
}
|
|
223
|
+
let asOf = Math.max(social?.asOf || 0, social?.liveAt ? Date.parse(social.liveAt) || 0 : 0);
|
|
224
|
+
for (const v of merged.values()) if ((v.last || 0) > asOf) asOf = v.last;
|
|
225
|
+
return { merged, asOf, chapters: social?.chapters || [], posts: social?.posts?.posts || [] };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Your job starts and posts, oldest first, for the replay's timeline. */
|
|
229
|
+
export function milestones(social) {
|
|
230
|
+
const out = [];
|
|
231
|
+
for (const c of social?.chapters || []) if (Number.isFinite(c.from)) out.push({ t: c.from, kind: 'job', label: c.company });
|
|
232
|
+
for (const p of social?.posts || []) if (Number.isFinite(p.t)) out.push({ t: p.t, kind: 'post', label: 'You posted' });
|
|
233
|
+
return out.sort((a, b) => a.t - b.t);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Where you worked at a moment: the latest job started by then. */
|
|
237
|
+
export function chapterAt(social, t) {
|
|
238
|
+
let at = null;
|
|
239
|
+
for (const c of social?.chapters || []) if (Number.isFinite(c.from) && c.from <= t && (!at || c.from > at.from)) at = c;
|
|
240
|
+
return at && (!Number.isFinite(at.to) || at.to > t) ? at : null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const DEGREE_COLOURS = { 1: '#4FC3F7', 2: '#BA68C8', 3: '#FFB74D' };
|
|
244
|
+
const COMPANY_COLOURS = ['#FF6B6B', '#4ECDC4', '#FFD93D', '#A29BFE', '#00B894', '#FD79A8', '#74B9FF', '#E17055'];
|
|
245
|
+
const WARMTH_COLOURS = { warm: '#FF7043', cool: '#4FC3F7', dormant: '#8D6E63', never: '#546E7A' };
|
|
246
|
+
const OTHER = '#4a5568';
|
|
247
|
+
const companyKey = (c) => String(c || '').trim().toLowerCase();
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* How to colour the Galaxy's dots, like Obsidian's groups: by tier (as it
|
|
251
|
+
* always has), degree, company (the eight most common, the rest grey) or, with
|
|
252
|
+
* the Social tab's data, how warm you are with each of your connections.
|
|
253
|
+
* Returns { of(person) -> colour, legend: [[label, colour]] }.
|
|
254
|
+
*/
|
|
255
|
+
export function colourScheme(mode, people, tierColors, social) {
|
|
256
|
+
if (mode === 'degree') {
|
|
257
|
+
return {
|
|
258
|
+
of: (d) => DEGREE_COLOURS[Math.min(d.degree || 1, 3)],
|
|
259
|
+
legend: [['1st', DEGREE_COLOURS[1]], ['2nd', DEGREE_COLOURS[2]], ['3rd+', DEGREE_COLOURS[3]]],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (mode === 'company') {
|
|
263
|
+
const counts = new Map();
|
|
264
|
+
for (const p of people) {
|
|
265
|
+
const k = companyKey(p.company);
|
|
266
|
+
if (!k) continue;
|
|
267
|
+
const c = counts.get(k) || { name: String(p.company).trim(), n: 0 };
|
|
268
|
+
c.n += 1;
|
|
269
|
+
counts.set(k, c);
|
|
270
|
+
}
|
|
271
|
+
const top = [...counts.entries()].filter(([, c]) => c.n > 1).sort((a, b) => b[1].n - a[1].n).slice(0, COMPANY_COLOURS.length);
|
|
272
|
+
const colour = new Map(top.map(([k], i) => [k, COMPANY_COLOURS[i]]));
|
|
273
|
+
return {
|
|
274
|
+
of: (d) => colour.get(companyKey(d.company)) || OTHER,
|
|
275
|
+
legend: [...top.map(([, c], i) => [c.name, COMPANY_COLOURS[i]]), ['Other', OTHER]],
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (mode === 'warmth' && social) {
|
|
279
|
+
return {
|
|
280
|
+
of: (d) => ((d.degree || 1) === 1 ? WARMTH_COLOURS[warmthOf(social.merged.get(keyFor(d)), social.asOf)] : '#2d3748'),
|
|
281
|
+
legend: [['Warm', WARMTH_COLOURS.warm], ['Cool', WARMTH_COLOURS.cool], ['Dormant', WARMTH_COLOURS.dormant], ['Never messaged', WARMTH_COLOURS.never]],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return { of: (d) => tierColors[d.tier] || '#666', legend: Object.entries(tierColors) };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Who Find matches: name, company, role or headline containing the text (2 letters or more). */
|
|
288
|
+
export function findMatches(nodes, query) {
|
|
289
|
+
const q = String(query || '').trim().toLowerCase();
|
|
290
|
+
if (q.length < 2) return null;
|
|
291
|
+
const hit = new Set();
|
|
292
|
+
for (const n of nodes) {
|
|
293
|
+
if ([n.name, n.company, n.role, n.headline].some((v) => v && String(v).toLowerCase().includes(q))) hit.add(n.id);
|
|
294
|
+
}
|
|
295
|
+
return hit;
|
|
296
|
+
}
|