vibe coded this. nostr firehose stream, media only, scroll to pause, tap media to get to profile
https://nostrstream.replit.app
nice in full screen.
(you may see things you don't want to see)
vibe coded this. nostr firehose stream, media only, scroll to pause, tap media to get to profile
https://nostrstream.replit.app
nice in full screen.
(you may see things you don't want to see)
Dont know what vibe coding is and im afraid to ask at this point. π«
As I understand itβs when you have an idea and talk to Ai to code it for you.
Cool
this is unfortunately really cool
nevent1qqsdx7crpdl7s2myeuc04exgxt2wgyea9v0e8xqqaa58dpdfrdvgp4q08gneq
How does this work exactly? Is it a βlivestreamβ of all the media posted to nostr as it gets uploaded? Is it in chronological order?
Oops π¬ 
source:
:root {
--bg-color: #ffffff;
--text-color: #333333;
}
[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #ffffff;
}
body {
font-family: -apple-system, system-ui, sans-serif;
margin: 0;
padding: 0;
background: var(--bg-color);
color: var(--text-color);
}
#header {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 15px 20px;
background: var(--bg-color);
display: flex;
justify-content: space-between;
align-items: center;
z-index: 1000;
font-size: 14px;
}
#feed {
margin-top: 52px;
}
.note {
margin-bottom: 0;
}
.media-container {
background: #000;
line-height: 0;
width: 100%;
}
.media-container a {
display: block;
line-height: 0;
}
.media-container img,
.media-container video {
width: 100%;
height: auto;
object-fit: contain;
opacity: 0;
transition: opacity 0.5s ease-in;
}
.media-container img.loaded,
.media-container video.loaded {
opacity: 1;
}
#status {
display: flex;
align-items: center;
gap: 6px;
opacity: 0.7;
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
.status-live .status-dot {
background: #4CAF50;
}
.status-paused .status-dot {
background: #ff9800;
}
@media (min-width: 800px) {
.media-container img,
.media-container video {
max-height: 100vh;
}
}
@media (max-width: 799px) {
.media-container img,
.media-container video {
max-height: none;
}
}
Live
// Auto dark mode
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.setAttribute('data-theme', 'dark');
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
document.body.setAttribute('data-theme', e.matches ? 'dark' : 'light');
});
// Initialize
const feed = document.getElementById('feed');
const status = document.getElementById('status');
const seenNotes = new Set();
const seenMedia = new Set();
let isPaused = false;
// Relays
const RELAYS = [
'wss://relay.damus.io',
'wss://relay.nostr.band',
'wss://nos.lol',
'wss://relay.nostr.info'
];
const relayPool = new Map();
// Function to update status display
function updateStatus(paused) {
status.className = paused ? 'status-paused' : 'status-live';
status.querySelector('span').textContent = paused ? 'Paused' : 'Live';
}
// Pause/Resume based on scroll position
let lastScrollTop = 0;
window.addEventListener('scroll', () => {
const st = window.pageYOffset || document.documentElement.scrollTop;
if (st > lastScrollTop && st > 100) {
// Scrolling down
if (!isPaused) {
isPaused = true;
updateStatus(true);
}
} else if (st === 0) {
// At top
if (isPaused) {
isPaused = false;
updateStatus(false);
}
}
lastScrollTop = st;
});
// Connect to relays
function connect() {
let connectedRelays = 0;
RELAYS.forEach(relayUrl => {
const socket = new WebSocket(relayUrl);
relayPool.set(relayUrl, socket);
socket.onopen = () => {
connectedRelays++;
if (connectedRelays === 1) {
updateStatus(false);
}
// Subscribe to notes with media
const recentSub = JSON.stringify([
"REQ",
"recent_" + relayUrl,
{
"kinds": [1],
"limit": 500
}
]);
socket.send(recentSub);
};
socket.onclose = () => {
relayPool.delete(relayUrl);
connectedRelays--;
if (connectedRelays === 0) {
setTimeout(() => connect(), 2000);
}
};
socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
// Handle incoming messages
socket.onmessage = async (event) => {
if (isPaused) return;
const data = JSON.parse(event.data);
if (data[0] !== 'EVENT') return;
const msg = data[2];
// Handle notes
if (msg.kind !== 1) return;
if (seenNotes.has(msg.id)) return;
seenNotes.add(msg.id);
// Look for media URLs
const mediaUrls = [];
const urlRegex = /(https?:\/\/[^\s<]+\.(jpg|jpeg|png|gif|mp4|webm))/gi;
let match;
while ((match = urlRegex.exec(msg.content)) !== null) {
mediaUrls.push(match[0]);
}
if (mediaUrls.length === 0) return;
// Check for duplicate media
const mediaKey = mediaUrls.sort().join(',');
if (seenMedia.has(mediaKey)) return;
seenMedia.add(mediaKey);
try {
// Create note element
const noteEl = document.createElement('div');
noteEl.className = 'note';
// Add media
const mediaContainer = document.createElement('div');
mediaContainer.className = 'media-container';
// Make media container clickable
const mediaLink = document.createElement('a');
mediaLink.href = `https://njump.me/${msg.id}`;
mediaLink.target = '_blank';
mediaLink.style.cursor = 'pointer';
mediaContainer.appendChild(mediaLink);
for (const url of mediaUrls) {
if (url.match(/\.(jpg|jpeg|png|gif)$/i)) {
try {
// Create and preload image
const img = document.createElement('img');
img.style.opacity = '0';
img.src = url;
img.loading = 'lazy';
// Wait for image to load
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
// Add to container and fade in
mediaLink.appendChild(img);
requestAnimationFrame(() => {
img.style.opacity = '1';
});
} catch (e) {
console.error('Failed to load image:', url);
}
} else {
const video = document.createElement('video');
video.src = url;
video.controls = true;
video.autoplay = true;
video.muted = true;
video.loop = true;
video.playsInline = true;
video.style.opacity = '0';
// Fade in once video starts playing
video.addEventListener('playing', () => {
requestAnimationFrame(() => {
video.style.opacity = '1';
});
}, { once: true });
video.onerror = () => {
video.remove();
};
mediaLink.appendChild(video);
// Try to start playing
video.play().catch(e => console.log('Auto-play prevented:', e));
}
}
noteEl.appendChild(mediaContainer);
feed.insertBefore(noteEl, feed.firstChild);
} catch (e) {
console.error('Error creating note element:', e);
}
};
});
}
// Initial connection
connect();
Nice. Kinda funny, amethyst thinks the CSS colors are hashtags and lists them at the bottom of this post π
This is some of the best code I've seen in a nostr client. Unlike every other client, it uses pure javascript, which is native everywhere. It doesnt require a compiler. It's blazingly fast. And it just works. Well done Jack, this is a gold standard in how to write client apps. Keep going!
simp
LFG vibes
Letsgoo
nevent1qqsdx7crpdl7s2myeuc04exgxt2wgyea9v0e8xqqaa58dpdfrdvgp4q08gneq
"Ela quer massacrar os olhos delas com incessantes imagens de telejornais" -- Fausto Fawcett
This is wild lol
nostr:nprofile1qqsgydql3q4ka27d9wnlrmus4tvkrnc8ftc4h8h5fgyln54gl0a7dgspz3mhxue69uhhyetvv9ujuerpd46hxtnfduq3vamnwvaz7tmjv4kxz7fwdehhxtnnda3kjctv6t04z5 is talented too. Thanks for sharing. β¨
nevent1qqsdx7crpdl7s2myeuc04exgxt2wgyea9v0e8xqqaa58dpdfrdvgp4q08gneq
βMay see things you donβt want to seeβ is an understatement
So pleasing to use..
nevent1qqsdx7crpdl7s2myeuc04exgxt2wgyea9v0e8xqqaa58dpdfrdvgp4q08gneq
So much hentai π
Are you okay?
I love that this is vanilla js.
This is nostr.TV ! ..
A remote with just one button - click to see the profile :-)
π heard youβre into chaos
you can just make things
nevent1qqsdx7crpdl7s2myeuc04exgxt2wgyea9v0e8xqqaa58dpdfrdvgp4q08gneq
*you will definitely see things you donβt want to see π
lol I've seen exactly what I wanted to see...
Anything produced by Jack is defintely something I don't want to see.
vibecoding and nostr are a match made in heaven
π wild sht! π
nevent1qqsdx7crpdl7s2myeuc04exgxt2wgyea9v0e8xqqaa58dpdfrdvgp4q08gneq
Replit is cool & Lovable is awesome
It's not vibe coding unless your using the free versions of Ai.
If you pay for an Ai to code for you, you've hired a digital worker!
γΈγ£γγ―γ Nostr γ―γ©γ€γ’γ³γδ½γ£γ¦γπ
Sweet.
I kind of wish I had the exact opposite. Like instead of something to just view. Something that just posts notes.
I'm picturing myself driving and having a fire note to drop. . .
I hold down a button voice to text opens up I say my note and then when I release it sends.
No other interface needed other than maybe a text editor.
Could do the same with a camera app...
Productivity gains would be nice but I'm also thinking of people in protest trying to get information out right before they get gassed.
I have a dev position open btw
not working on Tor and Ironfox browsers.
Gorgeous on Chrome so far.
doesn't show anything for me
you know, the type of shit hitler saw every time he closed his eyes
i love the media only, simplicity to use and portrayal of the divergent information. it feels so internet-y that you canβt help but smile.
you should commit to an app per week attempt (like your reading goal) and get the juices flowing.
A few years ago, I saw more eerie than this.
No. It happened several times at Facebook.
Computational Intelligence?
Spiral mind down.
An endless labyrinth.
Itβs still happening at Twitter/X.
A peer into the human conscious of the world.
Amazing!! Can you share the code of it please as a base to fork for Replit?
π
This is awesome.
(I did not see anything too smutty.)
BITCH SLUT nostr:npub1jmq8tlhf28r3832zs8pwx8y9d60gqzdk7f4zlgznrquxmgtjxklqcmyzuz You CHEAP WHORE ππ
You reposted this AFTER nostr:npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m SLUT-FUCKED you for your birthday?
Or BEFORE he BOUGHT YOUR CHEAP WHORE ASS to FUCK?
πππ