DNYUZ
No Result
View All Result
DNYUZ
No Result
View All Result
DNYUZ
Home News

Meet a firefighter vibe coding an app to get you in and out of Aldi quicker

May 19, 2026
in News
Meet a firefighter vibe coding an app to get you in and out of Aldi quicker
Joe Poynton
Pål Hansen for BI

When Joe Poynton went to the grocery store, he dutifully worked through his shopping list — before getting to the bottom and realizing he’d passed those items aisles ago.

The things he’d forgotten would taunt him from across the store, forcing him back to produce or pasta. The busy 44-year-old father — he’s worked in the UK fire service for 20 years, where he handles daily emergencies — had resigned himself to suffering this mild inefficiency every time he went shopping.

No more. Poynton has finally fixed his grocery store problem — by building an app to optimize grocery runs based on how you navigate the store.

.vertical-video-wrapper {
display: flex;
justify-content: center;
}

@media (min-width: 768px) {
.vertical-video-wrapper .vertical-video {
width: min(calc(80vh * var(–video-w) / var(–video-h)), 100%);
min-width: 0;
}
}

Your browser does not support the video element.

Poynton’s app walks you through how to get set up

Grocery Flow

Poynton is part of a small but growing group of civilian coders: regular people who can solve daily annoyances and inefficiencies with their own hyper-specific programs. It’s a practice called “vibe coding,” and it doesn’t involve programming skills. Poynton, for instance, has never taken a tech course. Instead, users chat with an AI bot like Claude or Lovable and tell it what they want to build — think of it as bossing around your very own eager-to-please programmer.

“I just started by saying, ‘I’m brand new to this, treat me like an idiot. I don’t know a single word of code. This is my vision. What’s the steps that I need to take to get there?'” Poynton said.

To create the app, Poynton bounced between Claude and Gemini, using the two LLMs to troubleshoot each other. He began with a roughly $25 Gemini Pro subscription, which walked him through app development 101.

.vertical-pair__images {
display: flex;/*Remove to avoid stacking on mobile*/
flex-direction: column;
gap: 4px; /*Adjust to change space between the images on mobile*/
}

.vertical-pair__images .figure {
width: 100%;
}

@media (min-width: 768px) {
.vertical-pair__images {
flex-direction: row;/*Remove to avoid stacking on mobile*/
align-items: flex-start;
gap: 8px; /*Adjust to change space between the images on desktop*/
}

.vertical-pair__images .figure {
flex: 1;
min-width: 0;
}

/* OPTIONAL: Make the pair wider than the text column on desktop.
Remove or comment out this block to revert to default width.
Adjust the percentage to control how wide it extends —
margin-left should always be half the extra width to keep it centered.
Examples:
width: 110% → margin-left: -5%
width: 120% → margin-left: -10%
width: 130% → margin-left: -15% */
@media (min-width: 768px) {
.vertical-pair {
width: 150%;
margin-left: -25%;
}
}

}

Joe Poynton

Joe Poynton

Poynton has friends, family, and a few folks online sorting their shopping lists with his app.

Pål Hansen for BI

“I’m now lucky enough to have drifted into that period of life where I’m a little bit older, which means that I’m trying to be a little bit more creative in a whole range of aspects of my life,” Poynton said.

Gemini talked him through each step, detailing what the process might cost and what software he’d need. He learned he’d need XCode, an Apple tool package for app developers, and have Gemini create files in Swift, the coding platform for Apple. His XCode, along with his app previews and icons, would have to go through the app review process before the app could go live.

“It was fun,” Poynton said. “It’s a bit like trying to write a book in a language that you don’t speak, and I’m just doing it through a translator.”

Since Claude had a better reputation for coding, Poynton uploaded his code to a free version and asked for feedback. Claude made tweaks and told him how it would do things differently, and he incorporated the new feedback. Gemini helped him take his iPad drawings from brainstorms into graphics. The entire process, which took place in spurts between work and other obligations, lasted about two to three months.

AI Conversation Embed — Annotated

{
“platform”: “generic”,
“caption”: “A conversation with Claude about improving the grocery app’s reordering UI.”,
“credit”: “Conversation edited for length.”,
“messages”: [
{ “role”: “user”, “text”: “Minor thing to tweak before we do the big structural changes. When the ‘Edit Order’ button is pressed for items in the Trolly, there is no visual change to the list to show that the user can interact with it. I think there should be drag handles or something when this button is active and then it reverts to the normal state when done is pressed.” },
{ “role”: “assistant”, “text”: “Straightforward fix. Here’s the prompt:\n\nWhen \”Edit Order\” is active in the checkedItems section (isReordering == true), the list rows give no visual indication that they are draggable. Add drag handles to each row in the checked items section when isReordering is true.\n\nRequirements:\n- Show a drag handle icon (line.3.horizontal or similar SF Symbol) on the trailing edge of each checked item row when isReordering is true\n- The handle should disappear when isReordering is false\n- Use a subtle colour — .secondary or .tertiaryLabel — so it doesn’t dominate the row\n- The existing .onMove and editMode behaviour should be unchanged\n\nThe change should be in ContentView’s checkedItemsSection. RowView does not need to be modified — add the handle as an overlay or within the row’s HStack in the ForEach, not inside RowView itself.\n\nNo proposal needed, this is a small contained change.”, “note”: “The manual reordering feature wasn’t visually clear enough. I explained the problem to Claude, and it gave me a perfectly worded prompt to feed into Claude Code. It turns out bouncing ideas off one AI to write better instructions for another is a remarkably effective way to get the code you actually want.” }
]
}

:root {
–bg: #f7f7f8;
–frame: #ffffff;
–border: #e5e5ea;
–text: #1a1a1a;
–muted: #8e8e93;
–user-bubble: #e9e9eb;
–assistant-bubble: #ffffff;
–brand: #1a1a1a;
–accent: #d97757;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, Helvetica, Arial, sans-serif;
color: var(–text);
-webkit-font-smoothing: antialiased;
}

/* — Two-column layout — */
.embed-outer {
max-width: 780px;
margin: 16px auto;
padding: 0 12px;
}
.two-col {
display: flex;
gap: 0;
align-items: stretch;
}
.chat-col {
flex: 0 0 380px;
max-width: 380px;
min-width: 0;
}
.notes-col {
flex: 1;
min-width: 160px;
padding: 0 0 0 20px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 16px;
}

/* — Chat frame (same as standard embed) — */
.frame {
background: var(–frame);
border: 1px solid var(–border);
border-radius: 18px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06);
}
.header {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 14px;
background: var(–frame);
color: var(–text);
border-bottom: 1px solid var(–border);
}
.header .logo {
width: 22px; height: 22px;
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.header .logo svg { width: 100%; height: 100%; display: block; }
.header .name {
font-weight: 600; font-size: 13px; line-height: 1.2;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.messages {
padding: 12px 12px 14px;
background: var(–bg);
display: flex;
flex-direction: column;
gap: 8px;
height: 420px;
overflow-y: auto;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
.messages::-webkit-scrollbar { width: 6px; }
.messages::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.18); border-radius: 3px; }
.messages::-webkit-scrollbar-track { background: transparent; }

/* — Messages — */
.msg-row {
opacity: 0;
transform: translateY(10px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.msg-row.in { opacity: 1; transform: translateY(0); }
.msg {
display: flex;
max-width: 100%;
}
.msg.user { justify-content: flex-end; }
.msg.assistant { justify-content: flex-start; }
.bubble {
max-width: 84%;
padding: 8px 12px;
border-radius: 16px;
font-size: 13.5px;
line-height: 1.42;
word-wrap: break-word;
white-space: pre-wrap;
position: relative;
}
.msg.user .bubble {
background: var(–user-bubble);
color: var(–text);
border-bottom-right-radius: 6px;
}
.msg.assistant .bubble {
background: var(–assistant-bubble);
color: var(–text);
border: 1px solid var(–border);
border-bottom-left-radius: 6px;
}
.bubble.image-bubble { padding: 6px; overflow: hidden; }
.bubble.image-bubble img { display: block; width: 100%; height: auto; border-radius: 12px; }
.bubble .caption { font-size: 12px; color: var(–muted); padding: 8px 6px 4px; line-height: 1.35; }

/* — Annotation marker (inline, inside the chat bubble row) — */
.note-marker {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px; height: 18px;
border-radius: 50%;
background: var(–accent);
color: #fff;
font-size: 10px;
font-weight: 700;
flex-shrink: 0;
line-height: 1;
margin-left: 6px;
vertical-align: middle;
cursor: default;
}
.msg.assistant .note-marker { margin-left: 0; margin-right: 6px; }

/* — Annotation notes (sidebar) — */
.note-sidebar {
opacity: 0;
transform: translateY(6px);
transition: opacity 0.3s ease, transform 0.5s ease 0.15s;
font-size: 13px !important;
line-height: 1.3 !important;
font-family: garnett, helvetica, arial, sans-serif !important;
font-weight: 600 !important;
color: #555 !important;
}
.note-sidebar.in { opacity: 1; transform: translateY(0); }
.single-note .note-marker,
.single-note .note-num { display: none; }
.note-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px; height: 18px;
border-radius: 50%;
background: var(–accent);
color: #fff;
font-size: 10px;
font-weight: 700;
line-height: 1;
margin-right: 8px;
flex-shrink: 0;
vertical-align: top;
}
.note-text {
font-size: 13px !important;
line-height: 1.3 !important;
color: #555 !important;
letter-spacing: -0.01em !important;
font-family: garnett, helvetica, arial, sans-serif !important;
font-weight: 600 !important;
}
.note-sidebar .note-text { display: block; margin: 0 !important; padding: 0 !important; }
.note-sidebar::before {
content: “\201C”;
display: block;
font-size: 48px;
line-height: 1;
color: var(–accent);
margin-bottom: -12px;
font-family: garnett, helvetica, arial, sans-serif;
}

/* — Inline note (mobile fallback) — */
.note-inline {
display: none !important;
padding: 10px 0;
background: none;
border-radius: 0;
font-size: 13px;
line-height: 1.3;
color: #555;
}
.note-quote {
display: none;
font-family: Georgia, serif;
font-size: 32px;
line-height: 1;
color: var(–accent);
margin-bottom: -6px;
}

/* — Divider — */
.divider {
display: flex; align-items: center; gap: 10px; margin: 6px 2px 2px;
opacity: 0; transform: translateY(10px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.divider.in { opacity: 1; transform: translateY(0); }
.divider::before, .divider::after { content: ”; flex: 1; height: 1px; background: var(–border); }
.divider span { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(–muted); font-weight: 600; white-space: nowrap; }

/* — Caption + credit — */
.image-source-caption { display: block; margin: 8px 4px 0; }
.image-caption { font-size: 13px; line-height: 1.4; color: #1a1a1a; font-weight: 600; margin: 0; }
.image-source { font-weight: 400; color: #6b6b73; }

/* — Platform themes — */
.platform-chatgpt { –accent: #10a37f; }
.platform-gemini { –accent: #1a73e8; }
.platform-claude { –accent: #d97757; }
.platform-generic { –accent: #395aff; }

/* — Responsive: collapse notes inline on narrow widths — */
@media (max-width: 620px) {
.two-col { flex-direction: column; }
.chat-col { flex: 1 1 auto; max-width: 100%; }
.notes-col { display: none; }
.note-inline {
display: block !important;
max-width: 100%;
margin: 4px 0;
font-family: garnett, helvetica, arial, sans-serif !important;
font-weight: 600 !important;
font-size: 13px !important;
line-height: 1.4 !important;
color: var(–accent) !important;
}
.note-inline .note-num { display: none; }
.note-inline .note-quote { display: none !important; }
.note-marker { display: none; }
}

(function() {
const cfg = JSON.parse(document.getElementById(‘chat-config’).textContent);
const platform = (cfg.platform || ‘chatgpt’).toLowerCase();

const PLATFORMS = {
chatgpt: {
name: ‘ChatGPT’,
logo: ”
},
gemini: {
name: ‘Gemini’,
logo: ”
},
claude: {
name: ‘Claude’,
logo: ”
},
generic: {
name: ‘AI Chat’,
// Simple chat-bubble icon — neutral, no brand
logo: ”
}
};

const p = PLATFORMS[platform] || PLATFORMS.generic;
// Allow config “name” to override the default platform name (e.g. “Lovable”, “Cursor”, “Bolt”)
const displayName = cfg.name || p.name;
const root = document.getElementById(‘root’);
root.classList.add(‘platform-‘ + platform);

// Collect notes for sidebar
let noteNum = 0;
const notes = [];
(cfg.messages || []).forEach((m, i) => {
if (m.note) {
noteNum++;
notes.push({ num: noteNum, text: m.note, msgIndex: i });
m._noteNum = noteNum;
}
});
// When there’s only one annotation, hide numbered badges everywhere
if (notes.length <= 1) root.classList.add('single-note'); const captionHtml = (cfg.caption || cfg.credit) ? `

${cfg.caption ? esc(cfg.caption) + ‘ ‘ : ”}
${cfg.credit ? ‘‘ + esc(cfg.credit) + ‘‘ : ”}

` : ”;

const sidebarHtml = notes.map(n =>
” +
‘‘ + n.num + ‘‘ +
‘

‘ + esc(n.text) + ‘

‘ +
”
).join(”);

root.innerHTML = `

${p.logo}
${esc(displayName)}

${captionHtml}

${sidebarHtml}

`;

const msgsEl = document.getElementById(‘messages’);
(cfg.messages || []).forEach((m, i) => {
if (m.role === ‘divider’) {
const d = document.createElement(‘div’);
d.className=”divider”;
d.style.transitionDelay = (Math.min(i, 6) * 80) + ‘ms’;
d.innerHTML = m.label ? ‘‘ + esc(m.label) + ‘‘ : ”;
msgsEl.appendChild(d);
return;
}

const row = document.createElement(‘div’);
row.className=”msg-row”;
row.style.transitionDelay = (Math.min(i, 6) * 80) + ‘ms’;
row.dataset.index = i;

const msgDiv = document.createElement(‘div’);
msgDiv.className=”msg ” + (m.role === ‘user’ ? ‘user’ : ‘assistant’);

const markerHtml = m._noteNum
? ‘‘ + m._noteNum + ‘‘
: ”;

if (m.image) {
const cap = m.caption ? ” + esc(m.caption) + ” : ”;
if (m.role === ‘user’) {
msgDiv.innerHTML =
” +
‘' + esc(m.alt || '') + '‘ +
cap +
” + markerHtml;
} else {
msgDiv.innerHTML =
markerHtml +
” +
‘' + esc(m.alt || '') + '‘ +
cap +
”;
}
} else {
if (m.role === ‘user’) {
msgDiv.innerHTML = ” + esc(m.text || ”) + ” + markerHtml;
} else {
msgDiv.innerHTML = markerHtml + ” + esc(m.text || ”) + ”;
}
}

row.appendChild(msgDiv);

// Inline note for mobile — placed before the annotated message
if (m.note) {
const inlineNote = document.createElement(‘div’);
inlineNote.className=”note-inline msg-row”;
inlineNote.style.transitionDelay = (Math.min(i, 6) * 80) + ‘ms’;
inlineNote.innerHTML = esc(m.note);
msgsEl.appendChild(inlineNote);
}

msgsEl.appendChild(row);
});

// Animate in
const items = msgsEl.querySelectorAll(‘.msg-row, .divider’);
const sideNotes = document.querySelectorAll(‘.note-sidebar’);
if (‘IntersectionObserver’ in window) {
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add(‘in’);
// Also reveal matching sidebar note
const idx = e.target.dataset && e.target.dataset.index;
if (idx !== undefined) {
const sn = document.querySelector(‘.note-sidebar[data-for=”‘ + idx + ‘”]’);
if (sn) sn.classList.add(‘in’);
}
io.unobserve(e.target);
}
});
}, { root: msgsEl, threshold: 0.15, rootMargin: ‘0px 0px -20px 0px’ });
items.forEach(el => io.observe(el));
requestAnimationFrame(() => {
const cr = msgsEl.getBoundingClientRect();
items.forEach(el => {
const r = el.getBoundingClientRect();
if (r.top cr.top) {
el.classList.add(‘in’);
const idx = el.dataset && el.dataset.index;
if (idx !== undefined) {
const sn = document.querySelector(‘.note-sidebar[data-for=”‘ + idx + ‘”]’);
if (sn) sn.classList.add(‘in’);
}
io.unobserve(el);
}
});
});
} else {
items.forEach(el => el.classList.add(‘in’));
sideNotes.forEach(el => el.classList.add(‘in’));
}

// Show sidebar notes after a short delay
setTimeout(() => {
document.querySelectorAll(‘.note-sidebar’).forEach(el => el.classList.add(‘in’));
}, 600);

function esc(s) {
return String(s).replace(/[&”‘]/g, c => ({
‘&’:’&’,”:’>’,'”‘:'”‘,”‘”:”’
}[c]));
}
})();

Now, his app is live in the Apple App Store and has become his trusted companion at any grocery store. Poynton didn’t have to get his hands on any retailers’ floor plans. Instead, the app dynamically learns the layout of shoppers’ preferred stores by noting where they are when they check something off their list, and then sorts future lists with the newfound knowledge of where those items live.

“As I’ve used it over and over again, it’s iteratively improving the knowledge of the store; it gets better as you use it,” Poynton said. “It’s nice to hold something in my hand to say that it’s me that’s created that, and it does a job.”

For Poynton, vibe coding likely won’t be a lucrative side hustle anytime soon. He thinks it would be “lovely” to have enough people download and pay for his app to make back his App Store developer fees of around $106. Ideally, he’d like it to be more visible on the app store — because grocery lists are such a crowded category, he believes his app is unlikely to surface for most shoppers. He’s had a handful of downloads from family, friends, and Redditors.

“So far the reaction’s been positive or at least encouraging,” Poynton said. “This isn’t going to sweep Silicon Valley by storm. It’s a really basic idea and a basic execution, but it’s satisfying to have something out there that my mom’s used.”

Read the original article on Business Insider

The post Meet a firefighter vibe coding an app to get you in and out of Aldi quicker appeared first on Business Insider.

Chicks Hatch From World’s First Artificial Eggs—A Breakthrough Key to Bringing Giant Birds Back From Extinction
News

Chicks Hatch From World’s First Artificial Eggs—A Breakthrough Key to Bringing Giant Birds Back From Extinction

by TIME
May 19, 2026

The artificial egg and its stabilizing holder —Colossal Biosciences The egg is one of nature’s greatest little brainstorms. The largest ...

Read more
News

Trump wanders out of White House to give free-wheeling rant in front ballroom construction

May 19, 2026
News

Top LAUSD academic chiefs leaving as test scores rise and FBI raid sidelines Carvalho

May 19, 2026
News

This is Why Hair in Resident Evil Requiem Looked So Real on PS5 Pro

May 19, 2026
News

I prioritized my relationship over my classes in college. I graduated with no job and no friends, and I regret it all.

May 19, 2026
Could MAGA Turn Trump Against AI?

Could MAGA Turn Trump Against AI?

May 19, 2026
Trump Caught Buying Tech Stocks and Then Pumping Their Value by Publicly Praising Them

Trump Caught Buying Tech Stocks and Then Pumping Their Value by Publicly Praising Them

May 19, 2026
Former OpenAI Staffers Warn xAI’s Poor Safety Record Could Complicate SpaceX’s IPO

Former OpenAI Staffers Warn xAI’s Poor Safety Record Could Complicate SpaceX’s IPO

May 19, 2026

DNYUZ © 2026

No Result
View All Result

DNYUZ © 2026