diff --git a/src/components/Island/IslandUniverse.jsx b/src/components/Island/IslandUniverse.jsx
index 44bdd90..cee77cf 100644
--- a/src/components/Island/IslandUniverse.jsx
+++ b/src/components/Island/IslandUniverse.jsx
@@ -51,8 +51,8 @@ export default function IslandUniverse({ onClose, onIslandClick }) {
]
},
center: [centerLng, centerLat],
- zoom: 0.8,
- pitch: 70, // 3D angle
+ zoom: 0.5, // Zoomed out more to see spread islands
+ pitch: 65, // 3D angle
bearing: 0,
antialias: true,
attributionControl: false
@@ -64,14 +64,21 @@ export default function IslandUniverse({ onClose, onIslandClick }) {
// Create island markers
const markers = [];
+ // Use grid-based positioning to prevent overlap
+ const gridSize = Math.ceil(Math.sqrt(islands.length));
+ const spacing = 8; // degrees between islands
+
islands.forEach((island, idx) => {
- // Truly scatter islands randomly across wide area
- const randomAngle = Math.random() * Math.PI * 2;
- const randomRadius = 5 + Math.random() * 15; // Large random radius 5-20 degrees
- const randomOffsetX = (Math.random() - 0.5) * 10; // Additional random scatter
- const randomOffsetY = (Math.random() - 0.5) * 10;
- const lng = centerLng + Math.cos(randomAngle) * randomRadius + randomOffsetX;
- const lat = centerLat + Math.sin(randomAngle) * randomRadius + randomOffsetY;
+ // Grid position with random offset for organic feel
+ const gridX = idx % gridSize;
+ const gridY = Math.floor(idx / gridSize);
+ const baseX = (gridX - gridSize / 2) * spacing;
+ const baseY = (gridY - gridSize / 2) * spacing;
+ // Add controlled randomness within cell
+ const jitterX = (Math.random() - 0.5) * spacing * 0.6;
+ const jitterY = (Math.random() - 0.5) * spacing * 0.6;
+ const lng = centerLng + baseX + jitterX;
+ const lat = centerLat + baseY + jitterY;
// Create marker element
const el = document.createElement('div');
@@ -81,32 +88,46 @@ export default function IslandUniverse({ onClose, onIslandClick }) {
const islandSVG = generateMiniIsland(island.seed || idx, terrainColor);
+ const postCount = island.content_count || 0;
+ const terrain = island.terrain_type || 'tropical';
+
el.innerHTML = `
${islandSVG}
+
@${island.username}
+ box-shadow: 0 4px 16px rgba(0,0,0,0.5), 0 0 12px ${terrainColor}33;
+ backdrop-filter: blur(8px);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ ">
+
@${island.username}
+
+ ${postCount} posts • ${terrain}
+
+
`;
@@ -260,7 +281,7 @@ function getTerrainColor(terrainType) {
}
/**
- * Generate a mini island SVG shape (organic, irregular)
+ * Generate a detailed island SVG shape (bigger, more features)
*/
function generateMiniIsland(seed, terrainColor) {
// Seeded random
@@ -269,57 +290,127 @@ function generateMiniIsland(seed, terrainColor) {
return seed / 233280;
};
- // Generate organic island shape using bezier curves
- const size = 50;
+ // Much bigger island
+ const size = 120;
const centerX = size / 2;
const centerY = size / 2;
- const numPoints = 8;
+ const numPoints = 12;
let path = '';
const points = [];
for (let i = 0; i < numPoints; i++) {
const angle = (i / numPoints) * Math.PI * 2;
- const radiusVariation = 0.7 + rng() * 0.6; // Random radius
- const radius = (size / 2) * radiusVariation;
+ const radiusVariation = 0.65 + rng() * 0.5;
+ const radius = (size / 2.2) * radiusVariation;
const x = centerX + Math.cos(angle) * radius;
const y = centerY + Math.sin(angle) * radius;
points.push({ x, y });
}
- // Create smooth curve using quadratic bezier
path = `M ${points[0].x} ${points[0].y}`;
for (let i = 0; i < numPoints; i++) {
const current = points[i];
const next = points[(i + 1) % numPoints];
- const controlX = (current.x + next.x) / 2 + (rng() - 0.5) * 5;
- const controlY = (current.y + next.y) / 2 + (rng() - 0.5) * 5;
+ const controlX = (current.x + next.x) / 2 + (rng() - 0.5) * 8;
+ const controlY = (current.y + next.y) / 2 + (rng() - 0.5) * 8;
path += ` Q ${controlX} ${controlY} ${next.x} ${next.y}`;
}
path += ' Z';
- // Add some trees/details
- const numTrees = 2 + Math.floor(rng() * 3);
+ // Beach ring
+ let beachPath = '';
+ const beachPoints = [];
+ for (let i = 0; i < numPoints; i++) {
+ const angle = (i / numPoints) * Math.PI * 2;
+ const radiusVariation = 0.72 + rng() * 0.45;
+ const radius = (size / 2.1) * radiusVariation;
+ beachPoints.push({
+ x: centerX + Math.cos(angle) * radius,
+ y: centerY + Math.sin(angle) * radius
+ });
+ }
+ beachPath = `M ${beachPoints[0].x} ${beachPoints[0].y}`;
+ for (let i = 0; i < numPoints; i++) {
+ const current = beachPoints[i];
+ const next = beachPoints[(i + 1) % numPoints];
+ beachPath += ` Q ${(current.x + next.x) / 2} ${(current.y + next.y) / 2} ${next.x} ${next.y}`;
+ }
+ beachPath += ' Z';
+
+ // Trees (more, varied sizes)
+ const numTrees = 5 + Math.floor(rng() * 6);
let trees = '';
for (let i = 0; i < numTrees; i++) {
- const tx = centerX + (rng() - 0.5) * (size * 0.4);
- const ty = centerY + (rng() - 0.5) * (size * 0.4);
- const treeSize = 3 + rng() * 2;
- trees += ``;
+ const angle = rng() * Math.PI * 2;
+ const dist = 8 + rng() * 25;
+ const tx = centerX + Math.cos(angle) * dist;
+ const ty = centerY + Math.sin(angle) * dist;
+ const treeHeight = 8 + rng() * 10;
+ const treeWidth = 4 + rng() * 4;
+ // Palm tree shape
+ trees += `
+
+
+
+
+
+ `;
+ }
+
+ // Small buildings/huts
+ const numHuts = 1 + Math.floor(rng() * 2);
+ let huts = '';
+ for (let i = 0; i < numHuts; i++) {
+ const angle = rng() * Math.PI * 2;
+ const dist = 10 + rng() * 15;
+ const hx = centerX + Math.cos(angle) * dist;
+ const hy = centerY + Math.sin(angle) * dist;
+ huts += `
+
+
+
+
+ `;
+ }
+
+ // Water sparkles
+ let sparkles = '';
+ for (let i = 0; i < 6; i++) {
+ const sx = rng() * size;
+ const sy = rng() * size;
+ sparkles += ``;
}
return `
`;
diff --git a/src/components/Island/IslandViewer.jsx b/src/components/Island/IslandViewer.jsx
index a0661a8..76b7f1a 100644
--- a/src/components/Island/IslandViewer.jsx
+++ b/src/components/Island/IslandViewer.jsx
@@ -491,6 +491,42 @@ export default function IslandViewer({ island, onClose }) {
+ {/* Action buttons */}
+
+ {isSelf && (
+
+ )}
+
+
+
🌱 Seed: {island.seed} • 🎨 {island.color_palette || 'vibrant'} palette
@@ -605,37 +641,74 @@ function createIslandElement(seed, terrainType = 'tropical') {
el.className = 'island-terrain-marker';
const terrainColor = getTerrainColorCSS(terrainType);
- const size = 220 + rng() * 50; // 220-270px
+ const size = 320 + rng() * 80; // Much bigger: 320-400px
+
+ // Terrain-specific decorations
+ const terrainEmoji = {
+ 'tropical': '🌴',
+ 'desert': '🌵',
+ 'arctic': '❄️',
+ 'volcanic': '🌋',
+ 'meadow': '🌸'
+ }[terrainType] || '🌴';
el.innerHTML = `
+
+
+
+
✨
+ ">${terrainEmoji}
+
${terrainEmoji}
+
🏠
${createIslandTexture(seed, terrainType)}
`;
diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js
index 7c0707c..df5f4f1 100644
--- a/src/components/Map/markerManager.js
+++ b/src/components/Map/markerManager.js
@@ -1476,11 +1476,11 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
Watch Live Stream
- ` : `
-
- `}
+ ` : url ? `
+
+ Open source
+
+ ` : ``}
${!isCamera ? `` : ''}
diff --git a/src/styles/island.css b/src/styles/island.css
index 3ab5972..ffb6ed3 100644
--- a/src/styles/island.css
+++ b/src/styles/island.css
@@ -688,3 +688,71 @@ body[data-theme="light"] .password-modal-field input {
color: #1e293b;
border-color: rgba(99, 102, 241, 0.3);
}
+
+/* ===== ISLAND ACTION BUTTONS ===== */
+
+.island-viewer-actions {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ margin-top: 1.5rem;
+ flex-wrap: wrap;
+}
+
+.island-action-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1.25rem;
+ border-radius: 12px;
+ font-size: 0.9rem;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+ border: 1px solid rgba(148, 163, 184, 0.3);
+ background: rgba(30, 41, 59, 0.6);
+ color: #e2e8f0;
+ backdrop-filter: blur(8px);
+}
+
+.island-action-btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
+ border-color: rgba(96, 165, 250, 0.5);
+}
+
+.island-action-btn:active {
+ transform: scale(0.97);
+}
+
+.island-action-btn i {
+ font-size: 1rem;
+}
+
+.island-action-primary {
+ background: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);
+ border-color: rgba(99, 102, 241, 0.5);
+ color: white;
+ box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);
+}
+
+.island-action-primary:hover {
+ background: linear-gradient(135deg, #60a5fa 0%, #818cf8 100%);
+ box-shadow: 0 8px 28px rgba(99, 102, 241, 0.4);
+ border-color: rgba(129, 140, 248, 0.6);
+}
+
+body[data-theme="light"] .island-action-btn {
+ background: rgba(255, 255, 255, 0.8);
+ color: #1e3a8a;
+ border-color: rgba(99, 102, 241, 0.25);
+}
+
+body[data-theme="light"] .island-action-btn:hover {
+ border-color: rgba(99, 102, 241, 0.5);
+}
+
+body[data-theme="light"] .island-action-primary {
+ background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%);
+ color: white;
+}