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
|
@@ -19,7 +19,7 @@ import Link from 'next/link';
|
|
|
19
19
|
import { Section, Body, LINE } from '../ui';
|
|
20
20
|
import {
|
|
21
21
|
AUTO, PEOPLE_PER_SEARCH, DANGER_AT, REPORTED_MONTH, LEVELS, usageLevel, warningParts, estimate,
|
|
22
|
-
barMax, pacificText, whenText, untilText, agoText, hoursText,
|
|
22
|
+
barMax, pacificText, whenText, untilText, agoText, hoursText, INVITE_CAPS, REPORTED_WEEKLY_INVITES,
|
|
23
23
|
} from '../../../lib/usage';
|
|
24
24
|
import { RESTRICTED_AT, RISKY_DAILY, SAFE_LIMITS } from '../../../lib/search-risk';
|
|
25
25
|
import { paceOf, searchesPerHour } from '../../../lib/scan-pace';
|
|
@@ -100,6 +100,51 @@ function Meter({ title, used, of, max, color, ticks = [], band, children }) {
|
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Auto's connection requests (lib/auto-connect.js): the last 24 hours and the
|
|
105
|
+
* last 7 days, each against its cap, one bar under the other. Its own caps,
|
|
106
|
+
* not the search budget: a request is the one thing Six Degrees sends.
|
|
107
|
+
*/
|
|
108
|
+
function InviteMeter({ day, week, freesAt, weekFreesAt, now }) {
|
|
109
|
+
const known = Number.isFinite(day) && Number.isFinite(week);
|
|
110
|
+
const bars = [
|
|
111
|
+
{ label: 'Last 24 hours', used: day, cap: INVITE_CAPS.day },
|
|
112
|
+
{ label: 'Last 7 days', used: week, cap: INVITE_CAPS.week },
|
|
113
|
+
];
|
|
114
|
+
const left = known ? Math.max(0, Math.min(INVITE_CAPS.day - day, INVITE_CAPS.week - week)) : null;
|
|
115
|
+
return (
|
|
116
|
+
<div style={{ marginTop: 18 }} data-usage="invites">
|
|
117
|
+
<div style={{ fontSize: 13.5, fontWeight: 650, color: 'var(--sd-fg-1, #e8e8ee)' }}>Connection requests (Auto)</div>
|
|
118
|
+
{bars.map((b) => (
|
|
119
|
+
<div key={b.label} style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 8 }}>
|
|
120
|
+
<span style={{ flex: '0 0 88px', fontSize: 12, color: 'var(--sd-fg-3, #8b9a9a)' }}>{b.label}</span>
|
|
121
|
+
<div style={{ position: 'relative', flex: 1, height: 10, borderRadius: 5, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.07)' }}>
|
|
122
|
+
<div style={{
|
|
123
|
+
position: 'absolute', top: 0, bottom: 0, left: 0, borderRadius: 5, transition: 'width 0.4s ease',
|
|
124
|
+
width: Number.isFinite(b.used) ? at(b.used, Math.max(b.cap, b.used)) : 0,
|
|
125
|
+
background: b.used >= b.cap ? '#ff6b6b' : 'linear-gradient(90deg, #FFD700, #FF6B35)',
|
|
126
|
+
}} />
|
|
127
|
+
</div>
|
|
128
|
+
<span style={{ flex: '0 0 auto', fontSize: 13, color: 'var(--sd-fg-3, #8b9a9a)', fontVariantNumeric: 'tabular-nums', minWidth: 58, textAlign: 'right' }}>
|
|
129
|
+
<b style={{ fontSize: 15, color: 'var(--sd-fg-1, #fff)' }}>{count(b.used)}</b> of {b.cap}
|
|
130
|
+
</span>
|
|
131
|
+
</div>
|
|
132
|
+
))}
|
|
133
|
+
<div style={{ fontSize: 12.5, color: 'var(--sd-fg-3, #8b9a9a)', lineHeight: 1.6, marginTop: 6 }}>
|
|
134
|
+
{!known ? null
|
|
135
|
+
: freesAt ? <>Auto’s {INVITE_CAPS.day} a day are used. The next one frees {whenAndUntil(freesAt, now)}.</>
|
|
136
|
+
: weekFreesAt ? <>Auto’s {INVITE_CAPS.week} a week are used. The next one frees {whenAndUntil(weekFreesAt, now)}.</>
|
|
137
|
+
: <>{plural(left, 'request')} left for Auto now.</>}
|
|
138
|
+
<div style={small}>
|
|
139
|
+
Each press of Auto sends one request from the scanner’s Chrome, without a note, and opens their
|
|
140
|
+
profile once (a profile view). It stops at {INVITE_CAPS.day} in any 24 hours and {INVITE_CAPS.week} in any 7 days.
|
|
141
|
+
LinkedIn doesn’t publish its invitation limit; people commonly report about {REPORTED_WEEKLY_INVITES} a week.
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
103
148
|
function Row({ label, children }) {
|
|
104
149
|
return (
|
|
105
150
|
<div style={{ display: 'flex', gap: '4px 16px', flexWrap: 'wrap', padding: '10px 0', borderTop: LINE, fontSize: 13 }}>
|
|
@@ -260,6 +305,8 @@ export default function UsageSection() {
|
|
|
260
305
|
</div>
|
|
261
306
|
</Meter>
|
|
262
307
|
|
|
308
|
+
<InviteMeter day={u.invitesToday} week={u.invitesWeek} freesAt={u.invitesFreeAt} weekFreesAt={u.invitesWeekFreeAt} now={now} />
|
|
309
|
+
|
|
263
310
|
<div style={{ marginTop: 22 }}>
|
|
264
311
|
<Row label="Speed">
|
|
265
312
|
{pace.label}: about {searchesPerHour(limits.pace)} searches an hour at most while a scan runs.
|
|
@@ -301,8 +348,8 @@ export default function UsageSection() {
|
|
|
301
348
|
</div>
|
|
302
349
|
|
|
303
350
|
<div style={{ ...small, marginTop: 14, paddingTop: 12, borderTop: LINE }}>
|
|
304
|
-
Where these numbers come from: Six Degrees writes down every search
|
|
305
|
-
LinkedIn account, on this computer, and counts them here. Searches you make yourself on linkedin.com
|
|
351
|
+
Where these numbers come from: Six Degrees writes down every search, profile view and Auto request it makes
|
|
352
|
+
on your LinkedIn account, on this computer, and counts them here. Searches you make yourself on linkedin.com
|
|
306
353
|
aren’t in it, and LinkedIn shows no count of its own. The {RESTRICTED_AT} searches and the 20 to 25
|
|
307
354
|
profile views are what happened to one real account, not safe limits. Change the budget on the
|
|
308
355
|
{' '}<Link href="/setup" style={link}>Scan page</Link>, under Fine-tune the scanner. Nothing here is sent anywhere.
|
|
@@ -107,13 +107,12 @@ html:has([data-field-question]) {
|
|
|
107
107
|
border-radius: 3px;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
/* Where the scan status bar sits: just under the header (app/components/ScanStatusBar.js). */
|
|
111
|
-
:root { --scan-bar-top: 94px; }
|
|
112
|
-
@media (max-width: 767px) { :root { --scan-bar-top: 66px; } }
|
|
113
110
|
|
|
114
111
|
/* The notch's running dot (app/components/ScanStatusBar.js). */
|
|
115
112
|
@keyframes notch-pulse { 50% { opacity: 0.35; } }
|
|
116
113
|
@media (prefers-reduced-motion: no-preference) { .notch-pulse { animation: notch-pulse 1.6s ease-in-out infinite; } }
|
|
114
|
+
@keyframes notch-in { from { opacity: 0; } }
|
|
115
|
+
@media (prefers-reduced-motion: no-preference) { .notch-in { animation: notch-in 0.25s ease-out; } }
|
|
117
116
|
|
|
118
117
|
/* The Social tab's CRM (app/social/Crm.js): the list and the person side by
|
|
119
118
|
side, stacked on a phone. In Pipeline the board takes the whole width and
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// Insights: your network ranked by power, and the boards that read it
|
|
4
|
+
// (Blake, 2026-10-03: "stats and insights … data and insights based off their
|
|
5
|
+
// circles … almost no moral tie to the person … base it off value like
|
|
6
|
+
// richest person or highest power people … an intro perspective look at the
|
|
7
|
+
// data of what we have"). Mock-up B, "The Power Index", is the page; mock-up
|
|
8
|
+
// C, the scrolling report, is its Report view.
|
|
9
|
+
//
|
|
10
|
+
// Its views sit in the notch under the header, as every page's do
|
|
11
|
+
// (lib/island.js setNotchTabs): People, Kingmakers, Gatekeepers, Companies,
|
|
12
|
+
// Industries and Report. Every number comes from lib/insights-board.js, which
|
|
13
|
+
// says where each came from; nothing here is estimated and nothing is about
|
|
14
|
+
// money.
|
|
15
|
+
|
|
16
|
+
import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
|
|
17
|
+
import { useSearchParams } from 'next/navigation';
|
|
18
|
+
import Link from 'next/link';
|
|
19
|
+
import AppHeader from '../components/AppHeader';
|
|
20
|
+
import OnboardingGate from '../components/OnboardingGate';
|
|
21
|
+
import { useUser } from '../components/UserProvider';
|
|
22
|
+
import useRequests from '../components/useRequests';
|
|
23
|
+
import { setNotchTabs } from '../../lib/island';
|
|
24
|
+
import { loadNetwork } from '../../lib/network';
|
|
25
|
+
import { loadCsvNetwork } from '../../lib/csv';
|
|
26
|
+
import { loadScanNotes, NO_SCAN_NOTES } from '../../lib/scraper-client';
|
|
27
|
+
import { hasRequest } from '../../lib/requests-client';
|
|
28
|
+
import { networkLevel } from '../../lib/level';
|
|
29
|
+
import { IS_DEMO } from '../../lib/demo';
|
|
30
|
+
import {
|
|
31
|
+
boardBase, powerIndex, kingmakers, gatekeepers, hiddenGiants, tierByRarity, untapped,
|
|
32
|
+
companyPower, industries, titleLadder, coverage, howSure, reachSummary,
|
|
33
|
+
} from '../../lib/insights-board';
|
|
34
|
+
import PeopleView from '../components/insights/PeopleView';
|
|
35
|
+
import { KingmakersBoard, GatekeepersBoard, CompaniesBoard, IndustriesBoard } from '../components/insights/Boards';
|
|
36
|
+
import Report from '../components/insights/Report';
|
|
37
|
+
import { fmt, useIsMobile, TITLE } from '../components/insights/parts';
|
|
38
|
+
|
|
39
|
+
const VIEWS = ['people', 'kingmakers', 'gatekeepers', 'companies', 'industries', 'report'];
|
|
40
|
+
const LABELS = { people: 'People', kingmakers: 'Kingmakers', gatekeepers: 'Gatekeepers', companies: 'Companies', industries: 'Industries', report: 'Report' };
|
|
41
|
+
|
|
42
|
+
export default function InsightsPage() {
|
|
43
|
+
// Suspense because the page reads the address's ?view= (useSearchParams),
|
|
44
|
+
// which Next requires to sit inside one for the page to build (TRAPS §41).
|
|
45
|
+
return <OnboardingGate><Suspense><InsightsInner /></Suspense></OnboardingGate>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function InsightsInner() {
|
|
49
|
+
const { userId } = useUser();
|
|
50
|
+
const isMobile = useIsMobile();
|
|
51
|
+
const asked = useSearchParams().get('view');
|
|
52
|
+
const [view, setView] = useState(() => (VIEWS.includes(asked) ? asked : 'people'));
|
|
53
|
+
const [net, setNet] = useState(null); // { degree1, degree2, degree3, notes, source, budget }
|
|
54
|
+
const [failed, setFailed] = useState(null);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (IS_DEMO || !userId) return;
|
|
58
|
+
let live = true;
|
|
59
|
+
(async () => {
|
|
60
|
+
try {
|
|
61
|
+
// The sample (this window) or a kept CSV import, else your own network:
|
|
62
|
+
// lib/csv.js openNetworkSource, the one rule every page opens on.
|
|
63
|
+
const local = await loadCsvNetwork();
|
|
64
|
+
if (local) {
|
|
65
|
+
if (live) setNet({ degree1: local.degree1 || [], degree2: local.degree2 || [], degree3: [], notes: NO_SCAN_NOTES, source: local.source || 'csv', budget: null });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Your daily search budget and speed, for what scanning the next circles would take:
|
|
69
|
+
// ?usage only reads the scanner's files (no checks, no Python, nothing written).
|
|
70
|
+
const usage = fetch('/api/scraper?usage').then((r) => (r.ok ? r.json() : null)).catch(() => null);
|
|
71
|
+
const [n, notes, used] = await Promise.all([loadNetwork(userId), loadScanNotes(), usage]);
|
|
72
|
+
const limits = used?.limits;
|
|
73
|
+
if (live) setNet({ ...n, notes, source: 'own', budget: limits ? { daily: limits.daily, pace: limits.pace } : null });
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (live) setFailed(e.message || 'The network couldn’t be loaded.');
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
78
|
+
return () => { live = false; };
|
|
79
|
+
}, [userId]);
|
|
80
|
+
|
|
81
|
+
// The quick part first: the ranking and the boards that read circles.
|
|
82
|
+
const base = useMemo(() => (net ? boardBase(net) : null), [net]);
|
|
83
|
+
const data = useMemo(() => {
|
|
84
|
+
if (!base) return null;
|
|
85
|
+
const index = powerIndex(base);
|
|
86
|
+
return {
|
|
87
|
+
index, reach: reachSummary(base), kings: kingmakers(base), gates: gatekeepers(base), hidden: hiddenGiants(base),
|
|
88
|
+
grid: tierByRarity(base), cover: coverage(base, net.budget || {}), sure: howSure(index),
|
|
89
|
+
};
|
|
90
|
+
}, [base, net]);
|
|
91
|
+
// Companies and titles read every headline (about half a second on 7,000
|
|
92
|
+
// people), so they follow once the table is on screen.
|
|
93
|
+
const [slow, setSlow] = useState(null);
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!base) return undefined;
|
|
96
|
+
const t = setTimeout(() => {
|
|
97
|
+
const companies = companyPower(base);
|
|
98
|
+
setSlow({ base, companies, industries: industries(companies), ladder: titleLadder(base) });
|
|
99
|
+
}, 30);
|
|
100
|
+
return () => clearTimeout(t);
|
|
101
|
+
}, [base]);
|
|
102
|
+
const heavy = slow && slow.base === base ? slow : null;
|
|
103
|
+
|
|
104
|
+
const requests = useRequests();
|
|
105
|
+
const isAsked = useCallback((row) => hasRequest(row, requests), [requests]);
|
|
106
|
+
const open = useMemo(() => (base ? untapped(base, isAsked) : null), [base, isAsked]);
|
|
107
|
+
|
|
108
|
+
const pick = useCallback((v) => {
|
|
109
|
+
setView(v);
|
|
110
|
+
try {
|
|
111
|
+
const u = new URL(window.location.href);
|
|
112
|
+
if (v === 'people') u.searchParams.delete('view'); else u.searchParams.set('view', v);
|
|
113
|
+
window.history.replaceState(null, '', u.pathname + u.search);
|
|
114
|
+
} catch { /* the address stays */ }
|
|
115
|
+
window.scrollTo(0, 0);
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
// The views in the notch, with how many each ranks.
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (!data || data.index.total === 0) { setNotchTabs(null); return undefined; }
|
|
121
|
+
const count = {
|
|
122
|
+
people: data.index.total, kingmakers: data.kings.scanned, gatekeepers: data.gates.list.length,
|
|
123
|
+
companies: heavy?.companies.count, industries: heavy?.industries.list.length,
|
|
124
|
+
};
|
|
125
|
+
setNotchTabs({
|
|
126
|
+
items: VIEWS.map((key) => ({
|
|
127
|
+
key,
|
|
128
|
+
label: count[key] ? <>{LABELS[key]}<small style={{ fontSize: 10.5, opacity: 0.65, fontWeight: 600 }}>{fmt(count[key])}</small></> : LABELS[key],
|
|
129
|
+
})),
|
|
130
|
+
current: view, onPick: pick,
|
|
131
|
+
});
|
|
132
|
+
return () => setNotchTabs(null);
|
|
133
|
+
}, [data, heavy, view, pick]);
|
|
134
|
+
|
|
135
|
+
const level = useMemo(() => (net ? networkLevel(net.degree1, net.degree2) : null), [net]);
|
|
136
|
+
const csvMode = Boolean(net && net.source !== 'own');
|
|
137
|
+
|
|
138
|
+
if (IS_DEMO) {
|
|
139
|
+
return (
|
|
140
|
+
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, color: 'var(--sd-fg-2, #ccc)', fontFamily: 'var(--sd-font)' }}>
|
|
141
|
+
<h1 style={{ fontSize: 20, fontWeight: 700, margin: 0, color: 'var(--sd-fg-1, #fff)' }}>Not part of the demo</h1>
|
|
142
|
+
<p style={{ fontSize: 13, margin: 0 }}>The public demo includes the Network Circle and Degrees views only.</p>
|
|
143
|
+
<Link href="/" style={{ fontSize: 13, color: 'var(--sd-fg-3, #888)', textDecoration: 'none' }}>← Back to the network</Link>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const shared = { data, heavy, open, net, source: net?.source, isMobile, onView: pick };
|
|
149
|
+
return (
|
|
150
|
+
<div style={{ minHeight: '100vh', background: 'var(--sd-page)', color: 'var(--sd-fg-1, #fff)', fontFamily: 'var(--sd-font)' }}>
|
|
151
|
+
<AppHeader active="insights" csvMode={csvMode} level={level} isMobile={isMobile} />
|
|
152
|
+
{/* Room for the notch, which hangs under the header with the views in it */}
|
|
153
|
+
<main style={{ maxWidth: view === 'report' ? 920 : 1400, margin: '0 auto', padding: isMobile ? '52px 12px 0' : '58px 30px 0' }}>
|
|
154
|
+
{failed ? <Message title="The network couldn’t be loaded" body={failed} />
|
|
155
|
+
: !data ? <Message title="Reading your network…" />
|
|
156
|
+
: data.index.total === 0 ? <Empty source={net.source} />
|
|
157
|
+
: view === 'people' ? <PeopleView {...shared} requests={requests} />
|
|
158
|
+
: view === 'kingmakers' ? <KingmakersBoard {...shared} />
|
|
159
|
+
: view === 'gatekeepers' ? <GatekeepersBoard {...shared} />
|
|
160
|
+
: view === 'companies' ? <CompaniesBoard {...shared} />
|
|
161
|
+
: view === 'industries' ? <IndustriesBoard {...shared} />
|
|
162
|
+
: <Report {...shared} />}
|
|
163
|
+
<Footer net={net} data={data} isMobile={isMobile} />
|
|
164
|
+
</main>
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function Message({ title, body }) {
|
|
170
|
+
return (
|
|
171
|
+
<div style={{ padding: '80px 0', textAlign: 'center' }}>
|
|
172
|
+
<div style={{ fontSize: 17, fontWeight: 700 }}>{title}</div>
|
|
173
|
+
{body && <div style={{ fontSize: 13, color: 'var(--sd-fg-3, #888)', marginTop: 8 }}>{body}</div>}
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** No one to rank yet: where people come from. */
|
|
179
|
+
function Empty({ source }) {
|
|
180
|
+
return (
|
|
181
|
+
<div style={{ maxWidth: 560, margin: '60px auto', textAlign: 'center' }}>
|
|
182
|
+
<h1 style={TITLE}>The Power Index</h1>
|
|
183
|
+
<p style={{ fontSize: 15, color: 'var(--sd-fg-2, #b8c4c4)', lineHeight: 1.6, margin: '12px 0 22px' }}>
|
|
184
|
+
Nothing to rank yet. Once your connections are in, everyone within two steps of you shows here, ranked by power,
|
|
185
|
+
with who holds the doors and what’s still closed.
|
|
186
|
+
</p>
|
|
187
|
+
<div style={{ display: 'flex', gap: 10, justifyContent: 'center', flexWrap: 'wrap' }}>
|
|
188
|
+
{source === 'own' && (
|
|
189
|
+
<Link href="/setup" style={{ padding: '10px 16px', borderRadius: 8, fontWeight: 700, fontSize: 13.5, textDecoration: 'none', color: '#000', background: 'linear-gradient(135deg, #00ff88, #1abc9c)' }}>
|
|
190
|
+
Scan your connections →
|
|
191
|
+
</Link>
|
|
192
|
+
)}
|
|
193
|
+
<Link href="/import" style={{ padding: '10px 16px', borderRadius: 8, fontWeight: 700, fontSize: 13.5, textDecoration: 'none', color: 'var(--sd-fg-1, #fff)', border: '1px solid rgba(var(--sd-ink, 255, 255, 255), 0.2)' }}>
|
|
194
|
+
Import a CSV
|
|
195
|
+
</Link>
|
|
196
|
+
</div>
|
|
197
|
+
<p style={{ fontSize: 12.5, color: 'var(--sd-fg-4, #6f7a88)', marginTop: 18 }}>
|
|
198
|
+
Or look around first: the <Link href="/" style={{ color: 'var(--sd-blue, #3498DB)', textDecoration: 'none' }}>map’s welcome screen</Link> opens a sample network where every person is invented.
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** The rule every screen with a tier says, and which network this is. */
|
|
205
|
+
function Footer({ net, data, isMobile }) {
|
|
206
|
+
const what = !net || !data?.index.total ? null
|
|
207
|
+
: net.source === 'sample' ? 'The sample network: every person in it is invented.'
|
|
208
|
+
: net.source === 'csv' ? `Your CSV import: ${fmt(data?.reach.d1)} connections, no circles.`
|
|
209
|
+
: data ? `Counted from your ${fmt(data.reach.d1)} connections and the ${fmt(data.reach.circles)} circle${data.reach.circles === 1 ? '' : 's'} you’ve scanned.` : null;
|
|
210
|
+
return (
|
|
211
|
+
<footer style={{
|
|
212
|
+
margin: '36px 0 0', padding: '18px 0 34px', borderTop: '1px solid rgba(var(--sd-ink, 255, 255, 255), 0.08)',
|
|
213
|
+
display: 'flex', flexDirection: isMobile ? 'column' : 'row', justifyContent: 'space-between', gap: isMobile ? 8 : 20,
|
|
214
|
+
color: 'var(--sd-fg-4, #6f7a88)', fontSize: 11.5, lineHeight: 1.5,
|
|
215
|
+
}}>
|
|
216
|
+
<div><b style={{ color: 'var(--sd-fg-2, #b8c4c4)' }}>Power ranks reachability, not people.</b> A tier is a statement about network position, not human worth.</div>
|
|
217
|
+
{what && <div>{what}</div>}
|
|
218
|
+
</footer>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
@@ -4,8 +4,6 @@ import { Suspense, useEffect, useState, useRef, useMemo, useCallback } from 'rea
|
|
|
4
4
|
import { useSearchParams } from 'next/navigation';
|
|
5
5
|
import { fillsCircles, loadScanNotes, NO_SCAN_NOTES } from '../lib/scraper-client';
|
|
6
6
|
import useScanner from './components/useScanner';
|
|
7
|
-
import useRequests from './components/useRequests';
|
|
8
|
-
import { requestCount } from '../lib/requests-client';
|
|
9
7
|
import { loadNetwork } from '../lib/network';
|
|
10
8
|
import { resolveView } from './components/views';
|
|
11
9
|
import { peopleByDegree } from '../lib/degrees';
|
|
@@ -105,7 +103,6 @@ function HomeInner() {
|
|
|
105
103
|
const [scanNotes, setScanNotes] = useState(NO_SCAN_NOTES);
|
|
106
104
|
const shapeRef = useRef('');
|
|
107
105
|
// Everyone with a request out, shared with every view (lib/requests-client.js).
|
|
108
|
-
const pendingCount = requestCount(useRequests());
|
|
109
106
|
|
|
110
107
|
useEffect(() => {
|
|
111
108
|
if (!userId) return;
|
|
@@ -305,20 +302,7 @@ function HomeInner() {
|
|
|
305
302
|
</button>
|
|
306
303
|
</div>
|
|
307
304
|
)}
|
|
308
|
-
|
|
309
|
-
{/* Pending count badge — in header: everyone with a request out, once each (lib/requests-client.js) */}
|
|
310
|
-
{isDegreesMode && !csvMode && pendingCount > 0 && (
|
|
311
|
-
<Link href="/queue" style={{
|
|
312
|
-
padding: '5px 12px', borderRadius: 16, fontSize: 12, fontWeight: 600, textDecoration: 'none',
|
|
313
|
-
background: 'rgba(255,107,53,0.12)', color: 'var(--sd-orange, #FF6B35)',
|
|
314
|
-
border: '1px solid rgba(255,107,53,0.25)',
|
|
315
|
-
display: 'inline-flex', alignItems: 'center', gap: 5,
|
|
316
|
-
}}>
|
|
317
|
-
<span style={{ fontSize: 10 }}>⏳</span>
|
|
318
|
-
{pendingCount} Pending
|
|
319
|
-
</Link>
|
|
320
|
-
)}
|
|
321
|
-
</AppHeader>
|
|
305
|
+
/>
|
|
322
306
|
|
|
323
307
|
<div style={{ display: 'flex', height: isMobile ? 'calc(100vh - 70px)' : 'calc(100vh - 130px)', position: 'relative' }}>
|
|
324
308
|
<FilterPanel
|
|
@@ -19,6 +19,10 @@ import { localPhoto } from '../../lib/photos';
|
|
|
19
19
|
import { reachIndex } from '../../lib/reach';
|
|
20
20
|
import { loadScanNotes } from '../../lib/scraper-client';
|
|
21
21
|
import { TIER_COLORS } from '../../lib/themes';
|
|
22
|
+
import ConnectChoice, { secondaryLook } from '../components/AutoConnect';
|
|
23
|
+
|
|
24
|
+
// Add, standing second to Auto: LinkedIn's blue, as an outline.
|
|
25
|
+
const BLUE_OUTLINE = { color: '#5aa9e6', line: 'rgba(0,119,181,0.75)' };
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
// Who to reach first: power, +1 when the company their score is built on is a
|
|
@@ -342,6 +346,7 @@ function QueueInner() {
|
|
|
342
346
|
reach={reach}
|
|
343
347
|
onSend={(r) => markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {})}
|
|
344
348
|
onUndo={(r) => undoRequest(r).catch(() => {})}
|
|
349
|
+
canAuto={!IS_DEMO}
|
|
345
350
|
/>
|
|
346
351
|
</div>
|
|
347
352
|
)}
|
|
@@ -600,12 +605,16 @@ function QueueInner() {
|
|
|
600
605
|
{sentIds.has(r.id) ? (
|
|
601
606
|
<span style={{ padding: '3px 6px', borderRadius: 3, fontSize: 8, fontWeight: 700, background: 'rgba(255,107,53,0.15)', color: 'var(--sd-orange, #FF6B35)', flexShrink: 0 }}>Sent</span>
|
|
602
607
|
) : (
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
608
|
+
// Auto first, then Add as an outline (components/AutoConnect.js).
|
|
609
|
+
<ConnectChoice person={r} canAuto={!IS_DEMO} size="row" connect={(second) => (
|
|
610
|
+
<a href={r.profile_url} target="_blank" rel="noopener noreferrer"
|
|
611
|
+
onClick={(e) => {
|
|
612
|
+
e.stopPropagation();
|
|
613
|
+
markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {});
|
|
614
|
+
}}
|
|
615
|
+
style={second ? { ...secondaryLook('row', BLUE_OUTLINE), padding: '3px 6px', fontSize: 8 }
|
|
616
|
+
: { padding: '3px 6px', borderRadius: 3, fontSize: 8, fontWeight: 700, background: '#0077B5', color: '#fff', textDecoration: 'none', flexShrink: 0 }}>Add</a>
|
|
617
|
+
)} />
|
|
609
618
|
)}
|
|
610
619
|
</div>
|
|
611
620
|
))}
|
|
@@ -670,18 +679,21 @@ function QueueInner() {
|
|
|
670
679
|
background: 'rgba(255,107,53,0.15)', color: 'var(--sd-orange, #FF6B35)', flexShrink: 0,
|
|
671
680
|
}}>⏳ Sent</span>
|
|
672
681
|
) : (
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
682
|
+
// Auto first, the better pick; Add beside it as an outline (components/AutoConnect.js).
|
|
683
|
+
<ConnectChoice person={r} canAuto={!IS_DEMO} size="row" connect={(second) => (
|
|
684
|
+
<a href={r.profile_url} target="_blank" rel="noopener noreferrer"
|
|
685
|
+
onClick={(e) => {
|
|
686
|
+
e.stopPropagation();
|
|
687
|
+
// Opens LinkedIn, and marks the request sent everywhere.
|
|
688
|
+
markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {});
|
|
689
|
+
}}
|
|
690
|
+
style={second ? secondaryLook('row', BLUE_OUTLINE) : {
|
|
691
|
+
padding: '4px 8px', borderRadius: 4, fontSize: 9, fontWeight: 700,
|
|
692
|
+
background: '#0077B5', color: '#fff', textDecoration: 'none', flexShrink: 0,
|
|
693
|
+
}}>
|
|
694
|
+
Add
|
|
695
|
+
</a>
|
|
696
|
+
)} />
|
|
685
697
|
)}
|
|
686
698
|
</div>
|
|
687
699
|
))}
|
|
@@ -5,7 +5,8 @@ import { useSearchParams } from 'next/navigation';
|
|
|
5
5
|
import OnboardingGate from '../components/OnboardingGate';
|
|
6
6
|
import Link from 'next/link';
|
|
7
7
|
import { stopScrape, pickedPerson, scanRequest, HIDE_CHROME_KEY } from '../../lib/scraper-client';
|
|
8
|
-
import { setupStep, askForField } from '../../lib/scanner-setup';
|
|
8
|
+
import { setupStep, askForField, appManagementStep } from '../../lib/scanner-setup';
|
|
9
|
+
import { saveSettings } from '../../lib/settings-client';
|
|
9
10
|
import { RISK_POINTS } from '../../lib/scan-risk';
|
|
10
11
|
import { IS_DEMO } from '../../lib/demo';
|
|
11
12
|
import { circleScanCost } from '../../lib/reach';
|
|
@@ -35,6 +36,7 @@ const ACTION_LABELS = {
|
|
|
35
36
|
'auto-bridge-retry': 'Mapping 2nd degree, hidden ones included',
|
|
36
37
|
resume: 'Carrying on with one paused list',
|
|
37
38
|
'resume-all': 'Carrying on with every paused list',
|
|
39
|
+
connect: 'Sending a connection request (Auto)',
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
export default function SetupPage() {
|
|
@@ -81,6 +83,8 @@ function SetupInner() {
|
|
|
81
83
|
// loads, null when it couldn't be read. And the answer given here, if any.
|
|
82
84
|
const [settings, setSettings] = useState(undefined);
|
|
83
85
|
const [field, setField] = useState(null);
|
|
86
|
+
// Step 1's App Management item, answered here ('done' or 'skipped'), if it was.
|
|
87
|
+
const [appAnswer, setAppAnswer] = useState(null);
|
|
84
88
|
// Someone sent here to have their circle scanned (Bridge Chains, the Degrees
|
|
85
89
|
// panel's Ready to scan): /setup?scan=<id>. Nothing starts until it's confirmed.
|
|
86
90
|
// The router's search params rather than window.location: every way here is a
|
|
@@ -142,6 +146,15 @@ function SetupInner() {
|
|
|
142
146
|
window.scrollTo(0, 0);
|
|
143
147
|
}
|
|
144
148
|
|
|
149
|
+
// Done or Skip on the App Management item: it goes at once, and the answer is
|
|
150
|
+
// kept so it isn't offered again. If that can't be saved it still goes for
|
|
151
|
+
// now and comes back next time, which is all it costs (as Skip for now does,
|
|
152
|
+
// app/components/FieldStep.js).
|
|
153
|
+
async function answerAppManagement(answer) {
|
|
154
|
+
setAppAnswer(answer);
|
|
155
|
+
await saveSettings({ appManagement: answer });
|
|
156
|
+
}
|
|
157
|
+
|
|
145
158
|
// "I understand": when, kept with your settings so it travels with a copy.
|
|
146
159
|
async function acceptRisk() {
|
|
147
160
|
setError(null);
|
|
@@ -211,6 +224,10 @@ function SetupInner() {
|
|
|
211
224
|
// Your field, asked once when your connections are in, before who they know
|
|
212
225
|
// (lib/scanner-setup.js askForField); left out while that isn't known (null).
|
|
213
226
|
const askField = IS_DEMO || field ? false : askForField(s, settings);
|
|
227
|
+
// On a Mac, macOS may ask about App Management when the scanner starts Chrome
|
|
228
|
+
// (lib/scanner-setup.js appManagementStep): offered once, under step 1, and
|
|
229
|
+
// never part of whether step 1 is done.
|
|
230
|
+
const appMgmt = IS_DEMO || appAnswer ? false : appManagementStep(s, settings);
|
|
214
231
|
// Step 4, honestly: the first circle shows in minutes, the rest takes days.
|
|
215
232
|
const pace = li?.limits?.pace;
|
|
216
233
|
const firstCircle = durationText(firstCircleSeconds(pace));
|
|
@@ -256,7 +273,7 @@ function SetupInner() {
|
|
|
256
273
|
You launch it; it does the reading; you watch it go.
|
|
257
274
|
</div>
|
|
258
275
|
<div style={{ display: 'flex', gap: '8px 18px', flexWrap: 'wrap', marginTop: 16 }}>
|
|
259
|
-
{['Never posts or messages anyone', 'Never sees your password', 'Nothing leaves this Mac', 'Stops the moment you say'].map((t) => (
|
|
276
|
+
{['Never posts or messages anyone; sends a request only when you press Auto', 'Never sees your password', 'Nothing leaves this Mac', 'Stops the moment you say'].map((t) => (
|
|
260
277
|
<span key={t} style={{ fontSize: 12.5, color: 'var(--sd-fg-1, #cfe8dc)', display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
261
278
|
<span style={{ color: 'var(--sd-green, #00ff88)', fontWeight: 900 }}>✓</span>{t}
|
|
262
279
|
</span>
|
|
@@ -374,6 +391,10 @@ function SetupInner() {
|
|
|
374
391
|
more={s && step1.chrome && (
|
|
375
392
|
<Launch href={step1.chrome.href} quiet={Boolean(step1.button)}>{step1.chrome.label}</Launch>
|
|
376
393
|
)} />
|
|
394
|
+
{/* Blake, 2026-10-04: "shouldnt we have in the onboarding for scan … a button where the
|
|
395
|
+
user is basically brought to the app management and enables it like Flow does".
|
|
396
|
+
Optional, so it sits under the step and never holds it up. */}
|
|
397
|
+
{appMgmt && <AppManagementItem item={appMgmt} onAnswer={answerAppManagement} />}
|
|
377
398
|
</StepRow>
|
|
378
399
|
<StepRow n={2} label="Sign in" state={stateOf(2, !!c.signedIn)}>
|
|
379
400
|
<Mission title="Sign in to LinkedIn, once" time="You do this part" done={!!c.signedIn}
|
|
@@ -523,11 +544,23 @@ function SetupInner() {
|
|
|
523
544
|
</div>
|
|
524
545
|
)}
|
|
525
546
|
|
|
547
|
+
{/* ---- In progress: everyone whose scan stopped partway, with Resume (Blake, 2026-10-03:
|
|
548
|
+
"some sort of in progress ones in the scanner as well to see all the ones they stopped
|
|
549
|
+
and would like to resume"). Right after step 4, where someone who just stopped a scan
|
|
550
|
+
looks; it was folded away in Fine-tune, and nobody found it. Under Stop while a scan
|
|
551
|
+
runs, so Stop stays right under the step. Only there while someone is. ---- */}
|
|
552
|
+
<PausedList
|
|
553
|
+
paused={s?.paused || []}
|
|
554
|
+
disabled={!canSearch}
|
|
555
|
+
onResume={(p) => run('resume', { id: p.id })}
|
|
556
|
+
onResumeAll={() => run('resume-all', { maxBridges: batch })}
|
|
557
|
+
/>
|
|
558
|
+
|
|
526
559
|
{/* Everything you might want to change, out of the way until you do */}
|
|
527
560
|
<details style={{ margin: '18px 0 4px', borderRadius: 10, border: LINE, background: 'rgba(var(--sd-ink, 255, 255, 255), 0.02)' }}>
|
|
528
561
|
<summary style={{ padding: '12px 16px', cursor: 'pointer', fontSize: 13.5, fontWeight: 650, color: 'var(--sd-fg-2, #cfd8d8)' }}>
|
|
529
562
|
Fine-tune the scanner
|
|
530
|
-
<span style={{ fontWeight: 500, color: 'var(--sd-fg-4, #778)', marginLeft: 8 }}>daily budget, how much of each list, the hidden window
|
|
563
|
+
<span style={{ fontWeight: 500, color: 'var(--sd-fg-4, #778)', marginLeft: 8 }}>daily budget, how much of each list, the hidden window</span>
|
|
531
564
|
</summary>
|
|
532
565
|
<div style={{ padding: '4px 16px 16px', display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
533
566
|
<BudgetBox li={li} disabled={busy} onSetLimits={(l) => run('set-limits', l)} />
|
|
@@ -586,12 +619,6 @@ function SetupInner() {
|
|
|
586
619
|
</Btn>
|
|
587
620
|
<span style={{ fontSize: 12, color: 'var(--sd-fg-3, #8b9a9a)', marginLeft: 10 }}>People whose list was hidden last time, in case they’ve opened it.</span>
|
|
588
621
|
</div>
|
|
589
|
-
<PausedList
|
|
590
|
-
paused={s?.paused || []}
|
|
591
|
-
disabled={!canSearch}
|
|
592
|
-
onResume={(p) => run('resume', { id: p.id })}
|
|
593
|
-
onResumeAll={() => run('resume-all', { maxBridges: batch })}
|
|
594
|
-
/>
|
|
595
622
|
</div>
|
|
596
623
|
</details>
|
|
597
624
|
|
|
@@ -664,7 +691,7 @@ function ScanOne({ pick, pages, setPages, li, running, s, canSearch, busy, onSta
|
|
|
664
691
|
{circle === 'scanned' && (
|
|
665
692
|
<div style={{ color: 'var(--sd-gold, #FFD700)', fontSize: 12.5, marginTop: 6 }}>
|
|
666
693
|
Their circle is already scanned. This reads their whole list again from page 1; to carry on
|
|
667
|
-
from where a read stopped, use Resume on their card or
|
|
694
|
+
from where a read stopped, use Resume on their card or under In progress below.
|
|
668
695
|
</div>
|
|
669
696
|
)}
|
|
670
697
|
{circle === 'hidden' && (
|
|
@@ -792,15 +819,46 @@ function Mission({ title, time, body, launch, more, done = false, children }) {
|
|
|
792
819
|
);
|
|
793
820
|
}
|
|
794
821
|
|
|
822
|
+
/**
|
|
823
|
+
* Step 1's optional item on a Mac (lib/scanner-setup.js appManagementStep):
|
|
824
|
+
* why macOS may ask about App Management while scanning, the button that opens
|
|
825
|
+
* that pane, and Done or Skip, either of which puts it away for good.
|
|
826
|
+
*/
|
|
827
|
+
function AppManagementItem({ item, onAnswer }) {
|
|
828
|
+
return (
|
|
829
|
+
<section aria-labelledby="app-management-title" data-app-management style={{
|
|
830
|
+
marginTop: 10, padding: '14px 18px', borderRadius: 14,
|
|
831
|
+
background: 'rgba(var(--sd-ink, 255, 255, 255), 0.025)',
|
|
832
|
+
border: '1px dashed rgba(var(--sd-ink, 255, 255, 255), 0.16)',
|
|
833
|
+
}}>
|
|
834
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 10, flexWrap: 'wrap' }}>
|
|
835
|
+
<span id="app-management-title" style={{ fontSize: 16, fontWeight: 800 }}>{item.title}</span>
|
|
836
|
+
<span style={{ fontSize: 12, color: 'var(--sd-fg-2, #8fd9b6)', marginLeft: 'auto' }}>Optional, once</span>
|
|
837
|
+
</div>
|
|
838
|
+
<div style={{ fontSize: 13.5, color: 'var(--sd-fg-3, #9fb0bb)', lineHeight: 1.6, margin: '6px 0 12px', maxWidth: 600 }}>{item.text}</div>
|
|
839
|
+
<div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
|
|
840
|
+
<Launch href={item.href} quiet>Open App Management</Launch>
|
|
841
|
+
<Btn onClick={() => onAnswer('done')}>Done</Btn>
|
|
842
|
+
<Btn onClick={() => onAnswer('skipped')}>Skip</Btn>
|
|
843
|
+
</div>
|
|
844
|
+
<div style={{ fontSize: 12, color: 'var(--sd-fg-4, #778)', marginTop: 10 }}>It’s in {item.where}.</div>
|
|
845
|
+
</section>
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
795
849
|
/**
|
|
796
850
|
* The big go button. `quiet` once that step is done, so the next thing to do
|
|
797
|
-
* stands out. With `href`, a link that looks the same
|
|
798
|
-
*
|
|
851
|
+
* stands out. With `href`, a link that looks the same: a web address (Chrome's
|
|
852
|
+
* download) opens outside the app, which the desktop app sends to the browser.
|
|
853
|
+
* A System Settings address (App Management) opens in place: a browser then
|
|
854
|
+
* asks to open System Settings without leaving an empty tab behind, and the
|
|
855
|
+
* desktop app opens System Settings itself (desktop/main.mjs).
|
|
799
856
|
*/
|
|
800
857
|
function Launch({ children, onClick, disabled, quiet = false, href }) {
|
|
801
858
|
if (href) {
|
|
859
|
+
const web = /^https?:/i.test(href);
|
|
802
860
|
return (
|
|
803
|
-
<a className="launch-go" href={href} target
|
|
861
|
+
<a className="launch-go" href={href} {...(web ? { target: '_blank', rel: 'noreferrer' } : {})} style={{
|
|
804
862
|
...launchLook(false, quiet), textDecoration: 'none',
|
|
805
863
|
}}>
|
|
806
864
|
<span aria-hidden="true" style={{ fontSize: quiet ? 11 : 13 }}>▶</span>{children}
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
# 3. copies "Six Degrees.app" into /Applications (or ~/Applications)
|
|
10
10
|
# 4. opens it
|
|
11
11
|
#
|
|
12
|
-
# Why a script
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
12
|
+
# Why a script as well as the .dmg: one line that picks the right build for this
|
|
13
|
+
# Mac, checks it against the published checksums, installs it and opens it, and
|
|
14
|
+
# running it again updates. Until 0.8.0 the app wasn't signed, and this was also
|
|
15
|
+
# the way around macOS's first-launch block (a file fetched with curl isn't
|
|
16
|
+
# marked as downloaded from the internet). Since then the app is signed by Blake
|
|
17
|
+
# Burford and notarized by Apple (docs/brain/DESKTOP.md D4), so the .dmg opens
|
|
18
|
+
# like any Mac app too.
|
|
18
19
|
#
|
|
19
20
|
# Your data is never touched: it lives in ~/.six-degrees, not in the app, so
|
|
20
21
|
# running this again later is also how you update.
|