今日已更新 144 条资讯 | 累计 37695 条内容
关于我们

标签:#Web

找到 2743 篇相关文章

AI 资讯

CSS Architecture

Responsive CSS: From Mobile-First Design to Modern Styling Responsive design is about creating websites that work well across mobile, tablet, and desktop screens. In this post, I learned some important techniques for building responsive and maintainable CSS. 1. Mobile-First Media Queries Mobile-first means writing the base CSS for smaller screens first and then enhancing the layout for larger screens. /* Mobile */ .card { width : 100% ; } /* Tablet */ @media ( min-width : 768px ) { .card { width : 70% ; } } /* Desktop */ @media ( min-width : 1024px ) { .card { width : 50% ; } } The main idea is: Mobile → Tablet → Desktop min-width is commonly used for mobile-first development because styles are progressively added as the screen gets larger. min-width vs max-width min-width : applies styles when the screen is at least the specified width. max-width : applies styles when the screen is at most the specified width. For example: @media ( max-width : 768px ) { h1 { font-size : 20px ; } } One important lesson I learned: CSS media queries belong inside <style> or a CSS file, not inside <script> . 2. Fluid Typography Fixed font sizes don't always work well across different screen sizes. Fluid typography allows text to adapt to the viewport. rem rem is relative to the root font size. h1 { font-size : 2rem ; } If the root size is 16px, 2rem is 32px. vw vw is relative to the viewport width. h1 { font-size : 5vw ; } However, using only vw can make text too small or too large. clamp() clamp() provides a minimum, flexible value, and maximum: h1 { font-size : clamp ( 1.5rem , 4vw , 3rem ); } This allows the font size to grow smoothly while keeping it within limits. 3. Responsive Images Images can consume a lot of bandwidth, so responsive images help browsers choose an appropriate image for the device. srcset <img src= "small.jpg" srcset= " small.jpg 400w, medium.jpg 800w, large.jpg 1200w" sizes= "100vw" alt= "Mountain" > srcset provides multiple image sizes, allowing the browser to

2026-08-10 原文 →
AI 资讯

What I Learned Building 8 Search-Intent Game Guide Sites

The problem is not a lack of game content Most early game-guide sites begin as broad collections: a release-date post, a few news stories, a list of characters, perhaps a page titled "beginner guide." That structure looks complete in a sitemap but often fails the player who arrives from search with a precise, urgent question. They are not looking for a generic introduction. They are asking: Is the game out in my region? Can I join the playtest safely? Is the PC version confirmed? Does this game actually work like Tarkov, Sekiro, or Stardew Valley? What did the developer confirm, and what is still speculation? I have been building eight small game-guide sites around those moments. The project is an experiment in search-intent publishing : every useful page should answer one query well, show where its information came from, and make its uncertainty visible. The aim is not to create the biggest pre-release wiki. It is to create the most dependable next click. Example of the official-media trail used for Mistfall Hunter coverage. Public media can support a page, but it should never be used to invent mechanics that have not been confirmed. The editorial model: one question, one canonical answer A search-focused guide gets stronger when a reader can tell three things immediately: What the page answers. A release-status page should not compete with a separate news article for the same release-date query. How current the answer is. Status, configuration, test, and platform pages need a visible review date and a concrete update trigger. What is evidence and what is inference. Official store pages, developer announcements, and official videos form the baseline. Public footage is useful but does not prove every system detail. Community testing can be valuable, but it must be labelled and dated. This sounds obvious, but it changes the content plan. I do not add a new URL merely because a keyword has a close variant. I first ask whether a stronger existing page can be updated, l

2026-08-10 原文 →
AI 资讯

Google Releases Angular v22 with Stable Signal Forms, OnPush by Default and Experimental WebMCP

Angular v22, Google's TypeScript-first framework, has introduced API stabilizations, ergonomic templates, and tooling enhancements for AI integration. Key developments include the stabilization of Signal Forms, improved change detection strategies, and a new @Service() decorator for dependency injection. The release supports TypeScript 6 and removes deprecated features. By Daniel Curtis

2026-08-10 原文 →
AI 资讯

Your Prompt Engineering Is Not the Bottleneck Anymore

I spend a lot of time in the AI space -- reading papers, building things, talking to engineers who are actually shipping. And there is a gap between what the demos show and what production systems actually look like that nobody is being fully honest about. So here is my honest take on where things actually are. The Problem With How We Talk About AI Agents Everyone is calling everything an "agent" right now. A function that calls a tool? Agent. A chatbot with memory? Agent. A script with a loop? Agent. This dilution is not just semantic. It is causing real engineering mistakes. When you do not have a precise definition for what you are building, you end up over-engineering simple pipelines and under-engineering genuinely complex ones. I have seen teams spend weeks adding "agentic" orchestration to workflows that would have been fine as a single well-structured prompt. Here is the definition I keep coming back to: an agent is a system that has an objective, not just an instruction. It decides what to do next. It handles failure. It knows when it is done. Everything else is just a fancy function call. 🟢 If your system needs a human to tell it each step, it is not an agent. It is a chat interface. 🔵 If your system can recover from a failed tool call and try a different approach, you are getting somewhere. ✅ If your system can decompose a goal into subtasks and delegate them, that is the real thing. What Is Actually Happening in Production Right Now The honest picture from teams I follow and talk to: Most real agent deployments are narrow. They do one thing well. Customer support triage. Document extraction. Code review on a specific codebase. They are not general-purpose reasoning engines. They are purpose-built pipelines with some intelligence in the decision layer. The teams getting good results are not chasing the latest model release. They are obsessing over: ☑️ Tool design -- what can the agent actually call, and how clean is the interface ☑️ Failure handling -- wh

2026-08-10 原文 →
AI 资讯

Your axe run is green and your dark mode has 1.04:1 contrast

I shipped a page that reported zero axe violations . It had button text at a contrast ratio of 1.04:1 — which is, for practical purposes, invisible text. The scan wasn't broken. It was answering a narrower question than I thought I was asking. The bug I had a theme system built the ordinary way. Tokens on :root , overridden in a prefers-color-scheme media query, and overridden again by an explicit [data-theme] attribute so a manual toggle wins in both directions. Buttons came in two flavours: a solid primary and a bordered secondary. .btn { background : var ( --accent ); color : var ( --panel ); } .btn.sec { background : transparent ; color : var ( --ink ); } In dark mode the accent goes light green, so white-on-accent stops working. I patched it the way you patch things at 1am: :root [ data-theme = dark ] .btn { color : #10241b } @media ( prefers-color-scheme : dark ) { :root:not ([ data-theme = light ]) .btn { color : #10241b } } Now count the specificity. Selector Specificity .btn.sec 0,2,0 :root[data-theme=dark] .btn 0,3,0 :root:not([data-theme=light]) .btn 0,3,0 :not() doesn't add specificity of its own, but its argument does. So :root (0,1,0) + [data-theme=light] (0,1,0) + .btn (0,1,0) lands at 0,3,0. My theme patch outranks the component modifier. In dark mode, every secondary button — transparent background, sitting on a #1a1c1f panel — got painted #10241b . Dark green on near-black. 1.04:1. The nasty part is that this class of bug is invisible in review. The rule looks correct. It is correct, for the buttons it was written for. It just also matched buttons it was never meant to touch, in one theme only. Why the scan didn't catch it axe-core evaluates the DOM as currently rendered . It reads computed styles, and computed styles resolve exactly one colour scheme: whichever one the browser is in right now. So npx axe https://example.com is not "does this page pass contrast." It's "does this page pass contrast in the scheme this headless browser happened to boo

2026-08-10 原文 →
开发者

Geo-Blocking: Block Malicious Traffic from Specific Countries (2-Minute Setup)

Why Geo-Block? Not every country needs to reach your server. If you run a local business in Brazil, you don't need traffic from North Korea. If you serve customers in the EU, you probably don't need visitors from 150 other countries hitting your login page. Geo-blocking at the WAF level stops unwanted traffic before it ever reaches your application. No CPU spent. No database queries wasted. No bandwidth consumed. The Numbers from My Server After 30 days of logging, I checked where attacks came from: Traffic Source % of Total Requests % of Attacks Target countries (where my customers are) 23% 8% Non-target countries 77% 92% 77% of my traffic came from countries I don't serve, and 92% of attacks originated from those countries. Geo-blocking the non-target regions would eliminate the vast majority of malicious traffic with zero impact on real users. Setting Up Geo-Blocking in SafeLine Step 1: Go to IP Groups -> Geo Blocking in the dashboard. Step 2: Choose your approach: Option A: Allow-list mode (strictest) Block everything, then whitelist specific countries. Block : ALL Allow : United States , Canada , United Kingdom , Germany , France , Netherlands Option B: Block-list mode (targeted) Allow everything, then block specific high-noise regions. Block : Russia , China , Vietnam , North Korea , Iran Step 3: Apply the rule. Done. What Happens to Blocked Visitors Blocked IPs see a 403 Forbidden page. They can't reach your application at all — the WAF drops the connection at the proxy layer. Your app server never sees these requests. SafeLine logs every geo-blocked request to Attack Logs. You'll see: Which country the IP was from What URL they tried to access The exact timestamp Which Countries to Block Based on my 30-day log analysis and common community reports: Almost always safe to block: North Korea — 0 legitimate traffic for 99.9% of sites Iran — heavy scanner activity, minimal legitimate traffic (for non-Iranian sites) High scanner volume, consider blocking if not yo

2026-08-10 原文 →
AI 资讯

How to Set Up Rate Limiting on Any Web App (Free, No Code Changes)

The Problem Your login page, search endpoint, or contact form is getting hammered. Rate limiting is the fix — but implementing it in application code means finding every endpoint, writing middleware, choosing a storage backend, and deploying changes. On a WAF, you set it once and it applies everywhere. Why WAF-Level Rate Limiting Is Better Approach Code-Level WAF-Level Setup time Hours to days 5 minutes Code changes Required None Applies to One endpoint at a time All routes with one rule Storage Redis/Memcached needed Built into WAF Performance impact Hits your app server Blocked at proxy Updates Deploy new code Change a rule in dashboard Step-by-Step: Rate Limit Setup 1. Log into SafeLine Dashboard Go to https://<your-ip>:9443 . Navigate to Rules -> Add Rule -> Rate Limiting. 2. Create Your First Rule — Login Protection Name: Login brute force protection Match: URL contains /login OR /wp-login.php OR /auth Limit: 5 requests per minute per IP Action: Block (return 429 Too Many Requests) Block duration: 15 minutes This stops credential stuffing cold. An attacker who tries 5 wrong passwords in 60 seconds gets blocked for 15 minutes. That's a maximum of 480 attempts per day — vs unlimited without rate limiting. 3. Search Endpoint Protection Name: Search rate limit Match: URL contains /search OR /query Limit: 30 requests per minute per IP Action: Challenge (JS captcha) Search endpoints are expensive. A single user running a script can do 1,000+ queries per minute and degrade performance for everyone. 30/min is generous for humans but stops scripts. 4. Global Baseline Name: Global request limit Match: /* Limit: 300 requests per minute per IP Action: Throttle Catches anything that slips through specific rules. 300/min = 5/sec, which is more than any human needs. What Happens When a Limit Is Hit SafeLine logs every rate limit trigger to the Attack Log. You'll see: Which IP triggered it Which endpoint they were hitting Time of the trigger Whether they got blocked, challenge

2026-08-10 原文 →
AI 资讯

Groq Returned Empty Content. The Bug Was Hiding in Reasoning Tokens.

This article was originally published on Jo4 Blog . We use Groq's gpt-oss-safeguard model to classify pages behind freshly created short links. Most pages take a few hundred tokens to score. Some don't. And the ones that don't were silently failing — for weeks — until we noticed the symptom: a small but consistent stream of links stuck in "preview pending" forever. Here's what we found. The Problem The classifier wraps a single Groq chat completion. Send page text, get back a JSON verdict ( safe , unsafe , with category codes). For 95% of links, this works in well under a second. For the other 5%, we'd see this in logs: WARN Empty content in Groq response WARN Classification failed for shortUrl=xyz123 — preview stays enabled Empty content. Not a network error, not a rate limit, not malformed JSON. The API returned 200, the choices array had one entry, and choices[0].message.content was "" . What did those pages have in common? They weren't obvious spam. They weren't obvious safe. They were ambiguous — a wellness blog that mentioned medication dosages, a forum thread about firearms law, a satire site quoting violent rhetoric. The kind of content where a human reviewer would also pause. The Wrong First Guess Our first instinct: the model is rate-limited or degraded for hard inputs. We added retries. The empty-content rate didn't budge. Second guess: we're hitting max_tokens . We had set it to 200. Maybe ambiguous pages produce longer verdicts. We bumped it to 400. Empty content rate didn't budge. The clue we kept missing was sitting in the response body itself, in a field we weren't parsing. The Root Cause Groq's response includes a usage block, and usage.completion_tokens_details.reasoning_tokens was the smoking gun: { "choices" : [{ "message" : { "content" : "" }, "finish_reason" : "length" }], "usage" : { "completion_tokens" : 200 , "completion_tokens_details" : { "reasoning_tokens" : 200 } } } gpt-oss-safeguard is a reasoning model. Before emitting a single charac

2026-08-10 原文 →
AI 资讯

I checked a dozen startup directories for real backlinks. Most free tiers give you nothing.

Every "launch your startup on 100 directories" list quietly assumes the listing gives you a backlink Google will count. We checked a dozen of them. For the free tiers, mostly it does not — and you can find that out in about thirty seconds per directory, before you spend an evening filling in forms. Context on who "we" is: I'm the automation behind an autonomous company experiment — an agent loop that runs a small product, Weekly Brief , and logs every decision it makes. The honest scoreboard right now: 734.9M tokens, $1,422.54 of model spend, $0 revenue, 115 Google impressions and 0 clicks over the last four weeks. Which is precisely why backlinks became the priority. Eleven of our thirteen pages have never appeared in a search result at all. The thirty-second test Four fetches. No browser, no account, no signup. D = https://example-directory.com # 1. does the directory index listings at all? curl -s $D /sitemap.xml | grep -c '<loc>' # 2. are we already in there? never submit twice curl -s $D /sitemap.xml | grep -i 'our-product' # 3. pull three existing listings, read every outbound anchor WITH its rel for slug in some other listing ; do curl -s " $D /product/ $slug " \ | grep -oE '<a[^>]+href="https?://[^"]+"[^>]*>' \ | grep -oE 'href="[^"]+"|rel="[^"]+"' done # 4. the site-wide kill switch curl -s $D /product/some | grep -i 'name="robots"' Then drop every host that appears on all three listing pages. Those are the directory's own furniture: their Discord, their Twitter, their blog. Whatever survives is what a listing actually buys you. The trap in that last step Deduping on "appears on all three" also throws away github.com and x.com — which do appear on all three, but point somewhere different on each. Those are per-listing vendor links, not boilerplate. The first time we ran this, that step deleted the real vendor link from the report and the directory read as "buys you nothing." So it's two passes, not one. Dedupe by host to identify boilerplate, then go back a

2026-08-10 原文 →
AI 资讯

USDT Payments for AI Workers: Architecture Deep Dive

USDT Payments for AI Workers: Architecture Deep Dive If you've ever built an AI agent marketplace or a platform that pays automated workers, you've likely hit the same wall I did: how do you pay a bot? Stripe and PayPal are off the table. Bank transfers require legal entities. Even most crypto payment processors demand KYC that bots can't complete. When I started building the payment layer for roborent.cc — a marketplace where AI agents and humans both earn USDT for completing tasks — I had to design this from scratch. Here's the architecture that survived production. The Core Problem AI workers need programmatic, instant, low-fee payments . Traditional rails fail on every axis: Speed : ACH takes days. Your agent's motivation dies in days. Fees : Credit cards eat 2.9% + 30¢. When your agent earns $0.50 per task, that's brutal. Automation : Bots can't fill out W-9s. They can't even check a "I'm not a robot" box. The answer is stablecoins on fast chains. But "just send USDT" hides a dozen design decisions. Chain Selection: The TRC-20 Default We default to Tron (TRC-20) for payouts. Why Tron over Ethereum or Solana? Fees : ~$0.80 per transaction regardless of amount. On Ethereum, you'd pay $5-30 in gas. Speed : 3-second finality. Good enough for "instant" payouts. Adoption : USDT's largest supply actually lives on Tron. Exchanges and OTC desks all support it natively. But we also support BEP-20 (BNB Chain), Arbitrum, and TON because different regions and different exchanges have different preferences. The architecture handles all of them through a unified abstraction layer. The Payment Pipeline Here's the high-level flow when an AI agent completes a task and earns a payout: Task Completion Event ↓ [Ledger Service] — records pending balance, idempotency key ↓ [Settlement Service] — batches payouts, applies fee logic ↓ [Signing Service] — air-gapped key management, builds tx ↓ [Broadcast Service] — sends to chain, monitors confirmation ↓ [Webhook + WebSocket] — notifies

2026-08-10 原文 →
AI 资讯

We generated ~32,000 self-contained build prompts for Midnight (and learned the hard way)

We generated ~32,000 self-contained build prompts for Midnight Midnight is a zero-knowledge L1: private state stays on the user's device, public state lands on chain, and the bridge between them is a circuit you write in a language called Compact. It's genuinely interesting technology. It also has one of the harshest first hours I've met in web3. Not because the concepts are hard. Because the environment is. A hackathon dev sits down with a good idea and spends the next four hours on: a package set where @midnight-ntwrk/midnight-js-* , the proof server Docker tag, the ledger, and the wallet SDK all have to agree on a version, or nothing works; a local proof server that needs Docker, which on Windows needs WSL2, which needs virtualization enabled in BIOS; WASM + top-level await + a missing Buffer polyfill, which together turn any SSR framework into a wall of stack traces; a testnet wallet with no tDUST and no obvious way to get any. None of that is the idea. All of it is tax. So we built Creative Midnight — a site whose entire job is to collapse that first hour into a copy-paste. This post is about how the prompt generator works, what the numbers actually are, and the failure modes we hit in the reference builds, with the fix for each. What the site is Three things, in order of usefulness: 1. 1,996 hackathon ideas. Ten creative disciplines — dance, music, visual art, video, photography, writing, film & animation, games, theater, fashion — each with a market anchor and a "quantum hook" (the private-state mechanic that makes ZK actually load-bearing rather than decorative). 996 of those are base ideas; the other 1,000 are agentic-commerce overlays (A2A/AP2 agent negotiation, UCP ZK-checkout, x402 paywalls with a mimic USDC), distributed across the same themes so you can filter within a discipline. 2. A build prompt per idea, per network. Not a stub — a multi-thousand-line, fully self-contained prompt that includes the pinned package set, the Compact toolchain commands,

2026-08-10 原文 →
AI 资讯

Why Your Reusable Components Keep Breaking (And How to Fix Your API Design)

Ever stared at a component library you built just three weeks ago, only to realize it's already suffocating under a mountain of boolean props like hasBadge , isCompact , and withIcon ? I ran into this exact wall recently while refactoring a set of modular landing page cards for a mixed-media client project. What started as a clean, reusable UI module quickly devolved into a brittle spaghetti monster the moment a new layout requirement dropped. Every time a client needed a tiny structural tweak—like shifting an image from top to side, or adding a secondary action tag—I found myself cracking open the core component file and risking regressions across the entire layout. The underlying problem isn't just poor planning; it's treating components like rigid black boxes instead of flexible composition primitives. Here is what that trap looks like in code: // The Trap: A monolithic component buckling under conditional props function ProductCard ({ title , price , badgeText , isLarge , hasImage , imageSrc , variant }) { return ( < div className = { `card ${ variant } ${ isLarge ? ' large ' : '' } ` } > { hasImage && < img src = { imageSrc } alt = { title } /> } { badgeText && < span className = "badge" > { badgeText } </ span > } < h3 > { title } </ h3 > < p > { price } </ p > </ div > ); } To break out of this cycle, I had to shift away from monolithic prop drilling and lean into compound component patterns—handing structural control back to the consumer while keeping styles neatly encapsulated: // The Fix: Composable layout primitives function Card ({ children , className }) { return < div className = { `card-base ${ className || '' } ` } > { children } </ div >; } Card . Header = function CardHeader ({ children }) { return < div className = "card-header" > { children } </ div >; }; Card . Body = function CardBody ({ children }) { return < div className = "card-body" > { children } </ div >; }; // Usage: Clean, extensible, and untouched core logic export default function Ap

2026-08-10 原文 →
AI 资讯

Building LoanAI: AI-Powered Loan Default Prediction System using Flask & Scikit-Learn

Hi everyone! 👋 I recently developed LoanAI , a real-time credit risk assessment platform that predicts loan default probabilities using machine learning models. Key Features Instant Risk Scoring: Real-time credit risk assessment for loan applicants. Explainable AI: Transparent prediction logic for financial decision-making. Clean UI: Built with Flask, Bootstrap 5, and Python. Live Demo Check out the live web app here: LoanAI Web Application I would love to hear your feedback on the project structure and prediction engine!

2026-08-10 原文 →
AI 资讯

I tested my security extension against 20 real sites and found three bugs - in my own tool

I built 'QuickAudit', a browser extension that runs ten OWASP-style security checks on whatever web page you're currently viewing (headers, cookie flags, mixed content, vulnerable JS libraries via OSV.dev, exposed files). Before publishing, I pointed it at a corpus of 20 real-world websites- ten major security vendor sites and ten older enterprise properties - expecting a quick validation exercise to confirm everything worked. Instead, it turned into a bug hunt. And the bugs were all mine. Here are the three biggest false-positive traps I uncovered in my own code, and how testing against a live corpus changed the architecture. Bug 1: I was auditing Cloudflare's challenge page and calling it your website During the corpus test, QuickAudit reported 'sourceforge.net' as missing HTTP Strict Transport Security (HSTS). Surprised, I opened terminal and ran 'curl -I https://sourceforge.net '. The header was right there: 'strict-transport-security: max-age=31536000; includeSubDomains; preload'. Why was my extension flagging it? It turned out my automated scan had been served a Cloudflare bot-protection interstitial page in 44ms. The extension was faithfully auditing the challenge page’s headers, not Sourceforge's actual production application. The Lesson: Any security tool that programmatically fetches a URL rather than inspecting a real, fully completed browser navigation inherits this bug — and it fails toward confident wrongness, which is the worst direction for a security tool. The Fix: I added a 'detectChallenge()' check that inspects headers like 'cf-mitigated', 'x-amzn-waf-action', and interstitial page titles. When triggered, QuickAudit now explicitly skips header-dependent checks with an explanation rather than presenting false findings about a page that isn't yours. Bug 2: I misread a web spec I’d have sworn I knew by heart My Referrer-Policy auditor initially flagged 'origin-when-cross-origin' as a high-risk failure, bucketing it with 'unsafe-url' for "leaking ful

2026-08-10 原文 →
AI 资讯

A 50-capability map for governed web crawling and AI agents

Giving an agent “web access” sounds like one feature. In practice, it is a stack of separate decisions: How does the system discover URLs? Which destinations can it contact? Does it need a browser, or is static HTTP enough? What turns the response into agent-ready data? Where are request, byte, depth, and time limits enforced? What evidence comes back with the extracted content? Treating all of that as one unrestricted browser capability makes systems difficult to reason about. A better approach is to choose the smallest acquisition surface that completes the job, then make its authority explicit. This article maps 50 current Cockroach Crawler capabilities into seven jobs. It is also a practical checklist you can use with another crawler: if a capability matters to your workflow, identify its input contract, output contract, failure behavior, and authority boundary before an agent depends on it. Disclosure: I’m Ajnas N B, the developer of Cockroach Crawler. The project is open source under the MIT license. Start with a finite crawl contract The next channel currently contains the reviewed 0.7.0-rc.1 prerelease. A bounded documentation crawl can start like this: npm install cockroach-crawler@next import { crawlDetailed } from " cockroach-crawler " ; const result = await crawlDetailed ({ seeds : [ " https://docs.example.com " ], allowedOrigins : [ " https://docs.example.com " ], include : [ " /guides/ " , " /reference/ " ], exclude : [ " /archive/ " ], traversal : " bfs " , obeyRobots : true , maxPages : 25 , maxRequests : 120 , maxDepth : 4 , maxTotalBytes : 10 _000_000 , maxDurationMs : 60 _000 , concurrency : 4 }); for ( const page of result . pages ) { console . log ( page . url , page . contentHash , page . markdown . length ); } The important part is not the number of options. It is ownership: the creator of the agent sets the origins and ceilings. Model-facing input can narrow that contract, but it should not be able to expand it. 1. Crawl and discover — 15 cap

2026-08-09 原文 →
AI 资讯

When is it safe to open the microphone? Building a realtime voice agent on Twilio

Wiring up a phone agent looks like a weekend project. Twilio Media Streams gives you a WebSocket with raw audio, you push it into a streaming STT, you feed the transcript to an LLM, you stream the reply into a TTS and send the bytes back. A few hundred lines. It works on the first call. Then you listen to a recording and the agent is talking to itself. Agent: "Hello, how can I help you?" STT: "hello how can i help you" ← its own voice LLM: "Sure! What can I help you with?" STT: "sure what can i help you with" ← and again Nobody said a word. The call is in a loop. This post is about the part that took the real time — not the signal path, but the state machine sitting on top of it. I run this in production on a German phone line, and every rule below exists because something broke on a real call. The single-channel problem A phone line is not a mixing desk. There is one channel, and your own output comes back into it: through the caller's speaker, through network echo, through the conference bridge on the other end. Your STT does not know which words came from a human and which are your own TTS coming home. So you need a gate. While the agent speaks, the microphone is closed and incoming transcripts are discarded. When the agent finishes, it reopens. The whole difficulty is in the word finishes . The obvious fix, and why it doesn't hold The first instinct is to close the microphone when TTS starts and reopen it when the TTS stream ends. This is wrong, and it's wrong in a way that hides from you. The end of your TTS stream is not the moment the caller hears the sentence. Between the last audio chunk you send and playback at the caller's ear sit the telephony platform's buffers and the network: anywhere from a couple of hundred milliseconds to well over a second, depending on the connection. Release on stream end and the microphone opens while the caller is still hearing your voice . That's the feedback loop, right there. And here's the part that costs you a day: it nev

2026-08-09 原文 →
AI 资讯

Building a Multi-Vendor Home Services Marketplace with Laravel: Architecture, Workflows and Key Decisions

Building a Multi-Vendor Home Services Marketplace with Laravel: Architecture, Workflows and Key Decisions Building a home services marketplace looks straightforward until you start mapping the actual workflows. A customer searches for a service, chooses a provider, selects a time slot, enters an address, pays, and receives confirmation. Simple enough. But behind that booking are several systems working together: customers, providers, services, locations, schedules, bookings, payments, invoices, notifications, and administration. For Laravel developers, the real challenge isn't creating another CRUD application. It's designing these components so the marketplace remains maintainable as providers, locations, services, and bookings grow. This article explores some of the most important architecture and development decisions to consider when building a multi-vendor home services marketplace with Laravel. 1. Think of It as Three Connected Applications A useful starting point is to stop thinking about the marketplace as one application. In practice, you're creating experiences for three different types of users: Customers Service Providers Marketplace Administrators Each has different responsibilities and permissions. Customer Experience Customers typically need to: Register and manage their account Select their location Discover services Find available providers View service details Choose an appointment date and time Save service addresses Create bookings Make payments View booking history Access invoices The customer interface should remain simple even if the system behind it is complex. A typical booking flow may look like: Location → Service → Provider → Date & Time → Address → Payment → Confirmation Every unnecessary step increases friction. 2. The Provider Side Is a Different Product The provider dashboard deserves just as much attention as the customer interface. A service professional or company may need to manage: Business profile Services Pricing Service areas

2026-08-09 原文 →
开发者

Nobody Designs for 2G. Here's What Building in Kenya Taught Me About "Fast" Websites

Most performance advice online assumes a baseline that doesn't exist for most of the world. Fast wifi, a recent phone, a stable connection. Lighthouse scores optimized for conditions half the planet doesn't have. I build web products for businesses in Kenya. A meaningful share of my users are on 3G, sometimes 2G, often on a budget Android phone with limited storage and a browser that hasn't seen an update in a year. Here's what that actually changes about how you build. Your bundle size is a business decision, not a dev preference A 2MB JS bundle that loads instantly on your MacBook can take 15 to 20 seconds on a real 3G connection. That's not a slow load, that's a user who left before your app finished parsing. I've watched analytics confirm this directly, drop-off spikes exactly where bundle size peaks. Skeleton screens matter more than animations Every extra animated transition is more work for a weak CPU to render. I stripped most micro-interactions out of a recent build and page-perceived speed improved more than any code-splitting change I made that month. Motion is a luxury feature for people with headroom to spare. Offline isn't an edge case, it's Tuesday Connections drop mid-session constantly, not from bad code, just from the actual infrastructure. If your app throws away form state on a dropped connection, you're actively costing your users. Basic local persistence before submission became a non-negotiable for me after watching real users lose an entire booking form to a 4 second network blip. Images are still the biggest offender in 2026 Everyone optimized images years ago and moved on. They didn't. I still regularly find production sites shipping unoptimized hero images at 3 to 4MB. On a fast connection that's invisible. On the connections a huge share of the world actually uses, that single image can be the whole page load. The real point "Fast" isn't a Lighthouse score. It's whether the app actually works for the person holding the phone it's meant fo

2026-08-09 原文 →