Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assets/locales/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ msgstr "Unbounded"
msgid "help_fight_global_internet_censorship"
msgstr "Help Fight Global Internet Censorship"

msgid "share_bandwidth"
msgstr "Share Bandwidth"

msgid "unbounded_description"
msgstr "When enabled, Unbounded shares a small amount of your bandwidth to help people in censored countries access the open internet."

msgid "vpn_settings"
msgstr "VPN Settings"

Expand Down
256 changes: 256 additions & 0 deletions assets/unbounded/globe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Unbounded Globe</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 100%; height: 100%;
overflow: hidden;
background: transparent;
}
#globe-container {
width: 100%; height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
#globe-container canvas { touch-action: none; }
</style>
</head>
<body>
<div id="globe-container"></div>

<script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script>
<script src="https://unpkg.com/three-globe@2.27.4/dist/three-globe.min.js"></script>
<script src="https://unpkg.com/three@0.160.0/examples/js/controls/OrbitControls.js"></script>
<script>
(function() {
'use strict';

// --- Configuration ---
const COLORS = {
arcOrigin: 'rgba(0, 188, 212, 0.75)',
arcPeer: 'rgba(255, 193, 7, 0.75)',
pointOrigin: 'rgba(0, 188, 212, 0.15)',
pointPeer: 'rgba(255, 193, 7, 0.15)',
atmosphere: 'rgba(0, 188, 212, 1)',
gapFill: 'rgba(0, 188, 212, 0.4)',
};
const ARCH_ALTITUDE_MIN = 0.3;
const ARCH_ALTITUDE_GAP = 0.05;
const UV_MAP_DARK = 'https://embed.lantern.io/uv-map-dark.png';

// --- State ---
let arcs = [];
let points = [];
let originLat = null;
let originLng = null;
let countryPeerCounts = {};

// --- Globe Setup ---
const container = document.getElementById('globe-container');
const width = () => container.clientWidth;
const height = () => container.clientHeight;

const globe = new ThreeGlobe()
.globeImageUrl(UV_MAP_DARK)
.showAtmosphere(true)
.atmosphereColor(COLORS.atmosphere)
.atmosphereAltitude(0.25)
.arcColor('color')
.arcDashLength(1)
.arcDashGap(0.5)
.arcDashInitialGap(1)
.arcDashAnimateTime(1000)
.arcStroke(d => d.ghost ? 10 : 2)
.arcsTransitionDuration(0)
.pointColor('color')
.pointRadius(4)
.pointAltitude(0)
.pointsTransitionDuration(500);

// Three.js scene
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
powerPreference: 'high-performance',
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(width(), height());
renderer.setClearColor(0x000000, 0);
container.appendChild(renderer.domElement);

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, width() / height(), 1, 5000);
camera.position.z = 400;

// Lighting
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambient);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.25);
dirLight.position.set(0, 500, 0);
camera.add(dirLight);
scene.add(camera);

scene.add(globe);

// Controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = false;
controls.enablePan = false;
controls.autoRotate = true;
controls.autoRotateSpeed = 1.5;
controls.maxDistance = 1500;
controls.minDistance = 300;
controls.enableDamping = true;
controls.dampingFactor = 0.1;

// Animation loop
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();

// Resize
const ro = new ResizeObserver(() => {
camera.aspect = width() / height();
camera.updateProjectionMatrix();
renderer.setSize(width(), height());
});
ro.observe(container);

// --- Arc/Point Management ---
function updateGlobe() {
globe.arcsData(arcs);
globe.pointsData(points);
}

function rebuildPoints() {
const pts = [];
if (originLat !== null) {
pts.push({ lat: originLat, lng: originLng, color: COLORS.pointOrigin, id: -1 });
}
arcs.forEach(a => {
if (!a.ghost) {
pts.push({ lat: a.endLat, lng: a.endLng, color: COLORS.pointPeer, id: a.workerIdx });
}
});
points = pts;
}

function calcAltitude(count) {
return ARCH_ALTITUDE_MIN + (count - 1) * ARCH_ALTITUDE_GAP;
}

// --- Message API ---
// Messages from Flutter via postMessage or JavaScript handler
//
// setOrigin: { type: 'setOrigin', lat: number, lng: number }
// addArc: { type: 'addArc', workerIdx: number, endLat: number, endLng: number, country: string, iso: string }
// removeArc: { type: 'removeArc', workerIdx: number }
// clear: { type: 'clear' }
// setTheme: { type: 'setTheme', theme: 'dark' | 'light' }

function handleMessage(data) {
switch (data.type) {
case 'setOrigin':
originLat = data.lat;
originLng = data.lng;
// Update existing arcs with new origin
arcs.forEach(a => { a.startLat = originLat; a.startLng = originLng; });
rebuildPoints();
updateGlobe();
break;

case 'addArc': {
// Remove existing arc for this worker
arcs = arcs.filter(a => a.workerIdx !== data.workerIdx);

const iso = data.iso || 'XX';
countryPeerCounts[iso] = (countryPeerCounts[iso] || 0) + 1;

arcs.push({
startLat: originLat || 0,
startLng: originLng || 0,
endLat: data.endLat,
endLng: data.endLng,
country: data.country || '',
iso: iso,
workerIdx: data.workerIdx,
count: countryPeerCounts[iso],
altitude: calcAltitude(countryPeerCounts[iso]),
ghost: false,
color: [COLORS.arcOrigin, COLORS.arcPeer],
});
rebuildPoints();
updateGlobe();
break;
}

case 'removeArc': {
const removed = arcs.find(a => a.workerIdx === data.workerIdx);
arcs = arcs.filter(a => a.workerIdx !== data.workerIdx);
if (removed) {
const iso = removed.iso;
countryPeerCounts[iso] = Math.max(0, (countryPeerCounts[iso] || 1) - 1);
}
rebuildPoints();
updateGlobe();
break;
}

case 'clear':
arcs = [];
points = [];
countryPeerCounts = {};
updateGlobe();
break;

case 'setTheme': {
const dark = data.theme !== 'light';
globe.globeImageUrl(dark
? 'https://embed.lantern.io/uv-map-dark.png'
: 'https://embed.lantern.io/uv-map.png');
globe.atmosphereColor(dark
? 'rgba(0, 188, 212, 1)'
: 'rgba(0, 122, 124, 1)');
globe.atmosphereAltitude(dark ? 0.25 : 0.2);
break;
}
}
}

// Listen for postMessage from Flutter WebView
window.addEventListener('message', function(e) {
if (e.data && typeof e.data === 'object' && e.data.type) {
handleMessage(e.data);
} else if (typeof e.data === 'string') {
try { handleMessage(JSON.parse(e.data)); } catch(_) {}
}
});

// Also expose a global function for evaluateJavascript calls
window.unboundedGlobe = { handleMessage: handleMessage };

// Start with a demo arc to show the globe is alive
// This will be cleared when real data arrives
setTimeout(function() {
if (arcs.length === 0 && originLat === null) {
// Show a subtle idle animation
originLat = 37.7749;
originLng = -122.4194;
rebuildPoints();
updateGlobe();
}
}, 2000);

})();
</script>
</body>
</html>
40 changes: 31 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ module github.com/getlantern/lantern

go 1.25.4

// replace github.com/getlantern/radiance => ../radiance
replace github.com/getlantern/radiance => ../radiance

// replace github.com/getlantern/broflake => ../unbounded

// replace github.com/getlantern/lantern-server-provisioner => ../lantern-server-provisioner

// replace github.com/sagernet/sing-box => ../sing-box-minimal

replace github.com/enobufs/go-nats => github.com/noahlevenson/go-nats v0.0.0-20230720174341-49df1f749775

replace github.com/quic-go/quic-go => github.com/getlantern/quic-go-unbounded-fork v0.51.3-unbounded

replace github.com/sagernet/sing => github.com/getlantern/sing v0.7.18-lantern

replace github.com/sagernet/sing-box => github.com/getlantern/sing-box-minimal v1.12.19-lantern
Expand Down Expand Up @@ -156,6 +162,7 @@ require (
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/enobufs/go-nats v0.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/flynn/noise v1.0.1-0.20220214164934-d803f5c4b0f4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
Expand All @@ -164,7 +171,8 @@ require (
github.com/getlantern/algeneva v0.0.0-20250307163401-1824e7b54f52 // indirect
github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f // indirect
github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 // indirect
github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f // indirect
github.com/getlantern/broflake v0.0.0-20260223195036-4065257e0911 // indirect
github.com/getlantern/common v1.2.1-0.20260223192400-cc00002ef6c7 // indirect
github.com/getlantern/dnstt v0.0.0-20260112160750-05100563bd0d // indirect
github.com/getlantern/fronted v0.0.0-20260219001615-7eabaa834efe // indirect
github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 // indirect
Expand All @@ -185,6 +193,7 @@ require (
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 // indirect
github.com/go-llsqlite/crawshaw v0.4.0 // indirect
github.com/go-resty/resty/v2 v2.16.5 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/goccy/go-yaml v1.19.0 // indirect
Expand All @@ -193,6 +202,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
Expand Down Expand Up @@ -222,26 +232,37 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
github.com/pion/datachannel v1.5.10 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/dtls/v2 v2.2.12 // indirect
github.com/pion/dtls/v3 v3.0.6 // indirect
github.com/pion/ice/v2 v2.3.24 // indirect
github.com/pion/interceptor v0.1.37 // indirect
github.com/pion/logging v0.2.3 // indirect
github.com/pion/ice/v4 v4.0.10 // indirect
github.com/pion/interceptor v0.1.40 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/mdns/v2 v2.0.7 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.15 // indirect
github.com/pion/rtp v1.8.12 // indirect
github.com/pion/sctp v1.8.37 // indirect
github.com/pion/sdp/v3 v3.0.11 // indirect
github.com/pion/rtp v1.8.19 // indirect
github.com/pion/sctp v1.8.39 // indirect
github.com/pion/sdp/v3 v3.0.14 // indirect
github.com/pion/srtp/v2 v2.0.18 // indirect
github.com/pion/srtp/v3 v3.0.6 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.4 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport v0.14.1 // indirect
github.com/pion/transport/v2 v2.2.10 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn v1.3.7 // indirect
github.com/pion/turn/v2 v2.1.3 // indirect
github.com/pion/turn/v4 v4.0.2 // indirect
github.com/pion/webrtc/v3 v3.2.40 // indirect
github.com/pion/webrtc/v4 v4.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/quic-go/quic-go v0.51.0 // indirect
github.com/r3labs/sse/v2 v2.10.0 // indirect
github.com/refraction-networking/utls v1.8.2 // indirect
github.com/refraction-networking/water v0.7.1-alpha // indirect
Expand Down Expand Up @@ -269,6 +290,7 @@ require (
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tkuchiki/go-timezone v0.2.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xtaci/kcp-go/v5 v5.6.20 // indirect
Expand Down
Loading
Loading