six-degrees 0.7.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +32 -4
- package/.next/standalone/.next/routes-manifest.json +6 -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.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 +16 -16
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/backup/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/csv/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/restore/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +4 -4
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/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 +1 -1
- 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 +18 -18
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +19 -19
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/insights/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/insights/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/insights/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/insights/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/insights/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/insights/page.js +13 -0
- package/.next/standalone/.next/server/app/insights/page.js.map +5 -0
- package/.next/standalone/.next/server/app/insights/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/insights/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/insights.html +1 -0
- package/.next/standalone/.next/server/app/insights.meta +26 -0
- package/.next/standalone/.next/server/app/insights.rsc +35 -0
- package/.next/standalone/.next/server/app/insights.segments/_full.segment.rsc +35 -0
- package/.next/standalone/.next/server/app/insights.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/insights.segments/insights/__PAGE__.segment.rsc +33 -0
- 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 +18 -18
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -16
- 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 +19 -19
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -16
- 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 +19 -19
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -16
- 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 +19 -19
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +21 -20
- 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 +18 -18
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -16
- 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 +19 -19
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -16
- 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 +19 -19
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -16
- 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 +18 -18
- package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__063s0x7._.js → [root-of-the-server]__0r-0t-9._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ra6xyg._.js +15 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20rwfdw._.js → [root-of-the-server]__0rcpjad._.js} +5 -2
- 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/lib_02iz_9q._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_05agvk3._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_0v84u66._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_db-client_0_dch35.js +9 -9
- package/.next/standalone/.next/server/chunks/{lib_rpc_0jnfp2e.js → lib_rpc_00nndai.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0b17r6v.js → lib_rpc_03ibb-x.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0doq0t-.js → lib_rpc_03ri3ji.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0fd2rlw.js → lib_rpc_078ce48.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0a69mgr.js +3 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_005abye.js → lib_rpc_0cej3t8.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0cmh4g6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0nx2_pc.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0r79-w1.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0yx8ynd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_12p-o9-.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1466dnb.js +3 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_0is4qoc.js → lib_rpc_1hjjjr4.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_1sryxv6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1trn-x5.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1ygu_mv.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egamyq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04am_h8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_07zb7lu._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0_79idq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0gds903._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0klqt9z._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1k9qfsk._.js → _14jak5s._.js} +6 -2
- package/.next/standalone/.next/server/chunks/ssr/_14nxd3q._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1cj2yog._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1fm1ih_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1hdqw0-._.js +7 -0
- package/.next/standalone/.next/server/chunks/ssr/_1k2nmbw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0gy11ar._.js → _1r62six._.js} +6 -2
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_insights_page_actions_0-0tf75.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_0uuff77._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_components_1j3q-h5._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_01o3i5y._.js → app_components_settings_SectorPicker_1c-29qw.js} +2 -6
- package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_insights_page_1d_chja.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +14 -14
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +3 -3
- 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 +4 -4
- 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/02wnf8dkxegxg.js +34 -0
- package/.next/standalone/.next/static/chunks/0hggugbh84m4t.js +21 -0
- package/.next/{static/chunks/2eo9pqq9gay3r.js → standalone/.next/static/chunks/0ka4prpk3w9wn.js} +4 -4
- package/.next/standalone/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
- package/.next/standalone/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
- package/.next/{static/chunks/44yn0tcd9dfk3.js → standalone/.next/static/chunks/1bkt_hhjzvbh8.js} +1 -1
- package/.next/standalone/.next/static/chunks/1cmphb3kh2ers.css +3 -0
- package/.next/{static/chunks/1qbjermbf68xj.js → standalone/.next/static/chunks/1d8yv3k_atka7.js} +1 -1
- package/.next/standalone/.next/static/chunks/1ods5h86raivz.js +1 -0
- package/.next/standalone/.next/static/chunks/23hc51oa4vkhc.js +5 -0
- package/.next/standalone/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
- package/.next/standalone/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
- package/.next/{static/chunks/2zf4jmz9l3li2.js → standalone/.next/static/chunks/2w-y83mjlpnse.js} +1 -1
- package/.next/standalone/.next/static/chunks/2ws1xf293j-tm.js +5 -0
- package/.next/standalone/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
- package/.next/standalone/.next/static/chunks/3opgg1efrx8q6.js +1 -0
- package/.next/standalone/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
- package/.next/{static/chunks/0an_b6zi1r_qq.js → standalone/.next/static/chunks/3y40z6of3r9py.js} +1 -5
- package/.next/standalone/.next/static/chunks/403dpflln8ah5.js +1 -0
- package/.next/standalone/CHANGELOG.md +83 -0
- package/.next/standalone/README.md +40 -14
- package/.next/standalone/SECURITY.md +17 -4
- package/.next/standalone/app/api/outreach/route.js +13 -73
- package/.next/standalone/app/api/scraper/route.js +106 -5
- package/.next/standalone/app/components/AppHeader.js +22 -6
- package/.next/standalone/app/components/AppTabs.js +4 -0
- package/.next/standalone/app/components/AutoConnect.js +286 -0
- package/.next/standalone/app/components/ChainView.js +75 -2
- package/.next/standalone/app/components/FilterPanel.js +8 -1
- package/.next/standalone/app/components/LinkedInLimits.js +38 -13
- package/.next/standalone/app/components/OutlinkQuest.js +32 -11
- package/.next/standalone/app/components/ScanStatusBar.js +18 -3
- package/.next/standalone/app/components/ScanTrail.js +13 -3
- package/.next/standalone/app/components/SeparationView.js +6 -22
- package/.next/standalone/app/components/Sidebar.js +147 -38
- package/.next/standalone/app/components/insights/Boards.js +275 -0
- package/.next/standalone/app/components/insights/PeopleView.js +266 -0
- package/.next/standalone/app/components/insights/Rail.js +156 -0
- package/.next/standalone/app/components/insights/Report.js +382 -0
- package/.next/standalone/app/components/insights/parts.js +239 -0
- package/.next/standalone/app/components/settings/UsageSection.js +50 -3
- package/.next/standalone/app/globals.css +2 -3
- package/.next/standalone/app/insights/page.js +220 -0
- package/.next/standalone/app/page.js +1 -17
- package/.next/standalone/app/queue/page.js +30 -18
- package/.next/standalone/app/setup/page.js +71 -13
- package/.next/standalone/install.sh +7 -6
- package/.next/standalone/lib/auto-connect.js +145 -0
- package/.next/standalone/lib/companies.js +1 -1
- package/.next/standalone/lib/in-progress.js +46 -0
- package/.next/standalone/lib/insights-board.js +613 -0
- package/.next/standalone/lib/linkedin-limits.js +36 -11
- package/.next/standalone/lib/requests-client.js +10 -0
- package/.next/standalone/lib/requests.js +124 -0
- package/.next/standalone/lib/scan-state.js +1 -0
- package/.next/standalone/lib/scanner-setup.js +82 -3
- package/.next/standalone/lib/scraper-client.js +18 -5
- package/.next/standalone/lib/settings.js +6 -0
- package/.next/standalone/lib/usage.js +5 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/build-app.mjs +167 -11
- package/.next/standalone/scripts/build-desktop.mjs +3 -2
- package/.next/standalone/scripts/ci-mac-signing.sh +128 -0
- package/.next/standalone/scripts/entitlements/electron.plist +23 -0
- package/.next/standalone/scripts/entitlements/node.plist +27 -0
- package/.next/standalone/scripts/mac-sign.mjs +563 -0
- package/.next/standalone/scripts/make-dmg-background.mjs +2 -1
- package/.next/standalone/scripts/scrape.py +569 -9
- package/.next/standalone/scripts/set-signing-secrets.sh +96 -0
- package/.next/static/chunks/02wnf8dkxegxg.js +34 -0
- package/.next/static/chunks/0hggugbh84m4t.js +21 -0
- package/.next/{standalone/.next/static/chunks/2eo9pqq9gay3r.js → static/chunks/0ka4prpk3w9wn.js} +4 -4
- package/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
- package/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
- package/.next/{standalone/.next/static/chunks/44yn0tcd9dfk3.js → static/chunks/1bkt_hhjzvbh8.js} +1 -1
- package/.next/static/chunks/1cmphb3kh2ers.css +3 -0
- package/.next/{standalone/.next/static/chunks/1qbjermbf68xj.js → static/chunks/1d8yv3k_atka7.js} +1 -1
- package/.next/static/chunks/1ods5h86raivz.js +1 -0
- package/.next/static/chunks/23hc51oa4vkhc.js +5 -0
- package/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
- package/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
- package/.next/{standalone/.next/static/chunks/2zf4jmz9l3li2.js → static/chunks/2w-y83mjlpnse.js} +1 -1
- package/.next/static/chunks/2ws1xf293j-tm.js +5 -0
- package/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
- package/.next/static/chunks/3opgg1efrx8q6.js +1 -0
- package/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
- package/.next/{standalone/.next/static/chunks/0an_b6zi1r_qq.js → static/chunks/3y40z6of3r9py.js} +1 -5
- package/.next/static/chunks/403dpflln8ah5.js +1 -0
- package/CHANGELOG.md +83 -0
- package/README.md +40 -14
- package/SECURITY.md +17 -4
- package/lib/auto-connect.js +145 -0
- package/lib/companies.js +1 -1
- package/lib/in-progress.js +46 -0
- package/lib/insights-board.js +613 -0
- package/lib/linkedin-limits.js +36 -11
- package/lib/requests-client.js +10 -0
- package/lib/requests.js +124 -0
- package/lib/scan-state.js +1 -0
- package/lib/scanner-setup.js +82 -3
- package/lib/scraper-client.js +18 -5
- package/lib/settings.js +6 -0
- package/lib/usage.js +5 -0
- package/package.json +1 -1
- package/scripts/scrape.py +569 -9
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_4y8z7._.js +0 -8
- package/.next/standalone/.next/server/chunks/_02twlca._.js +0 -10
- package/.next/standalone/.next/server/chunks/lib_rpc_0nef_n7.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0plrzra.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_13a4m0c.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1if17c-.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1j-zw7v.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1sngt-q.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1v2sgme.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1vh-tgx.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1zaubz7.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_02sband._.js +0 -7
- package/.next/standalone/.next/server/chunks/ssr/_189g8lg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1f_n00p._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_components_0t908t-._.js +0 -3
- package/.next/standalone/.next/static/chunks/0e3g0sil79cus.js +0 -21
- package/.next/standalone/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
- package/.next/standalone/.next/static/chunks/163b-20vwa06i.js +0 -34
- package/.next/standalone/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
- package/.next/standalone/.next/static/chunks/23_1z47al92nz.js +0 -57
- package/.next/standalone/.next/static/chunks/260bomtr214m8.js +0 -34
- package/.next/standalone/.next/static/chunks/2xk2ra4piiuod.js +0 -34
- package/.next/standalone/.next/static/chunks/3b0-14cxqsprt.js +0 -1
- package/.next/standalone/.next/static/chunks/3jarus1_38s2z.js +0 -1
- package/.next/standalone/.next/static/chunks/3ssu341hav_sq.css +0 -3
- package/.next/standalone/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
- package/.next/static/chunks/0e3g0sil79cus.js +0 -21
- package/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
- package/.next/static/chunks/163b-20vwa06i.js +0 -34
- package/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
- package/.next/static/chunks/23_1z47al92nz.js +0 -57
- package/.next/static/chunks/260bomtr214m8.js +0 -34
- package/.next/static/chunks/2xk2ra4piiuod.js +0 -34
- package/.next/static/chunks/3b0-14cxqsprt.js +0 -1
- package/.next/static/chunks/3jarus1_38s2z.js +0 -1
- package/.next/static/chunks/3ssu341hav_sq.css +0 -3
- package/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → mwCGF3OV3CWV4SjUgg5T8}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → mwCGF3OV3CWV4SjUgg5T8}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → mwCGF3OV3CWV4SjUgg5T8}/_ssgManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → mwCGF3OV3CWV4SjUgg5T8}/_buildManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → mwCGF3OV3CWV4SjUgg5T8}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → mwCGF3OV3CWV4SjUgg5T8}/_ssgManifest.js +0 -0
|
@@ -35,6 +35,7 @@ import { useRouter } from 'next/navigation';
|
|
|
35
35
|
import { circleIndex, MAX_DEGREE } from '../../lib/circle';
|
|
36
36
|
import { localPhoto } from '../../lib/photos';
|
|
37
37
|
import { reachIndex, reachState, circleState, readyByCircle, circleScanCost, scanBars } from '../../lib/reach';
|
|
38
|
+
import { stoppedLine } from '../../lib/in-progress';
|
|
38
39
|
import { reachSegments, RING } from '../../lib/dot-rings';
|
|
39
40
|
import { ringLayout, chainTree, dotRadius, previewBand, tierBandLayout, circleActivity } from '../../lib/chain-layout';
|
|
40
41
|
import { noteCircle } from '../../lib/notifications';
|
|
@@ -42,7 +43,7 @@ import { useUser } from './UserProvider';
|
|
|
42
43
|
import { redundancy } from '../../lib/brokerage';
|
|
43
44
|
import { keyFor, score } from '../../lib/separation';
|
|
44
45
|
import { hasRequest } from '../../lib/requests-client';
|
|
45
|
-
import { watchScanner, scannerNow, isCircleScan } from '../../lib/scraper-client';
|
|
46
|
+
import { watchScanner, scannerNow, isCircleScan, beginScrape, scraperStatus, notReadyMessage, busyReason, resumePoint } from '../../lib/scraper-client';
|
|
46
47
|
import useRequests from './useRequests';
|
|
47
48
|
import { TIER_COLORS } from '../../lib/themes';
|
|
48
49
|
|
|
@@ -1111,6 +1112,17 @@ function EmptyCircle({ person, depth, reach, requests, scanning, canScan, onSele
|
|
|
1111
1112
|
const next = ordinal(depth + 1);
|
|
1112
1113
|
const yours = person.degree === 1;
|
|
1113
1114
|
const state = yours ? circleState(person, reach) : null;
|
|
1115
|
+
// Read partway with nothing saved yet (everyone on those pages was already
|
|
1116
|
+
// yours): that isn't a list that has been read, so it says it stopped, with
|
|
1117
|
+
// the card's Resume (Blake, 2026-10-03: "the ones they stopped and would like
|
|
1118
|
+
// to resume"). Where it stopped is the card's own answer, asked of the server
|
|
1119
|
+
// (GET /api/scraper?resume=<id>): the map's scan notes don't say how far a
|
|
1120
|
+
// list got. Undefined while it's asked.
|
|
1121
|
+
const stopped = useStoppedAt(state === 'scanned' && !scanning ? person.id : null);
|
|
1122
|
+
// Why Resume didn't start, kept here: a start greys this to "Scanning…" for a
|
|
1123
|
+
// moment even when it's refused, and the button's own state would go with it.
|
|
1124
|
+
const [problem, setProblem] = useState(null);
|
|
1125
|
+
if (state === 'scanned' && !scanning && stopped === undefined) return null; // never "has been read" by mistake
|
|
1114
1126
|
const cost = circleScanCost();
|
|
1115
1127
|
let title;
|
|
1116
1128
|
let body;
|
|
@@ -1118,6 +1130,10 @@ function EmptyCircle({ person, depth, reach, requests, scanning, canScan, onSele
|
|
|
1118
1130
|
if (scanning) {
|
|
1119
1131
|
title = `Scanning ${first}’s circle now`;
|
|
1120
1132
|
body = 'Their people appear here as the scan saves them, every 10 pages.';
|
|
1133
|
+
} else if (stopped) {
|
|
1134
|
+
title = `${first}’s scan stopped partway`;
|
|
1135
|
+
body = `${stoppedLine(stopped)}. Everyone on those pages was already one of your connections, so nobody new is here yet. Resume carries on from page ${stopped.nextPage}.`;
|
|
1136
|
+
if (canScan) action = <ResumeHere person={person} stopped={stopped} problem={problem} setProblem={setProblem} />;
|
|
1121
1137
|
} else if (state === 'hidden') {
|
|
1122
1138
|
title = `${first} keeps their connections hidden`;
|
|
1123
1139
|
body = 'LinkedIn doesn’t show their list, so there is no circle to scan.';
|
|
@@ -1149,7 +1165,7 @@ function EmptyCircle({ person, depth, reach, requests, scanning, canScan, onSele
|
|
|
1149
1165
|
return (
|
|
1150
1166
|
<div role="status" style={{
|
|
1151
1167
|
position: 'absolute', left: '50%', top, transform: 'translateX(-50%)', width: 'min(360px, calc(100% - 32px))',
|
|
1152
|
-
background: 'color-mix(in srgb, var(--sd-bg) 94%, transparent)', border: `1px solid ${state === 'hidden' ? 'rgba(var(--sd-ink, 255, 255, 255), 0.12)' : 'rgba(0,255,136,0.25)'}`,
|
|
1168
|
+
background: 'color-mix(in srgb, var(--sd-bg) 94%, transparent)', border: `1px solid ${state === 'hidden' ? 'rgba(var(--sd-ink, 255, 255, 255), 0.12)' : stopped && !scanning ? 'rgba(255,215,0,0.4)' : 'rgba(0,255,136,0.25)'}`,
|
|
1153
1169
|
borderRadius: 12, padding: '14px 16px', textAlign: 'center',
|
|
1154
1170
|
}}>
|
|
1155
1171
|
<div style={{ fontSize: 13, fontWeight: 800, color: state === 'hidden' ? 'var(--sd-fg-3, #aaa)' : 'var(--sd-fg-1, #fff)', marginBottom: 6 }}>
|
|
@@ -1161,6 +1177,63 @@ function EmptyCircle({ person, depth, reach, requests, scanning, canScan, onSele
|
|
|
1161
1177
|
);
|
|
1162
1178
|
}
|
|
1163
1179
|
|
|
1180
|
+
/**
|
|
1181
|
+
* Where Resume would carry on with one of your connections ({ nextPage,
|
|
1182
|
+
* pagesRead }), null when there's nothing to carry on with, undefined while
|
|
1183
|
+
* it's asked; and never asked for null. Asked again whenever `id` comes back,
|
|
1184
|
+
* as it does when a scan of theirs ends.
|
|
1185
|
+
*/
|
|
1186
|
+
function useStoppedAt(id) {
|
|
1187
|
+
const [got, setGot] = useState({ id: null, resume: undefined });
|
|
1188
|
+
useEffect(() => {
|
|
1189
|
+
if (!id) return undefined;
|
|
1190
|
+
let live = true;
|
|
1191
|
+
resumePoint(id).then((r) => { if (live) setGot({ id, resume: r }); }, () => { if (live) setGot({ id, resume: null }); });
|
|
1192
|
+
return () => { live = false; };
|
|
1193
|
+
}, [id]);
|
|
1194
|
+
if (!id) return null;
|
|
1195
|
+
return got.id === id ? got.resume : undefined;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* Resume for a circle that stopped partway, from the map: what the card's
|
|
1200
|
+
* Resume does (Sidebar.js CreateClusterCard), the same check first, carrying on
|
|
1201
|
+
* from the page it stopped at, never page 1 again. Once it starts, the empty
|
|
1202
|
+
* circle says it's being scanned. Greyed out, saying why, while another scan runs.
|
|
1203
|
+
*/
|
|
1204
|
+
function ResumeHere({ person, stopped, problem, setProblem }) {
|
|
1205
|
+
const busy = useSyncExternalStore(watchScanner, () => busyReason(scannerNow()), () => null);
|
|
1206
|
+
const [checking, setChecking] = useState(false);
|
|
1207
|
+
async function go() {
|
|
1208
|
+
setProblem(null);
|
|
1209
|
+
setChecking(true);
|
|
1210
|
+
const blocked = notReadyMessage(await scraperStatus().catch(() => null));
|
|
1211
|
+
setChecking(false);
|
|
1212
|
+
if (blocked) {
|
|
1213
|
+
setProblem(`${blocked} Open the Scan page to finish setting the scanner up.`);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
try {
|
|
1217
|
+
await beginScrape('resume', { id: person.id });
|
|
1218
|
+
} catch (e) {
|
|
1219
|
+
setProblem(e.message || 'Could not start the scan.');
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
const off = Boolean(busy) || checking;
|
|
1223
|
+
return (
|
|
1224
|
+
<>
|
|
1225
|
+
<button type="button" onClick={go} disabled={off} style={{
|
|
1226
|
+
...primary, border: 'none', cursor: off ? 'not-allowed' : 'pointer', opacity: off ? 0.45 : 1,
|
|
1227
|
+
}}>Resume from page {stopped.nextPage}</button>
|
|
1228
|
+
{(busy || problem) && (
|
|
1229
|
+
<div style={{ fontSize: 10.5, color: problem ? '#ff8080' : 'var(--sd-fg-3, #888)', marginTop: 8 }}>
|
|
1230
|
+
{problem || `${busy}. One scan at a time: this one can start when it finishes.`}
|
|
1231
|
+
</div>
|
|
1232
|
+
)}
|
|
1233
|
+
</>
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1164
1237
|
const crumb = { background: 'none', border: 'none', color: '#8fb8d6', fontSize: 11, fontWeight: 600, cursor: 'pointer', padding: 0 };
|
|
1165
1238
|
const note = {
|
|
1166
1239
|
fontSize: 11, padding: '6px 10px', borderRadius: 8, border: '1px solid', background: 'rgba(var(--sd-shade, 0, 0, 0), 0.6)', pointerEvents: 'auto',
|
|
@@ -34,9 +34,16 @@ export function TierGrid({ grid, counts, onChange, mode, isMobile }) {
|
|
|
34
34
|
const what = (d) => DEGREE_IS[mode]?.[d];
|
|
35
35
|
// Separation: your connections are the ways in, and all of them stay, whatever their tier.
|
|
36
36
|
const waysIn = mode === 'separation';
|
|
37
|
+
// Degrees and Separation draw no one past the 2nd degree: the 3rd, the people
|
|
38
|
+
// company scans find, is on Network Circle. Its dot says so instead of "no one
|
|
39
|
+
// yet", which read as locked (Blake, 2026-10-03: "the 3rd one is locked even
|
|
40
|
+
// though … we have 3rd degrees unlocked by now").
|
|
41
|
+
const elsewhere = (d) => d === 3 && (mode === 'degrees' || mode === 'separation');
|
|
37
42
|
const emptyTitle = (d, t) => (waysIn && d === 1
|
|
38
43
|
? 'Every connection stays a way in here, whatever their tier: the tiers choose who is ranked'
|
|
39
|
-
:
|
|
44
|
+
: elsewhere(d)
|
|
45
|
+
? `${t ? `${t}-Tier at 3rd degree` : '3rd degree'}, the people company scans find, isn't drawn here. Network Circle shows ${t ? 'them' : 'it'}.`
|
|
46
|
+
: `No ${t ? `${t}-Tier ` : 'one '}at ${DEGREE_NAMES[d]} degree yet`);
|
|
40
47
|
return (
|
|
41
48
|
<div style={{ marginBottom: 18 }}>
|
|
42
49
|
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 6 }}>
|
|
@@ -10,6 +10,7 @@ import { useState } from 'react';
|
|
|
10
10
|
import Link from 'next/link';
|
|
11
11
|
import { limitQuestion, limitNote, SAFE_LIMITS } from '../../lib/search-risk';
|
|
12
12
|
import { TIER_COLORS as THEME_TIERS } from '../../lib/themes';
|
|
13
|
+
import { inProgressSummary } from '../../lib/in-progress';
|
|
13
14
|
|
|
14
15
|
const LINE = '1px solid rgba(var(--sd-ink, 255, 255, 255), 0.1)';
|
|
15
16
|
const TIER = THEME_TIERS; // the theme's dot colours (lib/themes.js)
|
|
@@ -125,33 +126,57 @@ export function BudgetBox({ li, onSetLimits, disabled }) {
|
|
|
125
126
|
);
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Everyone whose list was only partly read: the Scan page's In progress card,
|
|
131
|
+
* with Resume for each and Resume all. Blake, 2026-10-03: "we need to have some
|
|
132
|
+
* sort of in progress ones in the scanner as well to see all the ones they
|
|
133
|
+
* stopped and would like to resume". It used to be folded away in Fine-tune the
|
|
134
|
+
* scanner, where nobody who had just stopped a scan would look; now it stands
|
|
135
|
+
* on its own right after step 4, and only while someone is stopped partway.
|
|
136
|
+
* Each name opens their circle on the map (/?chain=<id>, the link Watch it fill
|
|
137
|
+
* in uses), to see how far it got.
|
|
138
|
+
*/
|
|
128
139
|
export function PausedList({ paused = [], onResume, onResumeAll, disabled }) {
|
|
129
140
|
const [showAll, setShowAll] = useState(false);
|
|
130
141
|
if (!paused.length) return null;
|
|
131
|
-
const legacy = paused.filter((p) => p.legacy).length;
|
|
132
142
|
const shown = showAll ? paused : paused.slice(0, 8);
|
|
133
143
|
return (
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
<section aria-labelledby="in-progress-title" style={{
|
|
145
|
+
margin: '20px 0 4px', borderRadius: 14, overflow: 'hidden',
|
|
146
|
+
border: '1px solid rgba(255,215,0,0.3)', background: 'rgba(255,215,0,0.035)',
|
|
147
|
+
}}>
|
|
148
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '16px 18px 14px', borderBottom: LINE, flexWrap: 'wrap' }}>
|
|
149
|
+
<div style={{ flex: 1, minWidth: 220 }}>
|
|
150
|
+
<h2 id="in-progress-title" style={{ fontSize: 17, fontWeight: 800, margin: 0, display: 'flex', alignItems: 'center', gap: 9 }}>
|
|
151
|
+
{/* The pause sign the person card shows by a name that stopped partway (Sidebar.js StoppedPartway). */}
|
|
152
|
+
<span aria-hidden="true" style={{ display: 'inline-flex', gap: 2 }}>
|
|
153
|
+
<span style={{ width: 3, height: 12, borderRadius: 1, background: 'var(--sd-gold, #FFD700)' }} />
|
|
154
|
+
<span style={{ width: 3, height: 12, borderRadius: 1, background: 'var(--sd-gold, #FFD700)' }} />
|
|
155
|
+
</span>
|
|
156
|
+
In progress
|
|
157
|
+
</h2>
|
|
158
|
+
<div style={{ fontSize: 13, color: 'var(--sd-fg-3, #8b9a9a)', marginTop: 4, lineHeight: 1.5 }}>
|
|
159
|
+
{inProgressSummary(paused)}
|
|
140
160
|
</div>
|
|
141
161
|
</div>
|
|
142
|
-
<button onClick={onResumeAll} disabled={disabled} style={{ ...btn, opacity: disabled ? 0.4 : 1, cursor: disabled ? 'not-allowed' : 'pointer', background: disabled ? 'rgba(var(--sd-ink, 255, 255, 255), 0.06)' : 'linear-gradient(135deg, #3498DB, #9B59B6)' }}>
|
|
162
|
+
<button onClick={onResumeAll} disabled={disabled} style={{ ...btn, opacity: disabled ? 0.4 : 1, cursor: disabled ? 'not-allowed' : 'pointer', background: disabled ? 'rgba(var(--sd-ink, 255, 255, 255), 0.06)' : 'linear-gradient(135deg, #3498DB, #9B59B6)', ...(disabled ? {} : { color: '#fff' }) }}>
|
|
143
163
|
Resume all
|
|
144
164
|
</button>
|
|
145
165
|
</div>
|
|
146
166
|
<div>
|
|
147
167
|
{shown.map((p) => (
|
|
148
|
-
<div key={p.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '
|
|
168
|
+
<div key={p.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '9px 18px', borderBottom: '1px solid rgba(var(--sd-ink, 255, 255, 255), 0.04)' }}>
|
|
149
169
|
<span style={{ width: 8, height: 8, borderRadius: '50%', background: TIER[p.tier] || '#667', flexShrink: 0 }} />
|
|
150
170
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
151
|
-
<div style={{ fontSize: 13, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
171
|
+
<div style={{ fontSize: 13, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
172
|
+
<Link href={`/?chain=${encodeURIComponent(p.id)}`} title={`See ${p.name}’s circle on the map`} style={{
|
|
173
|
+
color: 'var(--sd-fg-1, #fff)', textDecoration: 'underline', textDecorationColor: 'rgba(var(--sd-ink, 255, 255, 255), 0.35)', textUnderlineOffset: 3,
|
|
174
|
+
}}>{p.name}</Link>
|
|
175
|
+
</div>
|
|
152
176
|
<div style={{ fontSize: 11.5, color: 'var(--sd-fg-3, #8b9a9a)' }}>
|
|
153
177
|
Read to page {p.pagesRead} · carries on at {p.nextPage}
|
|
154
|
-
{
|
|
178
|
+
{/* Kept whole: on a phone "Oct" and "3" landed on two lines. */}
|
|
179
|
+
{p.at ? <> <span style={{ whiteSpace: 'nowrap' }}>· {day(p.at)}</span></> : ''}
|
|
155
180
|
{p.unclear >= 2 ? ' · came back unclear twice, so it waits at the back of Resume all' : ''}
|
|
156
181
|
</div>
|
|
157
182
|
</div>
|
|
@@ -162,11 +187,11 @@ export function PausedList({ paused = [], onResume, onResumeAll, disabled }) {
|
|
|
162
187
|
))}
|
|
163
188
|
</div>
|
|
164
189
|
{paused.length > 8 && (
|
|
165
|
-
<button onClick={() => setShowAll(!showAll)} style={{ ...linkBtn, padding: '10px
|
|
190
|
+
<button onClick={() => setShowAll(!showAll)} style={{ ...linkBtn, padding: '10px 18px' }}>
|
|
166
191
|
{showAll ? 'Show fewer' : `Show all ${paused.length}`}
|
|
167
192
|
</button>
|
|
168
193
|
)}
|
|
169
|
-
</
|
|
194
|
+
</section>
|
|
170
195
|
);
|
|
171
196
|
}
|
|
172
197
|
|
|
@@ -13,6 +13,7 @@ import { buildQuest, XP_SEND, STAGE_SIZE } from '../../lib/quest';
|
|
|
13
13
|
import { initialsFor } from '../../lib/tiers';
|
|
14
14
|
import { localPhoto } from '../../lib/photos';
|
|
15
15
|
import { TIER_COLORS as THEME_TIERS } from '../../lib/themes';
|
|
16
|
+
import ConnectChoice from './AutoConnect';
|
|
16
17
|
|
|
17
18
|
const TIER = THEME_TIERS; // the theme's dot colours (lib/themes.js)
|
|
18
19
|
const LINE = '1px solid rgba(var(--sd-ink, 255, 255, 255), 0.1)';
|
|
@@ -49,7 +50,8 @@ function Ring({ progress, size = 86, color = '#FF6B35', children }) {
|
|
|
49
50
|
);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
// canAuto: Auto beside every invite (components/AutoConnect.js; Blake, 2026-10-03), on your own network.
|
|
54
|
+
export default function OutlinkQuest({ recs, sentIds, added, mappedIds, reach, onSend, onUndo, canAuto = false }) {
|
|
53
55
|
const quest = useMemo(() => buildQuest({ recs, sentIds, added, mappedIds, reach }), [recs, sentIds, added, mappedIds, reach]);
|
|
54
56
|
const [open, setOpen] = useState(null); // bridge id of the expanded cluster
|
|
55
57
|
const [toast, setToast] = useState(null);
|
|
@@ -57,6 +59,11 @@ export default function OutlinkQuest({ recs, sentIds, added, mappedIds, reach, o
|
|
|
57
59
|
|
|
58
60
|
async function send(person, cluster) {
|
|
59
61
|
await onSend(person);
|
|
62
|
+
cheer(person, cluster);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The points and the circle's progress, for an invite sent by hand or by Auto.
|
|
66
|
+
function cheer(person, cluster) {
|
|
60
67
|
const done = cluster.done + 1;
|
|
61
68
|
const first = String(cluster.bridge.name || '').split(/[\s,]+/)[0];
|
|
62
69
|
setToast(done >= cluster.targets.length
|
|
@@ -119,12 +126,17 @@ export default function OutlinkQuest({ recs, sentIds, added, mappedIds, reach, o
|
|
|
119
126
|
<div style={{ fontSize: 11.5, color: 'var(--sd-fg-2, #aab7b7)', margin: '10px 0' }}>
|
|
120
127
|
via <b style={{ color: TIER[cluster.bridge.tier] }}>{cluster.bridge.name}</b> · their circle {cluster.done}/{cluster.targets.length} this stage
|
|
121
128
|
</div>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
<ConnectChoice person={person} canAuto={canAuto} size="quest" onSent={(p) => cheer(p, cluster)} connect={(second) => {
|
|
130
|
+
const by = (
|
|
131
|
+
<>
|
|
132
|
+
{person.profile_url && (
|
|
133
|
+
<a href={person.profile_url} target="_blank" rel="noopener noreferrer" style={btnGhost}>Open on LinkedIn</a>
|
|
134
|
+
)}
|
|
135
|
+
<button onClick={() => send(person, cluster)} style={second ? btnGhost : btnHot}>I sent an invite · +{XP_SEND[person.tier] || 2}</button>
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
return second ? by : <div style={{ display: 'flex', gap: 8 }}>{by}</div>;
|
|
139
|
+
}} />
|
|
128
140
|
</div>
|
|
129
141
|
))}
|
|
130
142
|
</div>
|
|
@@ -186,10 +198,19 @@ export default function OutlinkQuest({ recs, sentIds, added, mappedIds, reach, o
|
|
|
186
198
|
<div style={{ fontWeight: 600, fontSize: 13 }}>{p.name} <span style={{ color: TIER[p.tier], fontSize: 11 }}>{p.tier}</span></div>
|
|
187
199
|
<div style={{ fontSize: 11.5, color: 'var(--sd-fg-3, #8b9a9a)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.headline}</div>
|
|
188
200
|
</div>
|
|
189
|
-
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
201
|
+
{sent ? (
|
|
202
|
+
<>
|
|
203
|
+
{p.profile_url && <a href={p.profile_url} target="_blank" rel="noopener noreferrer" style={btnGhost}>LinkedIn</a>}
|
|
204
|
+
<button onClick={() => onUndo(p)} style={{ ...btnGhost, color: 'var(--sd-green, #00ff88)' }}>Sent ✓ (undo)</button>
|
|
205
|
+
</>
|
|
206
|
+
) : (
|
|
207
|
+
<ConnectChoice person={p} canAuto={canAuto} size="quest" onSent={(x) => cheer(x, expanded)} connect={(second) => (
|
|
208
|
+
<>
|
|
209
|
+
{p.profile_url && <a href={p.profile_url} target="_blank" rel="noopener noreferrer" style={btnGhost}>LinkedIn</a>}
|
|
210
|
+
<button onClick={() => send(p, expanded)} style={second ? btnGhost : btnHot}>Sent · +{XP_SEND[p.tier] || 2}</button>
|
|
211
|
+
</>
|
|
212
|
+
)} />
|
|
213
|
+
)}
|
|
193
214
|
</div>
|
|
194
215
|
);
|
|
195
216
|
})}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// (lib/island.js). It only reports: the pacing and the caps live in the scanner
|
|
14
14
|
// (scripts/scrape.py, lib/linkedin-limits.js).
|
|
15
15
|
|
|
16
|
-
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
16
|
+
import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
17
17
|
import { usePathname } from 'next/navigation';
|
|
18
18
|
import Link from 'next/link';
|
|
19
19
|
import { watchScanner, scannerNow, stopScrape } from '../../lib/scraper-client';
|
|
@@ -35,6 +35,7 @@ const WHAT = {
|
|
|
35
35
|
photos: 'Saving photos',
|
|
36
36
|
messages: 'Reading your messages list',
|
|
37
37
|
'messages-full': 'Reading your whole messages history',
|
|
38
|
+
connect: 'Sending a connection request',
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
// Green while there's plenty left, amber past 60%, red past 90%.
|
|
@@ -59,6 +60,7 @@ export default function ScanStatusBar() {
|
|
|
59
60
|
// be swapped for a new one, or wrap to two rows.
|
|
60
61
|
const pathname = usePathname();
|
|
61
62
|
const [top, setTop] = useState(null);
|
|
63
|
+
const barRef = useRef(null);
|
|
62
64
|
const [centre, setCentre] = useState(null);
|
|
63
65
|
useEffect(() => {
|
|
64
66
|
let header = null;
|
|
@@ -82,7 +84,13 @@ export default function ScanStatusBar() {
|
|
|
82
84
|
};
|
|
83
85
|
const later = () => { if (!queued) queued = requestAnimationFrame(measure); };
|
|
84
86
|
later();
|
|
85
|
-
|
|
87
|
+
// Measuring waits for the next frame, which drew the notch once over the
|
|
88
|
+
// blank page: hidden here, before that frame is painted, the moment the
|
|
89
|
+
// header goes (and shown again the moment it's back).
|
|
90
|
+
const mo = new MutationObserver(() => {
|
|
91
|
+
if (barRef.current) barRef.current.style.visibility = document.querySelector('header') ? '' : 'hidden';
|
|
92
|
+
later();
|
|
93
|
+
});
|
|
86
94
|
mo.observe(document.body, { childList: true, subtree: true });
|
|
87
95
|
window.addEventListener('resize', later);
|
|
88
96
|
return () => { cancelAnimationFrame(queued); ro?.disconnect(); mo.disconnect(); window.removeEventListener('resize', later); };
|
|
@@ -98,6 +106,11 @@ export default function ScanStatusBar() {
|
|
|
98
106
|
return () => setNotchShown(false);
|
|
99
107
|
}, [showing]);
|
|
100
108
|
if (!status && !tabs) return null;
|
|
109
|
+
// Only with a page under it: between tabs the next page has no header until
|
|
110
|
+
// it has loaded, and the notch would hang over a blank screen (Blake,
|
|
111
|
+
// 2026-10-03: "the notch is seen the whole time as the screen blanks out
|
|
112
|
+
// loading to the next … have it appear with the section"). It fades in with it.
|
|
113
|
+
if (top == null) return null;
|
|
101
114
|
|
|
102
115
|
const p = job?.progress;
|
|
103
116
|
const step = p?.kind === 'batch' && p.total ? `${p.current || p.done || 0} of ${p.total}`
|
|
@@ -118,10 +131,12 @@ export default function ScanStatusBar() {
|
|
|
118
131
|
|
|
119
132
|
return (
|
|
120
133
|
<div
|
|
134
|
+
ref={barRef}
|
|
121
135
|
data-glass-panel="bar"
|
|
136
|
+
className="notch-in"
|
|
122
137
|
onMouseLeave={() => setOpen(false)}
|
|
123
138
|
style={{
|
|
124
|
-
position: 'fixed', left, top
|
|
139
|
+
position: 'fixed', left, top, transform: 'translateX(-50%)', zIndex: 60,
|
|
125
140
|
maxWidth: 'calc(100vw - 16px)',
|
|
126
141
|
padding: expanded && status ? '4px 6px 10px' : '4px 6px 5px',
|
|
127
142
|
borderRadius: '0 0 14px 14px', border: `1px solid ${running ? 'rgba(0,255,136,0.25)' : 'rgba(var(--sd-ink, 255, 255, 255), 0.1)'}`, borderTop: 'none',
|
|
@@ -57,9 +57,17 @@ export default function ScanTrail({ inset }) {
|
|
|
57
57
|
useEffect(() => {
|
|
58
58
|
const el = boxRef.current;
|
|
59
59
|
if (!el) return undefined;
|
|
60
|
-
|
|
60
|
+
// On a computer the tabs sit on this same line (AppHeader): the dots stop short of them.
|
|
61
|
+
const fit = () => {
|
|
62
|
+
const box = el.getBoundingClientRect();
|
|
63
|
+
const tabs = document.getElementById('main-tabs')?.getBoundingClientRect();
|
|
64
|
+
const width = tabs && tabs.left > box.left ? Math.min(box.width, tabs.left - box.left - 12) : box.width;
|
|
65
|
+
setRoom(Math.max(8, Math.floor(width / STEP) - 4));
|
|
66
|
+
};
|
|
67
|
+
const ro = new ResizeObserver(fit);
|
|
61
68
|
ro.observe(el);
|
|
62
|
-
|
|
69
|
+
window.addEventListener('resize', fit);
|
|
70
|
+
return () => { ro.disconnect(); window.removeEventListener('resize', fit); };
|
|
63
71
|
}, []);
|
|
64
72
|
|
|
65
73
|
const { pages } = shown;
|
|
@@ -69,7 +77,9 @@ export default function ScanTrail({ inset }) {
|
|
|
69
77
|
for (let i = first; i < pages; i++) dots.push(i);
|
|
70
78
|
const title = `${shown.label}: ${pages === 0 ? 'starting' : `${pages.toLocaleString('en-US')} page${pages === 1 ? '' : 's'} read`}. Open the Scan page`;
|
|
71
79
|
return (
|
|
72
|
-
|
|
80
|
+
// Above the page under the header: the dots straddle its line, and a page
|
|
81
|
+
// drawn after the header (Paths' panel) covered their lower half.
|
|
82
|
+
<div ref={boxRef} aria-hidden={!shown.on} style={{ position: 'absolute', left: inset, right: inset, bottom: -4, height: 7, pointerEvents: 'none', zIndex: 5 }}>
|
|
73
83
|
<style>{CSS}</style>
|
|
74
84
|
<Link href="/setup" title={title} tabIndex={shown.on ? 0 : -1} className="scan-trail" style={{
|
|
75
85
|
position: 'absolute', left: 0, top: 0, height: 7, display: 'flex', alignItems: 'center', gap: STEP - 5,
|
|
@@ -380,28 +380,13 @@ export default function SeparationView({ connections = [], degree2 = [], fullDeg
|
|
|
380
380
|
flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column',
|
|
381
381
|
background: 'var(--sd-page)', color: 'var(--sd-fg-1, #fff)', overflow: 'hidden',
|
|
382
382
|
}}>
|
|
383
|
-
{/* ──
|
|
383
|
+
{/* ── Top: the search box, and on a phone what it ranks ── */}
|
|
384
384
|
<div style={{ padding: `${notch ? 48 : 14}px ${SIDE}px 8px`, flexShrink: 0 }}>
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}}>Separation</h2>
|
|
391
|
-
<span style={{ fontSize: 12, color: 'var(--sd-fg-3, #aaa)', ...(isMobile ? {} : { flex: 1, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }) }}>
|
|
392
|
-
{isMobile
|
|
393
|
-
? `${plural(summary.people, 'person', 'people')} · ${fmt(summary.byTier.S)} S · ${fmt(summary.multi)} with 2+ ways`
|
|
394
|
-
: `${plural(summary.people, 'person', 'people')} two steps away · ${fmt(summary.byTier.S)} S-tier · ${fmt(summary.multi)} reachable 2+ ways · through ${fmt(summary.bridges)} of your connections`}
|
|
395
|
-
</span>
|
|
396
|
-
{!isMobile && searchBox}
|
|
397
|
-
</div>
|
|
398
|
-
{/* The whole population at a glance: one segment per tier, to scale. */}
|
|
399
|
-
<div aria-hidden="true" style={{ display: 'flex', gap: 1, height: 6, borderRadius: 3, overflow: 'hidden', margin: '8px 0 6px', background: 'rgba(var(--sd-ink, 255, 255, 255), 0.04)' }}>
|
|
400
|
-
{TIERS.filter((t) => summary.byTier[t] > 0).map((t) => (
|
|
401
|
-
<div key={t} style={{ flex: summary.byTier[t], background: tierColors[t] || '#555' }} />
|
|
402
|
-
))}
|
|
403
|
-
</div>
|
|
404
|
-
{/* On a computer the line says it on its own; what it ranks is the title's tooltip (RANK_NOTE). */}
|
|
385
|
+
{/* Blake, 2026-10-03: no title, count line or tier strip up here ("remove this
|
|
386
|
+
line of text … and the actual line of visual of the tiers"); the map and the
|
|
387
|
+
slider say it. The search box keeps the row. */}
|
|
388
|
+
{!isMobile && <div style={{ display: 'flex', justifyContent: 'flex-end' }}>{searchBox}</div>}
|
|
389
|
+
{/* What it ranks, on a phone, where there's room under the search box. */}
|
|
405
390
|
{isMobile && (
|
|
406
391
|
<div style={{ fontSize: 11, color: 'var(--sd-fg-4, #777)', lineHeight: 1.4 }}>Ranks reachability, not people · lists scanned so far</div>
|
|
407
392
|
)}
|
|
@@ -710,7 +695,6 @@ const MAP_CSS = `
|
|
|
710
695
|
.sepmap .in { animation: none; }
|
|
711
696
|
}
|
|
712
697
|
`;
|
|
713
|
-
const RANK_NOTE = 'Ranked by each person’s own score (title, company, headline). It ranks reachability, not people. From the lists scanned so far.';
|
|
714
698
|
const HALO = { paintOrder: 'stroke', stroke: 'var(--sd-bg)', strokeWidth: 3, strokeLinejoin: 'round' };
|
|
715
699
|
const at = (x, y) => ({ transform: `translate(${x}px, ${y}px)` });
|
|
716
700
|
|